diff options
author | Matt Carlson <mcarlson@broadcom.com> | 2008-05-02 19:49:29 -0400 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2008-05-02 19:49:29 -0400 |
commit | 7c5026aa9b81dd45df8d3f4e0be73e485976a8b6 (patch) | |
tree | 09b0e14566d630f1b8d3225b6fda0c2a862519e9 /drivers/net/tg3.c | |
parent | 109115e1991824b88306b374b763d6857b292aeb (diff) |
tg3: Add link state reporting to UMP firmware
All variants of the 5714, 5715, and 5780 offer a feature called the
"Universal Management Port". This feature is implemented in firmware
and is largely transparent to the driver, except...
It turns out that the UMP firmware needs to know the current status
of the link. Because the firmware cannot touch the PHY registers while
the driver is in control of the device, it needs the driver to report
link status changes through an additional handshaking mechanism.
Without this handshake, it has been observed in the field that the UMP
firmware will not operate correctly.
This patch implements the new handshake with the UMP firmware. Since
the handshake uses the same mechanism ASF heartbeats use, code was
added to detect and wait for completion of a pending previous event.
Signed-off-by: Matt Carlson <mcarlson@broadcom.com>
Signed-off-by: Michael Chan <mchan@broadcom.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'drivers/net/tg3.c')
-rw-r--r-- | drivers/net/tg3.c | 85 |
1 files changed, 75 insertions, 10 deletions
diff --git a/drivers/net/tg3.c b/drivers/net/tg3.c index bf376b32450e..3ba6c52fed4e 100644 --- a/drivers/net/tg3.c +++ b/drivers/net/tg3.c | |||
@@ -1656,12 +1656,76 @@ static int tg3_set_power_state(struct tg3 *tp, pci_power_t state) | |||
1656 | return 0; | 1656 | return 0; |
1657 | } | 1657 | } |
1658 | 1658 | ||
1659 | /* tp->lock is held. */ | ||
1660 | static void tg3_wait_for_event_ack(struct tg3 *tp) | ||
1661 | { | ||
1662 | int i; | ||
1663 | |||
1664 | /* Wait for up to 2.5 milliseconds */ | ||
1665 | for (i = 0; i < 250000; i++) { | ||
1666 | if (!(tr32(GRC_RX_CPU_EVENT) & GRC_RX_CPU_DRIVER_EVENT)) | ||
1667 | break; | ||
1668 | udelay(10); | ||
1669 | } | ||
1670 | } | ||
1671 | |||
1672 | /* tp->lock is held. */ | ||
1673 | static void tg3_ump_link_report(struct tg3 *tp) | ||
1674 | { | ||
1675 | u32 reg; | ||
1676 | u32 val; | ||
1677 | |||
1678 | if (!(tp->tg3_flags2 & TG3_FLG2_5780_CLASS) || | ||
1679 | !(tp->tg3_flags & TG3_FLAG_ENABLE_ASF)) | ||
1680 | return; | ||
1681 | |||
1682 | tg3_wait_for_event_ack(tp); | ||
1683 | |||
1684 | tg3_write_mem(tp, NIC_SRAM_FW_CMD_MBOX, FWCMD_NICDRV_LINK_UPDATE); | ||
1685 | |||
1686 | tg3_write_mem(tp, NIC_SRAM_FW_CMD_LEN_MBOX, 14); | ||
1687 | |||
1688 | val = 0; | ||
1689 | if (!tg3_readphy(tp, MII_BMCR, ®)) | ||
1690 | val = reg << 16; | ||
1691 | if (!tg3_readphy(tp, MII_BMSR, ®)) | ||
1692 | val |= (reg & 0xffff); | ||
1693 | tg3_write_mem(tp, NIC_SRAM_FW_CMD_DATA_MBOX, val); | ||
1694 | |||
1695 | val = 0; | ||
1696 | if (!tg3_readphy(tp, MII_ADVERTISE, ®)) | ||
1697 | val = reg << 16; | ||
1698 | if (!tg3_readphy(tp, MII_LPA, ®)) | ||
1699 | val |= (reg & 0xffff); | ||
1700 | tg3_write_mem(tp, NIC_SRAM_FW_CMD_DATA_MBOX + 4, val); | ||
1701 | |||
1702 | val = 0; | ||
1703 | if (!(tp->tg3_flags2 & TG3_FLG2_MII_SERDES)) { | ||
1704 | if (!tg3_readphy(tp, MII_CTRL1000, ®)) | ||
1705 | val = reg << 16; | ||
1706 | if (!tg3_readphy(tp, MII_STAT1000, ®)) | ||
1707 | val |= (reg & 0xffff); | ||
1708 | } | ||
1709 | tg3_write_mem(tp, NIC_SRAM_FW_CMD_DATA_MBOX + 8, val); | ||
1710 | |||
1711 | if (!tg3_readphy(tp, MII_PHYADDR, ®)) | ||
1712 | val = reg << 16; | ||
1713 | else | ||
1714 | val = 0; | ||
1715 | tg3_write_mem(tp, NIC_SRAM_FW_CMD_DATA_MBOX + 12, val); | ||
1716 | |||
1717 | val = tr32(GRC_RX_CPU_EVENT); | ||
1718 | val |= GRC_RX_CPU_DRIVER_EVENT; | ||
1719 | tw32_f(GRC_RX_CPU_EVENT, val); | ||
1720 | } | ||
1721 | |||
1659 | static void tg3_link_report(struct tg3 *tp) | 1722 | static void tg3_link_report(struct tg3 *tp) |
1660 | { | 1723 | { |
1661 | if (!netif_carrier_ok(tp->dev)) { | 1724 | if (!netif_carrier_ok(tp->dev)) { |
1662 | if (netif_msg_link(tp)) | 1725 | if (netif_msg_link(tp)) |
1663 | printk(KERN_INFO PFX "%s: Link is down.\n", | 1726 | printk(KERN_INFO PFX "%s: Link is down.\n", |
1664 | tp->dev->name); | 1727 | tp->dev->name); |
1728 | tg3_ump_link_report(tp); | ||
1665 | } else if (netif_msg_link(tp)) { | 1729 | } else if (netif_msg_link(tp)) { |
1666 | printk(KERN_INFO PFX "%s: Link is up at %d Mbps, %s duplex.\n", | 1730 | printk(KERN_INFO PFX "%s: Link is up at %d Mbps, %s duplex.\n", |
1667 | tp->dev->name, | 1731 | tp->dev->name, |
@@ -1679,6 +1743,7 @@ static void tg3_link_report(struct tg3 *tp) | |||
1679 | "on" : "off", | 1743 | "on" : "off", |
1680 | (tp->link_config.active_flowctrl & TG3_FLOW_CTRL_RX) ? | 1744 | (tp->link_config.active_flowctrl & TG3_FLOW_CTRL_RX) ? |
1681 | "on" : "off"); | 1745 | "on" : "off"); |
1746 | tg3_ump_link_report(tp); | ||
1682 | } | 1747 | } |
1683 | } | 1748 | } |
1684 | 1749 | ||
@@ -5500,19 +5565,17 @@ static void tg3_stop_fw(struct tg3 *tp) | |||
5500 | if ((tp->tg3_flags & TG3_FLAG_ENABLE_ASF) && | 5565 | if ((tp->tg3_flags & TG3_FLAG_ENABLE_ASF) && |
5501 | !(tp->tg3_flags3 & TG3_FLG3_ENABLE_APE)) { | 5566 | !(tp->tg3_flags3 & TG3_FLG3_ENABLE_APE)) { |
5502 | u32 val; | 5567 | u32 val; |
5503 | int i; | 5568 | |
5569 | /* Wait for RX cpu to ACK the previous event. */ | ||
5570 | tg3_wait_for_event_ack(tp); | ||
5504 | 5571 | ||
5505 | tg3_write_mem(tp, NIC_SRAM_FW_CMD_MBOX, FWCMD_NICDRV_PAUSE_FW); | 5572 | tg3_write_mem(tp, NIC_SRAM_FW_CMD_MBOX, FWCMD_NICDRV_PAUSE_FW); |
5506 | val = tr32(GRC_RX_CPU_EVENT); | 5573 | val = tr32(GRC_RX_CPU_EVENT); |
5507 | val |= (1 << 14); | 5574 | val |= GRC_RX_CPU_DRIVER_EVENT; |
5508 | tw32(GRC_RX_CPU_EVENT, val); | 5575 | tw32(GRC_RX_CPU_EVENT, val); |
5509 | 5576 | ||
5510 | /* Wait for RX cpu to ACK the event. */ | 5577 | /* Wait for RX cpu to ACK this event. */ |
5511 | for (i = 0; i < 100; i++) { | 5578 | tg3_wait_for_event_ack(tp); |
5512 | if (!(tr32(GRC_RX_CPU_EVENT) & (1 << 14))) | ||
5513 | break; | ||
5514 | udelay(1); | ||
5515 | } | ||
5516 | } | 5579 | } |
5517 | } | 5580 | } |
5518 | 5581 | ||
@@ -7402,14 +7465,16 @@ static void tg3_timer(unsigned long __opaque) | |||
7402 | if (tp->tg3_flags & TG3_FLAG_ENABLE_ASF) { | 7465 | if (tp->tg3_flags & TG3_FLAG_ENABLE_ASF) { |
7403 | u32 val; | 7466 | u32 val; |
7404 | 7467 | ||
7468 | tg3_wait_for_event_ack(tp); | ||
7469 | |||
7405 | tg3_write_mem(tp, NIC_SRAM_FW_CMD_MBOX, | 7470 | tg3_write_mem(tp, NIC_SRAM_FW_CMD_MBOX, |
7406 | FWCMD_NICDRV_ALIVE3); | 7471 | FWCMD_NICDRV_ALIVE3); |
7407 | tg3_write_mem(tp, NIC_SRAM_FW_CMD_LEN_MBOX, 4); | 7472 | tg3_write_mem(tp, NIC_SRAM_FW_CMD_LEN_MBOX, 4); |
7408 | /* 5 seconds timeout */ | 7473 | /* 5 seconds timeout */ |
7409 | tg3_write_mem(tp, NIC_SRAM_FW_CMD_DATA_MBOX, 5); | 7474 | tg3_write_mem(tp, NIC_SRAM_FW_CMD_DATA_MBOX, 5); |
7410 | val = tr32(GRC_RX_CPU_EVENT); | 7475 | val = tr32(GRC_RX_CPU_EVENT); |
7411 | val |= (1 << 14); | 7476 | val |= GRC_RX_CPU_DRIVER_EVENT; |
7412 | tw32(GRC_RX_CPU_EVENT, val); | 7477 | tw32_f(GRC_RX_CPU_EVENT, val); |
7413 | } | 7478 | } |
7414 | tp->asf_counter = tp->asf_multiplier; | 7479 | tp->asf_counter = tp->asf_multiplier; |
7415 | } | 7480 | } |