netdev.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* Re: [PATCH] RM9000 network driver
       [not found]   ` <20060623142127.GA23299@linux-mips.org>
@ 2006-07-05 18:00     ` Jeff Garzik
  0 siblings, 0 replies; only message in thread
From: Jeff Garzik @ 2006-07-05 18:00 UTC (permalink / raw)
  To: Ralf Baechle; +Cc: Thomas Koeller, Netdev List

Ralf Baechle wrote:
> +/* module parameters */
> +
> +static int param_set_ethaddr(const char *val, struct kernel_param *kp)
> +{
> +	static const char fmt[] = "%2hhx:%2hhx:%2hhx:%2hhx:%2hhx:%2hhx";
> +	unsigned char * const cc = (unsigned char *) kp->arg;
> +
> +	if (!val) return -EINVAL;
> +
> +	return (6 == sscanf(val, fmt, &cc[0], &cc[1], &cc[2], &cc[3],
> +			    &cc[4], &cc[5])) ? 0 : -EINVAL;
> +}
> +
> +static int param_get_ethaddr(char *buffer, struct kernel_param *kp)
> +{
> +	static const char fmt[] = "%02x:%02x:%02x:%02x:%02x:%02x";
> +	const unsigned char * const cc = (unsigned char *) kp->arg;
> +
> +	return scnprintf(buffer, 18, fmt,
> +			 cc[0], cc[1], cc[2], cc[3], cc[4], cc[5]);
> +}
> +
> +#define param_check_ethaddr(name, p) __param_check(name, *p, unsigned char)

kill this, it is settable via ifconfig already


> +enum {
> +	speed_10m = 0x0,
> +	speed_100m = 0x1,
> +	speed_1g = 0x2
> +};
> +
> +static int autoneg = 1;
> +static unsigned int speed = speed_1g;
> +static int full_duplex = 1;
> +static unsigned char hwaddr[6] = {0x00, 0x30, 0x53, 0x00, 0x00, 0x00};
> +
> +module_param(autoneg, bool, 0444);
> +module_param(full_duplex, bool, 0444);
> +module_param(speed, uint, 0444);
> +module_param(hwaddr, ethaddr, 0444);

kill all this, use ethtool


> +/* XDMA descriptors */
> +typedef struct {
> +	volatile u64	cpu_part;
> +	volatile u64	xdma_part;
> +} rx_desc_t;
> +
> +typedef volatile u64 tx_desc_t;

volatile inevitably implies that the programmer was too lazy with 
barriers such.

Remove 'volatile', and make sure it still works.  If not, debug...


> +/* Port specific data structure */
> +typedef struct {
> +	unsigned int			port_num;
> +	unsigned int			irq_phy;
> +	u8				port_mac_addr[6];
> +
> +	/* Statistics */
> +	spinlock_t			stat_lock;
> +	struct timer_list		stat_timer;
> +
> +	/* Tx next descriptor pointer */
> +	int 				tx_next_desc;
> +
> +	/* SKB queues */
> +	struct sk_buff_head		rx_queue;
> +	struct sk_buff_head		tx_queue;
> +
> +	/* XDMA descriptor area */
> +	rx_desc_t			* rx_desc_area;
> +	tx_desc_t			* tx_desc_area;
> +
> +	/* DMA structures and handles */
> +	dma_addr_t			tx_dma;
> +	dma_addr_t			rx_dma;
> +
> +	/* Device lock */
> +	spinlock_t			lock;
> +
> +	unsigned int			rx_ring_size;
> +	unsigned int			tx_ring_size;
> +
> +	struct net_device_stats		stats;
> +
> +	/* Tx and Rx coalescing */
> +	unsigned long			rx_int_coal;
> +	unsigned long			tx_int_coal;
> +
> +	/* NAPI work limit */
> +	unsigned int			rx_work_limit;
> +
> +	/* Values set from platform resources */
> +	unsigned int			slice;
> +	unsigned int			mdio_channel;
> +	unsigned int			rxfifo_start, rxfifo_len;
> +	unsigned int			txfifo_start, txfifo_len;
> +	void __iomem *			addr_xdma;
> +	void __iomem *			addr_mac;
> +	void __iomem *			addr_pktproc;
> +	void __iomem *			addr_mstat;
> +	void __iomem *			addr_fifo_rx;
> +	void __iomem *			addr_fifo_tx;
> +	void  __iomem *			addr_mdio;
> +} rm9k_ge_port_info;
> +

> +static inline rx_desc_t * rm9k_ge_rxdesc_from_skb(const struct sk_buff * skb)
> +{
> +	rx_desc_t ** const d = (rx_desc_t **) skb->cb;
> +	return *d;
> +}
> +
> +
> +
> +static inline void
> +rm9k_ge_rxdesc_to_skb(struct sk_buff * skb, rx_desc_t * desc)
> +{
> +	rx_desc_t ** const d = (rx_desc_t **) skb->cb;
> +	*d = desc;
> +}
> +
> +
> +
> +static inline tx_desc_t *
> +rm9k_ge_txdesc_from_skb(const struct sk_buff * skb)
> +{
> +	tx_desc_t ** const d = (tx_desc_t **) skb->cb;
> +	return *d;
> +}
> +
> +
> +
> +static inline void
> +rm9k_ge_txdesc_to_skb(struct sk_buff * skb, tx_desc_t * desc)
> +{
> +	tx_desc_t ** const d = (tx_desc_t **) skb->cb;
> +	*d = desc;
> +}

Don't use skb->cb for this stuff, that's not the intended usage.  It 
will get stomped on.

Besides that, this is backwards from how other net drivers set things 
up:  most net drivers create a list of descriptor information, and 
directly index the skb pointer from that.



> +/*
> + * Change the MTU of the Ethernet Device
> + */
> +static int rm9k_ge_change_mtu(struct net_device *netdev, int new_mtu)
> +{
> +	if ((new_mtu > ETH_DATA_LEN) || (new_mtu < 64))
> +		return -EINVAL;
> +
> +	netdev->mtu = new_mtu;
> +
> +	if (netif_running(netdev)) {
> +		rm9k_ge_port_info * const info = netdev_priv(netdev);
> +
> +		/* Rebuild the RX queue */
> +		rm9k_ge_free_rx_queue(netdev);
> +		gpi_writel(rm9k_eth_rx_frame_len(new_mtu),
> +			   info->addr_mac + 0x0008);
> +		gpi_writel(rm9k_eth_tx_frame_len(new_mtu),
> +			   info->addr_mac + 0x0058);
> +		rm9k_ge_setup_rx_queue(netdev);

Seems to need some sort of locking?


> +static int rm9k_ge_xdma_ints(u32 mask)
> +{
> +	static spinlock_t intflg_lock = SPIN_LOCK_UNLOCKED;
> +	u32 flg;
> +
> +	/*
> +	 * The interrupt decoding below assumes that the 'low priority core'
> +	 * feature of the RM9000 is not used!
> +	 */
> +	switch (smp_processor_id()) {
> +		case 0:
> +			spin_lock(&intflg_lock);
> +			flg = titan_readl(0x0048) & titan_readl(0x0038)
> +					  & mask;
> +			if (flg)
> +				titan_writel(flg, 0x0048);
> +			spin_unlock(&intflg_lock);
> +			break;
> +
> +		case 1:
> +			spin_lock(&intflg_lock);
> +			flg = titan_readl(0x004c) & titan_readl(0x003c)
> +					  & mask;
> +			if (flg)
> +				titan_writel(flg, 0x004c);
> +			spin_unlock(&intflg_lock);
> +			break;
> +
> +		default:
> +			flg = 0;
> +			break;
> +	}
> +
> +	return flg != 0;
> +}
> +
> +
> +
> +/*
> + * Tx Timeout function
> + */
> +static void rm9k_ge_tx_timeout(struct net_device *netdev)
> +{
> +	rm9k_ge_port_info * const info = netdev_priv(netdev);
> +	u32 reg;
> +
> +	/* Stop the tx queue */
> +	netif_stop_queue(netdev);
> +
> +	/* Reset the tx xdma */
> +	spin_lock(&info->tx_queue.lock);
> +	info->stats.tx_errors += skb_queue_len(&info->tx_queue);
> +	reg = gpi_readl(info->addr_xdma + 0x0000);
> +	gpi_writel(reg | 0x80000000, info->addr_xdma + 0x0000);
> +	iob();
> +	spin_unlock(&info->tx_queue.lock);
> +
> +	/* Flush the tx queue */
> +	__skb_queue_purge(&info->tx_queue);
> +	info->tx_next_desc = 0;
> +
> +	/* Restart the tx xdma channel */
> +	gpi_writel(reg & ~0x80000000, info->addr_xdma + 0x0000);
> +	iob();
> +
> +	/* Restart the tx queue */
> +	printk(KERN_ERR "%s: TX timeout - queue flushed\n", netdev->name);
> +	netif_start_queue(netdev);
> +}
> +
> +
> +
> +static struct sk_buff *rm9k_ge_get_tx_skb(rm9k_ge_port_info * info)
> +{
> +	struct sk_buff * s;
> +
> +	spin_lock(&info->tx_queue.lock);
> +	s = skb_queue_len(&info->tx_queue) > gpi_readl(info->addr_xdma + 0x0004) ?
> +	__skb_dequeue(&info->tx_queue) : NULL;
> +	spin_unlock(&info->tx_queue.lock);
> +	return s;
> +}
> +
> +
> +
> +
> +
> +static irqreturn_t rm9k_ge_main_int_handler(int irq, void *dev_id,
> +					    struct pt_regs *regs)
> +{
> +	u32 reg;
> +	struct net_device * const netdev = (struct net_device *) dev_id;
> +	rm9k_ge_port_info * const info = netdev_priv(netdev);
> +	const u32 eth_int_cause_error = gpi_readl(info->addr_xdma + 0x000c);
> +	const int int_tx = rm9k_ge_xdma_ints(0x1 << (info->slice * 8 + 1)),
> +		  int_rx = rm9k_ge_xdma_ints(0x1 << (info->slice * 8 + 0));
> +
> +	if (!int_tx && !int_rx && !eth_int_cause_error)
> +		return IRQ_NONE;
> +
> +	/* Handle Tx first */
> +	if (int_tx) {
> +		struct sk_buff *skb;
> +
> +		while (skb = rm9k_ge_get_tx_skb(info), skb != NULL) {
> +			tx_desc_t * const desc =
> +				rm9k_ge_txdesc_from_skb(skb);
> +
> +			dma_unmap_single(netdev->class_dev.dev,
> +					 *desc & 0x00000000ffffffffull,
> +					 skb->len, DMA_TO_DEVICE);
> +			dev_kfree_skb_irq(skb);
> +		}
> +
> +		if (netif_queue_stopped(netdev))
> +			netif_start_queue(netdev);
> +	}
> +
> +	/* Handle the Rx next */
> +	if (int_rx) {
> +		if (netif_rx_schedule_prep(netdev)) {
> +			/* Disable Tx and Rx */
> +			lock_titan_regs();
> +			reg = titan_readl(0x0058);
> +			reg &= ~(0x1 << (info->slice * 8));
> +			titan_writel(reg, 0x0058);
> +			unlock_titan_regs();
> +			__netif_rx_schedule(netdev);
> +		}
> +	}
> +
> +	/* Handle error interrupts */
> +	if (eth_int_cause_error && (eth_int_cause_error != 0x2)) {
> +		printk(KERN_ERR
> +			"XDMA Channel Error : %x  on port %d\n",
> +			eth_int_cause_error, info->slice);
> +
> +		printk(KERN_ERR
> +		       "XDMA GDI Hardware error : %x  on port %d\n",
> +		       titan_readl(0x5008), info->slice);
> +
> +		printk(KERN_ERR
> +			"XDMA currently has %d Rx descriptors \n",
> +			gpi_readl(info->addr_xdma +0x0008));
> +
> +		printk(KERN_ERR
> +			"XDMA currently has prefetcted %d Rx descriptors \n",
> +			gpi_readl(info->addr_xdma + 0x001c));
> +
> +		gpi_writel(eth_int_cause_error, info->addr_xdma + 0x000c);
> +	}
> +	return IRQ_HANDLED;
> +}
> +
> +static irqreturn_t rm9k_ge_phy_int_handler(int irq, void *dev_id,
> +					   struct pt_regs *regs)
> +{
> +	struct net_device * const netdev = (struct net_device *) dev_id;
> +	u16 phy_intstat;
> +
> +	/*
> +	 * PHY interrupt to inform abt the changes. Reading the
> +	 * PHY Status register will clear the interrupt
> +	 */
> +	rm9k_ge_mdio_read(PHY_ADDR, RM9K_GE_MDIO_PHY_INTSTAT, &phy_intstat);
> +
> +	if (phy_intstat & 0x0010) {
> +		rm9k_ge_port_info * const info = netdev_priv(netdev);
> +		/* Autonegotiation completed */
> +		static const u16 val[8] =
> +			{ 0x1000, 0x0000, 0x1100, 0x0100,
> +			  0x1201, 0x0201, 0x1201, 0x0201 };

magic numbers



> +		u16 phy_reg = 0;
> +		int i;
> +
> +		gpi_writel(0x00000000, info->addr_mac + 0x014c);
> +		rm9k_ge_mdio_read(PHY_ADDR, RM9K_GE_MDIO_PHY_STATUS, &phy_reg);
> +		i = ((phy_reg >> 1) & 0x1) | ((phy_reg >> 2) & 0x6);
> +
> +		wmb();
> +		gpi_writel(val[i], info->addr_mac + 0x0150);
> +		wmb();
> +		gpi_writel(0x00000003, info->addr_mac + 0x014c);
> +		iob();

author desperately needs to read memory-barriers doc...

and investigate write posting.


> +		netif_carrier_on(netdev);
> +		pr_debug("%s: autonegotiation completed\n", netdev->name);
> +	} else {
> +		u16 val;
> +		rm9k_ge_mdio_read(PHY_ADDR, RM9K_GE_MDIO_PHY_STATUS, &val);
> +
> +		if (val & 0x0004) {
> +			pr_debug("%s: carrier detected\n", netdev->name);
> +			if (!autoneg)
> +				netif_carrier_on(netdev);
> +		} else {
> +			netif_carrier_off(netdev);
> +			pr_debug("%s: carrier lost\n", netdev->name);
> +		}
> +	}
> +
> +	rm9k_ge_mdio_write(PHY_ADDR, RM9K_GE_MDIO_PHY_INTCLEAR, phy_intstat);
> +	return IRQ_HANDLED;
> +}
> +
> +/*
> + * Multicast and Promiscuous mode set. The
> + * set_multi entry point is called whenever the
> + * multicast address list or the network interface
> + * flags are updated.
> + */
> +static void rm9k_ge_set_multi(struct net_device *netdev)
> +{
> +	rm9k_ge_port_info * const info = netdev_priv(netdev);
> +	u32 reg;
> +
> +	reg = gpi_readl(info->addr_pktproc + 0x0124);
> +
> +	if (netdev->flags & IFF_PROMISC) {
> +		reg |= 0x2;
> +	}
> +	else if (netdev->flags & IFF_ALLMULTI) {
> +		reg |= 0x401; /* Use the 64-bit Multicast Hash bin */
> +	}
> +	else {
> +		reg = 0x2;

doesn't this code turn on promisc, even in unicast situations?


> +	gpi_writel(reg, info->addr_pktproc + 0x0124);
> +	if (reg & 0x01) {
> +		gpi_writel(0xffff, info->addr_pktproc + 0x0110);
> +		gpi_writel(0xffff, info->addr_pktproc + 0x0114);
> +		gpi_writel(0xffff, info->addr_pktproc + 0x0118);
> +		gpi_writel(0xffff, info->addr_pktproc + 0x011c);

magic numbers, and no comments.  WTF does this code actually do?

Seems like a broken way to program the multicast filter, to me.



> + * Open the network device
> + */
> +static int rm9k_ge_open(struct net_device *netdev)
> +{
> +	rm9k_ge_port_info * const info = netdev_priv(netdev);
> +	static const char irqerr[] =
> +		KERN_ERR "%s: failed to set up irq %u - error %d\n";
> +	int res;
> +	u32 reg, xdma_initval;
> +	u16 phy_reg;
> +	unsigned long end;
> +
> +	/* Check, if slice in GMII/MII (not TBI) mode */
> +	reg = titan_readl(0x0010);
> +	if (~reg & (0x100 << info->slice)) {

magic numbers


> +		printk(KERN_ERR "Ethernet slice #%u wrong mode\n",
> +		info->slice);
> +		return -EINVAL;
> +	}
> +
> +	/* Compute the XDMA channel initialization value */
> +	xdma_initval = 0x440;
> +
> +	switch (info->tx_ring_size) {
> +		case 512:
> +			break;
> +		case 256:
> +			xdma_initval |= 0x10000000;
> +			break;
> +		case 128:
> +			xdma_initval |= 0x20000000;
> +			break;
> +		case 64:
> +			xdma_initval |= 0x30000000;
> +			break;
> +		default:
> +			return -EINVAL;
> +	}
> +
> +	switch (info->rx_ring_size) {
> +		case 512:
> +			break;
> +		case 256:
> +			xdma_initval |= 0x00010000;
> +			break;
> +		case 128:
> +			xdma_initval |= 0x00020000;
> +			break;
> +		case 64:
> +			xdma_initval |= 0x00030000;
> +			break;
> +		default:
> +			return -EINVAL;
> +	}
> +
> +	/* Set up the GPI interrupt */
> +	res = request_irq(netdev->irq, rm9k_ge_main_int_handler,
> +		     SA_INTERRUPT | SA_SAMPLE_RANDOM | SA_SHIRQ,
> +		     netdev->name, netdev);
> +
> +	if (res) {
> +		printk(irqerr, netdev->name, netdev->irq, -res);
> +		return res;
> +	}
> +
> +	/* Set up the PHY interrupt */
> +	res = request_irq(info->irq_phy, rm9k_ge_phy_int_handler,
> +		     SA_INTERRUPT | SA_SAMPLE_RANDOM,
> +		     netdev->name, netdev);
> +
> +	if (res) {
> +		free_irq(netdev->irq, netdev);
> +		printk(irqerr, netdev->name, info->irq_phy, -res);
> +		return res;
> +	}
> +
> +	/* Set the MAC Address */
> +	memcpy(info->port_mac_addr, netdev->dev_addr, ETH_ALEN);
> +
> +	rm9k_ge_update_afx(info);
> +
> +	/* Enable the slice */
> +	lock_titan_regs();
> +	reg = titan_readl(0x0004);
> +	reg &= ~(0x30000 << (info->slice * 2));	/* enable slice */
> +	reg |= 0x3 << (info->slice * 2);	/* disable DLLs */
> +	titan_writel(reg, 0x0004);
> +	unlock_titan_regs();
> +
> +	/* Set up the Tx descriptor ring */
> +	gpi_writel((u32) info->tx_dma, info->addr_xdma + 0x0014);
> +	info->tx_next_desc = 0;
> +
> +	/* IR register for the XDMA: no Rx_OOD */
> +	gpi_writel(0x80068000, info->addr_xdma + 0x0010);
> +
> +	/* Start the XDMA controller */
> +	gpi_writel(xdma_initval, info->addr_xdma + 0x0000);
> +
> +	/* Set up the Rx skb queue */
> +	res = rm9k_ge_setup_rx_queue(netdev);
> +	if (res)
> +		return res;
> +
> +	/* Rx FIFO BAV2,BAV and DAV settings */
> +	reg = (info->rxfifo_len << 10) | info->rxfifo_start;
> +	gpi_writel(0x100000 | reg, info->addr_fifo_rx + 0x0000);
> +	gpi_writel((0x10 << 20) | (0x10 << 10) | 0x1,
> +		   info->addr_fifo_rx + 0x0004);
> +	gpi_writel(0x200000 | reg, info->addr_fifo_rx + 0x0000);
> +
> +	/* Tx FIFO BAV2, BAV and DAV settings */
> +	reg = (info->txfifo_len << 10) | info->txfifo_start;
> +	gpi_writel(0x100000 | reg, info->addr_fifo_tx + 0x0000);
> +	gpi_writel((0x1 << 20) | (0x1 << 10) | 0x10,
> +		   info->addr_fifo_tx + 0x0004);
> +	gpi_writel(0x200000 | reg, info->addr_fifo_tx + 0x0000);

magic numbers galore


> +	if (hw_hdr_align) {
> +		/*
> +		 * TRTG block enable
> +		 * This is the 1.2 revision of the chip. It has a fix for the
> +		 * IP header alignment. Now, the IP header begins at an
> +		 * aligned address and this wont need an extra copy in the
> +		 * driver. This performance drawback existed in the previous
> +		 * versions of the silicon.
> +		 */
> +		reg = gpi_readl(info->addr_pktproc + 0x003c);
> +		reg |= 0x40000000;
> +		gpi_writel(reg, info->addr_pktproc + 0x003c);
> +
> +		reg |= 0x04000000;
> +		gpi_writel(reg, info->addr_pktproc + 0x003c);
> +		udelay(7);
> +		reg &= ~0x04000000;
> +		gpi_writel(reg, info->addr_pktproc + 0x003c);
> +		udelay(2);

memory barriers / write posting


> +		/* Priority & start of range to checksum */
> +		gpi_writel(0x00010010 + ((ETH_HLEN + NET_IP_ALIGN) << 8),
> +			   info->addr_pktproc + 0x0038);
> +
> +	} else {
> +		/* Priority & start of range to checksum */
> +		gpi_writel(0x00010010 + (ETH_HLEN << 8),
> +			   info->addr_pktproc + 0x0038);
> +	}
> +
> +	gpi_writel(0x00008001, info->addr_pktproc + 0x0000);
> +
> +	gpi_writel(0x00000000, info->addr_mac + 0x0028);
> +	gpi_writel(0x00000000, info->addr_mac + 0x014c);
> +	wmb();
> +
> +	/* Start the Tx activity */
> +	gpi_writel(0xe1b7, info->addr_mac + 0x0044);
> +	gpi_writel(rm9k_eth_tx_frame_len(netdev->mtu), info->addr_mac + 0x0058);
> +	gpi_writel(RM9K_GE_TMAC_OPTIONS | 0x1, info->addr_mac + 0x0040);
> +
> +	/* Destination Address drop bit */
> +	gpi_writel(0x0009, info->addr_mac + 0x0004);
> +	gpi_writel(0x3, info->addr_mac + 0x0018);
> +	gpi_writel(rm9k_eth_rx_frame_len(netdev->mtu), info->addr_mac + 0x0008);
> +
> +	/* Start the Rx activity */
> +	gpi_writel(RM9K_GE_RMAC_OPTIONS | 0x1, info->addr_mac + 0x0000);
> +
> +	lock_titan_regs();
> +
> +	/* Enable the info->slice interrupts */
> +	reg = titan_readl(0x0050);
> +	titan_writel(reg | (0x1f << (info->slice * 5)), 0x0050);
> +
> +	/* Enable the Interrupts for Tx and Rx */
> +	reg = titan_readl(0x0058);
> +	reg |= 0x3 << (info->slice * 8);
> +	titan_writel(reg, 0x0058);
> +
> +	unlock_titan_regs();
> +
> +	/* Start the MDIO */
> +	rm9k_ge_init_mdio(info->addr_mdio, info->mdio_channel);
> +
> +	/* Reset the PHY */
> +	end = jiffies + HZ;
> +	rm9k_ge_mdio_write(PHY_ADDR, RM9K_GE_MDIO_PHY_BMCR, 0x8000);
> +	while (time_before(jiffies, end)) {
> +		rm9k_ge_mdio_read(PHY_ADDR, RM9K_GE_MDIO_PHY_BMCR, &phy_reg);
> +		if (~phy_reg & 0x8000)
> +			break;
> +		yield();
> +	}
> +	if (unlikely(phy_reg & 0x8000)) {
> +		free_irq(info->irq_phy, netdev);
> +		free_irq(netdev->irq, netdev);
> +		printk(KERN_ERR "%s: PHY reset timed out\n", netdev->name);
> +		return -ETIME;
> +	}
> +
> +	/* Set up the PHY */
> +	if (autoneg) {
> +		/* Force the connection to be re-negotiated. */
> +		rm9k_ge_mdio_write(PHY_ADDR, RM9K_GE_MDIO_PHY_BMCR, 0x1000);
> +		rm9k_ge_mdio_write(PHY_ADDR, RM9K_GE_MDIO_PHY_INTMASK, 0x4010);
> +	} else {
> +		u32 mac_val = full_duplex ? 0x00000000 : 0x00001000;
> +		phy_reg = full_duplex ? 0x0100 : 0x0000;
> +
> +		switch (speed) {
> +			case speed_1g:
> +				mac_val |= 0x00000200;
> +				phy_reg |= 0x0040;
> +				break;
> +			case speed_100m:
> +				mac_val |= 0x00000100;
> +				phy_reg |= 0x2000;
> +				break;
> +		}
> +		gpi_writel(mac_val, info->addr_mac + 0x0150);
> +		rm9k_ge_mdio_write(PHY_ADDR, RM9K_GE_MDIO_PHY_BMCR, phy_reg);
> +		rm9k_ge_mdio_write(PHY_ADDR, RM9K_GE_MDIO_PHY_INTMASK, 0x4000);
> +	}
> +
> +	gpi_writel(0x00000003, info->addr_mac + 0x014c);
> +
> +	rm9k_ge_mdio_read(PHY_ADDR, RM9K_GE_MDIO_PHY_STATUS, &phy_reg);
> +	if (phy_reg & 0x0004) {
> +		pr_debug("%s: link is up\n", netdev->name);
> +		netif_carrier_on(netdev);
> +	} else {
> +		pr_debug("%s: link is down\n", netdev->name);
> +		netif_carrier_off(netdev);
> +	}
> +
> +	/* Set up statistics gathering */
> +	rm9k_ge_update_stats(netdev);
> +	mod_timer(&info->stat_timer, jiffies + HZ * 600);
> +
> +	return 0;
> +}
> +
> +
> +
> +/*
> + * Close the network device
> + */
> +static int rm9k_ge_stop(struct net_device *netdev)
> +{
> +	rm9k_ge_port_info * const info = netdev_priv(netdev);
> +	u32 reg;
> +
> +	del_timer(&info->stat_timer);

del_timer_sync()


> +	/* Reset the XDMA */
> +	gpi_writel(0x80080000, info->addr_xdma + 0x0000);
> +
> +	/* Disable the GMII data paths */
> +	gpi_writel(0x00000000, info->addr_mac + 0x014c);
> +
> +	/* Reset the FIFOs */
> +	gpi_writel(0x00100000 | (info->rxfifo_len << 10) | info->rxfifo_start,
> +		   info->addr_fifo_rx + 0x0000);
> +	gpi_writel(0x00100000 | (info->txfifo_len << 10) | info->txfifo_start,
> +		   info->addr_fifo_tx + 0x0000);
> +
> +	/* Reset the packet processor */
> +	gpi_writel(0x00000001, info->addr_pktproc + 0x0000);
> +
> +	/* Reset RX and TX MACs */
> +	gpi_writel(RM9K_GE_RMAC_OPTIONS, info->addr_mac + 0x0000);
> +	gpi_writel(RM9K_GE_TMAC_OPTIONS, info->addr_mac + 0x0040);
> +
> +	/* Power down the PHY */
> +	rm9k_ge_mdio_write(PHY_ADDR, RM9K_GE_MDIO_PHY_INTMASK, 0x0000);
> +	rm9k_ge_mdio_write(PHY_ADDR, RM9K_GE_MDIO_PHY_BMCR, 0x0800);

magic numbers


> +	rm9k_ge_cleanup_mdio(info->mdio_channel);
> +
> +	/* Disable the Tx and Rx interrupts */
> +	lock_titan_regs();
> +	reg = titan_readl(0x0058);
> +	titan_writel(reg & ~(0xff << (info->slice * 8)), 0x0058);
> +	iob();
> +	unlock_titan_regs();
> +
> +	/* Release the interrupts */
> +	free_irq(info->irq_phy, netdev);
> +	free_irq(netdev->irq, netdev);
> +
> +	rm9k_ge_free_tx_queue(netdev);
> +	rm9k_ge_free_rx_queue(netdev);
> +
> +	/* Disable the Tx and Rx interrupts */
> +	lock_titan_regs();
> +	reg = titan_readl(0x0058);
> +	titan_writel(reg & ~(0xff << (info->slice * 8)), 0x0058);
> +	unlock_titan_regs();
> +
> +	return 0;
> +}
> +
> +
> +
> +/*
> + * Set up the skb queue for the RX direction
> + */
> +static int rm9k_ge_setup_rx_queue(struct net_device * netdev)
> +{
> +	rm9k_ge_port_info * const info = netdev_priv(netdev);
> +	struct sk_buff_head * const q = &info->rx_queue;
> +	rx_desc_t * d;
> +
> +	if (!skb_queue_empty(q))
> +		return -EBUSY;
> +
> +	spin_lock_irq(&q->lock);

spin_lock_irqsave is preferred -- much less fragile


> +	for (d = info->rx_desc_area;
> +	     d < info->rx_desc_area + info->rx_ring_size;
> +	     d++) {
> +		struct sk_buff * const s =
> +			alloc_skb(rm9k_eth_rx_frame_len(netdev->mtu), GFP_KERNEL);
> +
> +		if (!s) {
> +			__skb_queue_purge(q);
> +			spin_unlock_irq(&q->lock);
> +			return -ENOMEM;
> +		}
> +
> +		d->cpu_part =
> +			dma_map_single(netdev->class_dev.dev, s->data,
> +				       rm9k_eth_rx_frame_len(netdev->mtu),
> +				       DMA_FROM_DEVICE);
> +		d->xdma_part = RM9K_GE_RX_BUFFER_OWNED;
> +		s->dev = netdev;
> +		rm9k_ge_rxdesc_to_skb(s, d);
> +		skb_queue_tail(q, s);
> +	}
> +
> +	gpi_writel((unsigned long) info->rx_dma, info->addr_xdma + 0x0018);
> +	gpi_writel(info->rx_ring_size, info->addr_xdma + 0x0008);
> +	spin_unlock_irq(&q->lock);
> +	return 0;
> +}
> +
> +
> +
> +/*
> + * Free the RX queue
> + */
> +static void rm9k_ge_free_rx_queue(struct net_device *netdev)
> +{
> +	rm9k_ge_port_info * const info = netdev_priv(netdev);
> +	u32 reg;
> +
> +	/* Stop the RX DMA */
> +	reg = gpi_readl(info->addr_xdma + 0x0000);
> +	gpi_writel(reg | 0x000c0000, info->addr_xdma + 0x0000);
> +
> +	/* Disable the RMAC */
> +	gpi_writel(RM9K_GE_RMAC_OPTIONS, info->addr_mac + 0x0000);
> +	iob();
> +
> +	skb_queue_purge(&info->rx_queue);
> +}
> +
> +
> +
> +/*
> + * Free the TX queue
> + */
> +static void rm9k_ge_free_tx_queue(struct net_device *netdev)
> +{
> +	rm9k_ge_port_info * const info = netdev_priv(netdev);
> +	u32 reg;
> +
> +	/* Stop the Tx DMA */
> +	reg = gpi_readl(info->addr_xdma + 0x0000);
> +	gpi_writel(reg | 0xc0000000, info->addr_xdma + 0x0000);
> +
> +	/* Disable the TMAC */
> +	gpi_writel(RM9K_GE_TMAC_OPTIONS, info->addr_mac + 0x0040);
> +	iob();
> +
> +	skb_queue_purge(&info->tx_queue);
> +}
> +
> +
> +
> +/*
> + * Queue the packet for Tx. Currently no support for zero copy,
> + * checksum offload and Scatter Gather. The chip does support
> + * Scatter Gather only. But, that wont help here since zero copy
> + * requires support for Tx checksumming also.
> + */
> +static int rm9k_ge_start_xmit(struct sk_buff *skb, struct net_device *netdev)
> +{
> +	rm9k_ge_port_info * const info = netdev_priv(netdev);
> +
> +	struct device *device = netdev->class_dev.dev;
> +	const unsigned int curr_desc = info->tx_next_desc;
> +	tx_desc_t * const tx_curr = info->tx_desc_area + curr_desc;
> +
> +	*tx_curr = dma_map_single(device, skb->data, skb->len, DMA_TO_DEVICE)
> +		   | ((tx_desc_t) (skb->len & 0x00003fff) << 32)
> +		   | (1ULL << 48)
> +		   | (1ULL << 53)
> +		   | (1ULL << 63);
> +
> +	rm9k_ge_txdesc_to_skb(skb, tx_curr);
> +	skb_queue_tail(&info->tx_queue, skb);
> +	info->tx_next_desc = (curr_desc + 1) % info->tx_ring_size;
> +	gpi_writel(0x1, info->addr_xdma + 0x0004);
> +	netdev->trans_start = jiffies;
> +	pr_debug("%s: packet sent\n", netdev->name);

locking against TX completion?


> +static struct sk_buff * rm9k_ge_get_rx_skb(rm9k_ge_port_info * info)
> +{
> +	unsigned long flg;
> +	struct sk_buff * s;
> +
> +	spin_lock_irqsave(&info->rx_queue.lock, flg);
> +	s = skb_peek(&info->rx_queue);
> +	if (s) {
> +		const rx_desc_t * const desc =
> +			rm9k_ge_rxdesc_from_skb(s);
> +		s = (desc->xdma_part & RM9K_GE_RX_BUFFER_OWNED)
> +			? NULL : __skb_dequeue(&info->rx_queue);
> +	}
> +	spin_unlock_irqrestore(&info->rx_queue.lock, flg);
> +

rx queue rarely needs a lock


> +#define	TX_THRESHOLD	4
> +
> +/*
> + * Receive packets and send them to the kernel.
> + */
> +static int rm9k_ge_receive_queue(struct net_device *netdev, unsigned int max)
> +{
> +	rm9k_ge_port_info * const info = netdev_priv(netdev);
> +	unsigned long received_packets = 0;
> +
> +	while (info->rx_work_limit) {
> +		static const unsigned int error_mask =
> +			(0x1 << 27) | (0x1 << 20) | (0x1 << 19) | (0x1 << 15);
> +		struct sk_buff * skb_reuse;
> +		unsigned int len, cmd_sts, checksum;
> +		rx_desc_t * desc;
> +
> +		struct sk_buff * skb = rm9k_ge_get_rx_skb(info);
> +		if (!skb)
> +			break;
> +
> +		info->rx_work_limit--;
> +		pr_debug("%s: packet received\n", netdev->name);
> +
> +		desc = rm9k_ge_rxdesc_from_skb(skb);
> +		dma_unmap_single(netdev->class_dev.dev, desc->cpu_part,
> +				 rm9k_eth_rx_frame_len(netdev->mtu),
> +				 DMA_FROM_DEVICE);
> +		len = (desc->xdma_part >> 32) & 0x7fff;
> +		cmd_sts = desc->xdma_part >> 32;
> +		checksum = ntohs((desc->xdma_part >> 16) & 0x0000ffff);
> +
> +		if (cmd_sts & error_mask) {
> +			info->stats.rx_errors++;
> +			desc->cpu_part =
> +				dma_map_single(netdev->class_dev.dev,
> +					       skb->data,
> +					       rm9k_eth_rx_frame_len(netdev->mtu),
> +					       DMA_FROM_DEVICE);
> +			desc->xdma_part = RM9K_GE_RX_BUFFER_OWNED;
> +			skb_queue_tail(&info->rx_queue, skb);
> +			gpi_writel(1, info->addr_xdma + 0x0008);
> +			continue;
> +		}
> +
> +		received_packets++;
> +
> +		if (hw_hdr_align) {
> +			/* Hardware header alignment supported */
> +			skb_reuse = alloc_skb(rm9k_eth_rx_frame_len(netdev->mtu),
> +					      GFP_ATOMIC);
> +			if (skb_reuse) {
> +				skb_reserve(skb, NET_IP_ALIGN);
> +				skb_put(skb, len - NET_IP_ALIGN);
> +			} else {
> +				skb_reuse = skb;
> +				skb = NULL;
> +			}
> +		} else {
> +			/* Header alignment must be done in software */
> +			skb_reuse = skb;
> +
> +			skb = alloc_skb(len + NET_IP_ALIGN, GFP_ATOMIC);

dev_alloc_skb()


> +			if (skb) {
> +				skb->dev = netdev;
> +				skb_reserve(skb, NET_IP_ALIGN);
> +				skb_put(skb, len);
> +				memcpy(skb->data, skb_reuse->data, len);
> +			}
> +		}
> +
> +		if (skb) {
> +			skb->ip_summed = CHECKSUM_HW;
> +			skb->csum = checksum;
> +			skb->protocol = eth_type_trans(skb, netdev);
> +			netif_receive_skb(skb);
> +		} else {
> +			info->stats.rx_dropped++;
> +		}
> +
> +		desc->cpu_part =
> +			dma_map_single(netdev->class_dev.dev, skb_reuse->data,
> +				       rm9k_eth_rx_frame_len(netdev->mtu),
> +				       DMA_FROM_DEVICE);
> +		desc->xdma_part = RM9K_GE_RX_BUFFER_OWNED;
> +		skb_reuse->dev = netdev;
> +		rm9k_ge_rxdesc_to_skb(skb_reuse, desc);
> +		skb_queue_tail(&info->rx_queue, skb_reuse);
> +		gpi_writel(1, info->addr_xdma + 0x0008);
> +	}
> +
> +	return received_packets;
> +}
> +
> +
> +
> +/*
> + * Main function to handle the polling for Rx side NAPI.
> + * Receive interrupts have been disabled at this point.
> + */
> +static int rm9k_ge_poll(struct net_device *netdev, int *budget)
> +{
> +	rm9k_ge_port_info * const info = netdev_priv(netdev);
> +	int work_done = 0;
> +	unsigned long flags;
> +	u32 reg;
> +	const u32 mask = 0x1 << (info->slice * 8);
> +
> +	info->rx_work_limit = *budget;
> +	if (info->rx_work_limit > netdev->quota)
> +		info->rx_work_limit = netdev->quota;
> +
> +	do {
> +		work_done += rm9k_ge_receive_queue(netdev, 0);
> +
> +		/* Out of quota and there is work to be done */
> +		if (info->rx_work_limit < 0) {
> +			*budget -= work_done;
> +			netdev->quota -= work_done;
> +			return 1;
> +		}
> +
> +		reg = (titan_readl(0x0048) | titan_readl(0x004c)) & mask;
> +		titan_writel(reg, 0x0048);
> +		titan_writel(reg, 0x004c);
> +	} while (reg);
> +
> +	/*
> +	 * No more packets on the poll list. Turn the interrupts
> +	 * back on and we should be able to catch the new
> +	 * packets in the interrupt handler
> +	 */
> +	if (!work_done)
> +		work_done = 1;
> +
> +	*budget -= work_done;
> +	netdev->quota -= work_done;
> +
> +	spin_lock_irqsave(&info->lock, flags);
> +
> +	/* Remove us from the poll list */
> +	netif_rx_complete(netdev);
> +
> +	spin_unlock_irqrestore(&info->lock, flags);
> +
> +	/* Re-enable interrupts */
> +	lock_titan_regs();
> +	reg = titan_readl(0x0058);
> +	titan_writel(reg | mask, 0x0058);
> +	unlock_titan_regs();
> +
> +	return 0;
> +}
> +
> +
> +
> +static inline unsigned long long rm9k_ge_sctr(void __iomem * p)
> +{
> +	return	((unsigned long long) (gpi_readl(p + 8) & 0x0000ffff) << 32)
> +		| ((unsigned long long) (gpi_readl(p + 4) & 0x0000ffff) << 16)
> +		| (gpi_readl(p) & 0x0000ffff);
> +}
> +
> +
> +
> +static void rm9k_ge_update_stats(struct net_device *netdev)
> +{
> +	unsigned long flg;
> +	unsigned long long good_rx_frames, total_rx_frames;
> +	rm9k_ge_port_info * const info = netdev_priv(netdev);
> +	struct net_device_stats * const s = &info->stats;
> +
> +	spin_lock_irqsave(&info->stat_lock, flg);
> +
> +	/* Latch and clear counters */
> +	gpi_writel(0x00000022, info->addr_mstat + 0x0028);
> +	iob();
> +
> +	good_rx_frames = rm9k_ge_sctr(info->addr_mstat + 0x0040),
> +	total_rx_frames = rm9k_ge_sctr(info->addr_mstat + 0x0060);
> +
> +	s->rx_packets += good_rx_frames;
> +	s->rx_bytes += rm9k_ge_sctr(info->addr_mstat + 0x0050);
> +	s->rx_errors +=  total_rx_frames - good_rx_frames;
> +	s->multicast += rm9k_ge_sctr(info->addr_mstat + 0x00a0);
> +	s->rx_crc_errors += rm9k_ge_sctr(info->addr_mstat + 0x00e0);
> +	s->rx_frame_errors += rm9k_ge_sctr(info->addr_mstat + 0x00f0);
> +	s->rx_length_errors += rm9k_ge_sctr(info->addr_mstat + 0x0120)
> +				+ rm9k_ge_sctr(info->addr_mstat + 0x0130)
> +				+ rm9k_ge_sctr(info->addr_mstat + 0x0140)
> +				+ rm9k_ge_sctr(info->addr_mstat + 0x0150)
> +				+ rm9k_ge_sctr(info->addr_mstat + 0x0160);
> +
> +	s->tx_packets += rm9k_ge_sctr(info->addr_mstat + 0x0250);
> +	s->tx_bytes += rm9k_ge_sctr(info->addr_mstat + 0x0260);
> +
> +	spin_unlock_irqrestore(&info->stat_lock, flg);
> +	mod_timer(&info->stat_timer, jiffies + HZ * 600);
> +}
> +
> +
> +
> +/*
> + * Update the MAC address. Note that we have to write the
> + * address in three station registers, 16 bits each. And this
> + * has to be done for TMAC and RMAC
> + */
> +static void rm9k_ge_update_mac_address(struct net_device *netdev)
> +{
> +	rm9k_ge_port_info *const info = netdev_priv(netdev);
> +	u8 p_addr[6];
> +
> +	memcpy(info->port_mac_addr, netdev->dev_addr, ETH_ALEN);
> +	memcpy(p_addr, netdev->dev_addr, ETH_ALEN);
> +
> +	/* Update the Address Filtering Match tables */
> +	rm9k_ge_update_afx(info);
> +
> + 	pr_debug("%s: station MAC : %d %d %d %d %d %d\n",
> +		 netdev->name,
> + 		 p_addr[5], p_addr[4], p_addr[3],
> + 		 p_addr[2], p_addr[1], p_addr[0]);
> +
> + 	/* Set the MAC address here for TMAC and RMAC */
> + 	gpi_writel((p_addr[5] << 8) | p_addr[4], info->addr_mac + 0x004c);
> + 	gpi_writel((p_addr[3] << 8) | p_addr[2], info->addr_mac + 0x0050);
> + 	gpi_writel((p_addr[1] << 8) | p_addr[0], info->addr_mac + 0x0054);
> +
> + 	gpi_writel((p_addr[5] << 8) | p_addr[4], info->addr_mac + 0x000c);
> + 	gpi_writel((p_addr[3] << 8) | p_addr[2], info->addr_mac + 0x0010);
> + 	gpi_writel((p_addr[1] << 8) | p_addr[0], info->addr_mac + 0x0014);


> + * Set the MAC address of the Ethernet device
> + */
> +static int rm9k_ge_set_mac_address(struct net_device *netdev, void *addr)
> +{
> +	rm9k_ge_port_info * const info = netdev_priv(netdev);
> +	struct sockaddr *sa = addr;
> +
> +	memcpy(netdev->dev_addr, sa->sa_data, netdev->addr_len);
> +
> +	spin_lock_irq(&info->lock);
> +	rm9k_ge_update_mac_address(netdev);
> +	spin_unlock_irq(&info->lock);
> +
> +	return 0;
> +}
> +
> +#if defined(CONFIG_NET_POLL_CONTROLLER)
> +/*
> + * Netpoll support
> + */
> +static void rm9k_ge_netpoll(struct net_device *netdev)
> +{
> +	rm9k_ge_main_int_handler(netdev->irq, netdev, NULL);
> +}
> +#endif
> +
> +
> +
> +/*
> + * Get the Ethernet device stats
> + */
> +static struct net_device_stats * rm9k_ge_get_stats(struct net_device *netdev)
> +{
> +	rm9k_ge_port_info * const info = netdev_priv(netdev);
> +	rm9k_ge_update_stats(netdev);
> +	return &info->stats;
> +}
> +
> +/*
> + * Initialize the device as an Ethernet device
> + */
> +static int __init rm9k_ge_probe(struct device *dev)
> +{
> +	int err;
> +	unsigned int n_desc, n_rx, n_tx;
> +	struct platform_device * pdv;
> +	struct net_device * netdev;
> +	rm9k_ge_port_info * info;
> +	const struct resource
> +		*rsrc_slice, *rsrc_phy, *rsrc_rxdma, *rsrc_txdma,
> +		*rsrc_fifomem_rx, *rsrc_fifomem_tx, *rsrc_mac,
> +		*rsrc_mstat, *rsrc_pktproc, *rsrc_xdma, *rsrc_mdio,
> +		*rsrc_fifo_rx, *rsrc_fifo_tx, *rsrc_irq_main, *rsrc_irq_phy;
> +
> +	/* Get the platform dev. */
> +	if (unlikely(dev->bus != &platform_bus_type)) {
> +		err = -ENODEV;
> +		goto out;
> +	}
> +
> +	pdv = to_platform_device(dev);
> +
> +	rsrc_slice = rm9k_get_resource(pdv, 0, RM9K_GE_RESOURCE_GPI_SLICE);
> +	rsrc_phy = rm9k_get_resource(pdv, IORESOURCE_MEM, RM9K_GE_RESOURCE_PHY);
> +	rsrc_rxdma = rm9k_get_resource(pdv, IORESOURCE_MEM, RM9K_GE_RESOURCE_DMADESC_RX);
> +	rsrc_txdma = rm9k_get_resource(pdv, IORESOURCE_MEM, RM9K_GE_RESOURCE_DMADESC_TX);
> +	rsrc_fifomem_rx = rm9k_get_resource(pdv, 0,RM9K_GE_RESOURCE_FIFOMEM_RX);
> +	rsrc_fifomem_tx = rm9k_get_resource(pdv, 0,RM9K_GE_RESOURCE_FIFOMEM_TX);
> +	rsrc_mac = rm9k_get_resource(pdv, IORESOURCE_MEM, RM9K_GE_RESOURCE_MAC);
> +	rsrc_mstat = rm9k_get_resource(pdv, IORESOURCE_MEM, RM9K_GE_RESOURCE_MSTAT);
> +	rsrc_pktproc = rm9k_get_resource(pdv, IORESOURCE_MEM, RM9K_GE_RESOURCE_PKTPROC);
> +	rsrc_xdma = rm9k_get_resource(pdv, IORESOURCE_MEM, RM9K_GE_RESOURCE_XDMA);
> +	rsrc_mdio = rm9k_get_resource(pdv, 0, RM9K_GE_RESOURCE_MDIO_CHANNEL);
> +	rsrc_fifo_rx = rm9k_get_resource(pdv, IORESOURCE_MEM, RM9K_GE_RESOURCE_FIFO_RX);
> +	rsrc_fifo_tx = rm9k_get_resource(pdv, IORESOURCE_MEM, RM9K_GE_RESOURCE_FIFO_TX);
> +	rsrc_irq_main = rm9k_get_resource(pdv, IORESOURCE_IRQ, RM9K_GE_RESOURCE_IRQ_MAIN);
> +	rsrc_irq_phy = rm9k_get_resource(pdv, IORESOURCE_IRQ, RM9K_GE_RESOURCE_IRQ_PHY);
> +
> +	if (!rsrc_slice || !rsrc_phy || !rsrc_rxdma || !rsrc_fifo_rx
> +	    || !rsrc_fifo_tx || !rsrc_txdma || !rsrc_fifomem_rx
> +	    || !rsrc_mstat || !rsrc_fifomem_tx || !rsrc_mac || !rsrc_mdio
> +	    || !rsrc_pktproc || !rsrc_xdma || !rsrc_irq_main
> +	    || !rsrc_irq_phy) {
> +	    	err = -ENODEV;
> +		goto out;
> +	}

leak, if some succeed but others fail


> +	/* Compute RX and TX ring sizes */
> +	n_desc = (rsrc_rxdma->end - rsrc_rxdma->start + 1) / sizeof (rx_desc_t);
> +	switch (n_desc) {
> +		case 64 ... 127:
> +			n_rx = 64;
> +			break;
> +		case 128 ... 255:
> +			n_rx = 128;
> +			break;
> +		case 256 ... 511:
> +			n_rx = 256;
> +			break;
> +		default:
> +			n_rx = (n_desc >= 512) ? 512 : 0;
> +			break;
> +	}
> +
> +	n_desc = (rsrc_txdma->end - rsrc_txdma->start + 1) / sizeof (tx_desc_t);
> +	switch (n_desc) {
> +		case 64 ... 127:
> +			n_tx = 64;
> +			break;
> +		case 128 ... 255:
> +			n_tx = 128;
> +			break;
> +		case 256 ... 511:
> +			n_tx = 256;
> +			break;
> +		default:
> +			n_tx = (n_desc >= 512) ? 512 : 0;
> +			break;
> +	}
> +	if ((!n_rx || !n_tx)) {
> +		err = -EINVAL;
> +		goto out;
> +	}
> +
> +	netdev = alloc_etherdev(sizeof(rm9k_ge_port_info)
> +				+ (n_rx + n_tx) * sizeof (struct sk_buff *));

add some parens for increased readability


> +	if (!netdev) {
> +		err = -ENODEV;
> +		goto out;
> +	}
> +
> +	dev_set_drvdata(dev, netdev);
> +	SET_NETDEV_DEV(netdev, dev);
> +	netdev->irq = rsrc_irq_main->start;
> +	netdev->open = rm9k_ge_open;
> +	netdev->stop = rm9k_ge_stop;
> +	netdev->hard_start_xmit = rm9k_ge_start_xmit;
> +	netdev->get_stats = rm9k_ge_get_stats;
> +	netdev->set_multicast_list = rm9k_ge_set_multi;
> +	netdev->set_mac_address = rm9k_ge_set_mac_address;
> +	netdev->mtu = ETH_DATA_LEN;
> +#if defined(CONFIG_NET_POLL_CONTROLLER)
> +	netdev->poll_controller = rm9k_ge_netpoll;
> +#endif
> +
> +	/* Tx timeout */
> +	netdev->tx_timeout = rm9k_ge_tx_timeout;
> +	netdev->watchdog_timeo = 2 * HZ;
> +
> +	/* Set these to very high values */
> +	netdev->poll = rm9k_ge_poll;
> +	netdev->weight = 64;
> +	netdev->base_addr = 0;
> +
> +	netdev->change_mtu = rm9k_ge_change_mtu;
> +
> +	info = netdev_priv(netdev);
> +	spin_lock_init(&info->stat_lock);
> +	init_timer(&info->stat_timer);
> +	info->stat_timer.function = (void *) rm9k_ge_update_stats;
> +	info->stat_timer.data = (unsigned long) netdev;
> +
> +	skb_queue_head_init(&info->rx_queue);
> +	skb_queue_head_init(&info->tx_queue);
> +
> +	info->irq_phy = rsrc_irq_phy->start;
> +	info->port_num = pdv->id;
> +	info->slice = rsrc_slice->start;
> +	info->mdio_channel = rsrc_mdio->start;
> +
> +	info->addr_mdio = ioremap_nocache(
> +		rsrc_phy->start, rsrc_phy->end + 1 - rsrc_phy->start);

check for NULL return value, handle error


> +	/* Set up RX descriptor ring */
> +	info->rx_ring_size = n_rx;
> +	info->rx_desc_area = (rx_desc_t *) ioremap_nocache(
> +		rsrc_rxdma->start, n_rx * sizeof (rx_desc_t *));
> +	info->rx_dma = (dma_addr_t) rsrc_rxdma->start;
> +
> +	/* Set up the TX descriptor ring */
> +	info->tx_ring_size = n_tx;
> +	info->tx_desc_area = (tx_desc_t *) ioremap_nocache(
> +		rsrc_txdma->start, n_tx * sizeof (tx_desc_t *));
> +	info->tx_dma = (dma_addr_t) rsrc_txdma->start;
> +
> +	/* Set up the RX and TX FIFOs */
> +	info->rxfifo_start = rsrc_fifomem_rx->start;
> +	info->rxfifo_len = rsrc_fifomem_rx->end + 1 - rsrc_fifomem_rx->start;
> +	info->txfifo_start = rsrc_fifomem_tx->start;
> +	info->txfifo_len = rsrc_fifomem_tx->end + 1 - rsrc_fifomem_tx->start;
> +
> +	info->addr_mac = ioremap_nocache(
> +		rsrc_mac->start, rsrc_mac->end + 1 - rsrc_mac->start);
> +	info->addr_pktproc = ioremap_nocache(
> +		rsrc_pktproc->start,
> +		rsrc_pktproc->end + 1 - rsrc_pktproc->start);
> +	info->addr_xdma = ioremap_nocache(
> +		rsrc_xdma->start, rsrc_xdma->end + 1 - rsrc_xdma->start);
> +	info->addr_mstat = ioremap_nocache(
> +		rsrc_mstat->start, rsrc_mstat->end + 1 - rsrc_mstat->start);
> +	info->addr_fifo_rx = ioremap_nocache(
> +		rsrc_fifo_rx->start,
> +		rsrc_fifo_rx->end + 1 - rsrc_fifo_rx->start);
> +	info->addr_fifo_tx = ioremap_nocache(
> +		rsrc_fifo_tx->start,
> +		rsrc_fifo_tx->end + 1 - rsrc_fifo_tx->start);

check all these for NULL, handle failure


> +	spin_lock_init(&info->lock);
> +
> +	/* set MAC addresses */
> +	memcpy(netdev->dev_addr, hwaddr, ETH_ALEN);
> +
> +	err = register_netdev(netdev);
> +
> +	if (err)
> +		goto out_free_netdev;
> +
> +	return 0;
> +
> +out_free_netdev:
> +	if (info->addr_mdio) iounmap(info->addr_mdio);
> +	if (info->rx_desc_area) iounmap(info->rx_desc_area);
> +	if (info->tx_desc_area) iounmap(info->tx_desc_area);
> +	if (info->addr_mac) iounmap(info->addr_mac);
> +	if (info->addr_pktproc) iounmap(info->addr_pktproc);
> +	if (info->addr_xdma) iounmap(info->addr_xdma);
> +	if (info->addr_mstat) iounmap(info->addr_mstat);
> +	if (info->addr_fifo_rx) iounmap(info->addr_fifo_rx);
> +	if (info->addr_fifo_tx) iounmap(info->addr_fifo_tx);
> +	kfree(netdev);
> +
> +out:
> +	return err;
> +}
> +
> +static int __exit rm9k_ge_remove(struct device *dev)
> +{
> +	struct net_device * const netdev =
> +		(struct net_device *) dev_get_drvdata(dev);
> +	unregister_netdev(netdev);
> +	free_netdev(netdev);
> +	return 0;
> +}
> +
> +static struct device_driver rm9k_ge_driver = {
> +	.name   = RM9K_GE_NAME,
> +	.bus    = &platform_bus_type,
> +	.probe  = rm9k_ge_probe,
> +	.remove	= __exit_p(rm9k_ge_remove)
> +};
> +
> +static void rm9k_ge_init_mdio(void * addr, unsigned int chan)
> +{
> +	u32 reg;
> +
> +	/* Take the MDIO channel out of reset */
> +	lock_titan_regs();
> +	reg = titan_readl(0x0004);
> +	reg &= ~(0x1 << (11 + chan));
> +	titan_writel(reg, 0x0004);
> +	unlock_titan_regs();
> +	iob();
> +
> +	if(rm9k_ge_mdio_setup(addr, 0x1f)) {
> +		lock_titan_regs();
> +		reg = titan_readl(0x0004);
> +		reg |= 0x1 << (11 + chan);
> +		titan_writel(reg, 0x0004);
> +		unlock_titan_regs();
> +		iob();
> +	}
> +}
> +
> +static void rm9k_ge_cleanup_mdio(unsigned int chan)
> +{
> +	u32 reg;
> +	rm9k_ge_mdio_cleanup();
> +
> +	/* Reset the MDIO channel */
> +	lock_titan_regs();
> +	reg = titan_readl(0x0004);
> +	reg |= 0x1 << (11 + chan);
> +	titan_writel(reg, 0x0004);
> +	unlock_titan_regs();
> +}
> +
> +/*
> + * Register the Titan GE with the kernel
> + */
> +static int __init rm9k_ge_init_module(void)
> +{
> +	unsigned int rev;
> +
> +	rev = read_c0_prid() & 0x000000ff;
> +	hw_hdr_align = rev > 0x30;
> +
> +	if (driver_register(&rm9k_ge_driver)) {
> +		printk(KERN_ERR "%s: Driver registration failed\n",
> +		       rm9k_ge_name);
> +		return 1;
> +	}
> +
> +	printk(KERN_NOTICE "%s: RM9K Gigabit Ethernet Driver loaded "
> +	       "(H/W rev. %#04x)\n",
> +	       rm9k_ge_name, rev);
> +	return 0;
> +}
> +
> +/*
> + * Unregister the Titan GE from the kernel
> + */
> +static void __exit rm9k_ge_cleanup_module(void)
> +{
> +	driver_unregister(&rm9k_ge_driver);
> +}
> +
> +MODULE_AUTHOR("Thomas Koeller <thomas.koeller@baslerweb.com>");
> +MODULE_DESCRIPTION("Rm9k gigabit ethernet driver");
> +MODULE_LICENSE("GPL");
> +
> +MODULE_PARM_DESC(autoneg, "Enable speed/duplex autonegotiation (bool)");
> +MODULE_PARM_DESC(full_duplex, "Force full/half duplex mode (bool)");
> +MODULE_PARM_DESC(speed, "Force speed (0 - 10MBit, 1 - 100MBit, 2 - 1GBit)");
> +MODULE_PARM_DESC(hwaddr, "Ethernet address (xx:xx:xx:xx:xx:xx)");
> +
> +module_init(rm9k_ge_init_module);
> +module_exit(rm9k_ge_cleanup_module);
> Index: linux-excite/drivers/net/rm9k_mdio.c
> ===================================================================
> --- /dev/null	1970-01-01 00:00:00.000000000 +0000
> +++ linux-excite/drivers/net/rm9k_mdio.c	2006-06-23 13:28:55.000000000 +0100
> @@ -0,0 +1,165 @@
> +/*
> + * drivers/net/titan_mdio.c - Driver for Titan ethernet ports
> + *
> + * Copyright (C) 2003 PMC-Sierra Inc.
> + * Author : Manish Lachwani (lachwani@pmc-sierra.com)
> + *
> + * 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; either version 2
> + * of the License, or (at your option) any later version.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> + * GNU General Public License for more details.
> + *
> + * You should have received a copy of the GNU General Public License
> + * along with this program; if not, write to the Free Software
> + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
> + *
> + * Management Data IO (MDIO) driver for the Titan GMII. Interacts with the Marvel PHY
> + * on the Titan. No support for the TBI as yet.
> + *
> + */
> +
> +#include <linux/types.h>
> +#include <linux/ioport.h>
> +#include <asm/io.h>
> +#include <asm/delay.h>
> +
> +#include "rm9k_mdio.h"
> +
> +#define MDIO_DEBUG
> +
> +#define gpi_readl(__a__)		__raw_readl((__a__))
> +#define gpi_writel(__v__, __a__)	__raw_writel((__v__), (__a__))
> +
> +/*
> + * Local constants
> + */
> +#define MAX_CLKA		1023
> +#define MAX_PHY_DEV		31
> +#define MAX_PHY_REG		31
> +#define	READ_OPCODE		(0x2 << 8)
> +#define WRITE_OPCODE		(0x1 << 8)
> +#define MAX_MDIO_POLL		100
> +
> +/*
> + * Titan MDIO and SCMB register offsets
> + */
> +#define SCMB_CONTROL			0x00  /* SCMB Control */
> +#define SCMB_CLKA			0x04  /* SCMB Clock A */
> +#define MDIO_COMMAND			0x10  /* MDIO Command */
> +#define MDIO_DEVICE_PORT_ADDRESS	0x14  /* MDIO Device and Port addrs */
> +#define MDIO_DATA			0x18  /* MDIO Data */
> +#define MDIO_INTERRUPTS			0x1c  /* MDIO Interrupts */
> +
> +void __iomem * base = NULL;
> +
> +/*
> + * Function to poll the MDIO
> + */
> +static int mdio_poll(void)
> +{
> +	int i;
> +
> +	for (i = 0; i < MAX_MDIO_POLL; i++) {
> +		if(!(gpi_readl(base + MDIO_COMMAND) & 0x8000))
> +			break;
> +		udelay(10);
> +	}
> +
> +	return i >= MAX_MDIO_POLL;
> +}
> +
> +/*
> + * Initialize and configure the MDIO
> + */
> +int rm9k_ge_mdio_setup(void __iomem * addr, unsigned int clka)
> +{
> +	if (base)
> +		return -1;
> +
> +	base = addr;
> +
> +	/* Reset the SCMB and program into MDIO mode*/
> +	gpi_writel(0x9000, base + SCMB_CONTROL);
> +	wmb();
> +	gpi_writel(0x1000, base + SCMB_CONTROL);
> +	wmb();
> +
> +	/* CLK A */
> +	gpi_writel(clka & 0x3ff, base + SCMB_CLKA);
> +
> +	pr_debug("%s(%p, %u)\n"
> +		 "\tSCMB_CONTROL (%p) = %#010x\n",
> +		 __func__, addr, clka, base + SCMB_CLKA,
> +		 gpi_readl(base + SCMB_CLKA));
> +
> +	return 0;
> +}
> +
> +void rm9k_ge_mdio_cleanup(void)
> +{
> +	base = 0;
> +}
> +
> +/*
> + * Read the MDIO register. This is what the individual parametes mean:
> + *
> + * dev_addr : PHY ID
> + * reg_addr : register offset
> + *
> + * See the spec for the Titan MAC. We operate in the Direct Mode.
> + */
> +
> +#define MAX_RETRIES	2
> +
> +int rm9k_ge_mdio_read(int dev_addr, int reg_addr, u16 *pdata)
> +{
> +	u32 val;
> +	int retries;
> +
> +	for (retries = 0; retries < MAX_RETRIES; retries++) {
> +		gpi_writel(((dev_addr << 8) & 0x1f00)
> +			     | ( reg_addr & 0x001f) | 0x4000,
> +			     base + MDIO_DEVICE_PORT_ADDRESS);
> +			     wmb();
> +		gpi_writel(READ_OPCODE | 0x1, base + MDIO_COMMAND);
> +		if (mdio_poll())
> +			return -1;
> +		*pdata = (unsigned int)
> +			 (gpi_readl(base + MDIO_DATA) & 0x0000ffff);
> +		val = gpi_readl(base + MDIO_INTERRUPTS);
> +		if (~val & 0x2)
> +			return 0;
> +		udelay(30);
> +	}
> +
> +	return -1;
> +}
> +
> +/*
> + * Write to the MDIO register
> + *
> + * dev_addr : PHY ID
> + * reg_addr : register that needs to be written to
> + *
> + */
> +int rm9k_ge_mdio_write(int dev_addr, int reg_addr, u16 data)
> +{
> +	if (mdio_poll())
> +		return -1;
> +
> +	gpi_writel(((dev_addr << 8) & 0x1f00)
> +		     | (reg_addr & 0x001f) | 0x4000,
> +		     base + MDIO_DEVICE_PORT_ADDRESS);
> +	gpi_writel(data, base + MDIO_DATA);
> +	wmb();
> +	gpi_writel(WRITE_OPCODE | 0x1, base + MDIO_COMMAND);
> +	if (mdio_poll())
> +		return -1;
> +
> +	return	(gpi_readl(base + MDIO_INTERRUPTS) & 0x2) ? -1 : 0;
> +}
> Index: linux-excite/drivers/net/rm9k_mdio.h
> ===================================================================
> --- /dev/null	1970-01-01 00:00:00.000000000 +0000
> +++ linux-excite/drivers/net/rm9k_mdio.h	2006-06-23 13:28:55.000000000 +0100
> @@ -0,0 +1,36 @@
> +/*
> + * MDIO used to interact with the PHY when using GMII/MII
> + */
> +#ifndef _RM9K_MDIO_H
> +#define _RM9K_MDIO_H
> +
> +#include <asm/types.h>
> +
> +
> +/* GMII specific registers */
> +#define RM9K_GE_MDIO_PHY_BMCR		0x00
> +#define RM9K_GE_MDIO_PHY_BMSR		0x01
> +#define RM9K_GE_MDIO_PHY_ID_HI		0x02
> +#define RM9K_GE_MDIO_PHY_ID_LO		0x03
> +#define	RM9K_PHY_AUTONEG_ADV		0x04
> +#define	RM9K_PHY_LP_ABILITY		0x05
> +#define	RM9K_GE_MDIO_MII_CTRL		0x09
> +#define	RM9K_GE_MDIO_MII_EXTENDED	0x0f
> +#define	RM9K_GE_MDIO_PHY_CTRL		0x10
> +#define	RM9K_GE_MDIO_PHY_STATUS		0x11
> +#define	RM9K_GE_MDIO_PHY_INTSTAT	0x14
> +#define	RM9K_GE_MDIO_PHY_INTMASK	0x15
> +#define	RM9K_GE_MDIO_PHY_INTCLEAR	0x17
> +#define	RM9K_GE_MDIO_PHY_LED		0x18
> +#define	RM9K_GE_MDIO_PHY_LED_OVER	0x19
> +
> +/*
> + * Function Prototypes
> + */
> +int rm9k_ge_mdio_setup(void *, unsigned int);
> +void rm9k_ge_mdio_cleanup(void);
> +unsigned long rm9k_ge_mdio_ident_phy(int);
> +int rm9k_ge_mdio_read(int, int, u16 *);
> +int rm9k_ge_mdio_write(int, int, u16);
> +
> +#endif /* _RM9K_MDIO_H */
> 


^ permalink raw reply	[flat|nested] only message in thread

only message in thread, other threads:[~2006-07-05 18:00 UTC | newest]

Thread overview: (only message) (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
     [not found] <20060605235313.GA13994@linux-mips.org>
     [not found] ` <449B57DC.1040205@garzik.org>
     [not found]   ` <20060623142127.GA23299@linux-mips.org>
2006-07-05 18:00     ` [PATCH] RM9000 network driver Jeff Garzik

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).