📄 smc91x.c
字号:
.get_link = ethtool_op_get_link,// .get_eeprom = smc_ethtool_geteeprom,// .set_eeprom = smc_ethtool_seteeprom,};/*---------------------------------------------------------------------- . smc_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, ------------------------------------------------------------------------*/int __initsmc_findirq( unsigned long ioaddr ){ int timeout = 20; unsigned long cookie; PRINTK2("%s: %s\n", CARDNAME, __FUNCTION__); cookie = probe_irq_on(); /* * What I try to do here is trigger an ALLOC_INT. This is done * by allocating a small chunk of memory, which will give an interrupt * when done. */ /* enable ALLOCation interrupts ONLY */ SMC_SELECT_BANK(2); SMC_SET_INT_MASK( IM_ALLOC_INT ); /* . Allocate 512 bytes of memory. Note that the chip was just . reset so all the memory is available */ SMC_SET_MMU_CMD( MC_ALLOC | 1 ); /* . 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 __initsmc_probe(struct net_device *dev, unsigned long ioaddr){ struct smc_local *lp = (struct smc_local *)dev->priv; static int version_printed = 0; int i, retval; unsigned int val, revision_register; const char *version_string; PRINTK2("%s: %s\n", CARDNAME, __FUNCTION__);#if defined(CONFIG_ARCH_PXA_PNP2110) /* MAC ADDRESS AT FLASHBLOCK 1 / OFFSET 0x1010 */ volatile unsigned char *pnp2110_mac = ioremap_nocache(0x00021000, 0x1000); if (!pnp2110_mac) return -ENODEV; pnp2110_mac += 0x10; ioaddr = SMC_IOADDR; /* HACK ALERT!! FIXME (RSC) */ dev->irq = SMC_IRQ;#endif PRINTK2("%s: ioaddr=%08x\n", CARDNAME, ioaddr); /* First, see if the high byte is 0x33 */ val = SMC_CURRENT_BANK(); PRINTK2("%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 0x%lx\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 ( (ioaddr & ((PAGE_SIZE-1)<<SMC_IO_SHIFT)) != val ) { printk( "%s: IOADDR %lx 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(); PRINTK2("%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 0x%lx: 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); /* set the private data to zero by default */ memset(lp, 0, sizeof(struct smc_local)); /* fill in some of the fields */ dev->base_addr = ioaddr; lp->version = revision_register & 0xff; /* Get the MAC address */ SMC_SELECT_BANK( 1 );#if defined(CONFIG_ARCH_PXA_PNP2110) SMC_SET_MAC_ADDR(pnp2110_mac);#elif defined(CONFIG_ARCH_FS_PXA255) { char smc91xx_mac[6] = {0x00,0x12,0x34,0x56,0x80,0x48}; SMC_SET_MAC_ADDR(smc91xx_mac); }#endif 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 = HZ/10; dev->get_stats = smc_query_statistics; dev->set_multicast_list = smc_set_multicast_list; dev->ethtool_ops = &smc_ethtool_ops; spin_lock_init(&lp->lock); 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_detect_phy(dev); /* Set default parameters */ lp->msg_enable = NETIF_MSG_LINK; lp->ctl_rfduplx = 0; lp->ctl_rspeed = 10;#ifndef CONFIG_ARCH_RAMSES if (lp->version >= (CHIP_91100 << 4)) { lp->ctl_rfduplx = 1; lp->ctl_rspeed = 100; }#endif /* Grab the IRQ */ retval = request_irq(dev->irq, &smc_interrupt, 0, 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 %#lx IRQ %d", dev->name, version_string, revision_register & 0x0f, dev->base_addr, 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: ", dev->name); for (i = 0; i < 5; i++) printk("%2.2x:", dev->dev_addr[i]); printk("%2.2x\n", dev->dev_addr[5]); } }err_out:#ifdef SMC_USE_PXA_DMA if (retval && dev->dma != -1) pxa_free_dma(dev->dma);#endif return retval;}/*------------------------------------------------------------------------- | | 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_enable_device(unsigned long attrib_phys){ unsigned long flags; unsigned char ecor, ecsr; void *addr; PRINTK2("%s: %s\n", CARDNAME, __FUNCTION__); /* * Map the attribute space. This is overkill, but clean. */ addr = ioremap(attrib_phys, ATTRIB_SIZE); if (!addr) return -ENOMEM; /* * Reset the device. We must disable IRQs around this. */ local_irq_save(flags); ecor = readb(addr + (ECOR << SMC_IO_SHIFT)) & ~ECOR_RESET; writeb(ecor | ECOR_RESET, addr + (ECOR << SMC_IO_SHIFT)); 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;#ifndef SMC_CAN_USE_16BIT ecsr |= ECSR_IOIS8;#endif writeb(ecsr, addr + (ECSR << SMC_IO_SHIFT)); local_irq_restore(flags); iounmap(addr); /* * Wait for the chip to wake up. */ mdelay(1); return 0;}static int smc_drv_probe(struct device *dev){ struct platform_device *pdev = to_platform_device(dev); struct net_device *ndev; unsigned long base, ext_base = 0; unsigned int *addr = NULL; int ret = -ENODEV; PRINTK2("%s: %s\n", CARDNAME, __FUNCTION__); ndev = alloc_etherdev(sizeof(struct smc_local)); if (!ndev) { printk("%s: could not allocate device.\n", CARDNAME); return -ENOMEM; } SET_MODULE_OWNER(ndev); SET_NETDEV_DEV(ndev, dev); ndev->dma = (unsigned char)-1; if (pdev->num_resources < 2 || pdev->num_resources > 3) { ret = -ENODEV; goto out; } base = pdev->resource[0].start; ndev->irq = pdev->resource[1].start; /* * Request the regions. */ if (!request_mem_region(base, SMC_IO_EXTENT, ndev->name)) { ret = -EBUSY; goto out; } if (pdev->num_resources == 3) { ext_base = pdev->resource[2].start; if (!request_mem_region(ext_base, ATTRIB_SIZE, ndev->name)) { ret = -EBUSY; goto release_1; }#if defined(CONFIG_SA1100_ASSABET) NCR_0 |= NCR_ENET_OSC_EN;#endif ret = smc_enable_device(ext_base); if (ret) goto release_both; } addr = ioremap(base, SMC_IO_EXTENT); if (!addr) { ret = -ENOMEM; goto release_both; } dev_set_drvdata(dev, ndev); ret = smc_probe(ndev, (unsigned long)addr); if (ret != 0) { dev_set_drvdata(dev, NULL); iounmap(addr); release_both: if (pdev->num_resources == 3) release_mem_region(ext_base, ATTRIB_SIZE); release_1: release_mem_region(base, SMC_IO_EXTENT); out: printk("%s: not found (%d).\n", CARDNAME, ret); kfree(ndev); } return ret;}static int smc_drv_remove(struct device *dev){ struct platform_device *pdev = to_platform_device(dev); struct net_device *ndev = dev_get_drvdata(dev); dev_set_drvdata(dev, NULL); unregister_netdev(ndev); free_irq(ndev->irq, ndev);#ifdef SMC_USE_PXA_DMA if (ndev->dma != -1) pxa_free_dma(ndev->dma);#endif iounmap((void *)ndev->base_addr); if (pdev->num_resources == 3) release_mem_region(pdev->resource[2].start, ATTRIB_SIZE); release_mem_region(pdev->resource[0].start, SMC_IO_EXTENT); kfree(ndev); return 0;}static int smc_drv_suspend(struct device *dev, u32 state, u32 level){ struct net_device *ndev = dev_get_drvdata(dev); if (ndev && level == SUSPEND_DISABLE) { if (netif_running(ndev)) { netif_device_detach(ndev); smc_shutdown(ndev->base_addr); } } return 0;}static int smc_drv_resume(struct device *dev, u32 level){ struct platform_device *pdev = to_platform_device(dev); struct net_device *ndev = dev_get_drvdata(dev); if (ndev && level == RESUME_ENABLE) { struct smc_local *lp = (struct smc_local *)ndev->priv; unsigned long ioaddr = ndev->base_addr; if (pdev->num_resources == 3) smc_enable_device(pdev->resource[2].start); if (netif_running(ndev)) { smc_reset(ndev); smc_enable(ndev); SMC_SELECT_BANK(1); SMC_SET_MAC_ADDR(ndev->dev_addr); if (lp->phy_type != 0) smc_phy_configure(ndev); netif_device_attach(ndev); } } return 0;}static struct device_driver smc_driver = { .name = CARDNAME, .bus = &platform_bus_type, .probe = smc_drv_probe, .remove = smc_drv_remove, .suspend = smc_drv_suspend, .resume = smc_drv_resume,};static int __init smc_init(void){ PRINTK2("%s: %s\n", CARDNAME, __FUNCTION__); DEBUG_DIR(15); DEBUG_CLR(15);#ifdef MODULE if (io == -1) printk( KERN_WARNING "%s: You shouldn't use auto-probing with insmod!\n", CARDNAME );#endif return driver_register(&smc_driver);}static void __exit smc_cleanup(void){ driver_unregister(&smc_driver);}module_init(smc_init);module_exit(smc_cleanup);MODULE_DESCRIPTION("SM91Cxxx Net Card Interface Driver");MODULE_LICENSE("GPL");
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -