📄 uli526x.c
字号:
ret = request_irq(dev->irq, &uli526x_interrupt, SA_SHIRQ, dev->name, dev); if (ret) return ret; /* system variable init */ db->cr6_data = CR6_DEFAULT | uli526x_cr6_user_set; db->tx_packet_cnt = 0; db->rx_avail_cnt = 0; db->link_failed = 1; netif_carrier_off(dev); db->wait_reset = 0; db->NIC_capability = 0xf; /* All capability*/ db->PHY_reg4 = 0x1e0; /* CR6 operation mode decision */ db->cr6_data |= ULI526X_TXTH_256; db->cr0_data = CR0_DEFAULT; /* Initialize ULI526X board */ uli526x_init(dev); /* Active System Interface */ netif_wake_queue(dev); /* set and active a timer process */ init_timer(&db->timer); db->timer.expires = ULI526X_TIMER_WUT + HZ * 2; db->timer.data = (unsigned long)dev; db->timer.function = &uli526x_timer; add_timer(&db->timer); return 0;}/* Initialize ULI526X board * Reset ULI526X board * Initialize TX/Rx descriptor chain structure * Send the set-up frame * Enable Tx/Rx machine */static void uli526x_init(struct net_device *dev){ struct uli526x_board_info *db = netdev_priv(dev); unsigned long ioaddr = db->ioaddr; u8 phy_tmp; u16 phy_value; u16 phy_reg_reset; ULI526X_DBUG(0, "uli526x_init()", 0); /* Reset M526x MAC controller */ outl(ULI526X_RESET, ioaddr + DCR0); /* RESET MAC */ udelay(100); outl(db->cr0_data, ioaddr + DCR0); udelay(5); /* Phy addr : In some boards,M5261/M5263 phy address != 1 */ db->phy_addr = 1; for(phy_tmp=0;phy_tmp<32;phy_tmp++) { phy_value=phy_read(db->ioaddr,phy_tmp,3,db->chip_id);//peer add if(phy_value != 0xffff&&phy_value!=0) { db->phy_addr = phy_tmp; break; } } if(phy_tmp == 32) printk(KERN_WARNING "Can not find the phy address!!!"); /* Parser SROM and media mode */ db->media_mode = uli526x_media_mode; /* Phyxcer capability setting */ phy_reg_reset = phy_read(db->ioaddr, db->phy_addr, 0, db->chip_id); phy_reg_reset = (phy_reg_reset | 0x8000); phy_write(db->ioaddr, db->phy_addr, 0, phy_reg_reset, db->chip_id); udelay(500); /* Process Phyxcer Media Mode */ uli526x_set_phyxcer(db); /* Media Mode Process */ if ( !(db->media_mode & ULI526X_AUTO) ) db->op_mode = db->media_mode; /* Force Mode */ /* Initialize Transmit/Receive decriptor and CR3/4 */ uli526x_descriptor_init(db, ioaddr); /* Init CR6 to program M526X operation */ update_cr6(db->cr6_data, ioaddr); /* Send setup frame */ send_filter_frame(dev, dev->mc_count); /* M5261/M5263 */ /* Init CR7, interrupt active bit */ db->cr7_data = CR7_DEFAULT; outl(db->cr7_data, ioaddr + DCR7); /* Init CR15, Tx jabber and Rx watchdog timer */ outl(db->cr15_data, ioaddr + DCR15); /* Enable ULI526X Tx/Rx function */ db->cr6_data |= CR6_RXSC | CR6_TXSC; update_cr6(db->cr6_data, ioaddr);}/* * Hardware start transmission. * Send a packet to media from the upper layer. */static int uli526x_start_xmit(struct sk_buff *skb, struct net_device *dev){ struct uli526x_board_info *db = netdev_priv(dev); struct tx_desc *txptr; unsigned long flags; ULI526X_DBUG(0, "uli526x_start_xmit", 0); /* Resource flag check */ netif_stop_queue(dev); /* Too large packet check */ if (skb->len > MAX_PACKET_SIZE) { printk(KERN_ERR DRV_NAME ": big packet = %d\n", (u16)skb->len); dev_kfree_skb(skb); return 0; } spin_lock_irqsave(&db->lock, flags); /* No Tx resource check, it never happen nromally */ if (db->tx_packet_cnt >= TX_FREE_DESC_CNT) { spin_unlock_irqrestore(&db->lock, flags); printk(KERN_ERR DRV_NAME ": No Tx resource %ld\n", db->tx_packet_cnt); return 1; } /* Disable NIC interrupt */ outl(0, dev->base_addr + DCR7); /* transmit this packet */ txptr = db->tx_insert_ptr; memcpy(txptr->tx_buf_ptr, skb->data, skb->len); txptr->tdes1 = cpu_to_le32(0xe1000000 | skb->len); /* Point to next transmit free descriptor */ db->tx_insert_ptr = txptr->next_tx_desc; /* Transmit Packet Process */ if ( (db->tx_packet_cnt < TX_DESC_CNT) ) { txptr->tdes0 = cpu_to_le32(0x80000000); /* Set owner bit */ db->tx_packet_cnt++; /* Ready to send */ outl(0x1, dev->base_addr + DCR1); /* Issue Tx polling */ dev->trans_start = jiffies; /* saved time stamp */ } /* Tx resource check */ if ( db->tx_packet_cnt < TX_FREE_DESC_CNT ) netif_wake_queue(dev); /* Restore CR7 to enable interrupt */ spin_unlock_irqrestore(&db->lock, flags); outl(db->cr7_data, dev->base_addr + DCR7); /* free this SKB */ dev_kfree_skb(skb); return 0;}/* * Stop the interface. * The interface is stopped when it is brought. */static int uli526x_stop(struct net_device *dev){ struct uli526x_board_info *db = netdev_priv(dev); unsigned long ioaddr = dev->base_addr; ULI526X_DBUG(0, "uli526x_stop", 0); /* disable system */ netif_stop_queue(dev); /* deleted timer */ del_timer_sync(&db->timer); /* Reset & stop ULI526X board */ outl(ULI526X_RESET, ioaddr + DCR0); udelay(5); phy_write(db->ioaddr, db->phy_addr, 0, 0x8000, db->chip_id); /* free interrupt */ free_irq(dev->irq, dev); /* free allocated rx buffer */ uli526x_free_rxbuffer(db);#if 0 /* show statistic counter */ printk(DRV_NAME ": FU:%lx EC:%lx LC:%lx NC:%lx LOC:%lx TXJT:%lx RESET:%lx RCR8:%lx FAL:%lx TT:%lx\n", db->tx_fifo_underrun, db->tx_excessive_collision, db->tx_late_collision, db->tx_no_carrier, db->tx_loss_carrier, db->tx_jabber_timeout, db->reset_count, db->reset_cr8, db->reset_fatal, db->reset_TXtimeout);#endif return 0;}/* * M5261/M5263 insterrupt handler * receive the packet to upper layer, free the transmitted packet */static irqreturn_t uli526x_interrupt(int irq, void *dev_id, struct pt_regs *regs){ struct net_device *dev = dev_id; struct uli526x_board_info *db = netdev_priv(dev); unsigned long ioaddr = dev->base_addr; unsigned long flags; if (!dev) { ULI526X_DBUG(1, "uli526x_interrupt() without DEVICE arg", 0); return IRQ_NONE; } spin_lock_irqsave(&db->lock, flags); outl(0, ioaddr + DCR7); /* Got ULI526X status */ db->cr5_data = inl(ioaddr + DCR5); outl(db->cr5_data, ioaddr + DCR5); if ( !(db->cr5_data & 0x180c1) ) { spin_unlock_irqrestore(&db->lock, flags); outl(db->cr7_data, ioaddr + DCR7); return IRQ_HANDLED; } /* Check system status */ if (db->cr5_data & 0x2000) { /* system bus error happen */ ULI526X_DBUG(1, "System bus error happen. CR5=", db->cr5_data); db->reset_fatal++; db->wait_reset = 1; /* Need to RESET */ spin_unlock_irqrestore(&db->lock, flags); return IRQ_HANDLED; } /* Received the coming packet */ if ( (db->cr5_data & 0x40) && db->rx_avail_cnt ) uli526x_rx_packet(dev, db); /* reallocate rx descriptor buffer */ if (db->rx_avail_cnt<RX_DESC_CNT) allocate_rx_buffer(db); /* Free the transmitted descriptor */ if ( db->cr5_data & 0x01) uli526x_free_tx_pkt(dev, db); /* Restore CR7 to enable interrupt mask */ outl(db->cr7_data, ioaddr + DCR7); spin_unlock_irqrestore(&db->lock, flags); return IRQ_HANDLED;}/* * Free TX resource after TX complete */static void uli526x_free_tx_pkt(struct net_device *dev, struct uli526x_board_info * db){ struct tx_desc *txptr; u32 tdes0; txptr = db->tx_remove_ptr; while(db->tx_packet_cnt) { tdes0 = le32_to_cpu(txptr->tdes0); /* printk(DRV_NAME ": tdes0=%x\n", tdes0); */ if (tdes0 & 0x80000000) break; /* A packet sent completed */ db->tx_packet_cnt--; db->stats.tx_packets++; /* Transmit statistic counter */ if ( tdes0 != 0x7fffffff ) { /* printk(DRV_NAME ": tdes0=%x\n", tdes0); */ db->stats.collisions += (tdes0 >> 3) & 0xf; db->stats.tx_bytes += le32_to_cpu(txptr->tdes1) & 0x7ff; if (tdes0 & TDES0_ERR_MASK) { db->stats.tx_errors++; if (tdes0 & 0x0002) { /* UnderRun */ db->tx_fifo_underrun++; if ( !(db->cr6_data & CR6_SFT) ) { db->cr6_data = db->cr6_data | CR6_SFT; update_cr6(db->cr6_data, db->ioaddr); } } if (tdes0 & 0x0100) db->tx_excessive_collision++; if (tdes0 & 0x0200) db->tx_late_collision++; if (tdes0 & 0x0400) db->tx_no_carrier++; if (tdes0 & 0x0800) db->tx_loss_carrier++; if (tdes0 & 0x4000) db->tx_jabber_timeout++; } } txptr = txptr->next_tx_desc; }/* End of while */ /* Update TX remove pointer to next */ db->tx_remove_ptr = txptr; /* Resource available check */ if ( db->tx_packet_cnt < TX_WAKE_DESC_CNT ) netif_wake_queue(dev); /* Active upper layer, send again */}/* * Receive the come packet and pass to upper layer */static void uli526x_rx_packet(struct net_device *dev, struct uli526x_board_info * db){ struct rx_desc *rxptr; struct sk_buff *skb; int rxlen; u32 rdes0; rxptr = db->rx_ready_ptr; while(db->rx_avail_cnt) { rdes0 = le32_to_cpu(rxptr->rdes0); if (rdes0 & 0x80000000) /* packet owner check */ { break; } db->rx_avail_cnt--; db->interval_rx_cnt++; pci_unmap_single(db->pdev, le32_to_cpu(rxptr->rdes2), RX_ALLOC_SIZE, PCI_DMA_FROMDEVICE); if ( (rdes0 & 0x300) != 0x300) { /* A packet without First/Last flag */ /* reuse this SKB */ ULI526X_DBUG(0, "Reuse SK buffer, rdes0", rdes0); uli526x_reuse_skb(db, rxptr->rx_skb_ptr); } else { /* A packet with First/Last flag */ rxlen = ( (rdes0 >> 16) & 0x3fff) - 4; /* error summary bit check */ if (rdes0 & 0x8000) { /* This is a error packet */ //printk(DRV_NAME ": rdes0: %lx\n", rdes0); db->stats.rx_errors++; if (rdes0 & 1) db->stats.rx_fifo_errors++; if (rdes0 & 2) db->stats.rx_crc_errors++; if (rdes0 & 0x80) db->stats.rx_length_errors++; } if ( !(rdes0 & 0x8000) || ((db->cr6_data & CR6_PM) && (rxlen>6)) ) { skb = rxptr->rx_skb_ptr; /* Good packet, send to upper layer */ /* Shorst packet used new SKB */ if ( (rxlen < RX_COPY_SIZE) && ( (skb = dev_alloc_skb(rxlen + 2) ) != NULL) ) { /* size less than COPY_SIZE, allocate a rxlen SKB */ skb->dev = dev; skb_reserve(skb, 2); /* 16byte align */ memcpy(skb_put(skb, rxlen), rxptr->rx_skb_ptr->tail, rxlen); uli526x_reuse_skb(db, rxptr->rx_skb_ptr); } else { skb->dev = dev; skb_put(skb, rxlen); } skb->protocol = eth_type_trans(skb, dev); netif_rx(skb); dev->last_rx = jiffies; db->stats.rx_packets++; db->stats.rx_bytes += rxlen; } else { /* Reuse SKB buffer when the packet is error */ ULI526X_DBUG(0, "Reuse SK buffer, rdes0", rdes0); uli526x_reuse_skb(db, rxptr->rx_skb_ptr); } } rxptr = rxptr->next_rx_desc; } db->rx_ready_ptr = rxptr;}/* * Get statistics from driver. */static struct net_device_stats * uli526x_get_stats(struct net_device *dev){ struct uli526x_board_info *db = netdev_priv(dev); ULI526X_DBUG(0, "uli526x_get_stats", 0); return &db->stats;}/* * Set ULI526X multicast address */static void uli526x_set_filter_mode(struct net_device * dev)
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -