📄 smc911x.c
字号:
PRINTK("%s: %s timeout waiting for EEPROM CMD not busy\n", dev->name, __FUNCTION__); return -ETIMEDOUT; } return 0;}static inline int smc911x_ethtool_write_eeprom_cmd(struct net_device *dev, int cmd, int addr){ unsigned long ioaddr = dev->base_addr; int ret; if ((ret = smc911x_ethtool_wait_eeprom_ready(dev))!=0) return ret; SMC_SET_E2P_CMD(E2P_CMD_EPC_BUSY_ | ((cmd) & (0x7<<28)) | ((addr) & 0xFF)); return 0;}static inline int smc911x_ethtool_read_eeprom_byte(struct net_device *dev, u8 *data){ unsigned long ioaddr = dev->base_addr; int ret; if ((ret = smc911x_ethtool_wait_eeprom_ready(dev))!=0) return ret; *data = SMC_GET_E2P_DATA(); return 0;}static inline int smc911x_ethtool_write_eeprom_byte(struct net_device *dev, u8 data){ unsigned long ioaddr = dev->base_addr; int ret; if ((ret = smc911x_ethtool_wait_eeprom_ready(dev))!=0) return ret; SMC_SET_E2P_DATA(data); return 0;}static int smc911x_ethtool_geteeprom(struct net_device *dev, struct ethtool_eeprom *eeprom, u8 *data){ u8 eebuf[SMC911X_EEPROM_LEN]; int i, ret; for(i=0;i<SMC911X_EEPROM_LEN;i++) { if ((ret=smc911x_ethtool_write_eeprom_cmd(dev, E2P_CMD_EPC_CMD_READ_, i ))!=0) return ret; if ((ret=smc911x_ethtool_read_eeprom_byte(dev, &eebuf[i]))!=0) return ret; } memcpy(data, eebuf+eeprom->offset, eeprom->len); return 0;}static int smc911x_ethtool_seteeprom(struct net_device *dev, struct ethtool_eeprom *eeprom, u8 *data){ int i, ret; /* Enable erase */ if ((ret=smc911x_ethtool_write_eeprom_cmd(dev, E2P_CMD_EPC_CMD_EWEN_, 0 ))!=0) return ret; for(i=eeprom->offset;i<(eeprom->offset+eeprom->len);i++) { /* erase byte */ if ((ret=smc911x_ethtool_write_eeprom_cmd(dev, E2P_CMD_EPC_CMD_ERASE_, i ))!=0) return ret; /* write byte */ if ((ret=smc911x_ethtool_write_eeprom_byte(dev, *data))!=0) return ret; if ((ret=smc911x_ethtool_write_eeprom_cmd(dev, E2P_CMD_EPC_CMD_WRITE_, i ))!=0) return ret; } return 0;}static int smc911x_ethtool_geteeprom_len(struct net_device *dev){ return SMC911X_EEPROM_LEN;}static const struct ethtool_ops smc911x_ethtool_ops = { .get_settings = smc911x_ethtool_getsettings, .set_settings = smc911x_ethtool_setsettings, .get_drvinfo = smc911x_ethtool_getdrvinfo, .get_msglevel = smc911x_ethtool_getmsglevel, .set_msglevel = smc911x_ethtool_setmsglevel, .nway_reset = smc911x_ethtool_nwayreset, .get_link = ethtool_op_get_link, .get_regs_len = smc911x_ethtool_getregslen, .get_regs = smc911x_ethtool_getregs, .get_eeprom_len = smc911x_ethtool_geteeprom_len, .get_eeprom = smc911x_ethtool_geteeprom, .set_eeprom = smc911x_ethtool_seteeprom,};/* * smc911x_findirq * * This routine has a simple purpose -- make the SMC chip generate an * interrupt, so an auto-detect routine can detect it, and find the IRQ, */static int __init smc911x_findirq(unsigned long ioaddr){ int timeout = 20; unsigned long cookie; DBG(SMC_DEBUG_FUNC, "--> %s\n", __FUNCTION__); cookie = probe_irq_on(); /* * Force a SW interrupt */ SMC_SET_INT_EN(INT_EN_SW_INT_EN_); /* * Wait until positive that the interrupt has been generated */ do { int int_status; udelay(10); int_status = SMC_GET_INT_EN(); if (int_status & INT_EN_SW_INT_EN_) break; /* got the interrupt */ } while (--timeout); /* * there is really nothing that I can do here if timeout fails, * as autoirq_report will return a 0 anyway, which is what I * want in this case. Plus, the clean up is needed in both * cases. */ /* and disable all interrupts again */ SMC_SET_INT_EN(0); /* and return what I found */ return probe_irq_off(cookie);}/* * Function: smc911x_probe(unsigned long ioaddr) * * Purpose: * Tests to see if a given ioaddr points to an SMC911x chip. * Returns a 0 on success * * Algorithm: * (1) see if the endian word is OK * (1) see if I recognize the chip ID in the appropriate register * * Here I do typical initialization tasks. * * o Initialize the structure if needed * o print out my vanity message if not done so already * o print out what type of hardware is detected * o print out the ethernet address * o find the IRQ * o set up my private data * o configure the dev structure with my subroutines * o actually GRAB the irq. * o GRAB the region */static int __init smc911x_probe(struct net_device *dev, unsigned long ioaddr){ struct smc911x_local *lp = netdev_priv(dev); int i, retval; unsigned int val, chip_id, revision; const char *version_string; DBG(SMC_DEBUG_FUNC, "%s: --> %s\n", dev->name, __FUNCTION__); /* First, see if the endian word is recognized */ val = SMC_GET_BYTE_TEST(); DBG(SMC_DEBUG_MISC, "%s: endian probe returned 0x%04x\n", CARDNAME, val); if (val != 0x87654321) { printk(KERN_ERR "Invalid chip endian 0x08%x\n",val); retval = -ENODEV; goto err_out; } /* * check if the revision register is something that I * recognize. These might need to be added to later, * as future revisions could be added. */ chip_id = SMC_GET_PN(); DBG(SMC_DEBUG_MISC, "%s: id probe returned 0x%04x\n", CARDNAME, chip_id); for(i=0;chip_ids[i].id != 0; i++) { if (chip_ids[i].id == chip_id) break; } if (!chip_ids[i].id) { printk(KERN_ERR "Unknown chip ID %04x\n", chip_id); retval = -ENODEV; goto err_out; } version_string = chip_ids[i].name; revision = SMC_GET_REV(); DBG(SMC_DEBUG_MISC, "%s: revision = 0x%04x\n", CARDNAME, revision); /* At this point I'll assume that the chip is an SMC911x. */ DBG(SMC_DEBUG_MISC, "%s: Found a %s\n", CARDNAME, chip_ids[i].name); /* Validate the TX FIFO size requested */ if ((tx_fifo_kb < 2) || (tx_fifo_kb > 14)) { printk(KERN_ERR "Invalid TX FIFO size requested %d\n", tx_fifo_kb); retval = -EINVAL; goto err_out; } /* fill in some of the fields */ dev->base_addr = ioaddr; lp->version = chip_ids[i].id; lp->revision = revision; lp->tx_fifo_kb = tx_fifo_kb; /* Reverse calculate the RX FIFO size from the TX */ lp->tx_fifo_size=(lp->tx_fifo_kb<<10) - 512; lp->rx_fifo_size= ((0x4000 - 512 - lp->tx_fifo_size) / 16) * 15; /* Set the automatic flow control values */ switch(lp->tx_fifo_kb) { /* * AFC_HI is about ((Rx Data Fifo Size)*2/3)/64 * AFC_LO is AFC_HI/2 * BACK_DUR is about 5uS*(AFC_LO) rounded down */ case 2:/* 13440 Rx Data Fifo Size */ lp->afc_cfg=0x008C46AF;break; case 3:/* 12480 Rx Data Fifo Size */ lp->afc_cfg=0x0082419F;break; case 4:/* 11520 Rx Data Fifo Size */ lp->afc_cfg=0x00783C9F;break; case 5:/* 10560 Rx Data Fifo Size */ lp->afc_cfg=0x006E374F;break; case 6:/* 9600 Rx Data Fifo Size */ lp->afc_cfg=0x0064328F;break; case 7:/* 8640 Rx Data Fifo Size */ lp->afc_cfg=0x005A2D7F;break; case 8:/* 7680 Rx Data Fifo Size */ lp->afc_cfg=0x0050287F;break; case 9:/* 6720 Rx Data Fifo Size */ lp->afc_cfg=0x0046236F;break; case 10:/* 5760 Rx Data Fifo Size */ lp->afc_cfg=0x003C1E6F;break; case 11:/* 4800 Rx Data Fifo Size */ lp->afc_cfg=0x0032195F;break; /* * AFC_HI is ~1520 bytes less than RX Data Fifo Size * AFC_LO is AFC_HI/2 * BACK_DUR is about 5uS*(AFC_LO) rounded down */ case 12:/* 3840 Rx Data Fifo Size */ lp->afc_cfg=0x0024124F;break; case 13:/* 2880 Rx Data Fifo Size */ lp->afc_cfg=0x0015073F;break; case 14:/* 1920 Rx Data Fifo Size */ lp->afc_cfg=0x0006032F;break; default: PRINTK("%s: ERROR -- no AFC_CFG setting found", dev->name); break; } DBG(SMC_DEBUG_MISC | SMC_DEBUG_TX | SMC_DEBUG_RX, "%s: tx_fifo %d rx_fifo %d afc_cfg 0x%08x\n", CARDNAME, lp->tx_fifo_size, lp->rx_fifo_size, lp->afc_cfg); spin_lock_init(&lp->lock); /* Get the MAC address */ SMC_GET_MAC_ADDR(dev->dev_addr); /* now, reset the chip, and put it into a known state */ smc911x_reset(dev); /* * If dev->irq is 0, then the device has to be banged on to see * what the IRQ is. * * Specifying an IRQ is done with the assumption that the user knows * what (s)he is doing. No checking is done!!!! */ if (dev->irq < 1) { int trials; trials = 3; while (trials--) { dev->irq = smc911x_findirq(ioaddr); if (dev->irq) break; /* kick the card and try again */ smc911x_reset(dev); } } if (dev->irq == 0) { printk("%s: Couldn't autodetect your IRQ. Use irq=xx.\n", dev->name); retval = -ENODEV; goto err_out; } dev->irq = irq_canonicalize(dev->irq); /* Fill in the fields of the device structure with ethernet values. */ ether_setup(dev); dev->open = smc911x_open; dev->stop = smc911x_close; dev->hard_start_xmit = smc911x_hard_start_xmit; dev->tx_timeout = smc911x_timeout; dev->watchdog_timeo = msecs_to_jiffies(watchdog); dev->set_multicast_list = smc911x_set_multicast_list; dev->ethtool_ops = &smc911x_ethtool_ops;#ifdef CONFIG_NET_POLL_CONTROLLER dev->poll_controller = smc911x_poll_controller;#endif INIT_WORK(&lp->phy_configure, smc911x_phy_configure); lp->mii.phy_id_mask = 0x1f; lp->mii.reg_num_mask = 0x1f; lp->mii.force_media = 0; lp->mii.full_duplex = 0; lp->mii.dev = dev; lp->mii.mdio_read = smc911x_phy_read; lp->mii.mdio_write = smc911x_phy_write; /* * Locate the phy, if any. */ smc911x_phy_detect(dev); /* Set default parameters */ lp->msg_enable = NETIF_MSG_LINK; lp->ctl_rfduplx = 1; lp->ctl_rspeed = 100; /* Grab the IRQ */ retval = request_irq(dev->irq, &smc911x_interrupt, IRQF_SHARED | SMC_IRQ_SENSE, dev->name, dev); if (retval) goto err_out;#ifdef SMC_USE_DMA lp->rxdma = SMC_DMA_REQUEST(dev, smc911x_rx_dma_irq); lp->txdma = SMC_DMA_REQUEST(dev, smc911x_tx_dma_irq); lp->rxdma_active = 0; lp->txdma_active = 0; dev->dma = lp->rxdma;#endif retval = register_netdev(dev); if (retval == 0) { /* now, print out the card info, in a short format.. */ printk("%s: %s (rev %d) at %#lx IRQ %d", dev->name, version_string, lp->revision, dev->base_addr, dev->irq);#ifdef SMC_USE_DMA if (lp->rxdma != -1) printk(" RXDMA %d ", lp->rxdma); if (lp->txdma != -1) printk("TXDMA %d", lp->txdma);#endif printk("\n"); if (!is_valid_ether_addr(dev->dev_addr)) { printk("%s: Invalid ethernet MAC address. Please " "set using ifconfig\n", dev->name); } else { /* Print the Ethernet address */ printk("%s: Ethernet addr: ", dev->name); for (i = 0; i < 5; i++) printk("%2.2x:", dev->dev_addr[i]); printk("%2.2x\n", dev->dev_addr[5]); } if (lp->phy_type == 0) { PRINTK("%s: No PHY found\n", dev->name); } else if ((lp->phy_type & ~0xff) == LAN911X_INTERNAL_PHY_ID) { PRINTK("%s: LAN911x Internal PHY\n", dev->name); } else { PRINTK("%s: External PHY 0x%08x\n", dev->name, lp->phy_type); } }err_out:#ifdef SMC_USE_DMA if (retval) { if (lp->rxdma != -1) { SMC_DMA_FREE(dev, lp->rxdma); } if (lp->txdma != -1) { SMC_DMA_FREE(dev, lp->txdma); } }#endif return retval;}/* * smc911x_init(void) * * Output: * 0 --> there is a device * anything else, error */static int smc911x_drv_probe(struct platform_device *pdev){ struct net_device *ndev; struct resource *res; struct smc911x_local *lp; unsigned int *addr; int ret; DBG(SMC_DEBUG_FUNC, "--> %s\n", __FUNCTION__); res = platform_get_resource(pdev, IORESOURCE_MEM, 0); if (!res) { ret = -ENODEV; goto out; } /* * Request the regions. */ if (!request_mem_region(res->start, SMC911X_IO_EXTENT, CARDNAME)) { ret = -EBUSY; goto out; } ndev = alloc_etherdev(sizeof(struct smc911x_local)); if (!ndev) { printk("%s: could not allocate device.\n", CARDNAME); ret = -ENOMEM; goto release_1; } SET_NETDEV_DEV(ndev, &pdev->dev); ndev->dma = (unsigned char)-1; ndev->irq = platform_get_irq(pdev, 0); lp = netdev_priv(ndev); lp->netdev = ndev; addr = ioremap(res->start, SMC911X_IO_EXTENT); if (!addr) { ret = -ENOMEM; goto release_both; } platform_set_drvdata(pdev, ndev); ret = smc911x_probe(ndev, (unsigned long)addr); if (ret != 0) { platform_set_drvdata(pdev, NULL); iounmap(addr);release_both: free_netdev(ndev);release_1: release_mem_region(res->start, SMC911X_IO_EXTENT);out: printk("%s: not found (%d).\n", CARDNAME, ret); }#ifdef SMC_USE_DMA else { lp->physaddr = res->start; lp->dev = &pdev->dev; }#endif return ret;}static int smc911x_drv_remove(struct platform_device *pdev){ struct net_device *ndev = platform_get_drvdata(pdev); struct resource *res; DBG(SMC_DEBUG_FUNC, "--> %s\n", __FUNCTION__); platform_set_drvdata(pdev, NULL); unregister_netdev(ndev); free_irq(ndev->irq, ndev);#ifdef SMC_USE_DMA { struct smc911x_local *lp = netdev_priv(ndev); if (lp->rxdma != -1) { SMC_DMA_FREE(dev, lp->rxdma); } if (lp->txdma != -1) { SMC_DMA_FREE(dev, lp->txdma); } }#endif iounmap((void *)ndev->base_addr); res = platform_get_resource(pdev, IORESOURCE_MEM, 0); release_mem_region(res->start, SMC911X_IO_EXTENT); free_netdev(ndev); return 0;}static int smc911x_drv_suspend(struct platform_device *dev, pm_message_t state){ struct net_device *ndev = platform_get_drvdata(dev); unsigned long ioaddr = ndev->base_addr; DBG(SMC_DEBUG_FUNC, "--> %s\n", __FUNCTION__); if (ndev) { if (netif_running(ndev)) { netif_device_detach(ndev); smc911x_shutdown(ndev);#if POWER_DOWN /* Set D2 - Energy detect only setting */ SMC_SET_PMT_CTRL(2<<12);#endif } } return 0;}static int smc911x_drv_resume(struct platform_device *dev){ struct net_device *ndev = platform_get_drvdata(dev); DBG(SMC_DEBUG_FUNC, "--> %s\n", __FUNCTION__); if (ndev) { struct smc911x_local *lp = netdev_priv(ndev); if (netif_running(ndev)) { smc911x_reset(ndev); smc911x_enable(ndev); if (lp->phy_type != 0) smc911x_phy_configure(&lp->phy_configure); netif_device_attach(ndev); } } return 0;}static struct platform_driver smc911x_driver = { .probe = smc911x_drv_probe, .remove = smc911x_drv_remove, .suspend = smc911x_drv_suspend, .resume = smc911x_drv_resume, .driver = { .name = CARDNAME, },};static int __init smc911x_init(void){ return platform_driver_register(&smc911x_driver);}static void __exit smc911x_cleanup(void){ platform_driver_unregister(&smc911x_driver);}module_init(smc911x_init);module_exit(smc911x_cleanup);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -