| /* | 
|  * 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 "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; | 
|     struct sk_buff        *skb_last; | 
|     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; | 
| }; | 
|   | 
| 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 &= ~(0x1 << 8); | 
|     if (db->speed == SPEED_100) | 
|         reg_val |= 1 << 8; | 
|     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); | 
| } | 
|   | 
| /* ethtool ops */ | 
| static void emac_get_drvinfo(struct net_device *dev, | 
|                   struct ethtool_drvinfo *info) | 
| { | 
|     strlcpy(info->driver, DRV_NAME, sizeof(info->driver)); | 
|     strlcpy(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 |= 0x8; | 
|     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 &= (~(0xf << 2)); | 
|     reg_val |= (0xD << 2); | 
|     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; | 
|   | 
|     memcpy(dev->dev_addr, addr->sa_data, ETH_ALEN); | 
|   | 
|     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 |= (0xf << 0) | (0x01 << 8); | 
|     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; | 
|     static int rxlen_last; | 
|     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 ((db->skb_last != NULL) && (rxlen_last > 0)) { | 
|             dev->stats.rx_bytes += rxlen_last; | 
|   | 
|             /* Pass to upper layer */ | 
|             db->skb_last->protocol = eth_type_trans(db->skb_last, | 
|                                 dev); | 
|             netif_rx(db->skb_last); | 
|             dev->stats.rx_packets++; | 
|             db->skb_last = NULL; | 
|             rxlen_last = 0; | 
|   | 
|             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 (!rxcount) { | 
|             db->emacrx_completed_flag = 1; | 
|             reg_val = readl(db->membase + EMAC_INT_CTL_REG); | 
|             reg_val |= (0xf << 0) | (0x01 << 8); | 
|             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 |= (0xf << 0) | (0x01 << 8); | 
|             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); | 
|   | 
|             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 & (0x01 | 0x02)) | 
|         emac_tx_done(dev, db, int_status); | 
|   | 
|     if (int_status & (0x04 | 0x08)) | 
|         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 |= (0xf << 0) | (0x01 << 8); | 
|         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_do_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 | 
| }; | 
|   | 
| /* 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; | 
|     const char *mac_addr; | 
|   | 
|     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; | 
|     } | 
|   | 
|     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 */ | 
|     mac_addr = of_get_mac_address(np); | 
|     if (!IS_ERR(mac_addr)) | 
|         ether_addr_copy(ndev->dev_addr, mac_addr); | 
|   | 
|     /* Check if the MAC address is valid, if not get a random one */ | 
|     if (!is_valid_ether_addr(ndev->dev_addr)) { | 
|         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); | 
| 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); | 
|   | 
|     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"); |