Author | Tokens | Token Proportion | Commits | Commit Proportion |
---|---|---|---|---|
Stefan Roese | 3821 | 74.19% | 1 | 2.94% |
Conley Lee | 992 | 19.26% | 2 | 5.88% |
Michael Weiser | 76 | 1.48% | 2 | 5.88% |
Hans de Goede | 75 | 1.46% | 2 | 5.88% |
Marc Zyngier | 67 | 1.30% | 2 | 5.88% |
Maxime Ripard | 27 | 0.52% | 3 | 8.82% |
Philippe Reynes | 20 | 0.39% | 2 | 5.88% |
Christophe Jaillet | 18 | 0.35% | 1 | 2.94% |
Florian Westphal | 9 | 0.17% | 1 | 2.94% |
Jakub Kiciński | 8 | 0.16% | 3 | 8.82% |
Julia Lawall | 5 | 0.10% | 1 | 2.94% |
Andrew Lunn | 5 | 0.10% | 1 | 2.94% |
Michael Walle | 4 | 0.08% | 1 | 2.94% |
Joe Perches | 4 | 0.08% | 2 | 5.88% |
Michael S. Tsirkin | 4 | 0.08% | 1 | 2.94% |
Wei Yongjun | 3 | 0.06% | 1 | 2.94% |
wangyunjian | 2 | 0.04% | 1 | 2.94% |
Barry Song | 2 | 0.04% | 1 | 2.94% |
Sachin Kamat | 2 | 0.04% | 1 | 2.94% |
Wolfram Sang | 2 | 0.04% | 1 | 2.94% |
Chen-Yu Tsai | 1 | 0.02% | 1 | 2.94% |
Eric W. Biedermann | 1 | 0.02% | 1 | 2.94% |
Arnd Bergmann | 1 | 0.02% | 1 | 2.94% |
Heiner Kallweit | 1 | 0.02% | 1 | 2.94% |
Total | 5150 | 34 |
/* * Allwinner EMAC Fast Ethernet driver for Linux. * * Copyright 2012-2013 Stefan Roese <sr@denx.de> * Copyright 2013 Maxime Ripard <maxime.ripard@free-electrons.com> * * Based on the Linux driver provided by Allwinner: * Copyright (C) 1997 Sten Wang * * This file is licensed under the terms of the GNU General Public * License version 2. This program is licensed "as is" without any * warranty of any kind, whether express or implied. */ #include <linux/clk.h> #include <linux/etherdevice.h> #include <linux/ethtool.h> #include <linux/gpio.h> #include <linux/interrupt.h> #include <linux/irq.h> #include <linux/mii.h> #include <linux/module.h> #include <linux/netdevice.h> #include <linux/of_address.h> #include <linux/of_irq.h> #include <linux/of_mdio.h> #include <linux/of_net.h> #include <linux/of_platform.h> #include <linux/platform_device.h> #include <linux/phy.h> #include <linux/soc/sunxi/sunxi_sram.h> #include <linux/dmaengine.h> #include "sun4i-emac.h" #define DRV_NAME "sun4i-emac" #define EMAC_MAX_FRAME_LEN 0x0600 #define EMAC_DEFAULT_MSG_ENABLE 0x0000 static int debug = -1; /* defaults above */; module_param(debug, int, 0); MODULE_PARM_DESC(debug, "debug message flags"); /* Transmit timeout, default 5 seconds. */ static int watchdog = 5000; module_param(watchdog, int, 0400); MODULE_PARM_DESC(watchdog, "transmit timeout in milliseconds"); /* EMAC register address locking. * * The EMAC uses an address register to control where data written * to the data register goes. This means that the address register * must be preserved over interrupts or similar calls. * * During interrupt and other critical calls, a spinlock is used to * protect the system, but the calls themselves save the address * in the address register in case they are interrupting another * access to the device. * * For general accesses a lock is provided so that calls which are * allowed to sleep are serialised so that the address register does * not need to be saved. This lock also serves to serialise access * to the EEPROM and PHY access registers which are shared between * these two devices. */ /* The driver supports the original EMACE, and now the two newer * devices, EMACA and EMACB. */ struct emac_board_info { struct clk *clk; struct device *dev; struct platform_device *pdev; spinlock_t lock; void __iomem *membase; u32 msg_enable; struct net_device *ndev; u16 tx_fifo_stat; int emacrx_completed_flag; struct device_node *phy_node; unsigned int link; unsigned int speed; unsigned int duplex; phy_interface_t phy_interface; struct dma_chan *rx_chan; phys_addr_t emac_rx_fifo; }; struct emac_dma_req { struct emac_board_info *db; struct dma_async_tx_descriptor *desc; struct sk_buff *skb; dma_addr_t rxbuf; int count; }; static void emac_update_speed(struct net_device *dev) { struct emac_board_info *db = netdev_priv(dev); unsigned int reg_val; /* set EMAC SPEED, depend on PHY */ reg_val = readl(db->membase + EMAC_MAC_SUPP_REG); reg_val &= ~EMAC_MAC_SUPP_100M; if (db->speed == SPEED_100) reg_val |= EMAC_MAC_SUPP_100M; writel(reg_val, db->membase + EMAC_MAC_SUPP_REG); } static void emac_update_duplex(struct net_device *dev) { struct emac_board_info *db = netdev_priv(dev); unsigned int reg_val; /* set duplex depend on phy */ reg_val = readl(db->membase + EMAC_MAC_CTL1_REG); reg_val &= ~EMAC_MAC_CTL1_DUPLEX_EN; if (db->duplex) reg_val |= EMAC_MAC_CTL1_DUPLEX_EN; writel(reg_val, db->membase + EMAC_MAC_CTL1_REG); } static void emac_handle_link_change(struct net_device *dev) { struct emac_board_info *db = netdev_priv(dev); struct phy_device *phydev = dev->phydev; unsigned long flags; int status_change = 0; if (phydev->link) { if (db->speed != phydev->speed) { spin_lock_irqsave(&db->lock, flags); db->speed = phydev->speed; emac_update_speed(dev); spin_unlock_irqrestore(&db->lock, flags); status_change = 1; } if (db->duplex != phydev->duplex) { spin_lock_irqsave(&db->lock, flags); db->duplex = phydev->duplex; emac_update_duplex(dev); spin_unlock_irqrestore(&db->lock, flags); status_change = 1; } } if (phydev->link != db->link) { if (!phydev->link) { db->speed = 0; db->duplex = -1; } db->link = phydev->link; status_change = 1; } if (status_change) phy_print_status(phydev); } static int emac_mdio_probe(struct net_device *dev) { struct emac_board_info *db = netdev_priv(dev); struct phy_device *phydev; /* to-do: PHY interrupts are currently not supported */ /* attach the mac to the phy */ phydev = of_phy_connect(db->ndev, db->phy_node, &emac_handle_link_change, 0, db->phy_interface); if (!phydev) { netdev_err(db->ndev, "could not find the PHY\n"); return -ENODEV; } /* mask with MAC supported features */ phy_set_max_speed(phydev, SPEED_100); db->link = 0; db->speed = 0; db->duplex = -1; return 0; } static void emac_mdio_remove(struct net_device *dev) { phy_disconnect(dev->phydev); } static void emac_reset(struct emac_board_info *db) { dev_dbg(db->dev, "resetting device\n"); /* RESET device */ writel(0, db->membase + EMAC_CTL_REG); udelay(200); writel(EMAC_CTL_RESET, db->membase + EMAC_CTL_REG); udelay(200); } static void emac_outblk_32bit(void __iomem *reg, void *data, int count) { writesl(reg, data, round_up(count, 4) / 4); } static void emac_inblk_32bit(void __iomem *reg, void *data, int count) { readsl(reg, data, round_up(count, 4) / 4); } static struct emac_dma_req * emac_alloc_dma_req(struct emac_board_info *db, struct dma_async_tx_descriptor *desc, struct sk_buff *skb, dma_addr_t rxbuf, int count) { struct emac_dma_req *req; req = kzalloc(sizeof(struct emac_dma_req), GFP_ATOMIC); if (!req) return NULL; req->db = db; req->desc = desc; req->skb = skb; req->rxbuf = rxbuf; req->count = count; return req; } static void emac_free_dma_req(struct emac_dma_req *req) { kfree(req); } static void emac_dma_done_callback(void *arg) { struct emac_dma_req *req = arg; struct emac_board_info *db = req->db; struct sk_buff *skb = req->skb; struct net_device *dev = db->ndev; int rxlen = req->count; u32 reg_val; dma_unmap_single(db->dev, req->rxbuf, rxlen, DMA_FROM_DEVICE); skb->protocol = eth_type_trans(skb, dev); netif_rx(skb); dev->stats.rx_bytes += rxlen; /* Pass to upper layer */ dev->stats.rx_packets++; /* re enable cpu receive */ reg_val = readl(db->membase + EMAC_RX_CTL_REG); reg_val &= ~EMAC_RX_CTL_DMA_EN; writel(reg_val, db->membase + EMAC_RX_CTL_REG); /* re enable interrupt */ reg_val = readl(db->membase + EMAC_INT_CTL_REG); reg_val |= EMAC_INT_CTL_RX_EN; writel(reg_val, db->membase + EMAC_INT_CTL_REG); db->emacrx_completed_flag = 1; emac_free_dma_req(req); } static int emac_dma_inblk_32bit(struct emac_board_info *db, struct sk_buff *skb, void *rdptr, int count) { struct dma_async_tx_descriptor *desc; dma_cookie_t cookie; dma_addr_t rxbuf; struct emac_dma_req *req; int ret = 0; rxbuf = dma_map_single(db->dev, rdptr, count, DMA_FROM_DEVICE); ret = dma_mapping_error(db->dev, rxbuf); if (ret) { dev_err(db->dev, "dma mapping error.\n"); return ret; } desc = dmaengine_prep_slave_single(db->rx_chan, rxbuf, count, DMA_DEV_TO_MEM, DMA_PREP_INTERRUPT | DMA_CTRL_ACK); if (!desc) { dev_err(db->dev, "prepare slave single failed\n"); ret = -ENOMEM; goto prepare_err; } req = emac_alloc_dma_req(db, desc, skb, rxbuf, count); if (!req) { dev_err(db->dev, "alloc emac dma req error.\n"); ret = -ENOMEM; goto alloc_req_err; } desc->callback_param = req; desc->callback = emac_dma_done_callback; cookie = dmaengine_submit(desc); ret = dma_submit_error(cookie); if (ret) { dev_err(db->dev, "dma submit error.\n"); goto submit_err; } dma_async_issue_pending(db->rx_chan); return ret; submit_err: emac_free_dma_req(req); alloc_req_err: dmaengine_desc_free(desc); prepare_err: dma_unmap_single(db->dev, rxbuf, count, DMA_FROM_DEVICE); return ret; } /* ethtool ops */ static void emac_get_drvinfo(struct net_device *dev, struct ethtool_drvinfo *info) { strscpy(info->driver, DRV_NAME, sizeof(info->driver)); strscpy(info->bus_info, dev_name(&dev->dev), sizeof(info->bus_info)); } static u32 emac_get_msglevel(struct net_device *dev) { struct emac_board_info *db = netdev_priv(dev); return db->msg_enable; } static void emac_set_msglevel(struct net_device *dev, u32 value) { struct emac_board_info *db = netdev_priv(dev); db->msg_enable = value; } static const struct ethtool_ops emac_ethtool_ops = { .get_drvinfo = emac_get_drvinfo, .get_link = ethtool_op_get_link, .get_link_ksettings = phy_ethtool_get_link_ksettings, .set_link_ksettings = phy_ethtool_set_link_ksettings, .get_msglevel = emac_get_msglevel, .set_msglevel = emac_set_msglevel, }; static unsigned int emac_setup(struct net_device *ndev) { struct emac_board_info *db = netdev_priv(ndev); unsigned int reg_val; /* set up TX */ reg_val = readl(db->membase + EMAC_TX_MODE_REG); writel(reg_val | EMAC_TX_MODE_ABORTED_FRAME_EN, db->membase + EMAC_TX_MODE_REG); /* set MAC */ /* set MAC CTL0 */ reg_val = readl(db->membase + EMAC_MAC_CTL0_REG); writel(reg_val | EMAC_MAC_CTL0_RX_FLOW_CTL_EN | EMAC_MAC_CTL0_TX_FLOW_CTL_EN, db->membase + EMAC_MAC_CTL0_REG); /* set MAC CTL1 */ reg_val = readl(db->membase + EMAC_MAC_CTL1_REG); reg_val |= EMAC_MAC_CTL1_LEN_CHECK_EN; reg_val |= EMAC_MAC_CTL1_CRC_EN; reg_val |= EMAC_MAC_CTL1_PAD_EN; writel(reg_val, db->membase + EMAC_MAC_CTL1_REG); /* set up IPGT */ writel(EMAC_MAC_IPGT_FULL_DUPLEX, db->membase + EMAC_MAC_IPGT_REG); /* set up IPGR */ writel((EMAC_MAC_IPGR_IPG1 << 8) | EMAC_MAC_IPGR_IPG2, db->membase + EMAC_MAC_IPGR_REG); /* set up Collison window */ writel((EMAC_MAC_CLRT_COLLISION_WINDOW << 8) | EMAC_MAC_CLRT_RM, db->membase + EMAC_MAC_CLRT_REG); /* set up Max Frame Length */ writel(EMAC_MAX_FRAME_LEN, db->membase + EMAC_MAC_MAXF_REG); return 0; } static void emac_set_rx_mode(struct net_device *ndev) { struct emac_board_info *db = netdev_priv(ndev); unsigned int reg_val; /* set up RX */ reg_val = readl(db->membase + EMAC_RX_CTL_REG); if (ndev->flags & IFF_PROMISC) reg_val |= EMAC_RX_CTL_PASS_ALL_EN; else reg_val &= ~EMAC_RX_CTL_PASS_ALL_EN; writel(reg_val | EMAC_RX_CTL_PASS_LEN_OOR_EN | EMAC_RX_CTL_ACCEPT_UNICAST_EN | EMAC_RX_CTL_DA_FILTER_EN | EMAC_RX_CTL_ACCEPT_MULTICAST_EN | EMAC_RX_CTL_ACCEPT_BROADCAST_EN, db->membase + EMAC_RX_CTL_REG); } static unsigned int emac_powerup(struct net_device *ndev) { struct emac_board_info *db = netdev_priv(ndev); unsigned int reg_val; /* initial EMAC */ /* flush RX FIFO */ reg_val = readl(db->membase + EMAC_RX_CTL_REG); reg_val |= EMAC_RX_CTL_FLUSH_FIFO; writel(reg_val, db->membase + EMAC_RX_CTL_REG); udelay(1); /* initial MAC */ /* soft reset MAC */ reg_val = readl(db->membase + EMAC_MAC_CTL0_REG); reg_val &= ~EMAC_MAC_CTL0_SOFT_RESET; writel(reg_val, db->membase + EMAC_MAC_CTL0_REG); /* set MII clock */ reg_val = readl(db->membase + EMAC_MAC_MCFG_REG); reg_val &= ~EMAC_MAC_MCFG_MII_CLKD_MASK; reg_val |= EMAC_MAC_MCFG_MII_CLKD_72; writel(reg_val, db->membase + EMAC_MAC_MCFG_REG); /* clear RX counter */ writel(0x0, db->membase + EMAC_RX_FBC_REG); /* disable all interrupt and clear interrupt status */ writel(0, db->membase + EMAC_INT_CTL_REG); reg_val = readl(db->membase + EMAC_INT_STA_REG); writel(reg_val, db->membase + EMAC_INT_STA_REG); udelay(1); /* set up EMAC */ emac_setup(ndev); /* set mac_address to chip */ writel(ndev->dev_addr[0] << 16 | ndev->dev_addr[1] << 8 | ndev-> dev_addr[2], db->membase + EMAC_MAC_A1_REG); writel(ndev->dev_addr[3] << 16 | ndev->dev_addr[4] << 8 | ndev-> dev_addr[5], db->membase + EMAC_MAC_A0_REG); mdelay(1); return 0; } static int emac_set_mac_address(struct net_device *dev, void *p) { struct sockaddr *addr = p; struct emac_board_info *db = netdev_priv(dev); if (netif_running(dev)) return -EBUSY; eth_hw_addr_set(dev, addr->sa_data); writel(dev->dev_addr[0] << 16 | dev->dev_addr[1] << 8 | dev-> dev_addr[2], db->membase + EMAC_MAC_A1_REG); writel(dev->dev_addr[3] << 16 | dev->dev_addr[4] << 8 | dev-> dev_addr[5], db->membase + EMAC_MAC_A0_REG); return 0; } /* Initialize emac board */ static void emac_init_device(struct net_device *dev) { struct emac_board_info *db = netdev_priv(dev); unsigned long flags; unsigned int reg_val; spin_lock_irqsave(&db->lock, flags); emac_update_speed(dev); emac_update_duplex(dev); /* enable RX/TX */ reg_val = readl(db->membase + EMAC_CTL_REG); writel(reg_val | EMAC_CTL_RESET | EMAC_CTL_TX_EN | EMAC_CTL_RX_EN, db->membase + EMAC_CTL_REG); /* enable RX/TX0/RX Hlevel interrup */ reg_val = readl(db->membase + EMAC_INT_CTL_REG); reg_val |= (EMAC_INT_CTL_TX_EN | EMAC_INT_CTL_TX_ABRT_EN | EMAC_INT_CTL_RX_EN); writel(reg_val, db->membase + EMAC_INT_CTL_REG); spin_unlock_irqrestore(&db->lock, flags); } /* Our watchdog timed out. Called by the networking layer */ static void emac_timeout(struct net_device *dev, unsigned int txqueue) { struct emac_board_info *db = netdev_priv(dev); unsigned long flags; if (netif_msg_timer(db)) dev_err(db->dev, "tx time out.\n"); /* Save previous register address */ spin_lock_irqsave(&db->lock, flags); netif_stop_queue(dev); emac_reset(db); emac_init_device(dev); /* We can accept TX packets again */ netif_trans_update(dev); netif_wake_queue(dev); /* Restore previous register address */ spin_unlock_irqrestore(&db->lock, flags); } /* Hardware start transmission. * Send a packet to media from the upper layer. */ static netdev_tx_t emac_start_xmit(struct sk_buff *skb, struct net_device *dev) { struct emac_board_info *db = netdev_priv(dev); unsigned long channel; unsigned long flags; channel = db->tx_fifo_stat & 3; if (channel == 3) return NETDEV_TX_BUSY; channel = (channel == 1 ? 1 : 0); spin_lock_irqsave(&db->lock, flags); writel(channel, db->membase + EMAC_TX_INS_REG); emac_outblk_32bit(db->membase + EMAC_TX_IO_DATA_REG, skb->data, skb->len); dev->stats.tx_bytes += skb->len; db->tx_fifo_stat |= 1 << channel; /* TX control: First packet immediately send, second packet queue */ if (channel == 0) { /* set TX len */ writel(skb->len, db->membase + EMAC_TX_PL0_REG); /* start translate from fifo to phy */ writel(readl(db->membase + EMAC_TX_CTL0_REG) | 1, db->membase + EMAC_TX_CTL0_REG); /* save the time stamp */ netif_trans_update(dev); } else if (channel == 1) { /* set TX len */ writel(skb->len, db->membase + EMAC_TX_PL1_REG); /* start translate from fifo to phy */ writel(readl(db->membase + EMAC_TX_CTL1_REG) | 1, db->membase + EMAC_TX_CTL1_REG); /* save the time stamp */ netif_trans_update(dev); } if ((db->tx_fifo_stat & 3) == 3) { /* Second packet */ netif_stop_queue(dev); } spin_unlock_irqrestore(&db->lock, flags); /* free this SKB */ dev_consume_skb_any(skb); return NETDEV_TX_OK; } /* EMAC interrupt handler * receive the packet to upper layer, free the transmitted packet */ static void emac_tx_done(struct net_device *dev, struct emac_board_info *db, unsigned int tx_status) { /* One packet sent complete */ db->tx_fifo_stat &= ~(tx_status & 3); if (3 == (tx_status & 3)) dev->stats.tx_packets += 2; else dev->stats.tx_packets++; if (netif_msg_tx_done(db)) dev_dbg(db->dev, "tx done, NSR %02x\n", tx_status); netif_wake_queue(dev); } /* Received a packet and pass to upper layer */ static void emac_rx(struct net_device *dev) { struct emac_board_info *db = netdev_priv(dev); struct sk_buff *skb; u8 *rdptr; bool good_packet; unsigned int reg_val; u32 rxhdr, rxstatus, rxcount, rxlen; /* Check packet ready or not */ while (1) { /* race warning: the first packet might arrive with * the interrupts disabled, but the second will fix * it */ rxcount = readl(db->membase + EMAC_RX_FBC_REG); if (netif_msg_rx_status(db)) dev_dbg(db->dev, "RXCount: %x\n", rxcount); if (!rxcount) { db->emacrx_completed_flag = 1; reg_val = readl(db->membase + EMAC_INT_CTL_REG); reg_val |= (EMAC_INT_CTL_TX_EN | EMAC_INT_CTL_TX_ABRT_EN | EMAC_INT_CTL_RX_EN); writel(reg_val, db->membase + EMAC_INT_CTL_REG); /* had one stuck? */ rxcount = readl(db->membase + EMAC_RX_FBC_REG); if (!rxcount) return; } reg_val = readl(db->membase + EMAC_RX_IO_DATA_REG); if (netif_msg_rx_status(db)) dev_dbg(db->dev, "receive header: %x\n", reg_val); if (reg_val != EMAC_UNDOCUMENTED_MAGIC) { /* disable RX */ reg_val = readl(db->membase + EMAC_CTL_REG); writel(reg_val & ~EMAC_CTL_RX_EN, db->membase + EMAC_CTL_REG); /* Flush RX FIFO */ reg_val = readl(db->membase + EMAC_RX_CTL_REG); writel(reg_val | (1 << 3), db->membase + EMAC_RX_CTL_REG); do { reg_val = readl(db->membase + EMAC_RX_CTL_REG); } while (reg_val & (1 << 3)); /* enable RX */ reg_val = readl(db->membase + EMAC_CTL_REG); writel(reg_val | EMAC_CTL_RX_EN, db->membase + EMAC_CTL_REG); reg_val = readl(db->membase + EMAC_INT_CTL_REG); reg_val |= (EMAC_INT_CTL_TX_EN | EMAC_INT_CTL_TX_ABRT_EN | EMAC_INT_CTL_RX_EN); writel(reg_val, db->membase + EMAC_INT_CTL_REG); db->emacrx_completed_flag = 1; return; } /* A packet ready now & Get status/length */ good_packet = true; rxhdr = readl(db->membase + EMAC_RX_IO_DATA_REG); if (netif_msg_rx_status(db)) dev_dbg(db->dev, "rxhdr: %x\n", *((int *)(&rxhdr))); rxlen = EMAC_RX_IO_DATA_LEN(rxhdr); rxstatus = EMAC_RX_IO_DATA_STATUS(rxhdr); if (netif_msg_rx_status(db)) dev_dbg(db->dev, "RX: status %02x, length %04x\n", rxstatus, rxlen); /* Packet Status check */ if (rxlen < 0x40) { good_packet = false; if (netif_msg_rx_err(db)) dev_dbg(db->dev, "RX: Bad Packet (runt)\n"); } if (unlikely(!(rxstatus & EMAC_RX_IO_DATA_STATUS_OK))) { good_packet = false; if (rxstatus & EMAC_RX_IO_DATA_STATUS_CRC_ERR) { if (netif_msg_rx_err(db)) dev_dbg(db->dev, "crc error\n"); dev->stats.rx_crc_errors++; } if (rxstatus & EMAC_RX_IO_DATA_STATUS_LEN_ERR) { if (netif_msg_rx_err(db)) dev_dbg(db->dev, "length error\n"); dev->stats.rx_length_errors++; } } /* Move data from EMAC */ if (good_packet) { skb = netdev_alloc_skb(dev, rxlen + 4); if (!skb) continue; skb_reserve(skb, 2); rdptr = skb_put(skb, rxlen - 4); /* Read received packet from RX SRAM */ if (netif_msg_rx_status(db)) dev_dbg(db->dev, "RxLen %x\n", rxlen); if (rxlen >= dev->mtu && db->rx_chan) { reg_val = readl(db->membase + EMAC_RX_CTL_REG); reg_val |= EMAC_RX_CTL_DMA_EN; writel(reg_val, db->membase + EMAC_RX_CTL_REG); if (!emac_dma_inblk_32bit(db, skb, rdptr, rxlen)) break; /* re enable cpu receive. then try to receive by emac_inblk_32bit */ reg_val = readl(db->membase + EMAC_RX_CTL_REG); reg_val &= ~EMAC_RX_CTL_DMA_EN; writel(reg_val, db->membase + EMAC_RX_CTL_REG); } emac_inblk_32bit(db->membase + EMAC_RX_IO_DATA_REG, rdptr, rxlen); dev->stats.rx_bytes += rxlen; /* Pass to upper layer */ skb->protocol = eth_type_trans(skb, dev); netif_rx(skb); dev->stats.rx_packets++; } } } static irqreturn_t emac_interrupt(int irq, void *dev_id) { struct net_device *dev = dev_id; struct emac_board_info *db = netdev_priv(dev); int int_status; unsigned int reg_val; /* A real interrupt coming */ spin_lock(&db->lock); /* Disable all interrupts */ writel(0, db->membase + EMAC_INT_CTL_REG); /* Got EMAC interrupt status */ /* Got ISR */ int_status = readl(db->membase + EMAC_INT_STA_REG); /* Clear ISR status */ writel(int_status, db->membase + EMAC_INT_STA_REG); if (netif_msg_intr(db)) dev_dbg(db->dev, "emac interrupt %02x\n", int_status); /* Received the coming packet */ if ((int_status & 0x100) && (db->emacrx_completed_flag == 1)) { /* carrier lost */ db->emacrx_completed_flag = 0; emac_rx(dev); } /* Transmit Interrupt check */ if (int_status & EMAC_INT_STA_TX_COMPLETE) emac_tx_done(dev, db, int_status); if (int_status & EMAC_INT_STA_TX_ABRT) netdev_info(dev, " ab : %x\n", int_status); /* Re-enable interrupt mask */ if (db->emacrx_completed_flag == 1) { reg_val = readl(db->membase + EMAC_INT_CTL_REG); reg_val |= (EMAC_INT_CTL_TX_EN | EMAC_INT_CTL_TX_ABRT_EN | EMAC_INT_CTL_RX_EN); writel(reg_val, db->membase + EMAC_INT_CTL_REG); } else { reg_val = readl(db->membase + EMAC_INT_CTL_REG); reg_val |= (EMAC_INT_CTL_TX_EN | EMAC_INT_CTL_TX_ABRT_EN); writel(reg_val, db->membase + EMAC_INT_CTL_REG); } spin_unlock(&db->lock); return IRQ_HANDLED; } #ifdef CONFIG_NET_POLL_CONTROLLER /* * Used by netconsole */ static void emac_poll_controller(struct net_device *dev) { disable_irq(dev->irq); emac_interrupt(dev->irq, dev); enable_irq(dev->irq); } #endif /* Open the interface. * The interface is opened whenever "ifconfig" actives it. */ static int emac_open(struct net_device *dev) { struct emac_board_info *db = netdev_priv(dev); int ret; if (netif_msg_ifup(db)) dev_dbg(db->dev, "enabling %s\n", dev->name); if (request_irq(dev->irq, &emac_interrupt, 0, dev->name, dev)) return -EAGAIN; /* Initialize EMAC board */ emac_reset(db); emac_init_device(dev); ret = emac_mdio_probe(dev); if (ret < 0) { free_irq(dev->irq, dev); netdev_err(dev, "cannot probe MDIO bus\n"); return ret; } phy_start(dev->phydev); netif_start_queue(dev); return 0; } static void emac_shutdown(struct net_device *dev) { unsigned int reg_val; struct emac_board_info *db = netdev_priv(dev); /* Disable all interrupt */ writel(0, db->membase + EMAC_INT_CTL_REG); /* clear interrupt status */ reg_val = readl(db->membase + EMAC_INT_STA_REG); writel(reg_val, db->membase + EMAC_INT_STA_REG); /* Disable RX/TX */ reg_val = readl(db->membase + EMAC_CTL_REG); reg_val &= ~(EMAC_CTL_TX_EN | EMAC_CTL_RX_EN | EMAC_CTL_RESET); writel(reg_val, db->membase + EMAC_CTL_REG); } /* Stop the interface. * The interface is stopped when it is brought. */ static int emac_stop(struct net_device *ndev) { struct emac_board_info *db = netdev_priv(ndev); if (netif_msg_ifdown(db)) dev_dbg(db->dev, "shutting down %s\n", ndev->name); netif_stop_queue(ndev); netif_carrier_off(ndev); phy_stop(ndev->phydev); emac_mdio_remove(ndev); emac_shutdown(ndev); free_irq(ndev->irq, ndev); return 0; } static const struct net_device_ops emac_netdev_ops = { .ndo_open = emac_open, .ndo_stop = emac_stop, .ndo_start_xmit = emac_start_xmit, .ndo_tx_timeout = emac_timeout, .ndo_set_rx_mode = emac_set_rx_mode, .ndo_eth_ioctl = phy_do_ioctl_running, .ndo_validate_addr = eth_validate_addr, .ndo_set_mac_address = emac_set_mac_address, #ifdef CONFIG_NET_POLL_CONTROLLER .ndo_poll_controller = emac_poll_controller, #endif }; static int emac_configure_dma(struct emac_board_info *db) { struct platform_device *pdev = db->pdev; struct net_device *ndev = db->ndev; struct dma_slave_config conf = {}; struct resource *regs; int err = 0; regs = platform_get_resource(pdev, IORESOURCE_MEM, 0); if (!regs) { netdev_err(ndev, "get io resource from device failed.\n"); err = -ENOMEM; goto out_clear_chan; } netdev_info(ndev, "get io resource from device: %pa, size = %u\n", ®s->start, (unsigned int)resource_size(regs)); db->emac_rx_fifo = regs->start + EMAC_RX_IO_DATA_REG; db->rx_chan = dma_request_chan(&pdev->dev, "rx"); if (IS_ERR(db->rx_chan)) { netdev_err(ndev, "failed to request dma channel. dma is disabled\n"); err = PTR_ERR(db->rx_chan); goto out_clear_chan; } conf.direction = DMA_DEV_TO_MEM; conf.dst_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; conf.src_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; conf.src_addr = db->emac_rx_fifo; conf.dst_maxburst = 4; conf.src_maxburst = 4; conf.device_fc = false; err = dmaengine_slave_config(db->rx_chan, &conf); if (err) { netdev_err(ndev, "config dma slave failed\n"); err = -EINVAL; goto out_slave_configure_err; } return err; out_slave_configure_err: dma_release_channel(db->rx_chan); out_clear_chan: db->rx_chan = NULL; return err; } /* Search EMAC board, allocate space and register it */ static int emac_probe(struct platform_device *pdev) { struct device_node *np = pdev->dev.of_node; struct emac_board_info *db; struct net_device *ndev; int ret = 0; ndev = alloc_etherdev(sizeof(struct emac_board_info)); if (!ndev) { dev_err(&pdev->dev, "could not allocate device.\n"); return -ENOMEM; } SET_NETDEV_DEV(ndev, &pdev->dev); db = netdev_priv(ndev); db->dev = &pdev->dev; db->ndev = ndev; db->pdev = pdev; db->msg_enable = netif_msg_init(debug, EMAC_DEFAULT_MSG_ENABLE); spin_lock_init(&db->lock); db->membase = of_iomap(np, 0); if (!db->membase) { dev_err(&pdev->dev, "failed to remap registers\n"); ret = -ENOMEM; goto out; } /* fill in parameters for net-dev structure */ ndev->base_addr = (unsigned long)db->membase; ndev->irq = irq_of_parse_and_map(np, 0); if (ndev->irq == -ENXIO) { netdev_err(ndev, "No irq resource\n"); ret = ndev->irq; goto out_iounmap; } if (emac_configure_dma(db)) netdev_info(ndev, "configure dma failed. disable dma.\n"); db->clk = devm_clk_get(&pdev->dev, NULL); if (IS_ERR(db->clk)) { ret = PTR_ERR(db->clk); goto out_dispose_mapping; } ret = clk_prepare_enable(db->clk); if (ret) { dev_err(&pdev->dev, "Error couldn't enable clock (%d)\n", ret); goto out_dispose_mapping; } ret = sunxi_sram_claim(&pdev->dev); if (ret) { dev_err(&pdev->dev, "Error couldn't map SRAM to device\n"); goto out_clk_disable_unprepare; } db->phy_node = of_parse_phandle(np, "phy-handle", 0); if (!db->phy_node) db->phy_node = of_parse_phandle(np, "phy", 0); if (!db->phy_node) { dev_err(&pdev->dev, "no associated PHY\n"); ret = -ENODEV; goto out_release_sram; } /* Read MAC-address from DT */ ret = of_get_ethdev_address(np, ndev); if (ret) { /* if the MAC address is invalid get a random one */ eth_hw_addr_random(ndev); dev_warn(&pdev->dev, "using random MAC address %pM\n", ndev->dev_addr); } db->emacrx_completed_flag = 1; emac_powerup(ndev); emac_reset(db); ndev->netdev_ops = &emac_netdev_ops; ndev->watchdog_timeo = msecs_to_jiffies(watchdog); ndev->ethtool_ops = &emac_ethtool_ops; platform_set_drvdata(pdev, ndev); /* Carrier starts down, phylib will bring it up */ netif_carrier_off(ndev); ret = register_netdev(ndev); if (ret) { dev_err(&pdev->dev, "Registering netdev failed!\n"); ret = -ENODEV; goto out_release_sram; } dev_info(&pdev->dev, "%s: at %p, IRQ %d MAC: %pM\n", ndev->name, db->membase, ndev->irq, ndev->dev_addr); return 0; out_release_sram: sunxi_sram_release(&pdev->dev); out_clk_disable_unprepare: clk_disable_unprepare(db->clk); out_dispose_mapping: irq_dispose_mapping(ndev->irq); dma_release_channel(db->rx_chan); out_iounmap: iounmap(db->membase); out: dev_err(db->dev, "not found (%d).\n", ret); free_netdev(ndev); return ret; } static int emac_remove(struct platform_device *pdev) { struct net_device *ndev = platform_get_drvdata(pdev); struct emac_board_info *db = netdev_priv(ndev); if (db->rx_chan) { dmaengine_terminate_all(db->rx_chan); dma_release_channel(db->rx_chan); } unregister_netdev(ndev); sunxi_sram_release(&pdev->dev); clk_disable_unprepare(db->clk); irq_dispose_mapping(ndev->irq); iounmap(db->membase); free_netdev(ndev); dev_dbg(&pdev->dev, "released and freed device\n"); return 0; } static int emac_suspend(struct platform_device *dev, pm_message_t state) { struct net_device *ndev = platform_get_drvdata(dev); netif_carrier_off(ndev); netif_device_detach(ndev); emac_shutdown(ndev); return 0; } static int emac_resume(struct platform_device *dev) { struct net_device *ndev = platform_get_drvdata(dev); struct emac_board_info *db = netdev_priv(ndev); emac_reset(db); emac_init_device(ndev); netif_device_attach(ndev); return 0; } static const struct of_device_id emac_of_match[] = { {.compatible = "allwinner,sun4i-a10-emac",}, /* Deprecated */ {.compatible = "allwinner,sun4i-emac",}, {}, }; MODULE_DEVICE_TABLE(of, emac_of_match); static struct platform_driver emac_driver = { .driver = { .name = "sun4i-emac", .of_match_table = emac_of_match, }, .probe = emac_probe, .remove = emac_remove, .suspend = emac_suspend, .resume = emac_resume, }; module_platform_driver(emac_driver); MODULE_AUTHOR("Stefan Roese <sr@denx.de>"); MODULE_AUTHOR("Maxime Ripard <maxime.ripard@free-electrons.com>"); MODULE_DESCRIPTION("Allwinner A10 emac network driver"); MODULE_LICENSE("GPL");
Information contained on this website is for historical information purposes only and does not indicate or represent copyright ownership.
Created with Cregit http://github.com/cregit/cregit
Version 2.0-RC1