/* Cleopatre project {{{ * * Copyright (C) 2008 SPiDCOM Technologies * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, * MA 02111-1307 USA * * }}} */ /** * \file driver/net/arm/synop3504.c * \brief Driver for Synopsys 3504. * \ingroup cleopatre_net_driver. * * Linux level part of the Ethernet 3504 Synopsys IP. */ #define DRV_NAME "Synop3504" #define DRV_VERSION "3.2" #define DRV_RELDATE "nov 17, 2009" //#define TRACE_FRAME 1 //#define TRACE(...) printk(DRV_NAME": " __VA_ARGS__) #define TRACE(...) #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "synop3504_hw.h" #include "synop3504.h" /** Max size of a eth frame with double VLAN and CRC (size for rx buffer) must * be aligned on 4 bytes */ #define PKT_BUF_SZ ALIGN (ETH_FRAME_LEN + 2 * VLAN_HLEN + 4, 4) /** NAPI budget */ #define NAPI_BUDGET 64 /** Polling link timeout */ #define TIMEOUT (2 * HZ) /** Step time for AutoNegotiation */ #define AUTONEG_STEP (100) /** Timeout for AutoNegotiation */ #define AUTONEG_TIMEOUT (10) /** Wait delay */ #define MSEC_PER_JIFFY (1000 / HZ) //10ms /** Watchdog timeout for Tx frames */ #define TX_TIMEOUT (4 * HZ) /** Size of the DMA ring for tx */ #define TX_RING_SIZE 128 /** Size of the DMA ring for rx */ #define RX_RING_SIZE 128 /** Bit fields for MII_PHY identifier */ #define PHYSID_GET_ID(id1, id2) (id1 << 16 | id2) #define PHYSID_GET_OUI(id) ((id >> 10) & 0x00FFFFFF) #define PHYSID_GET_MODEL(id) ((id >> 4) & 0x0000003F) #define PHYSID_GET_REV(id) ((id >> 0) & 0x0000000F) /** Supported PHY reference */ #define OUI_ICPLUS 0x90C3 #define ICPLUS_MODEL_IP175 0x18 #define ICPLUS_MODEL_IP1001 0x19 /** TX management structure */ struct dma_tx { SynopsysDmaTx *ring; struct sk_buff *skbs[TX_RING_SIZE]; uint32_t phy_addr; uint32_t head_ptr; uint32_t tail_ptr; }; /** RX management structure */ struct dma_rx { SynopsysDmaRx *ring; struct sk_buff *skbs[RX_RING_SIZE]; uint32_t phy_addr; uint32_t head_ptr; uint32_t tail_ptr; }; /** Private structure for our net device */ struct net_priv { uint32_t gmac_addr; uint32_t dma_addr; uint32_t support_rgmii; uint32_t support_gmii; uint32_t support_rmii; uint32_t phy_addr; Synopsys synop; struct mii_if_info mii_if; struct net_device_stats stats; struct dma_tx tx; struct dma_rx rx; struct napi_struct napi; struct net_device *dev; struct platform_device *pdev; struct mii_bus *mii_bus; struct phy_device *phydev; unsigned int link; unsigned int speed; unsigned int duplex; uint8_t mac_id; #ifdef CONFIG_SEQ_CHECK struct seq_check_ctx seq_check_ctx; #endif }; /** How many Tx buffers to clean */ enum tx_clean_buff { ONE, ALL }; static void synop3504_tx_done(struct net_device *dev, enum tx_clean_buff clean); #ifdef TRACE_FRAME static const unsigned char __hexdigits[] = "0123456789ABCDEF"; static void sprintf_hex(unsigned char * str, const unsigned char * ptr, int len, unsigned char delim) { int i, j=0; for(i=0; i>4]; str[j++]=__hexdigits[ptr[i]&0x0F]; } str[j] = 0; } static void print_packet(const char * prefix, int descr, int len, struct sk_buff * skb) { struct ethhdr * h; unsigned char src[20], dst[20], body[50]; int l; h = (struct ethhdr *)skb->data; l = len - 14 > 16 ? 16 : len - 14; sprintf_hex(src, &h->h_source[0], 6, ':'); sprintf_hex(dst, &h->h_dest[0], 6, ':'); sprintf_hex(body, ((unsigned char *)skb->data)+14, l, ' '); printk("%08ld %s: d=%-3d len=%-4d proto=0x%04X src=%s dst=%s\n" " body=%s\n", jiffies, prefix, descr, len, be16_to_cpu(h->h_proto), src, dst, body); } #else #define print_packet(a,b,c,d) #endif /** These identify the driver base version and may not be removed */ static char version[] __devinitdata = DRV_NAME " 10/100/1000 Ethernet driver v" DRV_VERSION " (" DRV_RELDATE ")\n"; /** * Ethtool Set Settings. * \param dev device structure. * \param cmd ethtool command structure. * \return ethtool command. */ static int synop3504_ssettings(struct net_device *dev, struct ethtool_cmd *cmd) { struct net_priv *priv = NULL; //Check pointers if(dev == NULL) return -1; priv = (struct net_priv *)dev->priv; if(priv == NULL) return -1; return mii_ethtool_sset(&priv->mii_if, cmd); } /** * Ethtool Get Settings. * \param dev device structure. * \param cmd ethtool command structure. * \return ethtool command. */ static int synop3504_gsettings(struct net_device *dev, struct ethtool_cmd *cmd) { struct net_priv *priv = NULL; //Check pointers if(dev == NULL) return -1; priv = (struct net_priv *)dev->priv; if(priv == NULL) return -1; return mii_ethtool_gset(&priv->mii_if, cmd); } /** * Ethtool Get driver informations. * \param dev device structure. * \param drvinfo ethtool driver info structure. */ static void synop3504_gdrvinfo(struct net_device *dev, struct ethtool_drvinfo *drvinfo) { strcpy(drvinfo->driver, DRV_NAME); strcpy(drvinfo->version, DRV_VERSION); strcpy(drvinfo->fw_version, "N/A"); strcpy(drvinfo->bus_info, "N/A"); drvinfo->regdump_len = 0; drvinfo->eedump_len = 0; } static void synop3504_ethtool_get_pauseparam(struct net_device *netdev, struct ethtool_pauseparam* param) { struct net_priv *priv; int rx_pause_enabled; int tx_pause_enabled; priv = netdev_priv(netdev); SynopsysGetFlowControl(&priv->synop, &rx_pause_enabled, &tx_pause_enabled); param->rx_pause = rx_pause_enabled; param->tx_pause = tx_pause_enabled; } static int synop3504_ethtool_set_pauseparam(struct net_device *netdev, struct ethtool_pauseparam* param) { struct net_priv *priv; priv = netdev_priv(netdev); SynopsysSetFlowControl(&priv->synop, param->rx_pause, param->tx_pause); return 0; } /** Ethtool functions */ const struct ethtool_ops synop3504_ethtool_ops = { .get_settings = synop3504_gsettings, .set_settings = synop3504_ssettings, .get_drvinfo = synop3504_gdrvinfo, .get_link = ethtool_op_get_link, .get_pauseparam = synop3504_ethtool_get_pauseparam, .set_pauseparam = synop3504_ethtool_set_pauseparam, /* .get_regs_len = synop3504_reglen, .get_regs = synop3504_get_regs, .get_coalesce = synop3504_gcoalesce, .set_coalesce = synop3504_scoalesce, .get_ringparam = synop3504_gringparam, .set_ringparam = synop3504_sringparam, .get_strings = synop3504_gstrings, .get_sset_count = synop3504_sset_count, .get_ethtool_stats = synop3504_fill_stats, .get_rx_csum = synop3504_get_rx_csum, .get_tx_csum = synop3504_get_tx_csum, .set_rx_csum = synop3504_set_rx_csum, .set_tx_csum = synop3504_set_tx_csum, .get_msglevel = synop3504_get_msglevel, .set_msglevel = synop3504_set_msglevel,*/ }; /** * Read a MII register. * \param dev device structure. * \param phy phy addr (not used for us). * \param reg register to read. * \return value read. */ static int mdio_read(struct net_device *dev, int phy, int reg) { struct net_priv *priv = (struct net_priv*)dev->priv; Synopsys *synop = &priv->synop; if(synop) return SynopsysMiiRead(synop, (uint32_t)phy, reg); else return 0; }// mdio_read /** * Write a MII register. * \param dev device structure. * \param phy phy addr (not used for us). * \param reg register to write. * \param value value to write. */ static void mdio_write(struct net_device *dev, int phy, int reg, int value) { struct net_priv *priv = (struct net_priv*)dev->priv; Synopsys *synop = &priv->synop; if(synop) SynopsysMiiWrite(synop, (uint32_t)phy, reg, value); }// mdio_write /** * Initialise TX frames descriptors. * \param dev device structure. * \return error code. */ static int synop3504_txdesc_init(struct net_device *dev) { struct net_priv *priv = (struct net_priv *)dev->priv; int i; //Alloc TX DMA descriptors area if((priv->tx.ring = (SynopsysDmaTx *)dma_alloc_coherent(NULL, sizeof(SynopsysDmaTx)*TX_RING_SIZE, &priv->tx.phy_addr, GFP_DMA)) == NULL) { printk(KERN_ERR DRV_NAME": Error allocating TX descriptor buffers for %s\n", dev->name); return -ENOMEM; } //Flush DMA descriptors area memset(priv->tx.ring, 0, sizeof(SynopsysDmaTx)*TX_RING_SIZE); //Prepare descriptors for(i=0; itx.ring[i].addr2 = priv->tx.phy_addr + ((i+1)%TX_RING_SIZE)*sizeof(SynopsysDmaTx); priv->tx.ring[i].ctrl.bf.addr2en = 1; priv->tx.skbs[i] = NULL; } //Set head and tail pointers to the first descriptor priv->tx.head_ptr = 0; priv->tx.tail_ptr = 0; return 0; }// synop3504_txdesc_init /** * Initialise RX frames descriptors. * \param dev device structure. * \return error code. */ static int synop3504_rxdesc_init(struct net_device *dev) { struct net_priv *priv = (struct net_priv *)dev->priv; struct sk_buff *skb; int i; //Alloc RX DMA descriptors area if((priv->rx.ring = (SynopsysDmaRx *)dma_alloc_coherent(NULL, sizeof(SynopsysDmaRx)*RX_RING_SIZE, &priv->rx.phy_addr, GFP_DMA)) == NULL) { printk(KERN_ERR DRV_NAME": Error allocating RX descriptor buffers for %s\n", dev->name); return -ENOMEM; } //Flush DMA descriptors area memset(priv->rx.ring, 0, sizeof(SynopsysDmaRx)*RX_RING_SIZE); //Prepare descriptors for(i=0; irx.ring[i].addr2 = priv->rx.phy_addr + ((i+1)%RX_RING_SIZE)*sizeof(SynopsysDmaRx); priv->rx.ring[i].ctrl.bf.addr2en = 1; //Prepare data skb = dev_alloc_skb(PKT_BUF_SZ); if(!skb) { printk(KERN_ERR DRV_NAME": Error allocating RX buffers for %s\n",dev->name); return -ENOMEM; } priv->rx.ring[i].addr1 = (uint32_t)dma_map_single(NULL, skb->data, PKT_BUF_SZ, DMA_FROM_DEVICE); priv->rx.skbs[i] = skb; //Set data length priv->rx.ring[i].ctrl.bf.length1 = PKT_BUF_SZ; //Set own bit priv->rx.ring[i].status.bf.dma_own = 1; } //Set head and tail pointers to the first descriptor priv->rx.head_ptr = 0; priv->rx.tail_ptr = 0; return 0; }// synop3504_rxdesc_init /** * Uninitialise TX frames descriptors. * \param dev device structure. */ static void synop3504_txdesc_uninit(struct net_device *dev) { struct net_priv *priv = (struct net_priv *)dev->priv; int i; for(i=0 ; itx.skbs[i]) { dma_unmap_single(NULL, priv->tx.ring[i].addr1, priv->tx.skbs[i]->len, DMA_TO_DEVICE); dev_kfree_skb_any(priv->tx.skbs[i]); } } dma_free_coherent(NULL, sizeof(SynopsysDmaTx)*TX_RING_SIZE, priv->tx.ring, priv->tx.phy_addr); }// synop3504_txdesc_uninit /** * Uninitialise RX frames descriptors. * \param dev device structure. */ static void synop3504_rxdesc_uninit(struct net_device *dev) { struct net_priv *priv = (struct net_priv *)dev->priv; int i; for(i=0 ; irx.skbs[i]) { dma_unmap_single(NULL, priv->rx.ring[i].addr1, PKT_BUF_SZ, DMA_FROM_DEVICE); dev_kfree_skb_any(priv->rx.skbs[i]); } } dma_free_coherent(NULL, sizeof(SynopsysDmaTx)*TX_RING_SIZE, priv->rx.ring, priv->rx.phy_addr); }// synop3504_rxdesc_uninit /** * Reset TX frames descriptors. * \param dev device structure. */ static void synop3504_txdesc_reset(struct net_device *dev) { struct net_priv *priv = (struct net_priv *)dev->priv; int i; //Flush DMA descriptors area memset(priv->tx.ring, 0, sizeof(SynopsysDmaTx)*TX_RING_SIZE); //Prepare descriptors for(i=0; itx.ring[i].addr2 = priv->tx.phy_addr + ((i+1)%TX_RING_SIZE)*sizeof(SynopsysDmaTx); priv->tx.ring[i].ctrl.bf.addr2en = 1; if(priv->tx.skbs[i]) { dev_kfree_skb_any(priv->tx.skbs[i]); } priv->tx.skbs[i] = NULL; } }// synop3504_txdesc_reset /** * Reset RX frames descriptors. * \param dev device structure. */ static void synop3504_rxdesc_reset(struct net_device *dev) { struct net_priv *priv = (struct net_priv *)dev->priv; int i; //Only set own bit is enough for(i=0 ; irx.ring[i].status.val = 0; priv->rx.ring[i].status.bf.dma_own = 1; } }// synop3504_rxdesc_reset /** * Read packet status from the device. * \param dev device structure. * \return the device stats. */ static struct net_device_stats *synop3504_stats(struct net_device *dev) { struct net_priv *priv = NULL; //Check pointers if(dev == NULL) return NULL; priv = (struct net_priv *)dev->priv; if(priv == NULL) return NULL; return &priv->stats; }// synop3504_stats /** * Change the MAC address. * \param dev device structure. * \param p mac addr source. * \return error code. */ static int synop3504_set_mac_address(struct net_device *dev, void *p) { struct sockaddr *addr = p; struct net_priv *priv = NULL; Synopsys *synop = NULL; //Check pointers if(dev == NULL) return -1; priv = (struct net_priv *)dev->priv; if(priv == NULL) return -1; synop = &priv->synop; if(synop == NULL) return -1; if(p == NULL) return -1; //Store the new address for Linux memcpy(dev->dev_addr, addr->sa_data, dev->addr_len); //Store the new address for Synopsys hardware SynopsysSetupEthernetAddress(synop, dev->dev_addr); return 0; }// synop3504_set_mac_address /** * This will control promiscuous mode * and multicast mode. * \param dev net device. */ static void synop3504_set_multicast(struct net_device *dev) { struct net_priv *priv = NULL; Synopsys *synop = NULL; //Check pointers priv = (struct net_priv *)dev->priv; if(priv == NULL) return; synop = &priv->synop; if(synop == NULL) return; //Set promiscuous mode if it's asked for. if(dev->flags & IFF_PROMISC) { SynopsysSetPromiscuousMode(synop, 1); return; } else { SynopsysSetPromiscuousMode(synop, 0); return; } //Hardware cannot filter multicast addresses //Just block or unblock all addresses if(dev->flags & IFF_ALLMULTI || dev->mc_count > 0) SynopsysSetMulticastFilter(synop,1); else SynopsysSetMulticastFilter(synop,0); }// synop3504_set_multicast /** * This will control transmission timeout. * \param dev net device. */ #ifndef CONFIG_SYNOP3504_NO_TX_TIMEOUT static void synop3504_tx_timeout(struct net_device *dev) { struct net_priv *priv = (struct net_priv *)dev->priv; Synopsys *synop = &priv->synop; dev->stats.tx_errors++; dev->trans_start = jiffies; printk (KERN_WARNING DRV_NAME" %s: Transmit timed out\n",dev->name); //Reset TX descriptors. SynopsysDisableInt(synop); SynopsysStopTx(synop); synop3504_txdesc_reset(dev); SynopsysStartTx(synop); SynopsysEnableInt(synop); //The ring is empty, the queue can be awaken. netif_wake_queue(dev); }// synop3504_tx_timeout #endif /** * Ethtool device interface. * \param dev device structure. * \param useraddr user data address. * \return error code. */ static int synop3504_ethtool_ioctl(struct net_device *dev, void *useraddr) { #ifndef MODULE uint32_t ethcmd; struct net_priv *priv = (struct net_priv *)dev->priv; //dev_ioctl() in ../../net/core/dev.c has already checked //capable(CAP_NET_ADMIN), so don't bother with that here. if(get_user(ethcmd, (uint32_t *) useraddr)) return -EFAULT; TRACE("%s: ethtool(cmd=%08x)\n", dev->name, ethcmd); switch(ethcmd) { case ETHTOOL_GDRVINFO: { struct ethtool_drvinfo info = { ETHTOOL_GDRVINFO }; strcpy(info.driver, DRV_NAME); strcpy(info.version, DRV_VERSION); if(copy_to_user(useraddr, &info, sizeof(info))) return -EFAULT; return 0; } //Get settings case ETHTOOL_GSET: { struct ethtool_cmd ecmd = { ETHTOOL_GSET }; mii_ethtool_gset(&priv->mii_if, &ecmd); if(copy_to_user(useraddr, &ecmd, sizeof(ecmd))) return -EFAULT; return 0; } //Set settings case ETHTOOL_SSET: { int r; struct ethtool_cmd ecmd; if(copy_from_user(&ecmd, useraddr, sizeof(ecmd))) return -EFAULT; r = mii_ethtool_sset(&priv->mii_if, &ecmd); return r; } //Restart Auto-Negotiation case ETHTOOL_NWAY_RST: { /*TODO return synop3504_autonegotiate(dev, 1);*/ return 0; } //Get link status case ETHTOOL_GLINK: { struct ethtool_value edata = { ETHTOOL_GLINK }; edata.data = mii_link_ok(&priv->mii_if); if(copy_to_user(useraddr, &edata, sizeof(edata))) return -EFAULT; return 0; } case ETHTOOL_RVLAN_TABLE: { struct ethtool_vlanparam ecmd; if (priv->phydev->drv->read_vlan_table (priv->phydev, &ecmd)) return -EFAULT; if(copy_to_user(useraddr, &ecmd, sizeof(ecmd))) return -EFAULT; return 0; } case ETHTOOL_SVLAN_TABLE: { struct ethtool_vlanparam ecmd; if(copy_from_user(&ecmd, useraddr, sizeof(ecmd))) return -EFAULT; if (priv->phydev->drv->write_vlan_table (priv->phydev, &ecmd)) return -EFAULT; return 0; } default: break; } #endif return -EOPNOTSUPP; }// synop3504_ethtool_ioctl /** * User control device interface. * \param dev device structure. * \param ifr user exchange structure. * \param cmd command to execute. * \return error code. */ static int synop3504_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd) { struct net_priv *priv = NULL; //Check pointers if(ifr == NULL) return -1; if(dev == NULL) return -1; //Find the command switch(cmd) { //EthTool Interface case SIOCETHTOOL: { synop3504_ethtool_ioctl(dev, (void *)ifr->ifr_data); break; } case SIOCGMIIPHY: case SIOCGMIIREG: case SIOCSMIIREG: { priv = dev->priv; return generic_mii_ioctl (&priv->mii_if, (struct mii_ioctl_data *) &ifr->ifr_data, cmd, NULL); break; } default: return -EOPNOTSUPP; } return 0; }// synop3504_ioctl /** * transmit frame procedure. * \param skb frame structure. * \param dev device structure. * \return error code. */ static int synop3504_tx(struct sk_buff *skb, struct net_device *dev) { struct net_priv *priv = (struct net_priv*)dev->priv; Synopsys *synop = &priv->synop; struct dma_tx *tx = (struct dma_tx*)&priv->tx; uint32_t n; TRACE("%s: Transmit\n", dev->name); seq_check_tx(&priv->seq_check_ctx, skb); n = tx->head_ptr; //Increase head pointer and check end of ring if(++(tx->head_ptr) >= TX_RING_SIZE) tx->head_ptr = 0; //Check a free descriptor //No free buffer found : DMA use all the ring if(tx->ring[n].status.bf.dma_own) { netif_stop_queue(dev); printk(KERN_WARNING DRV_NAME "%s: TX dropped DMA Queue full\n", dev->name); priv->stats.tx_dropped++; dev_kfree_skb_any(skb); return 0; } //No free buffer found but DMA has just finish some previous transfers //so recover these buffers if(tx->skbs[n] != NULL) { synop3504_tx_done(dev, ONE); netif_stop_queue(dev); } //DMA own the next descriptor: stop TX queue if(tx->ring[tx->head_ptr].status.bf.dma_own) { netif_stop_queue(dev); } tx->skbs[n] = skb; //Set up the buffer descriptors, use only one buffer tx->ring[n].status.val = 0; tx->ring[n].ctrl.bf.first = 1; tx->ring[n].ctrl.bf.last = 1; tx->ring[n].ctrl.bf.int_oncomp = 1; tx->ring[n].ctrl.bf.length1 = skb->len; print_packet("TX", n, skb->len, skb); tx->ring[n].addr1 = dma_map_single(NULL, skb->data, skb->len, DMA_TO_DEVICE); tx->ring[n].status.val = 0; barrier(); tx->ring[n].status.bf.dma_own = 1; //Starting DMA transfert SynopsysRestartTx(synop); //Handle transmit dev->trans_start = jiffies; return 0; }// synop3504_tx /** * receive frame procedure. * \param dev device structure. * \param budget allocated budget. */ static void synop3504_rx(struct net_device *dev, int *budget) { struct sk_buff *new_skb; struct sk_buff *skb; uint32_t rxsize; struct net_priv *priv = (struct net_priv*)dev->priv; struct dma_rx *rx = (struct dma_rx*)&priv->rx; uint32_t n; if(*budget == 0) return; TRACE("%s: Receive Done\n", dev->name); while((!rx->ring[rx->tail_ptr].status.bf.dma_own) && *budget) { n = rx->tail_ptr; //Check errors for the current RX frame if((rx->ring[n].status.bf.err) || (rx->ring[n].status.bf.err_drib)) { //Giant frame, keep it if(rx->ring[n].status.bf.err_ipc_giant) { //Not an error } else { //Overflow error if(rx->ring[n].status.bf.err_over) { priv->stats.rx_fifo_errors++; priv->stats.rx_errors++; } //CRC error if(rx->ring[n].status.bf.err_crc) { priv->stats.rx_crc_errors++; priv->stats.rx_errors++; } //Collision error if(rx->ring[n].status.bf.err_coll) { priv->stats.collisions++; } //Dribble bit error if(rx->ring[n].status.bf.err_drib) { priv->stats.rx_errors++; priv->stats.rx_frame_errors++; } goto reuse_buffer; } } //This buffer doesn't contain all the frame but it's impossible //because jumbo frame isn't activate. So we need to drop it and //further ones until last descriptor. if(!(rx->ring[n].status.bf.first && rx->ring[n].status.bf.last)) { priv->stats.rx_frame_errors++; priv->stats.rx_errors++; goto reuse_buffer; } //Pre-allocate a new buffer if((new_skb = dev_alloc_skb(PKT_BUF_SZ)) == NULL) { //Cannot allocate a new buffer re-use the same and drop the //current frame priv->stats.rx_dropped++; goto reuse_buffer; } skb = rx->skbs[n]; BUG_ON(!skb); dma_unmap_single(NULL, rx->ring[n].addr1, PKT_BUF_SZ, DMA_FROM_DEVICE); rxsize = rx->ring[n].status.bf.length - 4; print_packet("RX", n, rxsize, skb); //Update the skbuff skb_put(skb, rxsize); skb->dev = dev; skb->protocol = eth_type_trans(skb, dev); dev->last_rx = jiffies; seq_check_rx(&priv->seq_check_ctx, skb); //Send RX packet to Linux netif_receive_skb(skb); //Update stats priv->stats.rx_packets++; priv->stats.rx_bytes += rxsize; //Update the current buffer descriptor with new buffer rx->ring[n].addr1 = (uint32_t)dma_map_single(NULL, new_skb->data, PKT_BUF_SZ, DMA_FROM_DEVICE); rx->skbs[n] = new_skb; reuse_buffer: //Reset current descriptor control rx->ring[n].ctrl.val = 0; rx->ring[n].ctrl.bf.addr2en = 1; rx->ring[n].ctrl.bf.length1 = PKT_BUF_SZ; //Reset current descriptor status rx->ring[n].status.val = 0; barrier (); rx->ring[n].status.bf.dma_own = 1; //Increase head pointer and check end of ring if(++(rx->tail_ptr) >= RX_RING_SIZE) rx->tail_ptr = 0; //Decrease budget (*budget)--; } }// synop3504_rx /** * finish the transmit frame procedure. * \param dev device structure. * \param clean how many buffers to clean. */ static void synop3504_tx_done(struct net_device *dev, enum tx_clean_buff clean) { struct net_priv *priv = (struct net_priv*)dev->priv; struct dma_tx *tx = (struct dma_tx*)&priv->tx; uint32_t n; uint32_t free_area = 0; TRACE("%s: Transmit Done (num=%d)\n", dev->name, tx->tail_ptr); while((tx->skbs[tx->tail_ptr] != NULL) && (!tx->ring[tx->tail_ptr].status.bf.dma_own)) { n = tx->tail_ptr; free_area++; TRACE("Found\n"); //Check errors for the current TX frame if(tx->ring[n].status.bf.err) { //Underflow error if(tx->ring[n].status.bf.err_under) { priv->stats.tx_fifo_errors++; priv->stats.tx_errors++; } //Carrier error if(tx->ring[n].status.bf.err_clost || tx->ring[n].status.bf.err_nocar) { priv->stats.tx_carrier_errors++; priv->stats.tx_errors++; } //Collision error if(tx->ring[n].status.bf.err_ecoll || tx->ring[n].status.bf.err_lcoll) { priv->stats.collisions++; } } else { //Update TX normal stats priv->stats.tx_packets++; priv->stats.tx_bytes += tx->skbs[n]->len; } //Buffer no longer used by DMA dma_unmap_single(NULL, tx->ring[n].addr1, tx->skbs[n]->len, DMA_TO_DEVICE); //Update the current buffer descriptor tx->ring[n].ctrl.val = 0; tx->ring[n].ctrl.bf.addr2en = 1; tx->ring[n].addr1 = 0; barrier(); tx->ring[n].status.val = 0; //Freeing skbuff dev_kfree_skb_any(tx->skbs[n]); tx->skbs[n] = NULL; //Increase tail pointer and check end of ring if(++(tx->tail_ptr) >= TX_RING_SIZE) tx->tail_ptr = 0; //Called for only one buffer if(clean == ONE) break; } //At least one free area, re-enable TX queue if(free_area && netif_queue_stopped(dev)) { netif_wake_queue(dev); TRACE("%s: TX queue Waked Up\n", dev->name); } }// synop3504_tx_done /** * Poll frame procedure. * \param napi NAPI structure. * \param budget max count of packets to receive. * \return error code. */ static int synop3504_poll(struct napi_struct *napi, int budget) { struct net_priv *priv = container_of(napi, struct net_priv, napi); struct net_device *dev = (struct net_device*)priv->dev; Synopsys *synop = &priv->synop; SynopsysIntStatus status; int budget_orig = budget; do { #ifdef CONFIG_CHIP_FEATURE_MIU_CTRL //Flush AHB2MIU AHB2MIU_FLUSH_REG_VA = AHB2MIU_BF (FLUSH_ETH1_DMA, 1); while (AHB2MIU_BFEXT (FLUSH_ETH1_DMA, AHB2MIU_FLUSH_REG_VA)) ; #endif //Get IRQ status SynopsysGetIntStatus(synop, &status); //Suppress rx, tx states and error bits (not needed in this function) status.bf.rxState = 0; status.bf.txState = 0; status.bf.errorBits = 0; TRACE("%s: IRQ (status=0x%x) ; budget=%d\n", dev->name, status.val, budget); if(status.val == 0) break; //Normal Interrupt //TX completed if(status.bf.intTxCompleted) { synop3504_tx_done(dev, ALL); } //RX completed if(status.bf.intRxCompleted) { synop3504_rx(dev, &budget); } //AbNormal interrupt //TX enter in stopped state if(status.bf.intTxStopped) { netif_stop_queue(dev); synop3504_txdesc_reset(dev); TRACE("%s: TX queue Stopped\n", dev->name); } //TX underflow if(status.bf.intTxUnderflow) { //Remove old buffers from TX descriptors synop3504_tx_done(dev, ALL); printk(KERN_ERR DRV_NAME ": %s: TX FIFO Error\n", dev->name); } //RX FIFO full if(status.bf.intRxOverflow) { synop3504_rx(dev, &budget); TRACE ("%s: RX FIFO overflow\n", dev->name); } //RX queue nearly full if(status.bf.intRxNoBuffer) { //Refresh the rx dma SynopsysRestartRx(synop); TRACE("%s: RX queue nearly Full\n", dev->name); } //Bus error if(status.bf.intBusError) { printk(KERN_ERR DRV_NAME ": %s: Fatal BUS error (0x%x)\n", dev->name, status.bf.errorBits); } } while(budget > 0); if(status.val == 0) { netif_rx_complete(dev, &priv->napi); //Enable interrupt SynopsysEnableInt(synop); } return budget_orig - budget; }// synop3504_poll /** * Receive frame procedure. * \param irq interrupt number. * \param dev device structure. * \param regs not used. * \return error code. */ static irqreturn_t synop3504_interrupt(int irq, void * dev_id) { struct net_device *dev = (struct net_device*)dev_id; struct net_priv *priv = NULL; Synopsys *synop; //Check pointer if(dev == NULL) return IRQ_NONE; priv = (struct net_priv *)dev->priv; if(priv == NULL) return IRQ_NONE; synop = &priv->synop; if(synop == NULL) return IRQ_NONE; //Disable interrupt SynopsysDisableInt(synop); //Prepare polling if(netif_rx_schedule_prep(dev, &priv->napi)) { __netif_rx_schedule(dev, &priv->napi); } else { printk(KERN_ERR DRV_NAME ": %s: ERROR interrupt while in poll\n", dev->name); } return IRQ_HANDLED; }// synop3504_interrupt /** * Read a MII register. * \param bus mii_bus structure. * \param phy_id phy addr (not used for us). * \param regnum register to write. * \return value read. */ static int mdiobus_read(struct mii_bus *bus, int phy_id, int regnum) { return mdio_read(((struct net_priv *)(bus->priv))->dev, phy_id, regnum); } /** * Write a MII register. * \param bus mii_bus structure. * \param phy_id phy addr (not used for us). * \param regnum register to write. * \param val value to write. */ static int mdiobus_write(struct mii_bus *bus, int phy_id, int regnum, u16 val) { mdio_write(((struct net_priv *)(bus->priv))->dev, phy_id, regnum, val); return 0; } /** * Read the PHY MII registers by /proc. * * \param file file structure. * \param buffer string pointer given by user. * \param start string pointer begin. * \param offset offset value. * \param count count parameter. * \param eof end of file. * \param data network device structure. * \return new pointer position. */ static int synop3504_readproc_mii(char *buf, char **start, off_t offset, int count, int *eof, void *data) { struct phy_device *phydev = (struct phy_device *)data; int reg; uint16_t val; char *p; p = buf; for(reg=0 ; reg<=31 ; reg++) { val = mdiobus_read(phydev->bus, phydev->addr, reg); p += sprintf(p, "PHY%d REG%-2d 0x%04x\n", phydev->addr, reg, val); } *eof = 1; return p-buf+1; } /** * Write into PHY MII registers by /proc. * * \param file file structure. * \param buffer string pointer given by user. * \param count count parameter. * \param data network device structure. * \return counter value. */ static int synop3504_writeproc_mii(struct file *file, const char *buffer, unsigned long count, void *data) { struct phy_device *phydev = (struct phy_device *)data; unsigned int reg; int value; if(sscanf(buffer,"REG%u %i", ®, &value) == 2) { mdiobus_write(phydev->bus, phydev->addr, reg, value); printk("Sent PHY%d REG%d 0x%04x\n", phydev->addr, reg, value); } return count; } /** * Link change callback. * \param dev device structure. */ static void synop3504_handle_link_change(struct net_device *dev) { struct net_priv *priv = NULL; Synopsys *synop = NULL; struct phy_device *phydev = NULL; int status_change = 0; //Check pointers if(dev == NULL) return; priv = netdev_priv(dev); if(priv == NULL) return; synop = &priv->synop; if(synop == NULL) return; phydev = priv->phydev; if (phydev == NULL) return; if (phydev->link) { if ((priv->speed != phydev->speed) || (priv->duplex != phydev->duplex)) { SynopsysSetMiiClkCap(synop, phydev->speed); SynopsysSetSpeedDuplex(synop, phydev->speed, phydev->duplex); priv->mii_if.full_duplex = phydev->duplex; priv->speed = phydev->speed; priv->duplex = phydev->duplex; status_change = 1; } } //link change if (phydev->link != priv->link) { if (!phydev->link) { //Force PHY communication mode to GMII if allowed SynopsysSetMiiClkCap(synop, 1000); priv->speed = 0; priv->duplex = -1; } priv->link = phydev->link; status_change = 1; } if (status_change) { if (phydev->link) printk(KERN_ERR DRV_NAME": %s: link up (%d/%s)\n", dev->name, phydev->speed, DUPLEX_FULL == phydev->duplex ? "Full":"Half"); else printk(KERN_ERR DRV_NAME": %s: link down\n", dev->name); } } /** * Connect the PHY * \param dev device structure. * \return error code. */ static int synop3504_mii_probe(struct net_device *dev) { struct net_priv *priv = netdev_priv(dev); struct phy_device *phydev = NULL; struct synop3504_platform_data *pdata; // Get phy addr from NVRAM data pdata = priv->pdev->dev.platform_data; phydev = priv->mii_bus->phy_map[pdata->phy_addr]; if (!phydev) { printk (KERN_ERR "%s: no PHY found\n", dev->name); return -1; } // attach the mac to the phy if (pdata && pdata->support_gmii) phydev = phy_connect(dev, phydev->dev.bus_id, &synop3504_handle_link_change, 0, PHY_INTERFACE_MODE_GMII); else if (pdata && pdata->support_rgmii) phydev = phy_connect(dev, phydev->dev.bus_id, &synop3504_handle_link_change, 0, PHY_INTERFACE_MODE_RGMII); else if (pdata && pdata->support_rmii) phydev = phy_connect(dev, phydev->dev.bus_id, &synop3504_handle_link_change, 0, PHY_INTERFACE_MODE_RMII); else phydev = phy_connect(dev, phydev->dev.bus_id, &synop3504_handle_link_change, 0, PHY_INTERFACE_MODE_MII); if (IS_ERR(phydev)) { printk(KERN_ERR "%s: Could not attach to PHY\n", dev->name); return PTR_ERR(phydev); } // mask with MAC supported features phydev->supported &= PHY_GBIT_FEATURES; phydev->advertising = phydev->supported; #ifdef CONFIG_SYNOP3504_NO_GIGABIT phydev->advertising &= ~(SUPPORTED_1000baseT_Half | SUPPORTED_1000baseT_Full); #endif priv->link = 0; priv->speed = 0; priv->duplex = -1; priv->phydev = phydev; return 0; } /** * Initialize the MDIO Bus. * \param dev device structure. * \return error code. */ static int synop3504_mii_init(struct net_device *dev) { struct net_priv *priv = netdev_priv(dev); int err = -ENXIO, i; if (NULL == priv) return -EINVAL; priv->mii_bus = kzalloc(sizeof(*(priv->mii_bus)), GFP_KERNEL); if (NULL == priv->mii_bus) { err = -ENOMEM; goto err_out; } priv->mii_bus->name = "SYNOP3504 MII Bus"; priv->mii_bus->read = &mdiobus_read; priv->mii_bus->write = &mdiobus_write; priv->mii_bus->id = priv->pdev->id; priv->mii_bus->priv = priv; priv->mii_bus->dev = &priv->dev->dev; priv->mii_bus->irq = kmalloc(sizeof(int)*PHY_MAX_ADDR, GFP_KERNEL); if (NULL == priv->mii_bus->irq) { err = -ENOMEM; goto err_out_free_mdio_bus; } for(i = 0; i < PHY_MAX_ADDR; ++i) priv->mii_bus->irq[i] = PHY_POLL; platform_set_drvdata(priv->dev, &priv->mii_bus); if (mdiobus_register(priv->mii_bus)) goto err_out_free_mdio_irq; if (synop3504_mii_probe(priv->dev) != 0) goto err_out_unregister_bus; return 0; err_out_unregister_bus: mdiobus_unregister(priv->mii_bus); err_out_free_mdio_irq: kfree(priv->mii_bus->irq); err_out_free_mdio_bus: kfree(priv->mii_bus); err_out: return err; } /** * Initialize the device. * \param dev device structure. * \return error code. */ static int synop3504_open(struct net_device *dev) { struct net_priv *priv = NULL; Synopsys *synop = NULL; //Check pointers if(dev == NULL) return -1; priv = (struct net_priv *)dev->priv; if(priv == NULL) return -1; synop = &priv->synop; if(synop == NULL) return -1; if (!priv->phydev) return -EAGAIN; TRACE("%s: open\n", dev->name); //Reset synopsys hardware SynopsysReset(synop); //Reset PHY mdio_write(dev, priv->phy_addr, MII_BMCR, BMCR_RESET); while(mdio_read(dev, priv->phy_addr, MII_BMCR) & BMCR_RESET); #ifdef CONFIG_SYNOP3504_PHY_DINI //The PHY used on MSE500 DINI 300 mode is VSC8601. Its phy_id is 0x70421. BUG_ON(priv->phydev->phy_id != 0x70421); //Add a 2ns delay for TX and RX clock. //In register 23 "Extended Phy Control 1", enable "RGMII skew timing //compensation". mdio_write(dev, priv->phy_addr, 23, 0x0100); //To access the extended register 28E, put 1 in register 31 "Extended Page //Access". mdio_write(dev, priv->phy_addr, 31, 0x0001); //In register 28E "RGMII Skew Control", set both TX and RX RGMII Skew //compensation to 2ns. mdio_write(dev, priv->phy_addr, 28, 0xf000); //Switch back to main register space, by putting 0 in register 31 "Extended //Page Access". mdio_write(dev, priv->phy_addr, 31, 0x0000); #endif /* CONFIG_SYNOP3504_PHY_DINI */ //Initialise DMA descriptors synop3504_txdesc_init(dev); synop3504_rxdesc_init(dev); //Initialise synopsys hardware SynopsysInit(synop, priv->tx.phy_addr, priv->rx.phy_addr); //Set MAC address to synopsys hardware SynopsysSetupEthernetAddress(synop, dev->dev_addr); //Force PHY communication mode to GMII if allowed SynopsysSetMiiClkCap(synop, 1000); //Request irq if(request_irq(dev->irq, synop3504_interrupt, 0, dev->name, dev) != 0) { printk(KERN_ERR DRV_NAME ": %s - interrupt %d request fail\n", dev->name, dev->irq); return -ENODEV; } //Start NAPI napi_enable(&priv->napi); //Start TX and RX DMA SynopsysStartTx(synop); SynopsysStartRx(synop); //Enable Interrupts SynopsysEnableInt(synop); /* schedule a link state check */ phy_start(priv->phydev); netif_start_queue(dev); return 0; }// synop3504_open /** * Uninitialize the device. * \param dev device structure. * \return error code. */ static int synop3504_stop(struct net_device *dev) { struct net_priv *priv = NULL; Synopsys *synop = NULL; //Check pointers if(dev == NULL) return -1; priv = (struct net_priv *)dev->priv; if(priv == NULL) return -1; synop = &priv->synop; if(synop == NULL) return -1; TRACE("%s: stop\n", dev->name); //Disable transmitter netif_stop_queue(dev); //Stop NAPI napi_disable(&priv->napi); //Stop PHY if (priv->phydev) phy_stop(priv->phydev); //Disable Interrupts SynopsysDisableInt(synop); //Stop RX and TX DMAs SynopsysStopRx(synop); SynopsysStopTx(synop); //Disconnect from IRQ free_irq(dev->irq, dev); //Freeing descriptors synop3504_txdesc_uninit(dev); synop3504_rxdesc_uninit(dev); return 0; }// synop3504_stop /** * Initialise the network device. * \param dev device structure. * \return error code. */ static int synop3504_init(struct net_device *dev) { struct net_priv *priv = NULL; //Check pointers if(dev == NULL) return -1; priv = (struct net_priv *)dev->priv; if(priv == NULL) return -1; //Attach hardware layer addresses if(priv->support_gmii) SynopsysAttach(&priv->synop, priv->gmac_addr, priv->dma_addr, priv->mac_id, IS_GMII); else if(priv->support_rgmii) SynopsysAttach(&priv->synop, priv->gmac_addr, priv->dma_addr, priv->mac_id, IS_RGMII); else if(priv->support_rmii) SynopsysAttach(&priv->synop, priv->gmac_addr, priv->dma_addr, priv->mac_id, IS_RMII); else SynopsysAttach(&priv->synop, priv->gmac_addr, priv->dma_addr, priv->mac_id, IS_MII); //Initialise device functions ether_setup(dev); dev->open = synop3504_open; dev->stop = synop3504_stop; dev->do_ioctl = synop3504_ioctl; dev->ethtool_ops = &synop3504_ethtool_ops; dev->set_mac_address = synop3504_set_mac_address; dev->hard_start_xmit = synop3504_tx; dev->get_stats = synop3504_stats; dev->set_multicast_list = synop3504_set_multicast; #ifndef CONFIG_SYNOP3504_NO_TX_TIMEOUT dev->tx_timeout = synop3504_tx_timeout; dev->watchdog_timeo = TX_TIMEOUT; #endif //Set NAPI mode netif_napi_add(dev, &priv->napi, &synop3504_poll, NAPI_BUDGET); //Check MAC address for Linux if(!is_valid_ether_addr(dev->dev_addr)) { printk(KERN_ERR DRV_NAME": %s: Error with MAC address %02X:%02X:%02X:%02X:%02X:%02X\n", dev->name, dev->dev_addr[0], dev->dev_addr[1], dev->dev_addr[2], dev->dev_addr[3], dev->dev_addr[4], dev->dev_addr[5]); return -1; } //MII initialisation for mdio dialog by Linux APIs priv->mii_if.dev = dev; priv->mii_if.mdio_read = mdio_read; priv->mii_if.mdio_write = mdio_write; priv->mii_if.phy_id = priv->phy_addr; priv->mii_if.supports_gmii = priv->support_gmii | priv->support_rgmii; priv->mii_if.phy_id_mask = 0x1F; priv->mii_if.reg_num_mask = 0x1F; return 0; }// synop3504_init #ifdef CONFIG_PM /** * Suspend Synop3504 interface. * \param pdev platform device * \param state target suspend state * \return error code */ static int synop3504_suspend(struct platform_device *pdev, pm_message_t mesg) { struct net_device *dev = platform_get_drvdata(pdev); struct net_priv *priv = NULL; Synopsys *synop = NULL; priv = netdev_priv(dev); if(priv == NULL) return -1; synop = &priv->synop; if(synop == NULL) return -1; netif_device_detach(dev); SynopsysSuspendClk(synop); return 0; } /** * Resume Synop3504 interface. * \param pdev platform device * \return error code */ static int synop3504_resume(struct platform_device *pdev) { struct net_device *dev = platform_get_drvdata(pdev); struct net_priv *priv = NULL; Synopsys *synop = NULL; struct phy_device *phydev = NULL; priv = netdev_priv(dev); if(priv == NULL) return -1; synop = &priv->synop; if(synop == NULL) return -1; phydev = priv->phydev; if (phydev == NULL) return -1; SynopsysResumeClk(synop); SynopsysSetMiiClkCap(synop, phydev->speed); netif_device_attach(dev); return 0; } #endif /* CONFIG_PM */ /** * Number of devices registered */ static unsigned char device_cnt = 0; /** * Handle to the procfs entry */ static struct proc_dir_entry *phy_parent_dir; /** * Initialise the platform device. * \param pdev platform device structure. * \return error code. */ static int __init synop3504_module_probe(struct platform_device *pdev) { struct resource *gmac_regs, *dma_regs; uint32_t gmac_addr, dma_addr; int irq; int result; struct net_device *dev = NULL; struct net_priv *priv; struct synop3504_platform_data *pdata; struct proc_dir_entry *entry; struct proc_dir_entry *phy_dir; int i; char buf[10]; char dirname[12]; printk("%s", version); //Restore Registers base address gmac_regs = platform_get_resource(pdev, IORESOURCE_IO, 0); if(!gmac_regs) { result = -ENXIO; goto err_out; } gmac_addr = (uint32_t)ioremap(gmac_regs->start, gmac_regs->end - gmac_regs->start + 1); if(!gmac_addr) { result = -ENOMEM; goto err_out; } dma_regs = platform_get_resource(pdev, IORESOURCE_IO, 1); if(!dma_regs) { result = -ENXIO; goto err_out_remap_gmac; } dma_addr = (uint32_t)ioremap(dma_regs->start, dma_regs->end - dma_regs->start + 1); if(!dma_addr) { result = -ENOMEM; goto err_out_remap_gmac; } //Restore IRQ number irq = platform_get_irq(pdev, 0); if(irq < 0) { result = irq; goto err_out_remap_dma; } //Restore Platform datas pdata = pdev->dev.platform_data; //Allocate device memory dev = alloc_etherdev(sizeof(struct net_priv)); if(dev == NULL) { result = -ENOMEM; goto err_out_remap_dma; } //Fill net device structure SET_NETDEV_DEV(dev, &pdev->dev); priv = netdev_priv(dev); memset(priv, 0, sizeof(struct net_priv)); priv->pdev = pdev; priv->dev = dev; priv->gmac_addr = gmac_addr; priv->dma_addr = dma_addr; dev->irq = irq; dev->base_addr = gmac_regs->start; dev->dev_addr[0] = pdata->mac_addr[0]; dev->dev_addr[1] = pdata->mac_addr[1]; dev->dev_addr[2] = pdata->mac_addr[2]; dev->dev_addr[3] = pdata->mac_addr[3]; dev->dev_addr[4] = pdata->mac_addr[4]; dev->dev_addr[5] = pdata->mac_addr[5]; priv->support_rgmii = pdata->support_rgmii; priv->support_gmii = pdata->support_gmii; priv->support_rmii = pdata->support_rmii; priv->phy_addr = pdata->phy_addr; priv->mac_id = pdata->mac_id; seq_check_init(&priv->seq_check_ctx, "eth_drv"); //Proceed the init driver result = synop3504_init(dev); if(result < 0) { printk(KERN_ERR DRV_NAME": Error %i initializing driver\n", result); goto err_out_free_dev; } //Register net device result = register_netdev(dev); if(result < 0) { printk(KERN_ERR DRV_NAME": Error %i registering driver\n", result); goto err_out_free_dev; } if (synop3504_mii_init(dev) != 0) { goto err_out_unregister_netdev; } if (!device_cnt) { //Create a proc entry for MII phy_parent_dir = proc_mkdir("phy", init_net.proc_net); } sprintf(dirname, "synop3504.%d", pdev->id); phy_dir = proc_mkdir(dirname, phy_parent_dir); for(i = 0; i < PHY_MAX_ADDR; i++) { if (priv->mii_bus->phy_map[i]) { sprintf(buf, "phy%d", i); entry = create_proc_entry(buf, 0, phy_dir); entry->read_proc = synop3504_readproc_mii; entry->write_proc = synop3504_writeproc_mii; entry->data = (int*)(priv->mii_bus->phy_map[i]); } } device_cnt++; platform_set_drvdata(pdev, dev); return 0; err_out_unregister_netdev: unregister_netdev(dev); err_out_free_dev: free_netdev(dev); err_out_remap_dma: iounmap((void __iomem*)dma_addr); err_out_remap_gmac: iounmap((void __iomem*)gmac_addr); err_out: platform_set_drvdata(pdev, dev); return result; }// synop3504_module_probe /** * Uninitialise the platform device. */ static void __exit synop3504_module_remove(struct platform_device *pdev) { struct net_device *dev; struct net_priv *priv; int i; char buf[18]; char dirname[12]; dev = platform_get_drvdata(pdev); if(dev) { //Freeing private field of the net device structure priv = netdev_priv(dev); if (priv->phydev) phy_disconnect(priv->phydev); mdiobus_unregister(priv->mii_bus); kfree(priv->mii_bus->irq); kfree(priv->mii_bus); if(priv) kfree(priv); //Unregister net device unregister_netdev(dev); //Freeing network device free_netdev(dev); //Remove proc sprintf(dirname, "synop3504.%d", pdev->id); for(i = 0; i < PHY_MAX_ADDR; i++) { sprintf(buf, "synop3504.%d/phy%d", pdev->id, i); remove_proc_entry(buf, phy_parent_dir); } remove_proc_entry(dirname, phy_parent_dir); //Erase driver data informations platform_set_drvdata(pdev, NULL); } device_cnt--; if (!device_cnt) { remove_proc_entry("phy", init_net.proc_net); } }// synop3504_module_remove /** Module structure */ static struct platform_driver synop3504_eth_driver = { .driver = { .name = "synopsys3504", .owner = THIS_MODULE, }, #ifdef CONFIG_PM .suspend = synop3504_suspend, .resume = synop3504_resume, #endif .remove = __exit_p(synop3504_module_remove), }; /** * Module initialization. * \return error code. */ static int __init synop3504_module_init(void) { return platform_driver_probe(&synop3504_eth_driver, synop3504_module_probe); }// synop3504_module_init /** * Module uninitialization. */ static void __exit synop3504_module_exit(void) { platform_driver_unregister(&synop3504_eth_driver); }// synop3504_module_exit module_init(synop3504_module_init); module_exit(synop3504_module_exit); MODULE_AUTHOR ("SPiDCOM Technologies"); MODULE_DESCRIPTION ("Synopsys 3504 Ethernet driver"); MODULE_LICENSE ("GPL");