Messages in this thread | | | Date | Wed, 21 Mar 2012 15:49:34 +0100 | From | Florian Fainelli <> | Subject | Re: [PATCH v2] ethernet driver for the WIZnet W5300 chip |
| |
Hi,
Le 03/19/12 10:23, Mike Sinkovsky a écrit : > 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
Looks like you don't have to make such decisions at compile-time. Since it is a platform driver, better supply this through platform_data instead.
> + > +#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; > + }
Please implement phylib to properly report the link state to the networking stack and ethtool.
> + > + 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;
Allow platform_data to pass a valid MAC address to this driver instead of defaulting to random unconditionnaly.
> + 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); > > -- > To unsubscribe from this list: send the line "unsubscribe linux-kernel" in > the body of a message to majordomo@vger.kernel.org > More majordomo info at http://vger.kernel.org/majordomo-info.html > Please read the FAQ at http://www.tux.org/lkml/ -- To unsubscribe from this list: send the line "unsubscribe linux-kernel" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html Please read the FAQ at http://www.tux.org/lkml/
| |