uli526x.c | 335 +++++++++++++++++++++++++++++++++++++++++++++++--------------- 1 file changed, 255 insertions(+), 80 deletions(-) diff -ruNp 180-uli-power-manaagement.patch-old/drivers/net/tulip/uli526x.c 180-uli-power-manaagement.patch-new/drivers/net/tulip/uli526x.c --- 180-uli-power-manaagement.patch-old/drivers/net/tulip/uli526x.c 2006-03-13 10:02:46.000000000 +1000 +++ 180-uli-power-manaagement.patch-new/drivers/net/tulip/uli526x.c 2006-03-15 09:35:15.000000000 +1000 @@ -13,8 +13,8 @@ */ #define DRV_NAME "uli526x" -#define DRV_VERSION "0.9.3" -#define DRV_RELDATE "2005-7-29" +#define DRV_VERSION "0.9.4" +#define DRV_RELDATE "2006-1-17" #include @@ -248,6 +248,68 @@ static void uli526x_set_phyxcer(struct u /* ULI526X network board routine ---------------------------- */ +static void uli526x_free_resources(struct uli526x_board_info *db) +{ + if(db->desc_pool_ptr) { + pci_free_consistent(db->pdev, sizeof(struct tx_desc) * DESC_ALL_CNT + 0x20, + db->desc_pool_ptr, db->desc_pool_dma_ptr); + db->desc_pool_ptr = NULL; + } + + if(db->buf_pool_ptr != NULL) { + pci_free_consistent(db->pdev, TX_BUF_ALLOC * TX_DESC_CNT + 4, + db->buf_pool_ptr, db->buf_pool_dma_ptr); + db->buf_pool_ptr = NULL; + } +} + +static int uli526x_get_resources(struct uli526x_board_info *db) +{ + /* Allocate Tx/Rx descriptor memory */ + db->desc_pool_ptr = pci_alloc_consistent(db->pdev, sizeof(struct tx_desc) * DESC_ALL_CNT + 0x20, &db->desc_pool_dma_ptr); + if(!db->desc_pool_ptr) + return -ENOMEM; + + db->buf_pool_ptr = pci_alloc_consistent(db->pdev, TX_BUF_ALLOC * TX_DESC_CNT + 4, &db->buf_pool_dma_ptr); + return db->buf_pool_ptr ? 0 : -ENOMEM; +} + +static void do_reset(struct net_device *dev) +{ + struct uli526x_board_info *db = netdev_priv(dev); + int i; + + printk("Do_reset\n"); + + /* read 64 word srom data */ + for (i = 0; i < 64; i++) + ((u16 *) db->srom)[i] = cpu_to_le16(read_srom_word(db->ioaddr, i)); + + /* Set Node address */ + if(((u16 *) db->srom)[0] == 0xffff || ((u16 *) db->srom)[0] == 0) /* SROM absent, so read MAC address from ID Table */ + { + outl(0x10000, db->ioaddr + DCR0); //Diagnosis mode + outl(0x1c0, db->ioaddr + DCR13); //Reset dianostic pointer port + outl(0, db->ioaddr + DCR14); //Clear reset port + outl(0x10, db->ioaddr + DCR14); //Reset ID Table pointer + outl(0, db->ioaddr + DCR14); //Clear reset port + outl(0, db->ioaddr + DCR13); //Clear CR13 + outl(0x1b0, db->ioaddr + DCR13); //Select ID Table access port + //Read MAC address from CR14 + for (i = 0; i < 6; i++) + dev->dev_addr[i] = inl(db->ioaddr + DCR14); + //Read end + outl(0, db->ioaddr + DCR13); //Clear CR13 + outl(0, db->ioaddr + DCR0); //Clear CR0 + udelay(10); + } + else /*Exist SROM*/ + { + for (i = 0; i < 6; i++) + dev->dev_addr[i] = db->srom[20 + i]; + } +} + /* * Search ULI526X board, allocate space and register it */ @@ -256,18 +318,30 @@ static int __devinit uli526x_init_one (s const struct pci_device_id *ent) { struct uli526x_board_info *db; /* board information structure */ - struct net_device *dev; + struct net_device *dev = NULL; int i, err; ULI526X_DBUG(0, "uli526x_init_one()", 0); + printk("uli526x_init_one()\n"); if (!printed_version++) printk(version); + + /* Enable Master/IO access, Disable memory access */ + err = pci_enable_device(pdev); + if (err) + goto err_out_free; + + pci_set_power_state(pdev, PCI_D0); + /* Init network device */ dev = alloc_etherdev(sizeof(*db)); - if (dev == NULL) - return -ENOMEM; + if (dev == NULL) { + err = -ENOMEM; + goto err_out_disable; + } + SET_MODULE_OWNER(dev); SET_NETDEV_DEV(dev, &pdev->dev); @@ -277,11 +351,6 @@ static int __devinit uli526x_init_one (s goto err_out_free; } - /* Enable Master/IO access, Disable memory access */ - err = pci_enable_device(pdev); - if (err) - goto err_out_free; - if (!pci_resource_start(pdev, 0)) { printk(KERN_ERR DRV_NAME ": I/O base is zero\n"); err = -ENODEV; @@ -303,19 +372,9 @@ static int __devinit uli526x_init_one (s /* Init system & device */ db = netdev_priv(dev); - /* Allocate Tx/Rx descriptor memory */ - db->desc_pool_ptr = pci_alloc_consistent(pdev, sizeof(struct tx_desc) * DESC_ALL_CNT + 0x20, &db->desc_pool_dma_ptr); - if(db->desc_pool_ptr == NULL) - { - err = -ENOMEM; - goto err_out_nomem; - } - db->buf_pool_ptr = pci_alloc_consistent(pdev, TX_BUF_ALLOC * TX_DESC_CNT + 4, &db->buf_pool_dma_ptr); - if(db->buf_pool_ptr == NULL) - { - err = -ENOMEM; + err = uli526x_get_resources(db); + if (err) goto err_out_nomem; - } db->first_tx_desc = (struct tx_desc *) db->desc_pool_ptr; db->first_tx_desc_dma = db->desc_pool_dma_ptr; @@ -341,34 +400,8 @@ static int __devinit uli526x_init_one (s dev->ethtool_ops = &netdev_ethtool_ops; spin_lock_init(&db->lock); - - /* read 64 word srom data */ - for (i = 0; i < 64; i++) - ((u16 *) db->srom)[i] = cpu_to_le16(read_srom_word(db->ioaddr, i)); + do_reset(dev); - /* Set Node address */ - if(((u16 *) db->srom)[0] == 0xffff || ((u16 *) db->srom)[0] == 0) /* SROM absent, so read MAC address from ID Table */ - { - outl(0x10000, db->ioaddr + DCR0); //Diagnosis mode - outl(0x1c0, db->ioaddr + DCR13); //Reset dianostic pointer port - outl(0, db->ioaddr + DCR14); //Clear reset port - outl(0x10, db->ioaddr + DCR14); //Reset ID Table pointer - outl(0, db->ioaddr + DCR14); //Clear reset port - outl(0, db->ioaddr + DCR13); //Clear CR13 - outl(0x1b0, db->ioaddr + DCR13); //Select ID Table access port - //Read MAC address from CR14 - for (i = 0; i < 6; i++) - dev->dev_addr[i] = inl(db->ioaddr + DCR14); - //Read end - outl(0, db->ioaddr + DCR13); //Clear CR13 - outl(0, db->ioaddr + DCR0); //Clear CR0 - udelay(10); - } - else /*Exist SROM*/ - { - for (i = 0; i < 6; i++) - dev->dev_addr[i] = db->srom[20 + i]; - } err = register_netdev (dev); if (err) goto err_out_res; @@ -379,25 +412,34 @@ static int __devinit uli526x_init_one (s printk("%c%02x", i ? ':' : ' ', dev->dev_addr[i]); printk(", irq %d.\n", dev->irq); + pci_set_power_state(pdev, PCI_D0); + pci_set_master(pdev); + printk("Register 0 is %x.\n", inl(db->ioaddr)); + printk("Register 3 is %x.\n", inl(db->ioaddr + 0x18)); + printk("Register 4 is %x.\n", inl(db->ioaddr + 0x20)); + printk("Register 5 is %x.\n", inl(db->ioaddr + 0x28)); + printk("Register 6 is %x.\n", inl(db->ioaddr + 0x30)); + printk("Register 7 is %x.\n", inl(db->ioaddr + 0x38)); + printk("Register 8 is %x.\n", inl(db->ioaddr + 0x40)); + printk("Register 9 is %x.\n", inl(db->ioaddr + 0x48)); + printk("Register 10 is %x.\n", inl(db->ioaddr + 0x50)); + printk("Register 11 is %x.\n", inl(db->ioaddr + 0x58)); + printk("Register 12 is %x.\n", inl(db->ioaddr + 0x60)); + printk("Register 15 is %x.\n", inl(db->ioaddr + 0x78)); return 0; err_out_res: pci_release_regions(pdev); err_out_nomem: - if(db->desc_pool_ptr) - pci_free_consistent(pdev, sizeof(struct tx_desc) * DESC_ALL_CNT + 0x20, - db->desc_pool_ptr, db->desc_pool_dma_ptr); - - if(db->buf_pool_ptr != NULL) - pci_free_consistent(pdev, TX_BUF_ALLOC * TX_DESC_CNT + 4, - db->buf_pool_ptr, db->buf_pool_dma_ptr); + uli526x_free_resources(db); err_out_disable: pci_disable_device(pdev); err_out_free: pci_set_drvdata(pdev, NULL); - free_netdev(dev); + if (dev) + free_netdev(dev); return err; } @@ -410,15 +452,19 @@ static void __devexit uli526x_remove_one ULI526X_DBUG(0, "uli526x_remove_one()", 0); - pci_free_consistent(db->pdev, sizeof(struct tx_desc) * - DESC_ALL_CNT + 0x20, db->desc_pool_ptr, - db->desc_pool_dma_ptr); - pci_free_consistent(db->pdev, TX_BUF_ALLOC * TX_DESC_CNT + 4, - db->buf_pool_ptr, db->buf_pool_dma_ptr); + //20060306 - Uncommented + //netif_poll_disable(dev); + + //netif_stop_queue(dev); unregister_netdev(dev); - pci_release_regions(pdev); + uli526x_free_resources(db); free_netdev(dev); /* free board information */ + + //20060306 - Uncommented + //pci_iounmap(pdev, (void *) dev->base_addr); + pci_release_regions(pdev); pci_set_drvdata(pdev, NULL); + pci_set_power_state(pdev, PCI_D3hot); pci_disable_device(pdev); ULI526X_DBUG(0, "uli526x_remove_one() exit", 0); } @@ -435,6 +481,7 @@ static int uli526x_open(struct net_devic struct uli526x_board_info *db = netdev_priv(dev); ULI526X_DBUG(0, "uli526x_open", 0); + printk("uli526x_open\n"); ret = request_irq(dev->irq, &uli526x_interrupt, SA_SHIRQ, dev->name, dev); if (ret) @@ -443,6 +490,7 @@ static int uli526x_open(struct net_devic /* system variable init */ db->cr6_data = CR6_DEFAULT | uli526x_cr6_user_set; db->tx_packet_cnt = 0; + //ULI526X_DBUG(0, "db->tx_packet_cnt reset to %d.", db->tx_packet_cnt); db->rx_avail_cnt = 0; db->link_failed = 1; netif_carrier_off(dev); @@ -488,6 +536,7 @@ static void uli526x_init(struct net_devi u16 phy_reg_reset; ULI526X_DBUG(0, "uli526x_init()", 0); + printk("uli526x_init\n"); /* Reset M526x MAC controller */ outl(ULI526X_RESET, ioaddr + DCR0); /* RESET MAC */ @@ -557,7 +606,7 @@ static int uli526x_start_xmit(struct sk_ struct tx_desc *txptr; unsigned long flags; - ULI526X_DBUG(0, "uli526x_start_xmit", 0); + //ULI526X_DBUG(0, "uli526x_start_xmit", 0); /* Resource flag check */ netif_stop_queue(dev); @@ -593,6 +642,7 @@ static int uli526x_start_xmit(struct sk_ if ( (db->tx_packet_cnt < TX_DESC_CNT) ) { txptr->tdes0 = cpu_to_le32(0x80000000); /* Set owner bit */ db->tx_packet_cnt++; /* Ready to send */ + //ULI526X_DBUG(0, "db->tx_packet_cnt = %d.", db->tx_packet_cnt); outl(0x1, dev->base_addr + DCR1); /* Issue Tx polling */ dev->trans_start = jiffies; /* saved time stamp */ } @@ -638,6 +688,8 @@ static int uli526x_stop(struct net_devic /* free interrupt */ free_irq(dev->irq, dev); + netif_carrier_off(dev); + /* free allocated rx buffer */ uli526x_free_rxbuffer(db); @@ -678,6 +730,7 @@ static irqreturn_t uli526x_interrupt(int db->cr5_data = inl(ioaddr + DCR5); outl(db->cr5_data, ioaddr + DCR5); if ( !(db->cr5_data & 0x180c1) ) { + ULI526X_DBUG(1, "Earlier return. CR5=", db->cr5_data); spin_unlock_irqrestore(&db->lock, flags); outl(db->cr7_data, ioaddr + DCR7); return IRQ_HANDLED; @@ -698,7 +751,7 @@ static irqreturn_t uli526x_interrupt(int uli526x_rx_packet(dev, db); /* reallocate rx descriptor buffer */ - if (db->rx_avail_cntrx_avail_cnt < RX_DESC_CNT) allocate_rx_buffer(db); /* Free the transmitted descriptor */ @@ -729,8 +782,10 @@ static void uli526x_free_tx_pkt(struct n if (tdes0 & 0x80000000) break; + //ULI526X_DBUG(0, "Free tx pkt.\n", NULL); /* A packet sent completed */ db->tx_packet_cnt--; + //ULI526X_DBUG(0, "db->tx_packet_cnt = %d.", db->tx_packet_cnt); db->stats.tx_packets++; /* Transmit statistic counter */ @@ -788,10 +843,9 @@ static void uli526x_rx_packet(struct net while(db->rx_avail_cnt) { rdes0 = le32_to_cpu(rxptr->rdes0); if (rdes0 & 0x80000000) /* packet owner check */ - { break; - } + //ULI526X_DBUG(0, "rx pkt.", NULL); db->rx_avail_cnt--; db->interval_rx_cnt++; @@ -799,7 +853,7 @@ static void uli526x_rx_packet(struct net if ( (rdes0 & 0x300) != 0x300) { /* A packet without First/Last flag */ /* reuse this SKB */ - ULI526X_DBUG(0, "Reuse SK buffer, rdes0", rdes0); + //ULI526X_DBUG(0, "Reuse SK buffer, rdes0", rdes0); uli526x_reuse_skb(db, rxptr->rx_skb_ptr); } else { /* A packet with First/Last flag */ @@ -808,7 +862,7 @@ static void uli526x_rx_packet(struct net /* error summary bit check */ if (rdes0 & 0x8000) { /* This is a error packet */ - //printk(DRV_NAME ": rdes0: %lx\n", rdes0); + printk(DRV_NAME ": rdes0: %x\n", rdes0); db->stats.rx_errors++; if (rdes0 & 1) db->stats.rx_fifo_errors++; @@ -844,7 +898,7 @@ static void uli526x_rx_packet(struct net } else { /* Reuse SKB buffer when the packet is error */ - ULI526X_DBUG(0, "Reuse SK buffer, rdes0", rdes0); + //ULI526X_DBUG(0, "Reuse SK buffer, rdes0", rdes0); uli526x_reuse_skb(db, rxptr->rx_skb_ptr); } } @@ -864,7 +918,7 @@ static struct net_device_stats * uli526x { struct uli526x_board_info *db = netdev_priv(dev); - ULI526X_DBUG(0, "uli526x_get_stats", 0); + //ULI526X_DBUG(0, "uli526x_get_stats", 0); return &db->stats; } @@ -878,11 +932,11 @@ static void uli526x_set_filter_mode(stru struct uli526x_board_info *db = dev->priv; unsigned long flags; - ULI526X_DBUG(0, "uli526x_set_filter_mode()", 0); + //ULI526X_DBUG(0, "uli526x_set_filter_mode()", 0); spin_lock_irqsave(&db->lock, flags); if (dev->flags & IFF_PROMISC) { - ULI526X_DBUG(0, "Enable PROM Mode", 0); + //ULI526X_DBUG(0, "Enable PROM Mode", 0); db->cr6_data |= CR6_PM | CR6_PBF; update_cr6(db->cr6_data, db->ioaddr); spin_unlock_irqrestore(&db->lock, flags); @@ -890,14 +944,14 @@ static void uli526x_set_filter_mode(stru } if (dev->flags & IFF_ALLMULTI || dev->mc_count > ULI5261_MAX_MULTICAST) { - ULI526X_DBUG(0, "Pass all multicast address", dev->mc_count); + //ULI526X_DBUG(0, "Pass all multicast address", dev->mc_count); db->cr6_data &= ~(CR6_PM | CR6_PBF); db->cr6_data |= CR6_PAM; spin_unlock_irqrestore(&db->lock, flags); return; } - ULI526X_DBUG(0, "Set multicast address", dev->mc_count); + //ULI526X_DBUG(0, "Set multicast address", dev->mc_count); send_filter_frame(dev, dev->mc_count); /* M5261/M5263 */ spin_unlock_irqrestore(&db->lock, flags); } @@ -1005,6 +1059,7 @@ static void uli526x_timer(unsigned long struct uli526x_board_info *db = netdev_priv(dev); unsigned long flags; u8 TmpSpeed=10; + int link_status = 0; //ULI526X_DBUG(0, "uli526x_timer()", 0); spin_lock_irqsave(&db->lock, flags); @@ -1023,6 +1078,7 @@ static void uli526x_timer(unsigned long time_after(jiffies, dev->trans_start + ULI526X_TX_KICK) ) { outl(0x1, dev->base_addr + DCR1); // Tx polling again + //ULI526X_DBUG(0, "db->tx_packet_cnt = %d.", db->tx_packet_cnt); // TX Timeout if ( time_after(jiffies, dev->trans_start + ULI526X_TX_TIMEOUT) ) { db->reset_TXtimeout++; @@ -1043,7 +1099,9 @@ static void uli526x_timer(unsigned long } /* Link status check, Dynamic media type change */ - if((phy_read(db->ioaddr, db->phy_addr, 5, db->chip_id) & 0x01e0)!=0) + link_status = phy_read(db->ioaddr, db->phy_addr, 5, db->chip_id); + //ULI526X_DBUG(0, "Link status is ", link_status); + if (link_status & 0x1e0) tmp_cr12 = 3; if ( !(tmp_cr12 & 0x3) && !db->link_failed ) { @@ -1065,7 +1123,7 @@ static void uli526x_timer(unsigned long } } else if ((tmp_cr12 & 0x3) && db->link_failed) { - ULI526X_DBUG(0, "Link link OK", tmp_cr12); + //ULI526X_DBUG(0, "Link link OK", tmp_cr12); db->link_failed = 0; /* Auto Sense Speed */ @@ -1137,6 +1195,7 @@ static void uli526x_dynamic_reset(struct /* system variable init */ db->tx_packet_cnt = 0; + ULI526X_DBUG(0, "db->tx_packet_cnt reset to ", db->tx_packet_cnt); db->rx_avail_cnt = 0; db->link_failed = 1; db->init=1; @@ -1156,11 +1215,12 @@ static void uli526x_dynamic_reset(struct static void uli526x_free_rxbuffer(struct uli526x_board_info * db) { - ULI526X_DBUG(0, "uli526x_free_rxbuffer()", 0); + //ULI526X_DBUG(0, "uli526x_free_rxbuffer()", 0); /* free allocated rx buffer */ while (db->rx_avail_cnt) { - dev_kfree_skb(db->rx_ready_ptr->rx_skb_ptr); + if (db->rx_ready_ptr->rx_skb_ptr) + dev_kfree_skb(db->rx_ready_ptr->rx_skb_ptr); db->rx_ready_ptr = db->rx_ready_ptr->next_rx_desc; db->rx_avail_cnt--; } @@ -1277,7 +1337,7 @@ static void send_filter_frame(struct net u32 * suptr; int i; - ULI526X_DBUG(0, "send_filter_frame()", 0); + //ULI526X_DBUG(0, "send_filter_frame()", 0); txptr = db->tx_insert_ptr; suptr = (u32 *) txptr->tx_buf_ptr; @@ -1669,6 +1729,117 @@ static u16 phy_read_1bit(unsigned long i return phy_data; } +#ifdef CONFIG_PM + +#if 0 +static void uli526x_set_power_state ( + struct uli526x_board_info *deb, + int sleep, int snooze) +{ + if (tp->flags & HAS_ACPI) { + u32 tmp, newtmp; + pci_read_config_dword (tp->pdev, CFDD, &tmp); + newtmp = tmp & ~(CFDD_Sleep | CFDD_Snooze); + if (sleep) + newtmp |= CFDD_Sleep; + else if (snooze) + newtmp |= CFDD_Snooze; + if (tmp != newtmp) + pci_write_config_dword (tp->pdev, CFDD, newtmp); + } + +} +#endif + +static u32 *dcr_save; + +static int uli526x_suspend (struct pci_dev *pdev, pm_message_t state) +{ + struct net_device *dev = pci_get_drvdata (pdev); + struct uli526x_board_info *db = netdev_priv(dev); + unsigned long ioaddr = db->ioaddr; + unsigned long flags; + int i; + + if (!dev) + return -EINVAL; + + if (netif_running(dev)) + netif_stop_queue(dev); + + netif_device_detach(dev); + + spin_lock_irqsave(&db->lock, flags); + + /* Disable interrupts, stop Tx and Rx. */ + outl(0, ioaddr + DCR7); + db->cr6_data &= ~(CR6_RXSC | CR6_TXSC); + update_cr6(db->cr6_data, ioaddr); + + dcr_save = kmalloc(sizeof(u32) * 16, GFP_ATOMIC); + for (i=0; i<16; i++) + dcr_save[i] = inl(ioaddr + i); + + //phy_write_1bit(ioaddr, PHY_POWER_DOWN, chip_id); + + spin_unlock_irqrestore(&db->lock, flags); + + pci_save_state(pdev); + + pci_disable_device(pdev); + pci_set_power_state(pdev, + pci_choose_state(pdev, state)); + + return 0; +} + + +static int uli526x_resume (struct pci_dev *pdev) +{ + struct net_device *dev = pci_get_drvdata (pdev); + struct uli526x_board_info *db = netdev_priv(dev); + unsigned long ioaddr = db->ioaddr; + u32 val; + int i; + + pci_set_power_state(pdev, PCI_D0); + pci_restore_state(pdev); + pci_enable_device(pdev); + + netif_device_attach(dev); + + if (!netif_running(dev)) + return 0; + + pci_set_master(pdev); + + pci_read_config_dword(pdev, 0x40, &val); + if ((val & 0x0000ff00) != 0) + pci_write_config_dword(pdev, 0x40, val & 0xffff00ff); + + for (i=0; i<16; i++) + outl(ioaddr+i, dcr_save[i]); + + kfree(dcr_save); + dcr_save = NULL; + + db->tx_packet_cnt = 0; + db->first_tx_desc = (struct tx_desc *) db->desc_pool_ptr; + db->first_tx_desc_dma = db->desc_pool_dma_ptr; + db->buf_pool_start = db->buf_pool_ptr; + db->buf_pool_dma_start = db->buf_pool_dma_ptr; + + do_reset(dev); + + uli526x_get_resources(db); + + uli526x_init(dev); + + return 0; +} + +#endif /* CONFIG_PM */ + static struct pci_device_id uli526x_pci_tbl[] = { { 0x10B9, 0x5261, PCI_ANY_ID, PCI_ANY_ID, 0, 0, PCI_ULI5261_ID }, @@ -1683,6 +1854,10 @@ static struct pci_driver uli526x_driver .id_table = uli526x_pci_tbl, .probe = uli526x_init_one, .remove = __devexit_p(uli526x_remove_one), +#ifdef CONFIG_PM + .suspend = uli526x_suspend, + .resume = uli526x_resume, +#endif /* CONFIG_PM */ }; MODULE_AUTHOR("Peer Chen, peer.chen@uli.com.tw");