/*
* ADM5120 built-in ethernet switch driver
*
- * Copyright (C) 2007 Gabor Juhos <juhosg at openwrt.org>
+ * Copyright (C) 2007-2008 Gabor Juhos <juhosg@openwrt.org>
*
* This code was based on a driver for Linux 2.6.xx by Jeroen Vreeken.
* Copyright Jeroen Vreeken (pe1rxq@amsat.org), 2005
#include <linux/ioport.h>
#include <linux/spinlock.h>
#include <linux/platform_device.h>
+#include <linux/io.h>
+#include <linux/irq.h>
#include <linux/netdevice.h>
#include <linux/etherdevice.h>
#include <linux/skbuff.h>
-#include <linux/io.h>
-#include <linux/irq.h>
-
#include <asm/mipsregs.h>
-#include <adm5120_info.h>
-#include <adm5120_defs.h>
-#include <adm5120_irq.h>
-#include <adm5120_switch.h>
+#include <asm/mach-adm5120/adm5120_info.h>
+#include <asm/mach-adm5120/adm5120_defs.h>
+#include <asm/mach-adm5120/adm5120_switch.h>
#include "adm5120sw.h"
#define DRV_NAME "adm5120-switch"
#define DRV_DESC "ADM5120 built-in ethernet switch driver"
-#define DRV_VERSION "0.1.0"
+#define DRV_VERSION "0.1.1"
#define CONFIG_ADM5120_SWITCH_NAPI 1
#undef CONFIG_ADM5120_SWITCH_DEBUG
#define TX_RING_SIZE 32
#define TX_QUEUE_LEN 28 /* Limit ring entries actually used. */
-#define TX_TIMEOUT HZ*400
+#define TX_TIMEOUT (HZ * 400)
#define RX_DESCS_SIZE (RX_RING_SIZE * sizeof(struct dma_desc *))
#define RX_SKBS_SIZE (RX_RING_SIZE * sizeof(struct sk_buff *))
/* ------------------------------------------------------------------------ */
struct adm5120_if_priv {
+ struct net_device *dev;
+
unsigned int vlan_no;
unsigned int port_mask;
+
+#ifdef CONFIG_ADM5120_SWITCH_NAPI
+ struct napi_struct napi;
+#endif
};
struct dma_desc {
t = desc->buf2;
SW_DBG(" buf2 %08X addr=%08X%s\n", desc->buf2,
t & DESC_ADDR_MASK,
- (t & DESC_BUF2_EN) ? " EN" : "" );
+ (t & DESC_BUF2_EN) ? " EN" : "");
t = desc->misc;
if (tx)
{
u32 t;
- t = SW_READ_REG(PHY_STATUS);
+ t = sw_read_reg(SWITCH_REG_PHY_STATUS);
SW_DBG("phy_status: %08X\n", t);
- t = SW_READ_REG(CPUP_CONF);
+ t = sw_read_reg(SWITCH_REG_CPUP_CONF);
SW_DBG("cpup_conf: %08X%s%s%s\n", t,
(t & CPUP_CONF_DCPUP) ? " DCPUP" : "",
(t & CPUP_CONF_CRCP) ? " CRCP" : "",
(t & CPUP_CONF_BTM) ? " BTM" : "");
- t = SW_READ_REG(PORT_CONF0);
+ t = sw_read_reg(SWITCH_REG_PORT_CONF0);
SW_DBG("port_conf0: %08X\n", t);
- t = SW_READ_REG(PORT_CONF1);
+ t = sw_read_reg(SWITCH_REG_PORT_CONF1);
SW_DBG("port_conf1: %08X\n", t);
- t = SW_READ_REG(PORT_CONF2);
+ t = sw_read_reg(SWITCH_REG_PORT_CONF2);
SW_DBG("port_conf2: %08X\n", t);
- t = SW_READ_REG(VLAN_G1);
+ t = sw_read_reg(SWITCH_REG_VLAN_G1);
SW_DBG("vlan g1: %08X\n", t);
- t = SW_READ_REG(VLAN_G2);
+ t = sw_read_reg(SWITCH_REG_VLAN_G2);
SW_DBG("vlan g2: %08X\n", t);
- t = SW_READ_REG(BW_CNTL0);
+ t = sw_read_reg(SWITCH_REG_BW_CNTL0);
SW_DBG("bw_cntl0: %08X\n", t);
- t = SW_READ_REG(BW_CNTL1);
+ t = sw_read_reg(SWITCH_REG_BW_CNTL1);
SW_DBG("bw_cntl1: %08X\n", t);
- t = SW_READ_REG(PHY_CNTL0);
+ t = sw_read_reg(SWITCH_REG_PHY_CNTL0);
SW_DBG("phy_cntl0: %08X\n", t);
- t = SW_READ_REG(PHY_CNTL1);
+ t = sw_read_reg(SWITCH_REG_PHY_CNTL1);
SW_DBG("phy_cntl1: %08X\n", t);
- t = SW_READ_REG(PHY_CNTL2);
+ t = sw_read_reg(SWITCH_REG_PHY_CNTL2);
SW_DBG("phy_cntl2: %08X\n", t);
- t = SW_READ_REG(PHY_CNTL3);
+ t = sw_read_reg(SWITCH_REG_PHY_CNTL3);
SW_DBG("phy_cntl3: %08X\n", t);
- t = SW_READ_REG(PHY_CNTL4);
+ t = sw_read_reg(SWITCH_REG_PHY_CNTL4);
SW_DBG("phy_cntl4: %08X\n", t);
- t = SW_READ_REG(INT_STATUS);
+ t = sw_read_reg(SWITCH_REG_INT_STATUS);
sw_dump_intr_mask("int_status: ", t);
- t = SW_READ_REG(INT_MASK);
+ t = sw_read_reg(SWITCH_REG_INT_MASK);
sw_dump_intr_mask("int_mask: ", t);
- t = SW_READ_REG(SHDA);
+ t = sw_read_reg(SWITCH_REG_SHDA);
SW_DBG("shda: %08X\n", t);
- t = SW_READ_REG(SLDA);
+ t = sw_read_reg(SWITCH_REG_SLDA);
SW_DBG("slda: %08X\n", t);
- t = SW_READ_REG(RHDA);
+ t = sw_read_reg(SWITCH_REG_RHDA);
SW_DBG("rhda: %08X\n", t);
- t = SW_READ_REG(RLDA);
+ t = sw_read_reg(SWITCH_REG_RLDA);
SW_DBG("rlda: %08X\n", t);
}
-
/* ------------------------------------------------------------------------ */
static inline void adm5120_rx_dma_update(struct dma_desc *desc,
dma_cache_wback_inv((unsigned long)skb->data,
skb->len);
-#ifdef CONFIG_ADM5120_SWITCH_USE_NAPI
+#ifdef CONFIG_ADM5120_SWITCH_NAPI
netif_receive_skb(skb);
#else
netif_rx(skb);
}
#ifdef CONFIG_ADM5120_SWITCH_NAPI
-static int adm5120_if_poll(struct net_device *dev, int *budget)
+static int adm5120_if_poll(struct napi_struct *napi, int limit)
{
- int limit = min(dev->quota, *budget);
+ struct adm5120_if_priv *priv = container_of(napi,
+ struct adm5120_if_priv, napi);
+ struct net_device *dev = priv->dev;
int done;
u32 status;
SW_DBG("%s: processing RX ring\n", dev->name);
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", dev->name);
- netif_rx_complete(dev);
+ napi_complete(napi);
sw_int_unmask(SWITCH_INTS_POLL);
return 0;
}
dev->name, done, status);
return 1;
}
-#endif /* CONFIG_ADM5120_SWITCH_USE_NAPI */
+#endif /* CONFIG_ADM5120_SWITCH_NAPI */
static irqreturn_t adm5120_switch_irq(int irq, void *dev_id)
if (status & SWITCH_INTS_POLL) {
struct net_device *dev = dev_id;
+ struct adm5120_if_priv *priv = netdev_priv(dev);
+
sw_dump_intr_mask("poll ints", status);
SW_DBG("enable polling mode for %s\n", dev->name);
sw_int_mask(SWITCH_INTS_POLL);
- netif_rx_schedule(dev);
+ napi_schedule(&priv->napi);
}
#else
sw_int_ack(status);
- if (status & (SWITCH_INT_RLD | SWITCH_INT_LDF)) {
+ if (status & (SWITCH_INT_RLD | SWITCH_INT_LDF))
adm5120_switch_rx(RX_RING_SIZE);
- }
- if (status & SWITCH_INT_SLD) {
+ if (status & SWITCH_INT_SLD)
adm5120_switch_tx();
- }
#endif
return IRQ_HANDLED;
{
memset(desc, 0, num * sizeof(*desc));
desc[num-1].buf1 |= DESC_EOR;
- memset(skbl, 0, sizeof(struct skb*)*num);
+ memset(skbl, 0, sizeof(struct skb *) * num);
cur_txl = 0;
dirty_txl = 0;
break;
}
skb_reserve(skbl[i], SKB_RESERVE_LEN);
- adm5120_rx_dma_update(&desc[i], skbl[i], (num-1==i));
+ adm5120_rx_dma_update(&desc[i], skbl[i], (num - 1 == i));
}
cur_rxl = 0;
sw_write_reg(SWITCH_REG_MAC_WT0, t);
- while (!(sw_read_reg(SWITCH_REG_MAC_WT0) & MAC_WT0_MWD));
+ while (!(sw_read_reg(SWITCH_REG_MAC_WT0) & MAC_WT0_MWD))
+ ;
}
static void adm5120_set_vlan(char *matrix)
sw_write_reg(SWITCH_REG_VLAN_G2, val);
/* Now set/update the port vs. device lookup table */
- for (port=0; port<SWITCH_NUM_PORTS; port++) {
- for (vlan_port=0; vlan_port<SWITCH_NUM_PORTS && !(matrix[vlan_port] & (0x00000001 << port)); vlan_port++);
- if (vlan_port <SWITCH_NUM_PORTS)
+ for (port = 0; port < SWITCH_NUM_PORTS; port++) {
+ for (vlan_port = 0; vlan_port < SWITCH_NUM_PORTS && !(matrix[vlan_port] & (0x00000001 << port)); vlan_port++)
+ ;
+ if (vlan_port < SWITCH_NUM_PORTS)
adm5120_port[port] = adm5120_devs[vlan_port];
else
adm5120_port[port] = NULL;
/* ------------------------------------------------------------------------ */
+#ifdef CONFIG_ADM5120_SWITCH_NAPI
+static inline void adm5120_if_napi_enable(struct net_device *dev)
+{
+ struct adm5120_if_priv *priv = netdev_priv(dev);
+ napi_enable(&priv->napi);
+}
+
+static inline void adm5120_if_napi_disable(struct net_device *dev)
+{
+ struct adm5120_if_priv *priv = netdev_priv(dev);
+ napi_disable(&priv->napi);
+}
+#else
+static inline void adm5120_if_napi_enable(struct net_device *dev) {}
+static inline void adm5120_if_napi_disable(struct net_device *dev) {}
+#endif /* CONFIG_ADM5120_SWITCH_NAPI */
+
static int adm5120_if_open(struct net_device *dev)
{
u32 t;
int err;
int i;
- err = request_irq(dev->irq, adm5120_switch_irq,
- (IRQF_SHARED | IRQF_DISABLED), dev->name, dev);
+ adm5120_if_napi_enable(dev);
+
+ err = request_irq(dev->irq, adm5120_switch_irq, IRQF_SHARED,
+ dev->name, dev);
if (err) {
SW_ERR("unable to get irq for %s\n", dev->name);
goto err;
return 0;
err:
+ adm5120_if_napi_disable(dev);
return err;
}
int i;
netif_stop_queue(dev);
+ adm5120_if_napi_disable(dev);
/* disable port if not assigned to other devices */
t = sw_read_reg(SWITCH_REG_PORT_CONF0);
struct adm5120_if_priv *priv = netdev_priv(dev);
unsigned int entry;
unsigned long data;
+ int i;
/* lock switch irq */
spin_lock_irq(&tx_lock);
data |= DESC_ADDR(skb->data);
desc->misc =
- ((skb->len<ETH_ZLEN?ETH_ZLEN:skb->len) << DESC_PKTLEN_SHIFT) |
+ ((skb->len < ETH_ZLEN ? ETH_ZLEN : skb->len) << DESC_PKTLEN_SHIFT) |
(0x1 << priv->vlan_no);
desc->buflen = skb->len < ETH_ZLEN ? ETH_ZLEN : skb->len;
cur_txl++;
if (cur_txl == dirty_txl + TX_QUEUE_LEN) {
- /* FIXME: stop queue for all devices */
- netif_stop_queue(dev);
+ for (i = 0; i < SWITCH_NUM_PORTS; i++) {
+ if (!adm5120_devs[i])
+ continue;
+ netif_stop_queue(adm5120_devs[i]);
+ }
}
dev->trans_start = jiffies;
static void adm5120_if_tx_timeout(struct net_device *dev)
{
- SW_INFO("TX timeout on %s\n",dev->name);
+ SW_INFO("TX timeout on %s\n", dev->name);
}
static void adm5120_if_set_multicast_list(struct net_device *dev)
/* to the CPU, the Bridge Test Mode has to be activated. */
/* Check if there is any vlan in promisc mode. */
- if (t & (SWITCH_PORTS_NOCPU << CPUP_CONF_DUNP_SHIFT))
- t &= ~CPUP_CONF_BTM; /* Disable Bridge Testing Mode */
- else
+ if (~t & (SWITCH_PORTS_NOCPU << CPUP_CONF_DUNP_SHIFT))
t |= CPUP_CONF_BTM; /* Enable Bridge Testing Mode */
+ else
+ t &= ~CPUP_CONF_BTM; /* Disable Bridge Testing Mode */
sw_write_reg(SWITCH_REG_CPUP_CONF, t);
static int adm5120_if_set_mac_address(struct net_device *dev, void *p)
{
- struct sockaddr *addr = p;
+ int ret;
+
+ ret = eth_mac_addr(dev, p);
+ if (ret)
+ return ret;
- memcpy(dev->dev_addr, addr->sa_data, dev->addr_len);
adm5120_write_mac(dev);
return 0;
}
struct adm5120_sw_info info;
struct adm5120_if_priv *priv = netdev_priv(dev);
- switch(cmd) {
+ switch (cmd) {
case SIOCGADMINFO:
info.magic = 0x5120;
info.ports = adm5120_nrdevs;
return 0;
}
+static const struct net_device_ops adm5120sw_netdev_ops = {
+ .ndo_open = adm5120_if_open,
+ .ndo_stop = adm5120_if_stop,
+ .ndo_start_xmit = adm5120_if_hard_start_xmit,
+ .ndo_set_multicast_list = adm5120_if_set_multicast_list,
+ .ndo_do_ioctl = adm5120_if_do_ioctl,
+ .ndo_tx_timeout = adm5120_if_tx_timeout,
+ .ndo_validate_addr = eth_validate_addr,
+ .ndo_change_mtu = eth_change_mtu,
+ .ndo_set_mac_address = adm5120_if_set_mac_address,
+};
+
static struct net_device *adm5120_if_alloc(void)
{
struct net_device *dev;
if (!dev)
return NULL;
+ priv = netdev_priv(dev);
+ priv->dev = dev;
+
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_if_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->netdev_ops = &adm5120sw_netdev_ops;
+ dev->watchdog_timeo = TX_TIMEOUT;
+
#ifdef CONFIG_ADM5120_SWITCH_NAPI
- dev->poll = adm5120_if_poll;
- dev->weight = 64;
+ netif_napi_add(dev, &priv->napi, adm5120_if_poll, 64);
#endif
- SET_MODULE_OWNER(dev);
-
return dev;
}
(SWITCH_PORTS_PHY << PHY_CNTL2_PHYR_SHIFT) |
(SWITCH_PORTS_PHY << PHY_CNTL2_AMDIX_SHIFT) |
PHY_CNTL2_RMAE;
- SW_WRITE_REG(PHY_CNTL2, t);
+ sw_write_reg(SWITCH_REG_PHY_CNTL2, t);
t = sw_read_reg(SWITCH_REG_PHY_CNTL3);
t |= PHY_CNTL3_RNT;
module_exit(adm5120_switch_mod_exit);
MODULE_LICENSE("GPL v2");
-MODULE_AUTHOR("Gabor Juhos <juhosg at openwrt.org>");
+MODULE_AUTHOR("Gabor Juhos <juhosg@openwrt.org>");
MODULE_DESCRIPTION(DRV_DESC);
MODULE_VERSION(DRV_VERSION);