[PATCH v2 0/2] Adding new TI Common Platform ethernet SWitch driver

STALE5285d

Revision v2 of 5 in this series.

10 messages, 3 authors, 2012-02-23 · open the first message on its own page

[PATCH v2 0/2] Adding new TI Common Platform ethernet SWitch driver

From: Mugunthan V N <hidden>
Date: 2012-02-20 18:37:47

The following series contains driver implementation for TI Common Platform
ethernet SWitch (CPSW) driver.

CPSW is found in following SoC.
* AM335X - http://www.ti.com/litv/pdf/spruh73c
* DM814X - http://www.ti.com/litv/pdf/sprugz8

CPSW:
The three port switch gigabit ethernet subsystem provides ethernet packet
communication and can be configured as an ethernet switch.  It
supports 10/100/1000 Mbps. It provides the gigabit media independent
interface (G/MII), reduced gigabit media independent interface (RGMII),
reduced media independent interface (RMII), the management data input
output (MDIO) for physical layer device (PHY) management.

Changes from first posting:

* Made ale control table as const.

* Removed hw_stats sys fs entry and will be adding this as ethtool interface
  in ethtool feature implementation patch.

* If skb_padto() fails, we stop queue forever, corrected the sequence by
  incrementing tx_dropped and return.

Mugunthan V N (2):
  netdev: driver: ethernet: add cpsw address lookup engine support
  netdev: driver: ethernet: Add TI CPSW driver

 drivers/net/ethernet/ti/Kconfig    |   11 +
 drivers/net/ethernet/ti/Makefile   |    2 +
 drivers/net/ethernet/ti/cpsw.c     | 1029 ++++++++++++++++++++++++++++++++++++
 drivers/net/ethernet/ti/cpsw_ale.c |  505 ++++++++++++++++++
 drivers/net/ethernet/ti/cpsw_ale.h |   99 ++++
 include/linux/platform_data/cpsw.h |   55 ++
 6 files changed, 1701 insertions(+), 0 deletions(-)
 create mode 100644 drivers/net/ethernet/ti/cpsw.c
 create mode 100644 drivers/net/ethernet/ti/cpsw_ale.c
 create mode 100644 drivers/net/ethernet/ti/cpsw_ale.h
 create mode 100644 include/linux/platform_data/cpsw.h

[PATCH v2 1/2] netdev: driver: ethernet: add cpsw address lookup engine support

From: Mugunthan V N <hidden>
Date: 2012-02-20 18:37:47

TI CPSW ethernet switch has a built-in address lookup engine.  This patch adds
the code necessary for programming this module.

Signed-off-by: Cyril Chemparathy <redacted>
Signed-off-by: Mugunthan V N <redacted>
---
 drivers/net/ethernet/ti/cpsw_ale.c |  505 ++++++++++++++++++++++++++++++++++++
 drivers/net/ethernet/ti/cpsw_ale.h |   99 +++++++
 2 files changed, 604 insertions(+), 0 deletions(-)
 create mode 100644 drivers/net/ethernet/ti/cpsw_ale.c
 create mode 100644 drivers/net/ethernet/ti/cpsw_ale.h
diff --git a/drivers/net/ethernet/ti/cpsw_ale.c b/drivers/net/ethernet/ti/cpsw_ale.c
new file mode 100644
index 0000000..7c26130
--- /dev/null
+++ b/drivers/net/ethernet/ti/cpsw_ale.c
@@ -0,0 +1,505 @@
+/*
+ * Texas Instruments 3-Port Ethernet Switch Address Lookup Engine
+ *
+ * Copyright (C) 2012 Texas Instruments
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation version 2.
+ *
+ * This program is distributed "as is" WITHOUT ANY WARRANTY of any
+ * kind, whether express or implied; without even the implied warranty
+ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+#include <linux/kernel.h>
+#include <linux/platform_device.h>
+#include <linux/seq_file.h>
+#include <linux/slab.h>
+#include <linux/err.h>
+#include <linux/io.h>
+#include <linux/stat.h>
+#include <linux/sysfs.h>
+
+#include "cpsw_ale.h"
+
+#define BITMASK(bits)		(BIT(bits) - 1)
+#define ALE_ENTRY_BITS		68
+#define ALE_ENTRY_WORDS	DIV_ROUND_UP(ALE_ENTRY_BITS, 32)
+
+#define ALE_VERSION_MAJOR(rev)	((rev >> 8) & 0xff)
+#define ALE_VERSION_MINOR(rev)	(rev & 0xff)
+
+/* ALE Registers */
+#define ALE_IDVER		0x00
+#define ALE_CONTROL		0x08
+#define ALE_PRESCALE		0x10
+#define ALE_UNKNOWNVLAN		0x18
+#define ALE_TABLE_CONTROL	0x20
+#define ALE_TABLE		0x34
+#define ALE_PORTCTL		0x40
+
+#define ALE_TABLE_WRITE		BIT(31)
+
+#define ALE_TYPE_FREE			0
+#define ALE_TYPE_ADDR			1
+#define ALE_TYPE_VLAN			2
+#define ALE_TYPE_VLAN_ADDR		3
+
+#define ALE_UCAST_PERSISTANT		0
+#define ALE_UCAST_UNTOUCHED		1
+#define ALE_UCAST_OUI			2
+#define ALE_UCAST_TOUCHED		3
+
+static inline int cpsw_ale_get_field(u32 *ale_entry, u32 start, u32 bits)
+{
+	int idx;
+
+	idx    = start / 32;
+	start -= idx * 32;
+	idx    = 2 - idx; /* flip */
+	return (ale_entry[idx] >> start) & BITMASK(bits);
+}
+
+static inline void cpsw_ale_set_field(u32 *ale_entry, u32 start, u32 bits,
+				      u32 value)
+{
+	int idx;
+
+	value &= BITMASK(bits);
+	idx    = start / 32;
+	start -= idx * 32;
+	idx    = 2 - idx; /* flip */
+	ale_entry[idx] &= ~(BITMASK(bits) << start);
+	ale_entry[idx] |=  (value << start);
+}
+
+#define DEFINE_ALE_FIELD(name, start, bits)				\
+static inline int cpsw_ale_get_##name(u32 *ale_entry)			\
+{									\
+	return cpsw_ale_get_field(ale_entry, start, bits);		\
+}									\
+static inline void cpsw_ale_set_##name(u32 *ale_entry, u32 value)	\
+{									\
+	cpsw_ale_set_field(ale_entry, start, bits, value);		\
+}
+
+DEFINE_ALE_FIELD(entry_type,		60,	2)
+DEFINE_ALE_FIELD(vlan_id,		48,	12)
+DEFINE_ALE_FIELD(mcast_state,		62,	2)
+DEFINE_ALE_FIELD(port_mask,		66,     3)
+DEFINE_ALE_FIELD(super,			65,	1)
+DEFINE_ALE_FIELD(ucast_type,		62,     2)
+DEFINE_ALE_FIELD(port_num,		66,     2)
+DEFINE_ALE_FIELD(blocked,		65,     1)
+DEFINE_ALE_FIELD(secure,		64,     1)
+DEFINE_ALE_FIELD(vlan_untag_force,	24,	3)
+DEFINE_ALE_FIELD(vlan_reg_mcast,	16,	3)
+DEFINE_ALE_FIELD(vlan_unreg_mcast,	8,	3)
+DEFINE_ALE_FIELD(vlan_member_list,	0,	3)
+DEFINE_ALE_FIELD(mcast,			40,	1)
+
+/* The MAC address field in the ALE entry cannot be macroized as above */
+static inline void cpsw_ale_get_addr(u32 *ale_entry, u8 *addr)
+{
+	int i;
+
+	for (i = 0; i < 6; i++)
+		addr[i] = cpsw_ale_get_field(ale_entry, 40 - 8*i, 8);
+}
+
+static inline void cpsw_ale_set_addr(u32 *ale_entry, u8 *addr)
+{
+	int i;
+
+	for (i = 0; i < 6; i++)
+		cpsw_ale_set_field(ale_entry, 40 - 8*i, 8, addr[i]);
+}
+
+static int cpsw_ale_read(struct cpsw_ale *ale, int idx, u32 *ale_entry)
+{
+	int i;
+
+	WARN_ON(idx > ale->params.ale_entries);
+
+	__raw_writel(idx, ale->params.ale_regs + ALE_TABLE_CONTROL);
+
+	for (i = 0; i < ALE_ENTRY_WORDS; i++)
+		ale_entry[i] = __raw_readl(ale->params.ale_regs +
+					   ALE_TABLE + 4 * i);
+
+	return idx;
+}
+
+static int cpsw_ale_write(struct cpsw_ale *ale, int idx, u32 *ale_entry)
+{
+	int i;
+
+	WARN_ON(idx > ale->params.ale_entries);
+
+	for (i = 0; i < ALE_ENTRY_WORDS; i++)
+		__raw_writel(ale_entry[i], ale->params.ale_regs +
+			     ALE_TABLE + 4 * i);
+
+	__raw_writel(idx | ALE_TABLE_WRITE, ale->params.ale_regs +
+		     ALE_TABLE_CONTROL);
+
+	return idx;
+}
+
+static int cpsw_ale_match_addr(struct cpsw_ale *ale, u8 *addr)
+{
+	u32 ale_entry[ALE_ENTRY_WORDS];
+	int type, idx;
+
+	for (idx = 0; idx < ale->params.ale_entries; idx++) {
+		u8 entry_addr[6];
+
+		cpsw_ale_read(ale, idx, ale_entry);
+		type = cpsw_ale_get_entry_type(ale_entry);
+		if (type != ALE_TYPE_ADDR && type != ALE_TYPE_VLAN_ADDR)
+			continue;
+		cpsw_ale_get_addr(ale_entry, entry_addr);
+		if (memcmp(entry_addr, addr, 6) == 0)
+			return idx;
+	}
+	return -ENOENT;
+}
+
+static int cpsw_ale_match_free(struct cpsw_ale *ale)
+{
+	u32 ale_entry[ALE_ENTRY_WORDS];
+	int type, idx;
+
+	for (idx = 0; idx < ale->params.ale_entries; idx++) {
+		cpsw_ale_read(ale, idx, ale_entry);
+		type = cpsw_ale_get_entry_type(ale_entry);
+		if (type == ALE_TYPE_FREE)
+			return idx;
+	}
+	return -ENOENT;
+}
+
+static int cpsw_ale_find_ageable(struct cpsw_ale *ale)
+{
+	u32 ale_entry[ALE_ENTRY_WORDS];
+	int type, idx;
+
+	for (idx = 0; idx < ale->params.ale_entries; idx++) {
+		cpsw_ale_read(ale, idx, ale_entry);
+		type = cpsw_ale_get_entry_type(ale_entry);
+		if (type != ALE_TYPE_ADDR && type != ALE_TYPE_VLAN_ADDR)
+			continue;
+		if (cpsw_ale_get_mcast(ale_entry))
+			continue;
+		type = cpsw_ale_get_ucast_type(ale_entry);
+		if (type != ALE_UCAST_PERSISTANT &&
+		    type != ALE_UCAST_OUI)
+			return idx;
+	}
+	return -ENOENT;
+}
+
+static void cpsw_ale_flush_mcast(struct cpsw_ale *ale, u32 *ale_entry,
+				 int port_mask)
+{
+	int mask;
+
+	mask = cpsw_ale_get_port_mask(ale_entry);
+	if ((mask & port_mask) == 0)
+		return; /* ports dont intersect, not interested */
+	mask &= ~port_mask;
+
+	/* free if only remaining port is host port */
+	if (mask == BIT(ale->params.ale_ports))
+		cpsw_ale_set_entry_type(ale_entry, ALE_TYPE_FREE);
+	else
+		cpsw_ale_set_port_mask(ale_entry, mask);
+}
+
+static void cpsw_ale_flush_ucast(struct cpsw_ale *ale, u32 *ale_entry,
+				 int port_mask)
+{
+	int port;
+
+	port = cpsw_ale_get_port_num(ale_entry);
+	if ((BIT(port) & port_mask) == 0)
+		return; /* ports dont intersect, not interested */
+	cpsw_ale_set_entry_type(ale_entry, ALE_TYPE_FREE);
+}
+
+int cpsw_ale_flush(struct cpsw_ale *ale, int port_mask)
+{
+	u32 ale_entry[ALE_ENTRY_WORDS];
+	int ret, idx;
+
+	for (idx = 0; idx < ale->params.ale_entries; idx++) {
+		cpsw_ale_read(ale, idx, ale_entry);
+		ret = cpsw_ale_get_entry_type(ale_entry);
+		if (ret != ALE_TYPE_ADDR && ret != ALE_TYPE_VLAN_ADDR)
+			continue;
+
+		if (cpsw_ale_get_mcast(ale_entry))
+			cpsw_ale_flush_mcast(ale, ale_entry, port_mask);
+		else
+			cpsw_ale_flush_ucast(ale, ale_entry, port_mask);
+
+		cpsw_ale_write(ale, idx, ale_entry);
+	}
+	return 0;
+}
+
+int cpsw_ale_add_ucast(struct cpsw_ale *ale, u8 *addr, int port, int flags)
+{
+	u32 ale_entry[ALE_ENTRY_WORDS] = {0, 0, 0};
+	int idx;
+
+	cpsw_ale_set_entry_type(ale_entry, ALE_TYPE_ADDR);
+	cpsw_ale_set_addr(ale_entry, addr);
+	cpsw_ale_set_ucast_type(ale_entry, ALE_UCAST_PERSISTANT);
+	cpsw_ale_set_secure(ale_entry, (flags & ALE_SECURE) ? 1 : 0);
+	cpsw_ale_set_blocked(ale_entry, (flags & ALE_BLOCKED) ? 1 : 0);
+	cpsw_ale_set_port_num(ale_entry, port);
+
+	idx = cpsw_ale_match_addr(ale, addr);
+	if (idx < 0)
+		idx = cpsw_ale_match_free(ale);
+	if (idx < 0)
+		idx = cpsw_ale_find_ageable(ale);
+	if (idx < 0)
+		return -ENOMEM;
+
+	cpsw_ale_write(ale, idx, ale_entry);
+	return 0;
+}
+
+int cpsw_ale_del_ucast(struct cpsw_ale *ale, u8 *addr, int port)
+{
+	u32 ale_entry[ALE_ENTRY_WORDS] = {0, 0, 0};
+	int idx;
+
+	idx = cpsw_ale_match_addr(ale, addr);
+	if (idx < 0)
+		return -ENOENT;
+
+	cpsw_ale_set_entry_type(ale_entry, ALE_TYPE_FREE);
+	cpsw_ale_write(ale, idx, ale_entry);
+	return 0;
+}
+
+int cpsw_ale_add_mcast(struct cpsw_ale *ale, u8 *addr, int port_mask,
+			int super, int mcast_state)
+{
+	u32 ale_entry[ALE_ENTRY_WORDS] = {0, 0, 0};
+	int idx, mask;
+
+	idx = cpsw_ale_match_addr(ale, addr);
+	if (idx >= 0)
+		cpsw_ale_read(ale, idx, ale_entry);
+
+	cpsw_ale_set_entry_type(ale_entry, ALE_TYPE_ADDR);
+	cpsw_ale_set_addr(ale_entry, addr);
+	cpsw_ale_set_super(ale_entry, super);
+	cpsw_ale_set_mcast_state(ale_entry, mcast_state);
+
+	mask = cpsw_ale_get_port_mask(ale_entry);
+	port_mask |= mask;
+	cpsw_ale_set_port_mask(ale_entry, port_mask);
+
+	if (idx < 0)
+		idx = cpsw_ale_match_free(ale);
+	if (idx < 0)
+		idx = cpsw_ale_find_ageable(ale);
+	if (idx < 0)
+		return -ENOMEM;
+
+	cpsw_ale_write(ale, idx, ale_entry);
+	return 0;
+}
+
+int cpsw_ale_del_mcast(struct cpsw_ale *ale, u8 *addr, int port_mask)
+{
+	u32 ale_entry[ALE_ENTRY_WORDS] = {0, 0, 0};
+	int idx;
+
+	idx = cpsw_ale_match_addr(ale, addr);
+	if (idx < 0)
+		return -EINVAL;
+
+	cpsw_ale_read(ale, idx, ale_entry);
+
+	if (port_mask)
+		cpsw_ale_set_port_mask(ale_entry, port_mask);
+	else
+		cpsw_ale_set_entry_type(ale_entry, ALE_TYPE_FREE);
+
+	cpsw_ale_write(ale, idx, ale_entry);
+	return 0;
+}
+
+struct ale_control_info {
+	const char	*name;
+	int		offset, port_offset;
+	int		shift, port_shift;
+	int		bits;
+};
+
+#define CTRL_GLOBAL(name, bit)		{#name, ALE_CONTROL, 0, bit, 0, 1}
+#define CTRL_UNK(name, shift, bits)	{#name, ALE_UNKNOWNVLAN, 0, shift, \
+						0, bits}
+#define CTRL_PORTCTL(name, start, bits)	{#name, ALE_PORTCTL, 4, start, 0, bits}
+
+static const struct ale_control_info ale_controls[] = {
+	[ALE_ENABLE]		= CTRL_GLOBAL(enable, 31),
+	[ALE_CLEAR]		= CTRL_GLOBAL(clear, 30),
+	[ALE_AGEOUT]		= CTRL_GLOBAL(ageout, 29),
+	[ALE_VLAN_NOLEARN]	= CTRL_GLOBAL(vlan_nolearn, 7),
+	[ALE_NO_PORT_VLAN]	= CTRL_GLOBAL(no_port_vlan, 6),
+	[ALE_OUI_DENY]		= CTRL_GLOBAL(oui_deny, 5),
+	[ALE_BYPASS]		= CTRL_GLOBAL(bypass, 4),
+	[ALE_RATE_LIMIT_TX]	= CTRL_GLOBAL(rate_limit_tx, 3),
+	[ALE_VLAN_AWARE]	= CTRL_GLOBAL(vlan_aware, 2),
+	[ALE_AUTH_ENABLE]	= CTRL_GLOBAL(auth_enable, 1),
+	[ALE_RATE_LIMIT]	= CTRL_GLOBAL(rate_limit, 0),
+
+	[ALE_PORT_STATE]	     = CTRL_PORTCTL(port_state, 0, 2),
+	[ALE_PORT_DROP_UNTAGGED]     = CTRL_PORTCTL(drop_untagged, 2, 1),
+	[ALE_PORT_DROP_UNKNOWN_VLAN] = CTRL_PORTCTL(drop_unknown, 3, 1),
+	[ALE_PORT_NOLEARN]	     = CTRL_PORTCTL(nolearn, 4, 1),
+	[ALE_PORT_MCAST_LIMIT]	     = CTRL_PORTCTL(mcast_limit, 16, 8),
+	[ALE_PORT_BCAST_LIMIT]	     = CTRL_PORTCTL(bcast_limit, 24, 8),
+
+	[ALE_PORT_UNKNOWN_VLAN_MEMBER]	   =
+			CTRL_UNK(unknown_vlan_member, 0, 6),
+	[ALE_PORT_UNKNOWN_MCAST_FLOOD]	   =
+			CTRL_UNK(unknown_mcast_flood, 8, 6),
+	[ALE_PORT_UNKNOWN_REG_MCAST_FLOOD] =
+			CTRL_UNK(unknown_reg_flood, 16, 6),
+	[ALE_PORT_UNTAGGED_EGRESS]	   =
+			CTRL_UNK(untagged_egress, 24, 6),
+};
+
+int cpsw_ale_control_set(struct cpsw_ale *ale, int port, int control,
+			 int value)
+{
+	struct ale_control_info *info = &ale_controls[control];
+	int offset, shift;
+	u32 tmp, mask;
+
+	if (control < 0 || control >= ARRAY_SIZE(ale_controls))
+		return -EINVAL;
+
+	if (info->port_offset == 0 && info->port_shift == 0)
+		port = 0; /* global, port is a dont care */
+
+	if (port < 0 || port > ale->params.ale_ports)
+		return -EINVAL;
+
+	mask = BITMASK(info->bits);
+	if (value & ~mask)
+		return -EINVAL;
+
+	offset = info->offset + (port * info->port_offset);
+	shift  = info->shift  + (port * info->port_shift);
+
+	tmp = __raw_readl(ale->params.ale_regs + offset);
+	tmp = (tmp & ~(mask << shift)) | (value << shift);
+	__raw_writel(tmp, ale->params.ale_regs + offset);
+
+	return 0;
+}
+
+int cpsw_ale_control_get(struct cpsw_ale *ale, int port, int control)
+{
+	struct ale_control_info *info = &ale_controls[control];
+	int offset, shift;
+	u32 tmp;
+
+	if (control < 0 || control >= ARRAY_SIZE(ale_controls))
+		return -EINVAL;
+
+	if (info->port_offset == 0 && info->port_shift == 0)
+		port = 0; /* global, port is a dont care */
+
+	if (port < 0 || port > ale->params.ale_ports)
+		return -EINVAL;
+
+	offset = info->offset + (port * info->port_offset);
+	shift  = info->shift  + (port * info->port_shift);
+
+	tmp = __raw_readl(ale->params.ale_regs + offset) >> shift;
+	return tmp & BITMASK(info->bits);
+}
+
+static void cpsw_ale_timer(unsigned long arg)
+{
+	struct cpsw_ale *ale = (struct cpsw_ale *)arg;
+
+	cpsw_ale_control_set(ale, 0, ALE_AGEOUT, 1);
+
+	if (ale->ageout) {
+		ale->timer.expires = jiffies + ale->ageout;
+		add_timer(&ale->timer);
+	}
+}
+
+int cpsw_ale_set_ageout(struct cpsw_ale *ale, int ageout)
+{
+	del_timer_sync(&ale->timer);
+	ale->ageout = ageout * HZ;
+	if (ale->ageout) {
+		ale->timer.expires = jiffies + ale->ageout;
+		add_timer(&ale->timer);
+	}
+	return 0;
+}
+
+void cpsw_ale_start(struct cpsw_ale *ale)
+{
+	u32 rev;
+
+	rev = __raw_readl(ale->params.ale_regs + ALE_IDVER);
+	dev_dbg(ale->params.dev, "initialized cpsw ale revision %d.%d\n",
+		ALE_VERSION_MAJOR(rev), ALE_VERSION_MINOR(rev));
+	cpsw_ale_control_set(ale, 0, ALE_ENABLE, 1);
+	cpsw_ale_control_set(ale, 0, ALE_CLEAR, 1);
+
+	init_timer(&ale->timer);
+	ale->timer.data	    = (unsigned long)ale;
+	ale->timer.function = cpsw_ale_timer;
+	if (ale->ageout) {
+		ale->timer.expires = jiffies + ale->ageout;
+		add_timer(&ale->timer);
+	}
+}
+
+void cpsw_ale_stop(struct cpsw_ale *ale)
+{
+	del_timer_sync(&ale->timer);
+}
+
+struct cpsw_ale *cpsw_ale_create(struct cpsw_ale_params *params)
+{
+	struct cpsw_ale *ale;
+	int ret;
+
+	ret = -ENOMEM;
+	ale = kzalloc(sizeof(*ale), GFP_KERNEL);
+	if (WARN_ON(!ale))
+		return NULL;
+
+	ale->params = *params;
+	ale->ageout = ale->params.ale_ageout * HZ;
+
+	return ale;
+}
+
+int cpsw_ale_destroy(struct cpsw_ale *ale)
+{
+	if (!ale)
+		return -EINVAL;
+	cpsw_ale_stop(ale);
+	cpsw_ale_control_set(ale, 0, ALE_ENABLE, 0);
+	kfree(ale);
+	return 0;
+}
diff --git a/drivers/net/ethernet/ti/cpsw_ale.h b/drivers/net/ethernet/ti/cpsw_ale.h
new file mode 100644
index 0000000..e3f0f35
--- /dev/null
+++ b/drivers/net/ethernet/ti/cpsw_ale.h
@@ -0,0 +1,99 @@
+/*
+ * Texas Instruments 3-Port Ethernet Switch Address Lookup Engine APIs
+ *
+ * Copyright (C) 2012 Texas Instruments
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation version 2.
+ *
+ * This program is distributed "as is" WITHOUT ANY WARRANTY of any
+ * kind, whether express or implied; without even the implied warranty
+ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+#ifndef __TI_CPSW_ALE_H__
+#define __TI_CPSW_ALE_H__
+
+struct cpsw_ale_params {
+	struct device		*dev;
+	void __iomem		*ale_regs;
+	unsigned long		ale_ageout;	/* in secs */
+	unsigned long		ale_entries;
+	unsigned long		ale_ports;
+};
+
+struct cpsw_ale {
+	struct cpsw_ale_params	params;
+	struct timer_list	timer;
+	unsigned long		ageout;
+	struct device_attribute ale_control_attr;
+#define control_attr_to_ale(attr)	\
+	container_of(attr, struct cpsw_ale, ale_control_attr);
+	struct device_attribute ale_table_attr;
+#define table_attr_to_ale(attr)		\
+	container_of(attr, struct cpsw_ale, ale_table_attr);
+};
+
+enum cpsw_ale_control {
+	/* global */
+	ALE_ENABLE,
+	ALE_CLEAR,
+	ALE_AGEOUT,
+	ALE_VLAN_NOLEARN,
+	ALE_NO_PORT_VLAN,
+	ALE_OUI_DENY,
+	ALE_BYPASS,
+	ALE_RATE_LIMIT_TX,
+	ALE_VLAN_AWARE,
+	ALE_AUTH_ENABLE,
+	ALE_RATE_LIMIT,
+	/* port controls */
+	ALE_PORT_STATE,
+	ALE_PORT_DROP_UNTAGGED,
+	ALE_PORT_DROP_UNKNOWN_VLAN,
+	ALE_PORT_NOLEARN,
+	ALE_PORT_UNKNOWN_VLAN_MEMBER,
+	ALE_PORT_UNKNOWN_MCAST_FLOOD,
+	ALE_PORT_UNKNOWN_REG_MCAST_FLOOD,
+	ALE_PORT_UNTAGGED_EGRESS,
+	ALE_PORT_BCAST_LIMIT,
+	ALE_PORT_MCAST_LIMIT,
+	ALE_NUM_CONTROLS,
+};
+
+enum cpsw_ale_port_state {
+	ALE_PORT_STATE_DISABLE	= 0x00,
+	ALE_PORT_STATE_BLOCK	= 0x01,
+	ALE_PORT_STATE_LEARN	= 0x02,
+	ALE_PORT_STATE_FORWARD	= 0x03,
+};
+
+/* ALE unicast entry flags - passed into cpsw_ale_add_ucast() */
+#define ALE_SECURE			1
+#define ALE_BLOCKED			2
+
+#define ALE_MCAST_FWD			0
+#define ALE_MCAST_BLOCK_LEARN_FWD	1
+#define ALE_MCAST_FWD_LEARN		2
+#define ALE_MCAST_FWD_2			3
+
+struct cpsw_ale *cpsw_ale_create(struct cpsw_ale_params *params);
+int cpsw_ale_destroy(struct cpsw_ale *ale);
+
+void cpsw_ale_start(struct cpsw_ale *ale);
+void cpsw_ale_stop(struct cpsw_ale *ale);
+
+int cpsw_ale_set_ageout(struct cpsw_ale *ale, int ageout);
+int cpsw_ale_flush(struct cpsw_ale *ale, int port_mask);
+int cpsw_ale_add_ucast(struct cpsw_ale *ale, u8 *addr, int port, int flags);
+int cpsw_ale_del_ucast(struct cpsw_ale *ale, u8 *addr, int port);
+int cpsw_ale_add_mcast(struct cpsw_ale *ale, u8 *addr, int port_mask,
+			int super, int mcast_state);
+int cpsw_ale_del_mcast(struct cpsw_ale *ale, u8 *addr, int port_mask);
+
+int cpsw_ale_control_get(struct cpsw_ale *ale, int port, int control);
+int cpsw_ale_control_set(struct cpsw_ale *ale, int port,
+			 int control, int value);
+
+#endif
-- 
1.7.0.4

[PATCH v2 2/2] netdev: driver: ethernet: Add TI CPSW driver

From: Mugunthan V N <hidden>
Date: 2012-02-20 18:38:00

This patch adds support for TI's CPSW driver.

The three port switch gigabit ethernet subsystem provides ethernet packet
communication and can be configured as an ethernet switch. Supports
10/100/1000 Mbps.

Signed-off-by: Cyril Chemparathy <redacted>
Signed-off-by: Sriramakrishnan A G <redacted>
Signed-off-by: Mugunthan V N <redacted>
---
 drivers/net/ethernet/ti/Kconfig    |   11 +
 drivers/net/ethernet/ti/Makefile   |    2 +
 drivers/net/ethernet/ti/cpsw.c     | 1029 ++++++++++++++++++++++++++++++++++++
 include/linux/platform_data/cpsw.h |   55 ++
 4 files changed, 1097 insertions(+), 0 deletions(-)
 create mode 100644 drivers/net/ethernet/ti/cpsw.c
 create mode 100644 include/linux/platform_data/cpsw.h
diff --git a/drivers/net/ethernet/ti/Kconfig b/drivers/net/ethernet/ti/Kconfig
index de76c70..b42252c 100644
--- a/drivers/net/ethernet/ti/Kconfig
+++ b/drivers/net/ethernet/ti/Kconfig
@@ -49,6 +49,17 @@ config TI_DAVINCI_CPDMA
 	  To compile this driver as a module, choose M here: the module
 	  will be called davinci_cpdma.  This is recommended.
 
+config TI_CPSW
+	tristate "TI CPSW Switch Support"
+	depends on ARM && (ARCH_DAVINCI || SOC_OMAPAM33XX)
+	select TI_DAVINCI_CPDMA
+	select TI_DAVINCI_MDIO
+	---help---
+	  This driver supports TI's CPSW Ethernet Switch.
+
+	  To compile this driver as a module, choose M here: the module
+	  will be called cpsw.
+
 config TLAN
 	tristate "TI ThunderLAN support"
 	depends on (PCI || EISA)
diff --git a/drivers/net/ethernet/ti/Makefile b/drivers/net/ethernet/ti/Makefile
index aedb3af..91bd8bb 100644
--- a/drivers/net/ethernet/ti/Makefile
+++ b/drivers/net/ethernet/ti/Makefile
@@ -7,3 +7,5 @@ obj-$(CONFIG_CPMAC) += cpmac.o
 obj-$(CONFIG_TI_DAVINCI_EMAC) += davinci_emac.o
 obj-$(CONFIG_TI_DAVINCI_MDIO) += davinci_mdio.o
 obj-$(CONFIG_TI_DAVINCI_CPDMA) += davinci_cpdma.o
+obj-$(CONFIG_TI_CPSW) += ti_cpsw.o
+ti_cpsw-y := cpsw_ale.o cpsw.o
diff --git a/drivers/net/ethernet/ti/cpsw.c b/drivers/net/ethernet/ti/cpsw.c
new file mode 100644
index 0000000..8c66b49
--- /dev/null
+++ b/drivers/net/ethernet/ti/cpsw.c
@@ -0,0 +1,1029 @@
+/*
+ * Texas Instruments Ethernet Switch Driver
+ *
+ * Copyright (C) 2012 Texas Instruments
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation version 2.
+ *
+ * This program is distributed "as is" WITHOUT ANY WARRANTY of any
+ * kind, whether express or implied; without even the implied warranty
+ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+#include <linux/kernel.h>
+#include <linux/io.h>
+#include <linux/clk.h>
+#include <linux/timer.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/irqreturn.h>
+#include <linux/interrupt.h>
+#include <linux/if_ether.h>
+#include <linux/etherdevice.h>
+#include <linux/netdevice.h>
+#include <linux/phy.h>
+#include <linux/workqueue.h>
+#include <linux/delay.h>
+
+#include <linux/platform_data/cpsw.h>
+
+#include "cpsw_ale.h"
+#include "davinci_cpdma.h"
+
+#define CPSW_DEBUG	(NETIF_MSG_HW		| NETIF_MSG_WOL		| \
+			 NETIF_MSG_DRV		| NETIF_MSG_LINK	| \
+			 NETIF_MSG_IFUP		| NETIF_MSG_INTR	| \
+			 NETIF_MSG_PROBE	| NETIF_MSG_TIMER	| \
+			 NETIF_MSG_IFDOWN	| NETIF_MSG_RX_ERR	| \
+			 NETIF_MSG_TX_ERR	| NETIF_MSG_TX_DONE	| \
+			 NETIF_MSG_PKTDATA	| NETIF_MSG_TX_QUEUED	| \
+			 NETIF_MSG_RX_STATUS)
+
+#define msg(level, type, format, ...)				\
+do {								\
+	if (netif_msg_##type(priv) && net_ratelimit())		\
+		dev_##level(priv->dev, format, ## __VA_ARGS__);	\
+} while (0)
+
+#define CPSW_MAJOR_VERSION(reg)		(reg >> 8 & 0x7)
+#define CPSW_MINOR_VERSION(reg)		(reg & 0xff)
+#define CPSW_RTL_VERSION(reg)		((reg >> 11) & 0x1f)
+
+#define CPDMA_RXTHRESH		0x0c0
+#define CPDMA_RXFREE		0x0e0
+#define CPDMA_TXHDP		0x00
+#define CPDMA_RXHDP		0x20
+#define CPDMA_TXCP		0x40
+#define CPDMA_RXCP		0x60
+
+#define CPSW_POLL_WEIGHT	64
+#define CPSW_MIN_PACKET_SIZE	60
+#define CPSW_MAX_PACKET_SIZE	(1500 + 14 + 4 + 4)
+
+#define RX_PRIORITY_MAPPING	0x76543210
+#define TX_PRIORITY_MAPPING	0x33221100
+#define CPDMA_TX_PRIORITY_MAP	0x76543210
+
+#define cpsw_enable_irq(priv)	\
+	do {			\
+		u32 i;		\
+		for (i = 0; i < priv->num_irqs; i++) \
+			enable_irq(priv->irqs_table[i]); \
+	} while (0);
+#define cpsw_disable_irq(priv)	\
+	do {			\
+		u32 i;		\
+		for (i = 0; i < priv->num_irqs; i++) \
+			disable_irq_nosync(priv->irqs_table[i]); \
+	} while (0);
+
+static int debug_level;
+module_param(debug_level, int, 0);
+MODULE_PARM_DESC(debug_level, "cpsw debug level (NETIF_MSG bits)");
+
+static int ale_ageout = 10;
+module_param(ale_ageout, int, 0);
+MODULE_PARM_DESC(ale_ageout, "cpsw ale ageout interval (seconds)");
+
+static int rx_packet_max = CPSW_MAX_PACKET_SIZE;
+module_param(rx_packet_max, int, 0);
+MODULE_PARM_DESC(rx_packet_max, "maximum receive packet size (bytes)");
+
+struct cpsw_ss_regs {
+	u32	id_ver;
+	u32	soft_reset;
+	u32	control;
+	u32	int_control;
+	u32	rx_thresh_en;
+	u32	rx_en;
+	u32	tx_en;
+	u32	misc_en;
+};
+
+struct cpsw_regs {
+	u32	id_ver;
+	u32	control;
+	u32	soft_reset;
+	u32	stat_port_en;
+	u32	ptype;
+};
+
+struct cpsw_slave_regs {
+	u32	max_blks;
+	u32	blk_cnt;
+	u32	flow_thresh;
+	u32	port_vlan;
+	u32	tx_pri_map;
+	u32	ts_ctl;
+	u32	ts_seq_ltype;
+	u32	ts_vlan;
+	u32	sa_lo;
+	u32	sa_hi;
+};
+
+struct cpsw_host_regs {
+	u32	max_blks;
+	u32	blk_cnt;
+	u32	flow_thresh;
+	u32	port_vlan;
+	u32	tx_pri_map;
+	u32	cpdma_tx_pri_map;
+	u32	cpdma_rx_chan_map;
+};
+
+struct cpsw_sliver_regs {
+	u32	id_ver;
+	u32	mac_control;
+	u32	mac_status;
+	u32	soft_reset;
+	u32	rx_maxlen;
+	u32	__reserved_0;
+	u32	rx_pause;
+	u32	tx_pause;
+	u32	__reserved_1;
+	u32	rx_pri_map;
+};
+
+struct cpsw_hw_stats {
+	u32	rxgoodframes;
+	u32	rxbroadcastframes;
+	u32	rxmulticastframes;
+	u32	rxpauseframes;
+	u32	rxcrcerrors;
+	u32	rxaligncodeerrors;
+	u32	rxoversizedframes;
+	u32	rxjabberframes;
+	u32	rxundersizedframes;
+	u32	rxfragments;
+	u32	__pad_0[2];
+	u32	rxoctets;
+	u32	txgoodframes;
+	u32	txbroadcastframes;
+	u32	txmulticastframes;
+	u32	txpauseframes;
+	u32	txdeferredframes;
+	u32	txcollisionframes;
+	u32	txsinglecollframes;
+	u32	txmultcollframes;
+	u32	txexcessivecollisions;
+	u32	txlatecollisions;
+	u32	txunderrun;
+	u32	txcarriersenseerrors;
+	u32	txoctets;
+	u32	octetframes64;
+	u32	octetframes65t127;
+	u32	octetframes128t255;
+	u32	octetframes256t511;
+	u32	octetframes512t1023;
+	u32	octetframes1024tup;
+	u32	netoctets;
+	u32	rxsofoverruns;
+	u32	rxmofoverruns;
+	u32	rxdmaoverruns;
+};
+
+struct cpsw_slave {
+	struct cpsw_slave_regs __iomem	*regs;
+	struct cpsw_sliver_regs __iomem	*sliver;
+	int				slave_num;
+	u32				mac_control;
+	struct cpsw_slave_data		*data;
+	struct phy_device		*phy;
+};
+
+struct cpsw_priv {
+	spinlock_t			lock;
+	struct platform_device		*pdev;
+	struct net_device		*ndev;
+	struct resource			*cpsw_res;
+	struct resource			*cpsw_ss_res;
+	struct napi_struct		napi;
+#define napi_to_priv(napi)	container_of(napi, struct cpsw_priv, napi)
+	struct device			*dev;
+	struct cpsw_platform_data	data;
+	struct cpsw_regs __iomem	*regs;
+	struct cpsw_ss_regs __iomem	*ss_regs;
+	struct cpsw_hw_stats __iomem	*hw_stats;
+	struct cpsw_host_regs __iomem	*host_port_regs;
+	u32				msg_enable;
+	struct net_device_stats		stats;
+	int				rx_packet_max;
+	int				host_port;
+	struct clk			*clk;
+	u8				mac_addr[ETH_ALEN];
+	struct cpsw_slave		*slaves;
+#define for_each_slave(priv, func, arg...)			\
+	do {							\
+		int idx;					\
+		for (idx = 0; idx < (priv)->data.slaves; idx++)	\
+			(func)((priv)->slaves + idx, ##arg);	\
+	} while (0)
+
+	struct cpdma_ctlr		*dma;
+	struct cpdma_chan		*txch, *rxch;
+	struct cpsw_ale			*ale;
+	/* snapshot of IRQ numbers */
+	u32 irqs_table[4];
+	u32 num_irqs;
+};
+
+static void cpsw_intr_enable(struct cpsw_priv *priv)
+{
+	__raw_writel(0xFF, &priv->ss_regs->tx_en);
+	__raw_writel(0xFF, &priv->ss_regs->rx_en);
+
+	cpdma_ctlr_int_ctrl(priv->dma, true);
+	return;
+}
+
+static void cpsw_intr_disable(struct cpsw_priv *priv)
+{
+	__raw_writel(0, &priv->ss_regs->tx_en);
+	__raw_writel(0, &priv->ss_regs->rx_en);
+
+	cpdma_ctlr_int_ctrl(priv->dma, false);
+	return;
+}
+
+void cpsw_tx_handler(void *token, int len, int status)
+{
+	struct sk_buff		*skb = token;
+	struct net_device	*ndev = skb->dev;
+	struct cpsw_priv	*priv = netdev_priv(ndev);
+
+	if (unlikely(netif_queue_stopped(ndev)))
+		netif_start_queue(ndev);
+	priv->stats.tx_packets++;
+	priv->stats.tx_bytes += len;
+	dev_kfree_skb_any(skb);
+}
+
+void cpsw_rx_handler(void *token, int len, int status)
+{
+	struct sk_buff		*skb = token;
+	struct net_device	*ndev = skb->dev;
+	struct cpsw_priv	*priv = netdev_priv(ndev);
+	int			ret = 0;
+
+	/* free and bail if we are shutting down */
+	if (unlikely(!netif_running(ndev)) ||
+			unlikely(!netif_carrier_ok(ndev))) {
+		dev_kfree_skb_any(skb);
+		return;
+	}
+	if (likely(status >= 0)) {
+		skb_put(skb, len);
+		skb->protocol = eth_type_trans(skb, ndev);
+		netif_receive_skb(skb);
+		priv->stats.rx_bytes += len;
+		priv->stats.rx_packets++;
+		skb = NULL;
+	}
+
+	if (unlikely(!netif_running(ndev))) {
+		if (skb)
+			dev_kfree_skb_any(skb);
+		return;
+	}
+
+	if (likely(!skb)) {
+		skb = netdev_alloc_skb_ip_align(ndev, priv->rx_packet_max);
+		if (WARN_ON(!skb))
+			return;
+
+		ret = cpdma_chan_submit(priv->rxch, skb, skb->data,
+					skb_tailroom(skb), GFP_KERNEL);
+	}
+	WARN_ON(ret < 0);
+}
+
+static irqreturn_t cpsw_interrupt(int irq, void *dev_id)
+{
+	struct cpsw_priv *priv = dev_id;
+
+	if (likely(netif_running(priv->ndev))) {
+		cpsw_intr_disable(priv);
+		cpsw_disable_irq(priv);
+		napi_schedule(&priv->napi);
+	}
+	return IRQ_HANDLED;
+}
+
+static inline int cpsw_get_slave_port(struct cpsw_priv *priv, u32 slave_num)
+{
+	if (priv->host_port == 0)
+		return slave_num + 1;
+	else
+		return slave_num;
+}
+
+static int cpsw_poll(struct napi_struct *napi, int budget)
+{
+	struct cpsw_priv	*priv = napi_to_priv(napi);
+	int			num_tx, num_rx;
+
+	num_tx = cpdma_chan_process(priv->txch, 128);
+	num_rx = cpdma_chan_process(priv->rxch, budget);
+
+	if (num_rx || num_tx)
+		msg(dbg, intr, "poll %d rx, %d tx pkts\n", num_rx, num_tx);
+
+	if (num_rx < budget) {
+		napi_complete(napi);
+		cpsw_intr_enable(priv);
+		cpdma_ctlr_eoi(priv->dma);
+		cpsw_enable_irq(priv);
+	}
+
+	return num_rx;
+}
+
+static inline void soft_reset(const char *module, void __iomem *reg)
+{
+	unsigned long timeout = jiffies + HZ;
+
+	__raw_writel(1, reg);
+	do {
+		cpu_relax();
+	} while ((__raw_readl(reg) & 1) && time_after(timeout, jiffies));
+
+	WARN(__raw_readl(reg) & 1, "failed to soft-reset %s\n", module);
+}
+
+#define mac_hi(mac)	(((mac)[0] << 0) | ((mac)[1] << 8) |	\
+			 ((mac)[2] << 16) | ((mac)[3] << 24))
+#define mac_lo(mac)	(((mac)[4] << 0) | ((mac)[5] << 8))
+
+static void cpsw_set_slave_mac(struct cpsw_slave *slave,
+			       struct cpsw_priv *priv)
+{
+	__raw_writel(mac_hi(priv->mac_addr), &slave->regs->sa_hi);
+	__raw_writel(mac_lo(priv->mac_addr), &slave->regs->sa_lo);
+}
+
+static void _cpsw_adjust_link(struct cpsw_slave *slave,
+			      struct cpsw_priv *priv, bool *link)
+{
+	struct phy_device	*phy = slave->phy;
+	u32			mac_control = 0;
+	u32			slave_port;
+
+	if (!phy)
+		return;
+
+	slave_port = cpsw_get_slave_port(priv, slave->slave_num);
+
+	if (phy->link) {
+		mac_control = priv->data.mac_control;
+
+		/* enable forwarding */
+		cpsw_ale_control_set(priv->ale, slave_port,
+				     ALE_PORT_STATE, ALE_PORT_STATE_FORWARD);
+
+		if (phy->speed == 1000)
+			mac_control |= BIT(7);	/* GIGABITEN	*/
+		if (phy->duplex)
+			mac_control |= BIT(0);	/* FULLDUPLEXEN	*/
+		*link = true;
+	} else {
+		mac_control = 0;
+		/* diable forwarding */
+		cpsw_ale_control_set(priv->ale, slave_port,
+				     ALE_PORT_STATE, ALE_PORT_STATE_DISABLE);
+	}
+
+	if (mac_control != slave->mac_control) {
+		phy_print_status(phy);
+		__raw_writel(mac_control, &slave->sliver->mac_control);
+	}
+
+	slave->mac_control = mac_control;
+}
+
+static void cpsw_adjust_link(struct net_device *ndev)
+{
+	struct cpsw_priv	*priv = netdev_priv(ndev);
+	bool			link = false;
+
+	for_each_slave(priv, _cpsw_adjust_link, priv, &link);
+
+	if (link) {
+		netif_carrier_on(ndev);
+		if (netif_running(ndev))
+			netif_wake_queue(ndev);
+	} else {
+		netif_carrier_off(ndev);
+		netif_stop_queue(ndev);
+	}
+}
+
+static inline int __show_stat(char *buf, int maxlen, const char *name, u32 val)
+{
+	static char *leader = "........................................";
+
+	if (!val)
+		return 0;
+	else
+		return snprintf(buf, maxlen, "%s %s %10d\n", name,
+				leader + strlen(name), val);
+}
+
+static void cpsw_slave_open(struct cpsw_slave *slave, struct cpsw_priv *priv)
+{
+	char name[32];
+	u32 slave_port;
+
+	sprintf(name, "slave-%d", slave->slave_num);
+
+	soft_reset(name, &slave->sliver->soft_reset);
+
+	/* setup priority mapping */
+	__raw_writel(RX_PRIORITY_MAPPING, &slave->sliver->rx_pri_map);
+	__raw_writel(TX_PRIORITY_MAPPING, &slave->regs->tx_pri_map);
+
+	/* setup max packet size, and mac address */
+	__raw_writel(priv->rx_packet_max, &slave->sliver->rx_maxlen);
+	cpsw_set_slave_mac(slave, priv);
+
+	slave->mac_control = 0;	/* no link yet */
+
+	slave_port = cpsw_get_slave_port(priv, slave->slave_num);
+
+	cpsw_ale_add_mcast(priv->ale, priv->ndev->broadcast,
+			   1 << slave_port, 0, ALE_MCAST_FWD_2);
+
+	slave->phy = phy_connect(priv->ndev, slave->data->phy_id,
+				 &cpsw_adjust_link, 0, slave->data->phy_if);
+	if (IS_ERR(slave->phy)) {
+		msg(err, ifup, "phy %s not found on slave %d\n",
+		    slave->data->phy_id, slave->slave_num);
+		slave->phy = NULL;
+	} else {
+		printk(KERN_ERR "\nCPSW phy found : id is : 0x%x\n",
+			slave->phy->phy_id);
+		phy_start(slave->phy);
+	}
+}
+
+static void cpsw_init_host_port(struct cpsw_priv *priv)
+{
+	/* soft reset the controller and initialize ale */
+	soft_reset("cpsw", &priv->regs->soft_reset);
+	cpsw_ale_start(priv->ale);
+
+	/* switch to vlan unaware mode */
+	cpsw_ale_control_set(priv->ale, 0, ALE_VLAN_AWARE, 0);
+
+	/* setup host port priority mapping */
+	__raw_writel(CPDMA_TX_PRIORITY_MAP,
+		     &priv->host_port_regs->cpdma_tx_pri_map);
+	__raw_writel(0, &priv->host_port_regs->cpdma_rx_chan_map);
+
+	cpsw_ale_control_set(priv->ale, priv->host_port,
+			     ALE_PORT_STATE, ALE_PORT_STATE_FORWARD);
+
+	cpsw_ale_add_ucast(priv->ale, priv->mac_addr, priv->host_port, 0);
+	cpsw_ale_add_mcast(priv->ale, priv->ndev->broadcast,
+			   1 << priv->host_port, 0, ALE_MCAST_FWD_2);
+}
+
+static int cpsw_ndo_open(struct net_device *ndev)
+{
+	struct cpsw_priv *priv = netdev_priv(ndev);
+	int i, ret;
+	u32 reg;
+
+	cpsw_intr_disable(priv);
+	netif_carrier_off(ndev);
+
+	ret = clk_enable(priv->clk);
+	if (ret < 0) {
+		dev_err(priv->dev, "unable to turn on device clock\n");
+		return ret;
+	}
+
+	reg = __raw_readl(&priv->regs->id_ver);
+
+	msg(info, ifup, "initializing cpsw version %d.%d (%d)\n",
+	    CPSW_MAJOR_VERSION(reg), CPSW_MINOR_VERSION(reg),
+	    CPSW_RTL_VERSION(reg));
+
+	/* initialize host and slave ports */
+	cpsw_init_host_port(priv);
+	for_each_slave(priv, cpsw_slave_open, priv);
+
+	/* setup tx dma to fixed prio and zero offset */
+	cpdma_control_set(priv->dma, CPDMA_TX_PRIO_FIXED, 1);
+	cpdma_control_set(priv->dma, CPDMA_RX_BUFFER_OFFSET, 0);
+
+	/* disable priority elevation and enable statistics on all ports */
+	__raw_writel(0, &priv->regs->ptype);
+
+	/* enable statistics collection only on the host port */
+	__raw_writel(0x7, &priv->regs->stat_port_en);
+
+	if (WARN_ON(!priv->data.rx_descs))
+		priv->data.rx_descs = 128;
+
+	for (i = 0; i < priv->data.rx_descs; i++) {
+		struct sk_buff *skb;
+
+		ret = -ENOMEM;
+		skb = netdev_alloc_skb_ip_align(priv->ndev,
+						priv->rx_packet_max);
+		if (!skb)
+			break;
+		ret = cpdma_chan_submit(priv->rxch, skb, skb->data,
+					skb_tailroom(skb), GFP_KERNEL);
+		if (WARN_ON(ret < 0))
+			break;
+	}
+	/* continue even if we didn't manage to submit all receive descs */
+	msg(info, ifup, "submitted %d rx descriptors\n", i);
+
+	cpdma_ctlr_start(priv->dma);
+	cpsw_intr_enable(priv);
+	napi_enable(&priv->napi);
+	cpdma_ctlr_eoi(priv->dma);
+
+	return 0;
+}
+
+static void cpsw_slave_stop(struct cpsw_slave *slave, struct cpsw_priv *priv)
+{
+	if (!slave->phy)
+		return;
+	phy_stop(slave->phy);
+	phy_disconnect(slave->phy);
+	slave->phy = NULL;
+}
+
+static int cpsw_ndo_stop(struct net_device *ndev)
+{
+	struct cpsw_priv *priv = netdev_priv(ndev);
+
+	msg(info, ifdown, "shutting down cpsw device\n");
+	cpsw_intr_disable(priv);
+	cpdma_ctlr_int_ctrl(priv->dma, false);
+	cpdma_ctlr_stop(priv->dma);
+	netif_stop_queue(priv->ndev);
+	napi_disable(&priv->napi);
+	netif_carrier_off(priv->ndev);
+	cpsw_ale_stop(priv->ale);
+	for_each_slave(priv, cpsw_slave_stop, priv);
+	clk_disable(priv->clk);
+	return 0;
+}
+
+static netdev_tx_t cpsw_ndo_start_xmit(struct sk_buff *skb,
+				       struct net_device *ndev)
+{
+	struct cpsw_priv *priv = netdev_priv(ndev);
+	int ret;
+
+	ndev->trans_start = jiffies;
+
+	if (skb_padto(skb, CPSW_MIN_PACKET_SIZE)) {
+		msg(err, tx_err, "packet pad failed");
+		priv->stats.tx_dropped++;
+		return NETDEV_TX_OK;
+	}
+
+	ret = cpdma_chan_submit(priv->txch, skb, skb->data,
+				skb->len, GFP_KERNEL);
+	if (unlikely(ret != 0)) {
+		msg(err, tx_err, "desc submit failed");
+		goto fail;
+	}
+
+	return NETDEV_TX_OK;
+fail:
+	priv->stats.tx_dropped++;
+	netif_stop_queue(ndev);
+	return NETDEV_TX_BUSY;
+}
+
+static void cpsw_ndo_change_rx_flags(struct net_device *ndev, int flags)
+{
+	/*
+	 * The switch cannot operate in promiscuous mode without substantial
+	 * headache.  For promiscuous mode to work, we would need to put the
+	 * ALE in bypass mode and route all traffic to the host port.
+	 * Subsequently, the host will need to operate as a "bridge", learn,
+	 * and flood as needed.  For now, we simply complain here and
+	 * do nothing about it :-)
+	 */
+	if ((flags & IFF_PROMISC) && (ndev->flags & IFF_PROMISC))
+		dev_err(&ndev->dev, "promiscuity ignored!\n");
+
+	/*
+	 * The switch cannot filter multicast traffic unless it is configured
+	 * in "VLAN Aware" mode.  Unfortunately, VLAN awareness requires a
+	 * whole bunch of additional logic that this driver does not implement
+	 * at present.
+	 */
+	if ((flags & IFF_ALLMULTI) && !(ndev->flags & IFF_ALLMULTI))
+		dev_err(&ndev->dev, "multicast traffic cannot be filtered!\n");
+}
+
+static void cpsw_ndo_tx_timeout(struct net_device *ndev)
+{
+	struct cpsw_priv *priv = netdev_priv(ndev);
+
+	msg(err, tx_err, "transmit timeout, restarting dma");
+	priv->stats.tx_errors++;
+	cpsw_intr_disable(priv);
+	cpdma_ctlr_int_ctrl(priv->dma, false);
+	cpdma_chan_stop(priv->txch);
+	cpdma_chan_start(priv->txch);
+	cpdma_ctlr_int_ctrl(priv->dma, true);
+	cpsw_intr_enable(priv);
+	cpdma_ctlr_eoi(priv->dma);
+}
+
+static struct net_device_stats *cpsw_ndo_get_stats(struct net_device *ndev)
+{
+	struct cpsw_priv *priv = netdev_priv(ndev);
+	return &priv->stats;
+}
+
+#ifdef CONFIG_NET_POLL_CONTROLLER
+static void cpsw_ndo_poll_controller(struct net_device *ndev)
+{
+	struct cpsw_priv *priv = netdev_priv(ndev);
+
+	cpsw_intr_disable(priv);
+	cpdma_ctlr_int_ctrl(priv->dma, false);
+	cpsw_interrupt(ndev->irq, priv);
+	cpdma_ctlr_int_ctrl(priv->dma, true);
+	cpsw_intr_enable(priv);
+	cpdma_ctlr_eoi(priv->dma);
+}
+#endif
+
+static const struct net_device_ops cpsw_netdev_ops = {
+	.ndo_open		= cpsw_ndo_open,
+	.ndo_stop		= cpsw_ndo_stop,
+	.ndo_start_xmit		= cpsw_ndo_start_xmit,
+	.ndo_change_rx_flags	= cpsw_ndo_change_rx_flags,
+	.ndo_validate_addr	= eth_validate_addr,
+	.ndo_tx_timeout		= cpsw_ndo_tx_timeout,
+	.ndo_get_stats		= cpsw_ndo_get_stats,
+#ifdef CONFIG_NET_POLL_CONTROLLER
+	.ndo_poll_controller	= cpsw_ndo_poll_controller,
+#endif
+};
+
+static void cpsw_get_drvinfo(struct net_device *ndev,
+			     struct ethtool_drvinfo *info)
+{
+	struct cpsw_priv *priv = netdev_priv(ndev);
+	strcpy(info->driver, "TI CPSW Driver v1.0");
+	strcpy(info->version, "1.0");
+	strcpy(info->bus_info, priv->pdev->name);
+}
+
+static u32 cpsw_get_msglevel(struct net_device *ndev)
+{
+	struct cpsw_priv *priv = netdev_priv(ndev);
+	return priv->msg_enable;
+}
+
+static void cpsw_set_msglevel(struct net_device *ndev, u32 value)
+{
+	struct cpsw_priv *priv = netdev_priv(ndev);
+	priv->msg_enable = value;
+}
+
+static const struct ethtool_ops cpsw_ethtool_ops = {
+	.get_drvinfo	= cpsw_get_drvinfo,
+	.get_msglevel	= cpsw_get_msglevel,
+	.set_msglevel	= cpsw_set_msglevel,
+	.get_link	= ethtool_op_get_link,
+};
+
+static void cpsw_slave_init(struct cpsw_slave *slave, struct cpsw_priv *priv)
+{
+	void __iomem		*regs = priv->regs;
+	int			slave_num = slave->slave_num;
+	struct cpsw_slave_data	*data = priv->data.slave_data + slave_num;
+
+	slave->data	= data;
+	slave->regs	= regs + data->slave_reg_ofs;
+	slave->sliver	= regs + data->sliver_reg_ofs;
+}
+
+static int __devinit cpsw_probe(struct platform_device *pdev)
+{
+	struct cpsw_platform_data	*data = pdev->dev.platform_data;
+	struct net_device		*ndev;
+	struct cpsw_priv		*priv;
+	struct cpdma_params		dma_params;
+	struct cpsw_ale_params		ale_params;
+	void __iomem			*regs;
+	struct resource			*res;
+	int ret = 0, i, k = 0;
+
+	if (!data) {
+		pr_err("cpsw: platform data missing\n");
+		return -ENODEV;
+	}
+
+	ndev = alloc_etherdev(sizeof(struct cpsw_priv));
+	if (!ndev) {
+		pr_err("cpsw: error allocating net_device\n");
+		return -ENOMEM;
+	}
+
+	platform_set_drvdata(pdev, ndev);
+	priv = netdev_priv(ndev);
+	spin_lock_init(&priv->lock);
+	priv->data = *data;
+	priv->pdev = pdev;
+	priv->ndev = ndev;
+	priv->dev  = &ndev->dev;
+	priv->msg_enable = netif_msg_init(debug_level, CPSW_DEBUG);
+	priv->rx_packet_max = max(rx_packet_max, 128);
+
+	if (is_valid_ether_addr(data->slave_data[0].mac_addr)) {
+		memcpy(priv->mac_addr, data->slave_data[0].mac_addr, ETH_ALEN);
+		printk(KERN_INFO"Detected MACID=%x:%x:%x:%x:%x:%x\n",
+			priv->mac_addr[0], priv->mac_addr[1],
+			priv->mac_addr[2], priv->mac_addr[3],
+			priv->mac_addr[4], priv->mac_addr[5]);
+	} else {
+		random_ether_addr(priv->mac_addr);
+		printk(KERN_INFO"Random MACID=%x:%x:%x:%x:%x:%x\n",
+			priv->mac_addr[0], priv->mac_addr[1],
+			priv->mac_addr[2], priv->mac_addr[3],
+			priv->mac_addr[4], priv->mac_addr[5]);
+	}
+
+	memcpy(ndev->dev_addr, priv->mac_addr, ETH_ALEN);
+
+	priv->slaves = kzalloc(sizeof(struct cpsw_slave) * data->slaves,
+			       GFP_KERNEL);
+	if (!priv->slaves) {
+		dev_err(priv->dev, "failed to allocate slave ports\n");
+		ret = -EBUSY;
+		goto clean_ndev_ret;
+	}
+	for (i = 0; i < data->slaves; i++)
+		priv->slaves[i].slave_num = i;
+
+	priv->clk = clk_get(&pdev->dev, NULL);
+	if (IS_ERR(priv->clk)) {
+		dev_err(priv->dev, "failed to get device clock\n");
+		ret = -EBUSY;
+	}
+
+	priv->cpsw_res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	if (!priv->cpsw_res) {
+		dev_err(priv->dev, "error getting i/o resource\n");
+		ret = -ENOENT;
+		goto clean_clk_ret;
+	}
+
+	if (!request_mem_region(priv->cpsw_res->start,
+				resource_size(priv->cpsw_res), ndev->name)) {
+		dev_err(priv->dev, "failed request i/o region\n");
+		ret = -ENXIO;
+		goto clean_clk_ret;
+	}
+
+	regs = ioremap(priv->cpsw_res->start, resource_size(priv->cpsw_res));
+	if (!regs) {
+		dev_err(priv->dev, "unable to map i/o region\n");
+		goto clean_cpsw_iores_ret;
+	}
+	priv->regs = regs;
+	priv->host_port = data->host_port_num;
+	priv->host_port_regs = regs + data->host_port_reg_ofs;
+	priv->hw_stats = regs + data->hw_stats_reg_ofs;
+
+	priv->cpsw_ss_res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
+	if (!priv->cpsw_ss_res) {
+		dev_err(priv->dev, "error getting i/o resource\n");
+		ret = -ENOENT;
+		goto clean_clk_ret;
+	}
+
+	if (!request_mem_region(priv->cpsw_ss_res->start,
+			resource_size(priv->cpsw_ss_res), ndev->name)) {
+		dev_err(priv->dev, "failed request i/o region\n");
+		ret = -ENXIO;
+		goto clean_clk_ret;
+	}
+
+	regs = ioremap(priv->cpsw_ss_res->start,
+				resource_size(priv->cpsw_ss_res));
+	if (!regs) {
+		dev_err(priv->dev, "unable to map i/o region\n");
+		goto clean_cpsw_ss_iores_ret;
+	}
+	priv->ss_regs = regs;
+
+	for_each_slave(priv, cpsw_slave_init, priv);
+
+	memset(&dma_params, 0, sizeof(dma_params));
+	dma_params.dev			= &pdev->dev;
+	dma_params.dmaregs		= (void __iomem *)(((u32)priv->regs) +
+						data->cpdma_reg_ofs);
+	dma_params.rxthresh		= (void __iomem *)(((u32)priv->regs) +
+					data->cpdma_reg_ofs + CPDMA_RXTHRESH);
+	dma_params.rxfree		= (void __iomem *)(((u32)priv->regs) +
+					data->cpdma_reg_ofs + CPDMA_RXFREE);
+
+	dma_params.txhdp	= (void __iomem *)(((u32)priv->regs) +
+				data->cpdma_sram_ofs + CPDMA_TXHDP);
+	dma_params.rxhdp	= (void __iomem *)(((u32)priv->regs) +
+				data->cpdma_sram_ofs + CPDMA_RXHDP);
+	dma_params.txcp		= (void __iomem *)(((u32)priv->regs) +
+				data->cpdma_sram_ofs + CPDMA_TXCP);
+	dma_params.rxcp		= (void __iomem *)(((u32)priv->regs) +
+				data->cpdma_sram_ofs + CPDMA_RXCP);
+
+	dma_params.num_chan		= data->channels;
+	dma_params.has_soft_reset	= true;
+	dma_params.min_packet_size	= CPSW_MIN_PACKET_SIZE;
+	dma_params.desc_mem_size	= data->bd_ram_size;
+	dma_params.desc_align		= 16;
+	dma_params.has_ext_regs		= true;
+	dma_params.desc_mem_phys        = data->no_bd_ram ? 0 :
+			(u32 __force)priv->cpsw_res->start + data->bd_ram_ofs;
+	dma_params.desc_hw_addr         = data->hw_ram_addr ?
+			data->hw_ram_addr : dma_params.desc_mem_phys ;
+
+	priv->dma = cpdma_ctlr_create(&dma_params);
+	if (!priv->dma) {
+		dev_err(priv->dev, "error initializing dma\n");
+		ret = -ENOMEM;
+		goto clean_iomap_ret;
+	}
+
+	priv->txch = cpdma_chan_create(priv->dma, tx_chan_num(0),
+				       cpsw_tx_handler);
+	priv->rxch = cpdma_chan_create(priv->dma, rx_chan_num(0),
+				       cpsw_rx_handler);
+
+	if (WARN_ON(!priv->txch || !priv->rxch)) {
+		dev_err(priv->dev, "error initializing dma channels\n");
+		ret = -ENOMEM;
+		goto clean_dma_ret;
+	}
+
+	memset(&ale_params, 0, sizeof(ale_params));
+	ale_params.dev			= &ndev->dev;
+	ale_params.ale_regs		= (void *)((u32)priv->regs) +
+						((u32)data->ale_reg_ofs);
+	ale_params.ale_ageout		= ale_ageout;
+	ale_params.ale_entries		= data->ale_entries;
+	ale_params.ale_ports		= data->slaves;
+
+	priv->ale = cpsw_ale_create(&ale_params);
+	if (!priv->ale) {
+		dev_err(priv->dev, "error initializing ale engine\n");
+		ret = -ENODEV;
+		goto clean_dma_ret;
+	}
+
+	ndev->irq = platform_get_irq(pdev, 0);
+	if (ndev->irq < 0) {
+		dev_err(priv->dev, "error getting irq resource\n");
+		ret = -ENOENT;
+		goto clean_ale_ret;
+	}
+
+	while ((res = platform_get_resource(priv->pdev, IORESOURCE_IRQ, k))) {
+		for (i = res->start; i <= res->end; i++) {
+			if (request_irq(i, cpsw_interrupt, IRQF_DISABLED,
+					dev_name(&pdev->dev), priv)) {
+				dev_err(priv->dev, "error attaching irq\n");
+				goto clean_ale_ret;
+			}
+			priv->irqs_table[k] = i;
+			priv->num_irqs = k;
+		}
+		k++;
+	}
+
+	ndev->flags |= IFF_ALLMULTI;	/* see cpsw_ndo_change_rx_flags() */
+
+	ndev->netdev_ops = &cpsw_netdev_ops;
+	SET_ETHTOOL_OPS(ndev, &cpsw_ethtool_ops);
+	netif_napi_add(ndev, &priv->napi, cpsw_poll, CPSW_POLL_WEIGHT);
+
+	/* register the network device */
+	SET_NETDEV_DEV(ndev, &pdev->dev);
+	ret = register_netdev(ndev);
+	if (ret) {
+		dev_err(priv->dev, "error registering net device\n");
+		ret = -ENODEV;
+		goto clean_irq_ret;
+	}
+
+	msg(notice, probe, "initialized device (regs %x, irq %d)\n",
+	    priv->cpsw_res->start, ndev->irq);
+
+	return 0;
+
+clean_irq_ret:
+	free_irq(ndev->irq, priv);
+clean_ale_ret:
+	cpsw_ale_destroy(priv->ale);
+clean_dma_ret:
+	cpdma_chan_destroy(priv->txch);
+	cpdma_chan_destroy(priv->rxch);
+	cpdma_ctlr_destroy(priv->dma);
+clean_iomap_ret:
+	iounmap(priv->regs);
+clean_cpsw_ss_iores_ret:
+	release_mem_region(priv->cpsw_ss_res->start,
+			   resource_size(priv->cpsw_ss_res));
+clean_cpsw_iores_ret:
+	release_mem_region(priv->cpsw_res->start,
+			   resource_size(priv->cpsw_res));
+clean_clk_ret:
+	clk_put(priv->clk);
+	kfree(priv->slaves);
+clean_ndev_ret:
+	free_netdev(ndev);
+	return ret;
+}
+
+static int __devexit cpsw_remove(struct platform_device *pdev)
+{
+	struct net_device *ndev = platform_get_drvdata(pdev);
+	struct cpsw_priv *priv = netdev_priv(ndev);
+
+	msg(notice, probe, "removing device\n");
+	platform_set_drvdata(pdev, NULL);
+
+	free_irq(ndev->irq, priv);
+	cpsw_ale_destroy(priv->ale);
+	cpdma_chan_destroy(priv->txch);
+	cpdma_chan_destroy(priv->rxch);
+	cpdma_ctlr_destroy(priv->dma);
+	iounmap(priv->regs);
+	release_mem_region(priv->cpsw_res->start,
+			   resource_size(priv->cpsw_res));
+	release_mem_region(priv->cpsw_ss_res->start,
+			   resource_size(priv->cpsw_ss_res));
+	clk_put(priv->clk);
+	kfree(priv->slaves);
+	free_netdev(ndev);
+
+	return 0;
+}
+
+static int cpsw_suspend(struct device *dev)
+{
+	struct platform_device	*pdev = to_platform_device(dev);
+	struct net_device	*ndev = platform_get_drvdata(pdev);
+
+	if (netif_running(ndev))
+		cpsw_ndo_stop(ndev);
+	return 0;
+}
+
+static int cpsw_resume(struct device *dev)
+{
+	struct platform_device	*pdev = to_platform_device(dev);
+	struct net_device	*ndev = platform_get_drvdata(pdev);
+
+	if (netif_running(ndev))
+		cpsw_ndo_open(ndev);
+	return 0;
+}
+
+static const struct dev_pm_ops cpsw_pm_ops = {
+	.suspend	= cpsw_suspend,
+	.resume		= cpsw_resume,
+};
+
+static struct platform_driver cpsw_driver = {
+	.driver = {
+		.name	 = "cpsw",
+		.owner	 = THIS_MODULE,
+		.pm	 = &cpsw_pm_ops,
+	},
+	.probe = cpsw_probe,
+	.remove = __devexit_p(cpsw_remove),
+};
+
+static int __init cpsw_init(void)
+{
+	return platform_driver_register(&cpsw_driver);
+}
+late_initcall(cpsw_init);
+
+static void __exit cpsw_exit(void)
+{
+	platform_driver_unregister(&cpsw_driver);
+}
+module_exit(cpsw_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("TI CPSW Ethernet driver");
diff --git a/include/linux/platform_data/cpsw.h b/include/linux/platform_data/cpsw.h
new file mode 100644
index 0000000..c4e23d0
--- /dev/null
+++ b/include/linux/platform_data/cpsw.h
@@ -0,0 +1,55 @@
+/*
+ * Texas Instruments Ethernet Switch Driver
+ *
+ * Copyright (C) 2012 Texas Instruments
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation version 2.
+ *
+ * This program is distributed "as is" WITHOUT ANY WARRANTY of any
+ * kind, whether express or implied; without even the implied warranty
+ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+#ifndef __CPSW_H__
+#define __CPSW_H__
+
+#include <linux/if_ether.h>
+
+struct cpsw_slave_data {
+	u32		slave_reg_ofs;
+	u32		sliver_reg_ofs;
+	const char	*phy_id;
+	int		phy_if;
+	u8		mac_addr[ETH_ALEN];
+};
+
+struct cpsw_platform_data {
+	u32	ss_reg_ofs;	/* Subsystem control register offset */
+	u32	channels;	/* number of cpdma channels (symmetric) */
+	u32	cpdma_reg_ofs;	/* cpdma register offset */
+	u32	cpdma_sram_ofs;	/* cpdma sram offset */
+
+	u32	slaves;		/* number of slave cpgmac ports */
+	struct cpsw_slave_data	*slave_data;
+
+	u32	ale_reg_ofs;	/* address lookup engine reg offset */
+	u32	ale_entries;	/* ale table size */
+
+	u32	host_port_reg_ofs; /* cpsw cpdma host port registers */
+	u32     host_port_num; /* The port number for the host port */
+
+	u32	hw_stats_reg_ofs;  /* cpsw hardware statistics counters */
+
+	u32	bd_ram_ofs;   /* embedded buffer descriptor RAM offset*/
+	u32	bd_ram_size;  /*buffer descriptor ram size */
+	u32	hw_ram_addr; /*if the HW address for BD RAM is different */
+	bool	no_bd_ram; /* no embedded BD ram*/
+
+	u32	rx_descs;	/* Number of Rx Descriptios */
+
+	u32	mac_control;	/* Mac control register */
+};
+
+#endif /* __CPSW_H__ */
-- 
1.7.0.4

Re: [PATCH v2 1/2] netdev: driver: ethernet: add cpsw address lookup engine support

From: Joe Perches <joe@perches.com>
Date: 2012-02-20 22:37:34

On Tue, 2012-02-21 at 00:07 +0530, Mugunthan V N wrote:
TI CPSW ethernet switch has a built-in address lookup engine.  This patch adds
the code necessary for programming this module.
just some style notes.
quoted hunk
diff --git a/drivers/net/ethernet/ti/cpsw_ale.c b/drivers/net/ethernet/ti/cpsw_ale.c
[]
+struct ale_control_info {
+	const char	*name;
+	int		offset, port_offset;
+	int		shift, port_shift;
+	int		bits;
+};
+
+#define CTRL_GLOBAL(name, bit)		{#name, ALE_CONTROL, 0, bit, 0, 1}
+#define CTRL_UNK(name, shift, bits)	{#name, ALE_UNKNOWNVLAN, 0, shift, \
+						0, bits}
+#define CTRL_PORTCTL(name, start, bits)	{#name, ALE_PORTCTL, 4, start, 0, bits}
named initializers might be more readable,
embedding the index might be too.
[]
+struct cpsw_ale *cpsw_ale_create(struct cpsw_ale_params *params)
+{
+	struct cpsw_ale *ale;
+	int ret;
+
+	ret = -ENOMEM;
+	ale = kzalloc(sizeof(*ale), GFP_KERNEL);
+	if (WARN_ON(!ale))
+		return NULL;
The WARN_ON is unnecessary.  alloc failures get a dump_stack.
quoted hunk
diff --git a/drivers/net/ethernet/ti/cpsw_ale.h b/drivers/net/ethernet/ti/cpsw_ale.h
[]
+struct cpsw_ale {
+	struct cpsw_ale_params	params;
+	struct timer_list	timer;
+	unsigned long		ageout;
+	struct device_attribute ale_control_attr;
+#define control_attr_to_ale(attr)	\
+	container_of(attr, struct cpsw_ale, ale_control_attr);
+	struct device_attribute ale_table_attr;
+#define table_attr_to_ale(attr)		\
+	container_of(attr, struct cpsw_ale, ale_table_attr);
+};
Embedding the #defines in a struct makes it difficult to read.
Perhaps this is simpler:

struct cpsw_ale {
	struct cpsw_ale_params	params;
	struct timer_list	timer;
	unsigned long		ageout;
	struct device_attribute ale_control_attr;
	struct device_attribute ale_table_attr;
};

#define control_attr_to_ale(attr)	\
	container_of(attr, struct cpsw_ale, ale_control_attr);
#define table_attr_to_ale(attr)		\
	container_of(attr, struct cpsw_ale, ale_table_attr);

Re: [PATCH v2 2/2] netdev: driver: ethernet: Add TI CPSW driver

From: Joe Perches <joe@perches.com>
Date: 2012-02-20 23:03:50

On Tue, 2012-02-21 at 00:07 +0530, Mugunthan V N wrote:
This patch adds support for TI's CPSW driver.

The three port switch gigabit ethernet subsystem provides ethernet packet
communication and can be configured as an ethernet switch. Supports
10/100/1000 Mbps.
some more notes.
quoted hunk
diff --git a/drivers/net/ethernet/ti/cpsw.c b/drivers/net/ethernet/ti/cpsw.c
[]
+#define msg(level, type, format, ...)				\
+do {								\
+	if (netif_msg_##type(priv) && net_ratelimit())		\
+		dev_##level(priv->dev, format, ## __VA_ARGS__);	\
+} while (0)
This hides an always used priv variable.

I suggest you expand this to multiple #defines like:

#define cpsw_info(priv, type, format, ...)			\
do {								\
	if (netif_msg_##type(priv) && net_ratelimit())		\
		dev_info(priv->dev, format, ##__VA_ARGS__);	\
} while (0)

and change the uses from
	msg(info,...)
to
	cpsw_info(priv, type, format, ...)

[]
+struct cpsw_priv {
+	spinlock_t			lock;
+	struct platform_device		*pdev;
+	struct net_device		*ndev;
+	struct resource			*cpsw_res;
+	struct resource			*cpsw_ss_res;
+	struct napi_struct		napi;
+#define napi_to_priv(napi)	container_of(napi, struct cpsw_priv, napi)
I still think embedded #defines in structs are hard to read.

[]
+static int cpsw_poll(struct napi_struct *napi, int budget)
+{
+	struct cpsw_priv	*priv = napi_to_priv(napi);
+	int			num_tx, num_rx;
+
+	num_tx = cpdma_chan_process(priv->txch, 128);
+	num_rx = cpdma_chan_process(priv->rxch, budget);
+
+	if (num_rx || num_tx)
+		msg(dbg, intr, "poll %d rx, %d tx pkts\n", num_rx, num_tx);
		cpsw_dbg(priv, intr, etc...)
[]
+		*link = true;
+	} else {
+		mac_control = 0;
+		/* diable forwarding */
disable tyop
+	if (IS_ERR(slave->phy)) {
+		msg(err, ifup, "phy %s not found on slave %d\n",
+		    slave->data->phy_id, slave->slave_num);
+		slave->phy = NULL;
+	} else {
+		printk(KERN_ERR "\nCPSW phy found : id is : 0x%x\n",
+			slave->phy->phy_id);
Using leading newlines doesn't help much.
I suggest dev_err.
[]
+	ndev = alloc_etherdev(sizeof(struct cpsw_priv));
+	if (!ndev) {
+		pr_err("cpsw: error allocating net_device\n");
Add
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
before all #includes and remove the embedded prefix
from all pr_<level> uses.
+	if (is_valid_ether_addr(data->slave_data[0].mac_addr)) {
+		memcpy(priv->mac_addr, data->slave_data[0].mac_addr, ETH_ALEN);
+		printk(KERN_INFO"Detected MACID=%x:%x:%x:%x:%x:%x\n",
+			priv->mac_addr[0], priv->mac_addr[1],
+			priv->mac_addr[2], priv->mac_addr[3],
+			priv->mac_addr[4], priv->mac_addr[5]);
Use printf extension %pM to print the MAC address

		pr_info("Detected MAC=%pM\n", priv->mac_addr);
+	priv->slaves = kzalloc(sizeof(struct cpsw_slave) * data->slaves,
+			       GFP_KERNEL);
+	if (!priv->slaves) {
+		dev_err(priv->dev, "failed to allocate slave ports\n");
No real need for this dev_err
[]
+	memset(&dma_params, 0, sizeof(dma_params));
+	dma_params.dev			= &pdev->dev;
+	dma_params.dmaregs		= (void __iomem *)(((u32)priv->regs) +
+						data->cpdma_reg_ofs);
+	dma_params.rxthresh		= (void __iomem *)(((u32)priv->regs) +
+					data->cpdma_reg_ofs + CPDMA_RXTHRESH);
+	dma_params.rxfree		= (void __iomem *)(((u32)priv->regs) +
+					data->cpdma_reg_ofs + CPDMA_RXFREE);
+
+	dma_params.txhdp	= (void __iomem *)(((u32)priv->regs) +
+				data->cpdma_sram_ofs + CPDMA_TXHDP);
+	dma_params.rxhdp	= (void __iomem *)(((u32)priv->regs) +
+				data->cpdma_sram_ofs + CPDMA_RXHDP);
+	dma_params.txcp		= (void __iomem *)(((u32)priv->regs) +
+				data->cpdma_sram_ofs + CPDMA_TXCP);
+	dma_params.rxcp		= (void __iomem *)(((u32)priv->regs) +
+				data->cpdma_sram_ofs + CPDMA_RXCP);
These might be more readable if you didn't try to align it
or maybe add macros or inline functions like:

#define cpsw_sram_addr(priv, data, offset)			\
	(void __iomem *)(((u32)(priv)->regs) + (data)->cpdma_sram_ofs + offset)

RE: [PATCH v2 1/2] netdev: driver: ethernet: add cpsw address lookup engine support

From: N, Mugunthan V <hidden>
Date: 2012-02-22 16:43:17

Joe
-----Original Message-----
From: Joe Perches [mailto:joe@perches.com]
Sent: Tuesday, February 21, 2012 4:08 AM
To: N, Mugunthan V
Cc: netdev@vger.kernel.org; davem@davemloft.net
Subject: Re: [PATCH v2 1/2] netdev: driver: ethernet: add cpsw address
lookup engine support

On Tue, 2012-02-21 at 00:07 +0530, Mugunthan V N wrote:
quoted
TI CPSW ethernet switch has a built-in address lookup engine.  This
patch adds
quoted
the code necessary for programming this module.
just some style notes.
quoted
diff --git a/drivers/net/ethernet/ti/cpsw_ale.c
b/drivers/net/ethernet/ti/cpsw_ale.c
[]
quoted
+struct ale_control_info {
+	const char	*name;
+	int		offset, port_offset;
+	int		shift, port_shift;
+	int		bits;
+};
+
+#define CTRL_GLOBAL(name, bit)		{#name, ALE_CONTROL, 0, bit, 0,
1}
quoted
+#define CTRL_UNK(name, shift, bits)	{#name, ALE_UNKNOWNVLAN, 0,
shift, \
quoted
+						0, bits}
+#define CTRL_PORTCTL(name, start, bits)	{#name, ALE_PORTCTL, 4,
start, 0, bits}

named initializers might be more readable,
embedding the index might be too.
Agreed, will used named initializers also with embedding the index.
[]
quoted
+struct cpsw_ale *cpsw_ale_create(struct cpsw_ale_params *params)
+{
+	struct cpsw_ale *ale;
+	int ret;
+
+	ret = -ENOMEM;
+	ale = kzalloc(sizeof(*ale), GFP_KERNEL);
+	if (WARN_ON(!ale))
+		return NULL;
The WARN_ON is unnecessary.  alloc failures get a dump_stack.
You mean kzalloc will provide a dump stack during allocation failure, if so
I will remove the WARN_ON
quoted
diff --git a/drivers/net/ethernet/ti/cpsw_ale.h
b/drivers/net/ethernet/ti/cpsw_ale.h
[]
quoted
+struct cpsw_ale {
+	struct cpsw_ale_params	params;
+	struct timer_list	timer;
+	unsigned long		ageout;
+	struct device_attribute ale_control_attr;
+#define control_attr_to_ale(attr)	\
+	container_of(attr, struct cpsw_ale, ale_control_attr);
+	struct device_attribute ale_table_attr;
+#define table_attr_to_ale(attr)		\
+	container_of(attr, struct cpsw_ale, ale_table_attr);
+};
Embedding the #defines in a struct makes it difficult to read.
Perhaps this is simpler:

struct cpsw_ale {
	struct cpsw_ale_params	params;
	struct timer_list	timer;
	unsigned long		ageout;
	struct device_attribute ale_control_attr;
	struct device_attribute ale_table_attr;
};

#define control_attr_to_ale(attr)	\
	container_of(attr, struct cpsw_ale, ale_control_attr);
#define table_attr_to_ale(attr)		\
	container_of(attr, struct cpsw_ale, ale_table_attr);
Yes, I will move the define outside struct definition in next set of
patch set.
With regards
Mugunthan V N

RE: [PATCH v2 2/2] netdev: driver: ethernet: Add TI CPSW driver

From: N, Mugunthan V <hidden>
Date: 2012-02-22 16:43:30

Joe
-----Original Message-----
From: Joe Perches [mailto:joe@perches.com]
Sent: Tuesday, February 21, 2012 4:34 AM
To: N, Mugunthan V
Cc: netdev@vger.kernel.org; davem@davemloft.net
Subject: Re: [PATCH v2 2/2] netdev: driver: ethernet: Add TI CPSW driver

On Tue, 2012-02-21 at 00:07 +0530, Mugunthan V N wrote:
quoted
This patch adds support for TI's CPSW driver.

The three port switch gigabit ethernet subsystem provides ethernet
packet
quoted
communication and can be configured as an ethernet switch. Supports
10/100/1000 Mbps.
some more notes.
quoted
diff --git a/drivers/net/ethernet/ti/cpsw.c
b/drivers/net/ethernet/ti/cpsw.c
[]
quoted
+#define msg(level, type, format, ...)				\
+do {								\
+	if (netif_msg_##type(priv) && net_ratelimit())		\
+		dev_##level(priv->dev, format, ## __VA_ARGS__);	\
+} while (0)
This hides an always used priv variable.

I suggest you expand this to multiple #defines like:

#define cpsw_info(priv, type, format, ...)			\
do {								\
	if (netif_msg_##type(priv) && net_ratelimit())		\
		dev_info(priv->dev, format, ##__VA_ARGS__);	\
} while (0)

and change the uses from
	msg(info,...)
to
	cpsw_info(priv, type, format, ...)
Agreed, will change the define.
[]
quoted
+struct cpsw_priv {
+	spinlock_t			lock;
+	struct platform_device		*pdev;
+	struct net_device		*ndev;
+	struct resource			*cpsw_res;
+	struct resource			*cpsw_ss_res;
+	struct napi_struct		napi;
+#define napi_to_priv(napi)	container_of(napi, struct cpsw_priv, napi)
I still think embedded #defines in structs are hard to read.
Agreed, will move defines outside the struct definition.
[]
quoted
+static int cpsw_poll(struct napi_struct *napi, int budget)
+{
+	struct cpsw_priv	*priv = napi_to_priv(napi);
+	int			num_tx, num_rx;
+
+	num_tx = cpdma_chan_process(priv->txch, 128);
+	num_rx = cpdma_chan_process(priv->rxch, budget);
+
+	if (num_rx || num_tx)
+		msg(dbg, intr, "poll %d rx, %d tx pkts\n", num_rx, num_tx);
		cpsw_dbg(priv, intr, etc...)
Will change this debug.
[]
quoted
+		*link = true;
+	} else {
+		mac_control = 0;
+		/* diable forwarding */
disable tyop
quoted
+	if (IS_ERR(slave->phy)) {
+		msg(err, ifup, "phy %s not found on slave %d\n",
+		    slave->data->phy_id, slave->slave_num);
+		slave->phy = NULL;
+	} else {
+		printk(KERN_ERR "\nCPSW phy found : id is : 0x%x\n",
+			slave->phy->phy_id);
Using leading newlines doesn't help much.
I suggest dev_err.
Agreed, will change this to dev_err
[]
quoted
+	ndev = alloc_etherdev(sizeof(struct cpsw_priv));
+	if (!ndev) {
+		pr_err("cpsw: error allocating net_device\n");
Add
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
before all #includes and remove the embedded prefix
from all pr_<level> uses.
Agreed, will make use of pr_fmt and remove embedded prefix in pr_<level>
uses
quoted
+	if (is_valid_ether_addr(data->slave_data[0].mac_addr)) {
+		memcpy(priv->mac_addr, data->slave_data[0].mac_addr,
ETH_ALEN);
quoted
+		printk(KERN_INFO"Detected MACID=%x:%x:%x:%x:%x:%x\n",
+			priv->mac_addr[0], priv->mac_addr[1],
+			priv->mac_addr[2], priv->mac_addr[3],
+			priv->mac_addr[4], priv->mac_addr[5]);
Use printf extension %pM to print the MAC address

		pr_info("Detected MAC=%pM\n", priv->mac_addr);
Will use %pM to MAC address
quoted
+	priv->slaves = kzalloc(sizeof(struct cpsw_slave) * data->slaves,
+			       GFP_KERNEL);
+	if (!priv->slaves) {
+		dev_err(priv->dev, "failed to allocate slave ports\n");
No real need for this dev_err
Agreed, will remove this dev_err
[]
quoted
+	memset(&dma_params, 0, sizeof(dma_params));
+	dma_params.dev			= &pdev->dev;
+	dma_params.dmaregs		= (void __iomem *)(((u32)priv->regs) +
+						data->cpdma_reg_ofs);
+	dma_params.rxthresh		= (void __iomem *)(((u32)priv->regs) +
+					data->cpdma_reg_ofs + CPDMA_RXTHRESH);
+	dma_params.rxfree		= (void __iomem *)(((u32)priv->regs) +
+					data->cpdma_reg_ofs + CPDMA_RXFREE);
+
+	dma_params.txhdp	= (void __iomem *)(((u32)priv->regs) +
+				data->cpdma_sram_ofs + CPDMA_TXHDP);
+	dma_params.rxhdp	= (void __iomem *)(((u32)priv->regs) +
+				data->cpdma_sram_ofs + CPDMA_RXHDP);
+	dma_params.txcp		= (void __iomem *)(((u32)priv->regs) +
+				data->cpdma_sram_ofs + CPDMA_TXCP);
+	dma_params.rxcp		= (void __iomem *)(((u32)priv->regs) +
+				data->cpdma_sram_ofs + CPDMA_RXCP);
These might be more readable if you didn't try to align it
or maybe add macros or inline functions like:

#define cpsw_sram_addr(priv, data, offset)			\
	(void __iomem *)(((u32)(priv)->regs) + (data)->cpdma_sram_ofs +
offset)
Agreed, will make it as macros

With regards
Mugunthan V N 

RE: [PATCH v2 2/2] netdev: driver: ethernet: Add TI CPSW driver

From: N, Mugunthan V <hidden>
Date: 2012-02-23 12:48:04

Joe/David
-----Original Message-----
From: N, Mugunthan V
Sent: Wednesday, February 22, 2012 10:13 PM
To: 'Joe Perches'
Cc: netdev@vger.kernel.org; davem@davemloft.net
Subject: RE: [PATCH v2 2/2] netdev: driver: ethernet: Add TI CPSW driver

Joe
quoted
-----Original Message-----
From: Joe Perches [mailto:joe@perches.com]
Sent: Tuesday, February 21, 2012 4:34 AM
[...]
quoted
Add
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
before all #includes and remove the embedded prefix
from all pr_<level> uses.
Agreed, will make use of pr_fmt and remove embedded prefix in pr_<level>
uses
Adding 
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
in the driver gives a checkpatch error as follows

#77: FILE: drivers/net/ethernet/ti/cpsw.c:16:
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt

total: 1 errors, 0 warnings, 1137 lines checked

Is it ok to ignore the checkpatch error as most of the drivers use it.

[...] 

With regards
Mugunthan V N

RE: [PATCH v2 2/2] netdev: driver: ethernet: Add TI CPSW driver

From: Joe Perches <joe@perches.com>
Date: 2012-02-23 15:46:05

On Thu, 2012-02-23 at 12:47 +0000, N, Mugunthan V wrote:
Joe/David
quoted
quoted
-----Original Message-----
From: Joe Perches [mailto:joe@perches.com]
Sent: Tuesday, February 21, 2012 4:34 AM
[...]
quoted
quoted
Add
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
before all #includes and remove the embedded prefix
from all pr_<level> uses.
Agreed, will make use of pr_fmt and remove embedded prefix in pr_<level>
uses
Adding 
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
in the driver gives a checkpatch error as follows
#77: FILE: drivers/net/ethernet/ti/cpsw.c:16:
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
total: 1 errors, 0 warnings, 1137 lines checked
Is it ok to ignore the checkpatch error as most of the drivers use it.
Yes.

Andy Whitcroft has a pending checkpatch fix
for this.  I hope he pushes it out soon.

Re: [PATCH v2 2/2] netdev: driver: ethernet: Add TI CPSW driver

From: Andy Whitcroft <hidden>
Date: 2012-02-23 17:12:56

On Thu, Feb 23, 2012 at 07:46:02AM -0800, Joe Perches wrote:
On Thu, 2012-02-23 at 12:47 +0000, N, Mugunthan V wrote:
quoted
Joe/David
quoted
quoted
-----Original Message-----
From: Joe Perches [mailto:joe@perches.com]
Sent: Tuesday, February 21, 2012 4:34 AM
[...]
quoted
quoted
Add
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
before all #includes and remove the embedded prefix
from all pr_<level> uses.
Agreed, will make use of pr_fmt and remove embedded prefix in pr_<level>
uses
Adding 
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
in the driver gives a checkpatch error as follows
#77: FILE: drivers/net/ethernet/ti/cpsw.c:16:
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
total: 1 errors, 0 warnings, 1137 lines checked
Is it ok to ignore the checkpatch error as most of the drivers use it.
Yes.

Andy Whitcroft has a pending checkpatch fix
for this.  I hope he pushes it out soon.
Bah good reminder.  Will get those re-tested and out as soon as I can
...

-apw
Keyboard shortcuts
hback out one level
jnext message in thread
kprevious message in thread
ldrill in
Escclose help / fold thread tree
?toggle this help