natsemi.c
来自「linux 内核源代码」· C语言 代码 · 共 2,280 行 · 第 1/5 页
C
2,280 行
#define RX_DRTH_VAL (128/8)enum ClkRun_bits { PMEEnable = 0x100, PMEStatus = 0x8000,};enum WolCmd_bits { WakePhy = 0x1, WakeUnicast = 0x2, WakeMulticast = 0x4, WakeBroadcast = 0x8, WakeArp = 0x10, WakePMatch0 = 0x20, WakePMatch1 = 0x40, WakePMatch2 = 0x80, WakePMatch3 = 0x100, WakeMagic = 0x200, WakeMagicSecure = 0x400, SecureHack = 0x100000, WokePhy = 0x400000, WokeUnicast = 0x800000, WokeMulticast = 0x1000000, WokeBroadcast = 0x2000000, WokeArp = 0x4000000, WokePMatch0 = 0x8000000, WokePMatch1 = 0x10000000, WokePMatch2 = 0x20000000, WokePMatch3 = 0x40000000, WokeMagic = 0x80000000, WakeOptsSummary = 0x7ff};enum RxFilterAddr_bits { RFCRAddressMask = 0x3ff, AcceptMulticast = 0x00200000, AcceptMyPhys = 0x08000000, AcceptAllPhys = 0x10000000, AcceptAllMulticast = 0x20000000, AcceptBroadcast = 0x40000000, RxFilterEnable = 0x80000000};enum StatsCtrl_bits { StatsWarn = 0x1, StatsFreeze = 0x2, StatsClear = 0x4, StatsStrobe = 0x8,};enum MIntrCtrl_bits { MICRIntEn = 0x2,};enum PhyCtrl_bits { PhyAddrMask = 0x1f,};#define PHY_ADDR_NONE 32#define PHY_ADDR_INTERNAL 1/* values we might find in the silicon revision register */#define SRR_DP83815_C 0x0302#define SRR_DP83815_D 0x0403#define SRR_DP83816_A4 0x0504#define SRR_DP83816_A5 0x0505/* The Rx and Tx buffer descriptors. *//* Note that using only 32 bit fields simplifies conversion to big-endian architectures. */struct netdev_desc { u32 next_desc; s32 cmd_status; u32 addr; u32 software_use;};/* Bits in network_desc.status */enum desc_status_bits { DescOwn=0x80000000, DescMore=0x40000000, DescIntr=0x20000000, DescNoCRC=0x10000000, DescPktOK=0x08000000, DescSizeMask=0xfff, DescTxAbort=0x04000000, DescTxFIFO=0x02000000, DescTxCarrier=0x01000000, DescTxDefer=0x00800000, DescTxExcDefer=0x00400000, DescTxOOWCol=0x00200000, DescTxExcColl=0x00100000, DescTxCollCount=0x000f0000, DescRxAbort=0x04000000, DescRxOver=0x02000000, DescRxDest=0x01800000, DescRxLong=0x00400000, DescRxRunt=0x00200000, DescRxInvalid=0x00100000, DescRxCRC=0x00080000, DescRxAlign=0x00040000, DescRxLoop=0x00020000, DesRxColl=0x00010000,};struct netdev_private { /* Descriptor rings first for alignment */ dma_addr_t ring_dma; struct netdev_desc *rx_ring; struct netdev_desc *tx_ring; /* The addresses of receive-in-place skbuffs */ struct sk_buff *rx_skbuff[RX_RING_SIZE]; dma_addr_t rx_dma[RX_RING_SIZE]; /* address of a sent-in-place packet/buffer, for later free() */ struct sk_buff *tx_skbuff[TX_RING_SIZE]; dma_addr_t tx_dma[TX_RING_SIZE]; struct net_device *dev; struct napi_struct napi; struct net_device_stats stats; /* Media monitoring timer */ struct timer_list timer; /* Frequently used values: keep some adjacent for cache effect */ struct pci_dev *pci_dev; struct netdev_desc *rx_head_desc; /* Producer/consumer ring indices */ unsigned int cur_rx, dirty_rx; unsigned int cur_tx, dirty_tx; /* Based on MTU+slack. */ unsigned int rx_buf_sz; int oom; /* Interrupt status */ u32 intr_status; /* Do not touch the nic registers */ int hands_off; /* Don't pay attention to the reported link state. */ int ignore_phy; /* external phy that is used: only valid if dev->if_port != PORT_TP */ int mii; int phy_addr_external; unsigned int full_duplex; /* Rx filter */ u32 cur_rx_mode; u32 rx_filter[16]; /* FIFO and PCI burst thresholds */ u32 tx_config, rx_config; /* original contents of ClkRun register */ u32 SavedClkRun; /* silicon revision */ u32 srr; /* expected DSPCFG value */ u16 dspcfg; int dspcfg_workaround; /* parms saved in ethtool format */ u16 speed; /* The forced speed, 10Mb, 100Mb, gigabit */ u8 duplex; /* Duplex, half or full */ u8 autoneg; /* Autonegotiation enabled */ /* MII transceiver section */ u16 advertising; unsigned int iosize; spinlock_t lock; u32 msg_enable; /* EEPROM data */ int eeprom_size;};static void move_int_phy(struct net_device *dev, int addr);static int eeprom_read(void __iomem *ioaddr, int location);static int mdio_read(struct net_device *dev, int reg);static void mdio_write(struct net_device *dev, int reg, u16 data);static void init_phy_fixup(struct net_device *dev);static int miiport_read(struct net_device *dev, int phy_id, int reg);static void miiport_write(struct net_device *dev, int phy_id, int reg, u16 data);static int find_mii(struct net_device *dev);static void natsemi_reset(struct net_device *dev);static void natsemi_reload_eeprom(struct net_device *dev);static void natsemi_stop_rxtx(struct net_device *dev);static int netdev_open(struct net_device *dev);static void do_cable_magic(struct net_device *dev);static void undo_cable_magic(struct net_device *dev);static void check_link(struct net_device *dev);static void netdev_timer(unsigned long data);static void dump_ring(struct net_device *dev);static void tx_timeout(struct net_device *dev);static int alloc_ring(struct net_device *dev);static void refill_rx(struct net_device *dev);static void init_ring(struct net_device *dev);static void drain_tx(struct net_device *dev);static void drain_ring(struct net_device *dev);static void free_ring(struct net_device *dev);static void reinit_ring(struct net_device *dev);static void init_registers(struct net_device *dev);static int start_tx(struct sk_buff *skb, struct net_device *dev);static irqreturn_t intr_handler(int irq, void *dev_instance);static void netdev_error(struct net_device *dev, int intr_status);static int natsemi_poll(struct napi_struct *napi, int budget);static void netdev_rx(struct net_device *dev, int *work_done, int work_to_do);static void netdev_tx_done(struct net_device *dev);static int natsemi_change_mtu(struct net_device *dev, int new_mtu);#ifdef CONFIG_NET_POLL_CONTROLLERstatic void natsemi_poll_controller(struct net_device *dev);#endifstatic void __set_rx_mode(struct net_device *dev);static void set_rx_mode(struct net_device *dev);static void __get_stats(struct net_device *dev);static struct net_device_stats *get_stats(struct net_device *dev);static int netdev_ioctl(struct net_device *dev, struct ifreq *rq, int cmd);static int netdev_set_wol(struct net_device *dev, u32 newval);static int netdev_get_wol(struct net_device *dev, u32 *supported, u32 *cur);static int netdev_set_sopass(struct net_device *dev, u8 *newval);static int netdev_get_sopass(struct net_device *dev, u8 *data);static int netdev_get_ecmd(struct net_device *dev, struct ethtool_cmd *ecmd);static int netdev_set_ecmd(struct net_device *dev, struct ethtool_cmd *ecmd);static void enable_wol_mode(struct net_device *dev, int enable_intr);static int netdev_close(struct net_device *dev);static int netdev_get_regs(struct net_device *dev, u8 *buf);static int netdev_get_eeprom(struct net_device *dev, u8 *buf);static const struct ethtool_ops ethtool_ops;#define NATSEMI_ATTR(_name) \static ssize_t natsemi_show_##_name(struct device *dev, \ struct device_attribute *attr, char *buf); \ static ssize_t natsemi_set_##_name(struct device *dev, \ struct device_attribute *attr, \ const char *buf, size_t count); \ static DEVICE_ATTR(_name, 0644, natsemi_show_##_name, natsemi_set_##_name)#define NATSEMI_CREATE_FILE(_dev, _name) \ device_create_file(&_dev->dev, &dev_attr_##_name)#define NATSEMI_REMOVE_FILE(_dev, _name) \ device_remove_file(&_dev->dev, &dev_attr_##_name)NATSEMI_ATTR(dspcfg_workaround);static ssize_t natsemi_show_dspcfg_workaround(struct device *dev, struct device_attribute *attr, char *buf){ struct netdev_private *np = netdev_priv(to_net_dev(dev)); return sprintf(buf, "%s\n", np->dspcfg_workaround ? "on" : "off");}static ssize_t natsemi_set_dspcfg_workaround(struct device *dev, struct device_attribute *attr, const char *buf, size_t count){ struct netdev_private *np = netdev_priv(to_net_dev(dev)); int new_setting; unsigned long flags; /* Find out the new setting */ if (!strncmp("on", buf, count - 1) || !strncmp("1", buf, count - 1)) new_setting = 1; else if (!strncmp("off", buf, count - 1) || !strncmp("0", buf, count - 1)) new_setting = 0; else return count; spin_lock_irqsave(&np->lock, flags); np->dspcfg_workaround = new_setting; spin_unlock_irqrestore(&np->lock, flags); return count;}static inline void __iomem *ns_ioaddr(struct net_device *dev){ return (void __iomem *) dev->base_addr;}static inline void natsemi_irq_enable(struct net_device *dev){ writel(1, ns_ioaddr(dev) + IntrEnable); readl(ns_ioaddr(dev) + IntrEnable);}static inline void natsemi_irq_disable(struct net_device *dev){ writel(0, ns_ioaddr(dev) + IntrEnable); readl(ns_ioaddr(dev) + IntrEnable);}static void move_int_phy(struct net_device *dev, int addr){ struct netdev_private *np = netdev_priv(dev); void __iomem *ioaddr = ns_ioaddr(dev); int target = 31; /* * The internal phy is visible on the external mii bus. Therefore we must * move it away before we can send commands to an external phy. * There are two addresses we must avoid: * - the address on the external phy that is used for transmission. * - the address that we want to access. User space can access phys * on the mii bus with SIOCGMIIREG/SIOCSMIIREG, independant from the * phy that is used for transmission. */ if (target == addr) target--; if (target == np->phy_addr_external) target--; writew(target, ioaddr + PhyCtrl); readw(ioaddr + PhyCtrl); udelay(1);}static void __devinit natsemi_init_media (struct net_device *dev){ struct netdev_private *np = netdev_priv(dev); u32 tmp; if (np->ignore_phy) netif_carrier_on(dev); else netif_carrier_off(dev); /* get the initial settings from hardware */ tmp = mdio_read(dev, MII_BMCR); np->speed = (tmp & BMCR_SPEED100)? SPEED_100 : SPEED_10; np->duplex = (tmp & BMCR_FULLDPLX)? DUPLEX_FULL : DUPLEX_HALF; np->autoneg = (tmp & BMCR_ANENABLE)? AUTONEG_ENABLE: AUTONEG_DISABLE; np->advertising= mdio_read(dev, MII_ADVERTISE); if ((np->advertising & ADVERTISE_ALL) != ADVERTISE_ALL && netif_msg_probe(np)) { printk(KERN_INFO "natsemi %s: Transceiver default autonegotiation %s " "10%s %s duplex.\n", pci_name(np->pci_dev), (mdio_read(dev, MII_BMCR) & BMCR_ANENABLE)? "enabled, advertise" : "disabled, force", (np->advertising & (ADVERTISE_100FULL|ADVERTISE_100HALF))? "0" : "", (np->advertising & (ADVERTISE_100FULL|ADVERTISE_10FULL))? "full" : "half"); } if (netif_msg_probe(np)) printk(KERN_INFO "natsemi %s: Transceiver status %#04x advertising %#04x.\n", pci_name(np->pci_dev), mdio_read(dev, MII_BMSR), np->advertising);}static int __devinit natsemi_probe1 (struct pci_dev *pdev, const struct pci_device_id *ent){ struct net_device *dev; struct netdev_private *np; int i, option, irq, chip_idx = ent->driver_data; static int find_cnt = -1; unsigned long iostart, iosize; void __iomem *ioaddr; const int pcibar = 1; /* PCI base address register */ int prev_eedata; u32 tmp; DECLARE_MAC_BUF(mac);/* when built into the kernel, we only print version if device is found */#ifndef MODULE static int printed_version; if (!printed_version++) printk(version);#endif i = pci_enable_device(pdev); if (i) return i; /* natsemi has a non-standard PM control register * in PCI config space. Some boards apparently need * to be brought to D0 in this manner. */ pci_read_config_dword(pdev, PCIPM, &tmp); if (tmp & PCI_PM_CTRL_STATE_MASK) { /* D0 state, disable PME assertion */ u32 newtmp = tmp & ~PCI_PM_CTRL_STATE_MASK; pci_write_config_dword(pdev, PCIPM, newtmp); } find_cnt++; iostart = pci_resource_start(pdev, pcibar); iosize = pci_resource_len(pdev, pcibar); irq = pdev->irq; pci_set_master(pdev); dev = alloc_etherdev(sizeof (struct netdev_private)); if (!dev) return -ENOMEM; SET_NETDEV_DEV(dev, &pdev->dev); i = pci_request_regions(pdev, DRV_NAME); if (i) goto err_pci_request_regions; ioaddr = ioremap(iostart, iosize); if (!ioaddr) { i = -ENOMEM; goto err_ioremap; } /* Work around the dropped serial bit. */ prev_eedata = eeprom_read(ioaddr, 6); for (i = 0; i < 3; i++) { int eedata = eeprom_read(ioaddr, i + 7); dev->dev_addr[i*2] = (eedata << 1) + (prev_eedata >> 15); dev->dev_addr[i*2+1] = eedata >> 7; prev_eedata = eedata; } dev->base_addr = (unsigned long __force) ioaddr; dev->irq = irq; np = netdev_priv(dev); netif_napi_add(dev, &np->napi, natsemi_poll, 64); np->dev = dev; np->pci_dev = pdev; pci_set_drvdata(pdev, dev); np->iosize = iosize; spin_lock_init(&np->lock); np->msg_enable = (debug >= 0) ? (1<<debug)-1 : NATSEMI_DEF_MSG; np->hands_off = 0; np->intr_status = 0; np->eeprom_size = natsemi_pci_info[chip_idx].eeprom_size; if (natsemi_pci_info[chip_idx].flags & NATSEMI_FLAG_IGNORE_PHY) np->ignore_phy = 1; else np->ignore_phy = 0; np->dspcfg_workaround = dspcfg_workaround; /* Initial port: * - If configured to ignore the PHY set up for external. * - If the nic was configured to use an external phy and if find_mii * finds a phy: use external port, first phy that replies. * - Otherwise: internal port. * Note that the phy address for the internal phy doesn't matter: * The address would be used to access a phy over the mii bus, but * the internal phy is accessed through mapped registers. */ if (np->ignore_phy || readl(ioaddr + ChipConfig) & CfgExtPhy) dev->if_port = PORT_MII; else dev->if_port = PORT_TP; /* Reset the chip to erase previous misconfiguration. */ natsemi_reload_eeprom(dev); natsemi_reset(dev); if (dev->if_port != PORT_TP) { np->phy_addr_external = find_mii(dev); /* If we're ignoring the PHY it doesn't matter if we can't * find one. */ if (!np->ignore_phy && np->phy_addr_external == PHY_ADDR_NONE) { dev->if_port = PORT_TP; np->phy_addr_external = PHY_ADDR_INTERNAL; } } else { np->phy_addr_external = PHY_ADDR_INTERNAL; } option = find_cnt < MAX_UNITS ? options[find_cnt] : 0;
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?