--- /dev/null
+/*\r
+ * linux/arch/arc/drivers/arcvmac.c\r
+ *\r
+ * Copyright (C) 2003-2006 Codito Technologies, for linux-2.4 port\r
+ * Copyright (C) 2006-2007 Celunite Inc, for linux-2.6 port\r
+ * Copyright (C) 2007-2008 Sagem Communications, Fehmi HAFSI\r
+ * Copyright (C) 2009 Sagem Communications, Andreas Fenkart\r
+ * All Rights Reserved.\r
+ *\r
+ * This program is free software; you can redistribute it and/or modify\r
+ * it under the terms of the GNU General Public License as published by\r
+ * the Free Software Foundation; either version 2 of the License, or\r
+ * (at your option) any later version.\r
+ *\r
+ * This program is distributed in the hope that it will be useful,\r
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\r
+ * GNU General Public License for more details.\r
+ *\r
+ * You should have received a copy of the GNU General Public License\r
+ * along with this program; if not, write to the Free Software\r
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA\r
+ *\r
+ * external PHY support based on dnet.c\r
+ * ring management based on bcm63xx_enet.c\r
+ *\r
+ * Authors: amit.bhor@celunite.com, sameer.dhavale@celunite.com\r
+ */\r
+\r
+//#define DEBUG\r
+\r
+#include <linux/clk.h>\r
+#include <linux/crc32.h>\r
+#include <linux/delay.h>\r
+#include <linux/dma-mapping.h>\r
+#include <linux/etherdevice.h>\r
+#include <linux/init.h>\r
+#include <linux/io.h>\r
+#include <linux/kernel.h>\r
+#include <linux/module.h>\r
+#include <linux/moduleparam.h>\r
+#include <linux/netdevice.h>\r
+#include <linux/phy.h>\r
+#include <linux/platform_device.h>\r
+#include <linux/slab.h>\r
+#include <linux/types.h>\r
+\r
+#include <mach/iomux.h>\r
+#include <mach/gpio.h>\r
+#include <mach/cru.h>\r
+#include <mach/board.h>\r
+#include "rk29_vmac.h"\r
+\r
+/* Register access macros */\r
+#define vmac_writel(port, value, reg) \\r
+ writel((value), (port)->regs + reg##_OFFSET)\r
+#define vmac_readl(port, reg) readl((port)->regs + reg##_OFFSET)\r
+\r
+static unsigned char *read_mac_reg(struct net_device *dev,\r
+ unsigned char hwaddr[ETH_ALEN])\r
+{\r
+ struct vmac_priv *ap = netdev_priv(dev);\r
+ unsigned mac_lo, mac_hi;\r
+\r
+ WARN_ON(!hwaddr);\r
+ mac_lo = vmac_readl(ap, ADDRL);\r
+ mac_hi = vmac_readl(ap, ADDRH);\r
+\r
+ hwaddr[0] = (mac_lo >> 0) & 0xff;\r
+ hwaddr[1] = (mac_lo >> 8) & 0xff;\r
+ hwaddr[2] = (mac_lo >> 16) & 0xff;\r
+ hwaddr[3] = (mac_lo >> 24) & 0xff;\r
+ hwaddr[4] = (mac_hi >> 0) & 0xff;\r
+ hwaddr[5] = (mac_hi >> 8) & 0xff;\r
+ return hwaddr;\r
+}\r
+\r
+static void write_mac_reg(struct net_device *dev, unsigned char* hwaddr)\r
+{\r
+ struct vmac_priv *ap = netdev_priv(dev);\r
+ unsigned mac_lo, mac_hi;\r
+\r
+ mac_lo = hwaddr[3] << 24 | hwaddr[2] << 16 | hwaddr[1] << 8 | hwaddr[0];\r
+ mac_hi = hwaddr[5] << 8 | hwaddr[4];\r
+\r
+ vmac_writel(ap, mac_lo, ADDRL);\r
+ vmac_writel(ap, mac_hi, ADDRH);\r
+}\r
+\r
+static void vmac_mdio_xmit(struct vmac_priv *ap, unsigned val)\r
+{\r
+ init_completion(&ap->mdio_complete);\r
+ vmac_writel(ap, val, MDIO_DATA);\r
+ wait_for_completion(&ap->mdio_complete);\r
+}\r
+\r
+static int vmac_mdio_read(struct mii_bus *bus, int phy_id, int phy_reg)\r
+{\r
+ struct vmac_priv *vmac = bus->priv;\r
+ unsigned int val;\r
+ /* only 5 bits allowed for phy-addr and reg_offset */\r
+ WARN_ON(phy_id & ~0x1f || phy_reg & ~0x1f);\r
+\r
+ val = MDIO_BASE | MDIO_OP_READ;\r
+ val |= phy_id << 23 | phy_reg << 18;\r
+ vmac_mdio_xmit(vmac, val);\r
+\r
+ val = vmac_readl(vmac, MDIO_DATA);\r
+ return val & MDIO_DATA_MASK;\r
+}\r
+\r
+static int vmac_mdio_write(struct mii_bus *bus, int phy_id, int phy_reg,\r
+ u16 value)\r
+{\r
+ struct vmac_priv *vmac = bus->priv;\r
+ unsigned int val;\r
+ /* only 5 bits allowed for phy-addr and reg_offset */\r
+ WARN_ON(phy_id & ~0x1f || phy_reg & ~0x1f);\r
+\r
+ val = MDIO_BASE | MDIO_OP_WRITE;\r
+ val |= phy_id << 23 | phy_reg << 18;\r
+ val |= (value & MDIO_DATA_MASK);\r
+ vmac_mdio_xmit(vmac, val);\r
+ return 0;\r
+}\r
+\r
+static void vmac_handle_link_change(struct net_device *dev)\r
+{\r
+ struct vmac_priv *ap = netdev_priv(dev);\r
+ struct phy_device *phydev = ap->phy_dev;\r
+ unsigned long flags;\r
+ int report_change = 0;\r
+\r
+ spin_lock_irqsave(&ap->lock, flags);\r
+\r
+ if (phydev->duplex != ap->duplex) {\r
+ unsigned tmp;\r
+\r
+ tmp = vmac_readl(ap, CONTROL);\r
+\r
+ if (phydev->duplex)\r
+ tmp |= ENFL_MASK;\r
+ else\r
+ tmp &= ~ENFL_MASK;\r
+\r
+ vmac_writel(ap, tmp, CONTROL);\r
+\r
+ ap->duplex = phydev->duplex;\r
+ report_change = 1;\r
+ }\r
+\r
+ if (phydev->speed != ap->speed) {\r
+ ap->speed = phydev->speed;\r
+ report_change = 1;\r
+ }\r
+\r
+ if (phydev->link != ap->link) {\r
+ ap->link = phydev->link;\r
+ report_change = 1;\r
+ }\r
+\r
+ spin_unlock_irqrestore(&ap->lock, flags);\r
+\r
+ if (report_change)\r
+ phy_print_status(ap->phy_dev);\r
+}\r
+\r
+static int __devinit vmac_mii_probe(struct net_device *dev)\r
+{\r
+ struct vmac_priv *ap = netdev_priv(dev);\r
+ struct phy_device *phydev = NULL;\r
+ struct clk *sys_clk;\r
+ unsigned long clock_rate;\r
+ int phy_addr, err;\r
+\r
+ /* find the first phy */\r
+ for (phy_addr = 0; phy_addr < PHY_MAX_ADDR; phy_addr++) {\r
+ if (ap->mii_bus->phy_map[phy_addr]) {\r
+ phydev = ap->mii_bus->phy_map[phy_addr];\r
+ break;\r
+ }\r
+ }\r
+\r
+ if (!phydev) {\r
+ dev_err(&dev->dev, "no PHY found\n");\r
+ return -ENODEV;\r
+ }\r
+\r
+ /* add pin_irq, if avail */\r
+ phydev = phy_connect(dev, dev_name(&phydev->dev),\r
+ &vmac_handle_link_change, 0,\r
+ //PHY_INTERFACE_MODE_MII);\r
+ PHY_INTERFACE_MODE_RMII);//????????\r
+ if (IS_ERR(phydev)) {\r
+ err = PTR_ERR(phydev);\r
+ dev_err(&dev->dev, "could not attach to PHY %d\n", err);\r
+ goto err_out;\r
+ }\r
+\r
+ phydev->supported &= PHY_BASIC_FEATURES;\r
+ phydev->supported |= SUPPORTED_Asym_Pause | SUPPORTED_Pause;\r
+\r
+#if 0\r
+ sys_clk = clk_get(NULL, "mac_ref");////////\r
+ if (IS_ERR(sys_clk)) {\r
+ err = PTR_ERR(sys_clk);\r
+ goto err_disconnect;\r
+ }\r
+ \r
+ clk_set_rate(sys_clk,50000000);\r
+ clock_rate = clk_get_rate(sys_clk);\r
+ clk_put(sys_clk);\r
+ \r
+ printk("%s::%d --mac clock = %d\n",__func__, __LINE__, clock_rate);\r
+ dev_dbg(&ap->pdev->dev, "clk_get: dev_name : %s %lu\n",\r
+ dev_name(&ap->pdev->dev),\r
+ clock_rate);\r
+\r
+ if (clock_rate < 25000000)\r
+ phydev->supported &= ~(SUPPORTED_100baseT_Half |\r
+ SUPPORTED_100baseT_Full);\r
+#endif\r
+\r
+ phydev->advertising = phydev->supported;\r
+\r
+ ap->link = 0;\r
+ ap->speed = 0;\r
+ ap->duplex = -1;\r
+ ap->phy_dev = phydev;\r
+\r
+ return 0;\r
+\r
+err_disconnect:\r
+ phy_disconnect(phydev);\r
+err_out:\r
+ return err;\r
+}\r
+\r
+static int __devinit vmac_mii_init(struct vmac_priv *ap)\r
+{\r
+ int err, i;\r
+\r
+ ap->mii_bus = mdiobus_alloc();\r
+ \r
+ if (ap->mii_bus == NULL)\r
+ return -ENOMEM;\r
+\r
+ ap->mii_bus->name = "vmac_mii_bus";\r
+ ap->mii_bus->read = &vmac_mdio_read;\r
+ ap->mii_bus->write = &vmac_mdio_write;\r
+\r
+ snprintf(ap->mii_bus->id, MII_BUS_ID_SIZE, "%x", 0);\r
+\r
+ ap->mii_bus->priv = ap;\r
+\r
+ err = -ENOMEM;\r
+ ap->mii_bus->irq = kmalloc(sizeof(int) * PHY_MAX_ADDR, GFP_KERNEL);\r
+ if (!ap->mii_bus->irq)\r
+ goto err_out;\r
+\r
+ for (i = 0; i < PHY_MAX_ADDR; i++)\r
+ ap->mii_bus->irq[i] = PHY_POLL;\r
+\r
+#if 0\r
+ /* FIXME: what is it used for? */\r
+ platform_set_drvdata(ap->dev, ap->mii_bus);\r
+#endif\r
+\r
+ err = mdiobus_register(ap->mii_bus);\r
+ if (err)\r
+ goto err_out_free_mdio_irq;\r
+\r
+ err = vmac_mii_probe(ap->dev);\r
+ if (err)\r
+ goto err_out_unregister_bus;\r
+\r
+ return 0;\r
+\r
+err_out_unregister_bus:\r
+ mdiobus_unregister(ap->mii_bus);\r
+err_out_free_mdio_irq:\r
+ kfree(ap->mii_bus->irq);\r
+err_out:\r
+ mdiobus_free(ap->mii_bus);\r
+ return err;\r
+}\r
+\r
+static void vmac_mii_exit(struct net_device *dev)\r
+{\r
+ struct vmac_priv *ap = netdev_priv(dev);\r
+\r
+ if (ap->phy_dev)\r
+ phy_disconnect(ap->phy_dev);\r
+\r
+ mdiobus_unregister(ap->mii_bus);\r
+ kfree(ap->mii_bus->irq);\r
+ mdiobus_free(ap->mii_bus);\r
+}\r
+\r
+static int vmacether_get_settings(struct net_device *dev,\r
+ struct ethtool_cmd *cmd)\r
+{\r
+ struct vmac_priv *ap = netdev_priv(dev);\r
+ struct phy_device *phydev = ap->phy_dev;\r
+\r
+ if (!phydev)\r
+ return -ENODEV;\r
+\r
+ return phy_ethtool_gset(phydev, cmd);\r
+}\r
+\r
+static int vmacether_set_settings(struct net_device *dev,\r
+ struct ethtool_cmd *cmd)\r
+{\r
+ struct vmac_priv *ap = netdev_priv(dev);\r
+ struct phy_device *phydev = ap->phy_dev;\r
+\r
+ if (!phydev)\r
+ return -ENODEV;\r
+\r
+ return phy_ethtool_sset(phydev, cmd);\r
+}\r
+\r
+static int vmac_ioctl(struct net_device *dev, struct ifreq *rq, int cmd)\r
+{\r
+ struct vmac_priv *ap = netdev_priv(dev);\r
+ struct phy_device *phydev = ap->phy_dev;\r
+\r
+ if (!netif_running(dev))\r
+ return -EINVAL;\r
+\r
+ if (!phydev)\r
+ return -ENODEV;\r
+\r
+ return phy_mii_ioctl(phydev, rq, cmd);\r
+}\r
+\r
+static void vmacether_get_drvinfo(struct net_device *dev,\r
+ struct ethtool_drvinfo *info)\r
+{\r
+ struct vmac_priv *ap = netdev_priv(dev);\r
+\r
+ strlcpy(info->driver, VMAC_NAME, sizeof(info->driver));\r
+ strlcpy(info->version, VMAC_VERSION, sizeof(info->version));\r
+ snprintf(info->bus_info, sizeof(info->bus_info),\r
+ "platform 0x%x", ap->mem_base);\r
+}\r
+\r
+static int update_error_counters(struct net_device *dev, int status)\r
+{\r
+ struct vmac_priv *ap = netdev_priv(dev);\r
+ dev_dbg(&ap->pdev->dev, "rx error counter overrun. status = 0x%x\n",\r
+ status);\r
+\r
+ /* programming error */\r
+ WARN_ON(status & TXCH_MASK);\r
+ WARN_ON(!(status & (MSER_MASK | RXCR_MASK | RXFR_MASK | RXFL_MASK)));\r
+\r
+ if (status & MSER_MASK)\r
+ ap->stats.rx_over_errors += 256; /* ran out of BD */\r
+ if (status & RXCR_MASK)\r
+ ap->stats.rx_crc_errors += 256;\r
+ if (status & RXFR_MASK)\r
+ ap->stats.rx_frame_errors += 256;\r
+ if (status & RXFL_MASK)\r
+ ap->stats.rx_fifo_errors += 256;\r
+\r
+ return 0;\r
+}\r
+\r
+static void update_tx_errors(struct net_device *dev, int status)\r
+{\r
+ struct vmac_priv *ap = netdev_priv(dev);\r
+\r
+ if (status & UFLO)\r
+ ap->stats.tx_fifo_errors++;\r
+\r
+ if (ap->duplex)\r
+ return;\r
+\r
+ /* half duplex flags */\r
+ if (status & LTCL)\r
+ ap->stats.tx_window_errors++;\r
+ if (status & RETRY_CT)\r
+ ap->stats.collisions += (status & RETRY_CT) >> 24;\r
+ if (status & DROP) /* too many retries */\r
+ ap->stats.tx_aborted_errors++;\r
+ if (status & DEFER)\r
+ dev_vdbg(&ap->pdev->dev, "\"defer to traffic\"\n");\r
+ if (status & CARLOSS)\r
+ ap->stats.tx_carrier_errors++;\r
+}\r
+\r
+static int vmac_rx_reclaim_force(struct net_device *dev)\r
+{\r
+ struct vmac_priv *ap = netdev_priv(dev);\r
+ int ct;\r
+\r
+ ct = 0;\r
+\r
+ dev_dbg(&ap->pdev->dev, "%s need to release %d rx sk_buff\n",\r
+ __func__, fifo_used(&ap->rx_ring));\r
+\r
+ while (!fifo_empty(&ap->rx_ring) && ct++ < ap->rx_ring.size) {\r
+ struct vmac_buffer_desc *desc;\r
+ struct sk_buff *skb;\r
+ int desc_idx;\r
+\r
+ desc_idx = ap->rx_ring.tail;\r
+ desc = &ap->rxbd[desc_idx];\r
+ fifo_inc_tail(&ap->rx_ring);\r
+\r
+ if (!ap->rx_skbuff[desc_idx]) {\r
+ dev_err(&ap->pdev->dev, "non-populated rx_skbuff found %d\n",\r
+ desc_idx);\r
+ continue;\r
+ }\r
+\r
+ skb = ap->rx_skbuff[desc_idx];\r
+ ap->rx_skbuff[desc_idx] = NULL;\r
+\r
+ dma_unmap_single(&ap->pdev->dev, desc->data, skb->len,\r
+ DMA_TO_DEVICE);\r
+\r
+ dev_kfree_skb(skb);\r
+ }\r
+\r
+ if (!fifo_empty(&ap->rx_ring)) {\r
+ dev_err(&ap->pdev->dev, "failed to reclaim %d rx sk_buff\n",\r
+ fifo_used(&ap->rx_ring));\r
+ }\r
+\r
+ return 0;\r
+}\r
+\r
+static int vmac_rx_refill(struct net_device *dev)\r
+{\r
+ struct vmac_priv *ap = netdev_priv(dev);\r
+\r
+ WARN_ON(fifo_full(&ap->rx_ring));\r
+\r
+ while (!fifo_full(&ap->rx_ring)) {\r
+ struct vmac_buffer_desc *desc;\r
+ struct sk_buff *skb;\r
+ dma_addr_t p;\r
+ int desc_idx;\r
+\r
+ desc_idx = ap->rx_ring.head;\r
+ desc = &ap->rxbd[desc_idx];\r
+\r
+ /* make sure we read the actual descriptor status */\r
+ rmb();\r
+\r
+ if (ap->rx_skbuff[desc_idx]) {\r
+ /* dropped packet / buffer chaining */\r
+ fifo_inc_head(&ap->rx_ring);\r
+\r
+ /* return to DMA */\r
+ wmb();\r
+ desc->info = OWN_MASK | ap->rx_skb_size;\r
+ continue;\r
+ }\r
+\r
+ skb = netdev_alloc_skb(dev, ap->rx_skb_size + 2);\r
+ if (!skb) {\r
+ dev_info(&ap->pdev->dev, "failed to allocate rx_skb, skb's left %d\n",\r
+ fifo_used(&ap->rx_ring));\r
+ break;\r
+ }\r
+\r
+ /* IP header Alignment (14 byte Ethernet header) */\r
+ skb_reserve(skb, 2);\r
+ WARN_ON(skb->len != 0); /* nothing received yet */\r
+\r
+ ap->rx_skbuff[desc_idx] = skb;\r
+\r
+ p = dma_map_single(&ap->pdev->dev, skb->data, ap->rx_skb_size,\r
+ DMA_FROM_DEVICE);\r
+\r
+ desc->data = p;\r
+\r
+ wmb();\r
+ desc->info = OWN_MASK | ap->rx_skb_size;\r
+\r
+ fifo_inc_head(&ap->rx_ring);\r
+ }\r
+\r
+ /* If rx ring is still empty, set a timer to try allocating\r
+ * again at a later time. */\r
+ if (fifo_empty(&ap->rx_ring) && netif_running(dev)) {\r
+ dev_warn(&ap->pdev->dev, "unable to refill rx ring\n");\r
+ ap->rx_timeout.expires = jiffies + HZ;\r
+ add_timer(&ap->rx_timeout);\r
+ }\r
+\r
+ return 0;\r
+}\r
+\r
+/*\r
+ * timer callback to defer refill rx queue in case we're OOM\r
+ */\r
+static void vmac_refill_rx_timer(unsigned long data)\r
+{\r
+ struct net_device *dev;\r
+ struct vmac_priv *ap;\r
+\r
+ dev = (struct net_device *)data;\r
+ ap = netdev_priv(dev);\r
+\r
+ spin_lock(&ap->rx_lock);\r
+ vmac_rx_refill(dev);\r
+ spin_unlock(&ap->rx_lock);\r
+}\r
+\r
+/* merge buffer chaining */\r
+struct sk_buff *vmac_merge_rx_buffers(struct net_device *dev,\r
+ struct vmac_buffer_desc *after,\r
+ int pkt_len) /* data */\r
+{\r
+ struct vmac_priv *ap = netdev_priv(dev);\r
+ struct sk_buff *merge_skb, *cur_skb;\r
+ struct dma_fifo *rx_ring;\r
+ struct vmac_buffer_desc *desc;\r
+\r
+ rx_ring = &ap->rx_ring;\r
+ desc = &ap->rxbd[rx_ring->tail];\r
+\r
+ WARN_ON(desc == after);\r
+\r
+ /* strip FCS */\r
+ pkt_len -= 4;\r
+\r
+ /* IP header Alignment (14 byte Ethernet header) */\r
+ merge_skb = netdev_alloc_skb(dev, pkt_len + 2);\r
+ if (!merge_skb) {\r
+ dev_err(&ap->pdev->dev, "failed to allocate merged rx_skb, rx skb's left %d\n",\r
+ fifo_used(rx_ring));\r
+\r
+ return NULL;\r
+ }\r
+\r
+ skb_reserve(merge_skb, 2);\r
+\r
+ while (desc != after && pkt_len) {\r
+ struct vmac_buffer_desc *desc;\r
+ int buf_len, valid;\r
+\r
+ /* desc needs wrapping */\r
+ desc = &ap->rxbd[rx_ring->tail];\r
+ cur_skb = ap->rx_skbuff[rx_ring->tail];\r
+ WARN_ON(!cur_skb);\r
+\r
+ dma_unmap_single(&ap->pdev->dev, desc->data, ap->rx_skb_size,\r
+ DMA_FROM_DEVICE);\r
+\r
+ /* do not copy FCS */\r
+ buf_len = desc->info & LEN_MASK;\r
+ valid = min(pkt_len, buf_len);\r
+ pkt_len -= valid;\r
+\r
+ memcpy(skb_put(merge_skb, valid), cur_skb->data, valid);\r
+\r
+ fifo_inc_tail(rx_ring);\r
+ }\r
+\r
+ /* merging_pressure++ */\r
+\r
+ if (unlikely(pkt_len != 0))\r
+ dev_err(&ap->pdev->dev, "buffer chaining bytes missing %d\n",\r
+ pkt_len);\r
+\r
+ WARN_ON(desc != after);\r
+\r
+ return merge_skb;\r
+}\r
+\r
+int vmac_rx_receive(struct net_device *dev, int budget)\r
+{\r
+ struct vmac_priv *ap = netdev_priv(dev);\r
+ struct vmac_buffer_desc *first;\r
+ int processed, pkt_len, pkt_err;\r
+ struct dma_fifo lookahead;\r
+\r
+ processed = 0;\r
+\r
+ first = NULL;\r
+ pkt_err = pkt_len = 0;\r
+\r
+ /* look ahead, till packet complete */\r
+ lookahead = ap->rx_ring;\r
+\r
+ do {\r
+ struct vmac_buffer_desc *desc; /* cur_ */\r
+ int desc_idx; /* cur_ */\r
+ struct sk_buff *skb; /* pkt_ */\r
+\r
+ desc_idx = lookahead.tail;\r
+ desc = &ap->rxbd[desc_idx];\r
+\r
+ /* make sure we read the actual descriptor status */\r
+ rmb();\r
+\r
+ /* break if dma ownership belongs to hw */\r
+ if (desc->info & OWN_MASK) {\r
+ ap->mac_rxring_head = vmac_readl(ap, MAC_RXRING_HEAD);\r
+ break;\r
+ }\r
+\r
+ if (desc->info & FRST_MASK) {\r
+ pkt_len = 0;\r
+ pkt_err = 0;\r
+\r
+ /* don't free current */\r
+ ap->rx_ring.tail = lookahead.tail;\r
+ first = desc;\r
+ }\r
+\r
+ fifo_inc_tail(&lookahead);\r
+\r
+ /* check bd */\r
+\r
+ pkt_len += desc->info & LEN_MASK;\r
+ pkt_err |= (desc->info & BUFF);\r
+\r
+ if (!(desc->info & LAST_MASK))\r
+ continue;\r
+\r
+ /* received complete packet */\r
+\r
+ if (unlikely(pkt_err || !first)) {\r
+ /* recycle buffers */\r
+ ap->rx_ring.tail = lookahead.tail;\r
+ continue;\r
+ }\r
+\r
+ WARN_ON(!(first->info & FRST_MASK) ||\r
+ !(desc->info & LAST_MASK));\r
+ WARN_ON(pkt_err);\r
+\r
+ /* -- valid packet -- */\r
+\r
+ if (first != desc) {\r
+ skb = vmac_merge_rx_buffers(dev, desc, pkt_len);\r
+\r
+ if (!skb) {\r
+ /* kill packet */\r
+ ap->rx_ring.tail = lookahead.tail;\r
+ ap->rx_merge_error++;\r
+ continue;\r
+ }\r
+ } else {\r
+ dma_unmap_single(&ap->pdev->dev, desc->data,\r
+ ap->rx_skb_size, DMA_FROM_DEVICE);\r
+\r
+ skb = ap->rx_skbuff[desc_idx];\r
+ ap->rx_skbuff[desc_idx] = NULL;\r
+ /* desc->data != skb->data => desc->data DMA mapped */\r
+\r
+ /* strip FCS */\r
+ skb_put(skb, pkt_len - 4);\r
+ }\r
+\r
+ /* free buffers */\r
+ ap->rx_ring.tail = lookahead.tail;\r
+\r
+ WARN_ON(skb->len != pkt_len - 4);\r
+ processed++;\r
+ skb->dev = dev;\r
+ skb->protocol = eth_type_trans(skb, dev);\r
+ ap->stats.rx_packets++;\r
+ ap->stats.rx_bytes += skb->len;\r
+ dev->last_rx = jiffies;\r
+ netif_rx(skb);\r
+\r
+ } while (!fifo_empty(&lookahead) && (processed < budget));\r
+\r
+ dev_vdbg(&ap->pdev->dev, "processed pkt %d, remaining rx buff %d\n",\r
+ processed,\r
+ fifo_used(&ap->rx_ring));\r
+\r
+ if (processed || fifo_empty(&ap->rx_ring))\r
+ vmac_rx_refill(dev);\r
+\r
+ return processed;\r
+}\r
+\r
+static void vmac_toggle_irqmask(struct net_device *dev, int enable, int mask)\r
+{\r
+ struct vmac_priv *ap = netdev_priv(dev);\r
+ unsigned long tmp;\r
+\r
+ tmp = vmac_readl(ap, ENABLE);\r
+ if (enable)\r
+ tmp |= mask;\r
+ else\r
+ tmp &= ~mask;\r
+ vmac_writel(ap, tmp, ENABLE);\r
+}\r
+\r
+static void vmac_toggle_txint(struct net_device *dev, int enable)\r
+{\r
+ struct vmac_priv *ap = netdev_priv(dev);\r
+ unsigned long flags;\r
+\r
+ spin_lock_irqsave(&ap->lock, flags);\r
+ vmac_toggle_irqmask(dev, enable, TXINT_MASK);\r
+ spin_unlock_irqrestore(&ap->lock, flags);\r
+}\r
+\r
+static void vmac_toggle_rxint(struct net_device *dev, int enable)\r
+{\r
+ vmac_toggle_irqmask(dev, enable, RXINT_MASK);\r
+}\r
+\r
+static int vmac_poll(struct napi_struct *napi, int budget)\r
+{\r
+ struct vmac_priv *ap;\r
+ struct net_device *dev;\r
+ int rx_work_done;\r
+ unsigned long flags;\r
+\r
+ ap = container_of(napi, struct vmac_priv, napi);\r
+ dev = ap->dev;\r
+\r
+ /* ack interrupt */\r
+ vmac_writel(ap, RXINT_MASK, STAT);\r
+\r
+ spin_lock(&ap->rx_lock);\r
+ rx_work_done = vmac_rx_receive(dev, budget);\r
+ spin_unlock(&ap->rx_lock);\r
+\r
+#ifdef VERBOSE_DEBUG\r
+ if (printk_ratelimit()) {\r
+ dev_vdbg(&ap->pdev->dev, "poll budget %d receive rx_work_done %d\n",\r
+ budget,\r
+ rx_work_done);\r
+ }\r
+#endif\r
+\r
+ if (rx_work_done >= budget) {\r
+ /* rx queue is not yet empty/clean */\r
+ return rx_work_done;\r
+ }\r
+\r
+ /* no more packet in rx/tx queue, remove device from poll\r
+ * queue */\r
+ spin_lock_irqsave(&ap->lock, flags);\r
+ napi_complete(napi);\r
+ vmac_toggle_rxint(dev, 1);\r
+ spin_unlock_irqrestore(&ap->lock, flags);\r
+\r
+ return rx_work_done;\r
+}\r
+\r
+static int vmac_tx_reclaim(struct net_device *dev, int force);\r
+\r
+static irqreturn_t vmac_intr(int irq, void *dev_instance)\r
+{\r
+ struct net_device *dev = dev_instance;\r
+ struct vmac_priv *ap = netdev_priv(dev);\r
+ unsigned int status;\r
+\r
+ spin_lock(&ap->lock);\r
+\r
+ status = vmac_readl(ap, STAT);\r
+ vmac_writel(ap, status, STAT);\r
+\r
+#ifdef DEBUG\r
+ if (unlikely(ap->shutdown))\r
+ dev_err(&ap->pdev->dev, "ISR during close\n");\r
+\r
+ if (unlikely(!status & (RXINT_MASK|MDIO_MASK|ERR_MASK)))\r
+ dev_err(&ap->pdev->dev, "No source of IRQ found\n");\r
+#endif\r
+\r
+ if ((status & RXINT_MASK) &&\r
+ (ap->mac_rxring_head !=\r
+ vmac_readl(ap, MAC_RXRING_HEAD))) {\r
+ vmac_toggle_rxint(dev, 0);\r
+ napi_schedule(&ap->napi);\r
+ }\r
+\r
+ if (unlikely(netif_queue_stopped(dev) && (status & TXINT_MASK)))\r
+ vmac_tx_reclaim(dev, 0);\r
+\r
+ if (status & MDIO_MASK)\r
+ complete(&ap->mdio_complete);\r
+\r
+ if (unlikely(status & ERR_MASK))\r
+ update_error_counters(dev, status);\r
+\r
+ spin_unlock(&ap->lock);\r
+\r
+ return IRQ_HANDLED;\r
+}\r
+\r
+static int vmac_tx_reclaim(struct net_device *dev, int force)\r
+{\r
+ struct vmac_priv *ap = netdev_priv(dev);\r
+ int released = 0;\r
+\r
+ /* buffer chaining not used, see vmac_start_xmit */\r
+\r
+ while (!fifo_empty(&ap->tx_ring)) {\r
+ struct vmac_buffer_desc *desc;\r
+ struct sk_buff *skb;\r
+ int desc_idx;\r
+\r
+ desc_idx = ap->tx_ring.tail;\r
+ desc = &ap->txbd[desc_idx];\r
+\r
+ /* ensure other field of the descriptor were not read\r
+ * before we checked ownership */\r
+ rmb();\r
+\r
+ if ((desc->info & OWN_MASK) && !force)\r
+ break;\r
+\r
+ if (desc->info & ERR_MSK_TX) {\r
+ update_tx_errors(dev, desc->info);\r
+ /* recycle packet, let upper level deal with it */\r
+ }\r
+\r
+ skb = ap->tx_skbuff[desc_idx];\r
+ ap->tx_skbuff[desc_idx] = NULL;\r
+ WARN_ON(!skb);\r
+\r
+ dma_unmap_single(&ap->pdev->dev, desc->data, skb->len,\r
+ DMA_TO_DEVICE);\r
+\r
+ dev_kfree_skb_any(skb);\r
+\r
+ released++;\r
+ fifo_inc_tail(&ap->tx_ring);\r
+ }\r
+\r
+ if (netif_queue_stopped(dev) && released) {\r
+ netif_wake_queue(dev);\r
+ vmac_toggle_txint(dev, 0);\r
+ }\r
+\r
+ if (unlikely(force && !fifo_empty(&ap->tx_ring))) {\r
+ dev_err(&ap->pdev->dev, "failed to reclaim %d tx sk_buff\n",\r
+ fifo_used(&ap->tx_ring));\r
+ }\r
+\r
+ return released;\r
+}\r
+\r
+int vmac_start_xmit(struct sk_buff *skb, struct net_device *dev)\r
+{\r
+ struct vmac_priv *ap = netdev_priv(dev);\r
+ struct vmac_buffer_desc *desc;\r
+ unsigned int tmp;\r
+\r
+ /* running under xmit lock */\r
+\r
+ /* no scatter/gatter see features below */\r
+ WARN_ON(skb_shinfo(skb)->nr_frags != 0);\r
+ WARN_ON(skb->len > MAX_TX_BUFFER_LEN);\r
+\r
+ if (unlikely(fifo_full(&ap->tx_ring))) {\r
+ netif_stop_queue(dev);\r
+ vmac_toggle_txint(dev, 1);\r
+ dev_err(&ap->pdev->dev, "xmit called with no tx desc available\n");\r
+ return NETDEV_TX_BUSY;\r
+ }\r
+\r
+ if (unlikely(skb->len < ETH_ZLEN)) {\r
+ struct sk_buff *short_skb;\r
+ short_skb = netdev_alloc_skb(dev, ETH_ZLEN);\r
+ if (!short_skb)\r
+ return NETDEV_TX_LOCKED;\r
+\r
+ memset(short_skb->data, 0, ETH_ZLEN);\r
+ memcpy(skb_put(short_skb, ETH_ZLEN), skb->data, skb->len);\r
+ dev_kfree_skb(skb);\r
+ skb = short_skb;\r
+ }\r
+\r
+ /* fill descriptor */\r
+ ap->tx_skbuff[ap->tx_ring.head] = skb;\r
+\r
+ desc = &ap->txbd[ap->tx_ring.head];\r
+ desc->data = dma_map_single(&ap->pdev->dev, skb->data, skb->len,\r
+ DMA_TO_DEVICE);\r
+\r
+ /* dma might already be polling */\r
+ wmb();\r
+ desc->info = OWN_MASK | FRST_MASK | LAST_MASK | skb->len;\r
+ wmb();\r
+\r
+ /* kick tx dma */\r
+ tmp = vmac_readl(ap, STAT);\r
+ vmac_writel(ap, tmp | TXPL_MASK, STAT);\r
+\r
+ ap->stats.tx_packets++;\r
+ ap->stats.tx_bytes += skb->len;\r
+ dev->trans_start = jiffies;\r
+ fifo_inc_head(&ap->tx_ring);\r
+\r
+ /* vmac_tx_reclaim independent of vmac_tx_timeout */\r
+ if (fifo_used(&ap->tx_ring) > 8)\r
+ vmac_tx_reclaim(dev, 0);\r
+\r
+ /* stop queue if no more desc available */\r
+ if (fifo_full(&ap->tx_ring)) {\r
+ netif_stop_queue(dev);\r
+ vmac_toggle_txint(dev, 1);\r
+ }\r
+\r
+ return NETDEV_TX_OK;\r
+}\r
+\r
+static int alloc_buffers(struct net_device *dev)\r
+{\r
+ struct vmac_priv *ap = netdev_priv(dev);\r
+ int err = -ENOMEM;\r
+ int size;\r
+\r
+ fifo_init(&ap->rx_ring, RX_BDT_LEN);\r
+ fifo_init(&ap->tx_ring, TX_BDT_LEN);\r
+\r
+ /* initialize skb list */\r
+ memset(ap->rx_skbuff, 0, sizeof(ap->rx_skbuff));\r
+ memset(ap->tx_skbuff, 0, sizeof(ap->tx_skbuff));\r
+\r
+ /* allocate DMA received descriptors */\r
+ size = sizeof(*ap->rxbd) * ap->rx_ring.size;\r
+ ap->rxbd = dma_alloc_coherent(&ap->pdev->dev, size,\r
+ &ap->rxbd_dma,\r
+ GFP_KERNEL);\r
+ if (ap->rxbd == NULL)\r
+ goto err_out;\r
+\r
+ /* allocate DMA transmit descriptors */\r
+ size = sizeof(*ap->txbd) * ap->tx_ring.size;\r
+ ap->txbd = dma_alloc_coherent(&ap->pdev->dev, size,\r
+ &ap->txbd_dma,\r
+ GFP_KERNEL);\r
+ if (ap->txbd == NULL)\r
+ goto err_free_rxbd;\r
+\r
+ /* ensure 8-byte aligned */\r
+ WARN_ON(((int)ap->txbd & 0x7) || ((int)ap->rxbd & 0x7));\r
+\r
+ memset(ap->txbd, 0, sizeof(*ap->txbd) * ap->tx_ring.size);\r
+ memset(ap->rxbd, 0, sizeof(*ap->rxbd) * ap->rx_ring.size);\r
+\r
+ /* allocate rx skb */\r
+ err = vmac_rx_refill(dev);\r
+ if (err)\r
+ goto err_free_txbd;\r
+\r
+ return 0;\r
+\r
+err_free_txbd:\r
+ dma_free_coherent(&ap->pdev->dev, sizeof(*ap->txbd) * ap->tx_ring.size,\r
+ ap->txbd, ap->txbd_dma);\r
+err_free_rxbd:\r
+ dma_free_coherent(&ap->pdev->dev, sizeof(*ap->rxbd) * ap->rx_ring.size,\r
+ ap->rxbd, ap->rxbd_dma);\r
+err_out:\r
+ return err;\r
+}\r
+\r
+static int free_buffers(struct net_device *dev)\r
+{\r
+ struct vmac_priv *ap = netdev_priv(dev);\r
+\r
+ /* free skbuff */\r
+ vmac_tx_reclaim(dev, 1);\r
+ vmac_rx_reclaim_force(dev);\r
+\r
+ /* free DMA ring */\r
+ dma_free_coherent(&ap->pdev->dev, sizeof(ap->txbd) * ap->tx_ring.size,\r
+ ap->txbd, ap->txbd_dma);\r
+ dma_free_coherent(&ap->pdev->dev, sizeof(ap->rxbd) * ap->rx_ring.size,\r
+ ap->rxbd, ap->rxbd_dma);\r
+\r
+ return 0;\r
+}\r
+\r
+static int vmac_hw_init(struct net_device *dev)\r
+{\r
+ struct vmac_priv *priv = netdev_priv(dev);\r
+\r
+ /* clear IRQ mask */\r
+ vmac_writel(priv, 0, ENABLE);\r
+\r
+ /* clear pending IRQ */\r
+ vmac_writel(priv, 0xffffffff, STAT);\r
+\r
+ /* Initialize logical address filter */\r
+ vmac_writel(priv, 0x0, LAFL);\r
+ vmac_writel(priv, 0x0, LAFH);\r
+\r
+ return 0;\r
+}\r
+\r
+#ifdef DEBUG\r
+static int vmac_register_print(struct net_device *dev)\r
+{\r
+ struct vmac_priv *ap = netdev_priv(dev);\r
+\r
+ printk("func::%s vmac register %s value = 0x%x\n", __func__, "ID", vmac_readl(ap, ID));\r
+ printk("func::%s vmac register %s value = 0x%x\n", __func__, "STAT", vmac_readl(ap, STAT));\r
+ printk("func::%s vmac register %s value = 0x%x\n", __func__, "ENABLE", vmac_readl(ap, ENABLE));\r
+ printk("func::%s vmac register %s value = 0x%x\n", __func__, "CONTROL", vmac_readl(ap, CONTROL));\r
+ printk("func::%s vmac register %s value = 0x%x\n", __func__, "ADDRL", vmac_readl(ap, ADDRL));\r
+ printk("func::%s vmac register %s value = 0x%x\n", __func__, "ADDRH", vmac_readl(ap, ADDRH));\r
+ \r
+ return 0;\r
+}\r
+#endif\r
+\r
+int vmac_open(struct net_device *dev)\r
+{\r
+ struct vmac_priv *ap = netdev_priv(dev);\r
+ struct phy_device *phydev;\r
+ unsigned int temp;\r
+ int err = 0;\r
+\r
+ if (ap == NULL)\r
+ return -ENODEV;\r
+\r
+ ap->shutdown = 0;\r
+ \r
+ vmac_hw_init(dev);\r
+\r
+ /* mac address changed? */\r
+ write_mac_reg(dev, dev->dev_addr);\r
+\r
+ err = alloc_buffers(dev);\r
+ if (err)\r
+ goto err_out;\r
+\r
+ err = request_irq(dev->irq, &vmac_intr, 0, dev->name, dev);\r
+ if (err) {\r
+ dev_err(&ap->pdev->dev, "Unable to request IRQ %d (error %d)\n",\r
+ dev->irq, err);\r
+ goto err_free_buffers;\r
+ }\r
+\r
+ /* install DMA ring pointers */\r
+ vmac_writel(ap, ap->rxbd_dma, RXRINGPTR);\r
+ vmac_writel(ap, ap->txbd_dma, TXRINGPTR);\r
+\r
+ /* set poll rate to 1 ms */\r
+ vmac_writel(ap, POLLRATE_TIME, POLLRATE);\r
+\r
+ /* make sure we enable napi before rx interrupt */\r
+ napi_enable(&ap->napi);\r
+\r
+ /* IRQ mask */\r
+ temp = RXINT_MASK | ERR_MASK | TXCH_MASK | MDIO_MASK;\r
+ vmac_writel(ap, temp, ENABLE);\r
+\r
+ /* Set control */\r
+ temp = (RX_BDT_LEN << 24) | (TX_BDT_LEN << 16) | TXRN_MASK | RXRN_MASK;\r
+ vmac_writel(ap, temp, CONTROL);\r
+\r
+ /* enable, after all other bits are set */\r
+ vmac_writel(ap, temp | EN_MASK, CONTROL);\r
+ \r
+ netif_start_queue(dev);\r
+ netif_carrier_off(dev);\r
+\r
+#ifdef DEBUG\r
+ vmac_register_print(dev);\r
+#endif\r
+\r
+ /* register the PHY board fixup, if needed */\r
+ err = vmac_mii_init(ap);\r
+ if (err)\r
+ goto err_free_irq;\r
+\r
+ /* schedule a link state check */\r
+ phy_start(ap->phy_dev);\r
+\r
+ phydev = ap->phy_dev;\r
+ dev_info(&ap->pdev->dev, "PHY driver [%s] (mii_bus:phy_addr=%s, irq=%d)\n",\r
+ phydev->drv->name, dev_name(&phydev->dev), phydev->irq);\r
+\r
+ return 0;\r
+\r
+err_free_irq:\r
+ free_irq(dev->irq, dev);\r
+err_free_buffers:\r
+ free_buffers(dev);\r
+err_out:\r
+ return err;\r
+}\r
+\r
+int vmac_close(struct net_device *dev)\r
+{\r
+ struct vmac_priv *ap = netdev_priv(dev);\r
+ unsigned int temp;\r
+\r
+ netif_stop_queue(dev);\r
+ napi_disable(&ap->napi);\r
+\r
+ /* stop running transfers */\r
+ temp = vmac_readl(ap, CONTROL);\r
+ temp &= ~(TXRN_MASK | RXRN_MASK);\r
+ vmac_writel(ap, temp, CONTROL);\r
+\r
+ del_timer_sync(&ap->rx_timeout);\r
+\r
+ /* disable phy */\r
+ phy_stop(ap->phy_dev);\r
+ vmac_mii_exit(dev);\r
+ netif_carrier_off(dev);\r
+\r
+ /* disable interrupts */\r
+ vmac_writel(ap, 0, ENABLE);\r
+ free_irq(dev->irq, dev);\r
+\r
+ /* turn off vmac */\r
+ vmac_writel(ap, 0, CONTROL);\r
+ /* vmac_reset_hw(vmac) */\r
+\r
+ ap->shutdown = 1;\r
+ wmb();\r
+\r
+ free_buffers(dev);\r
+ \r
+ return 0;\r
+}\r
+\r
+void vmac_update_stats(struct vmac_priv *ap)\r
+{\r
+ struct net_device_stats *_stats = &ap->stats;\r
+ unsigned long miss, rxerr;\r
+ unsigned long rxfram, rxcrc, rxoflow;\r
+\r
+ /* compare with /proc/net/dev,\r
+ * see net/core/dev.c:dev_seq_printf_stats */\r
+\r
+ /* rx stats */\r
+ rxerr = vmac_readl(ap, RXERR);\r
+ miss = vmac_readl(ap, MISS);\r
+\r
+ rxcrc = (rxerr & RXERR_CRC);\r
+ rxfram = (rxerr & RXERR_FRM) >> 8;\r
+ rxoflow = (rxerr & RXERR_OFLO) >> 16;\r
+\r
+ _stats->rx_length_errors = 0;\r
+ _stats->rx_over_errors += miss;\r
+ _stats->rx_crc_errors += rxcrc;\r
+ _stats->rx_frame_errors += rxfram;\r
+ _stats->rx_fifo_errors += rxoflow;\r
+ _stats->rx_missed_errors = 0;\r
+\r
+ /* TODO check rx_dropped/rx_errors/tx_dropped/tx_errors have not\r
+ * been updated elsewhere */\r
+ _stats->rx_dropped = _stats->rx_over_errors +\r
+ _stats->rx_fifo_errors +\r
+ ap->rx_merge_error;\r
+\r
+ _stats->rx_errors = _stats->rx_length_errors + _stats->rx_crc_errors +\r
+ _stats->rx_frame_errors +\r
+ _stats->rx_missed_errors +\r
+ _stats->rx_dropped;\r
+\r
+ /* tx stats */\r
+ _stats->tx_dropped = 0; /* otherwise queue stopped */\r
+\r
+ _stats->tx_errors = _stats->tx_aborted_errors +\r
+ _stats->tx_carrier_errors +\r
+ _stats->tx_fifo_errors +\r
+ _stats->tx_heartbeat_errors +\r
+ _stats->tx_window_errors +\r
+ _stats->tx_dropped +\r
+ ap->tx_timeout_error;\r
+}\r
+\r
+struct net_device_stats *vmac_stats(struct net_device *dev)\r
+{\r
+ struct vmac_priv *ap = netdev_priv(dev);\r
+ unsigned long flags;\r
+\r
+ spin_lock_irqsave(&ap->lock, flags);\r
+ vmac_update_stats(ap);\r
+ spin_unlock_irqrestore(&ap->lock, flags);\r
+\r
+ return &ap->stats;\r
+}\r
+\r
+void vmac_tx_timeout(struct net_device *dev)\r
+{\r
+ struct vmac_priv *ap = netdev_priv(dev);\r
+ unsigned int status;\r
+ unsigned long flags;\r
+\r
+ spin_lock_irqsave(&ap->lock, flags);\r
+\r
+ /* queue did not progress for timeo jiffies */\r
+ WARN_ON(!netif_queue_stopped(dev));\r
+ WARN_ON(!fifo_full(&ap->tx_ring));\r
+\r
+ /* TX IRQ lost? */\r
+ status = vmac_readl(ap, STAT);\r
+ if (status & TXINT_MASK) {\r
+ dev_err(&ap->pdev->dev, "lost tx interrupt, IRQ mask %x\n",\r
+ vmac_readl(ap, ENABLE));\r
+ vmac_writel(ap, TXINT_MASK, STAT);\r
+ }\r
+\r
+ /* TODO RX/MDIO/ERR as well? */\r
+\r
+ vmac_tx_reclaim(dev, 0);\r
+ if (fifo_full(&ap->tx_ring))\r
+ dev_err(&ap->pdev->dev, "DMA state machine not active\n");\r
+\r
+ /* We can accept TX packets again */\r
+ ap->tx_timeout_error++;\r
+ dev->trans_start = jiffies;\r
+ netif_wake_queue(dev);\r
+\r
+ spin_unlock_irqrestore(&ap->lock, flags);\r
+}\r
+\r
+static void create_multicast_filter(struct net_device *dev,\r
+ unsigned long *bitmask)\r
+{\r
+#if 1\r
+ printk("-----------------func %s-------------------\n", __func__);\r
+#else\r
+ struct netdev_hw_addr *ha;\r
+ unsigned long crc;\r
+ char *addrs;\r
+\r
+ WARN_ON(netdev_mc_count(dev) == 0);\r
+ WARN_ON(dev->flags & IFF_ALLMULTI);\r
+\r
+ bitmask[0] = bitmask[1] = 0;\r
+\r
+ netdev_for_each_mc_addr(ha, dev) {\r
+ addrs = ha->addr;\r
+\r
+ /* skip non-multicast addresses */\r
+ if (!(*addrs & 1))\r
+ continue;\r
+\r
+ crc = ether_crc_le(ETH_ALEN, addrs);\r
+ set_bit(crc >> 26, bitmask);\r
+ }\r
+#endif\r
+}\r
+static void vmac_set_multicast_list(struct net_device *dev)\r
+{\r
+#if 1\r
+ printk("-----------------func %s-------------------\n", __func__);\r
+#else\r
+ struct vmac_priv *ap = netdev_priv(dev);\r
+ unsigned long flags, bitmask[2];\r
+ int promisc, reg;\r
+\r
+ spin_lock_irqsave(&ap->lock, flags);\r
+\r
+ promisc = !!(dev->flags & IFF_PROMISC);\r
+ reg = vmac_readl(ap, ENABLE);\r
+ if (promisc != !!(reg & PROM_MASK)) {\r
+ reg ^= PROM_MASK;\r
+ vmac_writel(ap, reg, ENABLE);\r
+ }\r
+\r
+ if (dev->flags & IFF_ALLMULTI)\r
+ memset(bitmask, 1, sizeof(bitmask));\r
+ else if (netdev_mc_count(dev) == 0)\r
+ memset(bitmask, 0, sizeof(bitmask));\r
+ else\r
+ create_multicast_filter(dev, bitmask);\r
+\r
+ vmac_writel(ap, bitmask[0], LAFL);\r
+ vmac_writel(ap, bitmask[1], LAFH);\r
+\r
+ spin_unlock_irqrestore(&ap->lock, flags);\r
+#endif\r
+}\r
+\r
+static struct ethtool_ops vmac_ethtool_ops = {\r
+ .get_settings = vmacether_get_settings,\r
+ .set_settings = vmacether_set_settings,\r
+ .get_drvinfo = vmacether_get_drvinfo,\r
+ .get_link = ethtool_op_get_link,\r
+};\r
+\r
+static const struct net_device_ops vmac_netdev_ops = {\r
+ .ndo_open = vmac_open,\r
+ .ndo_stop = vmac_close,\r
+ .ndo_get_stats = vmac_stats,\r
+ .ndo_start_xmit = vmac_start_xmit,\r
+ .ndo_do_ioctl = vmac_ioctl,\r
+ .ndo_set_mac_address = eth_mac_addr,\r
+ .ndo_tx_timeout = vmac_tx_timeout,\r
+ //.ndo_set_multicast_list = vmac_set_multicast_list,\r
+ .ndo_validate_addr = eth_validate_addr,\r
+ .ndo_change_mtu = eth_change_mtu,\r
+};\r
+\r
+static int __devinit vmac_probe(struct platform_device *pdev)\r
+{\r
+ struct net_device *dev;\r
+ struct vmac_priv *ap;\r
+ struct resource *res;\r
+ unsigned int mem_base, mem_size, irq;\r
+ int err;\r
+ struct clk *sys_clk;\r
+ struct rk29_vmac_platform_data *pdata = pdev->dev.platform_data;\r
+\r
+ dev = alloc_etherdev(sizeof(*ap));\r
+ if (!dev) {\r
+ dev_err(&pdev->dev, "etherdev alloc failed, aborting.\n");\r
+ return -ENOMEM;\r
+ }\r
+\r
+ ap = netdev_priv(dev);\r
+\r
+ err = -ENODEV;\r
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);\r
+ if (!res) {\r
+ dev_err(&pdev->dev, "no mmio resource defined\n");\r
+ goto err_out;\r
+ }\r
+ mem_base = res->start;\r
+ mem_size = resource_size(res);\r
+ irq = platform_get_irq(pdev, 0);\r
+\r
+ err = -EBUSY;\r
+ if (!request_mem_region(mem_base, mem_size, VMAC_NAME)) {\r
+ dev_err(&pdev->dev, "no memory region available\n");\r
+ goto err_out;\r
+ }\r
+\r
+ err = -ENOMEM;\r
+ ap->regs = ioremap(mem_base, mem_size);\r
+ if (!ap->regs) {\r
+ dev_err(&pdev->dev, "failed to map registers, aborting.\n");\r
+ goto err_out_release_mem;\r
+ }\r
+\r
+ /* no checksum support, hence no scatter/gather */\r
+ dev->features |= NETIF_F_HIGHDMA;\r
+\r
+ spin_lock_init(&ap->lock);\r
+\r
+ SET_NETDEV_DEV(dev, &pdev->dev);\r
+ ap->dev = dev;\r
+ ap->pdev = pdev;\r
+\r
+ /* init rx timeout (used for oom) */\r
+ init_timer(&ap->rx_timeout);\r
+ ap->rx_timeout.function = vmac_refill_rx_timer;\r
+ ap->rx_timeout.data = (unsigned long)dev;\r
+\r
+ netif_napi_add(dev, &ap->napi, vmac_poll, 2);\r
+ dev->netdev_ops = &vmac_netdev_ops;\r
+ dev->ethtool_ops = &vmac_ethtool_ops;\r
+ dev->irq = irq;\r
+\r
+ dev->flags |= IFF_MULTICAST;////////////////////\r
+\r
+ dev->base_addr = (unsigned long)ap->regs;\r
+ ap->mem_base = mem_base;\r
+\r
+ /* prevent buffer chaining, favor speed over space */\r
+ ap->rx_skb_size = ETH_FRAME_LEN + VMAC_BUFFER_PAD;\r
+\r
+ /* private struct functional */\r
+\r
+ /* mac address intialize, set vmac_open */\r
+ read_mac_reg(dev, dev->dev_addr);\r
+\r
+ if (!is_valid_ether_addr(dev->dev_addr))\r
+ random_ether_addr(dev->dev_addr);\r
+\r
+ err = register_netdev(dev);\r
+ if (err) {\r
+ dev_err(&pdev->dev, "Cannot register net device, aborting.\n");\r
+ goto err_out_iounmap;\r
+ }\r
+\r
+ dev_info(&pdev->dev, "ARC VMAC at 0x%08x irq %d %pM\n", mem_base,\r
+ dev->irq, dev->dev_addr);\r
+ platform_set_drvdata(pdev, dev);\r
+\r
+ //config rk29 vmac as rmii, 100MHz \r
+ if (pdata && pdata->vmac_register_set)\r
+ pdata->vmac_register_set();\r
+ \r
+ //set rmii ref clock 50MHz\r
+ sys_clk = clk_get(NULL, "mac_ref_div");////////\r
+ clk_set_rate(sys_clk,50000000);\r
+\r
+ sys_clk = clk_get(NULL, "mac_ref");////////\r
+ clk_set_rate(sys_clk,50000000);\r
+\r
+ //power on\r
+ if (pdata && pdata->rmii_io_init)\r
+ pdata->rmii_io_init();\r
+\r
+ return 0;\r
+\r
+err_out_iounmap:\r
+ iounmap(ap->regs);\r
+err_out_release_mem:\r
+ release_mem_region(mem_base, mem_size);\r
+err_out:\r
+ free_netdev(dev);\r
+ return err;\r
+}\r
+\r
+static int __devexit vmac_remove(struct platform_device *pdev)\r
+{\r
+ struct net_device *dev;\r
+ struct vmac_priv *ap;\r
+ struct resource *res;\r
+\r
+ dev = platform_get_drvdata(pdev);\r
+ if (!dev) {\r
+ dev_err(&pdev->dev, "%s no valid dev found\n", __func__);\r
+ return 0;\r
+ }\r
+\r
+ ap = netdev_priv(dev);\r
+\r
+ /* MAC */\r
+ unregister_netdev(dev);\r
+ iounmap(ap->regs);\r
+\r
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);\r
+ release_mem_region(res->start, resource_size(res));\r
+\r
+ platform_set_drvdata(pdev, NULL);\r
+ free_netdev(dev);\r
+ return 0;\r
+}\r
+\r
+static struct platform_driver rk29_vmac_driver = {\r
+ .probe = vmac_probe,\r
+ .remove = __devexit_p(vmac_remove),\r
+ .driver = {\r
+ .name = "rk29 vmac",\r
+ },\r
+};\r
+\r
+static int __init vmac_init(void)\r
+{\r
+ return platform_driver_register(&rk29_vmac_driver);\r
+}\r
+\r
+static void __exit vmac_exit(void)\r
+{\r
+ platform_driver_unregister(&rk29_vmac_driver);\r
+}\r
+\r
+module_init(vmac_init);\r
+module_exit(vmac_exit);\r
+\r
+MODULE_LICENSE("GPL");\r
+MODULE_DESCRIPTION("RK29 VMAC Ethernet driver");\r
+MODULE_AUTHOR("amit.bhor@celunite.com, sameer.dhavale@celunite.com, andreas.fenkart@streamunlimited.com");\r