summaryrefslogtreecommitdiff
path: root/target/linux
diff options
context:
space:
mode:
Diffstat (limited to 'target/linux')
-rw-r--r--target/linux/adm5120/files/drivers/net/adm5120sw.c374
1 files changed, 214 insertions, 160 deletions
diff --git a/target/linux/adm5120/files/drivers/net/adm5120sw.c b/target/linux/adm5120/files/drivers/net/adm5120sw.c
index 987e1de311..e354a35a61 100644
--- a/target/linux/adm5120/files/drivers/net/adm5120sw.c
+++ b/target/linux/adm5120/files/drivers/net/adm5120sw.c
@@ -163,7 +163,6 @@ static spinlock_t sw_lock = SPIN_LOCK_UNLOCKED;
static spinlock_t poll_lock = SPIN_LOCK_UNLOCKED;
static struct net_device sw_dev;
-static struct net_device *poll_dev;
/* ------------------------------------------------------------------------ */
@@ -458,31 +457,6 @@ static int adm5120_switch_rx(int limit)
}
-static int adm5120_switch_poll(struct net_device *dev, int *budget)
-{
- int limit = min(dev->quota, *budget);
- int done;
- u32 status;
-
- done = adm5120_switch_rx(limit);
-
- *budget -= done;
- dev->quota -= done;
-
- status = sw_int_status() & SWITCH_INTS_POLL;
- if ((done < limit) && (!status)) {
- spin_lock_irq(&poll_lock);
- SW_DBG("disable polling mode for %s\n", poll_dev->name);
- netif_rx_complete(poll_dev);
- sw_int_unmask(SWITCH_INTS_POLL);
- poll_dev = NULL;
- spin_unlock_irq(&poll_lock);
- return 0;
- }
-
- return 1;
-}
-
static void adm5120_switch_tx(void)
{
unsigned int entry;
@@ -517,6 +491,28 @@ static void adm5120_switch_tx(void)
}
}
+static int adm5120_if_poll(struct net_device *dev, int *budget)
+{
+ int limit = min(dev->quota, *budget);
+ int done;
+ u32 status;
+
+ done = adm5120_switch_rx(limit);
+
+ *budget -= done;
+ dev->quota -= done;
+
+ status = sw_int_status() & SWITCH_INTS_POLL;
+ if ((done < limit) && (!status)) {
+ SW_DBG("disable polling mode for %s\n", poll_dev->name);
+ netif_rx_complete(dev);
+ sw_int_unmask(SWITCH_INTS_POLL);
+ return 0;
+ }
+
+ return 1;
+}
+
static irqreturn_t adm5120_poll_irq(int irq, void *dev_id)
{
struct net_device *dev = dev_id;
@@ -529,19 +525,9 @@ static irqreturn_t adm5120_poll_irq(int irq, void *dev_id)
sw_dump_intr_mask("poll ints", status);
- if (!netif_running(dev)) {
- SW_DBG("device %s is not running\n", dev->name);
- return IRQ_NONE;
- }
-
- spin_lock(&poll_lock);
- if (!poll_dev) {
- SW_DBG("enable polling mode for %s\n", dev->name);
- poll_dev = dev;
- sw_int_mask(SWITCH_INTS_POLL);
- netif_rx_schedule(poll_dev);
- }
- spin_unlock(&poll_lock);
+ SW_DBG("enable polling mode for %s\n", dev->name);
+ sw_int_mask(SWITCH_INTS_POLL);
+ netif_rx_schedule(dev);
return IRQ_HANDLED;
}
@@ -605,12 +591,143 @@ static void adm5120_set_bw(char *matrix)
sw_read_reg(SWITCH_REG_BW_CNTL1));
}
-static int adm5120_switch_open(struct net_device *dev)
+static void adm5120_switch_tx_ring_reset(struct dma_desc *desc,
+ struct sk_buff **skbl, int num)
+{
+ memset(desc, 0, num * sizeof(*desc));
+ desc[num-1].buf1 |= DESC_EOR;
+ memset(skbl, 0, sizeof(struct skb*)*num);
+
+ cur_txl = 0;
+ dirty_txl = 0;
+}
+
+static void adm5120_switch_rx_ring_reset(struct dma_desc *desc,
+ struct sk_buff **skbl, int num)
+{
+ int i;
+
+ memset(desc, 0, num * sizeof(*desc));
+ for (i = 0; i < num; i++) {
+ skbl[i] = dev_alloc_skb(SKB_ALLOC_LEN);
+ if (!skbl[i]) {
+ i = num;
+ break;
+ }
+ skb_reserve(skbl[i], SKB_RESERVE_LEN);
+ adm5120_rx_dma_update(&desc[i], skbl[i], (num-1==i));
+ }
+
+ cur_rxl = 0;
+ dirty_rxl = 0;
+}
+
+static int adm5120_switch_tx_ring_alloc(void)
+{
+ int err;
+
+ txl_descs = dma_alloc_coherent(NULL, TX_DESCS_SIZE, &txl_descs_dma,
+ GFP_ATOMIC);
+ if (!txl_descs) {
+ err = -ENOMEM;
+ goto err;
+ }
+
+ txl_skbuff = kzalloc(TX_SKBS_SIZE, GFP_KERNEL);
+ if (!txl_skbuff) {
+ err = -ENOMEM;
+ goto err;
+ }
+
+ return 0;
+
+err:
+ return err;
+}
+
+static void adm5120_switch_tx_ring_free(void)
+{
+ int i;
+
+ if (txl_skbuff) {
+ for (i = 0; i < TX_RING_SIZE; i++)
+ if (txl_skbuff[i])
+ kfree_skb(txl_skbuff[i]);
+ kfree(txl_skbuff);
+ }
+
+ if (txl_descs)
+ dma_free_coherent(NULL, TX_DESCS_SIZE, txl_descs,
+ txl_descs_dma);
+}
+
+static int adm5120_switch_rx_ring_alloc(void)
+{
+ int err;
+ int i;
+
+ /* init RX ring */
+ rxl_descs = dma_alloc_coherent(NULL, RX_DESCS_SIZE, &rxl_descs_dma,
+ GFP_ATOMIC);
+ if (!rxl_descs) {
+ err = -ENOMEM;
+ goto err;
+ }
+
+ rxl_skbuff = kzalloc(RX_SKBS_SIZE, GFP_KERNEL);
+ if (!rxl_skbuff) {
+ err = -ENOMEM;
+ goto err;
+ }
+
+ for (i = 0; i < RX_RING_SIZE; i++) {
+ struct sk_buff *skb;
+ skb = alloc_skb(SKB_ALLOC_LEN, GFP_ATOMIC);
+ if (!skb) {
+ err = -ENOMEM;
+ goto err;
+ }
+ rxl_skbuff[i] = skb;
+ skb_reserve(skb, SKB_RESERVE_LEN);
+ }
+
+ return 0;
+
+err:
+ return err;
+}
+
+static void adm5120_switch_rx_ring_free(void)
+{
+ int i;
+
+ if (rxl_skbuff) {
+ for (i = 0; i < RX_RING_SIZE; i++)
+ if (rxl_skbuff[i])
+ kfree_skb(rxl_skbuff[i]);
+ kfree(rxl_skbuff);
+ }
+
+ if (rxl_descs)
+ dma_free_coherent(NULL, RX_DESCS_SIZE, rxl_descs,
+ rxl_descs_dma);
+}
+
+/* ------------------------------------------------------------------------ */
+
+static int adm5120_if_open(struct net_device *dev)
{
u32 t;
+ int err;
int i;
- netif_start_queue(dev);
+ err = request_irq(dev->irq, adm5120_poll_irq,
+ (IRQF_SHARED | IRQF_DISABLED), dev->name, dev);
+ if (err) {
+ SW_ERR("unable to get irq for %s\n", dev->name);
+ goto err;
+ }
+
if (!sw_used++)
/* enable interrupts on first open */
sw_int_unmask(SWITCH_INTS_USED);
@@ -623,16 +740,20 @@ static int adm5120_switch_open(struct net_device *dev)
}
sw_write_reg(SWITCH_REG_PORT_CONF0, t);
+ netif_start_queue(dev);
+
return 0;
+
+err:
+ return err;
}
-static int adm5120_switch_stop(struct net_device *dev)
+static int adm5120_if_stop(struct net_device *dev)
{
u32 t;
int i;
- if (!--sw_used)
- sw_int_mask(SWITCH_INTS_USED);
+ netif_stop_queue(dev);
/* disable port if not assigned to other devices */
t = sw_read_reg(SWITCH_REG_PORT_CONF0);
@@ -643,11 +764,16 @@ static int adm5120_switch_stop(struct net_device *dev)
}
sw_write_reg(SWITCH_REG_PORT_CONF0, t);
- netif_stop_queue(dev);
+ if (!--sw_used)
+ sw_int_mask(SWITCH_INTS_USED);
+
+ free_irq(dev->irq, dev);
+
return 0;
}
-static int adm5120_sw_start_xmit(struct sk_buff *skb, struct net_device *dev)
+static int adm5120_if_hard_start_xmit(struct sk_buff *skb,
+ struct net_device *dev)
{
struct dma_desc *desc;
struct adm5120_sw *priv = netdev_priv(dev);
@@ -695,7 +821,7 @@ static int adm5120_sw_start_xmit(struct sk_buff *skb, struct net_device *dev)
return 0;
}
-static void adm5120_tx_timeout(struct net_device *dev)
+static void adm5120_if_tx_timeout(struct net_device *dev)
{
SW_INFO("TX timeout on %s\n",dev->name);
}
@@ -769,7 +895,7 @@ static void adm5120_write_mac(struct net_device *dev)
while (!(sw_read_reg(SWITCH_REG_MAC_WT0) & MAC_WT0_MWD));
}
-static int adm5120_sw_set_mac_address(struct net_device *dev, void *p)
+static int adm5120_if_set_mac_address(struct net_device *dev, void *p)
{
struct sockaddr *addr = p;
@@ -778,7 +904,7 @@ static int adm5120_sw_set_mac_address(struct net_device *dev, void *p)
return 0;
}
-static int adm5120_do_ioctl(struct net_device *dev, struct ifreq *rq, int cmd)
+static int adm5120_if_do_ioctl(struct net_device *dev, struct ifreq *rq, int cmd)
{
int err;
struct adm5120_sw_info info;
@@ -827,29 +953,30 @@ static int adm5120_do_ioctl(struct net_device *dev, struct ifreq *rq, int cmd)
return 0;
}
-static void adm5120_dma_tx_init(struct dma_desc *desc, struct sk_buff **skbl,
- int num)
+static struct net_device *adm5120_if_alloc(void)
{
- memset(desc, 0, num * sizeof(*desc));
- desc[num-1].buf1 |= DESC_EOR;
- memset(skbl, 0, sizeof(struct skb*)*num);
-}
-
-static void adm5120_dma_rx_init(struct dma_desc *desc, struct sk_buff **skbl,
- int num)
-{
- int i;
-
- memset(desc, 0, num * sizeof(*desc));
- for (i=0; i<num; i++) {
- skbl[i] = dev_alloc_skb(SKB_ALLOC_LEN);
- if (!skbl[i]) {
- i=num;
- break;
- }
- skb_reserve(skbl[i], SKB_RESERVE_LEN);
- adm5120_rx_dma_update(&desc[i], skbl[i], (num-1==i));
- }
+ struct net_device *dev;
+ struct adm5120_sw *priv;
+
+ dev = alloc_etherdev(sizeof(*priv));
+ if (!dev)
+ return NULL;
+
+ dev->irq = ADM5120_IRQ_SWITCH;
+ dev->open = adm5120_if_open;
+ dev->hard_start_xmit = adm5120_if_hard_start_xmit;
+ dev->stop = adm5120_if_stop;
+ dev->set_multicast_list = adm5120_set_multicast_list;
+ dev->do_ioctl = adm5120_if_do_ioctl;
+ dev->tx_timeout = adm5120_if_tx_timeout;
+ dev->watchdog_timeo = TX_TIMEOUT;
+ dev->set_mac_address = adm5120_if_set_mac_address;
+ dev->poll = adm5120_if_poll;
+ dev->weight = 64;
+
+ SET_MODULE_OWNER(dev);
+
+ return dev;
}
static void adm5120_switch_cleanup(void)
@@ -863,41 +990,18 @@ static void adm5120_switch_cleanup(void)
struct net_device *dev = adm5120_devs[i];
if (dev) {
unregister_netdev(dev);
- free_irq(ADM5120_IRQ_SWITCH, dev);
free_netdev(dev);
}
}
- /* cleanup TX ring */
- if (txl_skbuff) {
- for (i = 0; i < TX_RING_SIZE; i++)
- if (txl_skbuff[i])
- kfree_skb(txl_skbuff[i]);
- kfree(txl_skbuff);
- }
-
- if (txl_descs)
- dma_free_coherent(NULL, TX_DESCS_SIZE, txl_descs,
- txl_descs_dma);
-
- /* cleanup RX ring */
- if (rxl_skbuff) {
- for (i = 0; i < RX_RING_SIZE; i++)
- if (rxl_skbuff[i])
- kfree_skb(rxl_skbuff[i]);
- kfree(rxl_skbuff);
- }
-
- if (rxl_descs)
- dma_free_coherent(NULL, RX_DESCS_SIZE, rxl_descs,
- rxl_descs_dma);
+ adm5120_switch_tx_ring_free();
+ adm5120_switch_rx_ring_free();
free_irq(ADM5120_IRQ_SWITCH, &sw_dev);
}
static int __init adm5120_switch_init(void)
{
- struct net_device *dev;
u32 t;
int i, err;
@@ -939,49 +1043,16 @@ static int __init adm5120_switch_init(void)
sw_int_mask(SWITCH_INTS_ALL);
sw_int_ack(SWITCH_INTS_ALL);
- /* init RX ring */
- cur_rxl = dirty_rxl = 0;
- rxl_descs = dma_alloc_coherent(NULL, RX_DESCS_SIZE, &rxl_descs_dma,
- GFP_ATOMIC);
- if (!rxl_descs) {
- err = -ENOMEM;
- goto err;
- }
-
- rxl_skbuff = kzalloc(RX_SKBS_SIZE, GFP_KERNEL);
- if (!rxl_skbuff) {
- err = -ENOMEM;
+ err = adm5120_switch_rx_ring_alloc();
+ if (err)
goto err;
- }
-
- for (i = 0; i < RX_RING_SIZE; i++) {
- struct sk_buff *skb;
- skb = alloc_skb(SKB_ALLOC_LEN, GFP_ATOMIC);
- if (!skb) {
- err = -ENOMEM;
- goto err;
- }
- rxl_skbuff[i] = skb;
- skb_reserve(skb, SKB_RESERVE_LEN);
- }
- /* init TX ring */
- cur_txl = dirty_txl = 0;
- txl_descs = dma_alloc_coherent(NULL, TX_DESCS_SIZE, &txl_descs_dma,
- GFP_ATOMIC);
- if (!txl_descs) {
- err = -ENOMEM;
+ err = adm5120_switch_tx_ring_alloc();
+ if (err)
goto err;
- }
- txl_skbuff = kzalloc(TX_SKBS_SIZE, GFP_KERNEL);
- if (!txl_skbuff) {
- err = -ENOMEM;
- goto err;
- }
-
- adm5120_dma_tx_init(txl_descs, txl_skbuff, TX_RING_SIZE);
- adm5120_dma_rx_init(rxl_descs, rxl_skbuff, RX_RING_SIZE);
+ adm5120_switch_tx_ring_reset(txl_descs, txl_skbuff, TX_RING_SIZE);
+ adm5120_switch_rx_ring_reset(rxl_descs, rxl_skbuff, RX_RING_SIZE);
sw_write_reg(SWITCH_REG_SHDA, 0);
sw_write_reg(SWITCH_REG_SLDA, KSEG1ADDR(txl_descs));
@@ -989,35 +1060,19 @@ static int __init adm5120_switch_init(void)
sw_write_reg(SWITCH_REG_RLDA, KSEG1ADDR(rxl_descs));
for (i = 0; i < SWITCH_NUM_PORTS; i++) {
- adm5120_devs[i] = alloc_etherdev(sizeof(struct adm5120_sw));
- if (!adm5120_devs[i]) {
+ struct net_device *dev;
+ struct adm5120_sw *priv;
+
+ dev = adm5120_if_alloc();
+ if (!dev) {
err = -ENOMEM;
goto err;
}
- dev = adm5120_devs[i];
- err = request_irq(ADM5120_IRQ_SWITCH, adm5120_poll_irq,
- (IRQF_SHARED | IRQF_DISABLED), dev->name, dev);
- if (err) {
- SW_ERR("unable to get irq for %s\n", dev->name);
- goto err;
- }
+ adm5120_devs[i] = dev;
+ priv = netdev_priv(dev);
- SET_MODULE_OWNER(dev);
- memset(netdev_priv(dev), 0, sizeof(struct adm5120_sw));
- ((struct adm5120_sw*)netdev_priv(dev))->port = i;
- dev->base_addr = ADM5120_SWITCH_BASE;
- dev->irq = ADM5120_IRQ_SWITCH;
- dev->open = adm5120_switch_open;
- dev->hard_start_xmit = adm5120_sw_start_xmit;
- dev->stop = adm5120_switch_stop;
- dev->set_multicast_list = adm5120_set_multicast_list;
- dev->do_ioctl = adm5120_do_ioctl;
- dev->tx_timeout = adm5120_tx_timeout;
- dev->watchdog_timeo = TX_TIMEOUT;
- dev->set_mac_address = adm5120_sw_set_mac_address;
- dev->poll = adm5120_switch_poll;
- dev->weight = 64;
+ priv->port = i;
memcpy(dev->dev_addr, adm5120_eth_macs[i], 6);
adm5120_write_mac(dev);
@@ -1028,7 +1083,6 @@ static int __init adm5120_switch_init(void)
dev->name, err);
goto err;
}
- SW_INFO("%s created for switch port%d\n", dev->name, i);
}
/* setup vlan/port mapping after devs are filled up */