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, &reg);        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 + -
显示快捷键?