aboutsummaryrefslogtreecommitdiff
path: root/drivers/net/tg3.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/net/tg3.c')
-rw-r--r--drivers/net/tg3.c9083
1 files changed, 9083 insertions, 0 deletions
diff --git a/drivers/net/tg3.c b/drivers/net/tg3.c
new file mode 100644
index 00000000000..12de80884b1
--- /dev/null
+++ b/drivers/net/tg3.c
@@ -0,0 +1,9083 @@
+/*
+ * tg3.c: Broadcom Tigon3 ethernet driver.
+ *
+ * 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 Broadcom Corporation.
+ *
+ * Firmware is:
+ * Copyright (C) 2000-2003 Broadcom Corporation.
+ */
+
+#include <linux/config.h>
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/kernel.h>
+#include <linux/types.h>
+#include <linux/compiler.h>
+#include <linux/slab.h>
+#include <linux/delay.h>
+#include <linux/init.h>
+#include <linux/ioport.h>
+#include <linux/pci.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/skbuff.h>
+#include <linux/ethtool.h>
+#include <linux/mii.h>
+#include <linux/if_vlan.h>
+#include <linux/ip.h>
+#include <linux/tcp.h>
+#include <linux/workqueue.h>
+
+#include <net/checksum.h>
+
+#include <asm/system.h>
+#include <asm/io.h>
+#include <asm/byteorder.h>
+#include <asm/uaccess.h>
+
+#ifdef CONFIG_SPARC64
+#include <asm/idprom.h>
+#include <asm/oplib.h>
+#include <asm/pbm.h>
+#endif
+
+#if defined(CONFIG_VLAN_8021Q) || defined(CONFIG_VLAN_8021Q_MODULE)
+#define TG3_VLAN_TAG_USED 1
+#else
+#define TG3_VLAN_TAG_USED 0
+#endif
+
+#ifdef NETIF_F_TSO
+#define TG3_TSO_SUPPORT 1
+#else
+#define TG3_TSO_SUPPORT 0
+#endif
+
+#include "tg3.h"
+
+#define DRV_MODULE_NAME "tg3"
+#define PFX DRV_MODULE_NAME ": "
+#define DRV_MODULE_VERSION "3.25"
+#define DRV_MODULE_RELDATE "March 24, 2005"
+
+#define TG3_DEF_MAC_MODE 0
+#define TG3_DEF_RX_MODE 0
+#define TG3_DEF_TX_MODE 0
+#define TG3_DEF_MSG_ENABLE \
+ (NETIF_MSG_DRV | \
+ NETIF_MSG_PROBE | \
+ NETIF_MSG_LINK | \
+ NETIF_MSG_TIMER | \
+ NETIF_MSG_IFDOWN | \
+ NETIF_MSG_IFUP | \
+ NETIF_MSG_RX_ERR | \
+ NETIF_MSG_TX_ERR)
+
+/* length of time before we decide the hardware is borked,
+ * and dev->tx_timeout() should be called to fix the problem
+ */
+#define TG3_TX_TIMEOUT (5 * HZ)
+
+/* hardware minimum and maximum for a single frame's data payload */
+#define TG3_MIN_MTU 60
+#define TG3_MAX_MTU(tp) \
+ ((GET_ASIC_REV(tp->pci_chip_rev_id) != ASIC_REV_5705 && \
+ GET_ASIC_REV(tp->pci_chip_rev_id) != ASIC_REV_5750) ? 9000 : 1500)
+
+/* These numbers seem to be hard coded in the NIC firmware somehow.
+ * You can't change the ring sizes, but you can change where you place
+ * them in the NIC onboard memory.
+ */
+#define TG3_RX_RING_SIZE 512
+#define TG3_DEF_RX_RING_PENDING 200
+#define TG3_RX_JUMBO_RING_SIZE 256
+#define TG3_DEF_RX_JUMBO_RING_PENDING 100
+
+/* Do not place this n-ring entries value into the tp struct itself,
+ * we really want to expose these constants to GCC so that modulo et
+ * al. operations are done with shifts and masks instead of with
+ * hw multiply/modulo instructions. Another solution would be to
+ * replace things like '% foo' with '& (foo - 1)'.
+ */
+#define TG3_RX_RCB_RING_SIZE(tp) \
+ ((tp->tg3_flags2 & TG3_FLG2_5705_PLUS) ? 512 : 1024)
+
+#define TG3_TX_RING_SIZE 512
+#define TG3_DEF_TX_RING_PENDING (TG3_TX_RING_SIZE - 1)
+
+#define TG3_RX_RING_BYTES (sizeof(struct tg3_rx_buffer_desc) * \
+ TG3_RX_RING_SIZE)
+#define TG3_RX_JUMBO_RING_BYTES (sizeof(struct tg3_rx_buffer_desc) * \
+ TG3_RX_JUMBO_RING_SIZE)
+#define TG3_RX_RCB_RING_BYTES(tp) (sizeof(struct tg3_rx_buffer_desc) * \
+ TG3_RX_RCB_RING_SIZE(tp))
+#define TG3_TX_RING_BYTES (sizeof(struct tg3_tx_buffer_desc) * \
+ TG3_TX_RING_SIZE)
+#define TX_RING_GAP(TP) \
+ (TG3_TX_RING_SIZE - (TP)->tx_pending)
+#define TX_BUFFS_AVAIL(TP) \
+ (((TP)->tx_cons <= (TP)->tx_prod) ? \
+ (TP)->tx_cons + (TP)->tx_pending - (TP)->tx_prod : \
+ (TP)->tx_cons - (TP)->tx_prod - TX_RING_GAP(TP))
+#define NEXT_TX(N) (((N) + 1) & (TG3_TX_RING_SIZE - 1))
+
+#define RX_PKT_BUF_SZ (1536 + tp->rx_offset + 64)
+#define RX_JUMBO_PKT_BUF_SZ (9046 + tp->rx_offset + 64)
+
+/* minimum number of free TX descriptors required to wake up TX process */
+#define TG3_TX_WAKEUP_THRESH (TG3_TX_RING_SIZE / 4)
+
+/* number of ETHTOOL_GSTATS u64's */
+#define TG3_NUM_STATS (sizeof(struct tg3_ethtool_stats)/sizeof(u64))
+
+static char version[] __devinitdata =
+ DRV_MODULE_NAME ".c:v" DRV_MODULE_VERSION " (" DRV_MODULE_RELDATE ")\n";
+
+MODULE_AUTHOR("David S. Miller (davem@redhat.com) and Jeff Garzik (jgarzik@pobox.com)");
+MODULE_DESCRIPTION("Broadcom Tigon3 ethernet driver");
+MODULE_LICENSE("GPL");
+MODULE_VERSION(DRV_MODULE_VERSION);
+
+static int tg3_debug = -1; /* -1 == use TG3_DEF_MSG_ENABLE as value */
+module_param(tg3_debug, int, 0);
+MODULE_PARM_DESC(tg3_debug, "Tigon3 bitmapped debugging message enable value");
+
+static struct pci_device_id tg3_pci_tbl[] = {
+ { PCI_VENDOR_ID_BROADCOM, PCI_DEVICE_ID_TIGON3_5700,
+ PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0UL },
+ { PCI_VENDOR_ID_BROADCOM, PCI_DEVICE_ID_TIGON3_5701,
+ PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0UL },
+ { PCI_VENDOR_ID_BROADCOM, PCI_DEVICE_ID_TIGON3_5702,
+ PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0UL },
+ { PCI_VENDOR_ID_BROADCOM, PCI_DEVICE_ID_TIGON3_5703,
+ PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0UL },
+ { PCI_VENDOR_ID_BROADCOM, PCI_DEVICE_ID_TIGON3_5704,
+ PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0UL },
+ { PCI_VENDOR_ID_BROADCOM, PCI_DEVICE_ID_TIGON3_5702FE,
+ PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0UL },
+ { PCI_VENDOR_ID_BROADCOM, PCI_DEVICE_ID_TIGON3_5705,
+ PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0UL },
+ { PCI_VENDOR_ID_BROADCOM, PCI_DEVICE_ID_TIGON3_5705_2,
+ PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0UL },
+ { PCI_VENDOR_ID_BROADCOM, PCI_DEVICE_ID_TIGON3_5705M,
+ PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0UL },
+ { PCI_VENDOR_ID_BROADCOM, PCI_DEVICE_ID_TIGON3_5705M_2,
+ PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0UL },
+ { PCI_VENDOR_ID_BROADCOM, PCI_DEVICE_ID_TIGON3_5702X,
+ PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0UL },
+ { PCI_VENDOR_ID_BROADCOM, PCI_DEVICE_ID_TIGON3_5703X,
+ PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0UL },
+ { PCI_VENDOR_ID_BROADCOM, PCI_DEVICE_ID_TIGON3_5704S,
+ PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0UL },
+ { PCI_VENDOR_ID_BROADCOM, PCI_DEVICE_ID_TIGON3_5702A3,
+ PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0UL },
+ { PCI_VENDOR_ID_BROADCOM, PCI_DEVICE_ID_TIGON3_5703A3,
+ PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0UL },
+ { PCI_VENDOR_ID_BROADCOM, PCI_DEVICE_ID_TIGON3_5782,
+ PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0UL },
+ { PCI_VENDOR_ID_BROADCOM, PCI_DEVICE_ID_TIGON3_5788,
+ PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0UL },
+ { PCI_VENDOR_ID_BROADCOM, PCI_DEVICE_ID_TIGON3_5789,
+ PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0UL },
+ { PCI_VENDOR_ID_BROADCOM, PCI_DEVICE_ID_TIGON3_5901,
+ PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0UL },
+ { PCI_VENDOR_ID_BROADCOM, PCI_DEVICE_ID_TIGON3_5901_2,
+ PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0UL },
+ { PCI_VENDOR_ID_BROADCOM, PCI_DEVICE_ID_TIGON3_5704S_2,
+ PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0UL },
+ { PCI_VENDOR_ID_BROADCOM, PCI_DEVICE_ID_TIGON3_5705F,
+ PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0UL },
+ { PCI_VENDOR_ID_BROADCOM, PCI_DEVICE_ID_TIGON3_5720,
+ PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0UL },
+ { PCI_VENDOR_ID_BROADCOM, PCI_DEVICE_ID_TIGON3_5721,
+ PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0UL },
+ { PCI_VENDOR_ID_BROADCOM, PCI_DEVICE_ID_TIGON3_5750,
+ PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0UL },
+ { PCI_VENDOR_ID_BROADCOM, PCI_DEVICE_ID_TIGON3_5751,
+ PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0UL },
+ { PCI_VENDOR_ID_BROADCOM, PCI_DEVICE_ID_TIGON3_5750M,
+ PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0UL },
+ { PCI_VENDOR_ID_BROADCOM, PCI_DEVICE_ID_TIGON3_5751M,
+ PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0UL },
+ { PCI_VENDOR_ID_BROADCOM, PCI_DEVICE_ID_TIGON3_5751F,
+ PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0UL },
+ { PCI_VENDOR_ID_BROADCOM, PCI_DEVICE_ID_TIGON3_5753,
+ PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0UL },
+ { PCI_VENDOR_ID_BROADCOM, PCI_DEVICE_ID_TIGON3_5753M,
+ PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0UL },
+ { PCI_VENDOR_ID_BROADCOM, PCI_DEVICE_ID_TIGON3_5753F,
+ PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0UL },
+ { PCI_VENDOR_ID_BROADCOM, PCI_DEVICE_ID_TIGON3_5781,
+ PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0UL },
+ { PCI_VENDOR_ID_SYSKONNECT, PCI_DEVICE_ID_SYSKONNECT_9DXX,
+ PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0UL },
+ { PCI_VENDOR_ID_SYSKONNECT, PCI_DEVICE_ID_SYSKONNECT_9MXX,
+ PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0UL },
+ { PCI_VENDOR_ID_ALTIMA, PCI_DEVICE_ID_ALTIMA_AC1000,
+ PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0UL },
+ { PCI_VENDOR_ID_ALTIMA, PCI_DEVICE_ID_ALTIMA_AC1001,
+ PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0UL },
+ { PCI_VENDOR_ID_ALTIMA, PCI_DEVICE_ID_ALTIMA_AC1003,
+ PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0UL },
+ { PCI_VENDOR_ID_ALTIMA, PCI_DEVICE_ID_ALTIMA_AC9100,
+ PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0UL },
+ { PCI_VENDOR_ID_APPLE, PCI_DEVICE_ID_APPLE_TIGON3,
+ PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0UL },
+ { 0, }
+};
+
+MODULE_DEVICE_TABLE(pci, tg3_pci_tbl);
+
+static struct {
+ const char string[ETH_GSTRING_LEN];
+} ethtool_stats_keys[TG3_NUM_STATS] = {
+ { "rx_octets" },
+ { "rx_fragments" },
+ { "rx_ucast_packets" },
+ { "rx_mcast_packets" },
+ { "rx_bcast_packets" },
+ { "rx_fcs_errors" },
+ { "rx_align_errors" },
+ { "rx_xon_pause_rcvd" },
+ { "rx_xoff_pause_rcvd" },
+ { "rx_mac_ctrl_rcvd" },
+ { "rx_xoff_entered" },
+ { "rx_frame_too_long_errors" },
+ { "rx_jabbers" },
+ { "rx_undersize_packets" },
+ { "rx_in_length_errors" },
+ { "rx_out_length_errors" },
+ { "rx_64_or_less_octet_packets" },
+ { "rx_65_to_127_octet_packets" },
+ { "rx_128_to_255_octet_packets" },
+ { "rx_256_to_511_octet_packets" },
+ { "rx_512_to_1023_octet_packets" },
+ { "rx_1024_to_1522_octet_packets" },
+ { "rx_1523_to_2047_octet_packets" },
+ { "rx_2048_to_4095_octet_packets" },
+ { "rx_4096_to_8191_octet_packets" },
+ { "rx_8192_to_9022_octet_packets" },
+
+ { "tx_octets" },
+ { "tx_collisions" },
+
+ { "tx_xon_sent" },
+ { "tx_xoff_sent" },
+ { "tx_flow_control" },
+ { "tx_mac_errors" },
+ { "tx_single_collisions" },
+ { "tx_mult_collisions" },
+ { "tx_deferred" },
+ { "tx_excessive_collisions" },
+ { "tx_late_collisions" },
+ { "tx_collide_2times" },
+ { "tx_collide_3times" },
+ { "tx_collide_4times" },
+ { "tx_collide_5times" },
+ { "tx_collide_6times" },
+ { "tx_collide_7times" },
+ { "tx_collide_8times" },
+ { "tx_collide_9times" },
+ { "tx_collide_10times" },
+ { "tx_collide_11times" },
+ { "tx_collide_12times" },
+ { "tx_collide_13times" },
+ { "tx_collide_14times" },
+ { "tx_collide_15times" },
+ { "tx_ucast_packets" },
+ { "tx_mcast_packets" },
+ { "tx_bcast_packets" },
+ { "tx_carrier_sense_errors" },
+ { "tx_discards" },
+ { "tx_errors" },
+
+ { "dma_writeq_full" },
+ { "dma_write_prioq_full" },
+ { "rxbds_empty" },
+ { "rx_discards" },
+ { "rx_errors" },
+ { "rx_threshold_hit" },
+
+ { "dma_readq_full" },
+ { "dma_read_prioq_full" },
+ { "tx_comp_queue_full" },
+
+ { "ring_set_send_prod_index" },
+ { "ring_status_update" },
+ { "nic_irqs" },
+ { "nic_avoided_irqs" },
+ { "nic_tx_threshold_hit" }
+};
+
+static void tg3_write_indirect_reg32(struct tg3 *tp, u32 off, u32 val)
+{
+ if ((tp->tg3_flags & TG3_FLAG_PCIX_TARGET_HWBUG) != 0) {
+ unsigned long flags;
+
+ spin_lock_irqsave(&tp->indirect_lock, flags);
+ pci_write_config_dword(tp->pdev, TG3PCI_REG_BASE_ADDR, off);
+ pci_write_config_dword(tp->pdev, TG3PCI_REG_DATA, val);
+ spin_unlock_irqrestore(&tp->indirect_lock, flags);
+ } else {
+ writel(val, tp->regs + off);
+ if ((tp->tg3_flags & TG3_FLAG_5701_REG_WRITE_BUG) != 0)
+ readl(tp->regs + off);
+ }
+}
+
+static void _tw32_flush(struct tg3 *tp, u32 off, u32 val)
+{
+ if ((tp->tg3_flags & TG3_FLAG_PCIX_TARGET_HWBUG) != 0) {
+ unsigned long flags;
+
+ spin_lock_irqsave(&tp->indirect_lock, flags);
+ pci_write_config_dword(tp->pdev, TG3PCI_REG_BASE_ADDR, off);
+ pci_write_config_dword(tp->pdev, TG3PCI_REG_DATA, val);
+ spin_unlock_irqrestore(&tp->indirect_lock, flags);
+ } else {
+ void __iomem *dest = tp->regs + off;
+ writel(val, dest);
+ readl(dest); /* always flush PCI write */
+ }
+}
+
+static inline void _tw32_rx_mbox(struct tg3 *tp, u32 off, u32 val)
+{
+ void __iomem *mbox = tp->regs + off;
+ writel(val, mbox);
+ if (tp->tg3_flags & TG3_FLAG_MBOX_WRITE_REORDER)
+ readl(mbox);
+}
+
+static inline void _tw32_tx_mbox(struct tg3 *tp, u32 off, u32 val)
+{
+ void __iomem *mbox = tp->regs + off;
+ writel(val, mbox);
+ if (tp->tg3_flags & TG3_FLAG_TXD_MBOX_HWBUG)
+ writel(val, mbox);
+ if (tp->tg3_flags & TG3_FLAG_MBOX_WRITE_REORDER)
+ readl(mbox);
+}
+
+#define tw32_mailbox(reg, val) writel(((val) & 0xffffffff), tp->regs + (reg))
+#define tw32_rx_mbox(reg, val) _tw32_rx_mbox(tp, reg, val)
+#define tw32_tx_mbox(reg, val) _tw32_tx_mbox(tp, reg, val)
+
+#define tw32(reg,val) tg3_write_indirect_reg32(tp,(reg),(val))
+#define tw32_f(reg,val) _tw32_flush(tp,(reg),(val))
+#define tw16(reg,val) writew(((val) & 0xffff), tp->regs + (reg))
+#define tw8(reg,val) writeb(((val) & 0xff), tp->regs + (reg))
+#define tr32(reg) readl(tp->regs + (reg))
+#define tr16(reg) readw(tp->regs + (reg))
+#define tr8(reg) readb(tp->regs + (reg))
+
+static void tg3_write_mem(struct tg3 *tp, u32 off, u32 val)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&tp->indirect_lock, flags);
+ pci_write_config_dword(tp->pdev, TG3PCI_MEM_WIN_BASE_ADDR, off);
+ pci_write_config_dword(tp->pdev, TG3PCI_MEM_WIN_DATA, val);
+
+ /* Always leave this as zero. */
+ pci_write_config_dword(tp->pdev, TG3PCI_MEM_WIN_BASE_ADDR, 0);
+ spin_unlock_irqrestore(&tp->indirect_lock, flags);
+}
+
+static void tg3_read_mem(struct tg3 *tp, u32 off, u32 *val)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&tp->indirect_lock, flags);
+ pci_write_config_dword(tp->pdev, TG3PCI_MEM_WIN_BASE_ADDR, off);
+ pci_read_config_dword(tp->pdev, TG3PCI_MEM_WIN_DATA, val);
+
+ /* Always leave this as zero. */
+ pci_write_config_dword(tp->pdev, TG3PCI_MEM_WIN_BASE_ADDR, 0);
+ spin_unlock_irqrestore(&tp->indirect_lock, flags);
+}
+
+static void tg3_disable_ints(struct tg3 *tp)
+{
+ tw32(TG3PCI_MISC_HOST_CTRL,
+ (tp->misc_host_ctrl | MISC_HOST_CTRL_MASK_PCI_INT));
+ tw32_mailbox(MAILBOX_INTERRUPT_0 + TG3_64BIT_REG_LOW, 0x00000001);
+ tr32(MAILBOX_INTERRUPT_0 + TG3_64BIT_REG_LOW);
+}
+
+static inline void tg3_cond_int(struct tg3 *tp)
+{
+ if (tp->hw_status->status & SD_STATUS_UPDATED)
+ tw32(GRC_LOCAL_CTRL, tp->grc_local_ctrl | GRC_LCLCTRL_SETINT);
+}
+
+static void tg3_enable_ints(struct tg3 *tp)
+{
+ tw32(TG3PCI_MISC_HOST_CTRL,
+ (tp->misc_host_ctrl & ~MISC_HOST_CTRL_MASK_PCI_INT));
+ tw32_mailbox(MAILBOX_INTERRUPT_0 + TG3_64BIT_REG_LOW, 0x00000000);
+ tr32(MAILBOX_INTERRUPT_0 + TG3_64BIT_REG_LOW);
+
+ tg3_cond_int(tp);
+}
+
+/* tg3_restart_ints
+ * similar to tg3_enable_ints, but it can return without flushing the
+ * PIO write which reenables interrupts
+ */
+static void tg3_restart_ints(struct tg3 *tp)
+{
+ tw32(TG3PCI_MISC_HOST_CTRL,
+ (tp->misc_host_ctrl & ~MISC_HOST_CTRL_MASK_PCI_INT));
+ tw32_mailbox(MAILBOX_INTERRUPT_0 + TG3_64BIT_REG_LOW, 0x00000000);
+ mmiowb();
+
+ tg3_cond_int(tp);
+}
+
+static inline void tg3_netif_stop(struct tg3 *tp)
+{
+ netif_poll_disable(tp->dev);
+ netif_tx_disable(tp->dev);
+}
+
+static inline void tg3_netif_start(struct tg3 *tp)
+{
+ netif_wake_queue(tp->dev);
+ /* NOTE: unconditional netif_wake_queue is only appropriate
+ * so long as all callers are assured to have free tx slots
+ * (such as after tg3_init_hw)
+ */
+ netif_poll_enable(tp->dev);
+ tg3_cond_int(tp);
+}
+
+static void tg3_switch_clocks(struct tg3 *tp)
+{
+ u32 clock_ctrl = tr32(TG3PCI_CLOCK_CTRL);
+ u32 orig_clock_ctrl;
+
+ orig_clock_ctrl = clock_ctrl;
+ clock_ctrl &= (CLOCK_CTRL_FORCE_CLKRUN |
+ CLOCK_CTRL_CLKRUN_OENABLE |
+ 0x1f);
+ tp->pci_clock_ctrl = clock_ctrl;
+
+ if (tp->tg3_flags2 & TG3_FLG2_5705_PLUS) {
+ if (orig_clock_ctrl & CLOCK_CTRL_625_CORE) {
+ tw32_f(TG3PCI_CLOCK_CTRL,
+ clock_ctrl | CLOCK_CTRL_625_CORE);
+ udelay(40);
+ }
+ } else if ((orig_clock_ctrl & CLOCK_CTRL_44MHZ_CORE) != 0) {
+ tw32_f(TG3PCI_CLOCK_CTRL,
+ clock_ctrl |
+ (CLOCK_CTRL_44MHZ_CORE | CLOCK_CTRL_ALTCLK));
+ udelay(40);
+ tw32_f(TG3PCI_CLOCK_CTRL,
+ clock_ctrl | (CLOCK_CTRL_ALTCLK));
+ udelay(40);
+ }
+ tw32_f(TG3PCI_CLOCK_CTRL, clock_ctrl);
+ udelay(40);
+}
+
+#define PHY_BUSY_LOOPS 5000
+
+static int tg3_readphy(struct tg3 *tp, int reg, u32 *val)
+{
+ u32 frame_val;
+ unsigned int loops;
+ int ret;
+
+ if ((tp->mi_mode & MAC_MI_MODE_AUTO_POLL) != 0) {
+ tw32_f(MAC_MI_MODE,
+ (tp->mi_mode & ~MAC_MI_MODE_AUTO_POLL));
+ udelay(80);
+ }
+
+ *val = 0x0;
+
+ frame_val = ((PHY_ADDR << MI_COM_PHY_ADDR_SHIFT) &
+ MI_COM_PHY_ADDR_MASK);
+ frame_val |= ((reg << MI_COM_REG_ADDR_SHIFT) &
+ MI_COM_REG_ADDR_MASK);
+ frame_val |= (MI_COM_CMD_READ | MI_COM_START);
+
+ tw32_f(MAC_MI_COM, frame_val);
+
+ loops = PHY_BUSY_LOOPS;
+ while (loops != 0) {
+ udelay(10);
+ frame_val = tr32(MAC_MI_COM);
+
+ if ((frame_val & MI_COM_BUSY) == 0) {
+ udelay(5);
+ frame_val = tr32(MAC_MI_COM);
+ break;
+ }
+ loops -= 1;
+ }
+
+ ret = -EBUSY;
+ if (loops != 0) {
+ *val = frame_val & MI_COM_DATA_MASK;
+ ret = 0;
+ }
+
+ if ((tp->mi_mode & MAC_MI_MODE_AUTO_POLL) != 0) {
+ tw32_f(MAC_MI_MODE, tp->mi_mode);
+ udelay(80);
+ }
+
+ return ret;
+}
+
+static int tg3_writephy(struct tg3 *tp, int reg, u32 val)
+{
+ u32 frame_val;
+ unsigned int loops;
+ int ret;
+
+ if ((tp->mi_mode & MAC_MI_MODE_AUTO_POLL) != 0) {
+ tw32_f(MAC_MI_MODE,
+ (tp->mi_mode & ~MAC_MI_MODE_AUTO_POLL));
+ udelay(80);
+ }
+
+ frame_val = ((PHY_ADDR << MI_COM_PHY_ADDR_SHIFT) &
+ MI_COM_PHY_ADDR_MASK);
+ frame_val |= ((reg << MI_COM_REG_ADDR_SHIFT) &
+ MI_COM_REG_ADDR_MASK);
+ frame_val |= (val & MI_COM_DATA_MASK);
+ frame_val |= (MI_COM_CMD_WRITE | MI_COM_START);
+
+ tw32_f(MAC_MI_COM, frame_val);
+
+ loops = PHY_BUSY_LOOPS;
+ while (loops != 0) {
+ udelay(10);
+ frame_val = tr32(MAC_MI_COM);
+ if ((frame_val & MI_COM_BUSY) == 0) {
+ udelay(5);
+ frame_val = tr32(MAC_MI_COM);
+ break;
+ }
+ loops -= 1;
+ }
+
+ ret = -EBUSY;
+ if (loops != 0)
+ ret = 0;
+
+ if ((tp->mi_mode & MAC_MI_MODE_AUTO_POLL) != 0) {
+ tw32_f(MAC_MI_MODE, tp->mi_mode);
+ udelay(80);
+ }
+
+ return ret;
+}
+
+static void tg3_phy_set_wirespeed(struct tg3 *tp)
+{
+ u32 val;
+
+ if (tp->tg3_flags2 & TG3_FLG2_NO_ETH_WIRE_SPEED)
+ return;
+
+ if (!tg3_writephy(tp, MII_TG3_AUX_CTRL, 0x7007) &&
+ !tg3_readphy(tp, MII_TG3_AUX_CTRL, &val))
+ tg3_writephy(tp, MII_TG3_AUX_CTRL,
+ (val | (1 << 15) | (1 << 4)));
+}
+
+static int tg3_bmcr_reset(struct tg3 *tp)
+{
+ u32 phy_control;
+ int limit, err;
+
+ /* OK, reset it, and poll the BMCR_RESET bit until it
+ * clears or we time out.
+ */
+ phy_control = BMCR_RESET;
+ err = tg3_writephy(tp, MII_BMCR, phy_control);
+ if (err != 0)
+ return -EBUSY;
+
+ limit = 5000;
+ while (limit--) {
+ err = tg3_readphy(tp, MII_BMCR, &phy_control);
+ if (err != 0)
+ return -EBUSY;
+
+ if ((phy_control & BMCR_RESET) == 0) {
+ udelay(40);
+ break;
+ }
+ udelay(10);
+ }
+ if (limit <= 0)
+ return -EBUSY;
+
+ return 0;
+}
+
+static int tg3_wait_macro_done(struct tg3 *tp)
+{
+ int limit = 100;
+
+ while (limit--) {
+ u32 tmp32;
+
+ if (!tg3_readphy(tp, 0x16, &tmp32)) {
+ if ((tmp32 & 0x1000) == 0)
+ break;
+ }
+ }
+ if (limit <= 0)
+ return -EBUSY;
+
+ return 0;
+}
+
+static int tg3_phy_write_and_check_testpat(struct tg3 *tp, int *resetp)
+{
+ static const u32 test_pat[4][6] = {
+ { 0x00005555, 0x00000005, 0x00002aaa, 0x0000000a, 0x00003456, 0x00000003 },
+ { 0x00002aaa, 0x0000000a, 0x00003333, 0x00000003, 0x0000789a, 0x00000005 },
+ { 0x00005a5a, 0x00000005, 0x00002a6a, 0x0000000a, 0x00001bcd, 0x00000003 },
+ { 0x00002a5a, 0x0000000a, 0x000033c3, 0x00000003, 0x00002ef1, 0x00000005 }
+ };
+ int chan;
+
+ for (chan = 0; chan < 4; chan++) {
+ int i;
+
+ tg3_writephy(tp, MII_TG3_DSP_ADDRESS,
+ (chan * 0x2000) | 0x0200);
+ tg3_writephy(tp, 0x16, 0x0002);
+
+ for (i = 0; i < 6; i++)
+ tg3_writephy(tp, MII_TG3_DSP_RW_PORT,
+ test_pat[chan][i]);
+
+ tg3_writephy(tp, 0x16, 0x0202);
+ if (tg3_wait_macro_done(tp)) {
+ *resetp = 1;
+ return -EBUSY;
+ }
+
+ tg3_writephy(tp, MII_TG3_DSP_ADDRESS,
+ (chan * 0x2000) | 0x0200);
+ tg3_writephy(tp, 0x16, 0x0082);
+ if (tg3_wait_macro_done(tp)) {
+ *resetp = 1;
+ return -EBUSY;
+ }
+
+ tg3_writephy(tp, 0x16, 0x0802);
+ if (tg3_wait_macro_done(tp)) {
+ *resetp = 1;
+ return -EBUSY;
+ }
+
+ for (i = 0; i < 6; i += 2) {
+ u32 low, high;
+
+ if (tg3_readphy(tp, MII_TG3_DSP_RW_PORT, &low) ||
+ tg3_readphy(tp, MII_TG3_DSP_RW_PORT, &high) ||
+ tg3_wait_macro_done(tp)) {
+ *resetp = 1;
+ return -EBUSY;
+ }
+ low &= 0x7fff;
+ high &= 0x000f;
+ if (low != test_pat[chan][i] ||
+ high != test_pat[chan][i+1]) {
+ tg3_writephy(tp, MII_TG3_DSP_ADDRESS, 0x000b);
+ tg3_writephy(tp, MII_TG3_DSP_RW_PORT, 0x4001);
+ tg3_writephy(tp, MII_TG3_DSP_RW_PORT, 0x4005);
+
+ return -EBUSY;
+ }
+ }
+ }
+
+ return 0;
+}
+
+static int tg3_phy_reset_chanpat(struct tg3 *tp)
+{
+ int chan;
+
+ for (chan = 0; chan < 4; chan++) {
+ int i;
+
+ tg3_writephy(tp, MII_TG3_DSP_ADDRESS,
+ (chan * 0x2000) | 0x0200);
+ tg3_writephy(tp, 0x16, 0x0002);
+ for (i = 0; i < 6; i++)
+ tg3_writephy(tp, MII_TG3_DSP_RW_PORT, 0x000);
+ tg3_writephy(tp, 0x16, 0x0202);
+ if (tg3_wait_macro_done(tp))
+ return -EBUSY;
+ }
+
+ return 0;
+}
+
+static int tg3_phy_reset_5703_4_5(struct tg3 *tp)
+{
+ u32 reg32, phy9_orig;
+ int retries, do_phy_reset, err;
+
+ retries = 10;
+ do_phy_reset = 1;
+ do {
+ if (do_phy_reset) {
+ err = tg3_bmcr_reset(tp);
+ if (err)
+ return err;
+ do_phy_reset = 0;
+ }
+
+ /* Disable transmitter and interrupt. */
+ if (tg3_readphy(tp, MII_TG3_EXT_CTRL, &reg32))
+ continue;
+
+ reg32 |= 0x3000;
+ tg3_writephy(tp, MII_TG3_EXT_CTRL, reg32);
+
+ /* Set full-duplex, 1000 mbps. */
+ tg3_writephy(tp, MII_BMCR,
+ BMCR_FULLDPLX | TG3_BMCR_SPEED1000);
+
+ /* Set to master mode. */
+ if (tg3_readphy(tp, MII_TG3_CTRL, &phy9_orig))
+ continue;
+
+ tg3_writephy(tp, MII_TG3_CTRL,
+ (MII_TG3_CTRL_AS_MASTER |
+ MII_TG3_CTRL_ENABLE_AS_MASTER));
+
+ /* Enable SM_DSP_CLOCK and 6dB. */
+ tg3_writephy(tp, MII_TG3_AUX_CTRL, 0x0c00);
+
+ /* Block the PHY control access. */
+ tg3_writephy(tp, MII_TG3_DSP_ADDRESS, 0x8005);
+ tg3_writephy(tp, MII_TG3_DSP_RW_PORT, 0x0800);
+
+ err = tg3_phy_write_and_check_testpat(tp, &do_phy_reset);
+ if (!err)
+ break;
+ } while (--retries);
+
+ err = tg3_phy_reset_chanpat(tp);
+ if (err)
+ return err;
+
+ tg3_writephy(tp, MII_TG3_DSP_ADDRESS, 0x8005);
+ tg3_writephy(tp, MII_TG3_DSP_RW_PORT, 0x0000);
+
+ tg3_writephy(tp, MII_TG3_DSP_ADDRESS, 0x8200);
+ tg3_writephy(tp, 0x16, 0x0000);
+
+ if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5703 ||
+ GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5704) {
+ /* Set Extended packet length bit for jumbo frames */
+ tg3_writephy(tp, MII_TG3_AUX_CTRL, 0x4400);
+ }
+ else {
+ tg3_writephy(tp, MII_TG3_AUX_CTRL, 0x0400);
+ }
+
+ tg3_writephy(tp, MII_TG3_CTRL, phy9_orig);
+
+ if (!tg3_readphy(tp, MII_TG3_EXT_CTRL, &reg32)) {
+ reg32 &= ~0x3000;
+ tg3_writephy(tp, MII_TG3_EXT_CTRL, reg32);
+ } else if (!err)
+ err = -EBUSY;
+
+ return err;
+}
+
+/* This will reset the tigon3 PHY if there is no valid
+ * link unless the FORCE argument is non-zero.
+ */
+static int tg3_phy_reset(struct tg3 *tp)
+{
+ u32 phy_status;
+ int err;
+
+ err = tg3_readphy(tp, MII_BMSR, &phy_status);
+ err |= tg3_readphy(tp, MII_BMSR, &phy_status);
+ if (err != 0)
+ return -EBUSY;
+
+ if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5703 ||
+ GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5704 ||
+ GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5705) {
+ err = tg3_phy_reset_5703_4_5(tp);
+ if (err)
+ return err;
+ goto out;
+ }
+
+ err = tg3_bmcr_reset(tp);
+ if (err)
+ return err;
+
+out:
+ if (tp->tg3_flags2 & TG3_FLG2_PHY_ADC_BUG) {
+ tg3_writephy(tp, MII_TG3_AUX_CTRL, 0x0c00);
+ tg3_writephy(tp, MII_TG3_DSP_ADDRESS, 0x201f);
+ tg3_writephy(tp, MII_TG3_DSP_RW_PORT, 0x2aaa);
+ tg3_writephy(tp, MII_TG3_DSP_ADDRESS, 0x000a);
+ tg3_writephy(tp, MII_TG3_DSP_RW_PORT, 0x0323);
+ tg3_writephy(tp, MII_TG3_AUX_CTRL, 0x0400);
+ }
+ if (tp->tg3_flags2 & TG3_FLG2_PHY_5704_A0_BUG) {
+ tg3_writephy(tp, 0x1c, 0x8d68);
+ tg3_writephy(tp, 0x1c, 0x8d68);
+ }
+ if (tp->tg3_flags2 & TG3_FLG2_PHY_BER_BUG) {
+ tg3_writephy(tp, MII_TG3_AUX_CTRL, 0x0c00);
+ tg3_writephy(tp, MII_TG3_DSP_ADDRESS, 0x000a);
+ tg3_writephy(tp, MII_TG3_DSP_RW_PORT, 0x310b);
+ tg3_writephy(tp, MII_TG3_DSP_ADDRESS, 0x201f);
+ tg3_writephy(tp, MII_TG3_DSP_RW_PORT, 0x9506);
+ tg3_writephy(tp, MII_TG3_DSP_ADDRESS, 0x401f);
+ tg3_writephy(tp, MII_TG3_DSP_RW_PORT, 0x14e2);
+ tg3_writephy(tp, MII_TG3_AUX_CTRL, 0x0400);
+ }
+ /* Set Extended packet length bit (bit 14) on all chips that */
+ /* support jumbo frames */
+ if ((tp->phy_id & PHY_ID_MASK) == PHY_ID_BCM5401) {
+ /* Cannot do read-modify-write on 5401 */
+ tg3_writephy(tp, MII_TG3_AUX_CTRL, 0x4c20);
+ } else if (GET_ASIC_REV(tp->pci_chip_rev_id) != ASIC_REV_5705 &&
+ GET_ASIC_REV(tp->pci_chip_rev_id) != ASIC_REV_5750) {
+ u32 phy_reg;
+
+ /* Set bit 14 with read-modify-write to preserve other bits */
+ if (!tg3_writephy(tp, MII_TG3_AUX_CTRL, 0x0007) &&
+ !tg3_readphy(tp, MII_TG3_AUX_CTRL, &phy_reg))
+ tg3_writephy(tp, MII_TG3_AUX_CTRL, phy_reg | 0x4000);
+ }
+
+ /* Set phy register 0x10 bit 0 to high fifo elasticity to support
+ * jumbo frames transmission.
+ */
+ if (GET_ASIC_REV(tp->pci_chip_rev_id) != ASIC_REV_5705 &&
+ GET_ASIC_REV(tp->pci_chip_rev_id) != ASIC_REV_5750) {
+ u32 phy_reg;
+
+ if (!tg3_readphy(tp, MII_TG3_EXT_CTRL, &phy_reg))
+ tg3_writephy(tp, MII_TG3_EXT_CTRL,
+ phy_reg | MII_TG3_EXT_CTRL_FIFO_ELASTIC);
+ }
+
+ tg3_phy_set_wirespeed(tp);
+ return 0;
+}
+
+static void tg3_frob_aux_power(struct tg3 *tp)
+{
+ struct tg3 *tp_peer = tp;
+
+ if ((tp->tg3_flags & TG3_FLAG_EEPROM_WRITE_PROT) != 0)
+ return;
+
+ if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5704) {
+ tp_peer = pci_get_drvdata(tp->pdev_peer);
+ if (!tp_peer)
+ BUG();
+ }
+
+
+ if ((tp->tg3_flags & TG3_FLAG_WOL_ENABLE) != 0 ||
+ (tp_peer->tg3_flags & TG3_FLAG_WOL_ENABLE) != 0) {
+ if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5700 ||
+ GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5701) {
+ tw32_f(GRC_LOCAL_CTRL, tp->grc_local_ctrl |
+ (GRC_LCLCTRL_GPIO_OE0 |
+ GRC_LCLCTRL_GPIO_OE1 |
+ GRC_LCLCTRL_GPIO_OE2 |
+ GRC_LCLCTRL_GPIO_OUTPUT0 |
+ GRC_LCLCTRL_GPIO_OUTPUT1));
+ udelay(100);
+ } else {
+ u32 no_gpio2;
+ u32 grc_local_ctrl;
+
+ if (tp_peer != tp &&
+ (tp_peer->tg3_flags & TG3_FLAG_INIT_COMPLETE) != 0)
+ return;
+
+ /* On 5753 and variants, GPIO2 cannot be used. */
+ no_gpio2 = tp->nic_sram_data_cfg &
+ NIC_SRAM_DATA_CFG_NO_GPIO2;
+
+ grc_local_ctrl = GRC_LCLCTRL_GPIO_OE0 |
+ GRC_LCLCTRL_GPIO_OE1 |
+ GRC_LCLCTRL_GPIO_OE2 |
+ GRC_LCLCTRL_GPIO_OUTPUT1 |
+ GRC_LCLCTRL_GPIO_OUTPUT2;
+ if (no_gpio2) {
+ grc_local_ctrl &= ~(GRC_LCLCTRL_GPIO_OE2 |
+ GRC_LCLCTRL_GPIO_OUTPUT2);
+ }
+ tw32_f(GRC_LOCAL_CTRL, tp->grc_local_ctrl |
+ grc_local_ctrl);
+ udelay(100);
+
+ grc_local_ctrl |= GRC_LCLCTRL_GPIO_OUTPUT0;
+
+ tw32_f(GRC_LOCAL_CTRL, tp->grc_local_ctrl |
+ grc_local_ctrl);
+ udelay(100);
+
+ if (!no_gpio2) {
+ grc_local_ctrl &= ~GRC_LCLCTRL_GPIO_OUTPUT2;
+ tw32_f(GRC_LOCAL_CTRL, tp->grc_local_ctrl |
+ grc_local_ctrl);
+ udelay(100);
+ }
+ }
+ } else {
+ if (GET_ASIC_REV(tp->pci_chip_rev_id) != ASIC_REV_5700 &&
+ GET_ASIC_REV(tp->pci_chip_rev_id) != ASIC_REV_5701) {
+ if (tp_peer != tp &&
+ (tp_peer->tg3_flags & TG3_FLAG_INIT_COMPLETE) != 0)
+ return;
+
+ tw32_f(GRC_LOCAL_CTRL, tp->grc_local_ctrl |
+ (GRC_LCLCTRL_GPIO_OE1 |
+ GRC_LCLCTRL_GPIO_OUTPUT1));
+ udelay(100);
+
+ tw32_f(GRC_LOCAL_CTRL, tp->grc_local_ctrl |
+ (GRC_LCLCTRL_GPIO_OE1));
+ udelay(100);
+
+ tw32_f(GRC_LOCAL_CTRL, tp->grc_local_ctrl |
+ (GRC_LCLCTRL_GPIO_OE1 |
+ GRC_LCLCTRL_GPIO_OUTPUT1));
+ udelay(100);
+ }
+ }
+}
+
+static int tg3_setup_phy(struct tg3 *, int);
+
+#define RESET_KIND_SHUTDOWN 0
+#define RESET_KIND_INIT 1
+#define RESET_KIND_SUSPEND 2
+
+static void tg3_write_sig_post_reset(struct tg3 *, int);
+static int tg3_halt_cpu(struct tg3 *, u32);
+
+static int tg3_set_power_state(struct tg3 *tp, int state)
+{
+ u32 misc_host_ctrl;
+ u16 power_control, power_caps;
+ int pm = tp->pm_cap;
+
+ /* Make sure register accesses (indirect or otherwise)
+ * will function correctly.
+ */
+ pci_write_config_dword(tp->pdev,
+ TG3PCI_MISC_HOST_CTRL,
+ tp->misc_host_ctrl);
+
+ pci_read_config_word(tp->pdev,
+ pm + PCI_PM_CTRL,
+ &power_control);
+ power_control |= PCI_PM_CTRL_PME_STATUS;
+ power_control &= ~(PCI_PM_CTRL_STATE_MASK);
+ switch (state) {
+ case 0:
+ power_control |= 0;
+ pci_write_config_word(tp->pdev,
+ pm + PCI_PM_CTRL,
+ power_control);
+ tw32_f(GRC_LOCAL_CTRL, tp->grc_local_ctrl);
+ udelay(100);
+
+ return 0;
+
+ case 1:
+ power_control |= 1;
+ break;
+
+ case 2:
+ power_control |= 2;
+ break;
+
+ case 3:
+ power_control |= 3;
+ break;
+
+ default:
+ printk(KERN_WARNING PFX "%s: Invalid power state (%d) "
+ "requested.\n",
+ tp->dev->name, state);
+ return -EINVAL;
+ };
+
+ power_control |= PCI_PM_CTRL_PME_ENABLE;
+
+ misc_host_ctrl = tr32(TG3PCI_MISC_HOST_CTRL);
+ tw32(TG3PCI_MISC_HOST_CTRL,
+ misc_host_ctrl | MISC_HOST_CTRL_MASK_PCI_INT);
+
+ if (tp->link_config.phy_is_low_power == 0) {
+ tp->link_config.phy_is_low_power = 1;
+ 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->tg3_flags2 & TG3_FLG2_PHY_SERDES)) {
+ tp->link_config.speed = SPEED_10;
+ tp->link_config.duplex = DUPLEX_HALF;
+ tp->link_config.autoneg = AUTONEG_ENABLE;
+ tg3_setup_phy(tp, 0);
+ }
+
+ pci_read_config_word(tp->pdev, pm + PCI_PM_PMC, &power_caps);
+
+ if (tp->tg3_flags & TG3_FLAG_WOL_ENABLE) {
+ u32 mac_mode;
+
+ if (!(tp->tg3_flags2 & TG3_FLG2_PHY_SERDES)) {
+ tg3_writephy(tp, MII_TG3_AUX_CTRL, 0x5a);
+ udelay(40);
+
+ mac_mode = MAC_MODE_PORT_MODE_MII;
+
+ if (GET_ASIC_REV(tp->pci_chip_rev_id) != ASIC_REV_5700 ||
+ !(tp->tg3_flags & TG3_FLAG_WOL_SPEED_100MB))
+ mac_mode |= MAC_MODE_LINK_POLARITY;
+ } else {
+ mac_mode = MAC_MODE_PORT_MODE_TBI;
+ }
+
+ if (GET_ASIC_REV(tp->pci_chip_rev_id) != ASIC_REV_5750)
+ tw32(MAC_LED_CTRL, tp->led_ctrl);
+
+ if (((power_caps & PCI_PM_CAP_PME_D3cold) &&
+ (tp->tg3_flags & TG3_FLAG_WOL_ENABLE)))
+ mac_mode |= MAC_MODE_MAGIC_PKT_ENABLE;
+
+ tw32_f(MAC_MODE, mac_mode);
+ udelay(100);
+
+ tw32_f(MAC_RX_MODE, RX_MODE_ENABLE);
+ udelay(10);
+ }
+
+ if (!(tp->tg3_flags & TG3_FLAG_WOL_SPEED_100MB) &&
+ (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5700 ||
+ GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5701)) {
+ u32 base_val;
+
+ base_val = tp->pci_clock_ctrl;
+ base_val |= (CLOCK_CTRL_RXCLK_DISABLE |
+ CLOCK_CTRL_TXCLK_DISABLE);
+
+ tw32_f(TG3PCI_CLOCK_CTRL, base_val |
+ CLOCK_CTRL_ALTCLK |
+ CLOCK_CTRL_PWRDOWN_PLL133);
+ udelay(40);
+ } else if (!((GET_ASIC_REV(tp->pci_chip_rev_id) == 5750) &&
+ (tp->tg3_flags & TG3_FLAG_ENABLE_ASF))) {
+ u32 newbits1, newbits2;
+
+ if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5700 ||
+ GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5701) {
+ newbits1 = (CLOCK_CTRL_RXCLK_DISABLE |
+ CLOCK_CTRL_TXCLK_DISABLE |
+ CLOCK_CTRL_ALTCLK);
+ newbits2 = newbits1 | CLOCK_CTRL_44MHZ_CORE;
+ } else if (tp->tg3_flags2 & TG3_FLG2_5705_PLUS) {
+ newbits1 = CLOCK_CTRL_625_CORE;
+ newbits2 = newbits1 | CLOCK_CTRL_ALTCLK;
+ } else {
+ newbits1 = CLOCK_CTRL_ALTCLK;
+ newbits2 = newbits1 | CLOCK_CTRL_44MHZ_CORE;
+ }
+
+ tw32_f(TG3PCI_CLOCK_CTRL, tp->pci_clock_ctrl | newbits1);
+ udelay(40);
+
+ tw32_f(TG3PCI_CLOCK_CTRL, tp->pci_clock_ctrl | newbits2);
+ udelay(40);
+
+ if (!(tp->tg3_flags2 & TG3_FLG2_5705_PLUS)) {
+ u32 newbits3;
+
+ if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5700 ||
+ GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5701) {
+ newbits3 = (CLOCK_CTRL_RXCLK_DISABLE |
+ CLOCK_CTRL_TXCLK_DISABLE |
+ CLOCK_CTRL_44MHZ_CORE);
+ } else {
+ newbits3 = CLOCK_CTRL_44MHZ_CORE;
+ }
+
+ tw32_f(TG3PCI_CLOCK_CTRL,
+ tp->pci_clock_ctrl | newbits3);
+ udelay(40);
+ }
+ }
+
+ tg3_frob_aux_power(tp);
+
+ /* Workaround for unstable PLL clock */
+ if ((GET_CHIP_REV(tp->pci_chip_rev_id) == CHIPREV_5750_AX) ||
+ (GET_CHIP_REV(tp->pci_chip_rev_id) == CHIPREV_5750_BX)) {
+ u32 val = tr32(0x7d00);
+
+ val &= ~((1 << 16) | (1 << 4) | (1 << 2) | (1 << 1) | 1);
+ tw32(0x7d00, val);
+ if (!(tp->tg3_flags & TG3_FLAG_ENABLE_ASF))
+ tg3_halt_cpu(tp, RX_CPU_BASE);
+ }
+
+ /* Finally, set the new power state. */
+ pci_write_config_word(tp->pdev, pm + PCI_PM_CTRL, power_control);
+
+ tg3_write_sig_post_reset(tp, RESET_KIND_SHUTDOWN);
+
+ return 0;
+}
+
+static void tg3_link_report(struct tg3 *tp)
+{
+ if (!netif_carrier_ok(tp->dev)) {
+ printk(KERN_INFO PFX "%s: Link is down.\n", tp->dev->name);
+ } else {
+ printk(KERN_INFO PFX "%s: