📄 sis900.c
字号:
outl(EECS, ee_addr); eeprom_delay(); outl(EECS | EECLK, ee_addr); eeprom_delay(); retval = (retval << 1) | ((inl(ee_addr) & EEDO) ? 1 : 0); eeprom_delay(); } /* Terminate the EEPROM access. */ outl(0, ee_addr); eeprom_delay(); outl(EECLK, ee_addr); return (retval);}/* Read and write the MII management registers using software-generated serial MDIO protocol. Note that the command bits and data bits are send out seperately */#define mdio_delay() inl(mdio_addr)static void mdio_idle(long mdio_addr){ outl(MDIO | MDDIR, mdio_addr); mdio_delay(); outl(MDIO | MDDIR | MDC, mdio_addr);}/* Syncronize the MII management interface by shifting 32 one bits out. */static void mdio_reset(long mdio_addr){ int i; for (i = 31; i >= 0; i--) { outl(MDDIR | MDIO, mdio_addr); mdio_delay(); outl(MDDIR | MDIO | MDC, mdio_addr); mdio_delay(); } return;}/** * mdio_read: - read MII PHY register * @net_dev: the net device to read * @phy_id: the phy address to read * @location: the phy regiester id to read * * Read MII registers through MDIO and MDC * using MDIO management frame structure and protocol(defined by ISO/IEC). * Please see SiS7014 or ICS spec */static u16 mdio_read(struct net_device *net_dev, int phy_id, int location){ long mdio_addr = net_dev->base_addr + mear; int mii_cmd = MIIread|(phy_id<<MIIpmdShift)|(location<<MIIregShift); u16 retval = 0; int i; mdio_reset(mdio_addr); mdio_idle(mdio_addr); for (i = 15; i >= 0; i--) { int dataval = (mii_cmd & (1 << i)) ? MDDIR | MDIO : MDDIR; outl(dataval, mdio_addr); mdio_delay(); outl(dataval | MDC, mdio_addr); mdio_delay(); } /* Read the 16 data bits. */ for (i = 16; i > 0; i--) { outl(0, mdio_addr); mdio_delay(); retval = (retval << 1) | ((inl(mdio_addr) & MDIO) ? 1 : 0); outl(MDC, mdio_addr); mdio_delay(); } outl(0x00, mdio_addr); return retval;}/** * mdio_write: - write MII PHY register * @net_dev: the net device to write * @phy_id: the phy address to write * @location: the phy regiester id to write * @value: the register value to write with * * Write MII registers with @value through MDIO and MDC * using MDIO management frame structure and protocol(defined by ISO/IEC) * please see SiS7014 or ICS spec */static void mdio_write(struct net_device *net_dev, int phy_id, int location, int value){ long mdio_addr = net_dev->base_addr + mear; int mii_cmd = MIIwrite|(phy_id<<MIIpmdShift)|(location<<MIIregShift); int i; mdio_reset(mdio_addr); mdio_idle(mdio_addr); /* Shift the command bits out. */ for (i = 15; i >= 0; i--) { int dataval = (mii_cmd & (1 << i)) ? MDDIR | MDIO : MDDIR; outb(dataval, mdio_addr); mdio_delay(); outb(dataval | MDC, mdio_addr); mdio_delay(); } mdio_delay(); /* Shift the value bits out. */ for (i = 15; i >= 0; i--) { int dataval = (value & (1 << i)) ? MDDIR | MDIO : MDDIR; outl(dataval, mdio_addr); mdio_delay(); outl(dataval | MDC, mdio_addr); mdio_delay(); } mdio_delay(); /* Clear out extra bits. */ for (i = 2; i > 0; i--) { outb(0, mdio_addr); mdio_delay(); outb(MDC, mdio_addr); mdio_delay(); } outl(0x00, mdio_addr); return;}/** * sis900_open: - open sis900 device * @net_dev: the net device to open * * Do some initialization and start net interface. * enable interrupts and set sis900 timer. */static intsis900_open(struct net_device *net_dev){ struct sis900_private *sis_priv = (struct sis900_private *)net_dev->priv; long ioaddr = net_dev->base_addr; u8 revision; MOD_INC_USE_COUNT; /* Soft reset the chip. */ sis900_reset(net_dev); /* Equalizer workaround Rule */ pci_read_config_byte(sis_priv->pci_dev, PCI_CLASS_REVISION, &revision); if (revision == SIS630E_900_REV || revision == SIS630EA1_900_REV || revision == SIS630A_900_REV) sis630_set_eq(net_dev,revision); if (request_irq(net_dev->irq, &sis900_interrupt, SA_SHIRQ, net_dev->name, net_dev)) { MOD_DEC_USE_COUNT; return -EAGAIN; } sis900_init_rxfilter(net_dev); sis900_init_tx_ring(net_dev); sis900_init_rx_ring(net_dev); set_rx_mode(net_dev); netif_start_queue(net_dev); /* 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;}/** * sis900_init_rxfilter: - Initialize the Rx filter * @net_dev: the net device to initialize for * * Set receive filter address to our MAC address * and enable packet filtering. */static voidsis900_init_rxfilter (struct net_device * net_dev){ long ioaddr = net_dev->base_addr; u32 rfcrSave; u32 i; rfcrSave = inl(rfcr + ioaddr); /* disable packet filtering before setting filter */ outl(rfcrSave & ~RFEN, rfcr); /* load MAC addr to filter data register */ for (i = 0 ; i < 3 ; i++) { u32 w; w = (u32) *((u16 *)(net_dev->dev_addr)+i); outl((i << RFADDR_shift), ioaddr + rfcr); outl(w, ioaddr + rfdr); if (sis900_debug > 2) { printk(KERN_INFO "%s: Receive Filter Addrss[%d]=%x\n", net_dev->name, i, inl(ioaddr + rfdr)); } } /* enable packet filitering */ outl(rfcrSave | RFEN, rfcr + ioaddr);}/** * sis900_init_tx_ring: - Initialize the Tx descriptor ring * @net_dev: the net device to initialize for * * Initialize the Tx descriptor ring, */static voidsis900_init_tx_ring(struct net_device *net_dev){ struct sis900_private *sis_priv = (struct sis900_private *)net_dev->priv; long ioaddr = net_dev->base_addr; int i; sis_priv->tx_full = 0; sis_priv->dirty_tx = sis_priv->cur_tx = 0; for (i = 0; i < NUM_TX_DESC; i++) { sis_priv->tx_skbuff[i] = NULL; sis_priv->tx_ring[i].link = (u32) virt_to_bus(&sis_priv->tx_ring[i+1]); sis_priv->tx_ring[i].cmdsts = 0; sis_priv->tx_ring[i].bufptr = 0; } sis_priv->tx_ring[i-1].link = (u32) virt_to_bus(&sis_priv->tx_ring[0]); /* load Transmit Descriptor Register */ outl(virt_to_bus(&sis_priv->tx_ring[0]), ioaddr + txdp); if (sis900_debug > 2) printk(KERN_INFO "%s: TX descriptor register loaded with: %8.8x\n", net_dev->name, inl(ioaddr + txdp));}/** * sis900_init_rx_ring: - Initialize the Rx descriptor ring * @net_dev: the net device to initialize for * * Initialize the Rx descriptor ring, * and pre-allocate recevie buffers (socket buffer) */static void sis900_init_rx_ring(struct net_device *net_dev){ struct sis900_private *sis_priv = (struct sis900_private *)net_dev->priv; long ioaddr = net_dev->base_addr; int i; sis_priv->cur_rx = 0; sis_priv->dirty_rx = 0; /* init RX descriptor */ for (i = 0; i < NUM_RX_DESC; i++) { sis_priv->rx_skbuff[i] = NULL; sis_priv->rx_ring[i].link = (u32) virt_to_bus(&sis_priv->rx_ring[i+1]); sis_priv->rx_ring[i].cmdsts = 0; sis_priv->rx_ring[i].bufptr = 0; } sis_priv->rx_ring[i-1].link = (u32) virt_to_bus(&sis_priv->rx_ring[0]); /* allocate sock buffers */ for (i = 0; i < NUM_RX_DESC; i++) { struct sk_buff *skb; if ((skb = dev_alloc_skb(RX_BUF_SIZE)) == NULL) { /* not enough memory for skbuff, this makes a "hole" on the buffer ring, it is not clear how the hardware will react to this kind of degenerated buffer */ break; } skb->dev = net_dev; sis_priv->rx_skbuff[i] = skb; sis_priv->rx_ring[i].cmdsts = RX_BUF_SIZE; sis_priv->rx_ring[i].bufptr = virt_to_bus(skb->tail); } sis_priv->dirty_rx = (unsigned int) (i - NUM_RX_DESC); /* load Receive Descriptor Register */ outl(virt_to_bus(&sis_priv->rx_ring[0]), ioaddr + rxdp); if (sis900_debug > 2) printk(KERN_INFO "%s: RX descriptor register loaded with: %8.8x\n", net_dev->name, inl(ioaddr + rxdp));}/** * sis630_set_eq: - set phy equalizer value for 630 LAN * @net_dev: the net device to set equalizer value * @revision: 630 LAN revision number * * 630E equalizer workaround rule(Cyrus Huang 08/15) * PHY register 14h(Test) * Bit 14: 0 -- Automatically dectect (default) * 1 -- Manually set Equalizer filter * Bit 13: 0 -- (Default) * 1 -- Speed up convergence of equalizer setting * Bit 9 : 0 -- (Default) * 1 -- Disable Baseline Wander * Bit 3~7 -- Equalizer filter setting * Link ON: Set Bit 9, 13 to 1, Bit 14 to 0 * Then calculate equalizer value * Then set equalizer value, and set Bit 14 to 1, Bit 9 to 0 * Link Off:Set Bit 13 to 1, Bit 14 to 0 * Calculate Equalizer value: * When Link is ON and Bit 14 is 0, SIS900PHY will auto-dectect proper equalizer value. * When the equalizer is stable, this value is not a fixed value. It will be within * a small range(eg. 7~9). Then we get a minimum and a maximum value(eg. min=7, max=9) * 0 <= max <= 4 --> set equalizer to max * 5 <= max <= 14 --> set equalizer to max+1 or set equalizer to max+2 if max == min * max >= 15 --> set equalizer to max+5 or set equalizer to max+6 if max == min */static void sis630_set_eq(struct net_device *net_dev, u8 revision){ struct sis900_private *sis_priv = (struct sis900_private *)net_dev->priv; u16 reg14h, eq_value, max_value=0, min_value=0; u8 host_bridge_rev; int i, maxcount=10; struct pci_dev *dev=NULL; if ((dev = pci_find_device(SIS630_DEVICE_ID, SIS630_VENDOR_ID, dev))) pci_read_config_byte(dev, PCI_CLASS_REVISION, &host_bridge_rev); if (netif_carrier_ok(net_dev)) { reg14h=mdio_read(net_dev, sis_priv->cur_phy, MII_RESV); mdio_write(net_dev, sis_priv->cur_phy, MII_RESV, (0x2200 | reg14h) & 0xBFFF); for (i=0; i < maxcount; i++) { eq_value=(0x00F8 & mdio_read(net_dev, sis_priv->cur_phy, MII_RESV)) >> 3; if (i == 0) max_value=min_value=eq_value; max_value=(eq_value > max_value) ? eq_value : max_value; min_value=(eq_value < min_value) ? eq_value : min_value; } /* 630E rule to determine the equalizer value */ if (revision == SIS630E_900_REV || revision == SIS630EA1_900_REV) { if (max_value < 5) eq_value=max_value; else if (max_value >= 5 && max_value < 15) eq_value=(max_value == min_value) ? max_value+2 : max_value+1; else if (max_value >= 15) eq_value=(max_value == min_value) ? max_value+6 : max_value+5; } /* 630A0 rule to determine the equalizer value */ if (revision == SIS630A_900_REV && host_bridge_rev == SIS630A0) { if (max_value < 5) eq_value=max_value+3; else if (max_value >= 5) eq_value=max_value+5; } /* 630B0&B1 rule to determine the equalizer value */ if (revision == SIS630A_900_REV && (host_bridge_rev == SIS630B0 || host_bridge_rev == SIS630B1)) { if (max_value == 0) eq_value=3; else eq_value=(max_value+min_value+1)/2; } /* write equalizer value and setting */ reg14h=mdio_read(net_dev, sis_priv->cur_phy, MII_RESV); reg14h=(reg14h & 0xFF07) | ((eq_value << 3) & 0x00F8); reg14h=(reg14h | 0x6000) & 0xFDFF; mdio_write(net_dev, sis_priv->cur_phy, MII_RESV, reg14h); } else { reg14h=mdio_read(net_dev, sis_priv->cur_phy, MII_RESV); mdio_write(net_dev, sis_priv->cur_phy, MII_RESV, (reg14h | 0x2000) & 0xBFFF); } return;}/** * sis900_timer: - sis900 timer routine * @data: pointer to sis900 net device * * 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 net_device *net_dev = (struct net_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; u8 revision; status = mdio_read(net_dev, sis_priv->cur_phy, MII_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 (netif_carrier_ok(net_dev)) { /* link stat change from ON to OFF */ next_tick = HZ; netif_carrier_off(net_dev); /* Equalizer workaround Rule */ pci_read_config_byte(sis_priv->pci_dev, PCI_CLASS_REVISION, &revision); if (revision == SIS630E_900_REV || revision == SIS630EA1_900_REV || revision == SIS630A_900_REV) sis630_set_eq(net_dev, revision); 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 (!netif_carrier_ok(net_dev)) { /* link stat change forn OFF to ON, read and report link mode */ netif_carrier_on(net_dev); next_tick = 5*HZ; /* Equalizer workaround Rule */ pci_read_config_byte(sis_priv->pci_dev, PCI_CLASS_REVISION, &revision); if (revision == SIS630E_900_REV || revision == SIS630EA1_900_REV ||
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -