* Copyright (C) 2001, 2002, 2003, 2004 David S. Miller (davem@redhat.com)
* Copyright (C) 2001, 2002, 2003 Jeff Garzik (jgarzik@pobox.com)
* Copyright (C) 2004 Sun Microsystems Inc.
- * Copyright (C) 2005-2011 Broadcom Corporation.
+ * Copyright (C) 2005-2012 Broadcom Corporation.
*
* Firmware is:
* Derived from proprietary unpublished source code,
#define TG3_RAW_IP_ALIGN 2
#define TG3_FW_UPDATE_TIMEOUT_SEC 5
+#define TG3_FW_UPDATE_FREQ_SEC (TG3_FW_UPDATE_TIMEOUT_SEC / 2)
#define FIRMWARE_TG3 "tigon/tg3.bin"
#define FIRMWARE_TG3TSO "tigon/tg3_tso.bin"
}
/* tp->lock is held. */
-static void tg3_ump_link_report(struct tg3 *tp)
+static void tg3_phy_gather_ump_data(struct tg3 *tp, u32 *data)
{
- u32 reg;
- u32 val;
-
- if (!tg3_flag(tp, 5780_CLASS) || !tg3_flag(tp, ENABLE_ASF))
- return;
-
- tg3_wait_for_event_ack(tp);
-
- tg3_write_mem(tp, NIC_SRAM_FW_CMD_MBOX, FWCMD_NICDRV_LINK_UPDATE);
-
- tg3_write_mem(tp, NIC_SRAM_FW_CMD_LEN_MBOX, 14);
+ u32 reg, val;
val = 0;
if (!tg3_readphy(tp, MII_BMCR, ®))
val = reg << 16;
if (!tg3_readphy(tp, MII_BMSR, ®))
val |= (reg & 0xffff);
- tg3_write_mem(tp, NIC_SRAM_FW_CMD_DATA_MBOX, val);
+ *data++ = val;
val = 0;
if (!tg3_readphy(tp, MII_ADVERTISE, ®))
val = reg << 16;
if (!tg3_readphy(tp, MII_LPA, ®))
val |= (reg & 0xffff);
- tg3_write_mem(tp, NIC_SRAM_FW_CMD_DATA_MBOX + 4, val);
+ *data++ = val;
val = 0;
if (!(tp->phy_flags & TG3_PHYFLG_MII_SERDES)) {
if (!tg3_readphy(tp, MII_STAT1000, ®))
val |= (reg & 0xffff);
}
- tg3_write_mem(tp, NIC_SRAM_FW_CMD_DATA_MBOX + 8, val);
+ *data++ = val;
if (!tg3_readphy(tp, MII_PHYADDR, ®))
val = reg << 16;
else
val = 0;
- tg3_write_mem(tp, NIC_SRAM_FW_CMD_DATA_MBOX + 12, val);
+ *data++ = val;
+}
+
+/* tp->lock is held. */
+static void tg3_ump_link_report(struct tg3 *tp)
+{
+ u32 data[4];
+
+ if (!tg3_flag(tp, 5780_CLASS) || !tg3_flag(tp, ENABLE_ASF))
+ return;
+
+ tg3_phy_gather_ump_data(tp, data);
+
+ tg3_wait_for_event_ack(tp);
+
+ tg3_write_mem(tp, NIC_SRAM_FW_CMD_MBOX, FWCMD_NICDRV_LINK_UPDATE);
+ tg3_write_mem(tp, NIC_SRAM_FW_CMD_LEN_MBOX, 14);
+ tg3_write_mem(tp, NIC_SRAM_FW_CMD_DATA_MBOX + 0x0, data[0]);
+ tg3_write_mem(tp, NIC_SRAM_FW_CMD_DATA_MBOX + 0x4, data[1]);
+ tg3_write_mem(tp, NIC_SRAM_FW_CMD_DATA_MBOX + 0x8, data[2]);
+ tg3_write_mem(tp, NIC_SRAM_FW_CMD_DATA_MBOX + 0xc, data[3]);
tg3_generate_fw_event(tp);
}
(6 << TX_LENGTHS_IPG_SHIFT) |
(32 << TX_LENGTHS_SLOT_TIME_SHIFT)));
- if ((phydev->link && tp->link_config.active_speed == SPEED_INVALID) ||
- (!phydev->link && tp->link_config.active_speed != SPEED_INVALID) ||
+ if (phydev->link != tp->old_link ||
phydev->speed != tp->link_config.active_speed ||
phydev->duplex != tp->link_config.active_duplex ||
oldflowctrl != tp->link_config.active_flowctrl)
linkmesg = 1;
+ tp->old_link = phydev->link;
tp->link_config.active_speed = phydev->speed;
tp->link_config.active_duplex = phydev->duplex;
if (tp->phy_flags & TG3_PHYFLG_IS_LOW_POWER) {
tp->phy_flags &= ~TG3_PHYFLG_IS_LOW_POWER;
- phydev->speed = tp->link_config.orig_speed;
- phydev->duplex = tp->link_config.orig_duplex;
- phydev->autoneg = tp->link_config.orig_autoneg;
- phydev->advertising = tp->link_config.orig_advertising;
+ phydev->speed = tp->link_config.speed;
+ phydev->duplex = tp->link_config.duplex;
+ phydev->autoneg = tp->link_config.autoneg;
+ phydev->advertising = tp->link_config.advertising;
}
phy_start(phydev);
return 0;
}
-static int tg3_setup_phy(struct tg3 *, int);
-static int tg3_halt_cpu(struct tg3 *, u32);
-
static void tg3_power_down_phy(struct tg3 *tp, bool do_low_power)
{
u32 val;
return res;
}
-#define RX_CPU_SCRATCH_BASE 0x30000
-#define RX_CPU_SCRATCH_SIZE 0x04000
-#define TX_CPU_SCRATCH_BASE 0x34000
-#define TX_CPU_SCRATCH_SIZE 0x04000
-
-/* tp->lock is held. */
-static int tg3_halt_cpu(struct tg3 *tp, u32 offset)
+static int tg3_nvram_write_block_using_eeprom(struct tg3 *tp,
+ u32 offset, u32 len, u8 *buf)
{
- int i;
+ int i, j, rc = 0;
+ u32 val;
- BUG_ON(offset == TX_CPU_BASE && tg3_flag(tp, 5705_PLUS));
+ for (i = 0; i < len; i += 4) {
+ u32 addr;
+ __be32 data;
- if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5906) {
- u32 val = tr32(GRC_VCPU_EXT_CTRL);
+ addr = offset + i;
- tw32(GRC_VCPU_EXT_CTRL, val | GRC_VCPU_EXT_CTRL_HALT_CPU);
- return 0;
- }
- if (offset == RX_CPU_BASE) {
- for (i = 0; i < 10000; i++) {
- tw32(offset + CPU_STATE, 0xffffffff);
- tw32(offset + CPU_MODE, CPU_MODE_HALT);
- if (tr32(offset + CPU_MODE) & CPU_MODE_HALT)
- break;
- }
+ memcpy(&data, buf + i, 4);
- tw32(offset + CPU_STATE, 0xffffffff);
- tw32_f(offset + CPU_MODE, CPU_MODE_HALT);
- udelay(10);
- } else {
- for (i = 0; i < 10000; i++) {
- tw32(offset + CPU_STATE, 0xffffffff);
- tw32(offset + CPU_MODE, CPU_MODE_HALT);
- if (tr32(offset + CPU_MODE) & CPU_MODE_HALT)
+ /*
+ * The SEEPROM interface expects the data to always be opposite
+ * the native endian format. We accomplish this by reversing
+ * all the operations that would have been performed on the
+ * data from a call to tg3_nvram_read_be32().
+ */
+ tw32(GRC_EEPROM_DATA, swab32(be32_to_cpu(data)));
+
+ val = tr32(GRC_EEPROM_ADDR);
+ tw32(GRC_EEPROM_ADDR, val | EEPROM_ADDR_COMPLETE);
+
+ val &= ~(EEPROM_ADDR_ADDR_MASK | EEPROM_ADDR_DEVID_MASK |
+ EEPROM_ADDR_READ);
+ tw32(GRC_EEPROM_ADDR, val |
+ (0 << EEPROM_ADDR_DEVID_SHIFT) |
+ (addr & EEPROM_ADDR_ADDR_MASK) |
+ EEPROM_ADDR_START |
+ EEPROM_ADDR_WRITE);
+
+ for (j = 0; j < 1000; j++) {
+ val = tr32(GRC_EEPROM_ADDR);
+
+ if (val & EEPROM_ADDR_COMPLETE)
break;
+ msleep(1);
+ }
+ if (!(val & EEPROM_ADDR_COMPLETE)) {
+ rc = -EBUSY;
+ break;
}
}
- if (i >= 10000) {
- netdev_err(tp->dev, "%s timed out, %s CPU\n",
- __func__, offset == RX_CPU_BASE ? "RX" : "TX");
- return -ENODEV;
- }
-
- /* Clear firmware's nvram arbitration. */
- if (tg3_flag(tp, NVRAM))
- tw32(NVRAM_SWARB, SWARB_REQ_CLR0);
- return 0;
+ return rc;
}
-struct fw_info {
- unsigned int fw_base;
- unsigned int fw_len;
- const __be32 *fw_data;
-};
-
-/* tp->lock is held. */
-static int tg3_load_firmware_cpu(struct tg3 *tp, u32 cpu_base,
- u32 cpu_scratch_base, int cpu_scratch_size,
- struct fw_info *info)
+/* offset and length are dword aligned */
+static int tg3_nvram_write_block_unbuffered(struct tg3 *tp, u32 offset, u32 len,
+ u8 *buf)
{
- int err, lock_err, i;
- void (*write_op)(struct tg3 *, u32, u32);
+ int ret = 0;
+ u32 pagesize = tp->nvram_pagesize;
+ u32 pagemask = pagesize - 1;
+ u32 nvram_cmd;
+ u8 *tmp;
- if (cpu_base == TX_CPU_BASE && tg3_flag(tp, 5705_PLUS)) {
- netdev_err(tp->dev,
- "%s: Trying to load TX cpu firmware which is 5705\n",
- __func__);
- return -EINVAL;
- }
+ tmp = kmalloc(pagesize, GFP_KERNEL);
+ if (tmp == NULL)
+ return -ENOMEM;
- if (tg3_flag(tp, 5705_PLUS))
- write_op = tg3_write_mem;
- else
- write_op = tg3_write_indirect_reg32;
+ while (len) {
+ int j;
+ u32 phy_addr, page_off, size;
- /* It is possible that bootcode is still loading at this point.
- * Get the nvram lock first before halting the cpu.
- */
- lock_err = tg3_nvram_lock(tp);
- err = tg3_halt_cpu(tp, cpu_base);
- if (!lock_err)
- tg3_nvram_unlock(tp);
- if (err)
- goto out;
+ phy_addr = offset & ~pagemask;
- for (i = 0; i < cpu_scratch_size; i += sizeof(u32))
- write_op(tp, cpu_scratch_base + i, 0);
- tw32(cpu_base + CPU_STATE, 0xffffffff);
- tw32(cpu_base + CPU_MODE, tr32(cpu_base+CPU_MODE)|CPU_MODE_HALT);
- for (i = 0; i < (info->fw_len / sizeof(u32)); i++)
- write_op(tp, (cpu_scratch_base +
- (info->fw_base & 0xffff) +
- (i * sizeof(u32))),
- be32_to_cpu(info->fw_data[i]));
+ for (j = 0; j < pagesize; j += 4) {
+ ret = tg3_nvram_read_be32(tp, phy_addr + j,
+ (__be32 *) (tmp + j));
+ if (ret)
+ break;
+ }
+ if (ret)
+ break;
- err = 0;
+ page_off = offset & pagemask;
+ size = pagesize;
+ if (len < size)
+ size = len;
-out:
- return err;
-}
+ len -= size;
-/* tp->lock is held. */
-static int tg3_load_5701_a0_firmware_fix(struct tg3 *tp)
-{
- struct fw_info info;
- const __be32 *fw_data;
- int err, i;
+ memcpy(tmp + page_off, buf, size);
- fw_data = (void *)tp->fw->data;
+ offset = offset + (pagesize - page_off);
- /* Firmware blob starts with version numbers, followed by
- start address and length. We are setting complete length.
- length = end_address_of_bss - start_address_of_text.
- Remainder is the blob to be loaded contiguously
- from start address. */
+ tg3_enable_nvram_access(tp);
- info.fw_base = be32_to_cpu(fw_data[1]);
- info.fw_len = tp->fw->size - 12;
- info.fw_data = &fw_data[3];
+ /*
+ * Before we can erase the flash page, we need
+ * to issue a special "write enable" command.
+ */
+ nvram_cmd = NVRAM_CMD_WREN | NVRAM_CMD_GO | NVRAM_CMD_DONE;
- err = tg3_load_firmware_cpu(tp, RX_CPU_BASE,
- RX_CPU_SCRATCH_BASE, RX_CPU_SCRATCH_SIZE,
- &info);
- if (err)
- return err;
+ if (tg3_nvram_exec_cmd(tp, nvram_cmd))
+ break;
- err = tg3_load_firmware_cpu(tp, TX_CPU_BASE,
- TX_CPU_SCRATCH_BASE, TX_CPU_SCRATCH_SIZE,
- &info);
- if (err)
- return err;
+ /* Erase the target page */
+ tw32(NVRAM_ADDR, phy_addr);
- /* Now startup only the RX cpu. */
- tw32(RX_CPU_BASE + CPU_STATE, 0xffffffff);
- tw32_f(RX_CPU_BASE + CPU_PC, info.fw_base);
+ nvram_cmd = NVRAM_CMD_GO | NVRAM_CMD_DONE | NVRAM_CMD_WR |
+ NVRAM_CMD_FIRST | NVRAM_CMD_LAST | NVRAM_CMD_ERASE;
- for (i = 0; i < 5; i++) {
- if (tr32(RX_CPU_BASE + CPU_PC) == info.fw_base)
+ if (tg3_nvram_exec_cmd(tp, nvram_cmd))
break;
- tw32(RX_CPU_BASE + CPU_STATE, 0xffffffff);
- tw32(RX_CPU_BASE + CPU_MODE, CPU_MODE_HALT);
- tw32_f(RX_CPU_BASE + CPU_PC, info.fw_base);
- udelay(1000);
- }
- if (i >= 5) {
- netdev_err(tp->dev, "%s fails to set RX CPU PC, is %08x "
- "should be %08x\n", __func__,
- tr32(RX_CPU_BASE + CPU_PC), info.fw_base);
- return -ENODEV;
- }
- tw32(RX_CPU_BASE + CPU_STATE, 0xffffffff);
- tw32_f(RX_CPU_BASE + CPU_MODE, 0x00000000);
- return 0;
-}
+ /* Issue another write enable to start the write. */
+ nvram_cmd = NVRAM_CMD_WREN | NVRAM_CMD_GO | NVRAM_CMD_DONE;
-/* tp->lock is held. */
-static int tg3_load_tso_firmware(struct tg3 *tp)
-{
- struct fw_info info;
- const __be32 *fw_data;
- unsigned long cpu_base, cpu_scratch_base, cpu_scratch_size;
- int err, i;
+ if (tg3_nvram_exec_cmd(tp, nvram_cmd))
+ break;
- if (tg3_flag(tp, HW_TSO_1) ||
- tg3_flag(tp, HW_TSO_2) ||
- tg3_flag(tp, HW_TSO_3))
- return 0;
+ for (j = 0; j < pagesize; j += 4) {
+ __be32 data;
- fw_data = (void *)tp->fw->data;
+ data = *((__be32 *) (tmp + j));
+
+ tw32(NVRAM_WRDATA, be32_to_cpu(data));
+
+ tw32(NVRAM_ADDR, phy_addr + j);
+
+ nvram_cmd = NVRAM_CMD_GO | NVRAM_CMD_DONE |
+ NVRAM_CMD_WR;
+
+ if (j == 0)
+ nvram_cmd |= NVRAM_CMD_FIRST;
+ else if (j == (pagesize - 4))
+ nvram_cmd |= NVRAM_CMD_LAST;
+
+ ret = tg3_nvram_exec_cmd(tp, nvram_cmd);
+ if (ret)
+ break;
+ }
+ if (ret)
+ break;
+ }
+
+ nvram_cmd = NVRAM_CMD_WRDI | NVRAM_CMD_GO | NVRAM_CMD_DONE;
+ tg3_nvram_exec_cmd(tp, nvram_cmd);
+
+ kfree(tmp);
+
+ return ret;
+}
+
+/* offset and length are dword aligned */
+static int tg3_nvram_write_block_buffered(struct tg3 *tp, u32 offset, u32 len,
+ u8 *buf)
+{
+ int i, ret = 0;
+
+ for (i = 0; i < len; i += 4, offset += 4) {
+ u32 page_off, phy_addr, nvram_cmd;
+ __be32 data;
+
+ memcpy(&data, buf + i, 4);
+ tw32(NVRAM_WRDATA, be32_to_cpu(data));
+
+ page_off = offset % tp->nvram_pagesize;
+
+ phy_addr = tg3_nvram_phys_addr(tp, offset);
+
+ nvram_cmd = NVRAM_CMD_GO | NVRAM_CMD_DONE | NVRAM_CMD_WR;
+
+ if (page_off == 0 || i == 0)
+ nvram_cmd |= NVRAM_CMD_FIRST;
+ if (page_off == (tp->nvram_pagesize - 4))
+ nvram_cmd |= NVRAM_CMD_LAST;
+
+ if (i == (len - 4))
+ nvram_cmd |= NVRAM_CMD_LAST;
+
+ if ((nvram_cmd & NVRAM_CMD_FIRST) ||
+ !tg3_flag(tp, FLASH) ||
+ !tg3_flag(tp, 57765_PLUS))
+ tw32(NVRAM_ADDR, phy_addr);
+
+ if (GET_ASIC_REV(tp->pci_chip_rev_id) != ASIC_REV_5752 &&
+ !tg3_flag(tp, 5755_PLUS) &&
+ (tp->nvram_jedecnum == JEDEC_ST) &&
+ (nvram_cmd & NVRAM_CMD_FIRST)) {
+ u32 cmd;
+
+ cmd = NVRAM_CMD_WREN | NVRAM_CMD_GO | NVRAM_CMD_DONE;
+ ret = tg3_nvram_exec_cmd(tp, cmd);
+ if (ret)
+ break;
+ }
+ if (!tg3_flag(tp, FLASH)) {
+ /* We always do complete word writes to eeprom. */
+ nvram_cmd |= (NVRAM_CMD_FIRST | NVRAM_CMD_LAST);
+ }
+
+ ret = tg3_nvram_exec_cmd(tp, nvram_cmd);
+ if (ret)
+ break;
+ }
+ return ret;
+}
+
+/* offset and length are dword aligned */
+static int tg3_nvram_write_block(struct tg3 *tp, u32 offset, u32 len, u8 *buf)
+{
+ int ret;
+
+ if (tg3_flag(tp, EEPROM_WRITE_PROT)) {
+ tw32_f(GRC_LOCAL_CTRL, tp->grc_local_ctrl &
+ ~GRC_LCLCTRL_GPIO_OUTPUT1);
+ udelay(40);
+ }
+
+ if (!tg3_flag(tp, NVRAM)) {
+ ret = tg3_nvram_write_block_using_eeprom(tp, offset, len, buf);
+ } else {
+ u32 grc_mode;
+
+ ret = tg3_nvram_lock(tp);
+ if (ret)
+ return ret;
+
+ tg3_enable_nvram_access(tp);
+ if (tg3_flag(tp, 5750_PLUS) && !tg3_flag(tp, PROTECTED_NVRAM))
+ tw32(NVRAM_WRITE1, 0x406);
+
+ grc_mode = tr32(GRC_MODE);
+ tw32(GRC_MODE, grc_mode | GRC_MODE_NVRAM_WR_ENABLE);
+
+ if (tg3_flag(tp, NVRAM_BUFFERED) || !tg3_flag(tp, FLASH)) {
+ ret = tg3_nvram_write_block_buffered(tp, offset, len,
+ buf);
+ } else {
+ ret = tg3_nvram_write_block_unbuffered(tp, offset, len,
+ buf);
+ }
+
+ grc_mode = tr32(GRC_MODE);
+ tw32(GRC_MODE, grc_mode & ~GRC_MODE_NVRAM_WR_ENABLE);
+
+ tg3_disable_nvram_access(tp);
+ tg3_nvram_unlock(tp);
+ }
+
+ if (tg3_flag(tp, EEPROM_WRITE_PROT)) {
+ tw32_f(GRC_LOCAL_CTRL, tp->grc_local_ctrl);
+ udelay(40);
+ }
+
+ return ret;
+}
+
+#define RX_CPU_SCRATCH_BASE 0x30000
+#define RX_CPU_SCRATCH_SIZE 0x04000
+#define TX_CPU_SCRATCH_BASE 0x34000
+#define TX_CPU_SCRATCH_SIZE 0x04000
+
+/* tp->lock is held. */
+static int tg3_halt_cpu(struct tg3 *tp, u32 offset)
+{
+ int i;
+
+ BUG_ON(offset == TX_CPU_BASE && tg3_flag(tp, 5705_PLUS));
+
+ if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5906) {
+ u32 val = tr32(GRC_VCPU_EXT_CTRL);
+
+ tw32(GRC_VCPU_EXT_CTRL, val | GRC_VCPU_EXT_CTRL_HALT_CPU);
+ return 0;
+ }
+ if (offset == RX_CPU_BASE) {
+ for (i = 0; i < 10000; i++) {
+ tw32(offset + CPU_STATE, 0xffffffff);
+ tw32(offset + CPU_MODE, CPU_MODE_HALT);
+ if (tr32(offset + CPU_MODE) & CPU_MODE_HALT)
+ break;
+ }
+
+ tw32(offset + CPU_STATE, 0xffffffff);
+ tw32_f(offset + CPU_MODE, CPU_MODE_HALT);
+ udelay(10);
+ } else {
+ for (i = 0; i < 10000; i++) {
+ tw32(offset + CPU_STATE, 0xffffffff);
+ tw32(offset + CPU_MODE, CPU_MODE_HALT);
+ if (tr32(offset + CPU_MODE) & CPU_MODE_HALT)
+ break;
+ }
+ }
+
+ if (i >= 10000) {
+ netdev_err(tp->dev, "%s timed out, %s CPU\n",
+ __func__, offset == RX_CPU_BASE ? "RX" : "TX");
+ return -ENODEV;
+ }
+
+ /* Clear firmware's nvram arbitration. */
+ if (tg3_flag(tp, NVRAM))
+ tw32(NVRAM_SWARB, SWARB_REQ_CLR0);
+ return 0;
+}
+
+struct fw_info {
+ unsigned int fw_base;
+ unsigned int fw_len;
+ const __be32 *fw_data;
+};
+
+/* tp->lock is held. */
+static int tg3_load_firmware_cpu(struct tg3 *tp, u32 cpu_base,
+ u32 cpu_scratch_base, int cpu_scratch_size,
+ struct fw_info *info)
+{
+ int err, lock_err, i;
+ void (*write_op)(struct tg3 *, u32, u32);
+
+ if (cpu_base == TX_CPU_BASE && tg3_flag(tp, 5705_PLUS)) {
+ netdev_err(tp->dev,
+ "%s: Trying to load TX cpu firmware which is 5705\n",
+ __func__);
+ return -EINVAL;
+ }
+
+ if (tg3_flag(tp, 5705_PLUS))
+ write_op = tg3_write_mem;
+ else
+ write_op = tg3_write_indirect_reg32;
+
+ /* It is possible that bootcode is still loading at this point.
+ * Get the nvram lock first before halting the cpu.
+ */
+ lock_err = tg3_nvram_lock(tp);
+ err = tg3_halt_cpu(tp, cpu_base);
+ if (!lock_err)
+ tg3_nvram_unlock(tp);
+ if (err)
+ goto out;
+
+ for (i = 0; i < cpu_scratch_size; i += sizeof(u32))
+ write_op(tp, cpu_scratch_base + i, 0);
+ tw32(cpu_base + CPU_STATE, 0xffffffff);
+ tw32(cpu_base + CPU_MODE, tr32(cpu_base+CPU_MODE)|CPU_MODE_HALT);
+ for (i = 0; i < (info->fw_len / sizeof(u32)); i++)
+ write_op(tp, (cpu_scratch_base +
+ (info->fw_base & 0xffff) +
+ (i * sizeof(u32))),
+ be32_to_cpu(info->fw_data[i]));
+
+ err = 0;
+
+out:
+ return err;
+}
+
+/* tp->lock is held. */
+static int tg3_load_5701_a0_firmware_fix(struct tg3 *tp)
+{
+ struct fw_info info;
+ const __be32 *fw_data;
+ int err, i;
+
+ fw_data = (void *)tp->fw->data;
+
+ /* Firmware blob starts with version numbers, followed by
+ start address and length. We are setting complete length.
+ length = end_address_of_bss - start_address_of_text.
+ Remainder is the blob to be loaded contiguously
+ from start address. */
+
+ info.fw_base = be32_to_cpu(fw_data[1]);
+ info.fw_len = tp->fw->size - 12;
+ info.fw_data = &fw_data[3];
+
+ err = tg3_load_firmware_cpu(tp, RX_CPU_BASE,
+ RX_CPU_SCRATCH_BASE, RX_CPU_SCRATCH_SIZE,
+ &info);
+ if (err)
+ return err;
+
+ err = tg3_load_firmware_cpu(tp, TX_CPU_BASE,
+ TX_CPU_SCRATCH_BASE, TX_CPU_SCRATCH_SIZE,
+ &info);
+ if (err)
+ return err;
+
+ /* Now startup only the RX cpu. */
+ tw32(RX_CPU_BASE + CPU_STATE, 0xffffffff);
+ tw32_f(RX_CPU_BASE + CPU_PC, info.fw_base);
+
+ for (i = 0; i < 5; i++) {
+ if (tr32(RX_CPU_BASE + CPU_PC) == info.fw_base)
+ break;
+ tw32(RX_CPU_BASE + CPU_STATE, 0xffffffff);
+ tw32(RX_CPU_BASE + CPU_MODE, CPU_MODE_HALT);
+ tw32_f(RX_CPU_BASE + CPU_PC, info.fw_base);
+ udelay(1000);
+ }
+ if (i >= 5) {
+ netdev_err(tp->dev, "%s fails to set RX CPU PC, is %08x "
+ "should be %08x\n", __func__,
+ tr32(RX_CPU_BASE + CPU_PC), info.fw_base);
+ return -ENODEV;
+ }
+ tw32(RX_CPU_BASE + CPU_STATE, 0xffffffff);
+ tw32_f(RX_CPU_BASE + CPU_MODE, 0x00000000);
+
+ return 0;
+}
+
+/* tp->lock is held. */
+static int tg3_load_tso_firmware(struct tg3 *tp)
+{
+ struct fw_info info;
+ const __be32 *fw_data;
+ unsigned long cpu_base, cpu_scratch_base, cpu_scratch_size;
+ int err, i;
+
+ if (tg3_flag(tp, HW_TSO_1) ||
+ tg3_flag(tp, HW_TSO_2) ||
+ tg3_flag(tp, HW_TSO_3))
+ return 0;
+
+ fw_data = (void *)tp->fw->data;
/* Firmware blob starts with version numbers, followed by
start address and length. We are setting complete length.
return err;
}
+static int tg3_setup_phy(struct tg3 *, int);
+
static int tg3_power_down_prepare(struct tg3 *tp)
{
u32 misc_host_ctrl;
tp->phy_flags |= TG3_PHYFLG_IS_LOW_POWER;
- tp->link_config.orig_speed = phydev->speed;
- tp->link_config.orig_duplex = phydev->duplex;
- tp->link_config.orig_autoneg = phydev->autoneg;
- tp->link_config.orig_advertising = phydev->advertising;
+ tp->link_config.speed = phydev->speed;
+ tp->link_config.duplex = phydev->duplex;
+ tp->link_config.autoneg = phydev->autoneg;
+ tp->link_config.advertising = phydev->advertising;
advertising = ADVERTISED_TP |
ADVERTISED_Pause |
} else {
do_low_power = true;
- if (!(tp->phy_flags & TG3_PHYFLG_IS_LOW_POWER)) {
+ if (!(tp->phy_flags & TG3_PHYFLG_IS_LOW_POWER))
tp->phy_flags |= TG3_PHYFLG_IS_LOW_POWER;
- tp->link_config.orig_speed = tp->link_config.speed;
- tp->link_config.orig_duplex = tp->link_config.duplex;
- tp->link_config.orig_autoneg = tp->link_config.autoneg;
- }
- if (!(tp->phy_flags & TG3_PHYFLG_ANY_SERDES)) {
- tp->link_config.speed = SPEED_10;
- tp->link_config.duplex = DUPLEX_HALF;
- tp->link_config.autoneg = AUTONEG_ENABLE;
+ if (!(tp->phy_flags & TG3_PHYFLG_ANY_SERDES))
tg3_setup_phy(tp, 0);
- }
}
if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5906) {
DUPLEX_HALF;
break;
}
- *speed = SPEED_INVALID;
- *duplex = DUPLEX_INVALID;
+ *speed = SPEED_UNKNOWN;
+ *duplex = DUPLEX_UNKNOWN;
break;
}
}
static void tg3_phy_copper_begin(struct tg3 *tp)
{
- u32 new_adv;
- int i;
+ if (tp->link_config.autoneg == AUTONEG_ENABLE ||
+ (tp->phy_flags & TG3_PHYFLG_IS_LOW_POWER)) {
+ u32 adv, fc;
- if (tp->phy_flags & TG3_PHYFLG_IS_LOW_POWER) {
- new_adv = ADVERTISED_10baseT_Half |
- ADVERTISED_10baseT_Full;
- if (tg3_flag(tp, WOL_SPEED_100MB))
- new_adv |= ADVERTISED_100baseT_Half |
- ADVERTISED_100baseT_Full;
-
- tg3_phy_autoneg_cfg(tp, new_adv,
- FLOW_CTRL_TX | FLOW_CTRL_RX);
- } else if (tp->link_config.speed == SPEED_INVALID) {
- if (tp->phy_flags & TG3_PHYFLG_10_100_ONLY)
- tp->link_config.advertising &=
- ~(ADVERTISED_1000baseT_Half |
- ADVERTISED_1000baseT_Full);
+ if (tp->phy_flags & TG3_PHYFLG_IS_LOW_POWER) {
+ adv = ADVERTISED_10baseT_Half |
+ ADVERTISED_10baseT_Full;
+ if (tg3_flag(tp, WOL_SPEED_100MB))
+ adv |= ADVERTISED_100baseT_Half |
+ ADVERTISED_100baseT_Full;
- tg3_phy_autoneg_cfg(tp, tp->link_config.advertising,
- tp->link_config.flowctrl);
- } else {
- /* Asking for a specific link mode. */
- if (tp->link_config.speed == SPEED_1000) {
- if (tp->link_config.duplex == DUPLEX_FULL)
- new_adv = ADVERTISED_1000baseT_Full;
- else
- new_adv = ADVERTISED_1000baseT_Half;
- } else if (tp->link_config.speed == SPEED_100) {
- if (tp->link_config.duplex == DUPLEX_FULL)
- new_adv = ADVERTISED_100baseT_Full;
- else
- new_adv = ADVERTISED_100baseT_Half;
+ fc = FLOW_CTRL_TX | FLOW_CTRL_RX;
} else {
- if (tp->link_config.duplex == DUPLEX_FULL)
- new_adv = ADVERTISED_10baseT_Full;
- else
- new_adv = ADVERTISED_10baseT_Half;
+ adv = tp->link_config.advertising;
+ if (tp->phy_flags & TG3_PHYFLG_10_100_ONLY)
+ adv &= ~(ADVERTISED_1000baseT_Half |
+ ADVERTISED_1000baseT_Full);
+
+ fc = tp->link_config.flowctrl;
}
- tg3_phy_autoneg_cfg(tp, new_adv,
- tp->link_config.flowctrl);
- }
+ tg3_phy_autoneg_cfg(tp, adv, fc);
- if (tp->link_config.autoneg == AUTONEG_DISABLE &&
- tp->link_config.speed != SPEED_INVALID) {
+ tg3_writephy(tp, MII_BMCR,
+ BMCR_ANENABLE | BMCR_ANRESTART);
+ } else {
+ int i;
u32 bmcr, orig_bmcr;
tp->link_config.active_speed = tp->link_config.speed;
tg3_writephy(tp, MII_BMCR, bmcr);
udelay(40);
}
- } else {
- tg3_writephy(tp, MII_BMCR,
- BMCR_ANENABLE | BMCR_ANRESTART);
}
}
if (tg3_readphy(tp, MII_CTRL1000, &tg3_ctrl))
return false;
- tg3_ctrl &= (ADVERTISE_1000HALF | ADVERTISE_1000FULL);
+ if (tgtadv &&
+ (tp->pci_chip_rev_id == CHIPREV_ID_5701_A0 ||
+ tp->pci_chip_rev_id == CHIPREV_ID_5701_B0)) {
+ tgtadv |= CTL1000_AS_MASTER | CTL1000_ENABLE_MASTER;
+ tg3_ctrl &= (ADVERTISE_1000HALF | ADVERTISE_1000FULL |
+ CTL1000_AS_MASTER | CTL1000_ENABLE_MASTER);
+ } else {
+ tg3_ctrl &= (ADVERTISE_1000HALF | ADVERTISE_1000FULL);
+ }
+
if (tg3_ctrl != tgtadv)
return false;
}
}
current_link_up = 0;
- current_speed = SPEED_INVALID;
- current_duplex = DUPLEX_INVALID;
+ current_speed = SPEED_UNKNOWN;
+ current_duplex = DUPLEX_UNKNOWN;
tp->phy_flags &= ~TG3_PHYFLG_MDIX_STATE;
tp->link_config.rmt_adv = 0;
LED_CTRL_LNKLED_OVERRIDE |
LED_CTRL_1000MBPS_ON));
} else {
- tp->link_config.active_speed = SPEED_INVALID;
- tp->link_config.active_duplex = DUPLEX_INVALID;
+ tp->link_config.active_speed = SPEED_UNKNOWN;
+ tp->link_config.active_duplex = DUPLEX_UNKNOWN;
tw32(MAC_LED_CTRL, (tp->led_ctrl |
LED_CTRL_LNKLED_OVERRIDE |
LED_CTRL_TRAFFIC_OVERRIDE));
tg3_phy_reset(tp);
current_link_up = 0;
- current_speed = SPEED_INVALID;
- current_duplex = DUPLEX_INVALID;
+ current_speed = SPEED_UNKNOWN;
+ current_duplex = DUPLEX_UNKNOWN;
tp->link_config.rmt_adv = 0;
err |= tg3_readphy(tp, MII_BMSR, &bmsr);
}
}
- netdev_completed_queue(tp->dev, pkts_compl, bytes_compl);
+ netdev_tx_completed_queue(txq, pkts_compl, bytes_compl);
tnapi->tx_cons = sw_idx;
/* Refill RX ring(s). */
if (!tg3_flag(tp, ENABLE_RSS)) {
+ /* Sync BD data before updating mailbox */
+ wmb();
+
if (work_mask & RXD_OPAQUE_RING_STD) {
tpr->rx_std_prod_idx = std_prod_idx &
tp->rx_std_ring_mask;
{
cancel_work_sync(&tp->reset_task);
tg3_flag_clear(tp, RESET_TASK_PENDING);
+ tg3_flag_clear(tp, TX_RECOVERY_PENDING);
}
static int tg3_poll_msix(struct napi_struct *napi, int budget)
return IRQ_RETVAL(0);
}
-static int tg3_init_hw(struct tg3 *, int);
-static int tg3_halt(struct tg3 *, int, int);
-
-/* Restart hardware after configuration changes, self-test, etc.
- * Invoked with tp->lock held.
- */
-static int tg3_restart_hw(struct tg3 *tp, int reset_phy)
- __releases(tp->lock)
- __acquires(tp->lock)
-{
- int err;
-
- err = tg3_init_hw(tp, reset_phy);
- if (err) {
- netdev_err(tp->dev,
- "Failed to re-initialize device, aborting\n");
- tg3_halt(tp, RESET_KIND_SHUTDOWN, 1);
- tg3_full_unlock(tp);
- del_timer_sync(&tp->timer);
- tp->irq_sync = 0;
- tg3_napi_enable(tp);
- dev_close(tp->dev);
- tg3_full_lock(tp, 0);
- }
- return err;
-}
-
#ifdef CONFIG_NET_POLL_CONTROLLER
static void tg3_poll_controller(struct net_device *dev)
{
}
#endif
-static void tg3_reset_task(struct work_struct *work)
-{
- struct tg3 *tp = container_of(work, struct tg3, reset_task);
- int err;
-
- tg3_full_lock(tp, 0);
-
- if (!netif_running(tp->dev)) {
- tg3_flag_clear(tp, RESET_TASK_PENDING);
- tg3_full_unlock(tp);
- return;
- }
-
- tg3_full_unlock(tp);
-
- tg3_phy_stop(tp);
-
- tg3_netif_stop(tp);
-
- tg3_full_lock(tp, 1);
-
- if (tg3_flag(tp, TX_RECOVERY_PENDING)) {
- tp->write32_tx_mbox = tg3_write32_tx_mbox;
- tp->write32_rx_mbox = tg3_write_flush_reg32;
- tg3_flag_set(tp, MBOX_WRITE_REORDER);
- tg3_flag_clear(tp, TX_RECOVERY_PENDING);
- }
-
- tg3_halt(tp, RESET_KIND_SHUTDOWN, 0);
- err = tg3_init_hw(tp, 1);
- if (err)
- goto out;
-
- tg3_netif_start(tp);
-
-out:
- tg3_full_unlock(tp);
-
- if (!err)
- tg3_phy_start(tp);
-
- tg3_flag_clear(tp, RESET_TASK_PENDING);
-}
-
static void tg3_tx_timeout(struct net_device *dev)
{
struct tg3 *tp = netdev_priv(dev);
((skb_shinfo(skb)->nr_frags == 0) ? TXD_FLAG_END : 0),
mss, vlan)) {
would_hit_hwbug = 1;
- /* Now loop through additional data fragments, and queue them. */
} else if (skb_shinfo(skb)->nr_frags > 0) {
u32 tmp_mss = mss;
!tg3_flag(tp, HW_TSO_3))
tmp_mss = 0;
+ /* Now loop through additional data
+ * fragments, and queue them.
+ */
last = skb_shinfo(skb)->nr_frags - 1;
for (i = 0; i <= last; i++) {
skb_frag_t *frag = &skb_shinfo(skb)->frags[i];
}
skb_tx_timestamp(skb);
- netdev_sent_queue(tp->dev, skb->len);
+ netdev_tx_sent_queue(txq, skb->len);
+ /* Sync BD data before updating mailbox */
+ wmb();
+
/* Packets are ready, update Tx producer idx local and on card. */
tw32_tx_mbox(tnapi->prodmbox, entry);
return 0;
}
-static inline void tg3_set_mtu(struct net_device *dev, struct tg3 *tp,
- int new_mtu)
-{
- dev->mtu = new_mtu;
-
- if (new_mtu > ETH_DATA_LEN) {
- if (tg3_flag(tp, 5780_CLASS)) {
- netdev_update_features(dev);
- tg3_flag_clear(tp, TSO_CAPABLE);
- } else {
- tg3_flag_set(tp, JUMBO_RING_ENABLE);
- }
- } else {
- if (tg3_flag(tp, 5780_CLASS)) {
- tg3_flag_set(tp, TSO_CAPABLE);
- netdev_update_features(dev);
- }
- tg3_flag_clear(tp, JUMBO_RING_ENABLE);
- }
-}
-
-static int tg3_change_mtu(struct net_device *dev, int new_mtu)
-{
- struct tg3 *tp = netdev_priv(dev);
- int err;
-
- if (new_mtu < TG3_MIN_MTU || new_mtu > TG3_MAX_MTU(tp))
- return -EINVAL;
-
- if (!netif_running(dev)) {
- /* We'll just catch it later when the
- * device is up'd.
- */
- tg3_set_mtu(dev, tp, new_mtu);
- return 0;
- }
-
- tg3_phy_stop(tp);
-
- tg3_netif_stop(tp);
-
- tg3_full_lock(tp, 1);
-
- tg3_halt(tp, RESET_KIND_SHUTDOWN, 1);
-
- tg3_set_mtu(dev, tp, new_mtu);
-
- err = tg3_restart_hw(tp, 0);
-
- if (!err)
- tg3_netif_start(tp);
-
- tg3_full_unlock(tp);
-
- if (!err)
- tg3_phy_start(tp);
-
- return err;
-}
-
static void tg3_rx_prodring_free(struct tg3 *tp,
struct tg3_rx_prodring_set *tpr)
{
dev_kfree_skb_any(skb);
}
+ netdev_tx_reset_queue(netdev_get_tx_queue(tp->dev, j));
}
- netdev_reset_queue(tp->dev);
}
/* Initialize tx/rx rings for packet processing.
if (tp->hw_stats) {
/* Save the stats across chip resets... */
- tg3_get_nstats(tp, &tp->net_stats_prev),
+ tg3_get_nstats(tp, &tp->net_stats_prev);
tg3_get_estats(tp, &tp->estats_prev);
/* And make sure the next sample is new data */
int err = 0, skip_mac_1 = 0;
if (!is_valid_ether_addr(addr->sa_data))
- return -EINVAL;
+ return -EADDRNOTAVAIL;
memcpy(dev->dev_addr, addr->sa_data, dev->addr_len);
nic_addr);
}
-static void __tg3_set_rx_mode(struct net_device *);
static void __tg3_set_coalesce(struct tg3 *tp, struct ethtool_coalesce *ec)
{
int i;
tw32(JMB_REPLENISH_LWM, bdcache_maxcnt);
}
+static inline u32 calc_crc(unsigned char *buf, int len)
+{
+ u32 reg;
+ u32 tmp;
+ int j, k;
+
+ reg = 0xffffffff;
+
+ for (j = 0; j < len; j++) {
+ reg ^= buf[j];
+
+ for (k = 0; k < 8; k++) {
+ tmp = reg & 0x01;
+
+ reg >>= 1;
+
+ if (tmp)
+ reg ^= 0xedb88320;
+ }
+ }
+
+ return ~reg;
+}
+
+static void tg3_set_multi(struct tg3 *tp, unsigned int accept_all)
+{
+ /* accept or reject all multicast frames */
+ tw32(MAC_HASH_REG_0, accept_all ? 0xffffffff : 0);
+ tw32(MAC_HASH_REG_1, accept_all ? 0xffffffff : 0);
+ tw32(MAC_HASH_REG_2, accept_all ? 0xffffffff : 0);
+ tw32(MAC_HASH_REG_3, accept_all ? 0xffffffff : 0);
+}
+
+static void __tg3_set_rx_mode(struct net_device *dev)
+{
+ struct tg3 *tp = netdev_priv(dev);
+ u32 rx_mode;
+
+ rx_mode = tp->rx_mode & ~(RX_MODE_PROMISC |
+ RX_MODE_KEEP_VLAN_TAG);
+
+#if !defined(CONFIG_VLAN_8021Q) && !defined(CONFIG_VLAN_8021Q_MODULE)
+ /* When ASF is in use, we always keep the RX_MODE_KEEP_VLAN_TAG
+ * flag clear.
+ */
+ if (!tg3_flag(tp, ENABLE_ASF))
+ rx_mode |= RX_MODE_KEEP_VLAN_TAG;
+#endif
+
+ if (dev->flags & IFF_PROMISC) {
+ /* Promiscuous mode. */
+ rx_mode |= RX_MODE_PROMISC;
+ } else if (dev->flags & IFF_ALLMULTI) {
+ /* Accept all multicast. */
+ tg3_set_multi(tp, 1);
+ } else if (netdev_mc_empty(dev)) {
+ /* Reject all multicast. */
+ tg3_set_multi(tp, 0);
+ } else {
+ /* Accept one or more multicast(s). */
+ struct netdev_hw_addr *ha;
+ u32 mc_filter[4] = { 0, };
+ u32 regidx;
+ u32 bit;
+ u32 crc;
+
+ netdev_for_each_mc_addr(ha, dev) {
+ crc = calc_crc(ha->addr, ETH_ALEN);
+ bit = ~crc & 0x7f;
+ regidx = (bit & 0x60) >> 5;
+ bit &= 0x1f;
+ mc_filter[regidx] |= (1 << bit);
+ }
+
+ tw32(MAC_HASH_REG_0, mc_filter[0]);
+ tw32(MAC_HASH_REG_1, mc_filter[1]);
+ tw32(MAC_HASH_REG_2, mc_filter[2]);
+ tw32(MAC_HASH_REG_3, mc_filter[3]);
+ }
+
+ if (rx_mode != tp->rx_mode) {
+ tp->rx_mode = rx_mode;
+ tw32_f(MAC_RX_MODE, rx_mode);
+ udelay(10);
+ }
+}
+
static void tg3_rss_init_dflt_indir_tbl(struct tg3 *tp)
{
int i;
if (tg3_flag(tp, PCI_EXPRESS))
rdmac_mode |= RDMAC_MODE_FIFO_LONG_BURST;
- if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_57766)
- rdmac_mode |= RDMAC_MODE_JMB_2K_MMRR;
-
if (tg3_flag(tp, HW_TSO_1) ||
tg3_flag(tp, HW_TSO_2) ||
tg3_flag(tp, HW_TSO_3))
}
if (!tg3_flag(tp, USE_PHYLIB)) {
- if (tp->phy_flags & TG3_PHYFLG_IS_LOW_POWER) {
+ if (tp->phy_flags & TG3_PHYFLG_IS_LOW_POWER)
tp->phy_flags &= ~TG3_PHYFLG_IS_LOW_POWER;
- tp->link_config.speed = tp->link_config.orig_speed;
- tp->link_config.duplex = tp->link_config.orig_duplex;
- tp->link_config.autoneg = tp->link_config.orig_autoneg;
- }
err = tg3_setup_phy(tp, 0);
if (err)
tg3_serdes_parallel_detect(tp);
}
- tp->timer_counter = tp->timer_multiplier;
+ tp->timer_counter = tp->timer_multiplier;
+ }
+
+ /* Heartbeat is only sent once every 2 seconds.
+ *
+ * The heartbeat is to tell the ASF firmware that the host
+ * driver is still alive. In the event that the OS crashes,
+ * ASF needs to reset the hardware to free up the FIFO space
+ * that may be filled with rx packets destined for the host.
+ * If the FIFO is full, ASF will no longer function properly.
+ *
+ * Unintended resets have been reported on real time kernels
+ * where the timer doesn't run on time. Netpoll will also have
+ * same problem.
+ *
+ * The new FWCMD_NICDRV_ALIVE3 command tells the ASF firmware
+ * to check the ring condition when the heartbeat is expiring
+ * before doing the reset. This will prevent most unintended
+ * resets.
+ */
+ if (!--tp->asf_counter) {
+ if (tg3_flag(tp, ENABLE_ASF) && !tg3_flag(tp, ENABLE_APE)) {
+ tg3_wait_for_event_ack(tp);
+
+ tg3_write_mem(tp, NIC_SRAM_FW_CMD_MBOX,
+ FWCMD_NICDRV_ALIVE3);
+ tg3_write_mem(tp, NIC_SRAM_FW_CMD_LEN_MBOX, 4);
+ tg3_write_mem(tp, NIC_SRAM_FW_CMD_DATA_MBOX,
+ TG3_FW_UPDATE_TIMEOUT_SEC);
+
+ tg3_generate_fw_event(tp);
+ }
+ tp->asf_counter = tp->asf_multiplier;
+ }
+
+ spin_unlock(&tp->lock);
+
+restart_timer:
+ tp->timer.expires = jiffies + tp->timer_offset;
+ add_timer(&tp->timer);
+}
+
+static void __devinit tg3_timer_init(struct tg3 *tp)
+{
+ if (tg3_flag(tp, TAGGED_STATUS) &&
+ GET_ASIC_REV(tp->pci_chip_rev_id) != ASIC_REV_5717 &&
+ !tg3_flag(tp, 57765_CLASS))
+ tp->timer_offset = HZ;
+ else
+ tp->timer_offset = HZ / 10;
+
+ BUG_ON(tp->timer_offset > HZ);
+
+ tp->timer_multiplier = (HZ / tp->timer_offset);
+ tp->asf_multiplier = (HZ / tp->timer_offset) *
+ TG3_FW_UPDATE_FREQ_SEC;
+
+ init_timer(&tp->timer);
+ tp->timer.data = (unsigned long) tp;
+ tp->timer.function = tg3_timer;
+}
+
+static void tg3_timer_start(struct tg3 *tp)
+{
+ tp->asf_counter = tp->asf_multiplier;
+ tp->timer_counter = tp->timer_multiplier;
+
+ tp->timer.expires = jiffies + tp->timer_offset;
+ add_timer(&tp->timer);
+}
+
+static void tg3_timer_stop(struct tg3 *tp)
+{
+ del_timer_sync(&tp->timer);
+}
+
+/* Restart hardware after configuration changes, self-test, etc.
+ * Invoked with tp->lock held.
+ */
+static int tg3_restart_hw(struct tg3 *tp, int reset_phy)
+ __releases(tp->lock)
+ __acquires(tp->lock)
+{
+ int err;
+
+ err = tg3_init_hw(tp, reset_phy);
+ if (err) {
+ netdev_err(tp->dev,
+ "Failed to re-initialize device, aborting\n");
+ tg3_halt(tp, RESET_KIND_SHUTDOWN, 1);
+ tg3_full_unlock(tp);
+ tg3_timer_stop(tp);
+ tp->irq_sync = 0;
+ tg3_napi_enable(tp);
+ dev_close(tp->dev);
+ tg3_full_lock(tp, 0);
+ }
+ return err;
+}
+
+static void tg3_reset_task(struct work_struct *work)
+{
+ struct tg3 *tp = container_of(work, struct tg3, reset_task);
+ int err;
+
+ tg3_full_lock(tp, 0);
+
+ if (!netif_running(tp->dev)) {
+ tg3_flag_clear(tp, RESET_TASK_PENDING);
+ tg3_full_unlock(tp);
+ return;
+ }
+
+ tg3_full_unlock(tp);
+
+ tg3_phy_stop(tp);
+
+ tg3_netif_stop(tp);
+
+ tg3_full_lock(tp, 1);
+
+ if (tg3_flag(tp, TX_RECOVERY_PENDING)) {
+ tp->write32_tx_mbox = tg3_write32_tx_mbox;
+ tp->write32_rx_mbox = tg3_write_flush_reg32;
+ tg3_flag_set(tp, MBOX_WRITE_REORDER);
+ tg3_flag_clear(tp, TX_RECOVERY_PENDING);
}
- /* Heartbeat is only sent once every 2 seconds.
- *
- * The heartbeat is to tell the ASF firmware that the host
- * driver is still alive. In the event that the OS crashes,
- * ASF needs to reset the hardware to free up the FIFO space
- * that may be filled with rx packets destined for the host.
- * If the FIFO is full, ASF will no longer function properly.
- *
- * Unintended resets have been reported on real time kernels
- * where the timer doesn't run on time. Netpoll will also have
- * same problem.
- *
- * The new FWCMD_NICDRV_ALIVE3 command tells the ASF firmware
- * to check the ring condition when the heartbeat is expiring
- * before doing the reset. This will prevent most unintended
- * resets.
- */
- if (!--tp->asf_counter) {
- if (tg3_flag(tp, ENABLE_ASF) && !tg3_flag(tp, ENABLE_APE)) {
- tg3_wait_for_event_ack(tp);
+ tg3_halt(tp, RESET_KIND_SHUTDOWN, 0);
+ err = tg3_init_hw(tp, 1);
+ if (err)
+ goto out;
- tg3_write_mem(tp, NIC_SRAM_FW_CMD_MBOX,
- FWCMD_NICDRV_ALIVE3);
- tg3_write_mem(tp, NIC_SRAM_FW_CMD_LEN_MBOX, 4);
- tg3_write_mem(tp, NIC_SRAM_FW_CMD_DATA_MBOX,
- TG3_FW_UPDATE_TIMEOUT_SEC);
+ tg3_netif_start(tp);
- tg3_generate_fw_event(tp);
- }
- tp->asf_counter = tp->asf_multiplier;
- }
+out:
+ tg3_full_unlock(tp);
- spin_unlock(&tp->lock);
+ if (!err)
+ tg3_phy_start(tp);
-restart_timer:
- tp->timer.expires = jiffies + tp->timer_offset;
- add_timer(&tp->timer);
+ tg3_flag_clear(tp, RESET_TASK_PENDING);
}
static int tg3_request_irq(struct tg3 *tp, int irq_num)
}
err = request_irq(tnapi->irq_vec, tg3_test_isr,
- IRQF_SHARED | IRQF_SAMPLE_RANDOM, dev->name, tnapi);
+ IRQF_SHARED, dev->name, tnapi);
if (err)
return err;
if (err) {
tg3_halt(tp, RESET_KIND_SHUTDOWN, 1);
tg3_free_rings(tp);
- } else {
- if (tg3_flag(tp, TAGGED_STATUS) &&
- GET_ASIC_REV(tp->pci_chip_rev_id) != ASIC_REV_5717 &&
- !tg3_flag(tp, 57765_CLASS))
- tp->timer_offset = HZ;
- else
- tp->timer_offset = HZ / 10;
-
- BUG_ON(tp->timer_offset > HZ);
- tp->timer_counter = tp->timer_multiplier =
- (HZ / tp->timer_offset);
- tp->asf_counter = tp->asf_multiplier =
- ((HZ / tp->timer_offset) * 2);
-
- init_timer(&tp->timer);
- tp->timer.expires = jiffies + tp->timer_offset;
- tp->timer.data = (unsigned long) tp;
- tp->timer.function = tg3_timer;
}
tg3_full_unlock(tp);
tg3_full_lock(tp, 0);
- add_timer(&tp->timer);
+ tg3_timer_start(tp);
tg3_flag_set(tp, INIT_COMPLETE);
tg3_enable_ints(tp);
netif_tx_stop_all_queues(dev);
- del_timer_sync(&tp->timer);
+ tg3_timer_stop(tp);
tg3_phy_stop(tp);
struct tg3_ethtool_stats *old_estats = &tp->estats_prev;
struct tg3_hw_stats *hw_stats = tp->hw_stats;
- if (!hw_stats)
- return;
-
ESTAT_ADD(rx_octets);
ESTAT_ADD(rx_fragments);
ESTAT_ADD(rx_ucast_packets);
stats->tx_dropped = tp->tx_dropped;
}
-static inline u32 calc_crc(unsigned char *buf, int len)
-{
- u32 reg;
- u32 tmp;
- int j, k;
-
- reg = 0xffffffff;
-
- for (j = 0; j < len; j++) {
- reg ^= buf[j];
-
- for (k = 0; k < 8; k++) {
- tmp = reg & 0x01;
-
- reg >>= 1;
-
- if (tmp)
- reg ^= 0xedb88320;
- }
- }
-
- return ~reg;
-}
-
-static void tg3_set_multi(struct tg3 *tp, unsigned int accept_all)
-{
- /* accept or reject all multicast frames */
- tw32(MAC_HASH_REG_0, accept_all ? 0xffffffff : 0);
- tw32(MAC_HASH_REG_1, accept_all ? 0xffffffff : 0);
- tw32(MAC_HASH_REG_2, accept_all ? 0xffffffff : 0);
- tw32(MAC_HASH_REG_3, accept_all ? 0xffffffff : 0);
-}
-
-static void __tg3_set_rx_mode(struct net_device *dev)
-{
- struct tg3 *tp = netdev_priv(dev);
- u32 rx_mode;
-
- rx_mode = tp->rx_mode & ~(RX_MODE_PROMISC |
- RX_MODE_KEEP_VLAN_TAG);
-
-#if !defined(CONFIG_VLAN_8021Q) && !defined(CONFIG_VLAN_8021Q_MODULE)
- /* When ASF is in use, we always keep the RX_MODE_KEEP_VLAN_TAG
- * flag clear.
- */
- if (!tg3_flag(tp, ENABLE_ASF))
- rx_mode |= RX_MODE_KEEP_VLAN_TAG;
-#endif
-
- if (dev->flags & IFF_PROMISC) {
- /* Promiscuous mode. */
- rx_mode |= RX_MODE_PROMISC;
- } else if (dev->flags & IFF_ALLMULTI) {
- /* Accept all multicast. */
- tg3_set_multi(tp, 1);
- } else if (netdev_mc_empty(dev)) {
- /* Reject all multicast. */
- tg3_set_multi(tp, 0);
- } else {
- /* Accept one or more multicast(s). */
- struct netdev_hw_addr *ha;
- u32 mc_filter[4] = { 0, };
- u32 regidx;
- u32 bit;
- u32 crc;
-
- netdev_for_each_mc_addr(ha, dev) {
- crc = calc_crc(ha->addr, ETH_ALEN);
- bit = ~crc & 0x7f;
- regidx = (bit & 0x60) >> 5;
- bit &= 0x1f;
- mc_filter[regidx] |= (1 << bit);
- }
-
- tw32(MAC_HASH_REG_0, mc_filter[0]);
- tw32(MAC_HASH_REG_1, mc_filter[1]);
- tw32(MAC_HASH_REG_2, mc_filter[2]);
- tw32(MAC_HASH_REG_3, mc_filter[3]);
- }
-
- if (rx_mode != tp->rx_mode) {
- tp->rx_mode = rx_mode;
- tw32_f(MAC_RX_MODE, rx_mode);
- udelay(10);
- }
-}
-
-static void tg3_set_rx_mode(struct net_device *dev)
-{
- struct tg3 *tp = netdev_priv(dev);
-
- if (!netif_running(dev))
- return;
-
- tg3_full_lock(tp, 0);
- __tg3_set_rx_mode(dev);
- tg3_full_unlock(tp);
-}
-
static int tg3_get_regs_len(struct net_device *dev)
{
return TG3_REG_BLK_SIZE;
return 0;
}
-static int tg3_nvram_write_block(struct tg3 *tp, u32 offset, u32 len, u8 *buf);
-
static int tg3_set_eeprom(struct net_device *dev, struct ethtool_eeprom *eeprom, u8 *data)
{
struct tg3 *tp = netdev_priv(dev);
cmd->eth_tp_mdix = ETH_TP_MDI;
}
} else {
- ethtool_cmd_speed_set(cmd, SPEED_INVALID);
- cmd->duplex = DUPLEX_INVALID;
+ ethtool_cmd_speed_set(cmd, SPEED_UNKNOWN);
+ cmd->duplex = DUPLEX_UNKNOWN;
cmd->eth_tp_mdix = ETH_TP_MDI_INVALID;
}
cmd->phy_address = tp->phy_addr;
if (cmd->autoneg == AUTONEG_ENABLE) {
tp->link_config.advertising = (cmd->advertising |
ADVERTISED_Autoneg);
- tp->link_config.speed = SPEED_INVALID;
- tp->link_config.duplex = DUPLEX_INVALID;
+ tp->link_config.speed = SPEED_UNKNOWN;
+ tp->link_config.duplex = DUPLEX_UNKNOWN;
} else {
tp->link_config.advertising = 0;
tp->link_config.speed = speed;
tp->link_config.duplex = cmd->duplex;
}
- tp->link_config.orig_speed = tp->link_config.speed;
- tp->link_config.orig_duplex = tp->link_config.duplex;
- tp->link_config.orig_autoneg = tp->link_config.autoneg;
-
if (netif_running(dev))
tg3_setup_phy(tp, 1);
if (!epause->autoneg)
tg3_setup_flow_control(tp, 0, 0);
} else {
- tp->link_config.orig_advertising &=
+ tp->link_config.advertising &=
~(ADVERTISED_Pause |
ADVERTISED_Asym_Pause);
- tp->link_config.orig_advertising |= newadv;
+ tp->link_config.advertising |= newadv;
}
} else {
int irq_sync = 0;
{
struct tg3 *tp = netdev_priv(dev);
- tg3_get_estats(tp, (struct tg3_ethtool_stats *)tmp_stats);
+ if (tp->hw_stats)
+ tg3_get_estats(tp, (struct tg3_ethtool_stats *)tmp_stats);
+ else
+ memset(tmp_stats, 0, sizeof(struct tg3_ethtool_stats));
}
static __be32 *tg3_vpd_readblock(struct tg3 *tp, u32 *vpdlen)
} else {
num_pkts = 1;
data_off = ETH_HLEN;
+
+ if (tg3_flag(tp, USE_JUMBO_BDFLAG) &&
+ tx_len > VLAN_ETH_FRAME_LEN)
+ base_flags |= TXD_FLAG_JMB_PKT;
}
for (i = data_off; i < tx_len; i++)
tnapi->tx_prod++;
+ /* Sync BD data before updating mailbox */
+ wmb();
+
tw32_tx_mbox(tnapi->prodmbox, tnapi->tx_prod);
tr32_mailbox(tnapi->prodmbox);
{
int err = -EIO;
u32 eee_cap;
+ u32 jmb_pkt_sz = 9000;
+
+ if (tp->dma_limit)
+ jmb_pkt_sz = tp->dma_limit - ETH_HLEN;
eee_cap = tp->phy_flags & TG3_PHYFLG_EEE_CAP;
tp->phy_flags &= ~TG3_PHYFLG_EEE_CAP;
data[0] |= TG3_STD_LOOPBACK_FAILED;
if (tg3_flag(tp, JUMBO_RING_ENABLE) &&
- tg3_run_loopback(tp, 9000 + ETH_HLEN, false))
+ tg3_run_loopback(tp, jmb_pkt_sz + ETH_HLEN, false))
data[0] |= TG3_JMB_LOOPBACK_FAILED;
tg3_mac_loopback(tp, false);
tg3_run_loopback(tp, ETH_FRAME_LEN, true))
data[1] |= TG3_TSO_LOOPBACK_FAILED;
if (tg3_flag(tp, JUMBO_RING_ENABLE) &&
- tg3_run_loopback(tp, 9000 + ETH_HLEN, false))
+ tg3_run_loopback(tp, jmb_pkt_sz + ETH_HLEN, false))
data[1] |= TG3_JMB_LOOPBACK_FAILED;
if (do_extlpbk) {
tg3_run_loopback(tp, ETH_FRAME_LEN, true))
data[2] |= TG3_TSO_LOOPBACK_FAILED;
if (tg3_flag(tp, JUMBO_RING_ENABLE) &&
- tg3_run_loopback(tp, 9000 + ETH_HLEN, false))
+ tg3_run_loopback(tp, jmb_pkt_sz + ETH_HLEN, false))
data[2] |= TG3_JMB_LOOPBACK_FAILED;
}
.set_rxfh_indir = tg3_set_rxfh_indir,
};
+static struct rtnl_link_stats64 *tg3_get_stats64(struct net_device *dev,
+ struct rtnl_link_stats64 *stats)
+{
+ struct tg3 *tp = netdev_priv(dev);
+
+ if (!tp->hw_stats)
+ return &tp->net_stats_prev;
+
+ spin_lock_bh(&tp->lock);
+ tg3_get_nstats(tp, stats);
+ spin_unlock_bh(&tp->lock);
+
+ return stats;
+}
+
+static void tg3_set_rx_mode(struct net_device *dev)
+{
+ struct tg3 *tp = netdev_priv(dev);
+
+ if (!netif_running(dev))
+ return;
+
+ tg3_full_lock(tp, 0);
+ __tg3_set_rx_mode(dev);
+ tg3_full_unlock(tp);
+}
+
+static inline void tg3_set_mtu(struct net_device *dev, struct tg3 *tp,
+ int new_mtu)
+{
+ dev->mtu = new_mtu;
+
+ if (new_mtu > ETH_DATA_LEN) {
+ if (tg3_flag(tp, 5780_CLASS)) {
+ netdev_update_features(dev);
+ tg3_flag_clear(tp, TSO_CAPABLE);
+ } else {
+ tg3_flag_set(tp, JUMBO_RING_ENABLE);
+ }
+ } else {
+ if (tg3_flag(tp, 5780_CLASS)) {
+ tg3_flag_set(tp, TSO_CAPABLE);
+ netdev_update_features(dev);
+ }
+ tg3_flag_clear(tp, JUMBO_RING_ENABLE);
+ }
+}
+
+static int tg3_change_mtu(struct net_device *dev, int new_mtu)
+{
+ struct tg3 *tp = netdev_priv(dev);
+ int err, reset_phy = 0;
+
+ if (new_mtu < TG3_MIN_MTU || new_mtu > TG3_MAX_MTU(tp))
+ return -EINVAL;
+
+ if (!netif_running(dev)) {
+ /* We'll just catch it later when the
+ * device is up'd.
+ */
+ tg3_set_mtu(dev, tp, new_mtu);
+ return 0;
+ }
+
+ tg3_phy_stop(tp);
+
+ tg3_netif_stop(tp);
+
+ tg3_full_lock(tp, 1);
+
+ tg3_halt(tp, RESET_KIND_SHUTDOWN, 1);
+
+ tg3_set_mtu(dev, tp, new_mtu);
+
+ /* Reset PHY, otherwise the read DMA engine will be in a mode that
+ * breaks all requests to 256 bytes.
+ */
+ if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_57766)
+ reset_phy = 1;
+
+ err = tg3_restart_hw(tp, reset_phy);
+
+ if (!err)
+ tg3_netif_start(tp);
+
+ tg3_full_unlock(tp);
+
+ if (!err)
+ tg3_phy_start(tp);
+
+ return err;
+}
+
+static const struct net_device_ops tg3_netdev_ops = {
+ .ndo_open = tg3_open,
+ .ndo_stop = tg3_close,
+ .ndo_start_xmit = tg3_start_xmit,
+ .ndo_get_stats64 = tg3_get_stats64,
+ .ndo_validate_addr = eth_validate_addr,
+ .ndo_set_rx_mode = tg3_set_rx_mode,
+ .ndo_set_mac_address = tg3_set_mac_addr,
+ .ndo_do_ioctl = tg3_ioctl,
+ .ndo_tx_timeout = tg3_tx_timeout,
+ .ndo_change_mtu = tg3_change_mtu,
+ .ndo_fix_features = tg3_fix_features,
+ .ndo_set_features = tg3_set_features,
+#ifdef CONFIG_NET_POLL_CONTROLLER
+ .ndo_poll_controller = tg3_poll_controller,
+#endif
+};
+
static void __devinit tg3_get_eeprom_size(struct tg3 *tp)
{
u32 cursize, val, magic;
else
tg3_get_nvram_info(tp);
- if (tp->nvram_size == 0)
- tg3_get_nvram_size(tp);
-
- tg3_disable_nvram_access(tp);
- tg3_nvram_unlock(tp);
-
- } else {
- tg3_flag_clear(tp, NVRAM);
- tg3_flag_clear(tp, NVRAM_BUFFERED);
-
- tg3_get_eeprom_size(tp);
- }
-}
-
-static int tg3_nvram_write_block_using_eeprom(struct tg3 *tp,
- u32 offset, u32 len, u8 *buf)
-{
- int i, j, rc = 0;
- u32 val;
-
- for (i = 0; i < len; i += 4) {
- u32 addr;
- __be32 data;
-
- addr = offset + i;
-
- memcpy(&data, buf + i, 4);
-
- /*
- * The SEEPROM interface expects the data to always be opposite
- * the native endian format. We accomplish this by reversing
- * all the operations that would have been performed on the
- * data from a call to tg3_nvram_read_be32().
- */
- tw32(GRC_EEPROM_DATA, swab32(be32_to_cpu(data)));
-
- val = tr32(GRC_EEPROM_ADDR);
- tw32(GRC_EEPROM_ADDR, val | EEPROM_ADDR_COMPLETE);
-
- val &= ~(EEPROM_ADDR_ADDR_MASK | EEPROM_ADDR_DEVID_MASK |
- EEPROM_ADDR_READ);
- tw32(GRC_EEPROM_ADDR, val |
- (0 << EEPROM_ADDR_DEVID_SHIFT) |
- (addr & EEPROM_ADDR_ADDR_MASK) |
- EEPROM_ADDR_START |
- EEPROM_ADDR_WRITE);
-
- for (j = 0; j < 1000; j++) {
- val = tr32(GRC_EEPROM_ADDR);
-
- if (val & EEPROM_ADDR_COMPLETE)
- break;
- msleep(1);
- }
- if (!(val & EEPROM_ADDR_COMPLETE)) {
- rc = -EBUSY;
- break;
- }
- }
-
- return rc;
-}
-
-/* offset and length are dword aligned */
-static int tg3_nvram_write_block_unbuffered(struct tg3 *tp, u32 offset, u32 len,
- u8 *buf)
-{
- int ret = 0;
- u32 pagesize = tp->nvram_pagesize;
- u32 pagemask = pagesize - 1;
- u32 nvram_cmd;
- u8 *tmp;
-
- tmp = kmalloc(pagesize, GFP_KERNEL);
- if (tmp == NULL)
- return -ENOMEM;
-
- while (len) {
- int j;
- u32 phy_addr, page_off, size;
-
- phy_addr = offset & ~pagemask;
-
- for (j = 0; j < pagesize; j += 4) {
- ret = tg3_nvram_read_be32(tp, phy_addr + j,
- (__be32 *) (tmp + j));
- if (ret)
- break;
- }
- if (ret)
- break;
-
- page_off = offset & pagemask;
- size = pagesize;
- if (len < size)
- size = len;
-
- len -= size;
-
- memcpy(tmp + page_off, buf, size);
-
- offset = offset + (pagesize - page_off);
-
- tg3_enable_nvram_access(tp);
-
- /*
- * Before we can erase the flash page, we need
- * to issue a special "write enable" command.
- */
- nvram_cmd = NVRAM_CMD_WREN | NVRAM_CMD_GO | NVRAM_CMD_DONE;
-
- if (tg3_nvram_exec_cmd(tp, nvram_cmd))
- break;
-
- /* Erase the target page */
- tw32(NVRAM_ADDR, phy_addr);
-
- nvram_cmd = NVRAM_CMD_GO | NVRAM_CMD_DONE | NVRAM_CMD_WR |
- NVRAM_CMD_FIRST | NVRAM_CMD_LAST | NVRAM_CMD_ERASE;
-
- if (tg3_nvram_exec_cmd(tp, nvram_cmd))
- break;
-
- /* Issue another write enable to start the write. */
- nvram_cmd = NVRAM_CMD_WREN | NVRAM_CMD_GO | NVRAM_CMD_DONE;
-
- if (tg3_nvram_exec_cmd(tp, nvram_cmd))
- break;
-
- for (j = 0; j < pagesize; j += 4) {
- __be32 data;
-
- data = *((__be32 *) (tmp + j));
-
- tw32(NVRAM_WRDATA, be32_to_cpu(data));
-
- tw32(NVRAM_ADDR, phy_addr + j);
-
- nvram_cmd = NVRAM_CMD_GO | NVRAM_CMD_DONE |
- NVRAM_CMD_WR;
-
- if (j == 0)
- nvram_cmd |= NVRAM_CMD_FIRST;
- else if (j == (pagesize - 4))
- nvram_cmd |= NVRAM_CMD_LAST;
-
- if ((ret = tg3_nvram_exec_cmd(tp, nvram_cmd)))
- break;
- }
- if (ret)
- break;
- }
-
- nvram_cmd = NVRAM_CMD_WRDI | NVRAM_CMD_GO | NVRAM_CMD_DONE;
- tg3_nvram_exec_cmd(tp, nvram_cmd);
-
- kfree(tmp);
-
- return ret;
-}
-
-/* offset and length are dword aligned */
-static int tg3_nvram_write_block_buffered(struct tg3 *tp, u32 offset, u32 len,
- u8 *buf)
-{
- int i, ret = 0;
-
- for (i = 0; i < len; i += 4, offset += 4) {
- u32 page_off, phy_addr, nvram_cmd;
- __be32 data;
-
- memcpy(&data, buf + i, 4);
- tw32(NVRAM_WRDATA, be32_to_cpu(data));
-
- page_off = offset % tp->nvram_pagesize;
-
- phy_addr = tg3_nvram_phys_addr(tp, offset);
-
- tw32(NVRAM_ADDR, phy_addr);
-
- nvram_cmd = NVRAM_CMD_GO | NVRAM_CMD_DONE | NVRAM_CMD_WR;
-
- if (page_off == 0 || i == 0)
- nvram_cmd |= NVRAM_CMD_FIRST;
- if (page_off == (tp->nvram_pagesize - 4))
- nvram_cmd |= NVRAM_CMD_LAST;
-
- if (i == (len - 4))
- nvram_cmd |= NVRAM_CMD_LAST;
-
- if (GET_ASIC_REV(tp->pci_chip_rev_id) != ASIC_REV_5752 &&
- !tg3_flag(tp, 5755_PLUS) &&
- (tp->nvram_jedecnum == JEDEC_ST) &&
- (nvram_cmd & NVRAM_CMD_FIRST)) {
-
- if ((ret = tg3_nvram_exec_cmd(tp,
- NVRAM_CMD_WREN | NVRAM_CMD_GO |
- NVRAM_CMD_DONE)))
-
- break;
- }
- if (!tg3_flag(tp, FLASH)) {
- /* We always do complete word writes to eeprom. */
- nvram_cmd |= (NVRAM_CMD_FIRST | NVRAM_CMD_LAST);
- }
-
- if ((ret = tg3_nvram_exec_cmd(tp, nvram_cmd)))
- break;
- }
- return ret;
-}
-
-/* offset and length are dword aligned */
-static int tg3_nvram_write_block(struct tg3 *tp, u32 offset, u32 len, u8 *buf)
-{
- int ret;
-
- if (tg3_flag(tp, EEPROM_WRITE_PROT)) {
- tw32_f(GRC_LOCAL_CTRL, tp->grc_local_ctrl &
- ~GRC_LCLCTRL_GPIO_OUTPUT1);
- udelay(40);
- }
-
- if (!tg3_flag(tp, NVRAM)) {
- ret = tg3_nvram_write_block_using_eeprom(tp, offset, len, buf);
- } else {
- u32 grc_mode;
-
- ret = tg3_nvram_lock(tp);
- if (ret)
- return ret;
-
- tg3_enable_nvram_access(tp);
- if (tg3_flag(tp, 5750_PLUS) && !tg3_flag(tp, PROTECTED_NVRAM))
- tw32(NVRAM_WRITE1, 0x406);
-
- grc_mode = tr32(GRC_MODE);
- tw32(GRC_MODE, grc_mode | GRC_MODE_NVRAM_WR_ENABLE);
-
- if (tg3_flag(tp, NVRAM_BUFFERED) || !tg3_flag(tp, FLASH)) {
- ret = tg3_nvram_write_block_buffered(tp, offset, len,
- buf);
- } else {
- ret = tg3_nvram_write_block_unbuffered(tp, offset, len,
- buf);
- }
-
- grc_mode = tr32(GRC_MODE);
- tw32(GRC_MODE, grc_mode & ~GRC_MODE_NVRAM_WR_ENABLE);
+ if (tp->nvram_size == 0)
+ tg3_get_nvram_size(tp);
tg3_disable_nvram_access(tp);
tg3_nvram_unlock(tp);
- }
- if (tg3_flag(tp, EEPROM_WRITE_PROT)) {
- tw32_f(GRC_LOCAL_CTRL, tp->grc_local_ctrl);
- udelay(40);
- }
+ } else {
+ tg3_flag_clear(tp, NVRAM);
+ tg3_flag_clear(tp, NVRAM_BUFFERED);
- return ret;
+ tg3_get_eeprom_size(tp);
+ }
}
struct subsys_tbl_ent {
adv |= ADVERTISED_FIBRE;
tp->link_config.advertising = adv;
- tp->link_config.speed = SPEED_INVALID;
- tp->link_config.duplex = DUPLEX_INVALID;
+ tp->link_config.speed = SPEED_UNKNOWN;
+ tp->link_config.duplex = DUPLEX_UNKNOWN;
tp->link_config.autoneg = AUTONEG_ENABLE;
- tp->link_config.active_speed = SPEED_INVALID;
- tp->link_config.active_duplex = DUPLEX_INVALID;
- tp->link_config.orig_speed = SPEED_INVALID;
- tp->link_config.orig_duplex = DUPLEX_INVALID;
- tp->link_config.orig_autoneg = AUTONEG_INVALID;
+ tp->link_config.active_speed = SPEED_UNKNOWN;
+ tp->link_config.active_duplex = DUPLEX_UNKNOWN;
+
+ tp->old_link = -1;
}
static int __devinit tg3_phy_probe(struct tg3 *tp)
tp->fw_ver[TG3_VER_SIZE - 1] = 0;
}
-static struct pci_dev * __devinit tg3_find_peer(struct tg3 *);
-
static inline u32 tg3_rx_ret_ring_size(struct tg3 *tp)
{
if (tg3_flag(tp, LRG_PROD_RING_CAP))
{ },
};
-static int __devinit tg3_get_invariants(struct tg3 *tp)
+static struct pci_dev * __devinit tg3_find_peer(struct tg3 *tp)
{
- u32 misc_ctrl_reg;
- u32 pci_state_reg, grc_misc_cfg;
- u32 val;
- u16 pci_cmd;
- int err;
+ struct pci_dev *peer;
+ unsigned int func, devnr = tp->pdev->devfn & ~7;
- /* Force memory write invalidate off. If we leave it on,
- * then on 5700_BX chips we have to enable a workaround.
- * The workaround is to set the TG3PCI_DMA_RW_CTRL boundary
- * to match the cacheline size. The Broadcom driver have this
- * workaround but turns MWI off all the times so never uses
- * it. This seems to suggest that the workaround is insufficient.
+ for (func = 0; func < 8; func++) {
+ peer = pci_get_slot(tp->pdev->bus, devnr | func);
+ if (peer && peer != tp->pdev)
+ break;
+ pci_dev_put(peer);
+ }
+ /* 5704 can be configured in single-port mode, set peer to
+ * tp->pdev in that case.
*/
- pci_read_config_word(tp->pdev, PCI_COMMAND, &pci_cmd);
- pci_cmd &= ~PCI_COMMAND_INVALIDATE;
- pci_write_config_word(tp->pdev, PCI_COMMAND, pci_cmd);
+ if (!peer) {
+ peer = tp->pdev;
+ return peer;
+ }
- /* Important! -- Make sure register accesses are byteswapped
- * correctly. Also, for those chips that require it, make
- * sure that indirect register accesses are enabled before
- * the first operation.
+ /*
+ * We don't need to keep the refcount elevated; there's no way
+ * to remove one half of this device without removing the other
*/
- pci_read_config_dword(tp->pdev, TG3PCI_MISC_HOST_CTRL,
- &misc_ctrl_reg);
- tp->misc_host_ctrl |= (misc_ctrl_reg &
- MISC_HOST_CTRL_CHIPREV);
- pci_write_config_dword(tp->pdev, TG3PCI_MISC_HOST_CTRL,
- tp->misc_host_ctrl);
+ pci_dev_put(peer);
+
+ return peer;
+}
- tp->pci_chip_rev_id = (misc_ctrl_reg >>
- MISC_HOST_CTRL_CHIPREV_SHIFT);
+static void __devinit tg3_detect_asic_rev(struct tg3 *tp, u32 misc_ctrl_reg)
+{
+ tp->pci_chip_rev_id = misc_ctrl_reg >> MISC_HOST_CTRL_CHIPREV_SHIFT;
if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_USE_PROD_ID_REG) {
- u32 prod_id_asic_rev;
+ u32 reg;
+
+ /* All devices that use the alternate
+ * ASIC REV location have a CPMU.
+ */
+ tg3_flag_set(tp, CPMU_PRESENT);
if (tp->pdev->device == TG3PCI_DEVICE_TIGON3_5717 ||
tp->pdev->device == TG3PCI_DEVICE_TIGON3_5718 ||
tp->pdev->device == TG3PCI_DEVICE_TIGON3_5719 ||
tp->pdev->device == TG3PCI_DEVICE_TIGON3_5720)
- pci_read_config_dword(tp->pdev,
- TG3PCI_GEN2_PRODID_ASICREV,
- &prod_id_asic_rev);
+ reg = TG3PCI_GEN2_PRODID_ASICREV;
else if (tp->pdev->device == TG3PCI_DEVICE_TIGON3_57781 ||
tp->pdev->device == TG3PCI_DEVICE_TIGON3_57785 ||
tp->pdev->device == TG3PCI_DEVICE_TIGON3_57761 ||
tp->pdev->device == TG3PCI_DEVICE_TIGON3_57766 ||
tp->pdev->device == TG3PCI_DEVICE_TIGON3_57782 ||
tp->pdev->device == TG3PCI_DEVICE_TIGON3_57786)
- pci_read_config_dword(tp->pdev,
- TG3PCI_GEN15_PRODID_ASICREV,
- &prod_id_asic_rev);
+ reg = TG3PCI_GEN15_PRODID_ASICREV;
else
- pci_read_config_dword(tp->pdev, TG3PCI_PRODID_ASICREV,
- &prod_id_asic_rev);
+ reg = TG3PCI_PRODID_ASICREV;
- tp->pci_chip_rev_id = prod_id_asic_rev;
+ pci_read_config_dword(tp->pdev, reg, &tp->pci_chip_rev_id);
}
/* Wrong chip ID in 5752 A0. This code can be removed later
if (tp->pci_chip_rev_id == CHIPREV_ID_5752_A0_HW)
tp->pci_chip_rev_id = CHIPREV_ID_5752_A0;
+ if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5717 ||
+ GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5719 ||
+ GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5720)
+ tg3_flag_set(tp, 5717_PLUS);
+
+ if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_57765 ||
+ GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_57766)
+ tg3_flag_set(tp, 57765_CLASS);
+
+ if (tg3_flag(tp, 57765_CLASS) || tg3_flag(tp, 5717_PLUS))
+ tg3_flag_set(tp, 57765_PLUS);
+
+ /* Intentionally exclude ASIC_REV_5906 */
+ if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5755 ||
+ GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5787 ||
+ GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5784 ||
+ GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5761 ||
+ GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5785 ||
+ GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_57780 ||
+ tg3_flag(tp, 57765_PLUS))
+ tg3_flag_set(tp, 5755_PLUS);
+
+ if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5780 ||
+ GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5714)
+ tg3_flag_set(tp, 5780_CLASS);
+
+ if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5750 ||
+ GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5752 ||
+ GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5906 ||
+ tg3_flag(tp, 5755_PLUS) ||
+ tg3_flag(tp, 5780_CLASS))
+ tg3_flag_set(tp, 5750_PLUS);
+
+ if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5705 ||
+ tg3_flag(tp, 5750_PLUS))
+ tg3_flag_set(tp, 5705_PLUS);
+}
+
+static int __devinit tg3_get_invariants(struct tg3 *tp)
+{
+ u32 misc_ctrl_reg;
+ u32 pci_state_reg, grc_misc_cfg;
+ u32 val;
+ u16 pci_cmd;
+ int err;
+
+ /* Force memory write invalidate off. If we leave it on,
+ * then on 5700_BX chips we have to enable a workaround.
+ * The workaround is to set the TG3PCI_DMA_RW_CTRL boundary
+ * to match the cacheline size. The Broadcom driver have this
+ * workaround but turns MWI off all the times so never uses
+ * it. This seems to suggest that the workaround is insufficient.
+ */
+ pci_read_config_word(tp->pdev, PCI_COMMAND, &pci_cmd);
+ pci_cmd &= ~PCI_COMMAND_INVALIDATE;
+ pci_write_config_word(tp->pdev, PCI_COMMAND, pci_cmd);
+
+ /* Important! -- Make sure register accesses are byteswapped
+ * correctly. Also, for those chips that require it, make
+ * sure that indirect register accesses are enabled before
+ * the first operation.
+ */
+ pci_read_config_dword(tp->pdev, TG3PCI_MISC_HOST_CTRL,
+ &misc_ctrl_reg);
+ tp->misc_host_ctrl |= (misc_ctrl_reg &
+ MISC_HOST_CTRL_CHIPREV);
+ pci_write_config_dword(tp->pdev, TG3PCI_MISC_HOST_CTRL,
+ tp->misc_host_ctrl);
+
+ tg3_detect_asic_rev(tp, misc_ctrl_reg);
+
/* If we have 5702/03 A1 or A2 on certain ICH chipsets,
* we need to disable memory and use config. cycles
* only to access all registers. The 5702/03 chips
* Any tg3 device found behind the bridge will also need the 40-bit
* DMA workaround.
*/
- if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5780 ||
- GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5714) {
- tg3_flag_set(tp, 5780_CLASS);
+ if (tg3_flag(tp, 5780_CLASS)) {
tg3_flag_set(tp, 40BIT_DMA_BUG);
tp->msi_cap = pci_find_capability(tp->pdev, PCI_CAP_ID_MSI);
} else {
GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5714)
tp->pdev_peer = tg3_find_peer(tp);
- if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5717 ||
- GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5719 ||
- GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5720)
- tg3_flag_set(tp, 5717_PLUS);
-
- if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_57765 ||
- GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_57766)
- tg3_flag_set(tp, 57765_CLASS);
-
- if (tg3_flag(tp, 57765_CLASS) || tg3_flag(tp, 5717_PLUS))
- tg3_flag_set(tp, 57765_PLUS);
-
- /* Intentionally exclude ASIC_REV_5906 */
- if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5755 ||
- GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5787 ||
- GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5784 ||
- GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5761 ||
- GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5785 ||
- GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_57780 ||
- tg3_flag(tp, 57765_PLUS))
- tg3_flag_set(tp, 5755_PLUS);
-
- if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5750 ||
- GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5752 ||
- GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5906 ||
- tg3_flag(tp, 5755_PLUS) ||
- tg3_flag(tp, 5780_CLASS))
- tg3_flag_set(tp, 5750_PLUS);
-
- if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5705 ||
- tg3_flag(tp, 5750_PLUS))
- tg3_flag_set(tp, 5705_PLUS);
-
/* Determine TSO capabilities */
if (tp->pci_chip_rev_id == CHIPREV_ID_5719_A0)
; /* Do nothing. HW bug. */
if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5719)
tp->dma_limit = TG3_TX_BD_DMA_MAX_4K;
- else if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_57766)
- tp->dma_limit = TG3_TX_BD_DMA_MAX_2K;
if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5717 ||
GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5719 ||
tg3_flag_set(tp, PCI_EXPRESS);
- if (tp->pci_chip_rev_id == CHIPREV_ID_5719_A0) {
- int readrq = pcie_get_readrq(tp->pdev);
- if (readrq > 2048)
- pcie_set_readrq(tp->pdev, 2048);
- }
-
pci_read_config_word(tp->pdev,
pci_pcie_cap(tp->pdev) + PCI_EXP_LNKCTL,
&lnkctl);
tg3_ape_lock_init(tp);
}
- if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5784 ||
- GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5761 ||
- GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5785 ||
- GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_57780 ||
- tg3_flag(tp, 57765_PLUS))
- tg3_flag_set(tp, CPMU_PRESENT);
-
/* Set up tp->grc_local_ctrl before calling
* tg3_pwrsrc_switch_to_vmain(). GPIO1 driven high
* will bring 5700's external PHY out of reset.
return str;
}
-static struct pci_dev * __devinit tg3_find_peer(struct tg3 *tp)
-{
- struct pci_dev *peer;
- unsigned int func, devnr = tp->pdev->devfn & ~7;
-
- for (func = 0; func < 8; func++) {
- peer = pci_get_slot(tp->pdev->bus, devnr | func);
- if (peer && peer != tp->pdev)
- break;
- pci_dev_put(peer);
- }
- /* 5704 can be configured in single-port mode, set peer to
- * tp->pdev in that case.
- */
- if (!peer) {
- peer = tp->pdev;
- return peer;
- }
-
- /*
- * We don't need to keep the refcount elevated; there's no way
- * to remove one half of this device without removing the other
- */
- pci_dev_put(peer);
-
- return peer;
-}
-
static void __devinit tg3_init_coal(struct tg3 *tp)
{
struct ethtool_coalesce *ec = &tp->coal;
}
}
-static struct rtnl_link_stats64 *tg3_get_stats64(struct net_device *dev,
- struct rtnl_link_stats64 *stats)
-{
- struct tg3 *tp = netdev_priv(dev);
-
- if (!tp->hw_stats)
- return &tp->net_stats_prev;
-
- spin_lock_bh(&tp->lock);
- tg3_get_nstats(tp, stats);
- spin_unlock_bh(&tp->lock);
-
- return stats;
-}
-
-static const struct net_device_ops tg3_netdev_ops = {
- .ndo_open = tg3_open,
- .ndo_stop = tg3_close,
- .ndo_start_xmit = tg3_start_xmit,
- .ndo_get_stats64 = tg3_get_stats64,
- .ndo_validate_addr = eth_validate_addr,
- .ndo_set_rx_mode = tg3_set_rx_mode,
- .ndo_set_mac_address = tg3_set_mac_addr,
- .ndo_do_ioctl = tg3_ioctl,
- .ndo_tx_timeout = tg3_tx_timeout,
- .ndo_change_mtu = tg3_change_mtu,
- .ndo_fix_features = tg3_fix_features,
- .ndo_set_features = tg3_set_features,
-#ifdef CONFIG_NET_POLL_CONTROLLER
- .ndo_poll_controller = tg3_poll_controller,
-#endif
-};
-
static int __devinit tg3_init_one(struct pci_dev *pdev,
const struct pci_device_id *ent)
{
dev = alloc_etherdev_mq(sizeof(*tp), TG3_IRQ_MAX_VECS);
if (!dev) {
- dev_err(&pdev->dev, "Etherdev alloc failed, aborting\n");
err = -ENOMEM;
goto err_out_power_down;
}
tg3_frob_aux_power(tp, false);
}
+ tg3_timer_init(tp);
+
err = register_netdev(dev);
if (err) {
dev_err(&pdev->dev, "Cannot register net device, aborting\n");
tg3_phy_stop(tp);
tg3_netif_stop(tp);
- del_timer_sync(&tp->timer);
+ tg3_timer_stop(tp);
tg3_full_lock(tp, 1);
tg3_disable_ints(tp);
if (err2)
goto out;
- tp->timer.expires = jiffies + tp->timer_offset;
- add_timer(&tp->timer);
+ tg3_timer_start(tp);
netif_device_attach(dev);
tg3_netif_start(tp);
if (err)
goto out;
- tp->timer.expires = jiffies + tp->timer_offset;
- add_timer(&tp->timer);
+ tg3_timer_start(tp);
tg3_netif_start(tp);
tg3_netif_stop(tp);
- del_timer_sync(&tp->timer);
+ tg3_timer_stop(tp);
/* Want to make sure that the reset task doesn't run */
tg3_reset_task_cancel(tp);
- tg3_flag_clear(tp, TX_RECOVERY_PENDING);
netif_device_detach(netdev);
netif_device_attach(netdev);
- tp->timer.expires = jiffies + tp->timer_offset;
- add_timer(&tp->timer);
+ tg3_timer_start(tp);
tg3_netif_start(tp);
__le16 tx_underun;
};
+enum rtl_flag {
+ RTL_FLAG_TASK_ENABLED,
+ RTL_FLAG_TASK_SLOW_PENDING,
+ RTL_FLAG_TASK_RESET_PENDING,
+ RTL_FLAG_TASK_PHY_PENDING,
+ RTL_FLAG_MAX
+};
+
+struct rtl8169_stats {
+ u64 packets;
+ u64 bytes;
+ struct u64_stats_sync syncp;
+};
+
struct rtl8169_private {
void __iomem *mmio_addr; /* memory map physical address */
struct pci_dev *pci_dev;
struct net_device *dev;
struct napi_struct napi;
- spinlock_t lock;
u32 msg_enable;
u16 txd_version;
u16 mac_version;
u32 cur_tx; /* Index into the Tx descriptor buffer of next Rx pkt. */
u32 dirty_rx;
u32 dirty_tx;
+ struct rtl8169_stats rx_stats;
+ struct rtl8169_stats tx_stats;
struct TxDesc *TxDescArray; /* 256-aligned Tx descriptor ring */
struct RxDesc *RxDescArray; /* 256-aligned Rx descriptor ring */
dma_addr_t TxPhyAddr;
struct ring_info tx_skb[NUM_TX_DESC]; /* Tx data buffers */
struct timer_list timer;
u16 cp_cmd;
- u16 intr_event;
- u16 napi_event;
- u16 intr_mask;
+
+ u16 event_slow;
struct mdio_ops {
void (*write)(void __iomem *, int, int);
unsigned int (*phy_reset_pending)(struct rtl8169_private *tp);
unsigned int (*link_ok)(void __iomem *);
int (*do_ioctl)(struct rtl8169_private *tp, struct mii_ioctl_data *data, int cmd);
- struct delayed_work task;
+
+ struct {
+ DECLARE_BITMAP(flags, RTL_FLAG_MAX);
+ struct mutex mutex;
+ struct work_struct work;
+ } wk;
+
unsigned features;
struct mii_if_info mii;
static int rtl8169_close(struct net_device *dev);
static void rtl_set_rx_mode(struct net_device *dev);
static void rtl8169_tx_timeout(struct net_device *dev);
-static struct net_device_stats *rtl8169_get_stats(struct net_device *dev);
-static int rtl8169_rx_interrupt(struct net_device *, struct rtl8169_private *,
- void __iomem *, u32 budget);
+static struct rtnl_link_stats64 *rtl8169_get_stats64(struct net_device *dev,
+ struct rtnl_link_stats64
+ *stats);
static int rtl8169_change_mtu(struct net_device *dev, int new_mtu);
static void rtl8169_rx_clear(struct rtl8169_private *tp);
static int rtl8169_poll(struct napi_struct *napi, int budget);
+static void rtl_lock_work(struct rtl8169_private *tp)
+{
+ mutex_lock(&tp->wk.mutex);
+}
+
+static void rtl_unlock_work(struct rtl8169_private *tp)
+{
+ mutex_unlock(&tp->wk.mutex);
+}
+
static void rtl_tx_performance_tweak(struct pci_dev *pdev, u16 force)
{
int cap = pci_pcie_cap(pdev);
return value;
}
+static u16 rtl_get_events(struct rtl8169_private *tp)
+{
+ void __iomem *ioaddr = tp->mmio_addr;
+
+ return RTL_R16(IntrStatus);
+}
+
+static void rtl_ack_events(struct rtl8169_private *tp, u16 bits)
+{
+ void __iomem *ioaddr = tp->mmio_addr;
+
+ RTL_W16(IntrStatus, bits);
+ mmiowb();
+}
+
+static void rtl_irq_disable(struct rtl8169_private *tp)
+{
+ void __iomem *ioaddr = tp->mmio_addr;
+
+ RTL_W16(IntrMask, 0);
+ mmiowb();
+}
+
+static void rtl_irq_enable(struct rtl8169_private *tp, u16 bits)
+{
+ void __iomem *ioaddr = tp->mmio_addr;
+
+ RTL_W16(IntrMask, bits);
+}
+
+#define RTL_EVENT_NAPI_RX (RxOK | RxErr)
+#define RTL_EVENT_NAPI_TX (TxOK | TxErr)
+#define RTL_EVENT_NAPI (RTL_EVENT_NAPI_RX | RTL_EVENT_NAPI_TX)
+
+static void rtl_irq_enable_all(struct rtl8169_private *tp)
+{
+ rtl_irq_enable(tp, RTL_EVENT_NAPI | tp->event_slow);
+}
+
static void rtl8169_irq_mask_and_ack(struct rtl8169_private *tp)
{
void __iomem *ioaddr = tp->mmio_addr;
- RTL_W16(IntrMask, 0x0000);
- RTL_W16(IntrStatus, tp->intr_event);
+ rtl_irq_disable(tp);
+ rtl_ack_events(tp, RTL_EVENT_NAPI | tp->event_slow);
RTL_R8(ChipCmd);
}
struct rtl8169_private *tp,
void __iomem *ioaddr, bool pm)
{
- unsigned long flags;
-
- spin_lock_irqsave(&tp->lock, flags);
if (tp->link_ok(ioaddr)) {
rtl_link_chg_patch(tp);
/* This is to cancel a scheduled suspend if there's one. */
if (pm)
pm_schedule_suspend(&tp->pci_dev->dev, 5000);
}
- spin_unlock_irqrestore(&tp->lock, flags);
}
static void rtl8169_check_link_status(struct net_device *dev,
{
struct rtl8169_private *tp = netdev_priv(dev);
- spin_lock_irq(&tp->lock);
+ rtl_lock_work(tp);
wol->supported = WAKE_ANY;
wol->wolopts = __rtl8169_get_wol(tp);
- spin_unlock_irq(&tp->lock);
+ rtl_unlock_work(tp);
}
static void __rtl8169_set_wol(struct rtl8169_private *tp, u32 wolopts)
{
struct rtl8169_private *tp = netdev_priv(dev);
- spin_lock_irq(&tp->lock);
+ rtl_lock_work(tp);
if (wol->wolopts)
tp->features |= RTL_FEATURE_WOL;
else
tp->features &= ~RTL_FEATURE_WOL;
__rtl8169_set_wol(tp, wol->wolopts);
- spin_unlock_irq(&tp->lock);
+
+ rtl_unlock_work(tp);
device_set_wakeup_enable(&tp->pci_dev->dev, wol->wolopts);
static int rtl8169_set_settings(struct net_device *dev, struct ethtool_cmd *cmd)
{
struct rtl8169_private *tp = netdev_priv(dev);
- unsigned long flags;
int ret;
del_timer_sync(&tp->timer);
- spin_lock_irqsave(&tp->lock, flags);
+ rtl_lock_work(tp);
ret = rtl8169_set_speed(dev, cmd->autoneg, ethtool_cmd_speed(cmd),
cmd->duplex, cmd->advertising);
- spin_unlock_irqrestore(&tp->lock, flags);
+ rtl_unlock_work(tp);
return ret;
}
return features;
}
-static int rtl8169_set_features(struct net_device *dev,
- netdev_features_t features)
+static void __rtl8169_set_features(struct net_device *dev,
+ netdev_features_t features)
{
struct rtl8169_private *tp = netdev_priv(dev);
+ netdev_features_t changed = features ^ dev->features;
void __iomem *ioaddr = tp->mmio_addr;
- unsigned long flags;
- spin_lock_irqsave(&tp->lock, flags);
+ if (!(changed & (NETIF_F_RXALL | NETIF_F_RXCSUM | NETIF_F_HW_VLAN_RX)))
+ return;
+
+ if (changed & (NETIF_F_RXCSUM | NETIF_F_HW_VLAN_RX)) {
+ if (features & NETIF_F_RXCSUM)
+ tp->cp_cmd |= RxChkSum;
+ else
+ tp->cp_cmd &= ~RxChkSum;
- if (features & NETIF_F_RXCSUM)
- tp->cp_cmd |= RxChkSum;
- else
- tp->cp_cmd &= ~RxChkSum;
+ if (dev->features & NETIF_F_HW_VLAN_RX)
+ tp->cp_cmd |= RxVlan;
+ else
+ tp->cp_cmd &= ~RxVlan;
- if (dev->features & NETIF_F_HW_VLAN_RX)
- tp->cp_cmd |= RxVlan;
- else
- tp->cp_cmd &= ~RxVlan;
+ RTL_W16(CPlusCmd, tp->cp_cmd);
+ RTL_R16(CPlusCmd);
+ }
+ if (changed & NETIF_F_RXALL) {
+ int tmp = (RTL_R32(RxConfig) & ~(AcceptErr | AcceptRunt));
+ if (features & NETIF_F_RXALL)
+ tmp |= (AcceptErr | AcceptRunt);
+ RTL_W32(RxConfig, tmp);
+ }
+}
- RTL_W16(CPlusCmd, tp->cp_cmd);
- RTL_R16(CPlusCmd);
+static int rtl8169_set_features(struct net_device *dev,
+ netdev_features_t features)
+{
+ struct rtl8169_private *tp = netdev_priv(dev);
- spin_unlock_irqrestore(&tp->lock, flags);
+ rtl_lock_work(tp);
+ __rtl8169_set_features(dev, features);
+ rtl_unlock_work(tp);
return 0;
}
+
static inline u32 rtl8169_tx_vlan_tag(struct rtl8169_private *tp,
struct sk_buff *skb)
{
static int rtl8169_get_settings(struct net_device *dev, struct ethtool_cmd *cmd)
{
struct rtl8169_private *tp = netdev_priv(dev);
- unsigned long flags;
int rc;
- spin_lock_irqsave(&tp->lock, flags);
-
+ rtl_lock_work(tp);
rc = tp->get_settings(dev, cmd);
+ rtl_unlock_work(tp);
- spin_unlock_irqrestore(&tp->lock, flags);
return rc;
}
void *p)
{
struct rtl8169_private *tp = netdev_priv(dev);
- unsigned long flags;
if (regs->len > R8169_REGS_SIZE)
regs->len = R8169_REGS_SIZE;
- spin_lock_irqsave(&tp->lock, flags);
+ rtl_lock_work(tp);
memcpy_fromio(p, tp->mmio_addr, regs->len);
- spin_unlock_irqrestore(&tp->lock, flags);
+ rtl_unlock_work(tp);
}
static u32 rtl8169_get_msglevel(struct net_device *dev)
}
}
-static void rtl8169_phy_timer(unsigned long __opaque)
+static void rtl_phy_work(struct rtl8169_private *tp)
{
- struct net_device *dev = (struct net_device *)__opaque;
- struct rtl8169_private *tp = netdev_priv(dev);
struct timer_list *timer = &tp->timer;
void __iomem *ioaddr = tp->mmio_addr;
unsigned long timeout = RTL8169_PHY_TIMEOUT;
assert(tp->mac_version > RTL_GIGA_MAC_VER_01);
- spin_lock_irq(&tp->lock);
-
if (tp->phy_reset_pending(tp)) {
/*
* A busy loop could burn quite a few cycles on nowadays CPU.
}
if (tp->link_ok(ioaddr))
- goto out_unlock;
+ return;
- netif_warn(tp, link, dev, "PHY reset until link up\n");
+ netif_warn(tp, link, tp->dev, "PHY reset until link up\n");
tp->phy_reset_enable(tp);
out_mod_timer:
mod_timer(timer, jiffies + timeout);
-out_unlock:
- spin_unlock_irq(&tp->lock);
+}
+
+static void rtl_schedule_task(struct rtl8169_private *tp, enum rtl_flag flag)
+{
+ if (!test_and_set_bit(flag, tp->wk.flags))
+ schedule_work(&tp->wk.work);
+}
+
+static void rtl8169_phy_timer(unsigned long __opaque)
+{
+ struct net_device *dev = (struct net_device *)__opaque;
+ struct rtl8169_private *tp = netdev_priv(dev);
+
+ rtl_schedule_task(tp, RTL_FLAG_TASK_PHY_PENDING);
}
#ifdef CONFIG_NET_POLL_CONTROLLER
static void rtl8169_netpoll(struct net_device *dev)
{
struct rtl8169_private *tp = netdev_priv(dev);
- struct pci_dev *pdev = tp->pci_dev;
- disable_irq(pdev->irq);
- rtl8169_interrupt(pdev->irq, dev);
- enable_irq(pdev->irq);
+ rtl8169_interrupt(tp->pci_dev->irq, dev);
}
#endif
low = addr[0] | (addr[1] << 8) | (addr[2] << 16) | (addr[3] << 24);
high = addr[4] | (addr[5] << 8);
- spin_lock_irq(&tp->lock);
+ rtl_lock_work(tp);
RTL_W8(Cfg9346, Cfg9346_Unlock);
RTL_W8(Cfg9346, Cfg9346_Lock);
- spin_unlock_irq(&tp->lock);
+ rtl_unlock_work(tp);
}
static int rtl_set_mac_address(struct net_device *dev, void *p)
void (*hw_start)(struct net_device *);
unsigned int region;
unsigned int align;
- u16 intr_event;
- u16 napi_event;
+ u16 event_slow;
unsigned features;
u8 default_ver;
} rtl_cfg_infos [] = {
.hw_start = rtl_hw_start_8169,
.region = 1,
.align = 0,
- .intr_event = SYSErr | LinkChg | RxOverflow |
- RxFIFOOver | TxErr | TxOK | RxOK | RxErr,
- .napi_event = RxFIFOOver | TxErr | TxOK | RxOK | RxOverflow,
+ .event_slow = SYSErr | LinkChg | RxOverflow | RxFIFOOver,
.features = RTL_FEATURE_GMII,
.default_ver = RTL_GIGA_MAC_VER_01,
},
.hw_start = rtl_hw_start_8168,
.region = 2,
.align = 8,
- .intr_event = SYSErr | LinkChg | RxOverflow |
- TxErr | TxOK | RxOK | RxErr,
- .napi_event = TxErr | TxOK | RxOK | RxOverflow,
+ .event_slow = SYSErr | LinkChg | RxOverflow,
.features = RTL_FEATURE_GMII | RTL_FEATURE_MSI,
.default_ver = RTL_GIGA_MAC_VER_11,
},
.hw_start = rtl_hw_start_8101,
.region = 2,
.align = 8,
- .intr_event = SYSErr | LinkChg | RxOverflow | PCSTimeout |
- RxFIFOOver | TxErr | TxOK | RxOK | RxErr,
- .napi_event = RxFIFOOver | TxErr | TxOK | RxOK | RxOverflow,
+ .event_slow = SYSErr | LinkChg | RxOverflow | RxFIFOOver |
+ PCSTimeout,
.features = RTL_FEATURE_MSI,
.default_ver = RTL_GIGA_MAC_VER_13,
}
static const struct net_device_ops rtl8169_netdev_ops = {
.ndo_open = rtl8169_open,
.ndo_stop = rtl8169_close,
- .ndo_get_stats = rtl8169_get_stats,
+ .ndo_get_stats64 = rtl8169_get_stats64,
.ndo_start_xmit = rtl8169_start_xmit,
.ndo_tx_timeout = rtl8169_tx_timeout,
.ndo_validate_addr = eth_validate_addr,
static void r8168e_hw_jumbo_enable(struct rtl8169_private *tp)
{
void __iomem *ioaddr = tp->mmio_addr;
- struct pci_dev *pdev = tp->pci_dev;
RTL_W8(MaxTxPacketSize, 0x3f);
RTL_W8(Config3, RTL_R8(Config3) | Jumbo_En0);
RTL_W8(Config4, RTL_R8(Config4) | 0x01);
- pci_write_config_byte(pdev, 0x79, 0x20);
+ rtl_tx_performance_tweak(tp->pci_dev, 0x2 << MAX_READ_REQUEST_SHIFT);
}
static void r8168e_hw_jumbo_disable(struct rtl8169_private *tp)
{
void __iomem *ioaddr = tp->mmio_addr;
- struct pci_dev *pdev = tp->pci_dev;
RTL_W8(MaxTxPacketSize, 0x0c);
RTL_W8(Config3, RTL_R8(Config3) & ~Jumbo_En0);
RTL_W8(Config4, RTL_R8(Config4) & ~0x01);
- pci_write_config_byte(pdev, 0x79, 0x50);
+ rtl_tx_performance_tweak(tp->pci_dev, 0x5 << MAX_READ_REQUEST_SHIFT);
}
static void r8168b_0_hw_jumbo_enable(struct rtl8169_private *tp)
dev = alloc_etherdev(sizeof (*tp));
if (!dev) {
- if (netif_msg_drv(&debug))
- dev_err(&pdev->dev, "unable to alloc new ethernet\n");
rc = -ENOMEM;
goto out;
}
rtl_init_rxcfg(tp);
- RTL_W16(IntrMask, 0x0000);
+ rtl_irq_disable(tp);
rtl_hw_reset(tp);
- RTL_W16(IntrStatus, 0xffff);
+ rtl_ack_events(tp, 0xffff);
pci_set_master(pdev);
tp->do_ioctl = rtl_xmii_ioctl;
}
- spin_lock_init(&tp->lock);
+ mutex_init(&tp->wk.mutex);
/* Get MAC address */
for (i = 0; i < ETH_ALEN; i++)
/* 8110SCd requires hardware Rx VLAN - disallow toggling */
dev->hw_features &= ~NETIF_F_HW_VLAN_RX;
- tp->intr_mask = 0xffff;
+ dev->hw_features |= NETIF_F_RXALL;
+ dev->hw_features |= NETIF_F_RXFCS;
+
tp->hw_start = cfg->hw_start;
- tp->intr_event = cfg->intr_event;
- tp->napi_event = cfg->napi_event;
+ tp->event_slow = cfg->event_slow;
tp->opts1_mask = (tp->mac_version != RTL_GIGA_MAC_VER_01) ?
~(RxBOVF | RxFOVF) : ~0;
rtl8168_driver_stop(tp);
}
- cancel_delayed_work_sync(&tp->task);
+ cancel_work_sync(&tp->wk.work);
unregister_netdev(dev);
rtl_request_uncached_firmware(tp);
}
+static void rtl_task(struct work_struct *);
+
static int rtl8169_open(struct net_device *dev)
{
struct rtl8169_private *tp = netdev_priv(dev);
if (retval < 0)
goto err_free_rx_1;
- INIT_DELAYED_WORK(&tp->task, NULL);
+ INIT_WORK(&tp->wk.work, rtl_task);
smp_mb();
if (retval < 0)
goto err_release_fw_2;
+ rtl_lock_work(tp);
+
+ set_bit(RTL_FLAG_TASK_ENABLED, tp->wk.flags);
+
napi_enable(&tp->napi);
rtl8169_init_phy(dev, tp);
- rtl8169_set_features(dev, dev->features);
+ __rtl8169_set_features(dev, dev->features);
rtl_pll_power_up(tp);
rtl_hw_start(dev);
+ netif_start_queue(dev);
+
+ rtl_unlock_work(tp);
+
tp->saved_wolopts = 0;
pm_runtime_put_noidle(&pdev->dev);
tp->hw_start(dev);
- netif_start_queue(dev);
+ rtl_irq_enable_all(tp);
}
static void rtl_set_rx_tx_desc_registers(struct rtl8169_private *tp,
/* no early-rx interrupts */
RTL_W16(MultiIntr, RTL_R16(MultiIntr) & 0xF000);
-
- /* Enable all known interrupts by setting the interrupt mask. */
- RTL_W16(IntrMask, tp->intr_event);
}
static void rtl_csi_access_enable(void __iomem *ioaddr, u32 bits)
/* Work around for RxFIFO overflow. */
if (tp->mac_version == RTL_GIGA_MAC_VER_11) {
- tp->intr_event |= RxFIFOOver | PCSTimeout;
- tp->intr_event &= ~RxOverflow;
+ tp->event_slow |= RxFIFOOver | PCSTimeout;
+ tp->event_slow &= ~RxOverflow;
}
rtl_set_rx_tx_desc_registers(tp, ioaddr);
RTL_W8(Cfg9346, Cfg9346_Lock);
RTL_W16(MultiIntr, RTL_R16(MultiIntr) & 0xF000);
-
- RTL_W16(IntrMask, tp->intr_event);
}
#define R810X_CPCMD_QUIRK_MASK (\
void __iomem *ioaddr = tp->mmio_addr;
struct pci_dev *pdev = tp->pci_dev;
- if (tp->mac_version >= RTL_GIGA_MAC_VER_30) {
- tp->intr_event &= ~RxFIFOOver;
- tp->napi_event &= ~RxFIFOOver;
- }
+ if (tp->mac_version >= RTL_GIGA_MAC_VER_30)
+ tp->event_slow &= ~RxFIFOOver;
if (tp->mac_version == RTL_GIGA_MAC_VER_13 ||
tp->mac_version == RTL_GIGA_MAC_VER_16) {
rtl_set_rx_mode(dev);
RTL_W16(MultiIntr, RTL_R16(MultiIntr) & 0xf000);
-
- RTL_W16(IntrMask, tp->intr_event);
}
static int rtl8169_change_mtu(struct net_device *dev, int new_mtu)
{
rtl8169_tx_clear_range(tp, tp->dirty_tx, NUM_TX_DESC);
tp->cur_tx = tp->dirty_tx = 0;
+ netdev_reset_queue(tp->dev);
}
-static void rtl8169_schedule_work(struct net_device *dev, work_func_t task)
-{
- struct rtl8169_private *tp = netdev_priv(dev);
-
- PREPARE_DELAYED_WORK(&tp->task, task);
- schedule_delayed_work(&tp->task, 4);
-}
-
-static void rtl8169_wait_for_quiescence(struct net_device *dev)
-{
- struct rtl8169_private *tp = netdev_priv(dev);
- void __iomem *ioaddr = tp->mmio_addr;
-
- synchronize_irq(dev->irq);
-
- /* Wait for any pending NAPI task to complete */
- napi_disable(&tp->napi);
-
- rtl8169_irq_mask_and_ack(tp);
-
- tp->intr_mask = 0xffff;
- RTL_W16(IntrMask, tp->intr_event);
- napi_enable(&tp->napi);
-}
-
-static void rtl8169_reinit_task(struct work_struct *work)
-{
- struct rtl8169_private *tp =
- container_of(work, struct rtl8169_private, task.work);
- struct net_device *dev = tp->dev;
- int ret;
-
- rtnl_lock();
-
- if (!netif_running(dev))
- goto out_unlock;
-
- rtl8169_wait_for_quiescence(dev);
- rtl8169_close(dev);
-
- ret = rtl8169_open(dev);
- if (unlikely(ret < 0)) {
- if (net_ratelimit())
- netif_err(tp, drv, dev,
- "reinit failure (status = %d). Rescheduling\n",
- ret);
- rtl8169_schedule_work(dev, rtl8169_reinit_task);
- }
-
-out_unlock:
- rtnl_unlock();
-}
-
-static void rtl8169_reset_task(struct work_struct *work)
+static void rtl_reset_work(struct rtl8169_private *tp)
{
struct net_device *dev = tp->dev;
int i;
- rtnl_lock();
-
- if (!netif_running(dev))
- goto out_unlock;
+ napi_disable(&tp->napi);
+ netif_stop_queue(dev);
+ synchronize_sched();
rtl8169_hw_reset(tp);
- rtl8169_wait_for_quiescence(dev);
-
for (i = 0; i < NUM_RX_DESC; i++)
rtl8169_mark_to_asic(tp->RxDescArray + i, rx_buf_sz);
rtl8169_tx_clear(tp);
rtl8169_init_ring_indexes(tp);
+ napi_enable(&tp->napi);
rtl_hw_start(dev);
netif_wake_queue(dev);
rtl8169_check_link_status(dev, tp, tp->mmio_addr);
-
-out_unlock:
- rtnl_unlock();
}
static void rtl8169_tx_timeout(struct net_device *dev)
{
- rtl8169_schedule_work(dev, rtl8169_reset_task);
+ struct rtl8169_private *tp = netdev_priv(dev);
+
+ rtl_schedule_task(tp, RTL_FLAG_TASK_RESET_PENDING);
}
static int rtl8169_xmit_frags(struct rtl8169_private *tp, struct sk_buff *skb,
txd->opts2 = cpu_to_le32(opts[1]);
+ netdev_sent_queue(dev, skb->len);
+
wmb();
/* Anti gcc 2.95.3 bugware (sic) */
RTL_W8(TxPoll, NPQ);
+ mmiowb();
+
if (TX_BUFFS_AVAIL(tp) < MAX_SKB_FRAGS) {
+ /* Avoid wrongly optimistic queue wake-up: rtl_tx thread must
+ * not miss a ring update when it notices a stopped queue.
+ */
+ smp_wmb();
netif_stop_queue(dev);
- smp_rmb();
+ /* Sync with rtl_tx:
+ * - publish queue status and cur_tx ring index (write barrier)
+ * - refresh dirty_tx ring index (read barrier).
+ * May the current thread have a pessimistic view of the ring
+ * status and forget to wake up queue, a racing rtl_tx thread
+ * can't.
+ */
+ smp_mb();
if (TX_BUFFS_AVAIL(tp) >= MAX_SKB_FRAGS)
netif_wake_queue(dev);
}
rtl8169_hw_reset(tp);
- rtl8169_schedule_work(dev, rtl8169_reinit_task);
+ rtl_schedule_task(tp, RTL_FLAG_TASK_RESET_PENDING);
}
-static void rtl8169_tx_interrupt(struct net_device *dev,
- struct rtl8169_private *tp,
- void __iomem *ioaddr)
+struct rtl_txc {
+ int packets;
+ int bytes;
+};
+
+static void rtl_tx(struct net_device *dev, struct rtl8169_private *tp)
{
+ struct rtl8169_stats *tx_stats = &tp->tx_stats;
unsigned int dirty_tx, tx_left;
+ struct rtl_txc txc = { 0, 0 };
dirty_tx = tp->dirty_tx;
smp_rmb();
rtl8169_unmap_tx_skb(&tp->pci_dev->dev, tx_skb,
tp->TxDescArray + entry);
if (status & LastFrag) {
- dev->stats.tx_packets++;
- dev->stats.tx_bytes += tx_skb->skb->len;
- dev_kfree_skb(tx_skb->skb);
+ struct sk_buff *skb = tx_skb->skb;
+
+ txc.packets++;
+ txc.bytes += skb->len;
+ dev_kfree_skb(skb);
tx_skb->skb = NULL;
}
dirty_tx++;
tx_left--;
}
+ u64_stats_update_begin(&tx_stats->syncp);
+ tx_stats->packets += txc.packets;
+ tx_stats->bytes += txc.bytes;
+ u64_stats_update_end(&tx_stats->syncp);
+
+ netdev_completed_queue(dev, txc.packets, txc.bytes);
+
if (tp->dirty_tx != dirty_tx) {
tp->dirty_tx = dirty_tx;
- smp_wmb();
+ /* Sync with rtl8169_start_xmit:
+ * - publish dirty_tx ring index (write barrier)
+ * - refresh cur_tx ring index and queue status (read barrier)
+ * May the current thread miss the stopped queue condition,
+ * a racing xmit thread can only have a right view of the
+ * ring status.
+ */
+ smp_mb();
if (netif_queue_stopped(dev) &&
(TX_BUFFS_AVAIL(tp) >= MAX_SKB_FRAGS)) {
netif_wake_queue(dev);
* of start_xmit activity is detected (if it is not detected,
* it is slow enough). -- FR
*/
- smp_rmb();
- if (tp->cur_tx != dirty_tx)
+ if (tp->cur_tx != dirty_tx) {
+ void __iomem *ioaddr = tp->mmio_addr;
+
RTL_W8(TxPoll, NPQ);
+ }
}
}
return skb;
}
-static int rtl8169_rx_interrupt(struct net_device *dev,
- struct rtl8169_private *tp,
- void __iomem *ioaddr, u32 budget)
+static int rtl_rx(struct net_device *dev, struct rtl8169_private *tp, u32 budget)
{
unsigned int cur_rx, rx_left;
unsigned int count;
if (status & RxCRC)
dev->stats.rx_crc_errors++;
if (status & RxFOVF) {
- rtl8169_schedule_work(dev, rtl8169_reset_task);
+ rtl_schedule_task(tp, RTL_FLAG_TASK_RESET_PENDING);
dev->stats.rx_fifo_errors++;
}
+ if ((status & (RxRUNT | RxCRC)) &&
+ !(status & (RxRWT | RxFOVF)) &&
+ (dev->features & NETIF_F_RXALL))
+ goto process_pkt;
+
rtl8169_mark_to_asic(desc, rx_buf_sz);
} else {
struct sk_buff *skb;
- dma_addr_t addr = le64_to_cpu(desc->addr);
- int pkt_size = (status & 0x00003fff) - 4;
+ dma_addr_t addr;
+ int pkt_size;
+
+process_pkt:
+ addr = le64_to_cpu(desc->addr);
+ if (likely(!(dev->features & NETIF_F_RXFCS)))
+ pkt_size = (status & 0x00003fff) - 4;
+ else
+ pkt_size = status & 0x00003fff;
/*
* The driver does not support incoming fragmented
napi_gro_receive(&tp->napi, skb);
- dev->stats.rx_bytes += pkt_size;
- dev->stats.rx_packets++;
+ u64_stats_update_begin(&tp->rx_stats.syncp);
+ tp->rx_stats.packets++;
+ tp->rx_stats.bytes += pkt_size;
+ u64_stats_update_end(&tp->rx_stats.syncp);
}
/* Work around for AMD plateform. */
{
struct net_device *dev = dev_instance;
struct rtl8169_private *tp = netdev_priv(dev);
- void __iomem *ioaddr = tp->mmio_addr;
int handled = 0;
- int status;
+ u16 status;
- /* loop handling interrupts until we have no new ones or
- * we hit a invalid/hotplug case.
- */
- status = RTL_R16(IntrStatus);
- while (status && status != 0xffff) {
- status &= tp->intr_event;
- if (!status)
- break;
+ status = rtl_get_events(tp);
+ if (status && status != 0xffff) {
+ status &= RTL_EVENT_NAPI | tp->event_slow;
+ if (status) {
+ handled = 1;
- handled = 1;
+ rtl_irq_disable(tp);
+ napi_schedule(&tp->napi);
+ }
+ }
+ return IRQ_RETVAL(handled);
+}
- /* Handle all of the error cases first. These will reset
- * the chip, so just exit the loop.
- */
- if (unlikely(!netif_running(dev))) {
- rtl8169_hw_reset(tp);
+/*
+ * Workqueue context.
+ */
+static void rtl_slow_event_work(struct rtl8169_private *tp)
+{
+ struct net_device *dev = tp->dev;
+ u16 status;
+
+ status = rtl_get_events(tp) & tp->event_slow;
+ rtl_ack_events(tp, status);
+
+ if (unlikely(status & RxFIFOOver)) {
+ switch (tp->mac_version) {
+ /* Work around for rx fifo overflow */
+ case RTL_GIGA_MAC_VER_11:
+ netif_stop_queue(dev);
+ /* XXX - Hack alert. See rtl_task(). */
+ set_bit(RTL_FLAG_TASK_RESET_PENDING, tp->wk.flags);
+ default:
break;
}
+ }
- if (unlikely(status & RxFIFOOver)) {
- switch (tp->mac_version) {
- /* Work around for rx fifo overflow */
- case RTL_GIGA_MAC_VER_11:
- netif_stop_queue(dev);
- rtl8169_tx_timeout(dev);
- goto done;
- default:
- break;
- }
- }
+ if (unlikely(status & SYSErr))
+ rtl8169_pcierr_interrupt(dev);
- if (unlikely(status & SYSErr)) {
- rtl8169_pcierr_interrupt(dev);
- break;
- }
+ if (status & LinkChg)
+ __rtl8169_check_link_status(dev, tp, tp->mmio_addr, true);
- if (status & LinkChg)
- __rtl8169_check_link_status(dev, tp, ioaddr, true);
+ napi_disable(&tp->napi);
+ rtl_irq_disable(tp);
- /* We need to see the lastest version of tp->intr_mask to
- * avoid ignoring an MSI interrupt and having to wait for
- * another event which may never come.
- */
- smp_rmb();
- if (status & tp->intr_mask & tp->napi_event) {
- RTL_W16(IntrMask, tp->intr_event & ~tp->napi_event);
- tp->intr_mask = ~tp->napi_event;
+ napi_enable(&tp->napi);
+ napi_schedule(&tp->napi);
+}
- if (likely(napi_schedule_prep(&tp->napi)))
- __napi_schedule(&tp->napi);
- else
- netif_info(tp, intr, dev,
- "interrupt %04x in poll\n", status);
- }
+static void rtl_task(struct work_struct *work)
+{
+ static const struct {
+ int bitnr;
+ void (*action)(struct rtl8169_private *);
+ } rtl_work[] = {
+ /* XXX - keep rtl_slow_event_work() as first element. */
+ { RTL_FLAG_TASK_SLOW_PENDING, rtl_slow_event_work },
+ { RTL_FLAG_TASK_RESET_PENDING, rtl_reset_work },
+ { RTL_FLAG_TASK_PHY_PENDING, rtl_phy_work }
+ };
+ struct rtl8169_private *tp =
+ container_of(work, struct rtl8169_private, wk.work);
+ struct net_device *dev = tp->dev;
+ int i;
- /* We only get a new MSI interrupt when all active irq
- * sources on the chip have been acknowledged. So, ack
- * everything we've seen and check if new sources have become
- * active to avoid blocking all interrupts from the chip.
- */
- RTL_W16(IntrStatus,
- (status & RxFIFOOver) ? (status | RxOverflow) : status);
- status = RTL_R16(IntrStatus);
+ rtl_lock_work(tp);
+
+ if (!netif_running(dev) ||
+ !test_bit(RTL_FLAG_TASK_ENABLED, tp->wk.flags))
+ goto out_unlock;
+
+ for (i = 0; i < ARRAY_SIZE(rtl_work); i++) {
+ bool pending;
+
+ pending = test_and_clear_bit(rtl_work[i].bitnr, tp->wk.flags);
+ if (pending)
+ rtl_work[i].action(tp);
}
-done:
- return IRQ_RETVAL(handled);
+
+out_unlock:
+ rtl_unlock_work(tp);
}
static int rtl8169_poll(struct napi_struct *napi, int budget)
{
struct rtl8169_private *tp = container_of(napi, struct rtl8169_private, napi);
struct net_device *dev = tp->dev;
- void __iomem *ioaddr = tp->mmio_addr;
- int work_done;
+ u16 enable_mask = RTL_EVENT_NAPI | tp->event_slow;
+ int work_done= 0;
+ u16 status;
+
+ status = rtl_get_events(tp);
+ rtl_ack_events(tp, status & ~tp->event_slow);
+
+ if (status & RTL_EVENT_NAPI_RX)
+ work_done = rtl_rx(dev, tp, (u32) budget);
+
+ if (status & RTL_EVENT_NAPI_TX)
+ rtl_tx(dev, tp);
- work_done = rtl8169_rx_interrupt(dev, tp, ioaddr, (u32) budget);
- rtl8169_tx_interrupt(dev, tp, ioaddr);
+ if (status & tp->event_slow) {
+ enable_mask &= ~tp->event_slow;
+
+ rtl_schedule_task(tp, RTL_FLAG_TASK_SLOW_PENDING);
+ }
if (work_done < budget) {
napi_complete(napi);
- /* We need for force the visibility of tp->intr_mask
- * for other CPUs, as we can loose an MSI interrupt
- * and potentially wait for a retransmit timeout if we don't.
- * The posted write to IntrMask is safe, as it will
- * eventually make it to the chip and we won't loose anything
- * until it does.
- */
- tp->intr_mask = 0xffff;
- wmb();
- RTL_W16(IntrMask, tp->intr_event);
+ rtl_irq_enable(tp, enable_mask);
+ mmiowb();
}
return work_done;
del_timer_sync(&tp->timer);
- netif_stop_queue(dev);
-
napi_disable(&tp->napi);
-
- spin_lock_irq(&tp->lock);
+ netif_stop_queue(dev);
rtl8169_hw_reset(tp);
/*
* At this point device interrupts can not be enabled in any function,
- * as netif_running is not true (rtl8169_interrupt, rtl8169_reset_task,
- * rtl8169_reinit_task) and napi is disabled (rtl8169_poll).
+ * as netif_running is not true (rtl8169_interrupt, rtl8169_reset_task)
+ * and napi is disabled (rtl8169_poll).
*/
rtl8169_rx_missed(dev, ioaddr);
- spin_unlock_irq(&tp->lock);
-
- synchronize_irq(dev->irq);
-
/* Give a racing hard_start_xmit a few cycles to complete. */
- synchronize_sched(); /* FIXME: should this be synchronize_irq()? */
+ synchronize_sched();
rtl8169_tx_clear(tp);
/* Update counters before going down */
rtl8169_update_counters(dev);
+ rtl_lock_work(tp);
+ clear_bit(RTL_FLAG_TASK_ENABLED, tp->wk.flags);
+
rtl8169_down(dev);
+ rtl_unlock_work(tp);
free_irq(dev->irq, dev);
{
struct rtl8169_private *tp = netdev_priv(dev);
void __iomem *ioaddr = tp->mmio_addr;
- unsigned long flags;
u32 mc_filter[2]; /* Multicast hash filter */
int rx_mode;
u32 tmp = 0;
}
}
- spin_lock_irqsave(&tp->lock, flags);
+ if (dev->features & NETIF_F_RXALL)
+ rx_mode |= (AcceptErr | AcceptRunt);
tmp = (RTL_R32(RxConfig) & ~RX_CONFIG_ACCEPT_MASK) | rx_mode;
RTL_W32(MAR0 + 0, mc_filter[0]);
RTL_W32(RxConfig, tmp);
-
- spin_unlock_irqrestore(&tp->lock, flags);
}
-/**
- * rtl8169_get_stats - Get rtl8169 read/write statistics
- * @dev: The Ethernet Device to get statistics for
- *
- * Get TX/RX statistics for rtl8169
- */
-static struct net_device_stats *rtl8169_get_stats(struct net_device *dev)
+static struct rtnl_link_stats64 *
+rtl8169_get_stats64(struct net_device *dev, struct rtnl_link_stats64 *stats)
{
struct rtl8169_private *tp = netdev_priv(dev);
void __iomem *ioaddr = tp->mmio_addr;
- unsigned long flags;
+ unsigned int start;
- if (netif_running(dev)) {
- spin_lock_irqsave(&tp->lock, flags);
+ if (netif_running(dev))
rtl8169_rx_missed(dev, ioaddr);
- spin_unlock_irqrestore(&tp->lock, flags);
- }
- return &dev->stats;
+ do {
+ start = u64_stats_fetch_begin_bh(&tp->rx_stats.syncp);
+ stats->rx_packets = tp->rx_stats.packets;
+ stats->rx_bytes = tp->rx_stats.bytes;
+ } while (u64_stats_fetch_retry_bh(&tp->rx_stats.syncp, start));
+
+
+ do {
+ start = u64_stats_fetch_begin_bh(&tp->tx_stats.syncp);
+ stats->tx_packets = tp->tx_stats.packets;
+ stats->tx_bytes = tp->tx_stats.bytes;
+ } while (u64_stats_fetch_retry_bh(&tp->tx_stats.syncp, start));
+
+ stats->rx_dropped = dev->stats.rx_dropped;
+ stats->tx_dropped = dev->stats.tx_dropped;
+ stats->rx_length_errors = dev->stats.rx_length_errors;
+ stats->rx_errors = dev->stats.rx_errors;
+ stats->rx_crc_errors = dev->stats.rx_crc_errors;
+ stats->rx_fifo_errors = dev->stats.rx_fifo_errors;
+ stats->rx_missed_errors = dev->stats.rx_missed_errors;
+
+ return stats;
}
static void rtl8169_net_suspend(struct net_device *dev)
if (!netif_running(dev))
return;
- rtl_pll_power_down(tp);
-
netif_device_detach(dev);
netif_stop_queue(dev);
+
+ rtl_lock_work(tp);
+ napi_disable(&tp->napi);
+ clear_bit(RTL_FLAG_TASK_ENABLED, tp->wk.flags);
+ rtl_unlock_work(tp);
+
+ rtl_pll_power_down(tp);
}
#ifdef CONFIG_PM
rtl_pll_power_up(tp);
- rtl8169_schedule_work(dev, rtl8169_reset_task);
+ set_bit(RTL_FLAG_TASK_ENABLED, tp->wk.flags);
+
+ rtl_schedule_task(tp, RTL_FLAG_TASK_RESET_PENDING);
}
static int rtl8169_resume(struct device *device)
if (!tp->TxDescArray)
return 0;
- spin_lock_irq(&tp->lock);
+ rtl_lock_work(tp);
tp->saved_wolopts = __rtl8169_get_wol(tp);
__rtl8169_set_wol(tp, WAKE_ANY);
- spin_unlock_irq(&tp->lock);
+ rtl_unlock_work(tp);
rtl8169_net_suspend(dev);
if (!tp->TxDescArray)
return 0;
- spin_lock_irq(&tp->lock);
+ rtl_lock_work(tp);
__rtl8169_set_wol(tp, tp->saved_wolopts);
tp->saved_wolopts = 0;
- spin_unlock_irq(&tp->lock);
+ rtl_unlock_work(tp);
rtl8169_init_phy(dev, tp);
{
struct net_device *dev = pci_get_drvdata(pdev);
struct rtl8169_private *tp = netdev_priv(dev);
+ struct device *d = &pdev->dev;
+
+ pm_runtime_get_sync(d);
rtl8169_net_suspend(dev);
/* Restore original MAC address */
rtl_rar_set(tp, dev->perm_addr);
- spin_lock_irq(&tp->lock);
-
rtl8169_hw_reset(tp);
- spin_unlock_irq(&tp->lock);
-
if (system_state == SYSTEM_POWER_OFF) {
if (__rtl8169_get_wol(tp) & WAKE_ANY) {
rtl_wol_suspend_quirk(tp);
pci_wake_from_d3(pdev, true);
pci_set_power_state(pdev, PCI_D3hot);
}
+
+ pm_runtime_put_noidle(d);
}
static struct pci_driver rtl8169_pci_driver = {