diff options
Diffstat (limited to 'drivers/net/sky2.c')
-rw-r--r-- | drivers/net/sky2.c | 324 |
1 files changed, 235 insertions, 89 deletions
diff --git a/drivers/net/sky2.c b/drivers/net/sky2.c index 62436b3a18c..3805b9318be 100644 --- a/drivers/net/sky2.c +++ b/drivers/net/sky2.c @@ -24,7 +24,6 @@ #include <linux/crc32.h> #include <linux/kernel.h> -#include <linux/version.h> #include <linux/module.h> #include <linux/netdevice.h> #include <linux/dma-mapping.h> @@ -51,7 +50,7 @@ #include "sky2.h" #define DRV_NAME "sky2" -#define DRV_VERSION "1.21" +#define DRV_VERSION "1.22" #define PFX DRV_NAME " " /* @@ -98,7 +97,7 @@ static int disable_msi = 0; module_param(disable_msi, int, 0); MODULE_PARM_DESC(disable_msi, "Disable Message Signaled Interrupt (MSI)"); -static const struct pci_device_id sky2_id_table[] = { +static DEFINE_PCI_DEVICE_TABLE(sky2_id_table) = { { PCI_DEVICE(PCI_VENDOR_ID_SYSKONNECT, 0x9000) }, /* SK-9Sxx */ { PCI_DEVICE(PCI_VENDOR_ID_SYSKONNECT, 0x9E00) }, /* SK-9Exx */ { PCI_DEVICE(PCI_VENDOR_ID_DLINK, 0x4b00) }, /* DGE-560T */ @@ -118,6 +117,7 @@ static const struct pci_device_id sky2_id_table[] = { { PCI_DEVICE(PCI_VENDOR_ID_MARVELL, 0x4352) }, /* 88E8038 */ { PCI_DEVICE(PCI_VENDOR_ID_MARVELL, 0x4353) }, /* 88E8039 */ { PCI_DEVICE(PCI_VENDOR_ID_MARVELL, 0x4354) }, /* 88E8040 */ + { PCI_DEVICE(PCI_VENDOR_ID_MARVELL, 0x4355) }, /* 88E8040T */ { PCI_DEVICE(PCI_VENDOR_ID_MARVELL, 0x4356) }, /* 88EC033 */ { PCI_DEVICE(PCI_VENDOR_ID_MARVELL, 0x4357) }, /* 88E8042 */ { PCI_DEVICE(PCI_VENDOR_ID_MARVELL, 0x435A) }, /* 88E8048 */ @@ -136,6 +136,7 @@ static const struct pci_device_id sky2_id_table[] = { { PCI_DEVICE(PCI_VENDOR_ID_MARVELL, 0x436C) }, /* 88E8072 */ { PCI_DEVICE(PCI_VENDOR_ID_MARVELL, 0x436D) }, /* 88E8055 */ { PCI_DEVICE(PCI_VENDOR_ID_MARVELL, 0x4370) }, /* 88E8075 */ + { PCI_DEVICE(PCI_VENDOR_ID_MARVELL, 0x4380) }, /* 88E8057 */ { 0 } }; @@ -146,17 +147,6 @@ static const unsigned txqaddr[] = { Q_XA1, Q_XA2 }; static const unsigned rxqaddr[] = { Q_R1, Q_R2 }; static const u32 portirq_msk[] = { Y2_IS_PORT_1, Y2_IS_PORT_2 }; -/* This driver supports yukon2 chipset only */ -static const char *yukon2_name[] = { - "XL", /* 0xb3 */ - "EC Ultra", /* 0xb4 */ - "Extreme", /* 0xb5 */ - "EC", /* 0xb6 */ - "FE", /* 0xb7 */ - "FE+", /* 0xb8 */ - "Supreme", /* 0xb9 */ -}; - static void sky2_set_multicast(struct net_device *dev); /* Access to PHY via serial interconnect */ @@ -578,8 +568,7 @@ static void sky2_phy_init(struct sky2_hw *hw, unsigned port) ledover |= PHY_M_LED_MO_RX(MO_LED_OFF); } - if (hw->chip_id == CHIP_ID_YUKON_EC_U && - hw->chip_rev == CHIP_REV_YU_EC_U_A1) { + if (hw->chip_id == CHIP_ID_YUKON_EC_U || hw->chip_id == CHIP_ID_YUKON_UL_2) { /* apply fixes in PHY AFE */ gm_phy_write(hw, port, PHY_MARV_EXT_ADR, 255); @@ -587,9 +576,11 @@ static void sky2_phy_init(struct sky2_hw *hw, unsigned port) gm_phy_write(hw, port, 0x18, 0xaa99); gm_phy_write(hw, port, 0x17, 0x2011); - /* fix for IEEE A/B Symmetry failure in 1000BASE-T */ - gm_phy_write(hw, port, 0x18, 0xa204); - gm_phy_write(hw, port, 0x17, 0x2002); + if (hw->chip_id == CHIP_ID_YUKON_EC_U) { + /* fix for IEEE A/B Symmetry failure in 1000BASE-T */ + gm_phy_write(hw, port, 0x18, 0xa204); + gm_phy_write(hw, port, 0x17, 0x2002); + } /* set page register to 0 */ gm_phy_write(hw, port, PHY_MARV_EXT_ADR, 0); @@ -598,7 +589,8 @@ static void sky2_phy_init(struct sky2_hw *hw, unsigned port) /* apply workaround for integrated resistors calibration */ gm_phy_write(hw, port, PHY_MARV_PAGE_ADDR, 17); gm_phy_write(hw, port, PHY_MARV_PAGE_DATA, 0x3f60); - } else if (hw->chip_id != CHIP_ID_YUKON_EX) { + } else if (hw->chip_id != CHIP_ID_YUKON_EX && + hw->chip_id < CHIP_ID_YUKON_SUPR) { /* no effect on Yukon-XL */ gm_phy_write(hw, port, PHY_MARV_LED_CTRL, ledctrl); @@ -619,28 +611,81 @@ static void sky2_phy_init(struct sky2_hw *hw, unsigned port) gm_phy_write(hw, port, PHY_MARV_INT_MASK, PHY_M_DEF_MSK); } -static void sky2_phy_power(struct sky2_hw *hw, unsigned port, int onoff) +static const u32 phy_power[] = { PCI_Y2_PHY1_POWD, PCI_Y2_PHY2_POWD }; +static const u32 coma_mode[] = { PCI_Y2_PHY1_COMA, PCI_Y2_PHY2_COMA }; + +static void sky2_phy_power_up(struct sky2_hw *hw, unsigned port) { u32 reg1; - static const u32 phy_power[] = { PCI_Y2_PHY1_POWD, PCI_Y2_PHY2_POWD }; - static const u32 coma_mode[] = { PCI_Y2_PHY1_COMA, PCI_Y2_PHY2_COMA }; sky2_write8(hw, B2_TST_CTRL1, TST_CFG_WRITE_ON); reg1 = sky2_pci_read32(hw, PCI_DEV_REG1); - /* Turn on/off phy power saving */ - if (onoff) - reg1 &= ~phy_power[port]; - else - reg1 |= phy_power[port]; + reg1 &= ~phy_power[port]; - if (onoff && hw->chip_id == CHIP_ID_YUKON_XL && hw->chip_rev > 1) + if (hw->chip_id == CHIP_ID_YUKON_XL && hw->chip_rev > 1) reg1 |= coma_mode[port]; sky2_pci_write32(hw, PCI_DEV_REG1, reg1); sky2_write8(hw, B2_TST_CTRL1, TST_CFG_WRITE_OFF); sky2_pci_read32(hw, PCI_DEV_REG1); - udelay(100); + if (hw->chip_id == CHIP_ID_YUKON_FE) + gm_phy_write(hw, port, PHY_MARV_CTRL, PHY_CT_ANE); + else if (hw->flags & SKY2_HW_ADV_POWER_CTL) + sky2_write8(hw, SK_REG(port, GPHY_CTRL), GPC_RST_CLR); +} + +static void sky2_phy_power_down(struct sky2_hw *hw, unsigned port) +{ + u32 reg1; + u16 ctrl; + + /* release GPHY Control reset */ + sky2_write8(hw, SK_REG(port, GPHY_CTRL), GPC_RST_CLR); + + /* release GMAC reset */ + sky2_write8(hw, SK_REG(port, GMAC_CTRL), GMC_RST_CLR); + + if (hw->flags & SKY2_HW_NEWER_PHY) { + /* select page 2 to access MAC control register */ + gm_phy_write(hw, port, PHY_MARV_EXT_ADR, 2); + + ctrl = gm_phy_read(hw, port, PHY_MARV_PHY_CTRL); + /* allow GMII Power Down */ + ctrl &= ~PHY_M_MAC_GMIF_PUP; + gm_phy_write(hw, port, PHY_MARV_PHY_CTRL, ctrl); + + /* set page register back to 0 */ + gm_phy_write(hw, port, PHY_MARV_EXT_ADR, 0); + } + + /* setup General Purpose Control Register */ + gma_write16(hw, port, GM_GP_CTRL, + GM_GPCR_FL_PASS | GM_GPCR_SPEED_100 | GM_GPCR_AU_ALL_DIS); + + if (hw->chip_id != CHIP_ID_YUKON_EC) { + if (hw->chip_id == CHIP_ID_YUKON_EC_U) { + /* select page 2 to access MAC control register */ + gm_phy_write(hw, port, PHY_MARV_EXT_ADR, 2); + + ctrl = gm_phy_read(hw, port, PHY_MARV_PHY_CTRL); + /* enable Power Down */ + ctrl |= PHY_M_PC_POW_D_ENA; + gm_phy_write(hw, port, PHY_MARV_PHY_CTRL, ctrl); + + /* set page register back to 0 */ + gm_phy_write(hw, port, PHY_MARV_EXT_ADR, 0); + } + + /* set IEEE compatible Power Down Mode (dev. #4.99) */ + gm_phy_write(hw, port, PHY_MARV_CTRL, PHY_CT_PDOWN); + } + + sky2_write8(hw, B2_TST_CTRL1, TST_CFG_WRITE_ON); + reg1 = sky2_pci_read32(hw, PCI_DEV_REG1); + reg1 |= phy_power[port]; /* set PHY to PowerDown/COMA Mode */ + sky2_pci_write32(hw, PCI_DEV_REG1, reg1); + sky2_write8(hw, B2_TST_CTRL1, TST_CFG_WRITE_OFF); } /* Force a renegotiation */ @@ -675,8 +720,11 @@ static void sky2_wol_init(struct sky2_port *sky2) sky2->advertising &= ~(ADVERTISED_1000baseT_Half|ADVERTISED_1000baseT_Full); sky2->flow_mode = FC_NONE; - sky2_phy_power(hw, port, 1); - sky2_phy_reinit(sky2); + + spin_lock_bh(&sky2->phy_lock); + sky2_phy_power_up(hw, port); + sky2_phy_init(hw, port); + spin_unlock_bh(&sky2->phy_lock); sky2->flow_mode = save_mode; sky2->advertising = ctrl; @@ -781,6 +829,7 @@ static void sky2_mac_init(struct sky2_hw *hw, unsigned port) sky2_write8(hw, SK_REG(port, GMAC_IRQ_MSK), GMAC_DEF_MSK); spin_lock_bh(&sky2->phy_lock); + sky2_phy_power_up(hw, port); sky2_phy_init(hw, port); spin_unlock_bh(&sky2->phy_lock); @@ -1385,8 +1434,6 @@ static int sky2_up(struct net_device *dev) if (!sky2->rx_ring) goto err_out; - sky2_phy_power(hw, port, 1); - sky2_mac_init(hw, port); /* Register is number of 4K blocks on internal RAM buffer. */ @@ -1711,9 +1758,6 @@ static int sky2_down(struct net_device *dev) if (netif_msg_ifdown(sky2)) printk(KERN_INFO PFX "%s: disabling interface\n", dev->name); - /* Stop more packets from being queued */ - netif_stop_queue(dev); - /* Disable port IRQ */ imask = sky2_read32(hw, B0_IMSK); imask &= ~portirq_msk[port]; @@ -1767,9 +1811,7 @@ static int sky2_down(struct net_device *dev) sky2_write8(hw, SK_REG(port, RX_GMF_CTRL_T), GMF_RST_SET); sky2_write8(hw, SK_REG(port, TX_GMF_CTRL_T), GMF_RST_SET); - sky2_phy_power(hw, port, 0); - - netif_carrier_off(dev); + sky2_phy_power_down(hw, port); /* turn off LED's */ sky2_write16(hw, B0_Y2LED, LED_STAT_OFF); @@ -2693,6 +2735,7 @@ static u32 sky2_mhz(const struct sky2_hw *hw) case CHIP_ID_YUKON_EC_U: case CHIP_ID_YUKON_EX: case CHIP_ID_YUKON_SUPR: + case CHIP_ID_YUKON_UL_2: return 125; case CHIP_ID_YUKON_FE: @@ -2781,6 +2824,11 @@ static int __devinit sky2_init(struct sky2_hw *hw) | SKY2_HW_ADV_POWER_CTL; break; + case CHIP_ID_YUKON_UL_2: + hw->flags = SKY2_HW_GIGABIT + | SKY2_HW_ADV_POWER_CTL; + break; + default: dev_err(&hw->pdev->dev, "unsupported chip type 0x%x\n", hw->chip_id); @@ -2791,7 +2839,6 @@ static int __devinit sky2_init(struct sky2_hw *hw) if (hw->pmd_type == 'L' || hw->pmd_type == 'S' || hw->pmd_type == 'P') hw->flags |= SKY2_HW_FIBRE_PHY; - hw->ports = 1; t8 = sky2_read8(hw, B2_Y2_HW_RES); if ((t8 & CFG_DUAL_MAC_MSK) == CFG_DUAL_MAC_MSK) { @@ -3378,7 +3425,7 @@ static void sky2_led(struct sky2_port *sky2, enum led_mode mode) gm_phy_write(hw, port, PHY_MARV_EXT_ADR, pg); } else - gm_phy_write(hw, port, PHY_MARV_LED_OVER, + gm_phy_write(hw, port, PHY_MARV_LED_OVER, PHY_M_LED_MO_DUP(mode) | PHY_M_LED_MO_10(mode) | PHY_M_LED_MO_100(mode) | @@ -3685,27 +3732,63 @@ static int sky2_get_eeprom_len(struct net_device *dev) return 1 << ( ((reg2 & PCI_VPD_ROM_SZ) >> 14) + 8); } -static u32 sky2_vpd_read(struct sky2_hw *hw, int cap, u16 offset) +static int sky2_vpd_wait(const struct sky2_hw *hw, int cap, u16 busy) { - u32 val; + unsigned long start = jiffies; + + while ( (sky2_pci_read16(hw, cap + PCI_VPD_ADDR) & PCI_VPD_ADDR_F) == busy) { + /* Can take up to 10.6 ms for write */ + if (time_after(jiffies, start + HZ/4)) { + dev_err(&hw->pdev->dev, PFX "VPD cycle timed out"); + return -ETIMEDOUT; + } + mdelay(1); + } - sky2_pci_write16(hw, cap + PCI_VPD_ADDR, offset); + return 0; +} - do { - offset = sky2_pci_read16(hw, cap + PCI_VPD_ADDR); - } while (!(offset & PCI_VPD_ADDR_F)); +static int sky2_vpd_read(struct sky2_hw *hw, int cap, void *data, + u16 offset, size_t length) +{ + int rc = 0; - val = sky2_pci_read32(hw, cap + PCI_VPD_DATA); - return val; + while (length > 0) { + u32 val; + + sky2_pci_write16(hw, cap + PCI_VPD_ADDR, offset); + rc = sky2_vpd_wait(hw, cap, 0); + if (rc) + break; + + val = sky2_pci_read32(hw, cap + PCI_VPD_DATA); + + memcpy(data, &val, min(sizeof(val), length)); + offset += sizeof(u32); + data += sizeof(u32); + length -= sizeof(u32); + } + + return rc; } -static void sky2_vpd_write(struct sky2_hw *hw, int cap, u16 offset, u32 val) +static int sky2_vpd_write(struct sky2_hw *hw, int cap, const void *data, + u16 offset, unsigned int length) { - sky2_pci_write16(hw, cap + PCI_VPD_DATA, val); - sky2_pci_write32(hw, cap + PCI_VPD_ADDR, offset | PCI_VPD_ADDR_F); - do { - offset = sky2_pci_read16(hw, cap + PCI_VPD_ADDR); - } while (offset & PCI_VPD_ADDR_F); + unsigned int i; + int rc = 0; + + for (i = 0; i < length; i += sizeof(u32)) { + u32 val = *(u32 *)(data + i); + + sky2_pci_write32(hw, cap + PCI_VPD_DATA, val); + sky2_pci_write32(hw, cap + PCI_VPD_ADDR, offset | PCI_VPD_ADDR_F); + + rc = sky2_vpd_wait(hw, cap, PCI_VPD_ADDR_F); + if (rc) + break; + } + return rc; } static int sky2_get_eeprom(struct net_device *dev, struct ethtool_eeprom *eeprom, @@ -3713,24 +3796,13 @@ static int sky2_get_eeprom(struct net_device *dev, struct ethtool_eeprom *eeprom { struct sky2_port *sky2 = netdev_priv(dev); int cap = pci_find_capability(sky2->hw->pdev, PCI_CAP_ID_VPD); - int length = eeprom->len; - u16 offset = eeprom->offset; if (!cap) return -EINVAL; eeprom->magic = SKY2_EEPROM_MAGIC; - while (length > 0) { - u32 val = sky2_vpd_read(sky2->hw, cap, offset); - int n = min_t(int, length, sizeof(val)); - - memcpy(data, &val, n); - length -= n; - data += n; - offset += n; - } - return 0; + return sky2_vpd_read(sky2->hw, cap, data, eeprom->offset, eeprom->len); } static int sky2_set_eeprom(struct net_device *dev, struct ethtool_eeprom *eeprom, @@ -3738,8 +3810,6 @@ static int sky2_set_eeprom(struct net_device *dev, struct ethtool_eeprom *eeprom { struct sky2_port *sky2 = netdev_priv(dev); int cap = pci_find_capability(sky2->hw->pdev, PCI_CAP_ID_VPD); - int length = eeprom->len; - u16 offset = eeprom->offset; if (!cap) return -EINVAL; @@ -3747,21 +3817,11 @@ static int sky2_set_eeprom(struct net_device *dev, struct ethtool_eeprom *eeprom if (eeprom->magic != SKY2_EEPROM_MAGIC) return -EINVAL; - while (length > 0) { - u32 val; - int n = min_t(int, length, sizeof(val)); - - if (n < sizeof(val)) - val = sky2_vpd_read(sky2->hw, cap, offset); - memcpy(&val, data, n); - - sky2_vpd_write(sky2->hw, cap, offset, val); + /* Partial writes not supported */ + if ((eeprom->offset & 3) || (eeprom->len & 3)) + return -EINVAL; - length -= n; - data += n; - offset += n; - } - return 0; + return sky2_vpd_write(sky2->hw, cap, data, eeprom->offset, eeprom->len); } @@ -4131,12 +4191,97 @@ static int __devinit pci_wake_enabled(struct pci_dev *dev) return value & PCI_PM_CTRL_PME_ENABLE; } +/* + * Read and parse the first part of Vital Product Data + */ +#define VPD_SIZE 128 +#define VPD_MAGIC 0x82 + +static void __devinit sky2_vpd_info(struct sky2_hw *hw) +{ + int cap = pci_find_capability(hw->pdev, PCI_CAP_ID_VPD); + const u8 *p; + u8 *vpd_buf = NULL; + u16 len; + static struct vpd_tag { + char tag[2]; + char *label; + } vpd_tags[] = { + { "PN", "Part Number" }, + { "EC", "Engineering Level" }, + { "MN", "Manufacturer" }, + }; + + if (!cap) + goto out; + + vpd_buf = kmalloc(VPD_SIZE, GFP_KERNEL); + if (!vpd_buf) + goto out; + + if (sky2_vpd_read(hw, cap, vpd_buf, 0, VPD_SIZE)) + goto out; + + if (vpd_buf[0] != VPD_MAGIC) + goto out; + len = vpd_buf[1]; + if (len == 0 || len > VPD_SIZE - 4) + goto out; + p = vpd_buf + 3; + dev_info(&hw->pdev->dev, "%.*s\n", len, p); + p += len; + + while (p < vpd_buf + VPD_SIZE - 4) { + int i; + + if (!memcmp("RW", p, 2)) /* end marker */ + break; + + len = p[2]; + if (len > (p - vpd_buf) - 4) + break; + + for (i = 0; i < ARRAY_SIZE(vpd_tags); i++) { + if (!memcmp(vpd_tags[i].tag, p, 2)) { + printk(KERN_DEBUG " %s: %.*s\n", + vpd_tags[i].label, len, p + 3); + break; + } + } + p += len + 3; + } +out: + kfree(vpd_buf); +} + +/* This driver supports yukon2 chipset only */ +static const char *sky2_name(u8 chipid, char *buf, int sz) +{ + const char *name[] = { + "XL", /* 0xb3 */ + "EC Ultra", /* 0xb4 */ + "Extreme", /* 0xb5 */ + "EC", /* 0xb6 */ + "FE", /* 0xb7 */ + "FE+", /* 0xb8 */ + "Supreme", /* 0xb9 */ + "UL 2", /* 0xba */ + }; + + if (chipid >= CHIP_ID_YUKON_XL && chipid < CHIP_ID_YUKON_UL_2) + strncpy(buf, name[chipid - CHIP_ID_YUKON_XL], sz); + else + snprintf(buf, sz, "(chip %#x)", chipid); + return buf; +} + static int __devinit sky2_probe(struct pci_dev *pdev, const struct pci_device_id *ent) { struct net_device *dev; struct sky2_hw *hw; int err, using_dac = 0, wol_default; + char buf1[16]; err = pci_enable_device(pdev); if (err) { @@ -4207,13 +4352,13 @@ static int __devinit sky2_probe(struct pci_dev *pdev, if (err) goto err_out_iounmap; - dev_info(&pdev->dev, "v%s addr 0x%llx irq %d Yukon-%s (0x%x) rev %d\n", - DRV_VERSION, (unsigned long long)pci_resource_start(pdev, 0), - pdev->irq, yukon2_name[hw->chip_id - CHIP_ID_YUKON_XL], - hw->chip_id, hw->chip_rev); + dev_info(&pdev->dev, "Yukon-2 %s chip revision %d\n", + sky2_name(hw->chip_id, buf1, sizeof(buf1)), hw->chip_rev); sky2_reset(hw); + sky2_vpd_info(hw); + dev = sky2_init_netdev(hw, 0, using_dac, wol_default); if (!dev) { err = -ENOMEM; @@ -4448,7 +4593,6 @@ static void sky2_shutdown(struct pci_dev *pdev) pci_disable_device(pdev); pci_set_power_state(pdev, PCI_D3hot); - } static struct pci_driver sky2_driver = { @@ -4465,6 +4609,8 @@ static struct pci_driver sky2_driver = { static int __init sky2_init_module(void) { + pr_info(PFX "driver version " DRV_VERSION "\n"); + sky2_debug_init(); return pci_register_driver(&sky2_driver); } |