* [PATCH v2] ethernet driver for the WIZnet W5300 chip
From: Mike Sinkovsky @ 2012-03-19 9:23 UTC (permalink / raw)
To: netdev, linux-kernel; +Cc: msink
Based on original driver from chip manufacturer, but with many cleanups.
Hope now it is near to mainline kernel quality.
Tested and used in production with Blackfin BF531 embedded processor.
Signed-off-by: Mike Sinkovsky <msink@permonline.ru>
---
v2:
- corrected handling of NET_ADDR_RANDOM flag
- support for WIZNET_BUS_ANY mode
- link detection using gpio
- registers read using ethtool
- more cleanups
drivers/net/ethernet/Kconfig | 1 +
drivers/net/ethernet/Makefile | 1 +
drivers/net/ethernet/wiznet/Kconfig | 61 +++
drivers/net/ethernet/wiznet/Makefile | 1 +
drivers/net/ethernet/wiznet/w5300.c | 699 ++++++++++++++++++++++++++++++++++
5 files changed, 763 insertions(+), 0 deletions(-)
create mode 100644 drivers/net/ethernet/wiznet/Kconfig
create mode 100644 drivers/net/ethernet/wiznet/Makefile
create mode 100644 drivers/net/ethernet/wiznet/w5300.c
diff --git a/drivers/net/ethernet/Kconfig b/drivers/net/ethernet/Kconfig
index 3474a61..e87313f 100644
--- a/drivers/net/ethernet/Kconfig
+++ b/drivers/net/ethernet/Kconfig
@@ -173,6 +173,7 @@ source "drivers/net/ethernet/tile/Kconfig"
source "drivers/net/ethernet/toshiba/Kconfig"
source "drivers/net/ethernet/tundra/Kconfig"
source "drivers/net/ethernet/via/Kconfig"
+source "drivers/net/ethernet/wiznet/Kconfig"
source "drivers/net/ethernet/xilinx/Kconfig"
source "drivers/net/ethernet/xircom/Kconfig"
diff --git a/drivers/net/ethernet/Makefile b/drivers/net/ethernet/Makefile
index 08d5f03..d24db66 100644
--- a/drivers/net/ethernet/Makefile
+++ b/drivers/net/ethernet/Makefile
@@ -72,5 +72,6 @@ obj-$(CONFIG_TILE_NET) += tile/
obj-$(CONFIG_NET_VENDOR_TOSHIBA) += toshiba/
obj-$(CONFIG_NET_VENDOR_TUNDRA) += tundra/
obj-$(CONFIG_NET_VENDOR_VIA) += via/
+obj-$(CONFIG_NET_VENDOR_WIZNET) += wiznet/
obj-$(CONFIG_NET_VENDOR_XILINX) += xilinx/
obj-$(CONFIG_NET_VENDOR_XIRCOM) += xircom/
diff --git a/drivers/net/ethernet/wiznet/Kconfig b/drivers/net/ethernet/wiznet/Kconfig
new file mode 100644
index 0000000..748fa3b
--- /dev/null
+++ b/drivers/net/ethernet/wiznet/Kconfig
@@ -0,0 +1,61 @@
+#
+# WIZnet device configuration
+#
+
+config NET_VENDOR_WIZNET
+ bool "WIZnet devices"
+ default y
+ ---help---
+ If you have a network (Ethernet) card belonging to this class, say Y
+ and read the Ethernet-HOWTO, available from
+ <http://www.tldp.org/docs.html#howto>.
+
+ Note that the answer to this question doesn't directly affect the
+ kernel: saying N will just cause the configurator to skip all
+ the questions about WIZnet devices. If you say Y, you will be asked
+ for your specific card in the following questions.
+
+if NET_VENDOR_WIZNET
+
+config WIZNET_W5300
+ tristate "WIZnet W5300 Ethernet support"
+ depends on ARM || BLACKFIN
+ ---help---
+ Support for WIZnet W5300 chips.
+
+ W5300 is a single chip with integrated 10/100 Ethernet MAC,
+ PHY and hardware TCP/IP stack, but this driver is limited to
+ the MAC and PHY functions only, onchip TCP/IP is unused.
+
+ To compile this driver as a module, choose M here: the module
+ will be called w5300.
+
+choice
+ prompt "WIZnet interface mode"
+ depends on NET_VENDOR_WIZNET
+ default WIZNET_BUS_ANY
+
+config WIZNET_BUS_DIRECT
+ bool "Direct address bus mode"
+ ---help---
+ In direct address mode host system can directly access W5300 registers
+ after mapping to Memory-mapped I/O Space.
+ 0x400 bytes are required for memory space.
+
+config WIZNET_BUS_INDIRECT
+ bool "Indirect address bus mode"
+ ---help---
+ In indirect address mode host system indirectly accesses registers by
+ using Indirect Mode Address Register (IDM_AR) and Indirect Mode Data
+ Register (IDM_DR), which are directly mapped to Memory-mapped I/O Space.
+ Only 0x06 bytes are required for memory space.
+
+config WIZNET_BUS_ANY
+ bool "Select interface mode in runtime"
+ ---help---
+ If interface mode is unknown in compile time, you can selectied it
+ in runtime.
+ Performance may decrease compared to explicitly selected bus mode.
+endchoice
+
+endif # NET_VENDOR_WIZNET
diff --git a/drivers/net/ethernet/wiznet/Makefile b/drivers/net/ethernet/wiznet/Makefile
new file mode 100644
index 0000000..88e0a3e
--- /dev/null
+++ b/drivers/net/ethernet/wiznet/Makefile
@@ -0,0 +1 @@
+obj-$(CONFIG_WIZNET_W5300) += w5300.o
diff --git a/drivers/net/ethernet/wiznet/w5300.c b/drivers/net/ethernet/wiznet/w5300.c
new file mode 100644
index 0000000..8f7adfa
--- /dev/null
+++ b/drivers/net/ethernet/wiznet/w5300.c
@@ -0,0 +1,699 @@
+/*
+ * Ethernet driver for the WIZnet W5300 chip.
+ *
+ * Copyright (C) 2008-2009 WIZnet Co.,Ltd.
+ * Copyright (C) 2011 Taehun Kim <kth3321 <at> gmail.com>
+ * Copyright (C) 2012 Mike Sinkovsky <msink@permonline.ru>
+ *
+ * Licensed under the GPL-2 or later.
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/platform_device.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/ethtool.h>
+#include <linux/skbuff.h>
+
+#include <linux/slab.h>
+#include <linux/types.h>
+#include <linux/errno.h>
+#include <linux/delay.h>
+#include <linux/spinlock.h>
+
+#include <linux/io.h>
+#include <linux/ioport.h>
+#include <linux/interrupt.h>
+#include <linux/gpio.h>
+
+#define DRV_NAME "WIZnet W5300"
+#define DRV_VERSION "2012-03-19"
+
+MODULE_DESCRIPTION(DRV_NAME "Ethernet driver v" DRV_VERSION);
+MODULE_AUTHOR("Mike Sinkovsky <msink@permonline.ru>");
+MODULE_ALIAS("platform:" KBUILD_MODNAME);
+MODULE_LICENSE("GPL");
+
+/*
+ * Frame size is hardwired to 1514 bytes,
+ * and MTU for 802.1Q frames must me set to 1496
+ */
+#define W5300_FRAME_SIZE 1514
+
+/*
+ * Device driver private data structure
+ */
+struct w5300_private {
+ void __iomem *base;
+ int irq;
+ int link;
+
+ spinlock_t reg_lock;
+ bool promisc_mode;
+ u16 (*read_u16) (struct w5300_private *priv, u16 addr);
+ void (*write_u16)(struct w5300_private *priv, u16 addr, u16 data);
+
+ struct napi_struct napi;
+ struct net_device *ndev;
+};
+
+/************************************************************************
+ *
+ * Lowlevel I/O functions
+ *
+ ***********************************************************************/
+
+/*
+ * In direct address mode host system can directly access W5300 registers
+ * after mapping to Memory-mapped I/O Space.
+ *
+ * 0x400 bytes are required for memory space.
+ */
+static inline u16
+read_u16_direct(struct w5300_private *priv, u16 addr)
+{
+ return ioread16(priv->base + addr);
+}
+
+static inline void
+write_u16_direct(struct w5300_private *priv, u16 addr, u16 data)
+{
+ iowrite16(data, priv->base + addr);
+ mmiowb();
+}
+
+/*
+ * In indirect address mode host system indirectly accesses registers by
+ * using Indirect Mode Address Register (IDM_AR) and Indirect Mode Data
+ * Register (IDM_DR), which are directly mapped to Memory-mapped I/O Space.
+ * Mode Register (MR) is directly accessible.
+ *
+ * Only 0x06 bytes are required for memory space.
+ */
+#define W5300_MR 0x00 /* Mode Register offset */
+#define W5300_IDM_AR 0x02 /* Indirect Mode Address Register offset */
+#define W5300_IDM_DR 0x04 /* Indirect Mode Data Register offset */
+
+static inline u16
+read_u16_indirect(struct w5300_private *priv, u16 addr)
+{
+ unsigned long flags;
+ u16 data;
+
+ spin_lock_irqsave(&priv->reg_lock, flags);
+ write_u16_direct(priv, W5300_IDM_AR, addr);
+ data = read_u16_direct(priv, W5300_IDM_DR);
+ spin_unlock_irqrestore(&priv->reg_lock, flags);
+
+ return data;
+}
+
+static inline void
+write_u16_indirect(struct w5300_private *priv, u16 addr, u16 data)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&priv->reg_lock, flags);
+ write_u16_direct(priv, W5300_IDM_AR, addr);
+ write_u16_direct(priv, W5300_IDM_DR, data);
+ spin_unlock_irqrestore(&priv->reg_lock, flags);
+}
+
+#if defined(CONFIG_WIZNET_BUS_DIRECT)
+#define detect_bus_mode(priv, mem_size) do {} while(0)
+#define read_reg_u16 read_u16_direct
+#define write_reg_u16 write_u16_direct
+
+#elif defined(CONFIG_WIZNET_BUS_INDIRECT)
+#define detect_bus_mode(priv, mem_size) do {} while(0)
+#define read_reg_u16 read_u16_indirect
+#define write_reg_u16 write_u16_indirect
+
+#else /* CONFIG_WIZNET_BUS_ANY */
+static inline void
+detect_bus_mode(struct w5300_private *priv, u16 mem_size)
+{
+ if (mem_size < 0x400) {
+ netdev_info(priv->ndev, "bus mode: indirect\n");
+ priv->read_u16 = read_u16_indirect;
+ priv->write_u16 = write_u16_indirect;
+ } else {
+ netdev_info(priv->ndev, "bus mode: direct\n");
+ priv->read_u16 = read_u16_direct;
+ priv->write_u16 = write_u16_direct;
+ }
+}
+
+static inline u16
+read_reg_u16(struct w5300_private *priv, u16 addr)
+{
+ return priv->read_u16(priv, addr);
+}
+
+static inline void
+write_reg_u16(struct w5300_private *priv, u16 addr, u16 data)
+{
+ priv->write_u16(priv, addr, data);
+}
+#endif
+
+static inline u32
+read_reg_u32(struct w5300_private *priv, u16 addr)
+{
+ u32 data;
+ data = read_reg_u16(priv, addr) << 16;
+ data |= read_reg_u16(priv, addr + 2);
+ return data;
+}
+
+static inline void
+write_reg_u32(struct w5300_private *priv, u16 addr, u32 data)
+{
+ write_reg_u16(priv, addr, data >> 16);
+ write_reg_u16(priv, addr + 2, data);
+}
+
+static inline void write_MR(struct w5300_private *priv, u16 data)
+{
+ write_u16_direct(priv, W5300_MR, data);
+}
+
+#define DEFINE_REG_RD(ADDR, TYPE, NAME) \
+static inline TYPE read_##NAME(struct w5300_private *priv) \
+{ \
+ return read_reg_##TYPE(priv, ADDR); \
+}
+#define DEFINE_REG_WR(ADDR, TYPE, NAME) \
+static inline void write_##NAME(struct w5300_private *priv, TYPE data) \
+{ \
+ write_reg_##TYPE(priv, ADDR, data); \
+}
+#define DEFINE_REG_RW(ADDR, TYPE, NAME) \
+ DEFINE_REG_RD(ADDR, TYPE, NAME) \
+ DEFINE_REG_WR(ADDR, TYPE, NAME)
+
+DEFINE_REG_RW(0x002, u16, IR) /* Interrupt Register */
+DEFINE_REG_WR(0x004, u16, IMR) /* Interrupt Mask Register */
+DEFINE_REG_WR(0x008, u32, SHARL) /* Source MAC address (0123) */
+DEFINE_REG_WR(0x00c, u16, SHARH) /* Source MAC address (45) */
+DEFINE_REG_WR(0x020, u32, TMSRL) /* Transmit Memory Size (0123) */
+DEFINE_REG_WR(0x024, u32, TMSRH) /* Transmit Memory Size (4567) */
+DEFINE_REG_WR(0x028, u32, RMSRL) /* Receive Memory Size (0123) */
+DEFINE_REG_WR(0x02c, u32, RMSRH) /* Receive Memory Size (4567) */
+DEFINE_REG_WR(0x030, u16, MTYPE) /* Memory Type */
+DEFINE_REG_RD(0x0fe, u16, IDR) /* Chip ID register (=0x5300) */
+DEFINE_REG_WR(0x200, u16, S0_MR) /* S0 Mode Register */
+DEFINE_REG_RW(0x202, u16, S0_CR) /* S0 Command Register */
+DEFINE_REG_WR(0x204, u16, S0_IMR) /* S0 Interrupt Mask Register */
+DEFINE_REG_RW(0x206, u16, S0_IR) /* S0 Interrupt Register */
+DEFINE_REG_RD(0x208, u16, S0_SSR) /* S0 Socket Status Register */
+DEFINE_REG_WR(0x220, u32, S0_TX_WRSR) /* S0 TX Write Size Register */
+DEFINE_REG_RD(0x224, u32, S0_TX_FSR) /* S0 TX Free Size Register */
+DEFINE_REG_RD(0x228, u32, S0_RX_RSR) /* S0 Received data Size */
+DEFINE_REG_WR(0x22e, u16, S0_TX_FIFO) /* S0 Transmit FIFO */
+DEFINE_REG_RD(0x230, u16, S0_RX_FIFO) /* S0 Receive FIFO */
+
+/* Mode Register values */
+#define MR_DBW (1 << 15) /* Data bus width */
+#define MR_MPF (1 << 14) /* Mac layer pause frame */
+#define MR_WDF(n) (n << 11) /* Write data fetch time */
+#define MR_RDH (1 << 10) /* Read data hold time */
+#define MR_FS (1 << 8) /* FIFO swap */
+#define MR_RST (1 << 7) /* S/W reset */
+#define MR_PB (1 << 4) /* Ping block */
+#define MR_DBS (1 << 2) /* Data bus swap */
+#define MR_IND (1 << 0) /* Indirect mode */
+
+#ifdef CONFIG_WIZNET_BUS_INDIRECT
+#define MR_VALUE (MR_WDF(7) | MR_PB | MR_IND)
+#else
+#define MR_VALUE (MR_WDF(7) | MR_PB)
+#endif
+
+/* IR/IMR register values */
+#define IR_S0 0x01 /* S0 interrupt */
+
+/* S0_MR register values */
+#define S0_MR_CLOSE 0x00 /* Close mode */
+#define S0_MR_MACRAW 0x04 /* MAC RAW mode (promiscous) */
+#define S0_MR_MACRAW_MF 0x44 /* MAC RAW mode (filtered) */
+
+/* S0_CR register values */
+#define S0_CR_OPEN 0x01 /* OPEN command */
+#define S0_CR_CLOSE 0x10 /* CLOSE command */
+#define S0_CR_SEND 0x20 /* SEND command */
+#define S0_CR_RECV 0x40 /* RECV command */
+
+/* S0_IR/S0_IMR register values */
+#define S0_IR_RECV 0x04 /* Receive interrupt */
+
+static void read_fifo(struct w5300_private *priv, u8 *data, int len)
+{
+ for (; len > 0; len -= 2) {
+ u16 fifo = read_S0_RX_FIFO(priv);
+ *data++ = fifo >> 8;
+ *data++ = fifo;
+ }
+}
+
+static void write_fifo(struct w5300_private *priv, u8 *data, int len)
+{
+ for (; len > 0; len -= 2) {
+ u16 fifo = *data++ << 8;
+ fifo |= *data++;
+ write_S0_TX_FIFO(priv, fifo);
+ }
+}
+
+static inline int send_command(struct w5300_private *priv, u16 cmd)
+{
+ unsigned long timeout = jiffies + msecs_to_jiffies(100);
+
+ write_S0_CR(priv, cmd);
+
+ while (read_S0_CR(priv) != 0) {
+ if (time_after(jiffies, timeout))
+ return -EIO;
+ cpu_relax();
+ }
+
+ return 0;
+}
+
+static void write_macaddr(struct w5300_private *priv)
+{
+ struct net_device *ndev = priv->ndev;
+ write_SHARL(priv, ndev->dev_addr[0] << 24 |
+ ndev->dev_addr[1] << 16 |
+ ndev->dev_addr[2] << 8 |
+ ndev->dev_addr[3]);
+ write_SHARH(priv, ndev->dev_addr[4] << 8 |
+ ndev->dev_addr[5]);
+}
+
+static void reset_chip(struct w5300_private *priv)
+{
+ write_MR(priv, MR_RST);
+ mdelay(5);
+ write_MR(priv, MR_VALUE);
+
+ write_IMR(priv, 0);
+
+ /*
+ * Configure 128K of internal memory
+ * as 64K RX fifo and 64K TX fifo
+ */
+ write_RMSRL(priv, 64 << 24);
+ write_RMSRH(priv, 0);
+ write_TMSRL(priv, 64 << 24);
+ write_TMSRH(priv, 0);
+ write_MTYPE(priv, 0x00ff);
+
+ write_macaddr(priv);
+}
+
+/***********************************************************************
+ *
+ * Device driver functions / callbacks
+ *
+ ***********************************************************************/
+
+static void w5300_get_drvinfo(struct net_device *ndev,
+ struct ethtool_drvinfo *info)
+{
+ strlcpy(info->driver, DRV_NAME, sizeof(info->driver));
+ strlcpy(info->version, DRV_VERSION, sizeof(info->version));
+ strlcpy(info->fw_version, "N/A", sizeof(info->fw_version));
+ strlcpy(info->bus_info, dev_name(ndev->dev.parent),
+ sizeof(info->bus_info));
+}
+
+#define W5300_REGS_LEN 0x400
+
+static int w5300_get_regs_len(struct net_device *ndev)
+{
+ return W5300_REGS_LEN;
+}
+
+static void w5300_get_regs(struct net_device *ndev,
+ struct ethtool_regs *regs, void *_buf)
+{
+ struct w5300_private *priv = netdev_priv(ndev);
+ u8 *buf = _buf;
+ u16 addr;
+ u16 data;
+
+ regs->version = 1;
+ for (addr = 0; addr < W5300_REGS_LEN; addr += 2) {
+ switch (addr & 0x23f) {
+ case 0x22e: /* don't read TX_FIFO register! */
+ case 0x230: /* don't read RX_FIFO register! */
+ data = 0xffff;
+ break;
+ default:
+ data = read_reg_u16(priv, addr);
+ break;
+ }
+ *buf++ = data >> 8;
+ *buf++ = data;
+ }
+}
+
+static void w5300_tx_timeout(struct net_device *ndev)
+{
+ struct w5300_private *priv = netdev_priv(ndev);
+
+ netdev_err(ndev, "Transmit timeout!\n");
+
+ ndev->stats.tx_errors++;
+ reset_chip(priv);
+ netif_wake_queue(ndev);
+}
+
+static int w5300_start_tx(struct sk_buff *skb, struct net_device *ndev)
+{
+ struct w5300_private *priv = netdev_priv(ndev);
+
+ if (unlikely(read_S0_TX_FSR(priv) < skb->len)) {
+ ndev->stats.tx_dropped++;
+ return NETDEV_TX_BUSY;
+ }
+
+ write_fifo(priv, skb->data, skb->len);
+ write_S0_TX_WRSR(priv, skb->len);
+ send_command(priv, S0_CR_SEND);
+
+ ndev->stats.tx_packets++;
+ ndev->stats.tx_bytes += skb->len;
+ dev_kfree_skb(skb);
+
+ return NETDEV_TX_OK;
+}
+
+static int w5300_napi_poll(struct napi_struct *napi, int budget)
+{
+ struct w5300_private *priv =
+ container_of(napi, struct w5300_private, napi);
+ struct net_device *ndev = priv->ndev;
+ struct sk_buff *skb;
+ u16 rx_frame_size;
+ int rx_count;
+
+ for (rx_count = 0; rx_count < budget; rx_count++) {
+ u32 rx_fifo_size = read_S0_RX_RSR(priv);
+ if (rx_fifo_size == 0)
+ break;
+
+ rx_frame_size = read_S0_RX_FIFO(priv);
+
+ skb = netdev_alloc_skb(ndev, NET_IP_ALIGN +
+ roundup(rx_frame_size, 2));
+ if (unlikely(!skb)) {
+ int len = rx_frame_size + 4;
+ for (; len > 0; len -= 2)
+ read_S0_RX_FIFO(priv);
+ ndev->stats.rx_dropped++;
+ return -ENOMEM;
+ }
+
+ skb_reserve(skb, NET_IP_ALIGN);
+ skb_put(skb, rx_frame_size);
+ read_fifo(priv, skb->data, rx_frame_size);
+ read_S0_RX_FIFO(priv);
+ read_S0_RX_FIFO(priv);
+
+ skb->protocol = eth_type_trans(skb, ndev);
+ netif_receive_skb(skb);
+ ndev->stats.rx_packets++;
+ ndev->stats.rx_bytes += rx_frame_size;
+ }
+
+ if (rx_count < budget) {
+ write_IMR(priv, IR_S0);
+ napi_complete(napi);
+ }
+
+ return rx_count;
+}
+
+static irqreturn_t w5300_start_rx(int irq, void *ndev_instance)
+{
+ struct net_device *ndev = ndev_instance;
+ struct w5300_private *priv = netdev_priv(ndev);
+
+ write_S0_IR(priv, S0_IR_RECV);
+
+ if (napi_schedule_prep(&priv->napi)) {
+ write_IMR(priv, 0);
+ __napi_schedule(&priv->napi);
+ }
+
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t w5300_detect_link(int irq, void *ndev_instance)
+{
+ struct net_device *ndev = ndev_instance;
+ struct w5300_private *priv = netdev_priv(ndev);
+
+ if (netif_running(ndev)) {
+ if (gpio_get_value(priv->link) == 0)
+ netif_carrier_off(ndev);
+ else
+ netif_carrier_on(ndev);
+ }
+
+ return IRQ_HANDLED;
+}
+
+static void w5300_set_rx_mode(struct net_device *ndev)
+{
+ struct w5300_private *priv = netdev_priv(ndev);
+ bool set_promisc = (ndev->flags & IFF_PROMISC) != 0;
+ int mode = set_promisc ? S0_MR_MACRAW : S0_MR_MACRAW_MF;
+
+ if (priv->promisc_mode != set_promisc) {
+ priv->promisc_mode = set_promisc;
+ write_S0_MR(priv, mode);
+ send_command(priv, S0_CR_OPEN);
+ }
+}
+
+static int w5300_set_macaddr(struct net_device *ndev, void *addr)
+{
+ struct w5300_private *priv = netdev_priv(ndev);
+ struct sockaddr *sock_addr = addr;
+
+ if (!is_valid_ether_addr(sock_addr->sa_data))
+ return -EADDRNOTAVAIL;
+ memcpy(ndev->dev_addr, sock_addr->sa_data, ETH_ALEN);
+ ndev->addr_assign_type &= ~NET_ADDR_RANDOM;
+ write_macaddr(priv);
+ return 0;
+}
+
+static int w5300_open(struct net_device *ndev)
+{
+ struct w5300_private *priv = netdev_priv(ndev);
+ int mode = priv->promisc_mode ? S0_MR_MACRAW : S0_MR_MACRAW_MF;
+
+ if (!is_valid_ether_addr(ndev->dev_addr))
+ return -EINVAL;
+
+ write_S0_IMR(priv, S0_IR_RECV);
+ write_S0_MR(priv, mode);
+ send_command(priv, S0_CR_OPEN);
+ write_IMR(priv, IR_S0);
+
+ napi_enable(&priv->napi);
+ netif_start_queue(ndev);
+ if (priv->link < 0 || gpio_get_value(priv->link))
+ netif_carrier_on(ndev);
+ return 0;
+}
+
+static int w5300_stop(struct net_device *ndev)
+{
+ struct w5300_private *priv = netdev_priv(ndev);
+
+ write_IMR(priv, 0);
+ write_S0_CR(priv, S0_CR_CLOSE);
+
+ netif_carrier_off(ndev);
+ netif_stop_queue(ndev);
+ napi_disable(&priv->napi);
+ return 0;
+}
+
+static const struct ethtool_ops w5300_ethtool_ops = {
+ .get_drvinfo = w5300_get_drvinfo,
+ .get_regs_len = w5300_get_regs_len,
+ .get_regs = w5300_get_regs,
+};
+
+static const struct net_device_ops w5300_netdev_ops = {
+ .ndo_open = w5300_open,
+ .ndo_stop = w5300_stop,
+ .ndo_start_xmit = w5300_start_tx,
+ .ndo_tx_timeout = w5300_tx_timeout,
+ .ndo_set_rx_mode = w5300_set_rx_mode,
+ .ndo_set_mac_address = w5300_set_macaddr,
+ .ndo_validate_addr = eth_validate_addr,
+ .ndo_change_mtu = eth_change_mtu,
+};
+
+static int __devinit w5300_hw_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct net_device *ndev = platform_get_drvdata(pdev);
+ struct w5300_private *priv = netdev_priv(ndev);
+ const char *name = netdev_name(ndev);
+ struct resource *link;
+ struct resource *mem;
+ int mem_size;
+ int irq;
+ int ret;
+
+ mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!mem)
+ return -ENXIO;
+ mem_size = resource_size(mem);
+ if (!devm_request_mem_region(dev, mem->start, mem_size, name))
+ return -EBUSY;
+ priv->base = devm_ioremap(dev, mem->start, mem_size);
+ if (!priv->base)
+ return -EBUSY;
+
+ spin_lock_init(&priv->reg_lock);
+ detect_bus_mode(priv, mem_size);
+ reset_chip(priv);
+ if (read_IDR(priv) != 0x5300)
+ return -ENODEV;
+
+ irq = platform_get_irq(pdev, 0);
+ if (irq < 0)
+ return irq;
+ ret = devm_request_irq(dev, irq, w5300_start_rx,
+ IRQ_TYPE_LEVEL_LOW, name, ndev);
+ if (ret < 0)
+ return ret;
+ priv->irq = irq;
+
+ link = platform_get_resource(pdev, IORESOURCE_IO, 0);
+ if (!link) {
+ priv->link = -1;
+ } else {
+ char *link_name = devm_kzalloc(dev, 16, GFP_KERNEL);
+ snprintf(link_name, 16, "%s-link", name);
+ priv->link = link->start;
+ if (request_irq(gpio_to_irq(priv->link), w5300_detect_link,
+ IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING,
+ link_name, priv->ndev) < 0)
+ priv->link = -1;
+ }
+
+ netdev_info(ndev, "at 0x%llx irq %d\n", (u64)mem->start, irq);
+ return 0;
+}
+
+static int __devinit w5300_probe(struct platform_device *pdev)
+{
+ struct net_device *ndev;
+ struct w5300_private *priv;
+ int ret;
+
+ ndev = alloc_etherdev(sizeof(*priv));
+ if (!ndev)
+ return -ENOMEM;
+ SET_NETDEV_DEV(ndev, &pdev->dev);
+ platform_set_drvdata(pdev, ndev);
+ priv = netdev_priv(ndev);
+ priv->ndev = ndev;
+
+ ether_setup(ndev);
+ ndev->netdev_ops = &w5300_netdev_ops;
+ ndev->ethtool_ops = &w5300_ethtool_ops;
+ ndev->watchdog_timeo = 2 * HZ;
+ netif_napi_add(ndev, &priv->napi, w5300_napi_poll, 16);
+ ret = register_netdev(ndev);
+ if (ret < 0)
+ goto fail;
+
+ random_ether_addr(ndev->dev_addr);
+ ndev->addr_assign_type |= NET_ADDR_RANDOM;
+ ret = w5300_hw_probe(pdev);
+ if (ret < 0)
+ goto fail;
+
+ return 0;
+
+fail: netdev_info(ndev, "probe failed (%d)\n", ret);
+ unregister_netdev(ndev);
+ free_netdev(ndev);
+ platform_set_drvdata(pdev, NULL);
+ return ret;
+}
+
+static int __devexit w5300_remove(struct platform_device *pdev)
+{
+ struct net_device *ndev = platform_get_drvdata(pdev);
+
+ unregister_netdev(ndev);
+ free_netdev(ndev);
+ platform_set_drvdata(pdev, NULL);
+ return 0;
+}
+
+#ifdef CONFIG_PM
+static int w5300_suspend(struct platform_device *pdev, pm_message_t mesg)
+{
+ struct net_device *ndev = platform_get_drvdata(pdev);
+ struct w5300_private *priv = netdev_priv(ndev);
+
+ if (netif_running(ndev)) {
+ netif_carrier_off(ndev);
+ netif_device_detach(ndev);
+
+ write_IMR(priv, 0);
+ send_command(priv, S0_CR_CLOSE);
+ }
+ return 0;
+}
+
+static int w5300_resume(struct platform_device *pdev)
+{
+ struct net_device *ndev = platform_get_drvdata(pdev);
+ struct w5300_private *priv = netdev_priv(ndev);
+ int mode = priv->promisc_mode ? S0_MR_MACRAW : S0_MR_MACRAW_MF;
+
+ if (netif_running(ndev)) {
+ reset_chip(priv);
+ write_S0_MR(priv, mode);
+ send_command(priv, S0_CR_OPEN);
+ write_IMR(priv, IR_S0);
+
+ netif_device_attach(ndev);
+ if (priv->link < 0 || gpio_get_value(priv->link))
+ netif_carrier_on(ndev);
+ }
+ return 0;
+}
+#endif /* CONFIG_PM */
+
+static struct platform_driver wiznet_w5300_driver = {
+ .driver = {
+ .name = KBUILD_MODNAME,
+ .owner = THIS_MODULE,
+ },
+ .probe = w5300_probe,
+ .remove = __devexit_p(w5300_remove),
+#ifdef CONFIG_PM
+ .suspend = w5300_suspend,
+ .resume = w5300_resume,
+#endif
+};
+
+module_platform_driver(wiznet_w5300_driver);
^ permalink raw reply related
* Re: [PATCH] adjust __net_exit
From: Jan Beulich @ 2012-03-19 9:09 UTC (permalink / raw)
To: Sam Ravnborg; +Cc: David Miller, xemul, netdev, netfilter-devel, ebiederm
In-Reply-To: <20120319084703.GA20897@merkur.ravnborg.org>
>>> On 19.03.12 at 09:47, Sam Ravnborg <sam@ravnborg.org> wrote:
>> >> > Using the (bogus and unused elsewhere)
>> >> > __exit_refok to implement this is inefficient - any non-modular code
>> >> > really can reside in __init (as non-modular __exit code is never used).
>
>> > 2) fix up the bogus commit message
>>
>> Bogus in what way?
>
> I fail to see that __exit_refok is bogus.
Either an exit function references only permitted code (in which case
it is legitimately using __exit), or it references non-__exit code, in
which case it shouldn't have an override at all. Permitting an exit
function to reference e.g. __init code is suspicious (at best) in all
cases I can think of (in contrast to allowing a non-init function to
reference __init code on a code path that can be proven to be taken
only during system startup).
Jan
> The name is not optimal - which is why is is deprecated.
>
> Sam
^ permalink raw reply
* Re: [PATCH] adjust __net_exit
From: Sam Ravnborg @ 2012-03-19 8:47 UTC (permalink / raw)
To: Jan Beulich; +Cc: David Miller, xemul, netdev, netfilter-devel, ebiederm
In-Reply-To: <4F66F8F70200007800079305@nat28.tlf.novell.com>
> >> > Using the (bogus and unused elsewhere)
> >> > __exit_refok to implement this is inefficient - any non-modular code
> >> > really can reside in __init (as non-modular __exit code is never used).
> > 2) fix up the bogus commit message
>
> Bogus in what way?
I fail to see that __exit_refok is bogus.
The name is not optimal - which is why is is deprecated.
Sam
^ permalink raw reply
* Re: [V4 PATCH] virtio-net: send gratuitous packet when needed
From: Michael S. Tsirkin @ 2012-03-19 8:44 UTC (permalink / raw)
To: Rusty Russell; +Cc: netdev, linux-kernel, virtualization
In-Reply-To: <877gyhxrlu.fsf@rustcorp.com.au>
On Mon, Mar 19, 2012 at 12:46:29PM +1030, Rusty Russell wrote:
> On Tue, 13 Mar 2012 16:33:31 +0200, "Michael S. Tsirkin" <mst@redhat.com> wrote:
> > > diff --git a/include/linux/virtio_net.h b/include/linux/virtio_net.h
> > > index 970d5a2..44a38d6 100644
> > > --- a/include/linux/virtio_net.h
> > > +++ b/include/linux/virtio_net.h
> > > @@ -49,8 +49,10 @@
> > > #define VIRTIO_NET_F_CTRL_RX 18 /* Control channel RX mode support */
> > > #define VIRTIO_NET_F_CTRL_VLAN 19 /* Control channel VLAN filtering */
> > > #define VIRTIO_NET_F_CTRL_RX_EXTRA 20 /* Extra RX mode control support */
> > > +#define VIRTIO_NET_F_GUEST_ANNOUNCE 21 /* Guest can send gratituous packet */
> > >
> > > #define VIRTIO_NET_S_LINK_UP 1 /* Link is up */
> > > +#define VIRTIO_NET_S_ANNOUNCE 2 /* Announcement is needed */
> >
> > I would put this in bit 8 (0x100), this way low status byte
> > is RO, high byte is RW.
>
> The whole idea of acking by clearing the bit is unreliable, moving to a
> separate byte just controls the damage.
>
> How about you use bits 8-15 as a counter? It's still theoretically
> unreliable if 256 notifications pass before the guest notices, but it's
> probably better and clearer than this.
>
> I leave the final call to MST though.
>
> Thanks,
> Rusty.
I guess the point was that we want a single packet
so we don't care if multiple notifications are coalesced
into a single one.
> --
> How could I marry someone with more hair than me? http://baldalex.org
^ permalink raw reply
* 3.0.23 WARNING: at net/ipv4/tcp_input.c:3375 tcp_ack+0x1d62/0x2030()
From: Stefan Priebe - Profihost AG @ 2012-03-19 8:10 UTC (permalink / raw)
To: stable; +Cc: Linux Netdev List
Hi list,
today i've seen the following error / backtrace several times on a
machine running vanilla stable 3.0.23.
------------[ cut here ]------------
WARNING: at net/ipv4/tcp_input.c:3375 tcp_ack+0x1d62/0x2030()
Hardware name: empty
Modules linked in: xt_tcpudp ipt_REJECT iptable_filter ip_tables
x_tables k8temp ipv6 md_mod dm_snapshot dm_mod
Pid: 0, comm: swapper Tainted: G W 2.6.40.23intel #1
Call Trace:
[<c05de162>] ? tcp_ack+0x1d62/0x2030
[<c013d56c>] warn_slowpath_common+0x6c/0xa0
[<c05de162>] ? tcp_ack+0x1d62/0x2030
[<c0605e20>] ? bictcp_undo_cwnd+0x20/0x20
[<c013d5bd>] warn_slowpath_null+0x1d/0x20
[<c05de162>] tcp_ack+0x1d62/0x2030
[<c05daa09>] ? tcp_validate_incoming+0x269/0x330
[<c05de6d6>] tcp_rcv_established+0x2a6/0x5a0
[<c05e72d1>] tcp_v4_do_rcv+0xf1/0x250
[<c05e7a7a>] tcp_v4_rcv+0x64a/0x7b0
[<f874c0ab>] ? iptable_filter_hook+0x3b/0x74 [iptable_filter]
[<c05c809f>] ip_local_deliver_finish+0xef/0x260
[<c05c829f>] ip_local_deliver+0x8f/0xa0
[<c05c7fb0>] ? ip_call_ra_chain+0x110/0x110
[<c05c7973>] ip_rcv_finish+0x143/0x350
[<c05c7e1d>] ip_rcv+0x29d/0x320
[<c05e484e>] ? tcp4_gro_receive+0x4e/0xd0
[<c05a74cc>] __netif_receive_skb+0x37c/0x3f0
[<c05a8177>] netif_receive_skb+0x47/0x70
[<c05a829f>] napi_skb_finish+0x3f/0x50
[<c05a8f91>] napi_gro_receive+0x111/0x140
[<c04e61e7>] e1000_receive_skb+0x47/0x60
[<c04e7e56>] e1000_clean_rx_irq+0x1f6/0x370
[<c04e69f3>] e1000_clean+0x63/0x2b0
[<c05a874d>] net_rx_action+0xdd/0x230
[<c0143369>] __do_softirq+0xa9/0x180
[<c01432c0>] ? irq_enter+0x60/0x60
[<c01432c0>] ? irq_enter+0x60/0x60
<IRQ> [<c01431bd>] ? irq_exit+0x6d/0x90
[<c0104375>] ? do_IRQ+0x45/0xb0
[<c01431bd>] ? irq_exit+0x6d/0x90
[<c011bf09>] ? smp_apic_timer_interrupt+0x59/0x90
[<c065e9b0>] ? common_interrupt+0x30/0x38
[<c010aaee>] ? mwait_idle+0xee/0x100
[<c0101ee6>] ? cpu_idle+0x96/0xb0
[<c0638188>] ? rest_init+0x58/0x70
[<c08a08a5>] ? start_kernel+0x344/0x3a7
[<c08a0325>] ? kernel_init+0x13e/0x13e
[<c08a0079>] ? i386_start_kernel+0x79/0xb5
---[ end trace b310ee8f88560c3c ]---
Stefan
^ permalink raw reply
* Re: [PATCH] adjust __net_exit
From: Jan Beulich @ 2012-03-19 8:14 UTC (permalink / raw)
To: David Miller, Sam Ravnborg; +Cc: xemul, netdev, netfilter-devel, ebiederm
In-Reply-To: <20120317230318.GA19460@merkur.ravnborg.org>
>>> On 18.03.12 at 00:03, Sam Ravnborg <sam@ravnborg.org> wrote:
> On Fri, Mar 16, 2012 at 10:18:13PM -0700, David Miller wrote:
>> From: "Jan Beulich" <JBeulich@suse.com>
>> Date: Thu, 08 Mar 2012 09:37:32 +0000
>>
>> > __net_exit, judging by the majority of its uses, was intended to serve
>> > as an abstraction to allow calling such annotated functions from both
>> > __init and __exit functions. Using the (bogus and unused elsewhere)
>> > __exit_refok to implement this is inefficient - any non-modular code
>> > really can reside in __init (as non-modular __exit code is never used).
>> >
>> > Therefore, adjust __net_exit to resolve to nothing (i.e. normal .text)
>> > in modules, and __init in the core kernel.
>> >
>> > A few other adjustments are necessary/possible with this done - those
>> > were likely just oversights when added originally.
>> >
>> > Signed-off-by: Jan Beulich <jbeulich@suse.com>
>>
>> [ I have been waiting for more than a week for a netns developer
>> to review this patch, I guess I'm too optimistic these days. :-( ]
>>
>> The only reason you think __exit_refok is "bogus" is because it's
>> semantics got changed by Sam Ravnborg in commit
>> 312b1485fb509c9bc32eda28ad29537896658cb8 ("Introduce new section
>> reference annotations tags: __ref, __refdata, __refconst")
>>
>> Beforehand the __exit_refok was a real .exit section, so it got
>> completely discarded AT LINK TIME. Now it sits together with
>> __init_refok which is an unremovable kernel image section, which
>> neither gets removed at compile time nor boot time.
>
> Some misunderstanding is going on here.
>
> The *ref* annotation is used to teach modpost that this function (or data)
> may reference functions (or data) which is annotated __init*.
> And the *ref* annotation never caused the annotated code to be discarded.
>
> This was true before the above mentioned patch - and it is still true.
>
> Before the path ("Introduce new section reference ....") the __exit_refok
> annotation moved functions to the section named ".exit.text.refok"
> which was explicit part of .text (TEXT_TEXT in vmlinux).
>
> So __exit_refok does exactly what it is intended to do:
> It puts the function in a section so modpost does not warn about
> references to __init or __exit sections.
>
> As Jan points out there is only a single user left - so
> this would be a good time to kill it. It is even documented
> in init.h that this is a backward compatibility define.
>
> The intention with Jan's patch is to move functions annotated
> __net_exit to a discardable section in the core kernel.
> Then at least __exit should be used - there is no logic
> using __init for exit code.
>
> I suggest to:
> 1) fix the patch to use __exit
That won't work, as the goal is to have the exit functions call from
__net_init iiuc.
> 2) fix up the bogus commit message
Bogus in what way?
> Then it should be OK - iff the assumption hold that the functions
> can be discarded in the core kernel.
Since __exit functions won't ever be called when built in, they can as
well be marked __init and thus become possible to call from __init code
(again iiuc). Perhaps this should even become a concept outside of
net/, as not marking exit code __exit just because is also gets called
on error paths from __init is not uncommon. Maybe it should even be
__exit itself to get changed that way (albeit I vaguely recall that
differences between arches of when .exit.* sections get discarded
might need further consideration), as long as no arch would get
penalized in that it discards .exit.* but not .init.* post-boot.
Jan
> I have grepped a little and saw no uses where this did not hold true.
> So based on this I assume the assumption is OK.
>
> Sam
^ permalink raw reply
* Re: [PATCH] ipv6: fix incorrent ipv6 ipsec packet fragment
From: Steffen Klassert @ 2012-03-19 8:02 UTC (permalink / raw)
To: Gao feng; +Cc: davem, eric.dumazet, netdev
In-Reply-To: <1331888235-10368-1-git-send-email-gaofeng@cn.fujitsu.com>
On Fri, Mar 16, 2012 at 04:57:15PM +0800, Gao feng wrote:
> Since commit 299b0767(ipv6: Fix IPsec slowpath fragmentation problem)
> In func ip6_append_data,after call skb_put(skb, fraglen + dst_exthdrlen)
> the skb->len contains dst_exthdrlen,and we don't reduce dst_exthdrlen at last
> This will make fraggap>0 in next "while cycle",and cause the size of skb incorrent
Good catch!
>
> Call skb_pull reduce skb->len before next "while cycle"
I think it would be better to just reserve headroom for
dst_exthdrlen instead of adding and removing data to/from
the skb.
^ permalink raw reply
* [net-next PATCH v0 1/5] net: add generic PF_BRIDGE:RTM_ FDB hooks
From: John Fastabend @ 2012-03-19 7:19 UTC (permalink / raw)
To: Jamal Hadi Salim, Stephen Hemminger, Ben Hutchings, Roopa Prabhu,
David Miller, Kirsher, Jeffrey T
Cc: netdev@vger.kernel.org, kvm
In-Reply-To: <20120319065543.11166.76186.stgit@jf-dev1-dcblab>
Forgot to change the title resending with a title that won't
be dropped by netdev and kvm mailing lists. And updated my
local repo so it won't happen again.
---
This adds two new flags NTF_MASTER and NTF_LOWERDEV that can
now be used to specify where PF_BRIDGE netlink commands should
be sent. NTF_MASTER sends the commands to the 'dev->master'
device for parsing. Typically this will be the linux net/bridge,
macvlan, or open-vswitch devices. Also without any flags set
the command will be handled by the master device as well so
that current user space tools continue to work as expected.
The NTF_LOWERDEV flag will push the PF_BRIDGE commands to the
device. In the basic example below the commands are then parsed
and programmed in the embedded bridge.
Note if both NTF_LOWERDEV and NTF_MASTER bits are set then the
command will be sent both to 'dev->master' and 'dev' this allows
user space to easily keep the embedded bridge and software bridge
in sync.
To support this new net device ops were added to call into
the device and the existing bridging code was refactored
to use these. There should be no change from user space.
A basic setup with a SR-IOV enabled NIC looks like this,
veth0 veth2
| |
------------
| bridge0 | <---- software bridging
------------
/
/
ethx.y ethx
VF PF
\ \ <---- propagate FDB entries to HW
\ \
--------------------
| Embedded Bridge | <---- hardware offloaded switching
--------------------
In this case the embedded bridge must be managed to allow 'veth0'
to communicate with 'ethx.y' correctly. At present drivers managing
the embedded bridge either send frames onto the network which
then get dropped by the switch OR the embedded bridge will flood
these frames. With this patch we have a mechanism to manage the
embedded bridge correctly from user space. This example is specific
to SR-IOV but replacing the VF with another PF or dropping this
into the DSA framework generates similar management issues.
Examples session using the 'br'[1] tool to add, dump and then
delete a mac address with a new "embedded" option and enabled
ixgbe driver:
# br fdb add 22:35:19:ac:60:59 dev eth3
# br fdb
port mac addr flags
veth0 22:35:19:ac:60:58 static
veth0 9a:5f:81:f7:f6:ec local
eth3 00:1b:21:55:23:59 local
eth3 22:35:19:ac:60:59 static
veth0 22:35:19:ac:60:57 static
#br fdb add 22:35:19:ac:60:59 embedded dev eth3
#br fdb
port mac addr flags
veth0 22:35:19:ac:60:58 static
veth0 9a:5f:81:f7:f6:ec local
eth3 00:1b:21:55:23:59 local
eth3 22:35:19:ac:60:59 static
veth0 22:35:19:ac:60:57 static
eth3 22:35:19:ac:60:59 local embedded
#br fdb del 22:35:19:ac:60:59 embedded dev eth3
I added a couple lines to 'br' to set the flags correctly is all. It
is my opinion that the merit of this patch is now embedded and SW
bridges can both be modeled correctly in user space using very nearly
the same message passing.
[1] 'br' tool was published as an RFC here and will be renamed 'bridge'
http://patchwork.ozlabs.org/patch/117664/
Thanks to Jamal Hadi Salim, Stephen Hemminger and Ben Hutchings for
valuable feedback, suggestions, and review.
Signed-off-by: John Fastabend <john.r.fastabend@intel.com>
---
include/linux/neighbour.h | 3 +
include/linux/netdevice.h | 26 ++++++++
include/linux/rtnetlink.h | 4 +
net/bridge/br_device.c | 3 +
net/bridge/br_fdb.c | 128 ++++++++++--------------------------------
net/bridge/br_netlink.c | 12 ----
net/bridge/br_private.h | 15 ++++-
net/core/rtnetlink.c | 138 +++++++++++++++++++++++++++++++++++++++++++++
8 files changed, 217 insertions(+), 112 deletions(-)
diff --git a/include/linux/neighbour.h b/include/linux/neighbour.h
index b188f68..3a94409 100644
--- a/include/linux/neighbour.h
+++ b/include/linux/neighbour.h
@@ -33,6 +33,9 @@ enum {
#define NTF_PROXY 0x08 /* == ATF_PUBL */
#define NTF_ROUTER 0x80
+#define NTF_LOWERDEV 0x02
+#define NTF_MASTER 0x04
+
/*
* Neighbor Cache Entry States.
*/
diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h
index 4535a4e..4208901 100644
--- a/include/linux/netdevice.h
+++ b/include/linux/netdevice.h
@@ -54,6 +54,7 @@
#include <net/netprio_cgroup.h>
#include <linux/netdev_features.h>
+#include <linux/neighbour.h>
struct netpoll_info;
struct phy_device;
@@ -904,6 +905,19 @@ struct netdev_fcoe_hbainfo {
* feature set might be less than what was returned by ndo_fix_features()).
* Must return >0 or -errno if it changed dev->features itself.
*
+ * int (*ndo_fdb_add)(struct ndmsg *ndm, struct net_device *dev,
+ * unsigned char *addr, u16 flags)
+ * Adds an FDB entry to dev for addr. The ndmsg contains flags to indicate
+ * if the dev->master FDB should be updated or the devices internal FDB.
+ * int (*ndo_fdb_del)(struct ndmsg *ndm, struct net_device *dev,
+ * unsigned char *addr)
+ * Deletes the FDB entry from dev coresponding to addr. The ndmsg
+ * contains flags to indicate if the dev->master FDB should be
+ * updated or the devices internal FDB.
+ * int (*ndo_fdb_dump)(struct sk_buff *skb, struct netlink_callback *cb,
+ * struct net_device *dev, int idx)
+ * Used to add FDB entries to dump requests. Implementers should add
+ * entries to skb and update idx with the number of entries.
*/
struct net_device_ops {
int (*ndo_init)(struct net_device *dev);
@@ -1001,6 +1015,18 @@ struct net_device_ops {
netdev_features_t features);
int (*ndo_neigh_construct)(struct neighbour *n);
void (*ndo_neigh_destroy)(struct neighbour *n);
+
+ int (*ndo_fdb_add)(struct ndmsg *ndm,
+ struct net_device *dev,
+ unsigned char *addr,
+ u16 flags);
+ int (*ndo_fdb_del)(struct ndmsg *ndm,
+ struct net_device *dev,
+ unsigned char *addr);
+ int (*ndo_fdb_dump)(struct sk_buff *skb,
+ struct netlink_callback *cb,
+ struct net_device *dev,
+ int idx);
};
/*
diff --git a/include/linux/rtnetlink.h b/include/linux/rtnetlink.h
index 577592e..2c1de89 100644
--- a/include/linux/rtnetlink.h
+++ b/include/linux/rtnetlink.h
@@ -801,6 +801,10 @@ rtattr_failure:
return table;
}
+extern int ndo_dflt_fdb_dump(struct sk_buff *skb,
+ struct netlink_callback *cb,
+ struct net_device *dev,
+ int idx);
#endif /* __KERNEL__ */
diff --git a/net/bridge/br_device.c b/net/bridge/br_device.c
index ba829de..d6e5929 100644
--- a/net/bridge/br_device.c
+++ b/net/bridge/br_device.c
@@ -317,6 +317,9 @@ static const struct net_device_ops br_netdev_ops = {
.ndo_add_slave = br_add_slave,
.ndo_del_slave = br_del_slave,
.ndo_fix_features = br_fix_features,
+ .ndo_fdb_add = br_fdb_add,
+ .ndo_fdb_del = br_fdb_delete,
+ .ndo_fdb_dump = br_fdb_dump,
};
static void br_dev_free(struct net_device *dev)
diff --git a/net/bridge/br_fdb.c b/net/bridge/br_fdb.c
index 5ba0c84..2f9dc98 100644
--- a/net/bridge/br_fdb.c
+++ b/net/bridge/br_fdb.c
@@ -535,44 +535,38 @@ errout:
}
/* Dump information about entries, in response to GETNEIGH */
-int br_fdb_dump(struct sk_buff *skb, struct netlink_callback *cb)
+int br_fdb_dump(struct sk_buff *skb,
+ struct netlink_callback *cb,
+ struct net_device *dev,
+ int idx)
{
- struct net *net = sock_net(skb->sk);
- struct net_device *dev;
- int idx = 0;
-
- rcu_read_lock();
- for_each_netdev_rcu(net, dev) {
- struct net_bridge *br = netdev_priv(dev);
- int i;
-
- if (!(dev->priv_flags & IFF_EBRIDGE))
- continue;
+ struct net_bridge *br = netdev_priv(dev);
+ int i;
- for (i = 0; i < BR_HASH_SIZE; i++) {
- struct hlist_node *h;
- struct net_bridge_fdb_entry *f;
+ if (!(dev->priv_flags & IFF_EBRIDGE))
+ goto out;
- hlist_for_each_entry_rcu(f, h, &br->hash[i], hlist) {
- if (idx < cb->args[0])
- goto skip;
+ for (i = 0; i < BR_HASH_SIZE; i++) {
+ struct hlist_node *h;
+ struct net_bridge_fdb_entry *f;
- if (fdb_fill_info(skb, br, f,
- NETLINK_CB(cb->skb).pid,
- cb->nlh->nlmsg_seq,
- RTM_NEWNEIGH,
- NLM_F_MULTI) < 0)
- break;
+ hlist_for_each_entry_rcu(f, h, &br->hash[i], hlist) {
+ if (idx < cb->args[0])
+ goto skip;
+
+ if (fdb_fill_info(skb, br, f,
+ NETLINK_CB(cb->skb).pid,
+ cb->nlh->nlmsg_seq,
+ RTM_NEWNEIGH,
+ NLM_F_MULTI) < 0)
+ break;
skip:
- ++idx;
- }
+ ++idx;
}
}
- rcu_read_unlock();
-
- cb->args[0] = idx;
- return skb->len;
+out:
+ return idx;
}
/* Update (create or replace) forwarding database entry */
@@ -614,43 +608,11 @@ static int fdb_add_entry(struct net_bridge_port *source, const __u8 *addr,
}
/* Add new permanent fdb entry with RTM_NEWNEIGH */
-int br_fdb_add(struct sk_buff *skb, struct nlmsghdr *nlh, void *arg)
+int br_fdb_add(struct ndmsg *ndm, struct net_device *dev,
+ unsigned char *addr, u16 nlh_flags)
{
- struct net *net = sock_net(skb->sk);
- struct ndmsg *ndm;
- struct nlattr *tb[NDA_MAX+1];
- struct net_device *dev;
struct net_bridge_port *p;
- const __u8 *addr;
- int err;
-
- ASSERT_RTNL();
- err = nlmsg_parse(nlh, sizeof(*ndm), tb, NDA_MAX, NULL);
- if (err < 0)
- return err;
-
- ndm = nlmsg_data(nlh);
- if (ndm->ndm_ifindex == 0) {
- pr_info("bridge: RTM_NEWNEIGH with invalid ifindex\n");
- return -EINVAL;
- }
-
- dev = __dev_get_by_index(net, ndm->ndm_ifindex);
- if (dev == NULL) {
- pr_info("bridge: RTM_NEWNEIGH with unknown ifindex\n");
- return -ENODEV;
- }
-
- if (!tb[NDA_LLADDR] || nla_len(tb[NDA_LLADDR]) != ETH_ALEN) {
- pr_info("bridge: RTM_NEWNEIGH with invalid address\n");
- return -EINVAL;
- }
-
- addr = nla_data(tb[NDA_LLADDR]);
- if (!is_valid_ether_addr(addr)) {
- pr_info("bridge: RTM_NEWNEIGH with invalid ether address\n");
- return -EINVAL;
- }
+ int err = 0;
if (!(ndm->ndm_state & (NUD_PERMANENT|NUD_NOARP|NUD_REACHABLE))) {
pr_info("bridge: RTM_NEWNEIGH with invalid state %#x\n", ndm->ndm_state);
@@ -670,14 +632,14 @@ int br_fdb_add(struct sk_buff *skb, struct nlmsghdr *nlh, void *arg)
rcu_read_unlock();
} else {
spin_lock_bh(&p->br->hash_lock);
- err = fdb_add_entry(p, addr, ndm->ndm_state, nlh->nlmsg_flags);
+ err = fdb_add_entry(p, addr, ndm->ndm_state, nlh_flags);
spin_unlock_bh(&p->br->hash_lock);
}
return err;
}
-static int fdb_delete_by_addr(struct net_bridge_port *p, const u8 *addr)
+static int fdb_delete_by_addr(struct net_bridge_port *p, u8 *addr)
{
struct net_bridge *br = p->br;
struct hlist_head *head = &br->hash[br_mac_hash(addr)];
@@ -692,40 +654,12 @@ static int fdb_delete_by_addr(struct net_bridge_port *p, const u8 *addr)
}
/* Remove neighbor entry with RTM_DELNEIGH */
-int br_fdb_delete(struct sk_buff *skb, struct nlmsghdr *nlh, void *arg)
+int br_fdb_delete(struct ndmsg *ndm, struct net_device *dev,
+ unsigned char *addr)
{
- struct net *net = sock_net(skb->sk);
- struct ndmsg *ndm;
struct net_bridge_port *p;
- struct nlattr *llattr;
- const __u8 *addr;
- struct net_device *dev;
int err;
- ASSERT_RTNL();
- if (nlmsg_len(nlh) < sizeof(*ndm))
- return -EINVAL;
-
- ndm = nlmsg_data(nlh);
- if (ndm->ndm_ifindex == 0) {
- pr_info("bridge: RTM_DELNEIGH with invalid ifindex\n");
- return -EINVAL;
- }
-
- dev = __dev_get_by_index(net, ndm->ndm_ifindex);
- if (dev == NULL) {
- pr_info("bridge: RTM_DELNEIGH with unknown ifindex\n");
- return -ENODEV;
- }
-
- llattr = nlmsg_find_attr(nlh, sizeof(*ndm), NDA_LLADDR);
- if (llattr == NULL || nla_len(llattr) != ETH_ALEN) {
- pr_info("bridge: RTM_DELNEIGH with invalid address\n");
- return -EINVAL;
- }
-
- addr = nla_data(llattr);
-
p = br_port_get_rtnl(dev);
if (p == NULL) {
pr_info("bridge: RTM_DELNEIGH %s not a bridge port\n",
diff --git a/net/bridge/br_netlink.c b/net/bridge/br_netlink.c
index a1daf82..15f74fc 100644
--- a/net/bridge/br_netlink.c
+++ b/net/bridge/br_netlink.c
@@ -235,18 +235,6 @@ int __init br_netlink_init(void)
br_rtm_setlink, NULL, NULL);
if (err)
goto err3;
- err = __rtnl_register(PF_BRIDGE, RTM_NEWNEIGH,
- br_fdb_add, NULL, NULL);
- if (err)
- goto err3;
- err = __rtnl_register(PF_BRIDGE, RTM_DELNEIGH,
- br_fdb_delete, NULL, NULL);
- if (err)
- goto err3;
- err = __rtnl_register(PF_BRIDGE, RTM_GETNEIGH,
- NULL, br_fdb_dump, NULL);
- if (err)
- goto err3;
return 0;
diff --git a/net/bridge/br_private.h b/net/bridge/br_private.h
index 0b67a63..929b9f6 100644
--- a/net/bridge/br_private.h
+++ b/net/bridge/br_private.h
@@ -363,9 +363,18 @@ extern int br_fdb_insert(struct net_bridge *br,
extern void br_fdb_update(struct net_bridge *br,
struct net_bridge_port *source,
const unsigned char *addr);
-extern int br_fdb_dump(struct sk_buff *skb, struct netlink_callback *cb);
-extern int br_fdb_add(struct sk_buff *skb, struct nlmsghdr *nlh, void *arg);
-extern int br_fdb_delete(struct sk_buff *skb, struct nlmsghdr *nlh, void *arg);
+
+extern int br_fdb_delete(struct ndmsg *ndm,
+ struct net_device *dev,
+ unsigned char *addr);
+extern int br_fdb_add(struct ndmsg *nlh,
+ struct net_device *dev,
+ unsigned char *addr,
+ u16 nlh_flags);
+extern int br_fdb_dump(struct sk_buff *skb,
+ struct netlink_callback *cb,
+ struct net_device *dev,
+ int idx);
/* br_forward.c */
extern void br_deliver(const struct net_bridge_port *to,
diff --git a/net/core/rtnetlink.c b/net/core/rtnetlink.c
index 1a63c6e..8c3278a 100644
--- a/net/core/rtnetlink.c
+++ b/net/core/rtnetlink.c
@@ -35,7 +35,9 @@
#include <linux/security.h>
#include <linux/mutex.h>
#include <linux/if_addr.h>
+#include <linux/if_bridge.h>
#include <linux/pci.h>
+#include <linux/etherdevice.h>
#include <asm/uaccess.h>
#include <asm/system.h>
@@ -1973,6 +1975,138 @@ errout:
rtnl_set_sk_err(net, RTNLGRP_LINK, err);
}
+static int rtnl_fdb_add(struct sk_buff *skb, struct nlmsghdr *nlh, void *arg)
+{
+ struct net *net = sock_net(skb->sk);
+ struct ndmsg *ndm;
+ struct nlattr *tb[NDA_MAX+1];
+ struct net_device *dev;
+ u8 *addr;
+ int err;
+
+ err = nlmsg_parse(nlh, sizeof(*ndm), tb, NDA_MAX, NULL);
+ if (err < 0)
+ return err;
+
+ ndm = nlmsg_data(nlh);
+ if (ndm->ndm_ifindex == 0) {
+ pr_info("PF_BRIDGE: RTM_NEWNEIGH with invalid ifindex\n");
+ return -EINVAL;
+ }
+
+ dev = __dev_get_by_index(net, ndm->ndm_ifindex);
+ if (dev == NULL) {
+ pr_info("PF_BRIDGE: RTM_NEWNEIGH with unknown ifindex\n");
+ return -ENODEV;
+ }
+
+ if (!tb[NDA_LLADDR] || nla_len(tb[NDA_LLADDR]) != ETH_ALEN) {
+ pr_info("PF_BRIDGE: RTM_NEWNEIGH with invalid address\n");
+ return -EINVAL;
+ }
+
+ addr = nla_data(tb[NDA_LLADDR]);
+ if (!is_valid_ether_addr(addr)) {
+ pr_info("PF_BRIDGE: RTM_NEWNEIGH with invalid ether address\n");
+ return -EINVAL;
+ }
+
+ err = -EOPNOTSUPP;
+
+ /* Support software bridge default usage */
+ if ((!ndm->ndm_flags || ndm->ndm_flags & NTF_MASTER) &&
+ (dev->priv_flags & IFF_BRIDGE_PORT)) {
+ struct net_device *master = dev->master;
+
+ if (master->netdev_ops->ndo_fdb_add)
+ err = master->netdev_ops->ndo_fdb_add(ndm, dev, addr,
+ nlh->nlmsg_flags);
+ }
+
+ /* Embedded bridge, macvlan, and any other device support */
+ if ((ndm->ndm_flags & NTF_LOWERDEV) &&
+ dev->netdev_ops->ndo_fdb_add)
+ err = dev->netdev_ops->ndo_fdb_add(ndm, dev, addr,
+ nlh->nlmsg_flags);
+
+ return err;
+}
+
+static int rtnl_fdb_del(struct sk_buff *skb, struct nlmsghdr *nlh, void *arg)
+{
+ struct net *net = sock_net(skb->sk);
+ struct ndmsg *ndm;
+ struct nlattr *llattr;
+ struct net_device *dev;
+ int err = -EINVAL;
+ __u8 *addr;
+
+ if (nlmsg_len(nlh) < sizeof(*ndm))
+ return -EINVAL;
+
+ ndm = nlmsg_data(nlh);
+ if (ndm->ndm_ifindex == 0) {
+ pr_info("PF_BRIDGE: RTM_DELNEIGH with invalid ifindex\n");
+ return -EINVAL;
+ }
+
+ dev = __dev_get_by_index(net, ndm->ndm_ifindex);
+ if (dev == NULL) {
+ pr_info("PF_BRIDGE: RTM_DELNEIGH with unknown ifindex\n");
+ return -ENODEV;
+ }
+
+ llattr = nlmsg_find_attr(nlh, sizeof(*ndm), NDA_LLADDR);
+ if (llattr == NULL || nla_len(llattr) != ETH_ALEN) {
+ pr_info("PF_BRIGDE: RTM_DELNEIGH with invalid address\n");
+ return -EINVAL;
+ }
+
+ addr = nla_data(llattr);
+ err = -EOPNOTSUPP;
+
+ /* Support software bridge default usage */
+ if ((!ndm->ndm_flags || ndm->ndm_flags & NTF_MASTER) &&
+ (dev->priv_flags & IFF_BRIDGE_PORT)) {
+ struct net_device *master = dev->master;
+
+ if (master->netdev_ops->ndo_fdb_del)
+ err = master->netdev_ops->ndo_fdb_del(ndm, dev, addr);
+ }
+
+ /* Embedded bridge, macvlan, and any other device support */
+ if ((ndm->ndm_flags & NTF_LOWERDEV) &&
+ dev->netdev_ops->ndo_fdb_del)
+ err = dev->netdev_ops->ndo_fdb_del(ndm, dev, addr);
+
+ return err;
+}
+
+static int rtnl_fdb_dump(struct sk_buff *skb, struct netlink_callback *cb)
+{
+ int idx = 0;
+ struct net *net = sock_net(skb->sk);
+ struct net_device *dev;
+
+ rcu_read_lock();
+ for_each_netdev_rcu(net, dev) {
+ if (dev->priv_flags & IFF_BRIDGE_PORT) {
+ struct net_device *master = dev->master;
+ const struct net_device_ops *ops = master->netdev_ops;
+
+ if (ops->ndo_fdb_dump)
+ idx = ops->ndo_fdb_dump(skb, cb, dev, idx);
+ }
+
+ if (dev->netdev_ops->ndo_fdb_dump)
+ idx = dev->netdev_ops->ndo_fdb_dump(skb, cb, dev, idx);
+ }
+ rcu_read_unlock();
+
+ cb->args[0] = idx;
+ return skb->len;
+}
+
/* Protected by RTNL sempahore. */
static struct rtattr **rta_buf;
static int rtattr_max;
@@ -2145,5 +2279,9 @@ void __init rtnetlink_init(void)
rtnl_register(PF_UNSPEC, RTM_GETADDR, NULL, rtnl_dump_all, NULL);
rtnl_register(PF_UNSPEC, RTM_GETROUTE, NULL, rtnl_dump_all, NULL);
+
+ rtnl_register(PF_BRIDGE, RTM_NEWNEIGH, rtnl_fdb_add, NULL, NULL);
+ rtnl_register(PF_BRIDGE, RTM_DELNEIGH, rtnl_fdb_del, NULL, NULL);
+ rtnl_register(PF_BRIDGE, RTM_GETNEIGH, NULL, rtnl_fdb_dump, NULL);
}
^ permalink raw reply related
* [net-next PATCH v0 2/5] net: addr_list: add exclusive dev_uc_add
From: John Fastabend @ 2012-03-19 6:51 UTC (permalink / raw)
To: jhs, shemminger, bhutchings, roprabhu, davem, jeffrey.t.kirsher
Cc: netdev, mst, chrisw, gregory.v.rose, kvm, sri, chealy
In-Reply-To: <20120319064719.10959.93361.stgit@jf-dev1-dcblab>
This adds a dev_uc_add_excl() call similar to the original
dev_uc_add() except it sets the global bit. With this
change the reference count will not be bumped and -EEXIST
will be returned if a duplicate address exists.
This is useful for drivers that support SR-IOV and want
to manage the unicast lists.
Signed-off-by: John Fastabend <john.r.fastabend@intel.com>
---
include/linux/netdevice.h | 1 +
net/core/dev_addr_lists.c | 19 +++++++++++++++++++
2 files changed, 20 insertions(+), 0 deletions(-)
diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h
index 4208901..5e43cec 100644
--- a/include/linux/netdevice.h
+++ b/include/linux/netdevice.h
@@ -2571,6 +2571,7 @@ extern int dev_addr_init(struct net_device *dev);
/* Functions used for unicast addresses handling */
extern int dev_uc_add(struct net_device *dev, unsigned char *addr);
+extern int dev_uc_add_excl(struct net_device *dev, unsigned char *addr);
extern int dev_uc_del(struct net_device *dev, unsigned char *addr);
extern int dev_uc_sync(struct net_device *to, struct net_device *from);
extern void dev_uc_unsync(struct net_device *to, struct net_device *from);
diff --git a/net/core/dev_addr_lists.c b/net/core/dev_addr_lists.c
index 29c07fe..c7d27ad 100644
--- a/net/core/dev_addr_lists.c
+++ b/net/core/dev_addr_lists.c
@@ -377,6 +377,25 @@ EXPORT_SYMBOL(dev_addr_del_multiple);
*/
/**
+ * dev_uc_add_excl - Add a global secondary unicast address
+ * @dev: device
+ * @addr: address to add
+ */
+int dev_uc_add_excl(struct net_device *dev, unsigned char *addr)
+{
+ int err;
+
+ netif_addr_lock_bh(dev);
+ err = __hw_addr_add_ex(&dev->uc, addr, dev->addr_len,
+ NETDEV_HW_ADDR_T_UNICAST, true);
+ if (!err)
+ __dev_set_rx_mode(dev);
+ netif_addr_unlock_bh(dev);
+ return err;
+}
+EXPORT_SYMBOL(dev_uc_add_excl);
+
+/**
* dev_uc_add - Add a secondary unicast address
* @dev: device
* @addr: address to add
^ permalink raw reply related
* [net-next PATCH v0 0/5] Series short description
From: John Fastabend @ 2012-03-19 6:51 UTC (permalink / raw)
To: jhs, shemminger, bhutchings, roprabhu, davem, jeffrey.t.kirsher
Cc: netdev, mst, chrisw, gregory.v.rose, kvm, sri, chealy
This series is a follow up to this thread:
http://www.spinics.net/lists/netdev/msg191360.html
This adds two NTF_XXX bits to signal if the PF_BRIDGE
netlink command should be parsed by the embedded bridge
or the SW bridge. The insight here is the SW bridge is
always the master device (NTF_MASTER) and the embedded
bridge is the lower device (NTF_LOWERDEV). Without either
flag set the command is parsed by the SW bridge to support
existing tooling.
To make this work correctly I added three new ndo ops
ndo_fdb_add
ndo_fdb_del
ndo_fdb_dump
to add, delete, and dump FDB entries. These operations
can be used by drivers to program embedded nics or by
software bridges. We have at least three SW bridge now
net/bridge, openvswitch, and macvlan. And three variants
of embedded bridges SR-IOV devices, multi-function devices
and Distributed Switch Architecture (DSA).
I think at least in this case adding netdevice ops is
the cleanest way to implement this. I thought about
notifier hooks and other methods but this seems to be
the simplest.
I've tested these three scenarios, embedded bridge only,
sw bridge only, and embedded bridge and SW bridge. These
are working on the Intel 82599 devices with this patch
series. I am also working on a patch for the macvlan
drivers. I'll submit that as an RFC shortly so far I
only have the passthru mode wired up.
Thanks to Stephen, Ben, and Jamal for bearing with me
and the feedback on the last round of patches.
As always any comments/feedback appreciated!
---
John Fastabend (5):
ixgbe: allow RAR table to be updated in promisc mode
ixgbe: enable FDB netdevice ops
net: add fdb generic dump routine
net: addr_list: add exclusive dev_uc_add
net: add generic PF_BRIDGE:RTM_XXX FDB hooks
drivers/net/ethernet/intel/ixgbe/ixgbe_main.c | 80 +++++++++-
include/linux/neighbour.h | 3
include/linux/netdevice.h | 27 +++
include/linux/rtnetlink.h | 4 +
net/bridge/br_device.c | 3
net/bridge/br_fdb.c | 128 ++++------------
net/bridge/br_netlink.c | 12 --
net/bridge/br_private.h | 15 ++
net/core/dev_addr_lists.c | 19 ++
net/core/rtnetlink.c | 194 +++++++++++++++++++++++++
10 files changed, 363 insertions(+), 122 deletions(-)
--
Signature
^ permalink raw reply
* [net-next PATCH v0 5/5] ixgbe: allow RAR table to be updated in promisc mode
From: John Fastabend @ 2012-03-19 6:52 UTC (permalink / raw)
To: jhs, shemminger, bhutchings, roprabhu, davem, jeffrey.t.kirsher
Cc: netdev, mst, chrisw, gregory.v.rose, kvm, sri, chealy
In-Reply-To: <20120319064719.10959.93361.stgit@jf-dev1-dcblab>
This allows RAR table updates while in promiscuous. With
SR-IOV enabled it is valuable to allow the RAR table to
be updated even when in promisc mode to configure forwarding
Signed-off-by: John Fastabend <john.r.fastabend@intel.com>
---
drivers/net/ethernet/intel/ixgbe/ixgbe_main.c | 21 +++++++++++----------
1 files changed, 11 insertions(+), 10 deletions(-)
diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c b/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c
index 32adb4f..d1925b5 100644
--- a/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c
+++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c
@@ -3400,16 +3400,17 @@ void ixgbe_set_rx_mode(struct net_device *netdev)
}
ixgbe_vlan_filter_enable(adapter);
hw->addr_ctrl.user_set_promisc = false;
- /*
- * Write addresses to available RAR registers, if there is not
- * sufficient space to store all the addresses then enable
- * unicast promiscuous mode
- */
- count = ixgbe_write_uc_addr_list(netdev);
- if (count < 0) {
- fctrl |= IXGBE_FCTRL_UPE;
- vmolr |= IXGBE_VMOLR_ROPE;
- }
+ }
+
+ /*
+ * Write addresses to available RAR registers, if there is not
+ * sufficient space to store all the addresses then enable
+ * unicast promiscuous mode
+ */
+ count = ixgbe_write_uc_addr_list(netdev);
+ if (count < 0) {
+ fctrl |= IXGBE_FCTRL_UPE;
+ vmolr |= IXGBE_VMOLR_ROPE;
}
if (adapter->num_vfs) {
^ permalink raw reply related
* [net-next PATCH v0 4/5] ixgbe: enable FDB netdevice ops
From: John Fastabend @ 2012-03-19 6:52 UTC (permalink / raw)
To: jhs, shemminger, bhutchings, roprabhu, davem, jeffrey.t.kirsher
Cc: netdev, mst, chrisw, gregory.v.rose, kvm, sri, chealy
In-Reply-To: <20120319064719.10959.93361.stgit@jf-dev1-dcblab>
Enable FDB ops on ixgbe when in SR-IOV mode.
Signed-off-by: John Fastabend <john.r.fastabend@intel.com>
---
drivers/net/ethernet/intel/ixgbe/ixgbe_main.c | 59 +++++++++++++++++++++++++
1 files changed, 59 insertions(+), 0 deletions(-)
diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c b/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c
index 1d8f9f8..32adb4f 100644
--- a/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c
+++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c
@@ -7586,6 +7586,62 @@ static int ixgbe_set_features(struct net_device *netdev,
}
+static int ixgbe_ndo_fdb_add(struct ndmsg *ndm,
+ struct net_device *dev,
+ unsigned char *addr,
+ u16 flags)
+{
+ struct ixgbe_adapter *adapter = netdev_priv(dev);
+ int err = -EOPNOTSUPP;
+
+ if (ndm->ndm_state & NUD_PERMANENT) {
+ pr_info("%s: FDB only supports static addresses\n",
+ ixgbe_driver_name);
+ return -EINVAL;
+ }
+
+ if (adapter->flags & IXGBE_FLAG_SRIOV_ENABLED)
+ err = dev_uc_add_excl(dev, addr);
+
+ /* Only return duplicate errors if NLM_F_EXCL is set */
+ if (err == -EEXIST && !(flags & NLM_F_EXCL))
+ err = 0;
+
+ return err;
+}
+
+static int ixgbe_ndo_fdb_del(struct ndmsg *ndm,
+ struct net_device *dev,
+ unsigned char *addr)
+{
+ struct ixgbe_adapter *adapter = netdev_priv(dev);
+ int err = -EOPNOTSUPP;
+
+ if (ndm->ndm_state & NUD_PERMANENT) {
+ pr_info("%s: FDB only supports static addresses\n",
+ ixgbe_driver_name);
+ return -EINVAL;
+ }
+
+ if (adapter->flags & IXGBE_FLAG_SRIOV_ENABLED)
+ err = dev_uc_del(dev, addr);
+
+ return err;
+}
+
+static int ixgbe_ndo_fdb_dump(struct sk_buff *skb,
+ struct netlink_callback *cb,
+ struct net_device *dev,
+ int idx)
+{
+ struct ixgbe_adapter *adapter = netdev_priv(dev);
+
+ if (adapter->flags & IXGBE_FLAG_SRIOV_ENABLED)
+ idx = ndo_dflt_fdb_dump(skb, cb, dev, idx);
+
+ return idx;
+}
+
static const struct net_device_ops ixgbe_netdev_ops = {
.ndo_open = ixgbe_open,
.ndo_stop = ixgbe_close,
@@ -7620,6 +7676,9 @@ static const struct net_device_ops ixgbe_netdev_ops = {
#endif /* IXGBE_FCOE */
.ndo_set_features = ixgbe_set_features,
.ndo_fix_features = ixgbe_fix_features,
+ .ndo_fdb_add = ixgbe_ndo_fdb_add,
+ .ndo_fdb_del = ixgbe_ndo_fdb_del,
+ .ndo_fdb_dump = ixgbe_ndo_fdb_dump,
};
static void __devinit ixgbe_probe_vf(struct ixgbe_adapter *adapter,
^ permalink raw reply related
* [net-next PATCH v0 3/5] net: add fdb generic dump routine
From: John Fastabend @ 2012-03-19 6:52 UTC (permalink / raw)
To: jhs, shemminger, bhutchings, roprabhu, davem, jeffrey.t.kirsher
Cc: netdev, mst, chrisw, gregory.v.rose, kvm, sri, chealy
In-Reply-To: <20120319064719.10959.93361.stgit@jf-dev1-dcblab>
This adds a generic dump routine drivers can call. It
should be sufficient to handle any bridging model that
uses the unicast address list. This should be most SR-IOV
enabled NICs.
Signed-off-by: John Fastabend <john.r.fastabend@intel.com>
---
net/core/rtnetlink.c | 56 ++++++++++++++++++++++++++++++++++++++++++++++++++
1 files changed, 56 insertions(+), 0 deletions(-)
diff --git a/net/core/rtnetlink.c b/net/core/rtnetlink.c
index 8c3278a..35ee2d6 100644
--- a/net/core/rtnetlink.c
+++ b/net/core/rtnetlink.c
@@ -2082,6 +2082,62 @@ static int rtnl_fdb_del(struct sk_buff *skb, struct nlmsghdr *nlh, void *arg)
return err;
}
+/**
+ * ndo_dflt_fdb_dump: default netdevice operation to dump an FDB table.
+ * @nlh: netlink message header
+ * @dev: netdevice
+ *
+ * Default netdevice operation to dump the existing unicast address list.
+ * Returns zero on success.
+ */
+int ndo_dflt_fdb_dump(struct sk_buff *skb,
+ struct netlink_callback *cb,
+ struct net_device *dev,
+ int idx)
+{
+ struct netdev_hw_addr *ha;
+ struct nlmsghdr *nlh;
+ struct ndmsg *ndm;
+ u32 pid, seq;
+
+ pid = NETLINK_CB(cb->skb).pid;
+ seq = cb->nlh->nlmsg_seq;
+
+ netif_addr_lock_bh(dev);
+ list_for_each_entry(ha, &dev->uc.list, list) {
+ if (idx < cb->args[0])
+ goto skip;
+
+ nlh = nlmsg_put(skb, pid, seq,
+ RTM_NEWNEIGH, sizeof(*ndm), NLM_F_MULTI);
+ if (!nlh)
+ break;
+
+ ndm = nlmsg_data(nlh);
+ ndm->ndm_family = AF_BRIDGE;
+ ndm->ndm_pad1 = 0;
+ ndm->ndm_pad2 = 0;
+ ndm->ndm_flags = NTF_LOWERDEV;
+ ndm->ndm_type = 0;
+ ndm->ndm_ifindex = dev->ifindex;
+ ndm->ndm_state = NUD_PERMANENT;
+
+ NLA_PUT(skb, NDA_LLADDR, ETH_ALEN, ha->addr);
+
+ nlmsg_end(skb, nlh);
+skip:
+ ++idx;
+ }
+ netif_addr_unlock_bh(dev);
+
+ return idx;
+nla_put_failure:
+ netif_addr_unlock_bh(dev);
+ nlmsg_cancel(skb, nlh);
+ return idx;
+}
+EXPORT_SYMBOL(ndo_dflt_fdb_dump);
+
static int rtnl_fdb_dump(struct sk_buff *skb, struct netlink_callback *cb)
{
int idx = 0;
^ permalink raw reply related
* [PATCH v5 2/2] netdev: driver: ethernet: Add TI CPSW driver
From: Mugunthan V N @ 2012-03-19 6:17 UTC (permalink / raw)
To: netdev; +Cc: davem, mugunthanvnm
In-Reply-To: <1332137874-3247-1-git-send-email-mugunthanvnm@ti.com>
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 <cyril@ti.com>
Signed-off-by: Sriramakrishnan A G <srk@ti.com>
Signed-off-by: Mugunthan V N <mugunthanvnm@ti.com>
---
drivers/net/ethernet/ti/Kconfig | 11 +
drivers/net/ethernet/ti/Makefile | 2 +
drivers/net/ethernet/ti/cpsw.c | 1018 ++++++++++++++++++++++++++++++++++++
include/linux/platform_data/cpsw.h | 55 ++
4 files changed, 1086 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..c68c9d9
--- /dev/null
+++ b/drivers/net/ethernet/ti/cpsw.c
@@ -0,0 +1,1018 @@
+/*
+ * 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 cpsw_info(priv, type, format, ...) \
+do { \
+ if (netif_msg_##type(priv) && net_ratelimit()) \
+ dev_info(priv->dev, format, ## __VA_ARGS__); \
+} while (0)
+
+#define cpsw_err(priv, type, format, ...) \
+do { \
+ if (netif_msg_##type(priv) && net_ratelimit()) \
+ dev_err(priv->dev, format, ## __VA_ARGS__); \
+} while (0)
+
+#define cpsw_dbg(priv, type, format, ...) \
+do { \
+ if (netif_msg_##type(priv) && net_ratelimit()) \
+ dev_dbg(priv->dev, format, ## __VA_ARGS__); \
+} while (0)
+
+#define cpsw_notice(priv, type, format, ...) \
+do { \
+ if (netif_msg_##type(priv) && net_ratelimit()) \
+ dev_notice(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_dma_regs(base, offset) \
+ (void __iomem *)((base) + (offset))
+#define cpsw_dma_rxthresh(base, offset) \
+ (void __iomem *)((base) + (offset) + CPDMA_RXTHRESH)
+#define cpsw_dma_rxfree(base, offset) \
+ (void __iomem *)((base) + (offset) + CPDMA_RXFREE)
+#define cpsw_dma_txhdp(base, offset) \
+ (void __iomem *)((base) + (offset) + CPDMA_TXHDP)
+#define cpsw_dma_rxhdp(base, offset) \
+ (void __iomem *)((base) + (offset) + CPDMA_RXHDP)
+#define cpsw_dma_txcp(base, offset) \
+ (void __iomem *)((base) + (offset) + CPDMA_TXCP)
+#define cpsw_dma_rxcp(base, offset) \
+ (void __iomem *)((base) + (offset) + CPDMA_RXCP)
+
+#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_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;
+ struct device *dev;
+ struct cpsw_platform_data data;
+ struct cpsw_regs __iomem *regs;
+ struct cpsw_ss_regs __iomem *ss_regs;
+ 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;
+ struct cpdma_ctlr *dma;
+ struct cpdma_chan *txch, *rxch;
+ struct cpsw_ale *ale;
+ /* snapshot of IRQ numbers */
+ u32 irqs_table[4];
+ u32 num_irqs;
+};
+
+#define napi_to_priv(napi) container_of(napi, struct cpsw_priv, napi)
+#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)
+
+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)
+ cpsw_dbg(priv, 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;
+ /* disable 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)) {
+ dev_err(priv->dev, "phy %s not found on slave %d\n",
+ slave->data->phy_id, slave->slave_num);
+ slave->phy = NULL;
+ } else {
+ dev_info(priv->dev, "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);
+
+ dev_info(priv->dev, "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 */
+ cpsw_info(priv, 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);
+
+ cpsw_info(priv, 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)) {
+ cpsw_err(priv, tx_err, "packet pad failed\n");
+ 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)) {
+ cpsw_err(priv, tx_err, "desc submit failed\n");
+ 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);
+
+ cpsw_err(priv, tx_err, "transmit timeout, restarting dma\n");
+ 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("platform data missing\n");
+ return -ENODEV;
+ }
+
+ ndev = alloc_etherdev(sizeof(struct cpsw_priv));
+ if (!ndev) {
+ pr_err("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);
+ pr_info("Detected MACID = %pM", priv->mac_addr);
+ } else {
+ random_ether_addr(priv->mac_addr);
+ pr_info("Random MACID = %pM", priv->mac_addr);
+ }
+
+ memcpy(ndev->dev_addr, priv->mac_addr, ETH_ALEN);
+
+ priv->slaves = kzalloc(sizeof(struct cpsw_slave) * data->slaves,
+ GFP_KERNEL);
+ if (!priv->slaves) {
+ 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->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 = cpsw_dma_regs((u32)priv->regs,
+ data->cpdma_reg_ofs);
+ dma_params.rxthresh = cpsw_dma_rxthresh((u32)priv->regs,
+ data->cpdma_reg_ofs);
+ dma_params.rxfree = cpsw_dma_rxfree((u32)priv->regs,
+ data->cpdma_reg_ofs);
+ dma_params.txhdp = cpsw_dma_txhdp((u32)priv->regs,
+ data->cpdma_sram_ofs);
+ dma_params.rxhdp = cpsw_dma_rxhdp((u32)priv->regs,
+ data->cpdma_sram_ofs);
+ dma_params.txcp = cpsw_dma_txcp((u32)priv->regs,
+ data->cpdma_sram_ofs);
+ dma_params.rxcp = cpsw_dma_rxcp((u32)priv->regs,
+ data->cpdma_sram_ofs);
+
+ 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;
+ }
+
+ cpsw_notice(priv, 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);
+
+ pr_info("removing device");
+ 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_AUTHOR("Cyril Chemparathy <cyril@ti.com>");
+MODULE_AUTHOR("Mugunthan V N <mugunthanvnm@ti.com>");
+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
^ permalink raw reply related
* [PATCH v5 1/2] netdev: driver: ethernet: add cpsw address lookup engine support
From: Mugunthan V N @ 2012-03-19 6:17 UTC (permalink / raw)
To: netdev; +Cc: davem, mugunthanvnm
In-Reply-To: <1332137874-3247-1-git-send-email-mugunthanvnm@ti.com>
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 <cyril@ti.com>
Signed-off-by: Mugunthan V N <mugunthanvnm@ti.com>
---
drivers/net/ethernet/ti/cpsw_ale.c | 641 ++++++++++++++++++++++++++++++++++++
drivers/net/ethernet/ti/cpsw_ale.h | 93 ++++++
2 files changed, 734 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..ca0d48a
--- /dev/null
+++ b/drivers/net/ethernet/ti/cpsw_ale.c
@@ -0,0 +1,641 @@
+/*
+ * 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;
+};
+
+static const struct ale_control_info ale_controls[ALE_NUM_CONTROLS] = {
+ [ALE_ENABLE] = {
+ .name = "enable",
+ .offset = ALE_CONTROL,
+ .port_offset = 0,
+ .shift = 31,
+ .port_shift = 0,
+ .bits = 1,
+ },
+ [ALE_CLEAR] = {
+ .name = "clear",
+ .offset = ALE_CONTROL,
+ .port_offset = 0,
+ .shift = 30,
+ .port_shift = 0,
+ .bits = 1,
+ },
+ [ALE_AGEOUT] = {
+ .name = "ageout",
+ .offset = ALE_CONTROL,
+ .port_offset = 0,
+ .shift = 29,
+ .port_shift = 0,
+ .bits = 1,
+ },
+ [ALE_VLAN_NOLEARN] = {
+ .name = "vlan_nolearn",
+ .offset = ALE_CONTROL,
+ .port_offset = 0,
+ .shift = 7,
+ .port_shift = 0,
+ .bits = 1,
+ },
+ [ALE_NO_PORT_VLAN] = {
+ .name = "no_port_vlan",
+ .offset = ALE_CONTROL,
+ .port_offset = 0,
+ .shift = 6,
+ .port_shift = 0,
+ .bits = 1,
+ },
+ [ALE_OUI_DENY] = {
+ .name = "oui_deny",
+ .offset = ALE_CONTROL,
+ .port_offset = 0,
+ .shift = 5,
+ .port_shift = 0,
+ .bits = 1,
+ },
+ [ALE_BYPASS] = {
+ .name = "bypass",
+ .offset = ALE_CONTROL,
+ .port_offset = 0,
+ .shift = 4,
+ .port_shift = 0,
+ .bits = 1,
+ },
+ [ALE_RATE_LIMIT_TX] = {
+ .name = "rate_limit_tx",
+ .offset = ALE_CONTROL,
+ .port_offset = 0,
+ .shift = 3,
+ .port_shift = 0,
+ .bits = 1,
+ },
+ [ALE_VLAN_AWARE] = {
+ .name = "vlan_aware",
+ .offset = ALE_CONTROL,
+ .port_offset = 0,
+ .shift = 2,
+ .port_shift = 0,
+ .bits = 1,
+ },
+ [ALE_AUTH_ENABLE] = {
+ .name = "auth_enable",
+ .offset = ALE_CONTROL,
+ .port_offset = 0,
+ .shift = 1,
+ .port_shift = 0,
+ .bits = 1,
+ },
+ [ALE_RATE_LIMIT] = {
+ .name = "rate_limit",
+ .offset = ALE_CONTROL,
+ .port_offset = 0,
+ .shift = 0,
+ .port_shift = 0,
+ .bits = 1,
+ },
+ [ALE_PORT_STATE] = {
+ .name = "port_state",
+ .offset = ALE_PORTCTL,
+ .port_offset = 4,
+ .shift = 0,
+ .port_shift = 0,
+ .bits = 2,
+ },
+ [ALE_PORT_DROP_UNTAGGED] = {
+ .name = "drop_untagged",
+ .offset = ALE_PORTCTL,
+ .port_offset = 4,
+ .shift = 2,
+ .port_shift = 0,
+ .bits = 1,
+ },
+ [ALE_PORT_DROP_UNKNOWN_VLAN] = {
+ .name = "drop_unknown",
+ .offset = ALE_PORTCTL,
+ .port_offset = 4,
+ .shift = 3,
+ .port_shift = 0,
+ .bits = 1,
+ },
+ [ALE_PORT_NOLEARN] = {
+ .name = "nolearn",
+ .offset = ALE_PORTCTL,
+ .port_offset = 4,
+ .shift = 4,
+ .port_shift = 0,
+ .bits = 1,
+ },
+ [ALE_PORT_MCAST_LIMIT] = {
+ .name = "mcast_limit",
+ .offset = ALE_PORTCTL,
+ .port_offset = 4,
+ .shift = 16,
+ .port_shift = 0,
+ .bits = 8,
+ },
+ [ALE_PORT_BCAST_LIMIT] = {
+ .name = "bcast_limit",
+ .offset = ALE_PORTCTL,
+ .port_offset = 4,
+ .shift = 24,
+ .port_shift = 0,
+ .bits = 8,
+ },
+ [ALE_PORT_UNKNOWN_VLAN_MEMBER] = {
+ .name = "unknown_vlan_member",
+ .offset = ALE_UNKNOWNVLAN,
+ .port_offset = 0,
+ .shift = 0,
+ .port_shift = 0,
+ .bits = 6,
+ },
+ [ALE_PORT_UNKNOWN_MCAST_FLOOD] = {
+ .name = "unknown_mcast_flood",
+ .offset = ALE_UNKNOWNVLAN,
+ .port_offset = 0,
+ .shift = 8,
+ .port_shift = 0,
+ .bits = 6,
+ },
+ [ALE_PORT_UNKNOWN_REG_MCAST_FLOOD] = {
+ .name = "unknown_reg_flood",
+ .offset = ALE_UNKNOWNVLAN,
+ .port_offset = 0,
+ .shift = 16,
+ .port_shift = 0,
+ .bits = 6,
+ },
+ [ALE_PORT_UNTAGGED_EGRESS] = {
+ .name = "untagged_egress",
+ .offset = ALE_UNKNOWNVLAN,
+ .port_offset = 0,
+ .shift = 24,
+ .port_shift = 0,
+ .bits = 6,
+ },
+};
+
+int cpsw_ale_control_set(struct cpsw_ale *ale, int port, int control,
+ int value)
+{
+ const struct ale_control_info *info;
+ int offset, shift;
+ u32 tmp, mask;
+
+ if (control < 0 || control >= ARRAY_SIZE(ale_controls))
+ return -EINVAL;
+
+ info = &ale_controls[control];
+ 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)
+{
+ const struct ale_control_info *info;
+ int offset, shift;
+ u32 tmp;
+
+ if (control < 0 || control >= ARRAY_SIZE(ale_controls))
+ return -EINVAL;
+
+ info = &ale_controls[control];
+ 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;
+
+ ale = kzalloc(sizeof(*ale), GFP_KERNEL);
+ if (!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..a95b37b
--- /dev/null
+++ b/drivers/net/ethernet/ti/cpsw_ale.h
@@ -0,0 +1,93 @@
+/*
+ * 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;
+};
+
+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
^ permalink raw reply related
* [PATCH v5 0/2] Adding new TI Common Platform ethernet SWitch driver
From: Mugunthan V N @ 2012-03-19 6:17 UTC (permalink / raw)
To: netdev; +Cc: davem, mugunthanvnm
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 v4:
* Handled Kzalloc failure in cpsw_ale_create
* Removed pr_fmt as it is not used in driver
* removed "cpsw" prefix as it is not required
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 | 1018 ++++++++++++++++++++++++++++++++++++
drivers/net/ethernet/ti/cpsw_ale.c | 641 +++++++++++++++++++++++
drivers/net/ethernet/ti/cpsw_ale.h | 93 ++++
include/linux/platform_data/cpsw.h | 55 ++
6 files changed, 1820 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
^ permalink raw reply
* Re: [PATCH wireless-next 2/3] ath5k: Introduce _ath5k_printk to reduce code/text
From: Joe Perches @ 2012-03-19 5:18 UTC (permalink / raw)
To: Adrian Chadd
Cc: Jiri Slaby, Nick Kossifidis, Luis R. Rodriguez, Bob Copeland,
John W. Linville, linux-wireless, ath5k-devel, netdev,
linux-kernel
In-Reply-To: <CAJ-Vmo=ceSV38vQGfs8cKJScWjYF4kbQdK3+JJvQLCmnURaEwA@mail.gmail.com>
On Sun, 2012-03-18 at 21:36 -0700, Adrian Chadd wrote:
> Hi,
Hi Adrian.
> So the reason this is a macro in the FreeBSD HAL is so that the args
> aren't evaluated unless the level (or debug bitmap in my case) fires
> off.
>
> Otherwise compiling in debugging will cause a _lot_ of spurious
> register reads to occur that are then tossed. This was one of the big
> reasons for instability and slow performance when AH_DEBUG was
> enabled.
That doesn't make any sense in this case.
It's either a call to printk or _ath5_printk
but it's still a call to a function.
+void __printf(3, 4)
+_ath5k_printk(const struct ath5k_hw *ah, const char *level,
+ const char *fmt, ...);
+
#define ATH5K_PRINTK(_sc, _level, _fmt, ...) \
- printk(_level pr_fmt("%s%s" _fmt), \
- ((_sc) && (_sc)->hw) ? wiphy_name((_sc)->hw->wiphy) : "", \
- ((_sc) && (_sc)->hw) ? ": " : "", \
- ##__VA_ARGS__)
+ _ath5k_printk(_sc, _level, _fmt, ##__VA_ARGS__)
If there are level/mask tests to macros that
are used to call ATH5K_PRINTK, that still
works. As far as I can tell, there aren't
any uses of macros like that.
cheers, Joe
^ permalink raw reply
* Re: [PATCH wireless-next 2/3] ath5k: Introduce _ath5k_printk to reduce code/text
From: Adrian Chadd @ 2012-03-19 4:36 UTC (permalink / raw)
To: Joe Perches
Cc: Jiri Slaby, Nick Kossifidis, Luis R. Rodriguez, Bob Copeland,
John W. Linville, linux-wireless, ath5k-devel, netdev,
linux-kernel
In-Reply-To: <b157e2d1a746733d1814b9726f326d52d36da2fc.1332116921.git.joe@perches.com>
Hi,
So the reason this is a macro in the FreeBSD HAL is so that the args
aren't evaluated unless the level (or debug bitmap in my case) fires
off.
Otherwise compiling in debugging will cause a _lot_ of spurious
register reads to occur that are then tossed. This was one of the big
reasons for instability and slow performance when AH_DEBUG was
enabled.
Adrian
^ permalink raw reply
* Re: WARNING: at net/sched/sch_generic.c:256 dev_watchdog+0x1f2/0x200()
From: Justin Mattock @ 2012-03-19 4:01 UTC (permalink / raw)
To: Francois Romieu; +Cc: Larry Finger, linux-kernel, netdev
In-Reply-To: <20120205145913.194746473361e324bda1eb4a@gmail.com>
On Sun, Feb 5, 2012 at 2:59 PM, Justin P. Mattock
<justinmattock@gmail.com> wrote:
> On Sun, 5 Feb 2012 23:21:41 +0100
> Francois Romieu <romieu@fr.zoreil.com> wrote:
>
>> Larry Finger <Larry.Finger@lwfinger.net> :
>> > On 02/05/2012 03:40 PM, Justin P. Mattock wrote:
>> > >
>> > >this showed up as I woke the machine up from suspend. machine is connected
>> > >I just see networkmanager constantly spinning trying to connect then telling me
>> > >the wired network is not connected(even though its just wireless that I have).
>>
>> [ 1273.447349] r8169 0000:06:00.0: Refused to change power state, currently in D3
>> [ 1273.571024] psmouse serio1: elantech: retrying ps2 command 0xf8 (2).
>> [ 1273.656709] r8169 0000:06:00.0: eth0: link up
>> ^^^^^^^
>>
>> Justin, you did not ever connect a cable, did you ?
>
> nope! was using wireless the whole time.
>
>>
>> [...]
>> > The error clearly comes from r8169, not from ath9k. If you unload
>> > r8169 before suspending, does this still happen? My suspicion is
>> > that NM is getting confused.
>>
>> It could hide the problem but the D3 + link up messages are nonetheless
>> a bit unexpected.
>>
>> > P.S. Unless it happens with just the ath9k active, this ML is likely
>> > not the correct one.
>>
>> Yes, please Cc: netdev@vger.kernel.org + me instead.
>>
>> Thanks.
>>
>> --
>> Ueimor
>
> removed linux-wireless and added netdev to the cc's
> will monitor this and report anything that shows up.
>
> --
> Justin P. Mattock <justinmattock@gmail.com>
seems I see this with the latest linux-next:
[21731.149774] wlan0: associate with 00:1e:2a:62:6b:1e (try 1/3)
[21731.152858] wlan0: RX AssocResp from 00:1e:2a:62:6b:1e (capab=0x31
status=0 aid=7)
[21731.152867] wlan0: associated
[21740.306348] ------------[ cut here ]------------
[21740.306363] WARNING: at net/sched/sch_generic.c:256
dev_watchdog+0x1f2/0x200()
[21740.306367] Hardware name: 0914
[21740.306370] NETDEV WATCHDOG: eth0 (r8169): transmit queue 0 timed out
[21740.306374] Modules linked in: dm_crypt xt_limit xt_tcpudp
xt_addrtype snd_hda_codec_hdmi xt_state joydev arc4
snd_hda_codec_conexant ath9k nf_conntrack_netbios_ns
nf_conntrack_broadcast snd_hda_intel snd_hda_codec mac80211 nf_nat_ftp
nf_nat snd_hwdep nf_conntrack_ipv4 nf_defrag_ipv4 snd_pcm
nf_conntrack_ftp snd_seq_midi ath9k_common ath9k_hw nf_conntrack
iptable_filter snd_rawmidi snd_seq_midi_event uvcvideo ath ip_tables
videobuf2_core videodev psmouse x_tables ideapad_laptop serio_raw
snd_seq cfg80211 lp snd_timer snd_seq_device snd videobuf2_vmalloc
videobuf2_memops soundcore parport sparse_keymap intel_ips
snd_page_alloc usb_storage uas i915 drm_kms_helper drm i2c_algo_bit
r8169 intel_agp intel_gtt agpgart video
[21740.306469] Pid: 0, comm: swapper/2 Not tainted 3.3.0-rc7-next-20120316 #1
[21740.306473] Call Trace:
[21740.306483] [<c1049072>] warn_slowpath_common+0x72/0xa0
[21740.306489] [<c14e00a2>] ? dev_watchdog+0x1f2/0x200
[21740.306493] [<c14e00a2>] ? dev_watchdog+0x1f2/0x200
[21740.306498] [<c1049143>] warn_slowpath_fmt+0x33/0x40
[21740.306503] [<c14e00a2>] dev_watchdog+0x1f2/0x200
[21740.306520] [<c1059c12>] run_timer_softirq+0x132/0x540
[21740.306525] [<c1059b98>] ? run_timer_softirq+0xb8/0x540
[21740.306529] [<c14dfeb0>] ? pfifo_fast_init+0x60/0x60
[21740.306534] [<c1050e67>] __do_softirq+0x97/0x360
[21740.306539] [<c1050dd0>] ? local_bh_enable+0xd0/0xd0
[21740.306542] <IRQ> [<c10513d6>] ? irq_exit+0x86/0xb0
[21740.306552] [<c159f179>] ? smp_apic_timer_interrupt+0x59/0x88
[21740.306559] [<c12cf1a8>] ? trace_hardirqs_off_thunk+0xc/0x14
[21740.306565] [<c1597682>] ? apic_timer_interrupt+0x36/0x3c
[21740.306572] [<c10a007b>] ? clocks_calc_mult_shift+0xfb/0x100
[21740.306580] [<c1319fd9>] ? intel_idle+0xc9/0x130
[21740.306588] [<c1495655>] ? cpuidle_enter+0x15/0x20
[21740.306592] [<c1495b5a>] ? cpuidle_idle_call+0x8a/0x320
[21740.306600] [<c10152c5>] ? cpu_idle+0xa5/0xf0
[21740.306607] [<c1585d2d>] ? start_secondary+0x209/0x20f
[21740.306611] ---[ end trace 9a4d94a7e2dfb4d0 ]---
[21740.318685] r8169 0000:06:00.0: eth0: link up
[21752.292679] r8169 0000:06:00.0: eth0: link up
[21764.268569] r8169 0000:06:00.0: eth0: link up
[21776.254393] r8169 0000:06:00.0: eth0: link up
[21788.235797] r8169 0000:06:00.0: eth0: link up
[21800.196524] r8169 0000:06:00.0: eth0: link up
[21812.172497] r8169 0000:06:00.0: eth0: link up
one thing I have noticed these few weeks is once I wake the machine
up, wireless connects, but Networkmanager keeps trying to connect
ethernet(switching ethernet off in preferences or waiting a while
resolves this issue from the daemon), so I think there must be some
strange thing with NM
causing this(disabling "auto connect" gets rid of this issue)
the system is ubuntu(oneiric)
--
Justin P. Mattock
^ permalink raw reply
* Re: [PATCH v2 net-next 2/2] tcp: reduce out_of_order memory use
From: Neal Cardwell @ 2012-03-19 3:53 UTC (permalink / raw)
To: Eric Dumazet
Cc: David Miller, netdev, Tom Herbert, Ilpo Järvinen,
H.K. Jerry Chu, Yuchung Cheng
In-Reply-To: <1332104867.3597.1.camel@edumazet-laptop>
On Sun, Mar 18, 2012 at 5:07 PM, Eric Dumazet <eric.dumazet@gmail.com> wrote:
> With increasing receive window sizes, but speed of light not improved
> that much, out of order queue can contain a huge number of skbs, waiting
> to be moved to receive_queue when missing packets can fill the holes.
>
> Some devices happen to use fat skbs (truesize of 4096 + sizeof(struct
> sk_buff)) to store regular (MTU <= 1500) frames. This makes highly
> probable sk_rmem_alloc hits sk_rcvbuf limit, which can be 4Mbytes in
> many cases.
>
> When limit is hit, tcp stack calls tcp_collapse_ofo_queue(), a true
> latency killer and cpu cache blower.
>
> Doing the coalescing attempt each time we add a frame in ofo queue
> permits to keep memory use tight and in many cases avoid the
> tcp_collapse() thing later.
>
> Tested on various wireless setups (b43, ath9k, ...) known to use big skb
> truesize, this patch removed the "packets collapsed in receive queue due
> to low socket buffer" I had before.
>
> This also reduced average memory used by tcp sockets.
>
> With help from Neal Cardwell.
>
> Signed-off-by: Eric Dumazet <eric.dumazet@gmail.com>
Acked-by: Neal Cardwell <ncardwell@google.com>
neal
^ permalink raw reply
* Re: [PATCH v2 net-next 1/2] tcp: introduce tcp_data_queue_ofo
From: Neal Cardwell @ 2012-03-19 3:49 UTC (permalink / raw)
To: Eric Dumazet
Cc: David Miller, netdev, Tom Herbert, Ilpo Järvinen,
H.K. Jerry Chu, Yuchung Cheng
In-Reply-To: <1332104804.3597.0.camel@edumazet-laptop>
On Sun, Mar 18, 2012 at 5:06 PM, Eric Dumazet <eric.dumazet@gmail.com> wrote:
> Split tcp_data_queue() in two parts for better readability.
>
> tcp_data_queue_ofo() is responsible for queueing incoming skb into out
> of order queue.
>
> Change code layout so that the skb_set_owner_r() is performed only if
> skb is not dropped.
>
> This is a preliminary patch before "reduce out_of_order memory use"
> following patch.
>
> Signed-off-by: Eric Dumazet <eric.dumazet@gmail.com>
Acked-by: Neal Cardwell <ncardwell@google.com>
Nice clean-up. Thanks for doing this!
neal
^ permalink raw reply
* [PATCH 4/4] rtlwifi: Remove unused ETH_ADDR_LEN defines
From: Joe Perches @ 2012-03-19 3:37 UTC (permalink / raw)
To: Larry Finger, Chaoming Li
Cc: John W. Linville, linux-wireless, netdev, linux-kernel
In-Reply-To: <cover.1332128181.git.joe@perches.com>
Just neatening.
Signed-off-by: Joe Perches <joe@perches.com>
---
drivers/net/wireless/rtlwifi/rtl8192ce/reg.h | 1 -
drivers/net/wireless/rtlwifi/rtl8192de/reg.h | 1 -
2 files changed, 0 insertions(+), 2 deletions(-)
diff --git a/drivers/net/wireless/rtlwifi/rtl8192ce/reg.h b/drivers/net/wireless/rtlwifi/rtl8192ce/reg.h
index 43806d9..e4d738f 100644
--- a/drivers/net/wireless/rtlwifi/rtl8192ce/reg.h
+++ b/drivers/net/wireless/rtlwifi/rtl8192ce/reg.h
@@ -1190,7 +1190,6 @@
#define USB_AGG_EN BIT(3)
-#define MAC_ADDR_LEN 6
#define LAST_ENTRY_OF_TX_PKT_BUFFER 255
#define POLLING_LLT_THRESHOLD 20
diff --git a/drivers/net/wireless/rtlwifi/rtl8192de/reg.h b/drivers/net/wireless/rtlwifi/rtl8192de/reg.h
index 9bc4623..ebb1d5f 100644
--- a/drivers/net/wireless/rtlwifi/rtl8192de/reg.h
+++ b/drivers/net/wireless/rtlwifi/rtl8192de/reg.h
@@ -998,7 +998,6 @@
#define SCR_RXBCUSEDK BIT(7)
/* General definitions */
-#define MAC_ADDR_LEN 6
#define LAST_ENTRY_OF_TX_PKT_BUFFER 255
#define LAST_ENTRY_OF_TX_PKT_BUFFER_DUAL_MAC 127
--
1.7.8.111.gad25c.dirty
^ permalink raw reply related
* [PATCH 3/4] igbvf: Use ETH_ALEN
From: Joe Perches @ 2012-03-19 3:37 UTC (permalink / raw)
To: linux-kernel
Cc: e1000-devel, Bruce Allan, Jesse Brandeburg, John Ronciak, netdev
In-Reply-To: <cover.1332128181.git.joe@perches.com>
Remove an unnecessary #define and use memcpy
instead of a loop to copy an ethernet address.
Signed-off-by: Joe Perches <joe@perches.com>
---
drivers/net/ethernet/intel/igbvf/defines.h | 4 ----
drivers/net/ethernet/intel/igbvf/vf.c | 7 ++-----
2 files changed, 2 insertions(+), 9 deletions(-)
diff --git a/drivers/net/ethernet/intel/igbvf/defines.h b/drivers/net/ethernet/intel/igbvf/defines.h
index 33f40d3..3e18045 100644
--- a/drivers/net/ethernet/intel/igbvf/defines.h
+++ b/drivers/net/ethernet/intel/igbvf/defines.h
@@ -97,10 +97,6 @@
#define E1000_ERR_MAC_INIT 5
#define E1000_ERR_MBX 15
-#ifndef ETH_ADDR_LEN
-#define ETH_ADDR_LEN 6
-#endif
-
/* SRRCTL bit definitions */
#define E1000_SRRCTL_BSIZEPKT_SHIFT 10 /* Shift _right_ */
#define E1000_SRRCTL_BSIZEHDRSIZE_MASK 0x00000F00
diff --git a/drivers/net/ethernet/intel/igbvf/vf.c b/drivers/net/ethernet/intel/igbvf/vf.c
index 1955197..30a6cc4 100644
--- a/drivers/net/ethernet/intel/igbvf/vf.c
+++ b/drivers/net/ethernet/intel/igbvf/vf.c
@@ -246,7 +246,7 @@ static void e1000_update_mc_addr_list_vf(struct e1000_hw *hw,
for (i = 0; i < cnt; i++) {
hash_value = e1000_hash_mc_addr_vf(hw, mc_addr_list);
hash_list[i] = hash_value & 0x0FFFF;
- mc_addr_list += ETH_ADDR_LEN;
+ mc_addr_list += ETH_ALEN;
}
mbx->ops.write_posted(hw, msgbuf, E1000_VFMAILBOX_SIZE);
@@ -333,10 +333,7 @@ static void e1000_rar_set_vf(struct e1000_hw *hw, u8 * addr, u32 index)
**/
static s32 e1000_read_mac_addr_vf(struct e1000_hw *hw)
{
- int i;
-
- for (i = 0; i < ETH_ADDR_LEN; i++)
- hw->mac.addr[i] = hw->mac.perm_addr[i];
+ memcpy(hw->mac.addr, hw->mac.perm_addr, ETH_ALEN);
return E1000_SUCCESS;
}
--
1.7.8.111.gad25c.dirty
------------------------------------------------------------------------------
This SF email is sponsosred by:
Try Windows Azure free for 90 days Click Here
http://p.sf.net/sfu/sfd2d-msazure
_______________________________________________
E1000-devel mailing list
E1000-devel@lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/e1000-devel
To learn more about Intel® Ethernet, visit http://communities.intel.com/community/wired
^ permalink raw reply related
* [PATCH 2/4] atlx: Use ETH_ALEN
From: Joe Perches @ 2012-03-19 3:37 UTC (permalink / raw)
To: linux-kernel; +Cc: Jay Cliburn, Chris Snook, netdev
In-Reply-To: <cover.1332128181.git.joe@perches.com>
No need for yet another #define for this.
Convert NODE_ADDRESS_SIZE use to ETH_ALEN and remove #define.
Use memcpy instead of a loop to copy an address.
Signed-off-by: Joe Perches <joe@perches.com>
---
drivers/net/ethernet/atheros/atlx/atl2.c | 13 +++++--------
drivers/net/ethernet/atheros/atlx/atl2.h | 5 ++---
drivers/net/ethernet/atheros/atlx/atlx.h | 1 -
3 files changed, 7 insertions(+), 12 deletions(-)
diff --git a/drivers/net/ethernet/atheros/atlx/atl2.c b/drivers/net/ethernet/atheros/atlx/atl2.c
index 071f4c8..6762dc4 100644
--- a/drivers/net/ethernet/atheros/atlx/atl2.c
+++ b/drivers/net/ethernet/atheros/atlx/atl2.c
@@ -2258,7 +2258,7 @@ static int get_permanent_address(struct atl2_hw *hw)
u32 Addr[2];
u32 i, Control;
u16 Register;
- u8 EthAddr[NODE_ADDRESS_SIZE];
+ u8 EthAddr[ETH_ALEN];
bool KeyValid;
if (is_valid_ether_addr(hw->perm_mac_addr))
@@ -2299,7 +2299,7 @@ static int get_permanent_address(struct atl2_hw *hw)
*(u16 *) &EthAddr[0] = SHORTSWAP(*(u16 *) &Addr[1]);
if (is_valid_ether_addr(EthAddr)) {
- memcpy(hw->perm_mac_addr, EthAddr, NODE_ADDRESS_SIZE);
+ memcpy(hw->perm_mac_addr, EthAddr, ETH_ALEN);
return 0;
}
return 1;
@@ -2334,7 +2334,7 @@ static int get_permanent_address(struct atl2_hw *hw)
*(u32 *) &EthAddr[2] = LONGSWAP(Addr[0]);
*(u16 *) &EthAddr[0] = SHORTSWAP(*(u16 *)&Addr[1]);
if (is_valid_ether_addr(EthAddr)) {
- memcpy(hw->perm_mac_addr, EthAddr, NODE_ADDRESS_SIZE);
+ memcpy(hw->perm_mac_addr, EthAddr, ETH_ALEN);
return 0;
}
/* maybe MAC-address is from BIOS */
@@ -2344,7 +2344,7 @@ static int get_permanent_address(struct atl2_hw *hw)
*(u16 *) &EthAddr[0] = SHORTSWAP(*(u16 *) &Addr[1]);
if (is_valid_ether_addr(EthAddr)) {
- memcpy(hw->perm_mac_addr, EthAddr, NODE_ADDRESS_SIZE);
+ memcpy(hw->perm_mac_addr, EthAddr, ETH_ALEN);
return 0;
}
@@ -2358,8 +2358,6 @@ static int get_permanent_address(struct atl2_hw *hw)
*/
static s32 atl2_read_mac_addr(struct atl2_hw *hw)
{
- u16 i;
-
if (get_permanent_address(hw)) {
/* for test */
/* FIXME: shouldn't we use random_ether_addr() here? */
@@ -2371,8 +2369,7 @@ static s32 atl2_read_mac_addr(struct atl2_hw *hw)
hw->perm_mac_addr[5] = 0x38;
}
- for (i = 0; i < NODE_ADDRESS_SIZE; i++)
- hw->mac_addr[i] = hw->perm_mac_addr[i];
+ memcpy(hw->mac_addr, hw->perm_mac_addr, ETH_ALEN);
return 0;
}
diff --git a/drivers/net/ethernet/atheros/atlx/atl2.h b/drivers/net/ethernet/atheros/atlx/atl2.h
index bf9016e..3ebe19f 100644
--- a/drivers/net/ethernet/atheros/atlx/atl2.h
+++ b/drivers/net/ethernet/atheros/atlx/atl2.h
@@ -47,7 +47,6 @@ extern int ethtool_ioctl(struct ifreq *ifr);
#define PCI_COMMAND_REGISTER PCI_COMMAND
#define CMD_MEM_WRT_INVALIDATE PCI_COMMAND_INVALIDATE
-#define ETH_ADDR_LEN ETH_ALEN
#define ATL2_WRITE_REG(a, reg, value) (iowrite32((value), \
((a)->hw_addr + (reg))))
@@ -429,8 +428,8 @@ struct atl2_hw {
u8 flash_vendor;
u8 dma_fairness;
- u8 mac_addr[NODE_ADDRESS_SIZE];
- u8 perm_mac_addr[NODE_ADDRESS_SIZE];
+ u8 mac_addr[ETH_ALEN];
+ u8 perm_mac_addr[ETH_ALEN];
/* FIXME */
/* bool phy_preamble_sup; */
diff --git a/drivers/net/ethernet/atheros/atlx/atlx.h b/drivers/net/ethernet/atheros/atlx/atlx.h
index 14054b7..448f5dc 100644
--- a/drivers/net/ethernet/atheros/atlx/atlx.h
+++ b/drivers/net/ethernet/atheros/atlx/atlx.h
@@ -484,7 +484,6 @@
/* For checksumming, the sum of all words in the EEPROM should equal 0xBABA */
#define EEPROM_SUM 0xBABA
-#define NODE_ADDRESS_SIZE 6
struct atlx_spi_flash_dev {
const char *manu_name; /* manufacturer id */
--
1.7.8.111.gad25c.dirty
^ permalink raw reply related
* [PATCH 1/4] if_vlan: Remove VLAN_ETH_ALEN define and the 1 use of it
From: Joe Perches @ 2012-03-19 3:37 UTC (permalink / raw)
To: Patrick McHardy; +Cc: netdev, linux-kernel
In-Reply-To: <cover.1332128181.git.joe@perches.com>
Just use ETH_ALEN.
Signed-off-by: Joe Perches <joe@perches.com>
---
include/linux/if_vlan.h | 7 +++----
1 files changed, 3 insertions(+), 4 deletions(-)
diff --git a/include/linux/if_vlan.h b/include/linux/if_vlan.h
index 13aff1e..33a6e19 100644
--- a/include/linux/if_vlan.h
+++ b/include/linux/if_vlan.h
@@ -18,10 +18,9 @@
#include <linux/etherdevice.h>
#include <linux/rtnetlink.h>
-#define VLAN_HLEN 4 /* The additional bytes (on top of the Ethernet header)
- * that VLAN requires.
+#define VLAN_HLEN 4 /* The additional bytes required by VLAN
+ * (in addition to the Ethernet header)
*/
-#define VLAN_ETH_ALEN 6 /* Octets in one ethernet addr */
#define VLAN_ETH_HLEN 18 /* Total octets in header. */
#define VLAN_ETH_ZLEN 64 /* Min. octets in frame sans FCS */
@@ -177,7 +176,7 @@ static inline struct sk_buff *vlan_insert_tag(struct sk_buff *skb, u16 vlan_tci)
veth = (struct vlan_ethhdr *)skb_push(skb, VLAN_HLEN);
/* Move the mac addresses to the beginning of the new header. */
- memmove(skb->data, skb->data + VLAN_HLEN, 2 * VLAN_ETH_ALEN);
+ memmove(skb->data, skb->data + VLAN_HLEN, 2 * ETH_ALEN);
skb->mac_header -= VLAN_HLEN;
/* first, the ethernet type */
--
1.7.8.111.gad25c.dirty
^ permalink raw reply related
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox