aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/net/sky2.c
diff options
context:
space:
mode:
authorStephen Hemminger <shemminger@linux-foundation.org>2008-08-27 23:46:26 -0400
committerJeff Garzik <jgarzik@redhat.com>2008-09-03 10:23:56 -0400
commit1413235c14301c4bd27aabf408e4336719b6f505 (patch)
tree5d54e54f1f2f23e7bebfd60f6eac3d617d2facc1 /drivers/net/sky2.c
parent9389523a77be0a7e01d957c836733b5c9d5530a1 (diff)
sky2: EEPROM read/write bug fixes
Cleanup and harden the routines accessing the EEPROM. 1. Prevent spin forever waiting for the TWSI bus 2. Fix write eeprom to write full words rather than only 16 bits Luckly the vendor doesn't provide EEPROM in Linux format so it must never have been used. 3. Don't allow partial eeprom writes, not needed, not safe. These are non-urgent bug fixes. Signed-off-by: Stephen Hemminger <shemminger@vyatta.com> Signed-off-by: Jeff Garzik <jgarzik@redhat.com>
Diffstat (limited to 'drivers/net/sky2.c')
-rw-r--r--drivers/net/sky2.c97
1 files changed, 55 insertions, 42 deletions
diff --git a/drivers/net/sky2.c b/drivers/net/sky2.c
index e24b25ca1c6..4e3e2a37c3d 100644
--- a/drivers/net/sky2.c
+++ b/drivers/net/sky2.c
@@ -3732,27 +3732,63 @@ static int sky2_get_eeprom_len(struct net_device *dev)
3732 return 1 << ( ((reg2 & PCI_VPD_ROM_SZ) >> 14) + 8); 3732 return 1 << ( ((reg2 & PCI_VPD_ROM_SZ) >> 14) + 8);
3733} 3733}
3734 3734
3735static u32 sky2_vpd_read(struct sky2_hw *hw, int cap, u16 offset) 3735static int sky2_vpd_wait(const struct sky2_hw *hw, int cap, u16 busy)
3736{ 3736{
3737 u32 val; 3737 unsigned long start = jiffies;
3738 3738
3739 sky2_pci_write16(hw, cap + PCI_VPD_ADDR, offset); 3739 while ( (sky2_pci_read16(hw, cap + PCI_VPD_ADDR) & PCI_VPD_ADDR_F) == busy) {
3740 /* Can take up to 10.6 ms for write */
3741 if (time_after(jiffies, start + HZ/4)) {
3742 dev_err(&hw->pdev->dev, PFX "VPD cycle timed out");
3743 return -ETIMEDOUT;
3744 }
3745 mdelay(1);
3746 }
3740 3747
3741 do { 3748 return 0;
3742 offset = sky2_pci_read16(hw, cap + PCI_VPD_ADDR); 3749}
3743 } while (!(offset & PCI_VPD_ADDR_F));
3744 3750
3745 val = sky2_pci_read32(hw, cap + PCI_VPD_DATA); 3751static int sky2_vpd_read(struct sky2_hw *hw, int cap, void *data,
3746 return val; 3752 u16 offset, size_t length)
3753{
3754 int rc = 0;
3755
3756 while (length > 0) {
3757 u32 val;
3758
3759 sky2_pci_write16(hw, cap + PCI_VPD_ADDR, offset);
3760 rc = sky2_vpd_wait(hw, cap, 0);
3761 if (rc)
3762 break;
3763
3764 val = sky2_pci_read32(hw, cap + PCI_VPD_DATA);
3765
3766 memcpy(data, &val, min(sizeof(val), length));
3767 offset += sizeof(u32);
3768 data += sizeof(u32);
3769 length -= sizeof(u32);
3770 }
3771
3772 return rc;
3747} 3773}
3748 3774
3749static void sky2_vpd_write(struct sky2_hw *hw, int cap, u16 offset, u32 val) 3775static int sky2_vpd_write(struct sky2_hw *hw, int cap, const void *data,
3776 u16 offset, unsigned int length)
3750{ 3777{
3751 sky2_pci_write16(hw, cap + PCI_VPD_DATA, val); 3778 unsigned int i;
3752 sky2_pci_write32(hw, cap + PCI_VPD_ADDR, offset | PCI_VPD_ADDR_F); 3779 int rc = 0;
3753 do { 3780
3754 offset = sky2_pci_read16(hw, cap + PCI_VPD_ADDR); 3781 for (i = 0; i < length; i += sizeof(u32)) {
3755 } while (offset & PCI_VPD_ADDR_F); 3782 u32 val = *(u32 *)(data + i);
3783
3784 sky2_pci_write32(hw, cap + PCI_VPD_DATA, val);
3785 sky2_pci_write32(hw, cap + PCI_VPD_ADDR, offset | PCI_VPD_ADDR_F);
3786
3787 rc = sky2_vpd_wait(hw, cap, PCI_VPD_ADDR_F);
3788 if (rc)
3789 break;
3790 }
3791 return rc;
3756} 3792}
3757 3793
3758static int sky2_get_eeprom(struct net_device *dev, struct ethtool_eeprom *eeprom, 3794static int sky2_get_eeprom(struct net_device *dev, struct ethtool_eeprom *eeprom,
@@ -3760,24 +3796,13 @@ static int sky2_get_eeprom(struct net_device *dev, struct ethtool_eeprom *eeprom
3760{ 3796{
3761 struct sky2_port *sky2 = netdev_priv(dev); 3797 struct sky2_port *sky2 = netdev_priv(dev);
3762 int cap = pci_find_capability(sky2->hw->pdev, PCI_CAP_ID_VPD); 3798 int cap = pci_find_capability(sky2->hw->pdev, PCI_CAP_ID_VPD);
3763 int length = eeprom->len;
3764 u16 offset = eeprom->offset;
3765 3799
3766 if (!cap) 3800 if (!cap)
3767 return -EINVAL; 3801 return -EINVAL;
3768 3802
3769 eeprom->magic = SKY2_EEPROM_MAGIC; 3803 eeprom->magic = SKY2_EEPROM_MAGIC;
3770 3804
3771 while (length > 0) { 3805 return sky2_vpd_read(sky2->hw, cap, data, eeprom->offset, eeprom->len);
3772 u32 val = sky2_vpd_read(sky2->hw, cap, offset);
3773 int n = min_t(int, length, sizeof(val));
3774
3775 memcpy(data, &val, n);
3776 length -= n;
3777 data += n;
3778 offset += n;
3779 }
3780 return 0;
3781} 3806}
3782 3807
3783static int sky2_set_eeprom(struct net_device *dev, struct ethtool_eeprom *eeprom, 3808static int sky2_set_eeprom(struct net_device *dev, struct ethtool_eeprom *eeprom,
@@ -3785,8 +3810,6 @@ static int sky2_set_eeprom(struct net_device *dev, struct ethtool_eeprom *eeprom
3785{ 3810{
3786 struct sky2_port *sky2 = netdev_priv(dev); 3811 struct sky2_port *sky2 = netdev_priv(dev);
3787 int cap = pci_find_capability(sky2->hw->pdev, PCI_CAP_ID_VPD); 3812 int cap = pci_find_capability(sky2->hw->pdev, PCI_CAP_ID_VPD);
3788 int length = eeprom->len;
3789 u16 offset = eeprom->offset;
3790 3813
3791 if (!cap) 3814 if (!cap)
3792 return -EINVAL; 3815 return -EINVAL;
@@ -3794,21 +3817,11 @@ static int sky2_set_eeprom(struct net_device *dev, struct ethtool_eeprom *eeprom
3794 if (eeprom->magic != SKY2_EEPROM_MAGIC) 3817 if (eeprom->magic != SKY2_EEPROM_MAGIC)
3795 return -EINVAL; 3818 return -EINVAL;
3796 3819
3797 while (length > 0) { 3820 /* Partial writes not supported */
3798 u32 val; 3821 if ((eeprom->offset & 3) || (eeprom->len & 3))
3799 int n = min_t(int, length, sizeof(val)); 3822 return -EINVAL;
3800
3801 if (n < sizeof(val))
3802 val = sky2_vpd_read(sky2->hw, cap, offset);
3803 memcpy(&val, data, n);
3804
3805 sky2_vpd_write(sky2->hw, cap, offset, val);
3806 3823
3807 length -= n; 3824 return sky2_vpd_write(sky2->hw, cap, data, eeprom->offset, eeprom->len);
3808 data += n;
3809 offset += n;
3810 }
3811 return 0;
3812} 3825}
3813 3826
3814 3827