115d014d13149aedd76cbff1b5c3bbfe839391457Lennert Buytenhek/*
215d014d13149aedd76cbff1b5c3bbfe839391457Lennert Buytenhek * IXP2000 MSF network device driver
315d014d13149aedd76cbff1b5c3bbfe839391457Lennert Buytenhek * Copyright (C) 2004, 2005 Lennert Buytenhek <buytenh@wantstofly.org>
415d014d13149aedd76cbff1b5c3bbfe839391457Lennert Buytenhek * Dedicated to Marija Kulikova.
515d014d13149aedd76cbff1b5c3bbfe839391457Lennert Buytenhek *
615d014d13149aedd76cbff1b5c3bbfe839391457Lennert Buytenhek * This program is free software; you can redistribute it and/or modify
715d014d13149aedd76cbff1b5c3bbfe839391457Lennert Buytenhek * it under the terms of the GNU General Public License as published by
815d014d13149aedd76cbff1b5c3bbfe839391457Lennert Buytenhek * the Free Software Foundation; either version 2 of the License, or
915d014d13149aedd76cbff1b5c3bbfe839391457Lennert Buytenhek * (at your option) any later version.
1015d014d13149aedd76cbff1b5c3bbfe839391457Lennert Buytenhek */
1115d014d13149aedd76cbff1b5c3bbfe839391457Lennert Buytenhek
1215d014d13149aedd76cbff1b5c3bbfe839391457Lennert Buytenhek#include <linux/module.h>
1315d014d13149aedd76cbff1b5c3bbfe839391457Lennert Buytenhek#include <linux/kernel.h>
1415d014d13149aedd76cbff1b5c3bbfe839391457Lennert Buytenhek#include <linux/netdevice.h>
1515d014d13149aedd76cbff1b5c3bbfe839391457Lennert Buytenhek#include <linux/etherdevice.h>
1615d014d13149aedd76cbff1b5c3bbfe839391457Lennert Buytenhek#include <linux/init.h>
17a6b7a407865aab9f849dd99a71072b7cd1175116Alexey Dobriyan#include <linux/interrupt.h>
1815d014d13149aedd76cbff1b5c3bbfe839391457Lennert Buytenhek#include <linux/moduleparam.h>
195a0e3ad6af8660be21ca98a971cd00f331318c05Tejun Heo#include <linux/gfp.h>
2018ec5c731271939acb414614e964c15c8ef52156Lennert Buytenhek#include <asm/hardware/uengine.h>
2115d014d13149aedd76cbff1b5c3bbfe839391457Lennert Buytenhek#include <asm/io.h>
2215d014d13149aedd76cbff1b5c3bbfe839391457Lennert Buytenhek#include "ixp2400_rx.ucode"
2315d014d13149aedd76cbff1b5c3bbfe839391457Lennert Buytenhek#include "ixp2400_tx.ucode"
2415d014d13149aedd76cbff1b5c3bbfe839391457Lennert Buytenhek#include "ixpdev_priv.h"
2515d014d13149aedd76cbff1b5c3bbfe839391457Lennert Buytenhek#include "ixpdev.h"
26b9696ea32533b3353a5390e7c34a4719512fb2a1Vincent Sanders#include "pm3386.h"
2715d014d13149aedd76cbff1b5c3bbfe839391457Lennert Buytenhek
28127477840b9fd205958203648b9fa89860d69a43Lennert Buytenhek#define DRV_MODULE_VERSION	"0.2"
29127477840b9fd205958203648b9fa89860d69a43Lennert Buytenhek
3015d014d13149aedd76cbff1b5c3bbfe839391457Lennert Buytenhekstatic int nds_count;
3115d014d13149aedd76cbff1b5c3bbfe839391457Lennert Buytenhekstatic struct net_device **nds;
3215d014d13149aedd76cbff1b5c3bbfe839391457Lennert Buytenhekstatic int nds_open;
3315d014d13149aedd76cbff1b5c3bbfe839391457Lennert Buytenhekstatic void (*set_port_admin_status)(int port, int up);
3415d014d13149aedd76cbff1b5c3bbfe839391457Lennert Buytenhek
3515d014d13149aedd76cbff1b5c3bbfe839391457Lennert Buytenhekstatic struct ixpdev_rx_desc * const rx_desc =
3615d014d13149aedd76cbff1b5c3bbfe839391457Lennert Buytenhek	(struct ixpdev_rx_desc *)(IXP2000_SRAM0_VIRT_BASE + RX_BUF_DESC_BASE);
3715d014d13149aedd76cbff1b5c3bbfe839391457Lennert Buytenhekstatic struct ixpdev_tx_desc * const tx_desc =
3815d014d13149aedd76cbff1b5c3bbfe839391457Lennert Buytenhek	(struct ixpdev_tx_desc *)(IXP2000_SRAM0_VIRT_BASE + TX_BUF_DESC_BASE);
3915d014d13149aedd76cbff1b5c3bbfe839391457Lennert Buytenhekstatic int tx_pointer;
4015d014d13149aedd76cbff1b5c3bbfe839391457Lennert Buytenhek
4115d014d13149aedd76cbff1b5c3bbfe839391457Lennert Buytenhek
4215d014d13149aedd76cbff1b5c3bbfe839391457Lennert Buytenhekstatic int ixpdev_xmit(struct sk_buff *skb, struct net_device *dev)
4315d014d13149aedd76cbff1b5c3bbfe839391457Lennert Buytenhek{
4415d014d13149aedd76cbff1b5c3bbfe839391457Lennert Buytenhek	struct ixpdev_priv *ip = netdev_priv(dev);
4515d014d13149aedd76cbff1b5c3bbfe839391457Lennert Buytenhek	struct ixpdev_tx_desc *desc;
4615d014d13149aedd76cbff1b5c3bbfe839391457Lennert Buytenhek	int entry;
474871953c0ef2cafeb37bbe186d9d13dcb24fc2c5Dongdong Deng	unsigned long flags;
4815d014d13149aedd76cbff1b5c3bbfe839391457Lennert Buytenhek
4915d014d13149aedd76cbff1b5c3bbfe839391457Lennert Buytenhek	if (unlikely(skb->len > PAGE_SIZE)) {
5015d014d13149aedd76cbff1b5c3bbfe839391457Lennert Buytenhek		/* @@@ Count drops.  */
5115d014d13149aedd76cbff1b5c3bbfe839391457Lennert Buytenhek		dev_kfree_skb(skb);
526ed106549d17474ca17a16057f4c0ed4eba5a7caPatrick McHardy		return NETDEV_TX_OK;
5315d014d13149aedd76cbff1b5c3bbfe839391457Lennert Buytenhek	}
5415d014d13149aedd76cbff1b5c3bbfe839391457Lennert Buytenhek
5515d014d13149aedd76cbff1b5c3bbfe839391457Lennert Buytenhek	entry = tx_pointer;
5615d014d13149aedd76cbff1b5c3bbfe839391457Lennert Buytenhek	tx_pointer = (tx_pointer + 1) % TX_BUF_COUNT;
5715d014d13149aedd76cbff1b5c3bbfe839391457Lennert Buytenhek
5815d014d13149aedd76cbff1b5c3bbfe839391457Lennert Buytenhek	desc = tx_desc + entry;
5915d014d13149aedd76cbff1b5c3bbfe839391457Lennert Buytenhek	desc->pkt_length = skb->len;
6015d014d13149aedd76cbff1b5c3bbfe839391457Lennert Buytenhek	desc->channel = ip->channel;
6115d014d13149aedd76cbff1b5c3bbfe839391457Lennert Buytenhek
6215d014d13149aedd76cbff1b5c3bbfe839391457Lennert Buytenhek	skb_copy_and_csum_dev(skb, phys_to_virt(desc->buf_addr));
6315d014d13149aedd76cbff1b5c3bbfe839391457Lennert Buytenhek	dev_kfree_skb(skb);
6415d014d13149aedd76cbff1b5c3bbfe839391457Lennert Buytenhek
6515d014d13149aedd76cbff1b5c3bbfe839391457Lennert Buytenhek	ixp2000_reg_write(RING_TX_PENDING,
6615d014d13149aedd76cbff1b5c3bbfe839391457Lennert Buytenhek		TX_BUF_DESC_BASE + (entry * sizeof(struct ixpdev_tx_desc)));
6715d014d13149aedd76cbff1b5c3bbfe839391457Lennert Buytenhek
684871953c0ef2cafeb37bbe186d9d13dcb24fc2c5Dongdong Deng	local_irq_save(flags);
6915d014d13149aedd76cbff1b5c3bbfe839391457Lennert Buytenhek	ip->tx_queue_entries++;
7015d014d13149aedd76cbff1b5c3bbfe839391457Lennert Buytenhek	if (ip->tx_queue_entries == TX_BUF_COUNT_PER_CHAN)
7115d014d13149aedd76cbff1b5c3bbfe839391457Lennert Buytenhek		netif_stop_queue(dev);
724871953c0ef2cafeb37bbe186d9d13dcb24fc2c5Dongdong Deng	local_irq_restore(flags);
7315d014d13149aedd76cbff1b5c3bbfe839391457Lennert Buytenhek
746ed106549d17474ca17a16057f4c0ed4eba5a7caPatrick McHardy	return NETDEV_TX_OK;
7515d014d13149aedd76cbff1b5c3bbfe839391457Lennert Buytenhek}
7615d014d13149aedd76cbff1b5c3bbfe839391457Lennert Buytenhek
7715d014d13149aedd76cbff1b5c3bbfe839391457Lennert Buytenhek
78bea3348eef27e6044b6161fd04c3152215f96411Stephen Hemmingerstatic int ixpdev_rx(struct net_device *dev, int processed, int budget)
7915d014d13149aedd76cbff1b5c3bbfe839391457Lennert Buytenhek{
80bea3348eef27e6044b6161fd04c3152215f96411Stephen Hemminger	while (processed < budget) {
8115d014d13149aedd76cbff1b5c3bbfe839391457Lennert Buytenhek		struct ixpdev_rx_desc *desc;
8215d014d13149aedd76cbff1b5c3bbfe839391457Lennert Buytenhek		struct sk_buff *skb;
8315d014d13149aedd76cbff1b5c3bbfe839391457Lennert Buytenhek		void *buf;
8415d014d13149aedd76cbff1b5c3bbfe839391457Lennert Buytenhek		u32 _desc;
8515d014d13149aedd76cbff1b5c3bbfe839391457Lennert Buytenhek
8615d014d13149aedd76cbff1b5c3bbfe839391457Lennert Buytenhek		_desc = ixp2000_reg_read(RING_RX_DONE);
8715d014d13149aedd76cbff1b5c3bbfe839391457Lennert Buytenhek		if (_desc == 0)
8815d014d13149aedd76cbff1b5c3bbfe839391457Lennert Buytenhek			return 0;
8915d014d13149aedd76cbff1b5c3bbfe839391457Lennert Buytenhek
9015d014d13149aedd76cbff1b5c3bbfe839391457Lennert Buytenhek		desc = rx_desc +
9115d014d13149aedd76cbff1b5c3bbfe839391457Lennert Buytenhek			((_desc - RX_BUF_DESC_BASE) / sizeof(struct ixpdev_rx_desc));
9215d014d13149aedd76cbff1b5c3bbfe839391457Lennert Buytenhek		buf = phys_to_virt(desc->buf_addr);
9315d014d13149aedd76cbff1b5c3bbfe839391457Lennert Buytenhek
9415d014d13149aedd76cbff1b5c3bbfe839391457Lennert Buytenhek		if (desc->pkt_length < 4 || desc->pkt_length > PAGE_SIZE) {
9515d014d13149aedd76cbff1b5c3bbfe839391457Lennert Buytenhek			printk(KERN_ERR "ixp2000: rx err, length %d\n",
9615d014d13149aedd76cbff1b5c3bbfe839391457Lennert Buytenhek					desc->pkt_length);
9715d014d13149aedd76cbff1b5c3bbfe839391457Lennert Buytenhek			goto err;
9815d014d13149aedd76cbff1b5c3bbfe839391457Lennert Buytenhek		}
9915d014d13149aedd76cbff1b5c3bbfe839391457Lennert Buytenhek
10015d014d13149aedd76cbff1b5c3bbfe839391457Lennert Buytenhek		if (desc->channel < 0 || desc->channel >= nds_count) {
10115d014d13149aedd76cbff1b5c3bbfe839391457Lennert Buytenhek			printk(KERN_ERR "ixp2000: rx err, channel %d\n",
10215d014d13149aedd76cbff1b5c3bbfe839391457Lennert Buytenhek					desc->channel);
10315d014d13149aedd76cbff1b5c3bbfe839391457Lennert Buytenhek			goto err;
10415d014d13149aedd76cbff1b5c3bbfe839391457Lennert Buytenhek		}
10515d014d13149aedd76cbff1b5c3bbfe839391457Lennert Buytenhek
10615d014d13149aedd76cbff1b5c3bbfe839391457Lennert Buytenhek		/* @@@ Make FCS stripping configurable.  */
10715d014d13149aedd76cbff1b5c3bbfe839391457Lennert Buytenhek		desc->pkt_length -= 4;
10815d014d13149aedd76cbff1b5c3bbfe839391457Lennert Buytenhek
10915d014d13149aedd76cbff1b5c3bbfe839391457Lennert Buytenhek		if (unlikely(!netif_running(nds[desc->channel])))
11015d014d13149aedd76cbff1b5c3bbfe839391457Lennert Buytenhek			goto err;
11115d014d13149aedd76cbff1b5c3bbfe839391457Lennert Buytenhek
11289d71a66c40d629e3b1285def543ab1425558cd5Eric Dumazet		skb = netdev_alloc_skb_ip_align(dev, desc->pkt_length);
11315d014d13149aedd76cbff1b5c3bbfe839391457Lennert Buytenhek		if (likely(skb != NULL)) {
1148c7b7faaa630fef7f68d8728cee1cce398cc9697David S. Miller			skb_copy_to_linear_data(skb, buf, desc->pkt_length);
11515d014d13149aedd76cbff1b5c3bbfe839391457Lennert Buytenhek			skb_put(skb, desc->pkt_length);
1164c13eb6657fe9ef7b4dc8f1a405c902e9e5234e0Arnaldo Carvalho de Melo			skb->protocol = eth_type_trans(skb, nds[desc->channel]);
11715d014d13149aedd76cbff1b5c3bbfe839391457Lennert Buytenhek
11815d014d13149aedd76cbff1b5c3bbfe839391457Lennert Buytenhek			netif_receive_skb(skb);
11915d014d13149aedd76cbff1b5c3bbfe839391457Lennert Buytenhek		}
12015d014d13149aedd76cbff1b5c3bbfe839391457Lennert Buytenhek
12115d014d13149aedd76cbff1b5c3bbfe839391457Lennert Buytenhekerr:
12215d014d13149aedd76cbff1b5c3bbfe839391457Lennert Buytenhek		ixp2000_reg_write(RING_RX_PENDING, _desc);
123bea3348eef27e6044b6161fd04c3152215f96411Stephen Hemminger		processed++;
12415d014d13149aedd76cbff1b5c3bbfe839391457Lennert Buytenhek	}
12515d014d13149aedd76cbff1b5c3bbfe839391457Lennert Buytenhek
126bea3348eef27e6044b6161fd04c3152215f96411Stephen Hemminger	return processed;
12715d014d13149aedd76cbff1b5c3bbfe839391457Lennert Buytenhek}
12815d014d13149aedd76cbff1b5c3bbfe839391457Lennert Buytenhek
12915d014d13149aedd76cbff1b5c3bbfe839391457Lennert Buytenhek/* dev always points to nds[0].  */
130bea3348eef27e6044b6161fd04c3152215f96411Stephen Hemmingerstatic int ixpdev_poll(struct napi_struct *napi, int budget)
13115d014d13149aedd76cbff1b5c3bbfe839391457Lennert Buytenhek{
132bea3348eef27e6044b6161fd04c3152215f96411Stephen Hemminger	struct ixpdev_priv *ip = container_of(napi, struct ixpdev_priv, napi);
133bea3348eef27e6044b6161fd04c3152215f96411Stephen Hemminger	struct net_device *dev = ip->dev;
134bea3348eef27e6044b6161fd04c3152215f96411Stephen Hemminger	int rx;
135bea3348eef27e6044b6161fd04c3152215f96411Stephen Hemminger
136bea3348eef27e6044b6161fd04c3152215f96411Stephen Hemminger	rx = 0;
13715d014d13149aedd76cbff1b5c3bbfe839391457Lennert Buytenhek	do {
13815d014d13149aedd76cbff1b5c3bbfe839391457Lennert Buytenhek		ixp2000_reg_write(IXP2000_IRQ_THD_RAW_STATUS_A_0, 0x00ff);
13915d014d13149aedd76cbff1b5c3bbfe839391457Lennert Buytenhek
140bea3348eef27e6044b6161fd04c3152215f96411Stephen Hemminger		rx = ixpdev_rx(dev, rx, budget);
141bea3348eef27e6044b6161fd04c3152215f96411Stephen Hemminger		if (rx >= budget)
142bea3348eef27e6044b6161fd04c3152215f96411Stephen Hemminger			break;
14315d014d13149aedd76cbff1b5c3bbfe839391457Lennert Buytenhek	} while (ixp2000_reg_read(IXP2000_IRQ_THD_RAW_STATUS_A_0) & 0x00ff);
14415d014d13149aedd76cbff1b5c3bbfe839391457Lennert Buytenhek
145288379f050284087578b77e04f040b57db3db3f8Ben Hutchings	napi_complete(napi);
14615d014d13149aedd76cbff1b5c3bbfe839391457Lennert Buytenhek	ixp2000_reg_write(IXP2000_IRQ_THD_ENABLE_SET_A_0, 0x00ff);
14715d014d13149aedd76cbff1b5c3bbfe839391457Lennert Buytenhek
148bea3348eef27e6044b6161fd04c3152215f96411Stephen Hemminger	return rx;
14915d014d13149aedd76cbff1b5c3bbfe839391457Lennert Buytenhek}
15015d014d13149aedd76cbff1b5c3bbfe839391457Lennert Buytenhek
15115d014d13149aedd76cbff1b5c3bbfe839391457Lennert Buytenhekstatic void ixpdev_tx_complete(void)
15215d014d13149aedd76cbff1b5c3bbfe839391457Lennert Buytenhek{
15315d014d13149aedd76cbff1b5c3bbfe839391457Lennert Buytenhek	int channel;
15415d014d13149aedd76cbff1b5c3bbfe839391457Lennert Buytenhek	u32 wake;
15515d014d13149aedd76cbff1b5c3bbfe839391457Lennert Buytenhek
15615d014d13149aedd76cbff1b5c3bbfe839391457Lennert Buytenhek	wake = 0;
15715d014d13149aedd76cbff1b5c3bbfe839391457Lennert Buytenhek	while (1) {
15815d014d13149aedd76cbff1b5c3bbfe839391457Lennert Buytenhek		struct ixpdev_priv *ip;
15915d014d13149aedd76cbff1b5c3bbfe839391457Lennert Buytenhek		u32 desc;
16015d014d13149aedd76cbff1b5c3bbfe839391457Lennert Buytenhek		int entry;
16115d014d13149aedd76cbff1b5c3bbfe839391457Lennert Buytenhek
16215d014d13149aedd76cbff1b5c3bbfe839391457Lennert Buytenhek		desc = ixp2000_reg_read(RING_TX_DONE);
16315d014d13149aedd76cbff1b5c3bbfe839391457Lennert Buytenhek		if (desc == 0)
16415d014d13149aedd76cbff1b5c3bbfe839391457Lennert Buytenhek			break;
16515d014d13149aedd76cbff1b5c3bbfe839391457Lennert Buytenhek
16615d014d13149aedd76cbff1b5c3bbfe839391457Lennert Buytenhek		/* @@@ Check whether entries come back in order.  */
16715d014d13149aedd76cbff1b5c3bbfe839391457Lennert Buytenhek		entry = (desc - TX_BUF_DESC_BASE) / sizeof(struct ixpdev_tx_desc);
16815d014d13149aedd76cbff1b5c3bbfe839391457Lennert Buytenhek		channel = tx_desc[entry].channel;
16915d014d13149aedd76cbff1b5c3bbfe839391457Lennert Buytenhek
17015d014d13149aedd76cbff1b5c3bbfe839391457Lennert Buytenhek		if (channel < 0 || channel >= nds_count) {
17115d014d13149aedd76cbff1b5c3bbfe839391457Lennert Buytenhek			printk(KERN_ERR "ixp2000: txcomp channel index "
17215d014d13149aedd76cbff1b5c3bbfe839391457Lennert Buytenhek					"out of bounds (%d, %.8i, %d)\n",
17315d014d13149aedd76cbff1b5c3bbfe839391457Lennert Buytenhek					channel, (unsigned int)desc, entry);
17415d014d13149aedd76cbff1b5c3bbfe839391457Lennert Buytenhek			continue;
17515d014d13149aedd76cbff1b5c3bbfe839391457Lennert Buytenhek		}
17615d014d13149aedd76cbff1b5c3bbfe839391457Lennert Buytenhek
17715d014d13149aedd76cbff1b5c3bbfe839391457Lennert Buytenhek		ip = netdev_priv(nds[channel]);
17815d014d13149aedd76cbff1b5c3bbfe839391457Lennert Buytenhek		if (ip->tx_queue_entries == TX_BUF_COUNT_PER_CHAN)
17915d014d13149aedd76cbff1b5c3bbfe839391457Lennert Buytenhek			wake |= 1 << channel;
18015d014d13149aedd76cbff1b5c3bbfe839391457Lennert Buytenhek		ip->tx_queue_entries--;
18115d014d13149aedd76cbff1b5c3bbfe839391457Lennert Buytenhek	}
18215d014d13149aedd76cbff1b5c3bbfe839391457Lennert Buytenhek
18315d014d13149aedd76cbff1b5c3bbfe839391457Lennert Buytenhek	for (channel = 0; wake != 0; channel++) {
18415d014d13149aedd76cbff1b5c3bbfe839391457Lennert Buytenhek		if (wake & (1 << channel)) {
18515d014d13149aedd76cbff1b5c3bbfe839391457Lennert Buytenhek			netif_wake_queue(nds[channel]);
18615d014d13149aedd76cbff1b5c3bbfe839391457Lennert Buytenhek			wake &= ~(1 << channel);
18715d014d13149aedd76cbff1b5c3bbfe839391457Lennert Buytenhek		}
18815d014d13149aedd76cbff1b5c3bbfe839391457Lennert Buytenhek	}
18915d014d13149aedd76cbff1b5c3bbfe839391457Lennert Buytenhek}
19015d014d13149aedd76cbff1b5c3bbfe839391457Lennert Buytenhek
1917d12e780e003f93433d49ce78cfedf4b4c52adc5David Howellsstatic irqreturn_t ixpdev_interrupt(int irq, void *dev_id)
19215d014d13149aedd76cbff1b5c3bbfe839391457Lennert Buytenhek{
19315d014d13149aedd76cbff1b5c3bbfe839391457Lennert Buytenhek	u32 status;
19415d014d13149aedd76cbff1b5c3bbfe839391457Lennert Buytenhek
19515d014d13149aedd76cbff1b5c3bbfe839391457Lennert Buytenhek	status = ixp2000_reg_read(IXP2000_IRQ_THD_STATUS_A_0);
19615d014d13149aedd76cbff1b5c3bbfe839391457Lennert Buytenhek	if (status == 0)
19715d014d13149aedd76cbff1b5c3bbfe839391457Lennert Buytenhek		return IRQ_NONE;
19815d014d13149aedd76cbff1b5c3bbfe839391457Lennert Buytenhek
19915d014d13149aedd76cbff1b5c3bbfe839391457Lennert Buytenhek	/*
20015d014d13149aedd76cbff1b5c3bbfe839391457Lennert Buytenhek	 * Any of the eight receive units signaled RX?
20115d014d13149aedd76cbff1b5c3bbfe839391457Lennert Buytenhek	 */
20215d014d13149aedd76cbff1b5c3bbfe839391457Lennert Buytenhek	if (status & 0x00ff) {
203bea3348eef27e6044b6161fd04c3152215f96411Stephen Hemminger		struct net_device *dev = nds[0];
204bea3348eef27e6044b6161fd04c3152215f96411Stephen Hemminger		struct ixpdev_priv *ip = netdev_priv(dev);
205bea3348eef27e6044b6161fd04c3152215f96411Stephen Hemminger
20615d014d13149aedd76cbff1b5c3bbfe839391457Lennert Buytenhek		ixp2000_reg_wrb(IXP2000_IRQ_THD_ENABLE_CLEAR_A_0, 0x00ff);
207bea3348eef27e6044b6161fd04c3152215f96411Stephen Hemminger		if (likely(napi_schedule_prep(&ip->napi))) {
208288379f050284087578b77e04f040b57db3db3f8Ben Hutchings			__napi_schedule(&ip->napi);
20915d014d13149aedd76cbff1b5c3bbfe839391457Lennert Buytenhek		} else {
21015d014d13149aedd76cbff1b5c3bbfe839391457Lennert Buytenhek			printk(KERN_CRIT "ixp2000: irq while polling!!\n");
21115d014d13149aedd76cbff1b5c3bbfe839391457Lennert Buytenhek		}
21215d014d13149aedd76cbff1b5c3bbfe839391457Lennert Buytenhek	}
21315d014d13149aedd76cbff1b5c3bbfe839391457Lennert Buytenhek
21415d014d13149aedd76cbff1b5c3bbfe839391457Lennert Buytenhek	/*
21515d014d13149aedd76cbff1b5c3bbfe839391457Lennert Buytenhek	 * Any of the eight transmit units signaled TXdone?
21615d014d13149aedd76cbff1b5c3bbfe839391457Lennert Buytenhek	 */
21715d014d13149aedd76cbff1b5c3bbfe839391457Lennert Buytenhek	if (status & 0xff00) {
21815d014d13149aedd76cbff1b5c3bbfe839391457Lennert Buytenhek		ixp2000_reg_wrb(IXP2000_IRQ_THD_RAW_STATUS_A_0, 0xff00);
21915d014d13149aedd76cbff1b5c3bbfe839391457Lennert Buytenhek		ixpdev_tx_complete();
22015d014d13149aedd76cbff1b5c3bbfe839391457Lennert Buytenhek	}
22115d014d13149aedd76cbff1b5c3bbfe839391457Lennert Buytenhek
22215d014d13149aedd76cbff1b5c3bbfe839391457Lennert Buytenhek	return IRQ_HANDLED;
22315d014d13149aedd76cbff1b5c3bbfe839391457Lennert Buytenhek}
22415d014d13149aedd76cbff1b5c3bbfe839391457Lennert Buytenhek
2258ce51d69b755c39e714826899631629209896b70Lennert Buytenhek#ifdef CONFIG_NET_POLL_CONTROLLER
2268ce51d69b755c39e714826899631629209896b70Lennert Buytenhekstatic void ixpdev_poll_controller(struct net_device *dev)
2278ce51d69b755c39e714826899631629209896b70Lennert Buytenhek{
2288ce51d69b755c39e714826899631629209896b70Lennert Buytenhek	disable_irq(IRQ_IXP2000_THDA0);
2290da2f0f164f098bb4447c714b552ac1681b2d6e8Yoann Padioleau	ixpdev_interrupt(IRQ_IXP2000_THDA0, dev);
2308ce51d69b755c39e714826899631629209896b70Lennert Buytenhek	enable_irq(IRQ_IXP2000_THDA0);
2318ce51d69b755c39e714826899631629209896b70Lennert Buytenhek}
2328ce51d69b755c39e714826899631629209896b70Lennert Buytenhek#endif
2338ce51d69b755c39e714826899631629209896b70Lennert Buytenhek
23415d014d13149aedd76cbff1b5c3bbfe839391457Lennert Buytenhekstatic int ixpdev_open(struct net_device *dev)
23515d014d13149aedd76cbff1b5c3bbfe839391457Lennert Buytenhek{
23615d014d13149aedd76cbff1b5c3bbfe839391457Lennert Buytenhek	struct ixpdev_priv *ip = netdev_priv(dev);
23715d014d13149aedd76cbff1b5c3bbfe839391457Lennert Buytenhek	int err;
23815d014d13149aedd76cbff1b5c3bbfe839391457Lennert Buytenhek
239bea3348eef27e6044b6161fd04c3152215f96411Stephen Hemminger	napi_enable(&ip->napi);
24015d014d13149aedd76cbff1b5c3bbfe839391457Lennert Buytenhek	if (!nds_open++) {
24115d014d13149aedd76cbff1b5c3bbfe839391457Lennert Buytenhek		err = request_irq(IRQ_IXP2000_THDA0, ixpdev_interrupt,
2421fb9df5d3069064c037c81c0ab8bf783ffa5e373Thomas Gleixner					IRQF_SHARED, "ixp2000_eth", nds);
24315d014d13149aedd76cbff1b5c3bbfe839391457Lennert Buytenhek		if (err) {
24415d014d13149aedd76cbff1b5c3bbfe839391457Lennert Buytenhek			nds_open--;
245bea3348eef27e6044b6161fd04c3152215f96411Stephen Hemminger			napi_disable(&ip->napi);
24615d014d13149aedd76cbff1b5c3bbfe839391457Lennert Buytenhek			return err;
24715d014d13149aedd76cbff1b5c3bbfe839391457Lennert Buytenhek		}
24815d014d13149aedd76cbff1b5c3bbfe839391457Lennert Buytenhek
24915d014d13149aedd76cbff1b5c3bbfe839391457Lennert Buytenhek		ixp2000_reg_write(IXP2000_IRQ_THD_ENABLE_SET_A_0, 0xffff);
25015d014d13149aedd76cbff1b5c3bbfe839391457Lennert Buytenhek	}
25115d014d13149aedd76cbff1b5c3bbfe839391457Lennert Buytenhek
25215d014d13149aedd76cbff1b5c3bbfe839391457Lennert Buytenhek	set_port_admin_status(ip->channel, 1);
25315d014d13149aedd76cbff1b5c3bbfe839391457Lennert Buytenhek	netif_start_queue(dev);
25415d014d13149aedd76cbff1b5c3bbfe839391457Lennert Buytenhek
25515d014d13149aedd76cbff1b5c3bbfe839391457Lennert Buytenhek	return 0;
25615d014d13149aedd76cbff1b5c3bbfe839391457Lennert Buytenhek}
25715d014d13149aedd76cbff1b5c3bbfe839391457Lennert Buytenhek
25815d014d13149aedd76cbff1b5c3bbfe839391457Lennert Buytenhekstatic int ixpdev_close(struct net_device *dev)
25915d014d13149aedd76cbff1b5c3bbfe839391457Lennert Buytenhek{
26015d014d13149aedd76cbff1b5c3bbfe839391457Lennert Buytenhek	struct ixpdev_priv *ip = netdev_priv(dev);
26115d014d13149aedd76cbff1b5c3bbfe839391457Lennert Buytenhek
26215d014d13149aedd76cbff1b5c3bbfe839391457Lennert Buytenhek	netif_stop_queue(dev);
263bea3348eef27e6044b6161fd04c3152215f96411Stephen Hemminger	napi_disable(&ip->napi);
26415d014d13149aedd76cbff1b5c3bbfe839391457Lennert Buytenhek	set_port_admin_status(ip->channel, 0);
26515d014d13149aedd76cbff1b5c3bbfe839391457Lennert Buytenhek
26615d014d13149aedd76cbff1b5c3bbfe839391457Lennert Buytenhek	if (!--nds_open) {
26715d014d13149aedd76cbff1b5c3bbfe839391457Lennert Buytenhek		ixp2000_reg_write(IXP2000_IRQ_THD_ENABLE_CLEAR_A_0, 0xffff);
26815d014d13149aedd76cbff1b5c3bbfe839391457Lennert Buytenhek		free_irq(IRQ_IXP2000_THDA0, nds);
26915d014d13149aedd76cbff1b5c3bbfe839391457Lennert Buytenhek	}
27015d014d13149aedd76cbff1b5c3bbfe839391457Lennert Buytenhek
27115d014d13149aedd76cbff1b5c3bbfe839391457Lennert Buytenhek	return 0;
27215d014d13149aedd76cbff1b5c3bbfe839391457Lennert Buytenhek}
27315d014d13149aedd76cbff1b5c3bbfe839391457Lennert Buytenhek
274b9696ea32533b3353a5390e7c34a4719512fb2a1Vincent Sandersstatic struct net_device_stats *ixpdev_get_stats(struct net_device *dev)
275b9696ea32533b3353a5390e7c34a4719512fb2a1Vincent Sanders{
276b9696ea32533b3353a5390e7c34a4719512fb2a1Vincent Sanders	struct ixpdev_priv *ip = netdev_priv(dev);
277b9696ea32533b3353a5390e7c34a4719512fb2a1Vincent Sanders
278b9696ea32533b3353a5390e7c34a4719512fb2a1Vincent Sanders	pm3386_get_stats(ip->channel, &(dev->stats));
279b9696ea32533b3353a5390e7c34a4719512fb2a1Vincent Sanders
280b9696ea32533b3353a5390e7c34a4719512fb2a1Vincent Sanders	return &(dev->stats);
281b9696ea32533b3353a5390e7c34a4719512fb2a1Vincent Sanders}
282b9696ea32533b3353a5390e7c34a4719512fb2a1Vincent Sanders
283e7090e347ad8bce0de35f9fe2b95e22e4ae8fbabAlexander Beregalovstatic const struct net_device_ops ixpdev_netdev_ops = {
284e7090e347ad8bce0de35f9fe2b95e22e4ae8fbabAlexander Beregalov	.ndo_open		= ixpdev_open,
285e7090e347ad8bce0de35f9fe2b95e22e4ae8fbabAlexander Beregalov	.ndo_stop		= ixpdev_close,
286e7090e347ad8bce0de35f9fe2b95e22e4ae8fbabAlexander Beregalov	.ndo_start_xmit		= ixpdev_xmit,
287e7090e347ad8bce0de35f9fe2b95e22e4ae8fbabAlexander Beregalov	.ndo_change_mtu		= eth_change_mtu,
288e7090e347ad8bce0de35f9fe2b95e22e4ae8fbabAlexander Beregalov	.ndo_validate_addr	= eth_validate_addr,
289e7090e347ad8bce0de35f9fe2b95e22e4ae8fbabAlexander Beregalov	.ndo_set_mac_address	= eth_mac_addr,
290b9696ea32533b3353a5390e7c34a4719512fb2a1Vincent Sanders	.ndo_get_stats		= ixpdev_get_stats,
291e7090e347ad8bce0de35f9fe2b95e22e4ae8fbabAlexander Beregalov#ifdef CONFIG_NET_POLL_CONTROLLER
292e7090e347ad8bce0de35f9fe2b95e22e4ae8fbabAlexander Beregalov	.ndo_poll_controller	= ixpdev_poll_controller,
293e7090e347ad8bce0de35f9fe2b95e22e4ae8fbabAlexander Beregalov#endif
294e7090e347ad8bce0de35f9fe2b95e22e4ae8fbabAlexander Beregalov};
295e7090e347ad8bce0de35f9fe2b95e22e4ae8fbabAlexander Beregalov
29615d014d13149aedd76cbff1b5c3bbfe839391457Lennert Buytenhekstruct net_device *ixpdev_alloc(int channel, int sizeof_priv)
29715d014d13149aedd76cbff1b5c3bbfe839391457Lennert Buytenhek{
29815d014d13149aedd76cbff1b5c3bbfe839391457Lennert Buytenhek	struct net_device *dev;
29915d014d13149aedd76cbff1b5c3bbfe839391457Lennert Buytenhek	struct ixpdev_priv *ip;
30015d014d13149aedd76cbff1b5c3bbfe839391457Lennert Buytenhek
30115d014d13149aedd76cbff1b5c3bbfe839391457Lennert Buytenhek	dev = alloc_etherdev(sizeof_priv);
30215d014d13149aedd76cbff1b5c3bbfe839391457Lennert Buytenhek	if (dev == NULL)
30315d014d13149aedd76cbff1b5c3bbfe839391457Lennert Buytenhek		return NULL;
30415d014d13149aedd76cbff1b5c3bbfe839391457Lennert Buytenhek
305e7090e347ad8bce0de35f9fe2b95e22e4ae8fbabAlexander Beregalov	dev->netdev_ops = &ixpdev_netdev_ops;
30615d014d13149aedd76cbff1b5c3bbfe839391457Lennert Buytenhek
30715d014d13149aedd76cbff1b5c3bbfe839391457Lennert Buytenhek	dev->features |= NETIF_F_SG | NETIF_F_HW_CSUM;
30815d014d13149aedd76cbff1b5c3bbfe839391457Lennert Buytenhek
30915d014d13149aedd76cbff1b5c3bbfe839391457Lennert Buytenhek	ip = netdev_priv(dev);
310bea3348eef27e6044b6161fd04c3152215f96411Stephen Hemminger	ip->dev = dev;
311bea3348eef27e6044b6161fd04c3152215f96411Stephen Hemminger	netif_napi_add(dev, &ip->napi, ixpdev_poll, 64);
31215d014d13149aedd76cbff1b5c3bbfe839391457Lennert Buytenhek	ip->channel = channel;
31315d014d13149aedd76cbff1b5c3bbfe839391457Lennert Buytenhek	ip->tx_queue_entries = 0;
31415d014d13149aedd76cbff1b5c3bbfe839391457Lennert Buytenhek
31515d014d13149aedd76cbff1b5c3bbfe839391457Lennert Buytenhek	return dev;
31615d014d13149aedd76cbff1b5c3bbfe839391457Lennert Buytenhek}
31715d014d13149aedd76cbff1b5c3bbfe839391457Lennert Buytenhek
31815d014d13149aedd76cbff1b5c3bbfe839391457Lennert Buytenhekint ixpdev_init(int __nds_count, struct net_device **__nds,
31915d014d13149aedd76cbff1b5c3bbfe839391457Lennert Buytenhek		void (*__set_port_admin_status)(int port, int up))
32015d014d13149aedd76cbff1b5c3bbfe839391457Lennert Buytenhek{
32115d014d13149aedd76cbff1b5c3bbfe839391457Lennert Buytenhek	int i;
32215d014d13149aedd76cbff1b5c3bbfe839391457Lennert Buytenhek	int err;
32315d014d13149aedd76cbff1b5c3bbfe839391457Lennert Buytenhek
3245d4fe2c1ce83c3e967ccc1ba3d580c1a5603a866Lennert Buytenhek	BUILD_BUG_ON(RX_BUF_COUNT > 192 || TX_BUF_COUNT > 192);
32515d014d13149aedd76cbff1b5c3bbfe839391457Lennert Buytenhek
326127477840b9fd205958203648b9fa89860d69a43Lennert Buytenhek	printk(KERN_INFO "IXP2000 MSF ethernet driver %s\n", DRV_MODULE_VERSION);
327127477840b9fd205958203648b9fa89860d69a43Lennert Buytenhek
32815d014d13149aedd76cbff1b5c3bbfe839391457Lennert Buytenhek	nds_count = __nds_count;
32915d014d13149aedd76cbff1b5c3bbfe839391457Lennert Buytenhek	nds = __nds;
33015d014d13149aedd76cbff1b5c3bbfe839391457Lennert Buytenhek	set_port_admin_status = __set_port_admin_status;
33115d014d13149aedd76cbff1b5c3bbfe839391457Lennert Buytenhek
33215d014d13149aedd76cbff1b5c3bbfe839391457Lennert Buytenhek	for (i = 0; i < RX_BUF_COUNT; i++) {
33315d014d13149aedd76cbff1b5c3bbfe839391457Lennert Buytenhek		void *buf;
33415d014d13149aedd76cbff1b5c3bbfe839391457Lennert Buytenhek
33515d014d13149aedd76cbff1b5c3bbfe839391457Lennert Buytenhek		buf = (void *)get_zeroed_page(GFP_KERNEL);
33615d014d13149aedd76cbff1b5c3bbfe839391457Lennert Buytenhek		if (buf == NULL) {
33715d014d13149aedd76cbff1b5c3bbfe839391457Lennert Buytenhek			err = -ENOMEM;
33815d014d13149aedd76cbff1b5c3bbfe839391457Lennert Buytenhek			while (--i >= 0)
33915d014d13149aedd76cbff1b5c3bbfe839391457Lennert Buytenhek				free_page((unsigned long)phys_to_virt(rx_desc[i].buf_addr));
3400c49919a4706cc8c72ff381da7f3ae094e6df03aLennert Buytenhek			goto err_out;
34115d014d13149aedd76cbff1b5c3bbfe839391457Lennert Buytenhek		}
34215d014d13149aedd76cbff1b5c3bbfe839391457Lennert Buytenhek		rx_desc[i].buf_addr = virt_to_phys(buf);
34315d014d13149aedd76cbff1b5c3bbfe839391457Lennert Buytenhek		rx_desc[i].buf_length = PAGE_SIZE;
34415d014d13149aedd76cbff1b5c3bbfe839391457Lennert Buytenhek	}
34515d014d13149aedd76cbff1b5c3bbfe839391457Lennert Buytenhek
34615d014d13149aedd76cbff1b5c3bbfe839391457Lennert Buytenhek	/* @@@ Maybe we shouldn't be preallocating TX buffers.  */
34715d014d13149aedd76cbff1b5c3bbfe839391457Lennert Buytenhek	for (i = 0; i < TX_BUF_COUNT; i++) {
34815d014d13149aedd76cbff1b5c3bbfe839391457Lennert Buytenhek		void *buf;
34915d014d13149aedd76cbff1b5c3bbfe839391457Lennert Buytenhek
35015d014d13149aedd76cbff1b5c3bbfe839391457Lennert Buytenhek		buf = (void *)get_zeroed_page(GFP_KERNEL);
35115d014d13149aedd76cbff1b5c3bbfe839391457Lennert Buytenhek		if (buf == NULL) {
35215d014d13149aedd76cbff1b5c3bbfe839391457Lennert Buytenhek			err = -ENOMEM;
35315d014d13149aedd76cbff1b5c3bbfe839391457Lennert Buytenhek			while (--i >= 0)
35415d014d13149aedd76cbff1b5c3bbfe839391457Lennert Buytenhek				free_page((unsigned long)phys_to_virt(tx_desc[i].buf_addr));
35515d014d13149aedd76cbff1b5c3bbfe839391457Lennert Buytenhek			goto err_free_rx;
35615d014d13149aedd76cbff1b5c3bbfe839391457Lennert Buytenhek		}
35715d014d13149aedd76cbff1b5c3bbfe839391457Lennert Buytenhek		tx_desc[i].buf_addr = virt_to_phys(buf);
35815d014d13149aedd76cbff1b5c3bbfe839391457Lennert Buytenhek	}
35915d014d13149aedd76cbff1b5c3bbfe839391457Lennert Buytenhek
36015d014d13149aedd76cbff1b5c3bbfe839391457Lennert Buytenhek	/* 256 entries, ring status set means 'empty', base address 0x0000.  */
36115d014d13149aedd76cbff1b5c3bbfe839391457Lennert Buytenhek	ixp2000_reg_write(RING_RX_PENDING_BASE, 0x44000000);
36215d014d13149aedd76cbff1b5c3bbfe839391457Lennert Buytenhek	ixp2000_reg_write(RING_RX_PENDING_HEAD, 0x00000000);
36315d014d13149aedd76cbff1b5c3bbfe839391457Lennert Buytenhek	ixp2000_reg_write(RING_RX_PENDING_TAIL, 0x00000000);
36415d014d13149aedd76cbff1b5c3bbfe839391457Lennert Buytenhek
36515d014d13149aedd76cbff1b5c3bbfe839391457Lennert Buytenhek	/* 256 entries, ring status set means 'full', base address 0x0400.  */
36615d014d13149aedd76cbff1b5c3bbfe839391457Lennert Buytenhek	ixp2000_reg_write(RING_RX_DONE_BASE, 0x40000400);
36715d014d13149aedd76cbff1b5c3bbfe839391457Lennert Buytenhek	ixp2000_reg_write(RING_RX_DONE_HEAD, 0x00000000);
36815d014d13149aedd76cbff1b5c3bbfe839391457Lennert Buytenhek	ixp2000_reg_write(RING_RX_DONE_TAIL, 0x00000000);
36915d014d13149aedd76cbff1b5c3bbfe839391457Lennert Buytenhek
37015d014d13149aedd76cbff1b5c3bbfe839391457Lennert Buytenhek	for (i = 0; i < RX_BUF_COUNT; i++) {
37115d014d13149aedd76cbff1b5c3bbfe839391457Lennert Buytenhek		ixp2000_reg_write(RING_RX_PENDING,
37215d014d13149aedd76cbff1b5c3bbfe839391457Lennert Buytenhek			RX_BUF_DESC_BASE + (i * sizeof(struct ixpdev_rx_desc)));
37315d014d13149aedd76cbff1b5c3bbfe839391457Lennert Buytenhek	}
37415d014d13149aedd76cbff1b5c3bbfe839391457Lennert Buytenhek
37515d014d13149aedd76cbff1b5c3bbfe839391457Lennert Buytenhek	ixp2000_uengine_load(0, &ixp2400_rx);
37615d014d13149aedd76cbff1b5c3bbfe839391457Lennert Buytenhek	ixp2000_uengine_start_contexts(0, 0xff);
37715d014d13149aedd76cbff1b5c3bbfe839391457Lennert Buytenhek
37815d014d13149aedd76cbff1b5c3bbfe839391457Lennert Buytenhek	/* 256 entries, ring status set means 'empty', base address 0x0800.  */
37915d014d13149aedd76cbff1b5c3bbfe839391457Lennert Buytenhek	ixp2000_reg_write(RING_TX_PENDING_BASE, 0x44000800);
38015d014d13149aedd76cbff1b5c3bbfe839391457Lennert Buytenhek	ixp2000_reg_write(RING_TX_PENDING_HEAD, 0x00000000);
38115d014d13149aedd76cbff1b5c3bbfe839391457Lennert Buytenhek	ixp2000_reg_write(RING_TX_PENDING_TAIL, 0x00000000);
38215d014d13149aedd76cbff1b5c3bbfe839391457Lennert Buytenhek
38315d014d13149aedd76cbff1b5c3bbfe839391457Lennert Buytenhek	/* 256 entries, ring status set means 'full', base address 0x0c00.  */
38415d014d13149aedd76cbff1b5c3bbfe839391457Lennert Buytenhek	ixp2000_reg_write(RING_TX_DONE_BASE, 0x40000c00);
38515d014d13149aedd76cbff1b5c3bbfe839391457Lennert Buytenhek	ixp2000_reg_write(RING_TX_DONE_HEAD, 0x00000000);
38615d014d13149aedd76cbff1b5c3bbfe839391457Lennert Buytenhek	ixp2000_reg_write(RING_TX_DONE_TAIL, 0x00000000);
38715d014d13149aedd76cbff1b5c3bbfe839391457Lennert Buytenhek
38815d014d13149aedd76cbff1b5c3bbfe839391457Lennert Buytenhek	ixp2000_uengine_load(1, &ixp2400_tx);
38915d014d13149aedd76cbff1b5c3bbfe839391457Lennert Buytenhek	ixp2000_uengine_start_contexts(1, 0xff);
39015d014d13149aedd76cbff1b5c3bbfe839391457Lennert Buytenhek
3910c49919a4706cc8c72ff381da7f3ae094e6df03aLennert Buytenhek	for (i = 0; i < nds_count; i++) {
3920c49919a4706cc8c72ff381da7f3ae094e6df03aLennert Buytenhek		err = register_netdev(nds[i]);
3930c49919a4706cc8c72ff381da7f3ae094e6df03aLennert Buytenhek		if (err) {
3940c49919a4706cc8c72ff381da7f3ae094e6df03aLennert Buytenhek			while (--i >= 0)
3950c49919a4706cc8c72ff381da7f3ae094e6df03aLennert Buytenhek				unregister_netdev(nds[i]);
3960c49919a4706cc8c72ff381da7f3ae094e6df03aLennert Buytenhek			goto err_free_tx;
3970c49919a4706cc8c72ff381da7f3ae094e6df03aLennert Buytenhek		}
3980c49919a4706cc8c72ff381da7f3ae094e6df03aLennert Buytenhek	}
3990c49919a4706cc8c72ff381da7f3ae094e6df03aLennert Buytenhek
4007ed98bfdea45dbdc66261660357659470935a03aLennert Buytenhek	for (i = 0; i < nds_count; i++) {
4017ed98bfdea45dbdc66261660357659470935a03aLennert Buytenhek		printk(KERN_INFO "%s: IXP2000 MSF ethernet (port %d), "
4027ed98bfdea45dbdc66261660357659470935a03aLennert Buytenhek			"%.2x:%.2x:%.2x:%.2x:%.2x:%.2x.\n", nds[i]->name, i,
4037ed98bfdea45dbdc66261660357659470935a03aLennert Buytenhek			nds[i]->dev_addr[0], nds[i]->dev_addr[1],
4047ed98bfdea45dbdc66261660357659470935a03aLennert Buytenhek			nds[i]->dev_addr[2], nds[i]->dev_addr[3],
4057ed98bfdea45dbdc66261660357659470935a03aLennert Buytenhek			nds[i]->dev_addr[4], nds[i]->dev_addr[5]);
4067ed98bfdea45dbdc66261660357659470935a03aLennert Buytenhek	}
4077ed98bfdea45dbdc66261660357659470935a03aLennert Buytenhek
40815d014d13149aedd76cbff1b5c3bbfe839391457Lennert Buytenhek	return 0;
40915d014d13149aedd76cbff1b5c3bbfe839391457Lennert Buytenhek
4100c49919a4706cc8c72ff381da7f3ae094e6df03aLennert Buytenhekerr_free_tx:
4110c49919a4706cc8c72ff381da7f3ae094e6df03aLennert Buytenhek	for (i = 0; i < TX_BUF_COUNT; i++)
4120c49919a4706cc8c72ff381da7f3ae094e6df03aLennert Buytenhek		free_page((unsigned long)phys_to_virt(tx_desc[i].buf_addr));
4130c49919a4706cc8c72ff381da7f3ae094e6df03aLennert Buytenhek
41415d014d13149aedd76cbff1b5c3bbfe839391457Lennert Buytenhekerr_free_rx:
41515d014d13149aedd76cbff1b5c3bbfe839391457Lennert Buytenhek	for (i = 0; i < RX_BUF_COUNT; i++)
41615d014d13149aedd76cbff1b5c3bbfe839391457Lennert Buytenhek		free_page((unsigned long)phys_to_virt(rx_desc[i].buf_addr));
41715d014d13149aedd76cbff1b5c3bbfe839391457Lennert Buytenhek
41815d014d13149aedd76cbff1b5c3bbfe839391457Lennert Buytenhekerr_out:
41915d014d13149aedd76cbff1b5c3bbfe839391457Lennert Buytenhek	return err;
42015d014d13149aedd76cbff1b5c3bbfe839391457Lennert Buytenhek}
42115d014d13149aedd76cbff1b5c3bbfe839391457Lennert Buytenhek
42215d014d13149aedd76cbff1b5c3bbfe839391457Lennert Buytenhekvoid ixpdev_deinit(void)
42315d014d13149aedd76cbff1b5c3bbfe839391457Lennert Buytenhek{
42415d014d13149aedd76cbff1b5c3bbfe839391457Lennert Buytenhek	int i;
42515d014d13149aedd76cbff1b5c3bbfe839391457Lennert Buytenhek
42615d014d13149aedd76cbff1b5c3bbfe839391457Lennert Buytenhek	/* @@@ Flush out pending packets.  */
42715d014d13149aedd76cbff1b5c3bbfe839391457Lennert Buytenhek
4280c49919a4706cc8c72ff381da7f3ae094e6df03aLennert Buytenhek	for (i = 0; i < nds_count; i++)
4290c49919a4706cc8c72ff381da7f3ae094e6df03aLennert Buytenhek		unregister_netdev(nds[i]);
4300c49919a4706cc8c72ff381da7f3ae094e6df03aLennert Buytenhek
43115d014d13149aedd76cbff1b5c3bbfe839391457Lennert Buytenhek	ixp2000_uengine_stop_contexts(1, 0xff);
43215d014d13149aedd76cbff1b5c3bbfe839391457Lennert Buytenhek	ixp2000_uengine_stop_contexts(0, 0xff);
43315d014d13149aedd76cbff1b5c3bbfe839391457Lennert Buytenhek	ixp2000_uengine_reset(0x3);
43415d014d13149aedd76cbff1b5c3bbfe839391457Lennert Buytenhek
43515d014d13149aedd76cbff1b5c3bbfe839391457Lennert Buytenhek	for (i = 0; i < TX_BUF_COUNT; i++)
43615d014d13149aedd76cbff1b5c3bbfe839391457Lennert Buytenhek		free_page((unsigned long)phys_to_virt(tx_desc[i].buf_addr));
43715d014d13149aedd76cbff1b5c3bbfe839391457Lennert Buytenhek
43815d014d13149aedd76cbff1b5c3bbfe839391457Lennert Buytenhek	for (i = 0; i < RX_BUF_COUNT; i++)
43915d014d13149aedd76cbff1b5c3bbfe839391457Lennert Buytenhek		free_page((unsigned long)phys_to_virt(rx_desc[i].buf_addr));
44015d014d13149aedd76cbff1b5c3bbfe839391457Lennert Buytenhek}
441