X-Git-Url: http://ftp.safe.ca/?a=blobdiff_plain;f=drivers%2Fnet%2Fnatsemi.c;h=6bb48ba809649ca7e3ab940f42ba98ba1f9dc643;hb=304e3bb7a15d839cef64b24a1bcc1230b2ae6fa6;hp=109e8025248855c9636864c9550bd575957f6196;hpb=d0ed48640e746a5537d0e7c49d5029949b15ee88;p=safe%2Fjmp%2Flinux-2.6 diff --git a/drivers/net/natsemi.c b/drivers/net/natsemi.c index 109e802..6bb48ba 100644 --- a/drivers/net/natsemi.c +++ b/drivers/net/natsemi.c @@ -81,6 +81,8 @@ static const int multicast_filter_limit = 100; Setting to > 1518 effectively disables this feature. */ static int rx_copybreak; +static int dspcfg_workaround = 1; + /* Used to pass the media type, etc. Both 'options[]' and 'full_duplex[]' should exist for driver interoperability. @@ -129,7 +131,6 @@ static const char version[] __devinitdata = KERN_INFO DRV_NAME " dp8381x driver, version " DRV_VERSION ", " DRV_RELDATE "\n" KERN_INFO " originally by Donald Becker \n" - KERN_INFO " http://www.scyld.com/network/natsemi.html\n" KERN_INFO " 2.4.x kernel port by Jeff Garzik, Tjeerd Mulder\n"; MODULE_AUTHOR("Donald Becker "); @@ -139,12 +140,14 @@ MODULE_LICENSE("GPL"); module_param(mtu, int, 0); module_param(debug, int, 0); module_param(rx_copybreak, int, 0); +module_param(dspcfg_workaround, int, 1); module_param_array(options, int, NULL, 0); module_param_array(full_duplex, int, NULL, 0); MODULE_PARM_DESC(mtu, "DP8381x MTU (all boards)"); MODULE_PARM_DESC(debug, "DP8381x default debug level"); MODULE_PARM_DESC(rx_copybreak, "DP8381x copy breakpoint for copy-only-tiny-frames"); +MODULE_PARM_DESC(dspcfg_workaround, "DP8381x: control DspCfg workaround"); MODULE_PARM_DESC(options, "DP8381x: Bits 0-3: media type, bit 17: full duplex"); MODULE_PARM_DESC(full_duplex, "DP8381x full duplex setting(s) (1)"); @@ -590,6 +593,7 @@ struct netdev_private { 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 */ @@ -656,6 +660,56 @@ 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; @@ -820,6 +874,7 @@ static int __devinit natsemi_probe1 (struct pci_dev *pdev, 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. @@ -899,6 +954,9 @@ static int __devinit natsemi_probe1 (struct pci_dev *pdev, if (i) goto err_register_netdev; + if (NATSEMI_CREATE_FILE(pdev, dspcfg_workaround)) + goto err_create_file; + if (netif_msg_drv(np)) { printk(KERN_INFO "natsemi %s: %s at %#08lx (%s), ", dev->name, natsemi_pci_info[chip_idx].name, iostart, @@ -915,6 +973,9 @@ static int __devinit natsemi_probe1 (struct pci_dev *pdev, } return 0; + err_create_file: + unregister_netdev(dev); + err_register_netdev: iounmap(ioaddr); @@ -1727,7 +1788,8 @@ static void init_registers(struct net_device *dev) * It seems that a reference set for this chip went out with incorrect info, * and there exist boards that aren't quite right. An unexpected voltage * drop can cause the PHY to get itself in a weird state (basically reset). - * NOTE: this only seems to affect revC chips. + * NOTE: this only seems to affect revC chips. The user can disable + * this check via dspcfg_workaround sysfs option. * 3) check of death of the RX path due to OOM */ static void netdev_timer(unsigned long data) @@ -1753,7 +1815,7 @@ static void netdev_timer(unsigned long data) writew(1, ioaddr+PGSEL); dspcfg = readw(ioaddr+DSPCFG); writew(0, ioaddr+PGSEL); - if (dspcfg != np->dspcfg) { + if (np->dspcfg_workaround && dspcfg != np->dspcfg) { if (!netif_queue_stopped(dev)) { spin_unlock_irq(&np->lock); if (netif_msg_drv(np)) @@ -2295,8 +2357,8 @@ static void netdev_rx(struct net_device *dev, int *work_done, int work_to_do) np->rx_dma[entry], buflen, PCI_DMA_FROMDEVICE); - eth_copy_and_sum(skb, - np->rx_skbuff[entry]->data, pkt_len, 0); + skb_copy_to_linear_data(skb, + np->rx_skbuff[entry]->data, pkt_len); skb_put(skb, pkt_len); pci_dma_sync_single_for_device(np->pci_dev, np->rx_dma[entry], @@ -3157,6 +3219,7 @@ static void __devexit natsemi_remove1 (struct pci_dev *pdev) struct net_device *dev = pci_get_drvdata(pdev); void __iomem * ioaddr = ns_ioaddr(dev); + NATSEMI_REMOVE_FILE(pdev, dspcfg_workaround); unregister_netdev (dev); pci_release_regions (pdev); iounmap(ioaddr);