aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMark Brown <broonie@sirena.org.uk>2007-05-03 05:36:56 -0400
committerJeff Garzik <jeff@garzik.org>2007-05-08 01:15:35 -0400
commit1a14780960888c97371a9918f42c4dbe6957efb4 (patch)
tree66c9d7ed4d983fd4b67c4213d5f2d6a7812bada1
parentd0ed48640e746a5537d0e7c49d5029949b15ee88 (diff)
Subject: natsemi: Allow users to disable workaround for DspCfg reset
The natsemi driver contains a workaround for broken hardware which can partially reset the chip at unpredictable times, detected by checking for spontaneous changes in the DspCfg register. The effects of the hardware bug appear to be variable and can range from minor problems like small numbers of corrupted packets to major ones such as the chip becoming non-functional. In the case of minor problems the chip reconfiguration required to work around the hardware can cause more problems than the bug itself. Since we have no way of automatically determining how badly the problem manifests on any given system provide an option in sysfs allowing users to disable the workaround at runtime and provides a module option to set the default. Signed-Off-By: Mark Brown <broonie@sirena.org.uk> Signed-off-by: Jeff Garzik <jeff@garzik.org>
-rw-r--r--drivers/net/natsemi.c68
1 files changed, 66 insertions, 2 deletions
diff --git a/drivers/net/natsemi.c b/drivers/net/natsemi.c
index 109e80252488..223e0e6264ba 100644
--- a/drivers/net/natsemi.c
+++ b/drivers/net/natsemi.c
@@ -81,6 +81,8 @@ static const int multicast_filter_limit = 100;
81 Setting to > 1518 effectively disables this feature. */ 81 Setting to > 1518 effectively disables this feature. */
82static int rx_copybreak; 82static int rx_copybreak;
83 83
84static int dspcfg_workaround = 1;
85
84/* Used to pass the media type, etc. 86/* Used to pass the media type, etc.
85 Both 'options[]' and 'full_duplex[]' should exist for driver 87 Both 'options[]' and 'full_duplex[]' should exist for driver
86 interoperability. 88 interoperability.
@@ -139,12 +141,14 @@ MODULE_LICENSE("GPL");
139module_param(mtu, int, 0); 141module_param(mtu, int, 0);
140module_param(debug, int, 0); 142module_param(debug, int, 0);
141module_param(rx_copybreak, int, 0); 143module_param(rx_copybreak, int, 0);
144module_param(dspcfg_workaround, int, 1);
142module_param_array(options, int, NULL, 0); 145module_param_array(options, int, NULL, 0);
143module_param_array(full_duplex, int, NULL, 0); 146module_param_array(full_duplex, int, NULL, 0);
144MODULE_PARM_DESC(mtu, "DP8381x MTU (all boards)"); 147MODULE_PARM_DESC(mtu, "DP8381x MTU (all boards)");
145MODULE_PARM_DESC(debug, "DP8381x default debug level"); 148MODULE_PARM_DESC(debug, "DP8381x default debug level");
146MODULE_PARM_DESC(rx_copybreak, 149MODULE_PARM_DESC(rx_copybreak,
147 "DP8381x copy breakpoint for copy-only-tiny-frames"); 150 "DP8381x copy breakpoint for copy-only-tiny-frames");
151MODULE_PARM_DESC(dspcfg_workaround, "DP8381x: control DspCfg workaround");
148MODULE_PARM_DESC(options, 152MODULE_PARM_DESC(options,
149 "DP8381x: Bits 0-3: media type, bit 17: full duplex"); 153 "DP8381x: Bits 0-3: media type, bit 17: full duplex");
150MODULE_PARM_DESC(full_duplex, "DP8381x full duplex setting(s) (1)"); 154MODULE_PARM_DESC(full_duplex, "DP8381x full duplex setting(s) (1)");
@@ -590,6 +594,7 @@ struct netdev_private {
590 u32 srr; 594 u32 srr;
591 /* expected DSPCFG value */ 595 /* expected DSPCFG value */
592 u16 dspcfg; 596 u16 dspcfg;
597 int dspcfg_workaround;
593 /* parms saved in ethtool format */ 598 /* parms saved in ethtool format */
594 u16 speed; /* The forced speed, 10Mb, 100Mb, gigabit */ 599 u16 speed; /* The forced speed, 10Mb, 100Mb, gigabit */
595 u8 duplex; /* Duplex, half or full */ 600 u8 duplex; /* Duplex, half or full */
@@ -656,6 +661,56 @@ static int netdev_get_regs(struct net_device *dev, u8 *buf);
656static int netdev_get_eeprom(struct net_device *dev, u8 *buf); 661static int netdev_get_eeprom(struct net_device *dev, u8 *buf);
657static const struct ethtool_ops ethtool_ops; 662static const struct ethtool_ops ethtool_ops;
658 663
664#define NATSEMI_ATTR(_name) \
665static ssize_t natsemi_show_##_name(struct device *dev, \
666 struct device_attribute *attr, char *buf); \
667 static ssize_t natsemi_set_##_name(struct device *dev, \
668 struct device_attribute *attr, \
669 const char *buf, size_t count); \
670 static DEVICE_ATTR(_name, 0644, natsemi_show_##_name, natsemi_set_##_name)
671
672#define NATSEMI_CREATE_FILE(_dev, _name) \
673 device_create_file(&_dev->dev, &dev_attr_##_name)
674#define NATSEMI_REMOVE_FILE(_dev, _name) \
675 device_create_file(&_dev->dev, &dev_attr_##_name)
676
677NATSEMI_ATTR(dspcfg_workaround);
678
679static ssize_t natsemi_show_dspcfg_workaround(struct device *dev,
680 struct device_attribute *attr,
681 char *buf)
682{
683 struct netdev_private *np = netdev_priv(to_net_dev(dev));
684
685 return sprintf(buf, "%s\n", np->dspcfg_workaround ? "on" : "off");
686}
687
688static ssize_t natsemi_set_dspcfg_workaround(struct device *dev,
689 struct device_attribute *attr,
690 const char *buf, size_t count)
691{
692 struct netdev_private *np = netdev_priv(to_net_dev(dev));
693 int new_setting;
694 u32 flags;
695
696 /* Find out the new setting */
697 if (!strncmp("on", buf, count - 1) || !strncmp("1", buf, count - 1))
698 new_setting = 1;
699 else if (!strncmp("off", buf, count - 1)
700 || !strncmp("0", buf, count - 1))
701 new_setting = 0;
702 else
703 return count;
704
705 spin_lock_irqsave(&np->lock, flags);
706
707 np->dspcfg_workaround = new_setting;
708
709 spin_unlock_irqrestore(&np->lock, flags);
710
711 return count;
712}
713
659static inline void __iomem *ns_ioaddr(struct net_device *dev) 714static inline void __iomem *ns_ioaddr(struct net_device *dev)
660{ 715{
661 return (void __iomem *) dev->base_addr; 716 return (void __iomem *) dev->base_addr;
@@ -820,6 +875,7 @@ static int __devinit natsemi_probe1 (struct pci_dev *pdev,
820 np->ignore_phy = 1; 875 np->ignore_phy = 1;
821 else 876 else
822 np->ignore_phy = 0; 877 np->ignore_phy = 0;
878 np->dspcfg_workaround = dspcfg_workaround;
823 879
824 /* Initial port: 880 /* Initial port:
825 * - If configured to ignore the PHY set up for external. 881 * - If configured to ignore the PHY set up for external.
@@ -899,6 +955,9 @@ static int __devinit natsemi_probe1 (struct pci_dev *pdev,
899 if (i) 955 if (i)
900 goto err_register_netdev; 956 goto err_register_netdev;
901 957
958 if (NATSEMI_CREATE_FILE(pdev, dspcfg_workaround))
959 goto err_create_file;
960
902 if (netif_msg_drv(np)) { 961 if (netif_msg_drv(np)) {
903 printk(KERN_INFO "natsemi %s: %s at %#08lx (%s), ", 962 printk(KERN_INFO "natsemi %s: %s at %#08lx (%s), ",
904 dev->name, natsemi_pci_info[chip_idx].name, iostart, 963 dev->name, natsemi_pci_info[chip_idx].name, iostart,
@@ -915,6 +974,9 @@ static int __devinit natsemi_probe1 (struct pci_dev *pdev,
915 } 974 }
916 return 0; 975 return 0;
917 976
977 err_create_file:
978 unregister_netdev(dev);
979
918 err_register_netdev: 980 err_register_netdev:
919 iounmap(ioaddr); 981 iounmap(ioaddr);
920 982
@@ -1727,7 +1789,8 @@ static void init_registers(struct net_device *dev)
1727 * It seems that a reference set for this chip went out with incorrect info, 1789 * It seems that a reference set for this chip went out with incorrect info,
1728 * and there exist boards that aren't quite right. An unexpected voltage 1790 * and there exist boards that aren't quite right. An unexpected voltage
1729 * drop can cause the PHY to get itself in a weird state (basically reset). 1791 * drop can cause the PHY to get itself in a weird state (basically reset).
1730 * NOTE: this only seems to affect revC chips. 1792 * NOTE: this only seems to affect revC chips. The user can disable
1793 * this check via dspcfg_workaround sysfs option.
1731 * 3) check of death of the RX path due to OOM 1794 * 3) check of death of the RX path due to OOM
1732 */ 1795 */
1733static void netdev_timer(unsigned long data) 1796static void netdev_timer(unsigned long data)
@@ -1753,7 +1816,7 @@ static void netdev_timer(unsigned long data)
1753 writew(1, ioaddr+PGSEL); 1816 writew(1, ioaddr+PGSEL);
1754 dspcfg = readw(ioaddr+DSPCFG); 1817 dspcfg = readw(ioaddr+DSPCFG);
1755 writew(0, ioaddr+PGSEL); 1818 writew(0, ioaddr+PGSEL);
1756 if (dspcfg != np->dspcfg) { 1819 if (np->dspcfg_workaround && dspcfg != np->dspcfg) {
1757 if (!netif_queue_stopped(dev)) { 1820 if (!netif_queue_stopped(dev)) {
1758 spin_unlock_irq(&np->lock); 1821 spin_unlock_irq(&np->lock);
1759 if (netif_msg_drv(np)) 1822 if (netif_msg_drv(np))
@@ -3157,6 +3220,7 @@ static void __devexit natsemi_remove1 (struct pci_dev *pdev)
3157 struct net_device *dev = pci_get_drvdata(pdev); 3220 struct net_device *dev = pci_get_drvdata(pdev);
3158 void __iomem * ioaddr = ns_ioaddr(dev); 3221 void __iomem * ioaddr = ns_ioaddr(dev);
3159 3222
3223 NATSEMI_REMOVE_FILE(pdev, dspcfg_workaround);
3160 unregister_netdev (dev); 3224 unregister_netdev (dev);
3161 pci_release_regions (pdev); 3225 pci_release_regions (pdev);
3162 iounmap(ioaddr); 3226 iounmap(ioaddr);