Subject: Re: [PATCH v2] ethernet driver for the WIZnet W5300 chip

From: Florian Fainelli <[email-masked]>
Date: Wed, 21 Mar 2012 15:49:34 +0100

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<[email-masked]>
> ---
>   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<[email-masked]>
> + *
> + * 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<[email-masked]>");
> +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 [email-masked]
> More majordomo info at  http://vger.kernel.org/majordomo-info.html
> Please read the FAQ at  http://www.tux.org/lkml/