sis190.c
来自「linux 内核源代码」· C语言 代码 · 共 1,862 行 · 第 1/3 页
C
1,862 行
else if (phy_lan) phy_default = phy_lan; else phy_default = list_entry(&tp->first_phy, struct sis190_phy, list); } if (mii_if->phy_id != phy_default->phy_id) { mii_if->phy_id = phy_default->phy_id; net_probe(tp, KERN_INFO "%s: Using transceiver at address %d as default.\n", pci_name(tp->pci_dev), mii_if->phy_id); } status = mdio_read(ioaddr, mii_if->phy_id, MII_BMCR); status &= (~BMCR_ISOLATE); mdio_write(ioaddr, mii_if->phy_id, MII_BMCR, status); status = mdio_read_latched(ioaddr, mii_if->phy_id, MII_BMSR); return status;}static void sis190_init_phy(struct net_device *dev, struct sis190_private *tp, struct sis190_phy *phy, unsigned int phy_id, u16 mii_status){ void __iomem *ioaddr = tp->mmio_addr; struct mii_chip_info *p; INIT_LIST_HEAD(&phy->list); phy->status = mii_status; phy->phy_id = phy_id; phy->id[0] = mdio_read(ioaddr, phy_id, MII_PHYSID1); phy->id[1] = mdio_read(ioaddr, phy_id, MII_PHYSID2); for (p = mii_chip_table; p->type; p++) { if ((p->id[0] == phy->id[0]) && (p->id[1] == (phy->id[1] & 0xfff0))) { break; } } if (p->id[1]) { phy->type = (p->type == MIX) ? ((mii_status & (BMSR_100FULL | BMSR_100HALF)) ? LAN : HOME) : p->type; tp->features |= p->feature; } else phy->type = UNKNOWN; net_probe(tp, KERN_INFO "%s: %s transceiver at address %d.\n", pci_name(tp->pci_dev), (phy->type == UNKNOWN) ? "Unknown PHY" : p->name, phy_id);}static void sis190_mii_probe_88e1111_fixup(struct sis190_private *tp){ if (tp->features & F_PHY_88E1111) { void __iomem *ioaddr = tp->mmio_addr; int phy_id = tp->mii_if.phy_id; u16 reg[2][2] = { { 0x808b, 0x0ce1 }, { 0x808f, 0x0c60 } }, *p; p = (tp->features & F_HAS_RGMII) ? reg[0] : reg[1]; mdio_write(ioaddr, phy_id, 0x1b, p[0]); udelay(200); mdio_write(ioaddr, phy_id, 0x14, p[1]); udelay(200); }}/** * sis190_mii_probe - Probe MII PHY for sis190 * @dev: the net device to probe for * * Search for total of 32 possible mii phy addresses. * Identify and set current phy if found one, * return error if it failed to found. */static int __devinit sis190_mii_probe(struct net_device *dev){ struct sis190_private *tp = netdev_priv(dev); struct mii_if_info *mii_if = &tp->mii_if; void __iomem *ioaddr = tp->mmio_addr; int phy_id; int rc = 0; INIT_LIST_HEAD(&tp->first_phy); for (phy_id = 0; phy_id < PHY_MAX_ADDR; phy_id++) { struct sis190_phy *phy; u16 status; status = mdio_read_latched(ioaddr, phy_id, MII_BMSR); // Try next mii if the current one is not accessible. if (status == 0xffff || status == 0x0000) continue; phy = kmalloc(sizeof(*phy), GFP_KERNEL); if (!phy) { sis190_free_phy(&tp->first_phy); rc = -ENOMEM; goto out; } sis190_init_phy(dev, tp, phy, phy_id, status); list_add(&tp->first_phy, &phy->list); } if (list_empty(&tp->first_phy)) { net_probe(tp, KERN_INFO "%s: No MII transceivers found!\n", pci_name(tp->pci_dev)); rc = -EIO; goto out; } /* Select default PHY for mac */ sis190_default_phy(dev); sis190_mii_probe_88e1111_fixup(tp); mii_if->dev = dev; mii_if->mdio_read = __mdio_read; mii_if->mdio_write = __mdio_write; mii_if->phy_id_mask = PHY_ID_ANY; mii_if->reg_num_mask = MII_REG_ANY;out: return rc;}static void sis190_mii_remove(struct net_device *dev){ struct sis190_private *tp = netdev_priv(dev); sis190_free_phy(&tp->first_phy);}static void sis190_release_board(struct pci_dev *pdev){ struct net_device *dev = pci_get_drvdata(pdev); struct sis190_private *tp = netdev_priv(dev); iounmap(tp->mmio_addr); pci_release_regions(pdev); pci_disable_device(pdev); free_netdev(dev);}static struct net_device * __devinit sis190_init_board(struct pci_dev *pdev){ struct sis190_private *tp; struct net_device *dev; void __iomem *ioaddr; int rc; dev = alloc_etherdev(sizeof(*tp)); if (!dev) { net_drv(&debug, KERN_ERR PFX "unable to alloc new ethernet\n"); rc = -ENOMEM; goto err_out_0; } SET_NETDEV_DEV(dev, &pdev->dev); tp = netdev_priv(dev); tp->dev = dev; tp->msg_enable = netif_msg_init(debug.msg_enable, SIS190_MSG_DEFAULT); rc = pci_enable_device(pdev); if (rc < 0) { net_probe(tp, KERN_ERR "%s: enable failure\n", pci_name(pdev)); goto err_free_dev_1; } rc = -ENODEV; if (!(pci_resource_flags(pdev, 0) & IORESOURCE_MEM)) { net_probe(tp, KERN_ERR "%s: region #0 is no MMIO resource.\n", pci_name(pdev)); goto err_pci_disable_2; } if (pci_resource_len(pdev, 0) < SIS190_REGS_SIZE) { net_probe(tp, KERN_ERR "%s: invalid PCI region size(s).\n", pci_name(pdev)); goto err_pci_disable_2; } rc = pci_request_regions(pdev, DRV_NAME); if (rc < 0) { net_probe(tp, KERN_ERR PFX "%s: could not request regions.\n", pci_name(pdev)); goto err_pci_disable_2; } rc = pci_set_dma_mask(pdev, DMA_32BIT_MASK); if (rc < 0) { net_probe(tp, KERN_ERR "%s: DMA configuration failed.\n", pci_name(pdev)); goto err_free_res_3; } pci_set_master(pdev); ioaddr = ioremap(pci_resource_start(pdev, 0), SIS190_REGS_SIZE); if (!ioaddr) { net_probe(tp, KERN_ERR "%s: cannot remap MMIO, aborting\n", pci_name(pdev)); rc = -EIO; goto err_free_res_3; } tp->pci_dev = pdev; tp->mmio_addr = ioaddr; sis190_irq_mask_and_ack(ioaddr); sis190_soft_reset(ioaddr);out: return dev;err_free_res_3: pci_release_regions(pdev);err_pci_disable_2: pci_disable_device(pdev);err_free_dev_1: free_netdev(dev);err_out_0: dev = ERR_PTR(rc); goto out;}static void sis190_tx_timeout(struct net_device *dev){ struct sis190_private *tp = netdev_priv(dev); void __iomem *ioaddr = tp->mmio_addr; u8 tmp8; /* Disable Tx, if not already */ tmp8 = SIS_R8(TxControl); if (tmp8 & CmdTxEnb) SIS_W8(TxControl, tmp8 & ~CmdTxEnb); net_tx_err(tp, KERN_INFO "%s: Transmit timeout, status %08x %08x.\n", dev->name, SIS_R32(TxControl), SIS_R32(TxSts)); /* Disable interrupts by clearing the interrupt mask. */ SIS_W32(IntrMask, 0x0000); /* Stop a shared interrupt from scavenging while we are. */ spin_lock_irq(&tp->lock); sis190_tx_clear(tp); spin_unlock_irq(&tp->lock); /* ...and finally, reset everything. */ sis190_hw_start(dev); netif_wake_queue(dev);}static void sis190_set_rgmii(struct sis190_private *tp, u8 reg){ tp->features |= (reg & 0x80) ? F_HAS_RGMII : 0;}static int __devinit sis190_get_mac_addr_from_eeprom(struct pci_dev *pdev, struct net_device *dev){ struct sis190_private *tp = netdev_priv(dev); void __iomem *ioaddr = tp->mmio_addr; u16 sig; int i; net_probe(tp, KERN_INFO "%s: Read MAC address from EEPROM\n", pci_name(pdev)); /* Check to see if there is a sane EEPROM */ sig = (u16) sis190_read_eeprom(ioaddr, EEPROMSignature); if ((sig == 0xffff) || (sig == 0x0000)) { net_probe(tp, KERN_INFO "%s: Error EEPROM read %x.\n", pci_name(pdev), sig); return -EIO; } /* Get MAC address from EEPROM */ for (i = 0; i < MAC_ADDR_LEN / 2; i++) { u16 w = sis190_read_eeprom(ioaddr, EEPROMMACAddr + i); ((__le16 *)dev->dev_addr)[i] = cpu_to_le16(w); } sis190_set_rgmii(tp, sis190_read_eeprom(ioaddr, EEPROMInfo)); return 0;}/** * sis190_get_mac_addr_from_apc - Get MAC address for SiS96x model * @pdev: PCI device * @dev: network device to get address for * * SiS96x model, use APC CMOS RAM to store MAC address. * APC CMOS RAM is accessed through ISA bridge. * MAC address is read into @net_dev->dev_addr. */static int __devinit sis190_get_mac_addr_from_apc(struct pci_dev *pdev, struct net_device *dev){ static const u16 __devinitdata ids[] = { 0x0965, 0x0966, 0x0968 }; struct sis190_private *tp = netdev_priv(dev); struct pci_dev *isa_bridge; u8 reg, tmp8; unsigned int i; net_probe(tp, KERN_INFO "%s: Read MAC address from APC.\n", pci_name(pdev)); for (i = 0; i < ARRAY_SIZE(ids); i++) { isa_bridge = pci_get_device(PCI_VENDOR_ID_SI, ids[i], NULL); if (isa_bridge) break; } if (!isa_bridge) { net_probe(tp, KERN_INFO "%s: Can not find ISA bridge.\n", pci_name(pdev)); return -EIO; } /* Enable port 78h & 79h to access APC Registers. */ pci_read_config_byte(isa_bridge, 0x48, &tmp8); reg = (tmp8 & ~0x02); pci_write_config_byte(isa_bridge, 0x48, reg); udelay(50); pci_read_config_byte(isa_bridge, 0x48, ®); for (i = 0; i < MAC_ADDR_LEN; i++) { outb(0x9 + i, 0x78); dev->dev_addr[i] = inb(0x79); } outb(0x12, 0x78); reg = inb(0x79); sis190_set_rgmii(tp, reg); /* Restore the value to ISA Bridge */ pci_write_config_byte(isa_bridge, 0x48, tmp8); pci_dev_put(isa_bridge); return 0;}/** * sis190_init_rxfilter - Initialize the Rx filter * @dev: network device to initialize * * Set receive filter address to our MAC address * and enable packet filtering. */static inline void sis190_init_rxfilter(struct net_device *dev){ struct sis190_private *tp = netdev_priv(dev); void __iomem *ioaddr = tp->mmio_addr; u16 ctl; int i; ctl = SIS_R16(RxMacControl); /* * Disable packet filtering before setting filter. * Note: SiS's driver writes 32 bits but RxMacControl is 16 bits * only and followed by RxMacAddr (6 bytes). Strange. -- FR */ SIS_W16(RxMacControl, ctl & ~0x0f00); for (i = 0; i < MAC_ADDR_LEN; i++) SIS_W8(RxMacAddr + i, dev->dev_addr[i]); SIS_W16(RxMacControl, ctl); SIS_PCI_COMMIT();}static int sis190_get_mac_addr(struct pci_dev *pdev, struct net_device *dev){ u8 from; pci_read_config_byte(pdev, 0x73, &from); return (from & 0x00000001) ? sis190_get_mac_addr_from_apc(pdev, dev) : sis190_get_mac_addr_from_eeprom(pdev, dev);}static void sis190_set_speed_auto(struct net_device *dev){ struct sis190_private *tp = netdev_priv(dev); void __iomem *ioaddr = tp->mmio_addr; int phy_id = tp->mii_if.phy_id; int val; net_link(tp, KERN_INFO "%s: Enabling Auto-negotiation.\n", dev->name); val = mdio_read(ioaddr, phy_id, MII_ADVERTISE); // Enable 10/100 Full/Half Mode, leave MII_ADVERTISE bit4:0 // unchanged. mdio_write(ioaddr, phy_id, MII_ADVERTISE, (val & ADVERTISE_SLCT) | ADVERTISE_100FULL | ADVERTISE_10FULL | ADVERTISE_100HALF | ADVERTISE_10HALF); // Enable 1000 Full Mode. mdio_write(ioaddr, phy_id, MII_CTRL1000, ADVERTISE_1000FULL); // Enable auto-negotiation and restart auto-negotiation. mdio_write(ioaddr, phy_id, MII_BMCR, BMCR_ANENABLE | BMCR_ANRESTART | BMCR_RESET);}static int sis190_get_settings(struct net_device *dev, struct ethtool_cmd *cmd){ struct sis190_private *tp = netdev_priv(dev); return mii_ethtool_gset(&tp->mii_if, cmd);}static int sis190_set_settings(struct net_device *dev, struct ethtool_cmd *cmd){ struct sis190_private *tp = netdev_priv(dev); return mii_ethtool_sset(&tp->mii_if, cmd);}static void sis190_get_drvinfo(struct net_device *dev, struct ethtool_drvinfo *info){ struct sis190_private *tp = netdev_priv(dev); strcpy(info->driver, DRV_NAME); strcpy(info->version, DRV_VERSION); strcpy(info->bus_info, pci_name(tp->pci_dev));}static int sis190_get_regs_len(struct net_device *dev){ return SIS190_REGS_SIZE;}static void sis190_get_regs(struct net_device *dev, struct ethtool_regs *regs, void *p){ struct sis190_private *tp = netdev_priv(dev); unsigned long flags; if (regs->len > SIS190_REGS_SIZE) regs->len = SIS190_REGS_SIZE; spin_lock_irqsave(&tp->lock, flags); memcpy_fromio(p, tp->mmio_addr, regs->len); spin_unlock_irqrestore(&tp->lock, flags);}static int sis190_nway_reset(struct net_device *dev){ struct sis190_private *tp = netdev_priv(dev); return mii_nway_restart(&tp->mii_if);}static u32 sis190_get_msglevel(struct net_device *dev){ struct sis190_private *tp = netdev_priv(dev); return tp->msg_enable;}static void sis190_set_msglevel(struct net_device *dev, u32 value){ struct sis190_private *tp = netdev_priv(dev); tp->msg_enable = value;}static const struct ethtool_ops sis190_ethtool_ops = { .get_settings = sis190_get_settings, .set_settings = sis190_set_settings, .get_drvinfo = sis190_get_drvinfo, .get_regs_len = sis190_get_regs_len, .get_regs = sis190_get_regs, .get_link = ethtool_op_get_link, .get_msglevel = sis190_get_msglevel, .set_msglevel = sis190_set_msglevel, .nway_reset = sis190_nway_reset,};static int sis190_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd){ struct sis190_private *tp = netdev_priv(dev); return !netif_running(dev) ? -EINVAL : generic_mii_ioctl(&tp->mii_if, if_mii(ifr), cmd, NULL);}static int __devinit sis190_init_one(struct pci_dev *pdev, const struct pci_device_id *ent){ static int printed_version = 0; struct sis190_private *tp; struct net_device *dev; void __iomem *ioaddr; int rc; DECLARE_MAC_BUF(mac); if (!printed_version) { net_drv(&debug, KERN_INFO SIS190_DRIVER_NAME " loaded.\n"); printed_version = 1; } dev = sis190_init_board(pdev); if (IS_ERR(dev)) { rc = PTR_ERR(dev); goto out; } pci_set_drvdata(pdev, dev); tp = netdev_priv(dev); ioaddr = tp->mmio_addr; rc = sis190_get_mac_addr(pdev, dev); if (rc < 0) goto err_release_board; sis190_init_rxfilter(dev); INIT_WORK(&tp->phy_task, sis190_phy_task); dev->open = sis190_open; dev->stop = sis190_close; dev->do_ioctl = sis190_ioctl; dev->tx_timeout = sis190_tx_timeout; dev->watchdog_timeo = SIS190_TX_TIMEOUT; dev->hard_start_xmit = sis190_start_xmit;#ifdef CONFIG_NET_POLL_CONTROLLER dev->poll_controller = sis190_netpoll;#endif dev->set_multicast_list = sis190_set_rx_mode; SET_ETHTOOL_OPS(dev, &sis190_ethtool_ops); dev->irq = pdev->irq; dev->base_addr = (unsigned long) 0xdead; spin_lock_init(&tp->lock); rc = sis190_mii_probe(dev); if (rc < 0) goto err_release_board; rc = register_netdev(dev); if (rc < 0) goto err_remove_mii; net_probe(tp, KERN_INFO "%s: %s at %p (IRQ: %d), " "%s\n", pci_name(pdev), sis_chip_info[ent->driver_data].name, ioaddr, dev->irq, print_mac(mac, dev->dev_addr)); net_probe(tp, KERN_INFO "%s: %s mode.\n", dev->name, (tp->features & F_HAS_RGMII) ? "RGMII" : "GMII"); netif_carrier_off(dev); sis190_set_speed_auto(dev);out: return rc;err_remove_mii: sis190_mii_remove(dev);err_release_board: sis190_release_board(pdev); goto out;}static void __devexit sis190_remove_one(struct pci_dev *pdev){ struct net_device *dev = pci_get_drvdata(pdev); sis190_mii_remove(dev); flush_scheduled_work(); unregister_netdev(dev); sis190_release_board(pdev); pci_set_drvdata(pdev, NULL);}static struct pci_driver sis190_pci_driver = { .name = DRV_NAME, .id_table = sis190_pci_tbl, .probe = sis190_init_one, .remove = __devexit_p(sis190_remove_one),};static int __init sis190_init_module(void){ return pci_register_driver(&sis190_pci_driver);}static void __exit sis190_cleanup_module(void){ pci_unregister_driver(&sis190_pci_driver);}module_init(sis190_init_module);module_exit(sis190_cleanup_module);
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?