gt96100eth.c
来自「Linux Kernel 2.6.9 for OMAP1710」· C语言 代码 · 共 1,572 行 · 第 1/3 页
C
1,572 行
dbg(3, "%s: SDMA comm = %x\n", __FUNCTION__, GT96100ETH_READ(gp, GT96100_ETH_SDMA_COMM)); // wait for abort to complete while (GT96100ETH_READ(gp, GT96100_ETH_SDMA_COMM) & abort_bits) { // snooze for 20 msec and check again gt96100_delay(1); if (--timedout == 0) { err("%s: timeout!!\n", __FUNCTION__); break; } } spin_unlock(&gp->lock);}static voidhard_stop(struct net_device *dev){ struct gt96100_private *gp = netdev_priv(dev); dbg(3, "%s\n", __FUNCTION__); disable_ether_irq(dev); abort(dev, sdcmrAR | sdcmrAT); // disable port GT96100ETH_WRITE(gp, GT96100_ETH_PORT_CONFIG, 0);}static voidenable_ether_irq(struct net_device *dev){ struct gt96100_private *gp = netdev_priv(dev); u32 intMask; /* * route ethernet interrupt to GT_SERINT0 for port 0, * GT_INT0 for port 1. */ int intr_mask_reg = (gp->port_num == 0) ? GT96100_SERINT0_MASK : GT96100_INT0_HIGH_MASK; if (gp->chip_rev >= REV_GT96100A_1) { intMask = icrTxBufferLow | icrTxEndLow | icrTxErrorLow | icrRxOVR | icrTxUdr | icrRxBufferQ0 | icrRxErrorQ0 | icrMIIPhySTC | icrEtherIntSum; } else { intMask = icrTxBufferLow | icrTxEndLow | icrTxErrorLow | icrRxOVR | icrTxUdr | icrRxBuffer | icrRxError | icrMIIPhySTC | icrEtherIntSum; } // unmask interrupts GT96100ETH_WRITE(gp, GT96100_ETH_INT_MASK, intMask); intMask = GT96100_READ(intr_mask_reg); intMask |= 1<<gp->port_num; GT96100_WRITE(intr_mask_reg, intMask);}static voiddisable_ether_irq(struct net_device *dev){ struct gt96100_private *gp = netdev_priv(dev); u32 intMask; int intr_mask_reg = (gp->port_num == 0) ? GT96100_SERINT0_MASK : GT96100_INT0_HIGH_MASK; intMask = GT96100_READ(intr_mask_reg); intMask &= ~(1<<gp->port_num); GT96100_WRITE(intr_mask_reg, intMask); GT96100ETH_WRITE(gp, GT96100_ETH_INT_MASK, 0);}/* * Init GT96100 ethernet controller driver */static int gt96100_init_module(void){ struct pci_dev *pci; int i, retval=0; u32 cpuConfig; /* * Stupid probe because this really isn't a PCI device */ if (!(pci = pci_find_device(PCI_VENDOR_ID_MARVELL, PCI_DEVICE_ID_MARVELL_GT96100, NULL)) && !(pci = pci_find_device(PCI_VENDOR_ID_MARVELL, PCI_DEVICE_ID_MARVELL_GT96100A, NULL))) { printk(KERN_ERR __FILE__ ": GT96100 not found!\n"); return -ENODEV; } cpuConfig = GT96100_READ(GT96100_CPU_INTERF_CONFIG); if (cpuConfig & (1<<12)) { printk(KERN_ERR __FILE__ ": must be in Big Endian mode!\n"); return -ENODEV; } for (i=0; i < NUM_INTERFACES; i++) retval |= gt96100_probe1(pci, i); return retval;}static int __init gt96100_probe1(struct pci_dev *pci, int port_num){ struct gt96100_private *gp = NULL; struct gt96100_if_t *gtif = >96100_iflist[port_num]; int phy_addr, phy_id1, phy_id2; u32 phyAD; int retval; unsigned char chip_rev; struct net_device *dev = NULL; if (gtif->irq < 0) { printk(KERN_ERR "%s: irq unknown - probing not supported\n", __FUNCTION__); return -ENODEV; } pci_read_config_byte(pci, PCI_REVISION_ID, &chip_rev); if (chip_rev >= REV_GT96100A_1) { phyAD = GT96100_READ(GT96100_ETH_PHY_ADDR_REG); phy_addr = (phyAD >> (5*port_num)) & 0x1f; } else { /* * not sure what's this about -- probably a gt bug */ phy_addr = port_num; phyAD = GT96100_READ(GT96100_ETH_PHY_ADDR_REG); phyAD &= ~(0x1f << (port_num*5)); phyAD |= phy_addr << (port_num*5); GT96100_WRITE(GT96100_ETH_PHY_ADDR_REG, phyAD); } // probe for the external PHY if ((phy_id1 = read_MII(phy_addr, 2)) <= 0 || (phy_id2 = read_MII(phy_addr, 3)) <= 0) { printk(KERN_ERR "%s: no PHY found on MII%d\n", __FUNCTION__, port_num); return -ENODEV; } if (!request_region(gtif->iobase, GT96100_ETH_IO_SIZE, "GT96100ETH")) { printk(KERN_ERR "%s: request_region failed\n", __FUNCTION__); return -EBUSY; } dev = alloc_etherdev(sizeof(struct gt96100_private)); if (!dev) goto out; gtif->dev = dev; /* private struct aligned and zeroed by alloc_etherdev */ /* Fill in the 'dev' fields. */ dev->base_addr = gtif->iobase; dev->irq = gtif->irq; if ((retval = parse_mac_addr(dev, gtif->mac_str))) { err("%s: MAC address parse failed\n", __FUNCTION__); retval = -EINVAL; goto out1; } gp = netdev_priv(dev); memset(gp, 0, sizeof(*gp)); // clear it gp->port_num = port_num; gp->io_size = GT96100_ETH_IO_SIZE; gp->port_offset = port_num * GT96100_ETH_IO_SIZE; gp->phy_addr = phy_addr; gp->chip_rev = chip_rev; info("%s found at 0x%x, irq %d\n", chip_name(gp->chip_rev), gtif->iobase, gtif->irq); dump_hw_addr(0, dev, "HW Address ", dev->dev_addr); info("%s chip revision=%d\n", chip_name(gp->chip_rev), gp->chip_rev); info("%s ethernet port %d\n", chip_name(gp->chip_rev), gp->port_num); info("external PHY ID1=0x%04x, ID2=0x%04x\n", phy_id1, phy_id2); // Allocate Rx and Tx descriptor rings if (gp->rx_ring == NULL) { // All descriptors in ring must be 16-byte aligned gp->rx_ring = dmaalloc(sizeof(gt96100_rd_t) * RX_RING_SIZE + sizeof(gt96100_td_t) * TX_RING_SIZE, &gp->rx_ring_dma); if (gp->rx_ring == NULL) { retval = -ENOMEM; goto out1; } gp->tx_ring = (gt96100_td_t *)(gp->rx_ring + RX_RING_SIZE); gp->tx_ring_dma = gp->rx_ring_dma + sizeof(gt96100_rd_t) * RX_RING_SIZE; } // Allocate the Rx Data Buffers if (gp->rx_buff == NULL) { gp->rx_buff = dmaalloc(PKT_BUF_SZ*RX_RING_SIZE, &gp->rx_buff_dma); if (gp->rx_buff == NULL) { retval = -ENOMEM; goto out2; } } dbg(3, "%s: rx_ring=%p, tx_ring=%p\n", __FUNCTION__, gp->rx_ring, gp->tx_ring); // Allocate Rx Hash Table if (gp->hash_table == NULL) { gp->hash_table = (char*)dmaalloc(RX_HASH_TABLE_SIZE, &gp->hash_table_dma); if (gp->hash_table == NULL) { retval = -ENOMEM; goto out3; } } dbg(3, "%s: hash=%p\n", __FUNCTION__, gp->hash_table); spin_lock_init(&gp->lock); dev->open = gt96100_open; dev->hard_start_xmit = gt96100_tx; dev->stop = gt96100_close; dev->get_stats = gt96100_get_stats; //dev->do_ioctl = gt96100_ioctl; dev->set_multicast_list = gt96100_set_rx_mode; dev->tx_timeout = gt96100_tx_timeout; dev->watchdog_timeo = GT96100ETH_TX_TIMEOUT; retval = register_netdev(dev); if (retval) goto out4; return 0;out4: dmafree(RX_HASH_TABLE_SIZE, gp->hash_table_dma);out3: dmafree(PKT_BUF_SZ*RX_RING_SIZE, gp->rx_buff);out2: dmafree(sizeof(gt96100_rd_t) * RX_RING_SIZE + sizeof(gt96100_td_t) * TX_RING_SIZE, gp->rx_ring);out1: free_netdev (dev);out: release_region(gtif->iobase, GT96100_ETH_IO_SIZE); err("%s failed. Returns %d\n", __FUNCTION__, retval); return retval;}static voidreset_tx(struct net_device *dev){ struct gt96100_private *gp = netdev_priv(dev); int i; abort(dev, sdcmrAT); for (i=0; i<TX_RING_SIZE; i++) { if (gp->tx_skbuff[i]) { if (in_interrupt()) dev_kfree_skb_irq(gp->tx_skbuff[i]); else dev_kfree_skb(gp->tx_skbuff[i]); gp->tx_skbuff[i] = NULL; } gp->tx_ring[i].cmdstat = 0; // CPU owns gp->tx_ring[i].byte_cnt = 0; gp->tx_ring[i].buff_ptr = 0; gp->tx_ring[i].next = cpu_to_dma32(gp->tx_ring_dma + sizeof(gt96100_td_t) * (i+1)); dump_tx_desc(4, dev, i); } /* Wrap the ring. */ gp->tx_ring[i-1].next = cpu_to_dma32(gp->tx_ring_dma); // setup only the lowest priority TxCDP reg GT96100ETH_WRITE(gp, GT96100_ETH_CURR_TX_DESC_PTR0, gp->tx_ring_dma); GT96100ETH_WRITE(gp, GT96100_ETH_CURR_TX_DESC_PTR1, 0); // init Tx indeces and pkt counter gp->tx_next_in = gp->tx_next_out = 0; gp->tx_count = 0;}static voidreset_rx(struct net_device *dev){ struct gt96100_private *gp = netdev_priv(dev); int i; abort(dev, sdcmrAR); for (i=0; i<RX_RING_SIZE; i++) { gp->rx_ring[i].next = cpu_to_dma32(gp->rx_ring_dma + sizeof(gt96100_rd_t) * (i+1)); gp->rx_ring[i].buff_ptr = cpu_to_dma32(gp->rx_buff_dma + i*PKT_BUF_SZ); gp->rx_ring[i].buff_sz = cpu_to_dma16(PKT_BUF_SZ); // Give ownership to device, set first and last, enable intr gp->rx_ring[i].cmdstat = cpu_to_dma32((u32)(rxFirst | rxLast | rxOwn | rxEI)); dump_rx_desc(4, dev, i); } /* Wrap the ring. */ gp->rx_ring[i-1].next = cpu_to_dma32(gp->rx_ring_dma); // Setup only the lowest priority RxFDP and RxCDP regs for (i=0; i<4; i++) { if (i == 0) { GT96100ETH_WRITE(gp, GT96100_ETH_1ST_RX_DESC_PTR0, gp->rx_ring_dma); GT96100ETH_WRITE(gp, GT96100_ETH_CURR_RX_DESC_PTR0, gp->rx_ring_dma); } else { GT96100ETH_WRITE(gp, GT96100_ETH_1ST_RX_DESC_PTR0 + i*4, 0); GT96100ETH_WRITE(gp, GT96100_ETH_CURR_RX_DESC_PTR0 + i*4, 0); } } // init Rx NextOut index gp->rx_next_out = 0;}// Returns 1 if the Tx counter and indeces don't gelstatic intgt96100_check_tx_consistent(struct gt96100_private *gp){ int diff = gp->tx_next_in - gp->tx_next_out; diff = diff<0 ? TX_RING_SIZE + diff : diff; diff = gp->tx_count == TX_RING_SIZE ? diff + TX_RING_SIZE : diff; return (diff != gp->tx_count);}static intgt96100_init(struct net_device *dev){ struct gt96100_private *gp = netdev_priv(dev); u32 tmp; u16 mii_reg; dbg(3, "%s: dev=%p\n", __FUNCTION__, dev); dbg(3, "%s: scs10_lo=%4x, scs10_hi=%4x\n", __FUNCTION__, GT96100_READ(0x8), GT96100_READ(0x10)); dbg(3, "%s: scs32_lo=%4x, scs32_hi=%4x\n", __FUNCTION__, GT96100_READ(0x18), GT96100_READ(0x20)); // Stop and disable Port hard_stop(dev); // Setup CIU Arbiter tmp = GT96100_READ(GT96100_CIU_ARBITER_CONFIG); tmp |= (0x0c << (gp->port_num*2)); // set Ether DMA req priority to hi#ifndef DESC_BE tmp &= ~(1<<31); // set desc endianess to little#else tmp |= (1<<31);#endif GT96100_WRITE(GT96100_CIU_ARBITER_CONFIG, tmp); dbg(3, "%s: CIU Config=%x/%x\n", __FUNCTION__, tmp, GT96100_READ(GT96100_CIU_ARBITER_CONFIG)); // Set routing. tmp = GT96100_READ(GT96100_ROUTE_MAIN) & (0x3f << 18); tmp |= (0x07 << (18 + gp->port_num*3)); GT96100_WRITE(GT96100_ROUTE_MAIN, tmp); /* set MII as peripheral func */ tmp = GT96100_READ(GT96100_GPP_CONFIG2); tmp |= 0x7fff << (gp->port_num*16); GT96100_WRITE(GT96100_GPP_CONFIG2, tmp); /* Set up MII port pin directions */ tmp = GT96100_READ(GT96100_GPP_IO2); tmp |= 0x003d << (gp->port_num*16); GT96100_WRITE(GT96100_GPP_IO2, tmp); // Set-up hash table memset(gp->hash_table, 0, RX_HASH_TABLE_SIZE); // clear it gp->hash_mode = 0; // Add a single entry to hash table - our ethernet address gt96100_add_hash_entry(dev, dev->dev_addr); // Set-up DMA ptr to hash table GT96100ETH_WRITE(gp, GT96100_ETH_HASH_TBL_PTR, gp->hash_table_dma); dbg(3, "%s: Hash Tbl Ptr=%x\n", __FUNCTION__, GT96100ETH_READ(gp, GT96100_ETH_HASH_TBL_PTR)); // Setup Tx reset_tx(dev); dbg(3, "%s: Curr Tx Desc Ptr0=%x\n", __FUNCTION__, GT96100ETH_READ(gp, GT96100_ETH_CURR_TX_DESC_PTR0)); // Setup Rx reset_rx(dev); dbg(3, "%s: 1st/Curr Rx Desc Ptr0=%x/%x\n", __FUNCTION__, GT96100ETH_READ(gp, GT96100_ETH_1ST_RX_DESC_PTR0), GT96100ETH_READ(gp, GT96100_ETH_CURR_RX_DESC_PTR0)); // eth port config register GT96100ETH_WRITE(gp, GT96100_ETH_PORT_CONFIG_EXT, pcxrFCTL | pcxrFCTLen | pcxrFLP | pcxrDPLXen); mii_reg = read_MII(gp->phy_addr, 0x11); /* int enable register */ mii_reg |= 2; /* enable mii interrupt */ write_MII(gp->phy_addr, 0x11, mii_reg); dbg(3, "%s: PhyAD=%x\n", __FUNCTION__, GT96100_READ(GT96100_ETH_PHY_ADDR_REG)); // setup DMA // We want the Rx/Tx DMA to write/read data to/from memory in // Big Endian mode. Also set DMA Burst Size to 8 64Bit words.#ifdef DESC_DATA_BE GT96100ETH_WRITE(gp, GT96100_ETH_SDMA_CONFIG, (0xf<<sdcrRCBit) | sdcrRIFB | (3<<sdcrBSZBit));#else GT96100ETH_WRITE(gp, GT96100_ETH_SDMA_CONFIG, sdcrBLMR | sdcrBLMT | (0xf<<sdcrRCBit) | sdcrRIFB | (3<<sdcrBSZBit));#endif dbg(3, "%s: SDMA Config=%x\n", __FUNCTION__, GT96100ETH_READ(gp, GT96100_ETH_SDMA_CONFIG)); // start Rx DMA GT96100ETH_WRITE(gp, GT96100_ETH_SDMA_COMM, sdcmrERD); dbg(3, "%s: SDMA Comm=%x\n", __FUNCTION__, GT96100ETH_READ(gp, GT96100_ETH_SDMA_COMM)); // enable this port (set hash size to 1/2K) GT96100ETH_WRITE(gp, GT96100_ETH_PORT_CONFIG, pcrEN | pcrHS); dbg(3, "%s: Port Config=%x\n", __FUNCTION__, GT96100ETH_READ(gp, GT96100_ETH_PORT_CONFIG)); /* * Disable all Type-of-Service queueing. All Rx packets will be * treated normally and will be sent to the lowest priority * queue. * * Disable flow-control for now. FIXME: support flow control? */ // clear all the MIB ctr regs GT96100ETH_WRITE(gp, GT96100_ETH_PORT_CONFIG_EXT, pcxrFCTL | pcxrFCTLen | pcxrFLP | pcxrPRIOrxOverride); read_mib_counters(gp); GT96100ETH_WRITE(gp, GT96100_ETH_PORT_CONFIG_EXT, pcxrFCTL | pcxrFCTLen | pcxrFLP | pcxrPRIOrxOverride | pcxrMIBclrMode); dbg(3, "%s: Port Config Ext=%x\n", __FUNCTION__, GT96100ETH_READ(gp, GT96100_ETH_PORT_CONFIG_EXT)); netif_start_queue(dev); dump_MII(4, dev); // enable interrupts enable_ether_irq(dev); // we should now be receiving frames return 0;}static intgt96100_open(struct net_device *dev){ int retval; dbg(2, "%s: dev=%p\n", __FUNCTION__, dev); // Initialize and startup the GT-96100 ethernet port if ((retval = gt96100_init(dev))) { err("error in gt96100_init\n"); free_irq(dev->irq, dev); return retval; } if ((retval = request_irq(dev->irq, >96100_interrupt, SA_SHIRQ, dev->name, dev))) { err("unable to get IRQ %d\n", dev->irq); return retval; } dbg(2, "%s: Initialization done.\n", __FUNCTION__); return 0;}static intgt96100_close(struct net_device *dev)
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?