/* on each timer ticks we check two things, Link Status (ON/OFF) and Link Mode (10/100/Full/Half) */ static void sis900_timer(unsigned long data) { struct device *net_dev = (struct device *)data; struct sis900_private *sis_priv = (struct sis900_private *)net_dev->priv; struct mii_phy *mii_phy = sis_priv->mii; static int next_tick = 5*HZ; u16 status; status = mdio_read(net_dev, sis_priv->cur_phy, MII_STATUS); /* current mii phy is failed to link, try another one */ while (!(status & MII_STAT_LINK)) { if (mii_phy->next == NULL) { if (sis_priv->LinkOn) { /* link stat change from ON to OFF */ next_tick = HZ; sis_priv->LinkOn = FALSE; printk(KERN_INFO "%s: Media Link Off\n", net_dev->name); } sis_priv->timer.expires = jiffies + next_tick; add_timer(&sis_priv->timer); return; } mii_phy = mii_phy->next; status = mdio_read(net_dev, mii_phy->phy_addr, MII_STATUS); } if (!sis_priv->LinkOn) { /* link stat change forn OFF to ON, read and report link mode */ sis_priv->LinkOn = TRUE; next_tick = 5*HZ; /* change what cur_phy means */ if (mii_phy->phy_addr != sis_priv->cur_phy) { printk(KERN_INFO "%s: Changing transceiver to %s\n", net_dev->name, mii_phy->chip_info->name); status = mdio_read(net_dev, sis_priv->cur_phy, MII_CONTROL); mdio_write(net_dev, sis_priv->cur_phy, MII_CONTROL, status | MII_CNTL_ISOLATE); status = mdio_read(net_dev, mii_phy->phy_addr, MII_CONTROL); mdio_write(net_dev, mii_phy->phy_addr, MII_CONTROL, status & ~MII_CNTL_ISOLATE); sis_priv->cur_phy = mii_phy->phy_addr; } sis900_check_mode(net_dev, mii_phy); } sis_priv->timer.expires = jiffies + next_tick; add_timer(&sis_priv->timer); }
static void sis900_init(struct nic *nic) { sis900_reset(nic); sis900_init_rxfilter(nic); sis900_init_txd(nic); sis900_init_rxd(nic); sis900_set_rx_mode(nic); sis900_check_mode(nic); outl(RxENA, ioaddr + cr); }
static int sis900_open(struct device *net_dev) { struct sis900_private *sis_priv = (struct sis900_private *)net_dev->priv; long ioaddr = net_dev->base_addr; /* Soft reset the chip. */ sis900_reset(net_dev); if (request_irq(net_dev->irq, &sis900_interrupt, SA_SHIRQ, net_dev->name, net_dev)) { return -EAGAIN; } MOD_INC_USE_COUNT; sis900_init_rxfilter(net_dev); sis900_init_tx_ring(net_dev); sis900_init_rx_ring(net_dev); set_rx_mode(net_dev); net_dev->tbusy = 0; net_dev->interrupt = 0; net_dev->start = 1; /* Enable all known interrupts by setting the interrupt mask. */ outl((RxSOVR|RxORN|RxERR|RxOK|TxURN|TxERR|TxIDLE), ioaddr + imr); outl(RxENA, ioaddr + cr); outl(IE, ioaddr + ier); sis900_check_mode(net_dev, sis_priv->mii); /* Set the timer to switch to check for link beat and perhaps switch to an alternate media type. */ init_timer(&sis_priv->timer); sis_priv->timer.expires = jiffies + HZ; sis_priv->timer.data = (unsigned long)net_dev; sis_priv->timer.function = &sis900_timer; add_timer(&sis_priv->timer); return 0; }