amd8111e.c
来自「Linux Kernel 2.6.9 for OMAP1710」· C语言 代码 · 共 2,175 行 · 第 1/4 页
C
2,175 行
/* stats.rx_frame_errors*/ new_stats->rx_frame_errors = amd8111e_read_mib(mmio, rcv_alignment_errors); /* stats.rx_fifo_errors */ new_stats->rx_fifo_errors = amd8111e_read_mib(mmio, rcv_miss_pkts); /* stats.rx_missed_errors */ new_stats->rx_missed_errors = amd8111e_read_mib(mmio, rcv_miss_pkts); /* stats.tx_aborted_errors*/ new_stats->tx_aborted_errors = amd8111e_read_mib(mmio, xmt_excessive_collision); /* stats.tx_carrier_errors*/ new_stats->tx_carrier_errors = amd8111e_read_mib(mmio, xmt_loss_carrier); /* stats.tx_fifo_errors*/ new_stats->tx_fifo_errors = amd8111e_read_mib(mmio, xmt_underrun_pkts); /* stats.tx_window_errors*/ new_stats->tx_window_errors = amd8111e_read_mib(mmio, xmt_late_collision); /* Reset the mibs for collecting new statistics */ /* writew(MIB_CLEAR, mmio + MIB_ADDR);*/ spin_unlock_irqrestore (&lp->lock, flags); return new_stats;}/* This function recalculate the interupt coalescing mode on every interrupt according to the datarate and the packet rate.*/static int amd8111e_calc_coalesce(struct net_device *dev){ struct amd8111e_priv *lp = netdev_priv(dev); struct amd8111e_coalesce_conf * coal_conf = &lp->coal_conf; int tx_pkt_rate; int rx_pkt_rate; int tx_data_rate; int rx_data_rate; int rx_pkt_size; int tx_pkt_size; tx_pkt_rate = coal_conf->tx_packets - coal_conf->tx_prev_packets; coal_conf->tx_prev_packets = coal_conf->tx_packets; tx_data_rate = coal_conf->tx_bytes - coal_conf->tx_prev_bytes; coal_conf->tx_prev_bytes = coal_conf->tx_bytes; rx_pkt_rate = coal_conf->rx_packets - coal_conf->rx_prev_packets; coal_conf->rx_prev_packets = coal_conf->rx_packets; rx_data_rate = coal_conf->rx_bytes - coal_conf->rx_prev_bytes; coal_conf->rx_prev_bytes = coal_conf->rx_bytes; if(rx_pkt_rate < 800){ if(coal_conf->rx_coal_type != NO_COALESCE){ coal_conf->rx_timeout = 0x0; coal_conf->rx_event_count = 0; amd8111e_set_coalesce(dev,RX_INTR_COAL); coal_conf->rx_coal_type = NO_COALESCE; } } else{ rx_pkt_size = rx_data_rate/rx_pkt_rate; if (rx_pkt_size < 128){ if(coal_conf->rx_coal_type != NO_COALESCE){ coal_conf->rx_timeout = 0; coal_conf->rx_event_count = 0; amd8111e_set_coalesce(dev,RX_INTR_COAL); coal_conf->rx_coal_type = NO_COALESCE; } } else if ( (rx_pkt_size >= 128) && (rx_pkt_size < 512) ){ if(coal_conf->rx_coal_type != LOW_COALESCE){ coal_conf->rx_timeout = 1; coal_conf->rx_event_count = 4; amd8111e_set_coalesce(dev,RX_INTR_COAL); coal_conf->rx_coal_type = LOW_COALESCE; } } else if ((rx_pkt_size >= 512) && (rx_pkt_size < 1024)){ if(coal_conf->rx_coal_type != MEDIUM_COALESCE){ coal_conf->rx_timeout = 1; coal_conf->rx_event_count = 4; amd8111e_set_coalesce(dev,RX_INTR_COAL); coal_conf->rx_coal_type = MEDIUM_COALESCE; } } else if(rx_pkt_size >= 1024){ if(coal_conf->rx_coal_type != HIGH_COALESCE){ coal_conf->rx_timeout = 2; coal_conf->rx_event_count = 3; amd8111e_set_coalesce(dev,RX_INTR_COAL); coal_conf->rx_coal_type = HIGH_COALESCE; } } } /* NOW FOR TX INTR COALESC */ if(tx_pkt_rate < 800){ if(coal_conf->tx_coal_type != NO_COALESCE){ coal_conf->tx_timeout = 0x0; coal_conf->tx_event_count = 0; amd8111e_set_coalesce(dev,TX_INTR_COAL); coal_conf->tx_coal_type = NO_COALESCE; } } else{ tx_pkt_size = tx_data_rate/tx_pkt_rate; if (tx_pkt_size < 128){ if(coal_conf->tx_coal_type != NO_COALESCE){ coal_conf->tx_timeout = 0; coal_conf->tx_event_count = 0; amd8111e_set_coalesce(dev,TX_INTR_COAL); coal_conf->tx_coal_type = NO_COALESCE; } } else if ( (tx_pkt_size >= 128) && (tx_pkt_size < 512) ){ if(coal_conf->tx_coal_type != LOW_COALESCE){ coal_conf->tx_timeout = 1; coal_conf->tx_event_count = 2; amd8111e_set_coalesce(dev,TX_INTR_COAL); coal_conf->tx_coal_type = LOW_COALESCE; } } else if ((tx_pkt_size >= 512) && (tx_pkt_size < 1024)){ if(coal_conf->tx_coal_type != MEDIUM_COALESCE){ coal_conf->tx_timeout = 2; coal_conf->tx_event_count = 5; amd8111e_set_coalesce(dev,TX_INTR_COAL); coal_conf->tx_coal_type = MEDIUM_COALESCE; } } else if(tx_pkt_size >= 1024){ if (tx_pkt_size >= 1024){ if(coal_conf->tx_coal_type != HIGH_COALESCE){ coal_conf->tx_timeout = 4; coal_conf->tx_event_count = 8; amd8111e_set_coalesce(dev,TX_INTR_COAL); coal_conf->tx_coal_type = HIGH_COALESCE; } } } } return 0;}/*This is device interrupt function. It handles transmit, receive,link change and hardware timer interrupts.*/static irqreturn_t amd8111e_interrupt(int irq, void *dev_id, struct pt_regs *regs){ struct net_device * dev = (struct net_device *) dev_id; struct amd8111e_priv *lp = netdev_priv(dev); void * mmio = lp->mmio; unsigned int intr0; unsigned int handled = 1; if(dev == NULL) return IRQ_NONE; if (regs) spin_lock (&lp->lock); /* disabling interrupt */ writel(INTREN, mmio + CMD0); /* Read interrupt status */ intr0 = readl(mmio + INT0); /* Process all the INT event until INTR bit is clear. */ if (!(intr0 & INTR)){ handled = 0; goto err_no_interrupt; } /* Current driver processes 4 interrupts : RINT,TINT,LCINT,STINT */ writel(intr0, mmio + INT0); /* Check if Receive Interrupt has occurred. */#if CONFIG_AMD8111E_NAPI if(intr0 & RINT0){ if(netif_rx_schedule_prep(dev)){ /* Disable receive interupts */ writel(RINTEN0, mmio + INTEN0); /* Schedule a polling routine */ __netif_rx_schedule(dev); } else { printk("************Driver bug! \ interrupt while in poll\n"); /* Fix by disabling interrupts */ writel(RINT0, mmio + INT0); } }#else if(intr0 & RINT0){ amd8111e_rx(dev); writel(VAL2 | RDMD0, mmio + CMD0); }#endif /* CONFIG_AMD8111E_NAPI */ /* Check if Transmit Interrupt has occurred. */ if(intr0 & TINT0) amd8111e_tx(dev); /* Check if Link Change Interrupt has occurred. */ if (intr0 & LCINT) amd8111e_link_change(dev); /* Check if Hardware Timer Interrupt has occurred. */ if (intr0 & STINT) amd8111e_calc_coalesce(dev);err_no_interrupt: writel( VAL0 | INTREN,mmio + CMD0); if (regs) spin_unlock(&lp->lock); return IRQ_RETVAL(handled);}#ifdef CONFIG_NET_POLL_CONTROLLERstatic void amd8111e_poll(struct net_device *dev){ unsigned long flags; local_save_flags(flags); local_irq_disable(); amd8111e_interrupt(0, dev, NULL); local_irq_restore(flags); } #endif/*This function closes the network interface and updates the statistics so that most recent statistics will be available after the interface is down.*/static int amd8111e_close(struct net_device * dev){ struct amd8111e_priv *lp = netdev_priv(dev); netif_stop_queue(dev); spin_lock_irq(&lp->lock); amd8111e_disable_interrupt(lp); amd8111e_stop_chip(lp); amd8111e_free_ring(lp); netif_carrier_off(lp->amd8111e_net_dev); /* Delete ipg timer */ if(lp->options & OPTION_DYN_IPG_ENABLE) del_timer_sync(&lp->ipg_data.ipg_timer); spin_unlock_irq(&lp->lock); free_irq(dev->irq, dev); /* Update the statistics before closing */ amd8111e_get_stats(dev); lp->opened = 0; return 0;}/* This function opens new interface.It requests irq for the device, initializes the device,buffers and descriptors, and starts the device. */static int amd8111e_open(struct net_device * dev ){ struct amd8111e_priv *lp = netdev_priv(dev); if(dev->irq ==0 || request_irq(dev->irq, amd8111e_interrupt, SA_SHIRQ, dev->name, dev)) return -EAGAIN; spin_lock_irq(&lp->lock); amd8111e_init_hw_default(lp); if(amd8111e_restart(dev)){ spin_unlock_irq(&lp->lock); return -ENOMEM; } /* Start ipg timer */ if(lp->options & OPTION_DYN_IPG_ENABLE){ add_timer(&lp->ipg_data.ipg_timer); printk(KERN_INFO "%s: Dynamic IPG Enabled.\n",dev->name); } lp->opened = 1; spin_unlock_irq(&lp->lock); netif_start_queue(dev); return 0; }/* This function checks if there is any transmit descriptors available to queue more packet.*/static int amd8111e_tx_queue_avail(struct amd8111e_priv* lp ){ int tx_index = lp->tx_idx & TX_BUFF_MOD_MASK; if(lp->tx_skbuff[tx_index] != 0) return -1; else return 0; }/* This function will queue the transmit packets to the descriptors and will trigger the send operation. It also initializes the transmit descriptors with buffer physical address, byte count, ownership to hardware etc.*/static int amd8111e_start_xmit(struct sk_buff *skb, struct net_device * dev){ struct amd8111e_priv *lp = netdev_priv(dev); int tx_index; unsigned long flags; spin_lock_irqsave(&lp->lock, flags); tx_index = lp->tx_idx & TX_RING_DR_MOD_MASK; lp->tx_ring[tx_index].buff_count = cpu_to_le16(skb->len); lp->tx_skbuff[tx_index] = skb; lp->tx_ring[tx_index].tx_flags = 0;#if AMD8111E_VLAN_TAG_USED if((lp->vlgrp != NULL) && vlan_tx_tag_present(skb)){ lp->tx_ring[tx_index].tag_ctrl_cmd |= cpu_to_le32(TCC_VLAN_INSERT); lp->tx_ring[tx_index].tag_ctrl_info = cpu_to_le16(vlan_tx_tag_get(skb)); }#endif lp->tx_dma_addr[tx_index] = pci_map_single(lp->pci_dev, skb->data, skb->len, PCI_DMA_TODEVICE); lp->tx_ring[tx_index].buff_phy_addr = (u32) cpu_to_le32(lp->tx_dma_addr[tx_index]); /* Set FCS and LTINT bits */ lp->tx_ring[tx_index].tx_flags |= cpu_to_le16(OWN_BIT | STP_BIT | ENP_BIT|ADD_FCS_BIT|LTINT_BIT); lp->tx_idx++; /* Trigger an immediate send poll. */ writel( VAL1 | TDMD0, lp->mmio + CMD0); writel( VAL2 | RDMD0,lp->mmio + CMD0); dev->trans_start = jiffies; if(amd8111e_tx_queue_avail(lp) < 0){ netif_stop_queue(dev); } spin_unlock_irqrestore(&lp->lock, flags); return 0;}/*This function returns all the memory mapped registers of the device.*/static char* amd8111e_read_regs(struct amd8111e_priv* lp){ void * mmio = lp->mmio; u32 * reg_buff; reg_buff = kmalloc( AMD8111E_REG_DUMP_LEN,GFP_KERNEL); if(NULL == reg_buff) return NULL; /* Read only necessary registers */ reg_buff[0] = readl(mmio + XMT_RING_BASE_ADDR0); reg_buff[1] = readl(mmio + XMT_RING_LEN0); reg_buff[2] = readl(mmio + RCV_RING_BASE_ADDR0); reg_buff[3] = readl(mmio + RCV_RING_LEN0); reg_buff[4] = readl(mmio + CMD0); reg_buff[5] = readl(mmio + CMD2); reg_buff[6] = readl(mmio + CMD3); reg_buff[7] = readl(mmio + CMD7); reg_buff[8] = readl(mmio + INT0); reg_buff[9] = readl(mmio + INTEN0); reg_buff[10] = readl(mmio + LADRF); reg_buff[11] = readl(mmio + LADRF+4); reg_buff[12] = readl(mmio + STAT0); return (char *)reg_buff;}/*amd8111e crc generator implementation is different from the kernelether_crc() function.*/int amd8111e_ether_crc(int len, char* mac_addr){ int i,byte; unsigned char octet; u32 crc= INITCRC; for(byte=0; byte < len; byte++){ octet = mac_addr[byte]; for( i=0;i < 8; i++){ /*If the next bit form the input stream is 1,subtract the divisor (CRC32) from the dividend(crc).*/ if( (octet & 0x1) ^ (crc & 0x1) ){ crc >>= 1; crc ^= CRC32; } else crc >>= 1; octet >>= 1; } } return crc; }/*This function sets promiscuos mode, all-multi mode or the multicast address list to the device.*/static void amd8111e_set_multicast_list(struct net_device *dev){ struct dev_mc_list* mc_ptr; struct amd8111e_priv *lp = netdev_priv(dev); u32 mc_filter[2] ; int i,bit_num; if(dev->flags & IFF_PROMISC){ printk(KERN_INFO "%s: Setting promiscuous mode.\n",dev->name); writel( VAL2 | PROM, lp->mmio + CMD2); return; } else writel( PROM, lp->mmio + CMD2); if(dev->flags & IFF_ALLMULTI || dev->mc_count > MAX_FILTER_SIZE){ /* get all multicast packet */ mc_filter[1] = mc_filter[0] = 0xffffffff; lp->mc_list = dev->mc_list; lp->options |= OPTION_MULTICAST_ENABLE; amd8111e_writeq(*(u64*)mc_filter,lp->mmio + LADRF); return; } if( dev->mc_count == 0 ){ /* get only own packets */ mc_filter[1] = mc_filter[0] = 0; lp->mc_list = NULL; lp->options &= ~OPTION_MULTICAST_ENABLE; amd8111e_writeq(*(u64*)mc_filter,lp->mmio + LADRF); /* disable promiscous mode */ writel(PROM, lp->mmio + CMD2); return; } /* load all the multicast addresses in the logic filter */ lp->options |= OPTION_MULTICAST_ENABLE; lp->mc_list = dev->mc_list; mc_filter[1] = mc_filter[0] = 0; for (i = 0, mc_ptr = dev->mc_list; mc_ptr && i < dev->mc_count; i++, mc_ptr = mc_ptr->next) { bit_num = ( amd8111e_ether_crc(ETH_ALEN,mc_ptr->dmi_addr) >> 26 ) & 0x3f; mc_filter[bit_num >> 5] |= 1 << (bit_num & 31); } amd8111e_writeq(*(u64*)mc_filter,lp->mmio+ LADRF); /* To eliminate PCI posting bug */ readl(lp->mmio + CMD2);}/*This function handles all the ethtool ioctls. It gives driver info, gets/sets driver speed, gets memory mapped register values, forces auto negotiation, sets/gets WOL options for ethtool application. */ static int amd8111e_ethtool_ioctl(struct net_device* dev, void __user *useraddr){ struct amd8111e_priv *lp = netdev_priv(dev); struct pci_dev *pci_dev = lp->pci_dev; u32 ethcmd; if( useraddr == NULL) return -EINVAL; if(copy_from_user (ðcmd, useraddr, sizeof (ethcmd))) return -EFAULT; switch(ethcmd){ case ETHTOOL_GDRVINFO:{ struct ethtool_drvinfo info = { ETHTOOL_GDRVINFO }; strcpy (info.driver, MODULE_NAME); strcpy (info.version, MODULE_VERS); memset(&info.fw_version, 0, sizeof(info.fw_version)); sprintf(info.fw_version,"%u",chip_version); strcpy (info.bus_info, pci_name(pci_dev)); info.eedump_len = 0; info.regdump_len = AMD8111E_REG_DUMP_LEN; if (copy_to_user (useraddr, &info, sizeof(info))) return -EFAULT; return 0; } /* get settings */ case ETHTOOL_GSET: { struct ethtool_cmd ecmd = { ETHTOOL_GSET }; spin_lock_irq(&lp->lock); mii_ethtool_gset(&lp->mii_if, &ecmd); spin_unlock_irq(&lp->lock); if (copy_to_user(useraddr, &ecmd, sizeof(ecmd))) return -EFAULT; return 0; } /* set settings */ case ETHTOOL_SSET: { int r; struct ethtool_cmd ecmd; if (copy_from_user(&ecmd, useraddr, sizeof(ecmd))) return -EFAULT; spin_lock_irq(&lp->lock); r = mii_ethtool_sset(&lp->mii_if, &ecmd); spin_unlock_irq(&lp->lock); return r; } case ETHTOOL_GREGS: { struct ethtool_regs regs; u8 *regbuf; int ret; if (copy_from_user(®s, useraddr, sizeof(regs))) return -EFAULT; if (regs.len > AMD8111E_REG_DUMP_LEN) regs.len = AMD8111E_REG_DUMP_LEN; regs.version = 0; if (copy_to_user(useraddr, ®s, sizeof(regs)))
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?