📄 smc91x.c
字号:
/* * Wait until positive that the interrupt has been generated */ do { int int_status; udelay(10); int_status = SMC_GET_INT(); if (int_status & IM_ALLOC_INT) 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_MASK(0); /* and return what I found */ return probe_irq_off(cookie);}/* * Function: smc_probe(unsigned long ioaddr) * * Purpose: * Tests to see if a given ioaddr points to an SMC91x chip. * Returns a 0 on success * * Algorithm: * (1) see if the high byte of BANK_SELECT is 0x33 * (2) compare the ioaddr with the base register's address * (3) 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 smc_probe(struct net_device *dev, void __iomem *ioaddr){ struct smc_local *lp = netdev_priv(dev); static int version_printed = 0; int retval; unsigned int val, revision_register; const char *version_string; DECLARE_MAC_BUF(mac); DBG(2, "%s: %s\n", CARDNAME, __FUNCTION__); /* First, see if the high byte is 0x33 */ val = SMC_CURRENT_BANK(); DBG(2, "%s: bank signature probe returned 0x%04x\n", CARDNAME, val); if ((val & 0xFF00) != 0x3300) { if ((val & 0xFF) == 0x33) { printk(KERN_WARNING "%s: Detected possible byte-swapped interface" " at IOADDR %p\n", CARDNAME, ioaddr); } retval = -ENODEV; goto err_out; } /* * The above MIGHT indicate a device, but I need to write to * further test this. */ SMC_SELECT_BANK(0); val = SMC_CURRENT_BANK(); if ((val & 0xFF00) != 0x3300) { retval = -ENODEV; goto err_out; } /* * well, we've already written once, so hopefully another * time won't hurt. This time, I need to switch the bank * register to bank 1, so I can access the base address * register */ SMC_SELECT_BANK(1); val = SMC_GET_BASE(); val = ((val & 0x1F00) >> 3) << SMC_IO_SHIFT; if (((unsigned int)ioaddr & (0x3e0 << SMC_IO_SHIFT)) != val) { printk("%s: IOADDR %p doesn't match configuration (%x).\n", CARDNAME, ioaddr, val); } /* * check if the revision register is something that I * recognize. These might need to be added to later, * as future revisions could be added. */ SMC_SELECT_BANK(3); revision_register = SMC_GET_REV(); DBG(2, "%s: revision = 0x%04x\n", CARDNAME, revision_register); version_string = chip_ids[ (revision_register >> 4) & 0xF]; if (!version_string || (revision_register & 0xff00) != 0x3300) { /* I don't recognize this chip, so... */ printk("%s: IO %p: Unrecognized revision register 0x%04x" ", Contact author.\n", CARDNAME, ioaddr, revision_register); retval = -ENODEV; goto err_out; } /* At this point I'll assume that the chip is an SMC91x. */ if (version_printed++ == 0) printk("%s", version); /* fill in some of the fields */ dev->base_addr = (unsigned long)ioaddr; lp->base = ioaddr; lp->version = revision_register & 0xff; spin_lock_init(&lp->lock); /* Get the MAC address */ SMC_SELECT_BANK(1); SMC_GET_MAC_ADDR(dev->dev_addr); /* now, reset the chip, and put it into a known state */ smc_reset(dev); /* * If dev->irq is 0, then the device has to be banged on to see * what the IRQ is. * * This banging doesn't always detect the IRQ, for unknown reasons. * a workaround is to reset the chip and try again. * * Interestingly, the DOS packet driver *SETS* the IRQ on the card to * be what is requested on the command line. I don't do that, mostly * because the card that I have uses a non-standard method of accessing * the IRQs, and because this _should_ work in most configurations. * * 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 = smc_findirq(ioaddr); if (dev->irq) break; /* kick the card and try again */ smc_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 = smc_open; dev->stop = smc_close; dev->hard_start_xmit = smc_hard_start_xmit; dev->tx_timeout = smc_timeout; dev->watchdog_timeo = msecs_to_jiffies(watchdog); dev->set_multicast_list = smc_set_multicast_list; dev->ethtool_ops = &smc_ethtool_ops;#ifdef CONFIG_NET_POLL_CONTROLLER dev->poll_controller = smc_poll_controller;#endif tasklet_init(&lp->tx_task, smc_hardware_send_pkt, (unsigned long)dev); INIT_WORK(&lp->phy_configure, smc_phy_configure); lp->dev = dev; 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 = smc_phy_read; lp->mii.mdio_write = smc_phy_write; /* * Locate the phy, if any. */ if (lp->version >= (CHIP_91100 << 4)) smc_phy_detect(dev); /* then shut everything down to save power */ smc_shutdown(dev); smc_phy_powerdown(dev); /* Set default parameters */ lp->msg_enable = NETIF_MSG_LINK; lp->ctl_rfduplx = 0; lp->ctl_rspeed = 10; if (lp->version >= (CHIP_91100 << 4)) { lp->ctl_rfduplx = 1; lp->ctl_rspeed = 100; } /* Grab the IRQ */ retval = request_irq(dev->irq, &smc_interrupt, SMC_IRQ_FLAGS, dev->name, dev); if (retval) goto err_out;#ifdef SMC_USE_PXA_DMA { int dma = pxa_request_dma(dev->name, DMA_PRIO_LOW, smc_pxa_dma_irq, NULL); if (dma >= 0) dev->dma = dma; }#endif retval = register_netdev(dev); if (retval == 0) { /* now, print out the card info, in a short format.. */ printk("%s: %s (rev %d) at %p IRQ %d", dev->name, version_string, revision_register & 0x0f, lp->base, dev->irq); if (dev->dma != (unsigned char)-1) printk(" DMA %d", dev->dma); printk("%s%s\n", nowait ? " [nowait]" : "", THROTTLE_TX_PKTS ? " [throttle_tx]" : ""); 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: %s\n", dev->name, print_mac(mac, dev->dev_addr)); } if (lp->phy_type == 0) { PRINTK("%s: No PHY found\n", dev->name); } else if ((lp->phy_type & 0xfffffff0) == 0x0016f840) { PRINTK("%s: PHY LAN83C183 (LAN91C111 Internal)\n", dev->name); } else if ((lp->phy_type & 0xfffffff0) == 0x02821c50) { PRINTK("%s: PHY LAN83C180\n", dev->name); } }err_out:#ifdef SMC_USE_PXA_DMA if (retval && dev->dma != (unsigned char)-1) pxa_free_dma(dev->dma);#endif return retval;}static int smc_enable_device(struct platform_device *pdev){ unsigned long flags; unsigned char ecor, ecsr; void __iomem *addr; struct resource * res; res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "smc91x-attrib"); if (!res) return 0; /* * Map the attribute space. This is overkill, but clean. */ addr = ioremap(res->start, ATTRIB_SIZE); if (!addr) return -ENOMEM; /* * Reset the device. We must disable IRQs around this * since a reset causes the IRQ line become active. */ local_irq_save(flags); ecor = readb(addr + (ECOR << SMC_IO_SHIFT)) & ~ECOR_RESET; writeb(ecor | ECOR_RESET, addr + (ECOR << SMC_IO_SHIFT)); readb(addr + (ECOR << SMC_IO_SHIFT)); /* * Wait 100us for the chip to reset. */ udelay(100); /* * The device will ignore all writes to the enable bit while * reset is asserted, even if the reset bit is cleared in the * same write. Must clear reset first, then enable the device. */ writeb(ecor, addr + (ECOR << SMC_IO_SHIFT)); writeb(ecor | ECOR_ENABLE, addr + (ECOR << SMC_IO_SHIFT)); /* * Set the appropriate byte/word mode. */ ecsr = readb(addr + (ECSR << SMC_IO_SHIFT)) & ~ECSR_IOIS8; if (!SMC_CAN_USE_16BIT) ecsr |= ECSR_IOIS8; writeb(ecsr, addr + (ECSR << SMC_IO_SHIFT)); local_irq_restore(flags); iounmap(addr); /* * Wait for the chip to wake up. We could poll the control * register in the main register space, but that isn't mapped * yet. We know this is going to take 750us. */ msleep(1); return 0;}static int smc_request_attrib(struct platform_device *pdev){ struct resource * res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "smc91x-attrib"); if (!res) return 0; if (!request_mem_region(res->start, ATTRIB_SIZE, CARDNAME)) return -EBUSY; return 0;}static void smc_release_attrib(struct platform_device *pdev){ struct resource * res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "smc91x-attrib"); if (res) release_mem_region(res->start, ATTRIB_SIZE);}static inline void smc_request_datacs(struct platform_device *pdev, struct net_device *ndev){ if (SMC_CAN_USE_DATACS) { struct resource * res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "smc91x-data32"); struct smc_local *lp = netdev_priv(ndev); if (!res) return; if(!request_mem_region(res->start, SMC_DATA_EXTENT, CARDNAME)) { printk(KERN_INFO "%s: failed to request datacs memory region.\n", CARDNAME); return; } lp->datacs = ioremap(res->start, SMC_DATA_EXTENT); }}static void smc_release_datacs(struct platform_device *pdev, struct net_device *ndev){ if (SMC_CAN_USE_DATACS) { struct smc_local *lp = netdev_priv(ndev); struct resource * res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "smc91x-data32"); if (lp->datacs) iounmap(lp->datacs); lp->datacs = NULL; if (res) release_mem_region(res->start, SMC_DATA_EXTENT); }}/* * smc_init(void) * Input parameters: * dev->base_addr == 0, try to find all possible locations * dev->base_addr > 0x1ff, this is the address to check * dev->base_addr == <anything else>, return failure code * * Output: * 0 --> there is a device * anything else, error */static int smc_drv_probe(struct platform_device *pdev){ struct net_device *ndev; struct resource *res; unsigned int __iomem *addr; int ret; res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "smc91x-regs"); if (!res) res = platform_get_resource(pdev, IORESOURCE_MEM, 0); if (!res) { ret = -ENODEV; goto out; } if (!request_mem_region(res->start, SMC_IO_EXTENT, CARDNAME)) { ret = -EBUSY; goto out; } ndev = alloc_etherdev(sizeof(struct smc_local)); if (!ndev) { printk("%s: could not allocate device.\n", CARDNAME); ret = -ENOMEM; goto out_release_io; } SET_NETDEV_DEV(ndev, &pdev->dev); ndev->dma = (unsigned char)-1; ndev->irq = platform_get_irq(pdev, 0); if (ndev->irq < 0) { ret = -ENODEV; goto out_free_netdev; } ret = smc_request_attrib(pdev); if (ret) goto out_free_netdev;#if defined(CONFIG_SA1100_ASSABET) NCR_0 |= NCR_ENET_OSC_EN;#endif ret = smc_enable_device(pdev); if (ret) goto out_release_attrib; addr = ioremap(res->start, SMC_IO_EXTENT); if (!addr) { ret = -ENOMEM; goto out_release_attrib; }#ifdef SMC_USE_PXA_DMA { struct smc_local *lp = netdev_priv(ndev); lp->device = &pdev->dev; lp->physaddr = res->start; }#endif platform_set_drvdata(pdev, ndev); ret = smc_probe(ndev, addr); if (ret != 0) goto out_iounmap; smc_request_datacs(pdev, ndev); return 0; out_iounmap: platform_set_drvdata(pdev, NULL); iounmap(addr); out_release_attrib: smc_release_attrib(pdev); out_free_netdev: free_netdev(ndev); out_release_io: release_mem_region(res->start, SMC_IO_EXTENT); out: printk("%s: not found (%d).\n", CARDNAME, ret); return ret;}static int smc_drv_remove(struct platform_device *pdev){ struct net_device *ndev = platform_get_drvdata(pdev); struct smc_local *lp = netdev_priv(ndev); struct resource *res; platform_set_drvdata(pdev, NULL); unregister_netdev(ndev); free_irq(ndev->irq, ndev);#ifdef SMC_USE_PXA_DMA if (ndev->dma != (unsigned char)-1) pxa_free_dma(ndev->dma);#endif iounmap(lp->base); smc_release_datacs(pdev,ndev); smc_release_attrib(pdev); res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "smc91x-regs"); if (!res) platform_get_resource(pdev, IORESOURCE_MEM, 0); release_mem_region(res->start, SMC_IO_EXTENT); free_netdev(ndev); return 0;}static int smc_drv_suspend(struct platform_device *dev, pm_message_t state){ struct net_device *ndev = platform_get_drvdata(dev); if (ndev) { if (netif_running(ndev)) { netif_device_detach(ndev); smc_shutdown(ndev); smc_phy_powerdown(ndev); } } return 0;}static int smc_drv_resume(struct platform_device *dev){ struct net_device *ndev = platform_get_drvdata(dev); if (ndev) { struct smc_local *lp = netdev_priv(ndev); smc_enable_device(dev); if (netif_running(ndev)) { smc_reset(ndev); smc_enable(ndev); if (lp->phy_type != 0) smc_phy_configure(&lp->phy_configure); netif_device_attach(ndev); } } return 0;}static struct platform_driver smc_driver = { .probe = smc_drv_probe, .remove = smc_drv_remove, .suspend = smc_drv_suspend, .resume = smc_drv_resume, .driver = { .name = CARDNAME, },};static int __init smc_init(void){#ifdef MODULE#ifdef CONFIG_ISA if (io == -1) printk(KERN_WARNING "%s: You shouldn't use auto-probing with insmod!\n", CARDNAME);#endif#endif return platform_driver_register(&smc_driver);}static void __exit smc_cleanup(void){ platform_driver_unregister(&smc_driver);}module_init(smc_init);module_exit(smc_cleanup);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -