diff options
Diffstat (limited to 'drivers/net/tg3.c')
-rw-r--r-- | drivers/net/tg3.c | 9083 |
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, ®32)) + 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, ®32)) { + 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: |