X-Git-Url: https://git.rohieb.name/openwrt.git/blobdiff_plain/06b8f376442456d4fe011c3090868864c041aed9..23e474a920c887df2121e17f2655b8788370e739:/target/linux/adm5120/files/drivers/net/adm5120sw.c?ds=sidebyside diff --git a/target/linux/adm5120/files/drivers/net/adm5120sw.c b/target/linux/adm5120/files/drivers/net/adm5120sw.c index 877542699..a92695485 100644 --- a/target/linux/adm5120/files/drivers/net/adm5120sw.c +++ b/target/linux/adm5120/files/drivers/net/adm5120sw.c @@ -1,7 +1,7 @@ /* * ADM5120 built-in ethernet switch driver * - * Copyright (C) 2007 Gabor Juhos + * Copyright (C) 2007-2008 Gabor Juhos * * This code was based on a driver for Linux 2.6.xx by Jeroen Vreeken. * Copyright Jeroen Vreeken (pe1rxq@amsat.org), 2005 @@ -24,26 +24,24 @@ #include #include #include +#include +#include #include #include #include -#include -#include - #include -#include -#include -#include -#include +#include +#include +#include #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 @@ -66,7 +64,7 @@ #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 *)) @@ -93,8 +91,14 @@ /* ------------------------------------------------------------------------ */ 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 { @@ -228,7 +232,7 @@ static void sw_dump_desc(char *label, struct dma_desc *desc, int tx) 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) @@ -280,60 +284,59 @@ static void sw_dump_regs(void) { 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, @@ -430,7 +433,7 @@ static int adm5120_switch_rx(int limit) 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); @@ -495,9 +498,11 @@ static void adm5120_switch_tx(void) } #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; @@ -509,13 +514,10 @@ static int adm5120_if_poll(struct net_device *dev, int *budget) 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; } @@ -524,7 +526,7 @@ static int adm5120_if_poll(struct net_device *dev, int *budget) 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) @@ -541,21 +543,21 @@ 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; @@ -585,7 +587,7 @@ static void adm5120_switch_tx_ring_reset(struct dma_desc *desc, { 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; @@ -604,7 +606,7 @@ static void adm5120_switch_rx_ring_reset(struct dma_desc *desc, 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; @@ -709,7 +711,7 @@ static void adm5120_write_mac(struct net_device *dev) u32 t; t = mac[2] | (mac[3] << MAC_WT1_MAC3_SHIFT) | - (mac[4] << MAC_WT1_MAC4_SHIFT) | (mac[5] << MAC_WT1_MAC4_SHIFT); + (mac[4] << MAC_WT1_MAC4_SHIFT) | (mac[5] << MAC_WT1_MAC5_SHIFT); sw_write_reg(SWITCH_REG_MAC_WT1, t); t = (mac[0] << MAC_WT0_MAC0_SHIFT) | (mac[1] << MAC_WT0_MAC1_SHIFT) | @@ -717,7 +719,8 @@ static void adm5120_write_mac(struct net_device *dev) 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) @@ -731,9 +734,10 @@ 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; portnapi); +} + +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; @@ -809,6 +832,7 @@ static int adm5120_if_open(struct net_device *dev) return 0; err: + adm5120_if_napi_disable(dev); return err; } @@ -818,6 +842,7 @@ static int adm5120_if_stop(struct net_device *dev) 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); @@ -843,6 +868,7 @@ static int adm5120_if_hard_start_xmit(struct sk_buff *skb, struct adm5120_if_priv *priv = netdev_priv(dev); unsigned int entry; unsigned long data; + int i; /* lock switch irq */ spin_lock_irq(&tx_lock); @@ -865,7 +891,7 @@ static int adm5120_if_hard_start_xmit(struct sk_buff *skb, data |= DESC_ADDR(skb->data); desc->misc = - ((skb->lenlen) << 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; @@ -875,8 +901,11 @@ static int adm5120_if_hard_start_xmit(struct sk_buff *skb, 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; @@ -888,7 +917,7 @@ static int adm5120_if_hard_start_xmit(struct sk_buff *skb, 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) @@ -933,10 +962,10 @@ 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); @@ -944,9 +973,12 @@ static void adm5120_if_set_multicast_list(struct net_device *dev) 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; } @@ -958,7 +990,7 @@ static int adm5120_if_do_ioctl(struct net_device *dev, struct ifreq *rq, 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; @@ -988,6 +1020,18 @@ static int adm5120_if_do_ioctl(struct net_device *dev, struct ifreq *rq, 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; @@ -997,22 +1041,17 @@ static struct net_device *adm5120_if_alloc(void) 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; } @@ -1061,7 +1100,7 @@ static int __init adm5120_switch_probe(struct platform_device *pdev) (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; @@ -1168,6 +1207,6 @@ module_init(adm5120_switch_mod_init); module_exit(adm5120_switch_mod_exit); MODULE_LICENSE("GPL v2"); -MODULE_AUTHOR("Gabor Juhos "); +MODULE_AUTHOR("Gabor Juhos "); MODULE_DESCRIPTION(DRV_DESC); MODULE_VERSION(DRV_VERSION);