sis900.c
来自「linux 内核源代码」· C语言 代码 · 共 2,188 行 · 第 1/5 页
C
2,188 行
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_reset_phy - reset sis900 mii phy. * @net_dev: the net device to write * @phy_addr: default phy address * * Some specific phy can't work properly without reset. * This function will be called during initialization and * link status change from ON to DOWN. */static u16 sis900_reset_phy(struct net_device *net_dev, int phy_addr){ int i; u16 status; for (i = 0; i < 2; i++) status = mdio_read(net_dev, phy_addr, MII_STATUS); mdio_write( net_dev, phy_addr, MII_CONTROL, MII_CNTL_RESET ); return status;}#ifdef CONFIG_NET_POLL_CONTROLLER/* * Polling 'interrupt' - used by things like netconsole to send skbs * without having to re-enable interrupts. It's not called while * the interrupt routine is executing.*/static void sis900_poll(struct net_device *dev){ disable_irq(dev->irq); sis900_interrupt(dev->irq, dev); enable_irq(dev->irq);}#endif/** * 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 = net_dev->priv; long ioaddr = net_dev->base_addr; int ret; /* Soft reset the chip. */ sis900_reset(net_dev); /* Equalizer workaround Rule */ sis630_set_eq(net_dev, sis_priv->chipset_rev); ret = request_irq(net_dev->irq, &sis900_interrupt, IRQF_SHARED, net_dev->name, net_dev); if (ret) return ret; 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); /* Workaround for EDB */ sis900_set_mode(ioaddr, HW_SPEED_10_MBPS, FDX_CAPABLE_HALF_SELECTED); /* Enable all known interrupts by setting the interrupt mask. */ outl((RxSOVR|RxORN|RxERR|RxOK|TxURN|TxERR|TxIDLE), ioaddr + imr); outl(RxENA | inl(ioaddr + cr), 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){ struct sis900_private *sis_priv = net_dev->priv; long ioaddr = net_dev->base_addr; u32 rfcrSave; u32 i; rfcrSave = inl(rfcr + ioaddr); /* disable packet filtering before setting filter */ outl(rfcrSave & ~RFEN, rfcr + ioaddr); /* 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 (netif_msg_hw(sis_priv)) { printk(KERN_DEBUG "%s: Receive Filter Addrss[%d]=%x\n", net_dev->name, i, inl(ioaddr + rfdr)); } } /* enable packet filtering */ 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 = 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 = sis_priv->tx_ring_dma + ((i+1)%NUM_TX_DESC)*sizeof(BufferDesc); sis_priv->tx_ring[i].cmdsts = 0; sis_priv->tx_ring[i].bufptr = 0; } /* load Transmit Descriptor Register */ outl(sis_priv->tx_ring_dma, ioaddr + txdp); if (netif_msg_hw(sis_priv)) printk(KERN_DEBUG "%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 voidsis900_init_rx_ring(struct net_device *net_dev){ struct sis900_private *sis_priv = 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 = sis_priv->rx_ring_dma + ((i+1)%NUM_RX_DESC)*sizeof(BufferDesc); sis_priv->rx_ring[i].cmdsts = 0; sis_priv->rx_ring[i].bufptr = 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; } sis_priv->rx_skbuff[i] = skb; sis_priv->rx_ring[i].cmdsts = RX_BUF_SIZE; sis_priv->rx_ring[i].bufptr = pci_map_single(sis_priv->pci_dev, skb->data, RX_BUF_SIZE, PCI_DMA_FROMDEVICE); } sis_priv->dirty_rx = (unsigned int) (i - NUM_RX_DESC); /* load Receive Descriptor Register */ outl(sis_priv->rx_ring_dma, ioaddr + rxdp); if (netif_msg_hw(sis_priv)) printk(KERN_DEBUG "%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 = net_dev->priv; u16 reg14h, eq_value=0, max_value=0, min_value=0; int i, maxcount=10; if ( !(revision == SIS630E_900_REV || revision == SIS630EA1_900_REV || revision == SIS630A_900_REV || revision == SIS630ET_900_REV) ) return; 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 || revision == SIS630ET_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; } /* 630B0&B1 rule to determine the equalizer value */ if (revision == SIS630A_900_REV && (sis_priv->host_bridge_rev == SIS630B0 || sis_priv->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); if (revision == SIS630A_900_REV && (sis_priv->host_bridge_rev == SIS630B0 || sis_priv->host_bridge_rev == SIS630B1)) mdio_write(net_dev, sis_priv->cur_phy, MII_RESV, (reg14h | 0x2200) & 0xBFFF); else 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 = net_dev->priv; struct mii_phy *mii_phy = sis_priv->mii; static const int next_tick = 5*HZ; u16 status; if (!sis_priv->autong_complete){ int speed, duplex = 0; sis900_read_mode(net_dev, &speed, &duplex); if (duplex){ sis900_set_mode(net_dev->base_addr, speed, duplex); sis630_set_eq(net_dev, sis_priv->chipset_rev); netif_start_queue(net_dev); } sis_priv->timer.expires = jiffies + HZ; add_timer(&sis_priv->timer); return; } status = mdio_read(net_dev, sis_priv->cur_phy, MII_STATUS); status = mdio_read(net_dev, sis_priv->cur_phy, MII_STATUS); /* Link OFF -> ON */ if (!netif_carrier_ok(net_dev)) { LookForLink: /* Search for new PHY */ status = sis900_default_phy(net_dev); mii_phy = sis_priv->mii; if (status & MII_STAT_LINK){ sis900_check_mode(net_dev, mii_phy); netif_carrier_on(net_dev); } } else { /* Link ON -> OFF */ if (!(status & MII_STAT_LINK)){ netif_carrier_off(net_dev); if(netif_msg_link(sis_priv)) printk(KERN_INFO "%s: Media Link Off\n", net_dev->name);
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?