📄 sis900.c
字号:
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){ 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 (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 = 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 (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 = 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; } 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 = pci_map_single(sis_priv->pci_dev, skb->tail, 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 (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 = net_dev->priv; u16 reg14h, eq_value=0, max_value=0, min_value=0; u8 host_bridge_rev; int i, maxcount=10; struct pci_dev *dev=NULL; if ( !(revision == SIS630E_900_REV || revision == SIS630EA1_900_REV || revision == SIS630A_900_REV || revision == SIS630ET_900_REV) ) return; dev = pci_find_device(PCI_VENDOR_ID_SI, PCI_DEVICE_ID_SI_630, dev); if (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 || 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 && (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); if (revision == SIS630A_900_REV && (host_bridge_rev == SIS630B0 || 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 int next_tick = 5*HZ; u16 status; u8 revision; 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); pci_read_config_byte(sis_priv->pci_dev, PCI_CLASS_REVISION, &revision); sis630_set_eq(net_dev, revision); 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); } } /* Link ON -> OFF */ else { if (!(status & MII_STAT_LINK)){ netif_carrier_off(net_dev); printk(KERN_INFO "%s: Media Link Off\n", net_dev->name); /* Change mode issue */ if ((mii_phy->phy_id0 == 0x001D) && ((mii_phy->phy_id1 & 0xFFF0) == 0x8000)) sis900_reset_phy(net_dev, sis_priv->cur_phy); pci_read_config_byte(sis_priv->pci_dev, PCI_CLASS_REVISION, &revision); sis630_set_eq(net_dev, revision); goto LookForLink; } } sis_priv->timer.expires = jiffies + next_tick; add_timer(&sis_priv->timer);}/** * sis900_check_mode: - check the media mode for sis900 * @net_dev: the net device to be checked * @mii_phy: the mii phy * * Older driver gets the media mode from mii status output * register. Now we set our media capability and auto-negotiate * to get the upper bound of speed and duplex between two ends. * If the types of mii phy is HOME, it doesn't need to auto-negotiate * and autong_complete should be set to 1. */static void sis900_check_mode (struct net_device *net_dev, struct mii_phy *mii_phy){ struct sis900_private *sis_priv = net_dev->priv; long ioaddr = net_dev->base_addr; int speed, duplex; if( mii_phy->phy_types == LAN ){ outl( ~EXD & inl( ioaddr + cfg ), ioaddr + cfg); sis900_set_capability(net_dev , mii_phy); sis900_auto_negotiate(net_dev, sis_priv->cur_phy); }else{ outl(EXD | inl( ioaddr + cfg ), ioaddr + cfg); speed = HW_SPEED_HOME; duplex = FDX_CAPABLE_HALF_SELECTED; sis900_set_mode(ioaddr, speed, duplex); sis_priv->autong_complete = 1; }}/** * sis900_set_mode: - Set the media mode of mac register. * @ioaddr: the address of the device * @speed : the transmit speed to be determined * @duplex: the duplex mode to be determined * * Set the media mode of mac register txcfg/rxcfg according to * speed and duplex of phy. Bit EDB_MASTER_EN indicates the EDB * bus is used instead of PCI bus. When this bit is set 1, the * Max DMA Burst Size for TX/RX DMA should be no larger than 16 * double words. */static void sis900_set_mode (long ioaddr, int speed, int duplex){ u32 tx_flags = 0, rx_flags = 0; if( inl(ioaddr + cfg) & EDB_MASTER_EN ){ tx_flags = TxATP | (DMA_BURST_64 << TxMXDMA_shift) | (TX_FILL_THRESH << TxFILLT_shift); rx_flags = DMA_BURST_64 << RxMXDMA_shift; } else{ tx_flags = TxATP | (DMA_BURST_512 << TxMXDMA_shift) | (TX_FILL_THRESH << TxFILLT_shift); rx_flags = DMA_BURST_512 << RxMXDMA_shift; } if (speed == HW_SPEED_HOME || speed == HW_SPEED_10_MBPS ) { rx_flags |= (RxDRNT_10 << RxDRNT_shift); tx_flags |= (TxDRNT_10 << TxDRNT_shift); } else { rx_flags |= (RxDRNT_100 << RxDRNT_shift); tx_flags |= (TxDRNT_100 << TxDRNT_shift); } if (duplex == FDX_CAPABLE_FULL_SELECTED) { tx_flags |= (TxCSI | TxHBI); rx_flags |= RxATX; } outl (tx_flags, ioaddr + txcfg); outl (rx_flags, ioaddr + rxcfg);}/** * sis900_auto_negotiate: Set the Auto-Negotiation Enable/Reset bit. * @net_dev: the net device to read mode for * @phy_addr: mii phy address * * If the adapter is link-on, set the auto-negotiate enable/reset bit. * autong_complete should be set to 0 when starting auto-negotiation. * autong_complete should be set to 1 if we didn't start auto-negotiation. * sis900_timer will wait for link on again if autong_complete = 0. */static void sis900_auto_negotiate(struct net_device *net_dev, int phy_addr){ struct sis900_private *sis_priv = net_dev->priv; int i = 0; u32 status; while (i++ < 2) status = mdio_read(net_dev, phy_addr, MII_STATUS); if (!(status & MII_STAT_LINK)){ printk(KERN_INFO "%s: Media Link Off\n", net_dev->name); sis_priv->autong_complete = 1; netif_carrier_off(net_dev); return; } /* (Re)start AutoNegotiate */ mdio_write(net_dev, phy_addr, MII_CONTROL, MII_CNTL_AUTO | MII_CNTL_RST_AUTO); sis_priv->autong_complete = 0;}/** * sis900_read_mode: - read media mode for sis900 internal phy * @net_dev: the net device to read mode for * @speed : the transmit speed to be determined * @duplex : the duplex mode to be determined * * The capability of remote end will be put in mii register autorec * after auto-negotiation. Use AND operation to get the upper bound * of speed and duplex between two ends. */static void sis900_read_mode(struct net_device *net_dev, int *speed, int *duplex){ struct sis900_private *sis_priv = net_dev->priv;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -