| /* |
| * 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: |
| * Derived from proprietary unpublished source code, |
| * Copyright (C) 2000-2003 Broadcom Corporation. |
| * |
| * Permission is hereby granted for the distribution of this firmware |
| * data in hexadecimal or equivalent format, provided this copyright |
| * notice is accompanying it. |
| */ |
| |
| #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.31" |
| #define DRV_MODULE_RELDATE "June 8, 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) \ |
| (!(tp->tg3_flags2 & TG3_FLG2_5705_PLUS) ? 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)) |
| |
| #define TG3_NUM_TEST 6 |
| |
| 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_5752, |
| PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0UL }, |
| { PCI_VENDOR_ID_BROADCOM, PCI_DEVICE_ID_TIGON3_5752M, |
| 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 struct { |
| const char string[ETH_GSTRING_LEN]; |
| } ethtool_test_keys[TG3_NUM_TEST] = { |
| { "nvram test (online) " }, |
| { "link test (online) " }, |
| { "register test (offline)" }, |
| { "memory test (offline)" }, |
| { "loopback test (offline)" }, |
| { "interrupt test (offline)" }, |
| }; |
| |
| 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, |
| (tp->last_tag << 24)); |
| tr32(MAILBOX_INTERRUPT_0 + TG3_64BIT_REG_LOW); |
| |
| tg3_cond_int(tp); |
| } |
| |
| static inline unsigned int tg3_has_work(struct tg3 *tp) |
| { |
| struct tg3_hw_status *sblk = tp->hw_status; |
| unsigned int work_exists = 0; |
| |
| /* check for phy events */ |
| if (!(tp->tg3_flags & |
| (TG3_FLAG_USE_LINKCHG_REG | |
| TG3_FLAG_POLL_SERDES))) { |
| if (sblk->status & SD_STATUS_LINK_CHG) |
| work_exists = 1; |
| } |
| /* check for RX/TX work to do */ |
| if (sblk->idx[0].tx_consumer != tp->tx_cons || |
| sblk->idx[0].rx_producer != tp->rx_rcb_ptr) |
| work_exists = 1; |
| |
| return work_exists; |
| } |
| |
| /* tg3_restart_ints |
| * similar to tg3_enable_ints, but it accurately determines whether there |
| * is new work pending and 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, |
| tp->last_tag << 24); |
| mmiowb(); |
| |
| /* When doing tagged status, this work check is unnecessary. |
| * The last_tag we write above tells the chip which piece of |
| * work we've completed. |
| */ |
| if (!(tp->tg3_flags & TG3_FLAG_TAGGED_STATUS) && |
| tg3_has_work(tp)) |
| tw32(HOSTCC_MODE, tp->coalesce_mode | |
| (HOSTCC_MODE_ENABLE | HOSTCC_MODE_NOW)); |
| } |
| |
| 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 (!(tp->tg3_flags2 & TG3_FLG2_5705_PLUS)) { |
| 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 (!(tp->tg3_flags2 & TG3_FLG2_5705_PLUS)) { |
| 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); |
| udelay(100); /* Delay after power state change */ |
| |
| /* Switch out of Vaux if it is not a LOM */ |
| if (!(tp->tg3_flags & TG3_FLAG_EEPROM_WRITE_PROT)) { |
| 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 (!(tp->tg3_flags2 & TG3_FLG2_5750_PLUS)) |
| 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 (!((tp->tg3_flags2 & TG3_FLG2_5750_PLUS) && |
| (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); |
| udelay(100); /* Delay after power state change */ |
| |
| 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: Link is up at %d Mbps, %s duplex.\n", |
| tp->dev->name, |
| (tp->link_config.active_speed == SPEED_1000 ? |
| 1000 : |
| (tp->link_config.active_speed == SPEED_100 ? |
| 100 : 10)), |
| (tp->link_config.active_duplex == DUPLEX_FULL ? |
| "full" : "half")); |
| |
| printk(KERN_INFO PFX "%s: Flow control is %s for TX and " |
| "%s for RX.\n", |
| tp->dev->name, |
| (tp->tg3_flags & TG3_FLAG_TX_PAUSE) ? "on" : "off", |
| (tp->tg3_flags & TG3_FLAG_RX_PAUSE) ? "on" : "off"); |
| } |
| } |
| |
| static void tg3_setup_flow_control(struct tg3 *tp, u32 local_adv, u32 remote_adv) |
| { |
| u32 new_tg3_flags = 0; |
| u32 old_rx_mode = tp->rx_mode; |
| u32 old_tx_mode = tp->tx_mode; |
| |
| if (tp->tg3_flags & TG3_FLAG_PAUSE_AUTONEG) { |
| if (local_adv & ADVERTISE_PAUSE_CAP) { |
| if (local_adv & ADVERTISE_PAUSE_ASYM) { |
| if (remote_adv & LPA_PAUSE_CAP) |
| new_tg3_flags |= |
| (TG3_FLAG_RX_PAUSE | |
| TG3_FLAG_TX_PAUSE); |
| else if (remote_adv & LPA_PAUSE_ASYM) |
| new_tg3_flags |= |
| (TG3_FLAG_RX_PAUSE); |
| } else { |
| if (remote_adv & LPA_PAUSE_CAP) |
| new_tg3_flags |= |
| (TG3_FLAG_RX_PAUSE | |
| TG3_FLAG_TX_PAUSE); |
| } |
| } else if (local_adv & ADVERTISE_PAUSE_ASYM) { |
| if ((remote_adv & LPA_PAUSE_CAP) && |
| (remote_adv & LPA_PAUSE_ASYM)) |
| new_tg3_flags |= TG3_FLAG_TX_PAUSE; |
| } |
| |
| tp->tg3_flags &= ~(TG3_FLAG_RX_PAUSE | TG3_FLAG_TX_PAUSE); |
| tp->tg3_flags |= new_tg3_flags; |
| } else { |
| new_tg3_flags = tp->tg3_flags; |
| } |
| |
| if (new_tg3_flags & TG3_FLAG_RX_PAUSE) |
| tp->rx_mode |= RX_MODE_FLOW_CTRL_ENABLE; |
| else |
| tp->rx_mode &= ~RX_MODE_FLOW_CTRL_ENABLE; |
| |
| if (old_rx_mode != tp->rx_mode) { |
| tw32_f(MAC_RX_MODE, tp->rx_mode); |
| } |
| |
| if (new_tg3_flags & TG3_FLAG_TX_PAUSE) |
| tp->tx_mode |= TX_MODE_FLOW_CTRL_ENABLE; |
| else |
| tp->tx_mode &= ~TX_MODE_FLOW_CTRL_ENABLE; |
| |
| if (old_tx_mode != tp->tx_mode) { |
| tw32_f(MAC_TX_MODE, tp->tx_mode); |
| } |
| } |
| |
| static void tg3_aux_stat_to_speed_duplex(struct tg3 *tp, u32 val, u16 *speed, u8 *duplex) |
| { |
| switch (val & MII_TG3_AUX_STAT_SPDMASK) { |
| case MII_TG3_AUX_STAT_10HALF: |
| *speed = SPEED_10; |
| *duplex = DUPLEX_HALF; |
| break; |
| |
| case MII_TG3_AUX_STAT_10FULL: |
| *speed = SPEED_10; |
| *duplex = DUPLEX_FULL; |
| break; |
| |
| case MII_TG3_AUX_STAT_100HALF: |
| *speed = SPEED_100; |
| *duplex = DUPLEX_HALF; |
| break; |
| |
| case MII_TG3_AUX_STAT_100FULL: |
| *speed = SPEED_100; |
| *duplex = DUPLEX_FULL; |
| break; |
| |
| case MII_TG3_AUX_STAT_1000HALF: |
| *speed = SPEED_1000; |
| *duplex = DUPLEX_HALF; |
| break; |
| |
| case MII_TG3_AUX_STAT_1000FULL: |
| *speed = SPEED_1000; |
| *duplex = DUPLEX_FULL; |
| break; |
| |
| default: |
| *speed = SPEED_INVALID; |
| *duplex = DUPLEX_INVALID; |
| break; |
| }; |
| } |
| |
| static void tg3_phy_copper_begin(struct tg3 *tp) |
| { |
| u32 new_adv; |
| int i; |
| |
| if (tp->link_config.phy_is_low_power) { |
| /* Entering low power mode. Disable gigabit and |
| * 100baseT advertisements. |
| */ |
| tg3_writephy(tp, MII_TG3_CTRL, 0); |
| |
| new_adv = (ADVERTISE_10HALF | ADVERTISE_10FULL | |
| ADVERTISE_CSMA | ADVERTISE_PAUSE_CAP); |
| if (tp->tg3_flags & TG3_FLAG_WOL_SPEED_100MB) |
| new_adv |= (ADVERTISE_100HALF | ADVERTISE_100FULL); |
| |
| tg3_writephy(tp, MII_ADVERTISE, new_adv); |
| } else if (tp->link_config.speed == SPEED_INVALID) { |
| tp->link_config.advertising = |
| (ADVERTISED_10baseT_Half | ADVERTISED_10baseT_Full | |
| ADVERTISED_100baseT_Half | ADVERTISED_100baseT_Full | |
| ADVERTISED_1000baseT_Half | ADVERTISED_1000baseT_Full | |
| ADVERTISED_Autoneg | ADVERTISED_MII); |
| |
| if (tp->tg3_flags & TG3_FLAG_10_100_ONLY) |
| tp->link_config.advertising &= |
| ~(ADVERTISED_1000baseT_Half | |
| ADVERTISED_1000baseT_Full); |
| |
| new_adv = (ADVERTISE_CSMA | ADVERTISE_PAUSE_CAP); |
| if (tp->link_config.advertising & ADVERTISED_10baseT_Half) |
| new_adv |= ADVERTISE_10HALF; |
| if (tp->link_config.advertising & ADVERTISED_10baseT_Full) |
| new_adv |= ADVERTISE_10FULL; |
| if (tp->link_config.advertising & ADVERTISED_100baseT_Half) |
| new_adv |= ADVERTISE_100HALF; |
| if (tp->link_config.advertising & ADVERTISED_100baseT_Full) |
| new_adv |= ADVERTISE_100FULL; |
| tg3_writephy(tp, MII_ADVERTISE, new_adv); |
| |
| if (tp->link_config.advertising & |
| (ADVERTISED_1000baseT_Half | ADVERTISED_1000baseT_Full)) { |
| new_adv = 0; |
| if (tp->link_config.advertising & ADVERTISED_1000baseT_Half) |
| new_adv |= MII_TG3_CTRL_ADV_1000_HALF; |
| if (tp->link_config.advertising & ADVERTISED_1000baseT_Full) |
| new_adv |= MII_TG3_CTRL_ADV_1000_FULL; |
| if (!(tp->tg3_flags & TG3_FLAG_10_100_ONLY) && |
| (tp->pci_chip_rev_id == CHIPREV_ID_5701_A0 || |
| tp->pci_chip_rev_id == CHIPREV_ID_5701_B0)) |
| new_adv |= (MII_TG3_CTRL_AS_MASTER | |
| MII_TG3_CTRL_ENABLE_AS_MASTER); |
| tg3_writephy(tp, MII_TG3_CTRL, new_adv); |
| } else { |
| tg3_writephy(tp, MII_TG3_CTRL, 0); |
| } |
| } else { |
| /* Asking for a specific link mode. */ |
| if (tp->link_config.speed == SPEED_1000) { |
| new_adv = ADVERTISE_CSMA | ADVERTISE_PAUSE_CAP; |
| tg3_writephy(tp, MII_ADVERTISE, new_adv); |
| |
| if (tp->link_config.duplex == DUPLEX_FULL) |
| new_adv = MII_TG3_CTRL_ADV_1000_FULL; |
| else |
| new_adv = MII_TG3_CTRL_ADV_1000_HALF; |
| if (tp->pci_chip_rev_id == CHIPREV_ID_5701_A0 || |
| tp->pci_chip_rev_id == CHIPREV_ID_5701_B0) |
| new_adv |= (MII_TG3_CTRL_AS_MASTER | |
| MII_TG3_CTRL_ENABLE_AS_MASTER); |
| tg3_writephy(tp, MII_TG3_CTRL, new_adv); |
| } else { |
| tg3_writephy(tp, MII_TG3_CTRL, 0); |
| |
| new_adv = ADVERTISE_CSMA | ADVERTISE_PAUSE_CAP; |
| if (tp->link_config.speed == SPEED_100) { |
| if (tp->link_config.duplex == DUPLEX_FULL) |
| new_adv |= ADVERTISE_100FULL; |
| else |
| new_adv |= ADVERTISE_100HALF; |
| } else { |
| if (tp->link_config.duplex == DUPLEX_FULL) |
| new_adv |= ADVERTISE_10FULL; |
| else |
| new_adv |= ADVERTISE_10HALF; |
| } |
| tg3_writephy(tp, MII_ADVERTISE, new_adv); |
| } |
| } |
| |
| if (tp->link_config.autoneg == AUTONEG_DISABLE && |
| tp->link_config.speed != SPEED_INVALID) { |
| u32 bmcr, orig_bmcr; |
| |
| tp->link_config.active_speed = tp->link_config.speed; |
| tp->link_config.active_duplex = tp->link_config.duplex; |
| |
| bmcr = 0; |
| switch (tp->link_config.speed) { |
| default: |
| case SPEED_10: |
| break; |
| |
| case SPEED_100: |
| bmcr |= BMCR_SPEED100; |
| break; |
| |
| case SPEED_1000: |
| bmcr |= TG3_BMCR_SPEED1000; |
| break; |
| }; |
| |
| if (tp->link_config.duplex == DUPLEX_FULL) |
| bmcr |= BMCR_FULLDPLX; |
| |
| if (!tg3_readphy(tp, MII_BMCR, &orig_bmcr) && |
| (bmcr != orig_bmcr)) { |
| tg3_writephy(tp, MII_BMCR, BMCR_LOOPBACK); |
| for (i = 0; i < 1500; i++) { |
| u32 tmp; |
| |
| udelay(10); |
| if (tg3_readphy(tp, MII_BMSR, &tmp) || |
| tg3_readphy(tp, MII_BMSR, &tmp)) |
| continue; |
| if (!(tmp & BMSR_LSTATUS)) { |
| udelay(40); |
| break; |
| } |
| } |
| tg3_writephy(tp, MII_BMCR, bmcr); |
| udelay(40); |
| } |
| } else { |
| tg3_writephy(tp, MII_BMCR, |
| BMCR_ANENABLE | BMCR_ANRESTART); |
| } |
| } |
| |
| static int tg3_init_5401phy_dsp(struct tg3 *tp) |
| { |
| int err; |
| |
| /* Turn off tap power management. */ |
| /* Set Extended packet length bit */ |
| err = tg3_writephy(tp, MII_TG3_AUX_CTRL, 0x4c20); |
| |
| err |= tg3_writephy(tp, MII_TG3_DSP_ADDRESS, 0x0012); |
| err |= tg3_writephy(tp, MII_TG3_DSP_RW_PORT, 0x1804); |
| |
| err |= tg3_writephy(tp, MII_TG3_DSP_ADDRESS, 0x0013); |
| err |= tg3_writephy(tp, MII_TG3_DSP_RW_PORT, 0x1204); |
| |
| err |= tg3_writephy(tp, MII_TG3_DSP_ADDRESS, 0x8006); |
| err |= tg3_writephy(tp, MII_TG3_DSP_RW_PORT, 0x0132); |
| |
| err |= tg3_writephy(tp, MII_TG3_DSP_ADDRESS, 0x8006); |
| err |= tg3_writephy(tp, MII_TG3_DSP_RW_PORT, 0x0232); |
| |
| err |= tg3_writephy(tp, MII_TG3_DSP_ADDRESS, 0x201f); |
| err |= tg3_writephy(tp, MII_TG3_DSP_RW_PORT, 0x0a20); |
| |
| udelay(40); |
| |
| return err; |
| } |
| |
| static int tg3_copper_is_advertising_all(struct tg3 *tp) |
| { |
| u32 adv_reg, all_mask; |
| |
| if (tg3_readphy(tp, MII_ADVERTISE, &adv_reg)) |
| return 0; |
| |
| all_mask = (ADVERTISE_10HALF | ADVERTISE_10FULL | |
| ADVERTISE_100HALF | ADVERTISE_100FULL); |
| if ((adv_reg & all_mask) != all_mask) |
| return 0; |
| if (!(tp->tg3_flags & TG3_FLAG_10_100_ONLY)) { |
| u32 tg3_ctrl; |
| |
| if (tg3_readphy(tp, MII_TG3_CTRL, &tg3_ctrl)) |
| return 0; |
| |
| all_mask = (MII_TG3_CTRL_ADV_1000_HALF | |
| MII_TG3_CTRL_ADV_1000_FULL); |
| if ((tg3_ctrl & all_mask) != all_mask) |
| return 0; |
| } |
| return 1; |
| } |
| |
| static int tg3_setup_copper_phy(struct tg3 *tp, int force_reset) |
| { |
| int current_link_up; |
| u32 bmsr, dummy; |
| u16 current_speed; |
| u8 current_duplex; |
| int i, err; |
| |
| tw32(MAC_EVENT, 0); |
| |
| tw32_f(MAC_STATUS, |
| (MAC_STATUS_SYNC_CHANGED | |
| MAC_STATUS_CFG_CHANGED | |
| MAC_STATUS_MI_COMPLETION | |
| MAC_STATUS_LNKSTATE_CHANGED)); |
| udelay(40); |
| |
| tp->mi_mode = MAC_MI_MODE_BASE; |
| tw32_f(MAC_MI_MODE, tp->mi_mode); |
| udelay(80); |
| |
| tg3_writephy(tp, MII_TG3_AUX_CTRL, 0x02); |
| |
| /* Some third-party PHYs need to be reset on link going |
| * down. |
| */ |
| 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) && |
| netif_carrier_ok(tp->dev)) { |
| tg3_readphy(tp, MII_BMSR, &bmsr); |
| if (!tg3_readphy(tp, MII_BMSR, &bmsr) && |
| !(bmsr & BMSR_LSTATUS)) |
| force_reset = 1; |
| } |
| if (force_reset) |
| tg3_phy_reset(tp); |
| |
| if ((tp->phy_id & PHY_ID_MASK) == PHY_ID_BCM5401) { |
| tg3_readphy(tp, MII_BMSR, &bmsr); |
| if (tg3_readphy(tp, MII_BMSR, &bmsr) || |
| !(tp->tg3_flags & TG3_FLAG_INIT_COMPLETE)) |
| bmsr = 0; |
| |
| if (!(bmsr & BMSR_LSTATUS)) { |
| err = tg3_init_5401phy_dsp(tp); |
| if (err) |
| return err; |
| |
| tg3_readphy(tp, MII_BMSR, &bmsr); |
| for (i = 0; i < 1000; i++) { |
| udelay(10); |
| if (!tg3_readphy(tp, MII_BMSR, &bmsr) && |
| (bmsr & BMSR_LSTATUS)) { |
| udelay(40); |
| break; |
| } |
| } |
| |
| if ((tp->phy_id & PHY_ID_REV_MASK) == PHY_REV_BCM5401_B0 && |
| !(bmsr & BMSR_LSTATUS) && |
| tp->link_config.active_speed == SPEED_1000) { |
| err = tg3_phy_reset(tp); |
| if (!err) |
| err = tg3_init_5401phy_dsp(tp); |
| if (err) |
| return err; |
| } |
| } |
| } else if (tp->pci_chip_rev_id == CHIPREV_ID_5701_A0 || |
| tp->pci_chip_rev_id == CHIPREV_ID_5701_B0) { |
| /* 5701 {A0,B0} CRC bug workaround */ |
| tg3_writephy(tp, 0x15, 0x0a75); |
| tg3_writephy(tp, 0x1c, 0x8c68); |
| tg3_writephy(tp, 0x1c, 0x8d68); |
| tg3_writephy(tp, 0x1c, 0x8c68); |
| } |
| |
| /* Clear pending interrupts... */ |
| tg3_readphy(tp, MII_TG3_ISTAT, &dummy); |
| tg3_readphy(tp, MII_TG3_ISTAT, &dummy); |
| |
| if (tp->tg3_flags & TG3_FLAG_USE_MI_INTERRUPT) |
| tg3_writephy(tp, MII_TG3_IMASK, ~MII_TG3_INT_LINKCHG); |
| else |
| tg3_writephy(tp, MII_TG3_IMASK, ~0); |
| |
| 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->led_ctrl == LED_CTRL_MODE_PHY_1) |
| tg3_writephy(tp, MII_TG3_EXT_CTRL, |
| MII_TG3_EXT_CTRL_LNK3_LED_MODE); |
| else |
| tg3_writephy(tp, MII_TG3_EXT_CTRL, 0); |
| } |
| |
| current_link_up = 0; |
| current_speed = SPEED_INVALID; |
| current_duplex = DUPLEX_INVALID; |
| |
| if (tp->tg3_flags2 & TG3_FLG2_CAPACITIVE_COUPLING) { |
| u32 val; |
| |
| tg3_writephy(tp, MII_TG3_AUX_CTRL, 0x4007); |
| tg3_readphy(tp, MII_TG3_AUX_CTRL, &val); |
| if (!(val & (1 << 10))) { |
| val |= (1 << 10); |
| tg3_writephy(tp, MII_TG3_AUX_CTRL, val); |
| goto relink; |
| } |
| } |
| |
| bmsr = 0; |
| for (i = 0; i < 100; i++) { |
| tg3_readphy(tp, MII_BMSR, &bmsr); |
| if (!tg3_readphy(tp, MII_BMSR, &bmsr) && |
| (bmsr & BMSR_LSTATUS)) |
| break; |
| udelay(40); |
| } |
| |
| if (bmsr & BMSR_LSTATUS) { |
| u32 aux_stat, bmcr; |
| |
| tg3_readphy(tp, MII_TG3_AUX_STAT, &aux_stat); |
| for (i = 0; i < 2000; i++) { |
| udelay(10); |
| if (!tg3_readphy(tp, MII_TG3_AUX_STAT, &aux_stat) && |
| aux_stat) |
| break; |
| } |
| |
| tg3_aux_stat_to_speed_duplex(tp, aux_stat, |
| ¤t_speed, |
| ¤t_duplex); |
| |
| bmcr = 0; |
| for (i = 0; i < 200; i++) { |
| tg3_readphy(tp, MII_BMCR, &bmcr); |
| if (tg3_readphy(tp, MII_BMCR, &bmcr)) |
| continue; |
| if (bmcr && bmcr != 0x7fff) |
| break; |
| udelay(10); |
| } |
| |
| if (tp->link_config.autoneg == AUTONEG_ENABLE) { |
| if (bmcr & BMCR_ANENABLE) { |
| current_link_up = 1; |
| |
| /* Force autoneg restart if we are exiting |
| * low power mode. |
| */ |
| if (!tg3_copper_is_advertising_all(tp)) |
| current_link_up = 0; |
| } else { |
| current_link_up = 0; |
| } |
| } else { |
| if (!(bmcr & BMCR_ANENABLE) && |
| tp->link_config.speed == current_speed && |
| tp->link_config.duplex == current_duplex) { |
| current_link_up = 1; |
| } else { |
| current_link_up = 0; |
| } |
| } |
| |
| tp->link_config.active_speed = current_speed; |
| tp->link_config.active_duplex = current_duplex; |
| } |
| |
| if (current_link_up == 1 && |
| (tp->link_config.active_duplex == DUPLEX_FULL) && |
| (tp->link_config.autoneg == AUTONEG_ENABLE)) { |
| u32 local_adv, remote_adv; |
| |
| if (tg3_readphy(tp, MII_ADVERTISE, &local_adv)) |
| local_adv = 0; |
| local_adv &= (ADVERTISE_PAUSE_CAP | ADVERTISE_PAUSE_ASYM); |
| |
| if (tg3_readphy(tp, MII_LPA, &remote_adv)) |
| remote_adv = 0; |
| |
| remote_adv &= (LPA_PAUSE_CAP | LPA_PAUSE_ASYM); |
| |
| /* If we are not advertising full pause capability, |
| * something is wrong. Bring the link down and reconfigure. |
| */ |
| if (local_adv != ADVERTISE_PAUSE_CAP) { |
| current_link_up = 0; |
| } else { |
| tg3_setup_flow_control(tp, local_adv, remote_adv); |
| } |
| } |
| relink: |
| if (current_link_up == 0) { |
| u32 tmp; |
| |
| tg3_phy_copper_begin(tp); |
| |
| tg3_readphy(tp, MII_BMSR, &tmp); |
| if (!tg3_readphy(tp, MII_BMSR, &tmp) && |
| (tmp & BMSR_LSTATUS)) |
| current_link_up = 1; |
| } |
| |
| tp->mac_mode &= ~MAC_MODE_PORT_MODE_MASK; |
| if (current_link_up == 1) { |
| if (tp->link_config.active_speed == SPEED_100 || |
| tp->link_config.active_speed == SPEED_10) |
| tp->mac_mode |= MAC_MODE_PORT_MODE_MII; |
| else |
| tp->mac_mode |= MAC_MODE_PORT_MODE_GMII; |
| } else |
| tp->mac_mode |= MAC_MODE_PORT_MODE_GMII; |
| |
| tp->mac_mode &= ~MAC_MODE_HALF_DUPLEX; |
| if (tp->link_config.active_duplex == DUPLEX_HALF) |
| tp->mac_mode |= MAC_MODE_HALF_DUPLEX; |
| |
| tp->mac_mode &= ~MAC_MODE_LINK_POLARITY; |
| if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5700) { |
| if ((tp->led_ctrl == LED_CTRL_MODE_PHY_2) || |
| (current_link_up == 1 && |
| tp->link_config.active_speed == SPEED_10)) |
| tp->mac_mode |= MAC_MODE_LINK_POLARITY; |
| } else { |
| if (current_link_up == 1) |
| tp->mac_mode |= MAC_MODE_LINK_POLARITY; |
| } |
| |
| /* ??? Without this setting Netgear GA302T PHY does not |
| * ??? send/receive packets... |
| */ |
| if ((tp->phy_id & PHY_ID_MASK) == PHY_ID_BCM5411 && |
| tp->pci_chip_rev_id == CHIPREV_ID_5700_ALTIMA) { |
| tp->mi_mode |= MAC_MI_MODE_AUTO_POLL; |
| tw32_f(MAC_MI_MODE, tp->mi_mode); |
| udelay(80); |
| } |
| |
| tw32_f(MAC_MODE, tp->mac_mode); |
| udelay(40); |
| |
| if (tp->tg3_flags & TG3_FLAG_USE_LINKCHG_REG) { |
| /* Polled via timer. */ |
| tw32_f(MAC_EVENT, 0); |
| } else { |
| tw32_f(MAC_EVENT, MAC_EVENT_LNKSTATE_CHANGED); |
| } |
| udelay(40); |
| |
| if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5700 && |
| current_link_up == 1 && |
| tp->link_config.active_speed == SPEED_1000 && |
| ((tp->tg3_flags & TG3_FLAG_PCIX_MODE) || |
| (tp->tg3_flags & TG3_FLAG_PCI_HIGH_SPEED))) { |
| udelay(120); |
| tw32_f(MAC_STATUS, |
| (MAC_STATUS_SYNC_CHANGED | |
| MAC_STATUS_CFG_CHANGED)); |
| udelay(40); |
| tg3_write_mem(tp, |
| NIC_SRAM_FIRMWARE_MBOX, |
| NIC_SRAM_FIRMWARE_MBOX_MAGIC2); |
| } |
| |
| if (current_link_up != netif_carrier_ok(tp->dev)) { |
| if (current_link_up) |
| netif_carrier_on(tp->dev); |
| else |
| netif_carrier_off(tp->dev); |
| tg3_link_report(tp); |
| } |
| |
| return 0; |
| } |
| |
| struct tg3_fiber_aneginfo { |
| int state; |
| #define ANEG_STATE_UNKNOWN 0 |
| #define ANEG_STATE_AN_ENABLE 1 |
| #define ANEG_STATE_RESTART_INIT 2 |
| #define ANEG_STATE_RESTART 3 |
| #define ANEG_STATE_DISABLE_LINK_OK 4 |
| #define ANEG_STATE_ABILITY_DETECT_INIT 5 |
| #define ANEG_STATE_ABILITY_DETECT 6 |
| #define ANEG_STATE_ACK_DETECT_INIT 7 |
| #define ANEG_STATE_ACK_DETECT 8 |
| #define ANEG_STATE_COMPLETE_ACK_INIT 9 |
| #define ANEG_STATE_COMPLETE_ACK 10 |
| #define ANEG_STATE_IDLE_DETECT_INIT 11 |
| #define ANEG_STATE_IDLE_DETECT 12 |
| #define ANEG_STATE_LINK_OK 13 |
| #define ANEG_STATE_NEXT_PAGE_WAIT_INIT 14 |
| #define ANEG_STATE_NEXT_PAGE_WAIT 15 |
| |
| u32 flags; |
| #define MR_AN_ENABLE 0x00000001 |
| #define MR_RESTART_AN 0x00000002 |
| #define MR_AN_COMPLETE 0x00000004 |
| #define MR_PAGE_RX 0x00000008 |
| #define MR_NP_LOADED 0x00000010 |
| #define MR_TOGGLE_TX 0x00000020 |
| #define MR_LP_ADV_FULL_DUPLEX 0x00000040 |
| #define MR_LP_ADV_HALF_DUPLEX 0x00000080 |
| #define MR_LP_ADV_SYM_PAUSE 0x00000100 |
| #define MR_LP_ADV_ASYM_PAUSE 0x00000200 |
| #define MR_LP_ADV_REMOTE_FAULT1 0x00000400 |
| #define MR_LP_ADV_REMOTE_FAULT2 0x00000800 |
| #define MR_LP_ADV_NEXT_PAGE 0x00001000 |
| #define MR_TOGGLE_RX 0x00002000 |
| #define MR_NP_RX 0x00004000 |
| |
| #define MR_LINK_OK 0x80000000 |
| |
| unsigned long link_time, cur_time; |
| |
| u32 ability_match_cfg; |
| int ability_match_count; |
| |
| char ability_match, idle_match, ack_match; |
| |
| u32 txconfig, rxconfig; |
| #define ANEG_CFG_NP 0x00000080 |
| #define ANEG_CFG_ACK 0x00000040 |
| #define ANEG_CFG_RF2 0x00000020 |
| #define ANEG_CFG_RF1 0x00000010 |
| #define ANEG_CFG_PS2 0x00000001 |
| #define ANEG_CFG_PS1 0x00008000 |
| #define ANEG_CFG_HD 0x00004000 |
| #define ANEG_CFG_FD 0x00002000 |
| #define ANEG_CFG_INVAL 0x00001f06 |
| |
| }; |
| #define ANEG_OK 0 |
| #define ANEG_DONE 1 |
| #define ANEG_TIMER_ENAB 2 |
| #define ANEG_FAILED -1 |
| |
| #define ANEG_STATE_SETTLE_TIME 10000 |
| |
| static int tg3_fiber_aneg_smachine(struct tg3 *tp, |
| struct tg3_fiber_aneginfo *ap) |
| { |
| unsigned long delta; |
| u32 rx_cfg_reg; |
| int ret; |
| |
| if (ap->state == ANEG_STATE_UNKNOWN) { |
| ap->rxconfig = 0; |
| ap->link_time = 0; |
| ap->cur_time = 0; |
| ap->ability_match_cfg = 0; |
| ap->ability_match_count = 0; |
| ap->ability_match = 0; |
| ap->idle_match = 0; |
| ap->ack_match = 0; |
| } |
| ap->cur_time++; |
| |
| if (tr32(MAC_STATUS) & MAC_STATUS_RCVD_CFG) { |
| rx_cfg_reg = tr32(MAC_RX_AUTO_NEG); |
| |
| if (rx_cfg_reg != ap->ability_match_cfg) { |
| ap->ability_match_cfg = rx_cfg_reg; |
| ap->ability_match = 0; |
| ap->ability_match_count = 0; |
| } else { |
| if (++ap->ability_match_count > 1) { |
| ap->ability_match = 1; |
| ap->ability_match_cfg = rx_cfg_reg; |
| } |
| } |
| if (rx_cfg_reg & ANEG_CFG_ACK) |
| ap->ack_match = 1; |
| else |
| ap->ack_match = 0; |
| |
| ap->idle_match = 0; |
| } else { |
| ap->idle_match = 1; |
| ap->ability_match_cfg = 0; |
| ap->ability_match_count = 0; |
| ap->ability_match = 0; |
| ap->ack_match = 0; |
| |
| rx_cfg_reg = 0; |
| } |
| |
| ap->rxconfig = rx_cfg_reg; |
| ret = ANEG_OK; |
| |
| switch(ap->state) { |
| case ANEG_STATE_UNKNOWN: |
| if (ap->flags & (MR_AN_ENABLE | MR_RESTART_AN)) |
| ap->state = ANEG_STATE_AN_ENABLE; |
| |
| /* fallthru */ |
| case ANEG_STATE_AN_ENABLE: |
| ap->flags &= ~(MR_AN_COMPLETE | MR_PAGE_RX); |
| if (ap->flags & MR_AN_ENABLE) { |
| ap->link_time = 0; |
| ap->cur_time = 0; |
| ap->ability_match_cfg = 0; |
| ap->ability_match_count = 0; |
| ap->ability_match = 0; |
| ap->idle_match = 0; |
| ap->ack_match = 0; |
| |
| ap->state = ANEG_STATE_RESTART_INIT; |
| } else { |
| ap->state = ANEG_STATE_DISABLE_LINK_OK; |
| } |
| break; |
| |
| case ANEG_STATE_RESTART_INIT: |
| ap->link_time = ap->cur_time; |
| ap->flags &= ~(MR_NP_LOADED); |
| ap->txconfig = 0; |
| tw32(MAC_TX_AUTO_NEG, 0); |
| tp->mac_mode |= MAC_MODE_SEND_CONFIGS; |
| tw32_f(MAC_MODE, tp->mac_mode); |
| udelay(40); |
| |
| ret = ANEG_TIMER_ENAB; |
| ap->state = ANEG_STATE_RESTART; |
| |
| /* fallthru */ |
| case ANEG_STATE_RESTART: |
| delta = ap->cur_time - ap->link_time; |
| if (delta > ANEG_STATE_SETTLE_TIME) { |
| ap->state = ANEG_STATE_ABILITY_DETECT_INIT; |
| } else { |
| ret = ANEG_TIMER_ENAB; |
| } |
| break; |
| |
| case ANEG_STATE_DISABLE_LINK_OK: |
| ret = ANEG_DONE; |
| break; |
| |
| case ANEG_STATE_ABILITY_DETECT_INIT: |
| ap->flags &= ~(MR_TOGGLE_TX); |
| ap->txconfig = (ANEG_CFG_FD | ANEG_CFG_PS1); |
| tw32(MAC_TX_AUTO_NEG, ap->txconfig); |
| tp->mac_mode |= MAC_MODE_SEND_CONFIGS; |
| tw32_f(MAC_MODE, tp->mac_mode); |
| udelay(40); |
| |
| ap->state = ANEG_STATE_ABILITY_DETECT; |
| break; |
| |
| case ANEG_STATE_ABILITY_DETECT: |
| if (ap->ability_match != 0 && ap->rxconfig != 0) { |
| ap->state = ANEG_STATE_ACK_DETECT_INIT; |
| } |
| break; |
| |
| case ANEG_STATE_ACK_DETECT_INIT: |
| ap->txconfig |= ANEG_CFG_ACK; |
| tw32(MAC_TX_AUTO_NEG, ap->txconfig); |
| tp->mac_mode |= MAC_MODE_SEND_CONFIGS; |
| tw32_f(MAC_MODE, tp->mac_mode); |
| udelay(40); |
| |
| ap->state = ANEG_STATE_ACK_DETECT; |
| |
| /* fallthru */ |
| case ANEG_STATE_ACK_DETECT: |
| if (ap->ack_match != 0) { |
| if ((ap->rxconfig & ~ANEG_CFG_ACK) == |
| (ap->ability_match_cfg & ~ANEG_CFG_ACK)) { |
| ap->state = ANEG_STATE_COMPLETE_ACK_INIT; |
| } else { |
| ap->state = ANEG_STATE_AN_ENABLE; |
| } |
| } else if (ap->ability_match != 0 && |
| ap->rxconfig == 0) { |
| ap->state = ANEG_STATE_AN_ENABLE; |
| } |
| break; |
| |
| case ANEG_STATE_COMPLETE_ACK_INIT: |
| if (ap->rxconfig & ANEG_CFG_INVAL) { |
| ret = ANEG_FAILED; |
| break; |
| } |
| ap->flags &= ~(MR_LP_ADV_FULL_DUPLEX | |
| MR_LP_ADV_HALF_DUPLEX | |
| MR_LP_ADV_SYM_PAUSE | |
| MR_LP_ADV_ASYM_PAUSE | |
| MR_LP_ADV_REMOTE_FAULT1 | |
| MR_LP_ADV_REMOTE_FAULT2 | |
| MR_LP_ADV_NEXT_PAGE | |
| MR_TOGGLE_RX | |
| MR_NP_RX); |
| if (ap->rxconfig & ANEG_CFG_FD) |
| ap->flags |= MR_LP_ADV_FULL_DUPLEX; |
| if (ap->rxconfig & ANEG_CFG_HD) |
| ap->flags |= MR_LP_ADV_HALF_DUPLEX; |
| if (ap->rxconfig & ANEG_CFG_PS1) |
| ap->flags |= MR_LP_ADV_SYM_PAUSE; |
| if (ap->rxconfig & ANEG_CFG_PS2) |
| ap->flags |= MR_LP_ADV_ASYM_PAUSE; |
| if (ap->rxconfig & ANEG_CFG_RF1) |
| ap->flags |= MR_LP_ADV_REMOTE_FAULT1; |
| if (ap->rxconfig & ANEG_CFG_RF2) |
| ap->flags |= MR_LP_ADV_REMOTE_FAULT2; |
| if (ap->rxconfig & ANEG_CFG_NP) |
| ap->flags |= MR_LP_ADV_NEXT_PAGE; |
| |
| ap->link_time = ap->cur_time; |
| |
| ap->flags ^= (MR_TOGGLE_TX); |
| if (ap->rxconfig & 0x0008) |
| ap->flags |= MR_TOGGLE_RX; |
| if (ap->rxconfig & ANEG_CFG_NP) |
| ap->flags |= MR_NP_RX; |
| ap->flags |= MR_PAGE_RX; |
| |
| ap->state = ANEG_STATE_COMPLETE_ACK; |
| ret = ANEG_TIMER_ENAB; |
| break; |
| |
| case ANEG_STATE_COMPLETE_ACK: |
| if (ap->ability_match != 0 && |
| ap->rxconfig == 0) { |
| ap->state = ANEG_STATE_AN_ENABLE; |
| break; |
| } |
| delta = ap->cur_time - ap->link_time; |
| if (delta > ANEG_STATE_SETTLE_TIME) { |
| if (!(ap->flags & (MR_LP_ADV_NEXT_PAGE))) { |
| ap->state = ANEG_STATE_IDLE_DETECT_INIT; |
| } else { |
| if ((ap->txconfig & ANEG_CFG_NP) == 0 && |
| !(ap->flags & MR_NP_RX)) { |
| ap->state = ANEG_STATE_IDLE_DETECT_INIT; |
| } else { |
| ret = ANEG_FAILED; |
| } |
| } |
| } |
| break; |
| |
| case ANEG_STATE_IDLE_DETECT_INIT: |
| ap->link_time = ap->cur_time; |
| tp->mac_mode &= ~MAC_MODE_SEND_CONFIGS; |
| tw32_f(MAC_MODE, tp->mac_mode); |
| udelay(40); |
| |
| ap->state = ANEG_STATE_IDLE_DETECT; |
| ret = ANEG_TIMER_ENAB; |
| break; |
| |
| case ANEG_STATE_IDLE_DETECT: |
| if (ap->ability_match != 0 && |
| ap->rxconfig == 0) { |
| ap->state = ANEG_STATE_AN_ENABLE; |
| break; |
| } |
| delta = ap->cur_time - ap->link_time; |
| if (delta > ANEG_STATE_SETTLE_TIME) { |
| /* XXX another gem from the Broadcom driver :( */ |
| ap->state = ANEG_STATE_LINK_OK; |
| } |
| break; |
| |
| case ANEG_STATE_LINK_OK: |
| ap->flags |= (MR_AN_COMPLETE | MR_LINK_OK); |
| ret = ANEG_DONE; |
| break; |
| |
| case ANEG_STATE_NEXT_PAGE_WAIT_INIT: |
| /* ??? unimplemented */ |
| break; |
| |
| case ANEG_STATE_NEXT_PAGE_WAIT: |
| /* ??? unimplemented */ |
| break; |
| |
| default: |
| ret = ANEG_FAILED; |
| break; |
| }; |
| |
| return ret; |
| } |
| |
| static int fiber_autoneg(struct tg3 *tp, u32 *flags) |
| { |
| int res = 0; |
| struct tg3_fiber_aneginfo aninfo; |
| int status = ANEG_FAILED; |
| unsigned int tick; |
| u32 tmp; |
| |
| tw32_f(MAC_TX_AUTO_NEG, 0); |
| |
| tmp = tp->mac_mode & ~MAC_MODE_PORT_MODE_MASK; |
| tw32_f(MAC_MODE, tmp | MAC_MODE_PORT_MODE_GMII); |
| udelay(40); |
| |
| tw32_f(MAC_MODE, tp->mac_mode | MAC_MODE_SEND_CONFIGS); |
| udelay(40); |
| |
| memset(&aninfo, 0, sizeof(aninfo)); |
| aninfo.flags |= MR_AN_ENABLE; |
| aninfo.state = ANEG_STATE_UNKNOWN; |
| aninfo.cur_time = 0; |
| tick = 0; |
| while (++tick < 195000) { |
| status = tg3_fiber_aneg_smachine(tp, &aninfo); |
| if (status == ANEG_DONE || status == ANEG_FAILED) |
| break; |
| |
| udelay(1); |
| } |
| |
| tp->mac_mode &= ~MAC_MODE_SEND_CONFIGS; |
| tw32_f(MAC_MODE, tp->mac_mode); |
| udelay(40); |
| |
| *flags = aninfo.flags; |
| |
| if (status == ANEG_DONE && |
| (aninfo.flags & (MR_AN_COMPLETE | MR_LINK_OK | |
| MR_LP_ADV_FULL_DUPLEX))) |
| res = 1; |
| |
| return res; |
| } |
| |
| static void tg3_init_bcm8002(struct tg3 *tp) |
| { |
| u32 mac_status = tr32(MAC_STATUS); |
| int i; |
| |
| /* Reset when initting first time or we have a link. */ |
| if ((tp->tg3_flags & TG3_FLAG_INIT_COMPLETE) && |
| !(mac_status & MAC_STATUS_PCS_SYNCED)) |
| return; |
| |
| /* Set PLL lock range. */ |
| tg3_writephy(tp, 0x16, 0x8007); |
| |
| /* SW reset */ |
| tg3_writephy(tp, MII_BMCR, BMCR_RESET); |
| |
| /* Wait for reset to complete. */ |
| /* XXX schedule_timeout() ... */ |
| for (i = 0; i < 500; i++) |
| udelay(10); |
| |
| /* Config mode; select PMA/Ch 1 regs. */ |
| tg3_writephy(tp, 0x10, 0x8411); |
| |
| /* Enable auto-lock and comdet, select txclk for tx. */ |
| tg3_writephy(tp, 0x11, 0x0a10); |
| |
| tg3_writephy(tp, 0x18, 0x00a0); |
| tg3_writephy(tp, 0x16, 0x41ff); |
| |
| /* Assert and deassert POR. */ |
| tg3_writephy(tp, 0x13, 0x0400); |
| udelay(40); |
| tg3_writephy(tp, 0x13, 0x0000); |
| |
| tg3_writephy(tp, 0x11, 0x0a50); |
| udelay(40); |
| tg3_writephy(tp, 0x11, 0x0a10); |
| |
| /* Wait for signal to stabilize */ |
| /* XXX schedule_timeout() ... */ |
| for (i = 0; i < 15000; i++) |
| udelay(10); |
| |
| /* Deselect the channel register so we can read the PHYID |
| * later. |
| */ |
| tg3_writephy(tp, 0x10, 0x8011); |
| } |
| |
| static int tg3_setup_fiber_hw_autoneg(struct tg3 *tp, u32 mac_status) |
| { |
| u32 sg_dig_ctrl, sg_dig_status; |
| u32 serdes_cfg, expected_sg_dig_ctrl; |
| int workaround, port_a; |
| int current_link_up; |
| |
| serdes_cfg = 0; |
| expected_sg_dig_ctrl = 0; |
| workaround = 0; |
| port_a = 1; |
| current_link_up = 0; |
| |
| if (tp->pci_chip_rev_id != CHIPREV_ID_5704_A0 && |
| tp->pci_chip_rev_id != CHIPREV_ID_5704_A1) { |
| workaround = 1; |
| if (tr32(TG3PCI_DUAL_MAC_CTRL) & DUAL_MAC_CTRL_ID) |
| port_a = 0; |
| |
| /* preserve bits 0-11,13,14 for signal pre-emphasis */ |
| /* preserve bits 20-23 for voltage regulator */ |
| serdes_cfg = tr32(MAC_SERDES_CFG) & 0x00f06fff; |
| } |
| |
| sg_dig_ctrl = tr32(SG_DIG_CTRL); |
| |
| if (tp->link_config.autoneg != AUTONEG_ENABLE) { |
| if (sg_dig_ctrl & (1 << 31)) { |
| if (workaround) { |
| u32 val = serdes_cfg; |
| |
| if (port_a) |
| val |= 0xc010000; |
| else |
| val |= 0x4010000; |
| tw32_f(MAC_SERDES_CFG, val); |
| } |
| tw32_f(SG_DIG_CTRL, 0x01388400); |
| } |
| if (mac_status & MAC_STATUS_PCS_SYNCED) { |
| tg3_setup_flow_control(tp, 0, 0); |
| current_link_up = 1; |
| } |
| goto out; |
| } |
| |
| /* Want auto-negotiation. */ |
| expected_sg_dig_ctrl = 0x81388400; |
| |
| /* Pause capability */ |
| expected_sg_dig_ctrl |= (1 << 11); |
| |
| /* Asymettric pause */ |
| expected_sg_dig_ctrl |= (1 << 12); |
| |
| if (sg_dig_ctrl != expected_sg_dig_ctrl) { |
| if (workaround) |
| tw32_f(MAC_SERDES_CFG, serdes_cfg | 0xc011000); |
| tw32_f(SG_DIG_CTRL, expected_sg_dig_ctrl | (1 << 30)); |
| udelay(5); |
| tw32_f(SG_DIG_CTRL, expected_sg_dig_ctrl); |
| |
| tp->tg3_flags2 |= TG3_FLG2_PHY_JUST_INITTED; |
| } else if (mac_status & (MAC_STATUS_PCS_SYNCED | |
| MAC_STATUS_SIGNAL_DET)) { |
| int i; |
| |
| /* Giver time to negotiate (~200ms) */ |
| for (i = 0; i < 40000; i++) { |
| sg_dig_status = tr32(SG_DIG_STATUS); |
| if (sg_dig_status & (0x3)) |
| break; |
| udelay(5); |
| } |
| mac_status = tr32(MAC_STATUS); |
| |
| if ((sg_dig_status & (1 << 1)) && |
| (mac_status & MAC_STATUS_PCS_SYNCED)) { |
| u32 local_adv, remote_adv; |
| |
| local_adv = ADVERTISE_PAUSE_CAP; |
| remote_adv = 0; |
| if (sg_dig_status & (1 << 19)) |
| remote_adv |= LPA_PAUSE_CAP; |
| if (sg_dig_status & (1 << 20)) |
| remote_adv |= LPA_PAUSE_ASYM; |
| |
| tg3_setup_flow_control(tp, local_adv, remote_adv); |
| current_link_up = 1; |
| tp->tg3_flags2 &= ~TG3_FLG2_PHY_JUST_INITTED; |
| } else if (!(sg_dig_status & (1 << 1))) { |
| if (tp->tg3_flags2 & TG3_FLG2_PHY_JUST_INITTED) |
| tp->tg3_flags2 &= ~TG3_FLG2_PHY_JUST_INITTED; |
| else { |
| if (workaround) { |
| u32 val = serdes_cfg; |
| |
| if (port_a) |
| val |= 0xc010000; |
| else |
| val |= 0x4010000; |
| |
| tw32_f(MAC_SERDES_CFG, val); |
| } |
| |
| tw32_f(SG_DIG_CTRL, 0x01388400); |
| udelay(40); |
| |
| /* Link parallel detection - link is up */ |
| /* only if we have PCS_SYNC and not */ |
| /* receiving config code words */ |
| mac_status = tr32(MAC_STATUS); |
| if ((mac_status & MAC_STATUS_PCS_SYNCED) && |
| !(mac_status & MAC_STATUS_RCVD_CFG)) { |
| tg3_setup_flow_control(tp, 0, 0); |
| current_link_up = 1; |
| } |
| } |
| } |
| } |
| |
| out: |
| return current_link_up; |
| } |
| |
| static int tg3_setup_fiber_by_hand(struct tg3 *tp, u32 mac_status) |
| { |
| int current_link_up = 0; |
| |
| if (!(mac_status & MAC_STATUS_PCS_SYNCED)) { |
| tp->tg3_flags &= ~TG3_FLAG_GOT_SERDES_FLOWCTL; |
| goto out; |
| } |
| |
| if (tp->link_config.autoneg == AUTONEG_ENABLE) { |
| u32 flags; |
| int i; |
| |
| if (fiber_autoneg(tp, &flags)) { |
| u32 local_adv, remote_adv; |
| |
| local_adv = ADVERTISE_PAUSE_CAP; |
| remote_adv = 0; |
| if (flags & MR_LP_ADV_SYM_PAUSE) |
| remote_adv |= LPA_PAUSE_CAP; |
| if (flags & MR_LP_ADV_ASYM_PAUSE) |
| remote_adv |= LPA_PAUSE_ASYM; |
| |
| tg3_setup_flow_control(tp, local_adv, remote_adv); |
| |
| tp->tg3_flags |= TG3_FLAG_GOT_SERDES_FLOWCTL; |
| current_link_up = 1; |
| } |
| for (i = 0; i < 30; i++) { |
| udelay(20); |
| tw32_f(MAC_STATUS, |
| (MAC_STATUS_SYNC_CHANGED | |
| MAC_STATUS_CFG_CHANGED)); |
| udelay(40); |
| if ((tr32(MAC_STATUS) & |
| (MAC_STATUS_SYNC_CHANGED | |
| MAC_STATUS_CFG_CHANGED)) == 0) |
| break; |
| } |
| |
| mac_status = tr32(MAC_STATUS); |
| if (current_link_up == 0 && |
| (mac_status & MAC_STATUS_PCS_SYNCED) && |
| !(mac_status & MAC_STATUS_RCVD_CFG)) |
| current_link_up = 1; |
| } else { |
| /* Forcing 1000FD link up. */ |
| current_link_up = 1; |
| tp->tg3_flags |= TG3_FLAG_GOT_SERDES_FLOWCTL; |
| |
| tw32_f(MAC_MODE, (tp->mac_mode | MAC_MODE_SEND_CONFIGS)); |
| udelay(40); |
| } |
| |
| out: |
| return current_link_up; |
| } |
| |
| static int tg3_setup_fiber_phy(struct tg3 *tp, int force_reset) |
| { |
| u32 orig_pause_cfg; |
| u16 orig_active_speed; |
| u8 orig_active_duplex; |
| u32 mac_status; |
| int current_link_up; |
| int i; |
| |
| orig_pause_cfg = |
| (tp->tg3_flags & (TG3_FLAG_RX_PAUSE | |
| TG3_FLAG_TX_PAUSE)); |
| orig_active_speed = tp->link_config.active_speed; |
| orig_active_duplex = tp->link_config.active_duplex; |
| |
| if (!(tp->tg3_flags2 & TG3_FLG2_HW_AUTONEG) && |
| netif_carrier_ok(tp->dev) && |
| (tp->tg3_flags & TG3_FLAG_INIT_COMPLETE)) { |
| mac_status = tr32(MAC_STATUS); |
| mac_status &= (MAC_STATUS_PCS_SYNCED | |
| MAC_STATUS_SIGNAL_DET | |
| MAC_STATUS_CFG_CHANGED | |
| MAC_STATUS_RCVD_CFG); |
| if (mac_status == (MAC_STATUS_PCS_SYNCED | |
| MAC_STATUS_SIGNAL_DET)) { |
| tw32_f(MAC_STATUS, (MAC_STATUS_SYNC_CHANGED | |
| MAC_STATUS_CFG_CHANGED)); |
| return 0; |
| } |
| } |
| |
| tw32_f(MAC_TX_AUTO_NEG, 0); |
| |
| tp->mac_mode &= ~(MAC_MODE_PORT_MODE_MASK | MAC_MODE_HALF_DUPLEX); |
| tp->mac_mode |= MAC_MODE_PORT_MODE_TBI; |
| tw32_f(MAC_MODE, tp->mac_mode); |
| udelay(40); |
| |
| if (tp->phy_id == PHY_ID_BCM8002) |
| tg3_init_bcm8002(tp); |
| |
| /* Enable link change event even when serdes polling. */ |
| tw32_f(MAC_EVENT, MAC_EVENT_LNKSTATE_CHANGED); |
| udelay(40); |
| |
| current_link_up = 0; |
| mac_status = tr32(MAC_STATUS); |
| |
| if (tp->tg3_flags2 & TG3_FLG2_HW_AUTONEG) |
| current_link_up = tg3_setup_fiber_hw_autoneg(tp, mac_status); |
| else |
| current_link_up = tg3_setup_fiber_by_hand(tp, mac_status); |
| |
| tp->mac_mode &= ~MAC_MODE_LINK_POLARITY; |
| tw32_f(MAC_MODE, tp->mac_mode); |
| udelay(40); |
| |
| tp->hw_status->status = |
| (SD_STATUS_UPDATED | |
| (tp->hw_status->status & ~SD_STATUS_LINK_CHG)); |
| |
| for (i = 0; i < 100; i++) { |
| tw32_f(MAC_STATUS, (MAC_STATUS_SYNC_CHANGED | |
| MAC_STATUS_CFG_CHANGED)); |
| udelay(5); |
| if ((tr32(MAC_STATUS) & (MAC_STATUS_SYNC_CHANGED | |
| MAC_STATUS_CFG_CHANGED)) == 0) |
| break; |
| } |
| |
| mac_status = tr32(MAC_STATUS); |
| if ((mac_status & MAC_STATUS_PCS_SYNCED) == 0) { |
| current_link_up = 0; |
| if (tp->link_config.autoneg == AUTONEG_ENABLE) { |
| tw32_f(MAC_MODE, (tp->mac_mode | |
| MAC_MODE_SEND_CONFIGS)); |
| udelay(1); |
| tw32_f(MAC_MODE, tp->mac_mode); |
| } |
| } |
| |
| if (current_link_up == 1) { |
| tp->link_config.active_speed = SPEED_1000; |
| tp->link_config.active_duplex = DUPLEX_FULL; |
| tw32(MAC_LED_CTRL, (tp->led_ctrl | |
| LED_CTRL_LNKLED_OVERRIDE | |
| LED_CTRL_1000MBPS_ON)); |
| } else { |
| tp->link_config.active_speed = SPEED_INVALID; |
| tp->link_config.active_duplex = DUPLEX_INVALID; |
| tw32(MAC_LED_CTRL, (tp->led_ctrl | |
| LED_CTRL_LNKLED_OVERRIDE | |
| LED_CTRL_TRAFFIC_OVERRIDE)); |
| } |
| |
| if (current_link_up != netif_carrier_ok(tp->dev)) { |
| if (current_link_up) |
| netif_carrier_on(tp->dev); |
| else |
| netif_carrier_off(tp->dev); |
| tg3_link_report(tp); |
| } else { |
| u32 now_pause_cfg = |
| tp->tg3_flags & (TG3_FLAG_RX_PAUSE | |
| TG3_FLAG_TX_PAUSE); |
| if (orig_pause_cfg != now_pause_cfg || |
| orig_active_speed != tp->link_config.active_speed || |
| orig_active_duplex != tp->link_config.active_duplex) |
| tg3_link_report(tp); |
| } |
| |
| return 0; |
| } |
| |
| static int tg3_setup_phy(struct tg3 *tp, int force_reset) |
| { |
| int err; |
| |
| if (tp->tg3_flags2 & TG3_FLG2_PHY_SERDES) { |
| err = tg3_setup_fiber_phy(tp, force_reset); |
| } else { |
| err = tg3_setup_copper_phy(tp, force_reset); |
| } |
| |
| if (tp->link_config.active_speed == SPEED_1000 && |
| tp->link_config.active_duplex == DUPLEX_HALF) |
| tw32(MAC_TX_LENGTHS, |
| ((2 << TX_LENGTHS_IPG_CRS_SHIFT) | |
| (6 << TX_LENGTHS_IPG_SHIFT) | |
| (0xff << TX_LENGTHS_SLOT_TIME_SHIFT))); |
| else |
| tw32(MAC_TX_LENGTHS, |
| ((2 << TX_LENGTHS_IPG_CRS_SHIFT) | |
| (6 << TX_LENGTHS_IPG_SHIFT) | |
| (32 << TX_LENGTHS_SLOT_TIME_SHIFT))); |
| |
| if (!(tp->tg3_flags2 & TG3_FLG2_5705_PLUS)) { |
| if (netif_carrier_ok(tp->dev)) { |
| tw32(HOSTCC_STAT_COAL_TICKS, |
| tp->coal.stats_block_coalesce_usecs); |
| } else { |
| tw32(HOSTCC_STAT_COAL_TICKS, 0); |
| } |
| } |
| |
| return err; |
| } |
| |
| /* Tigon3 never reports partial packet sends. So we do not |
| * need special logic to handle SKBs that have not had all |
| * of their frags sent yet, like SunGEM does. |
| */ |
| static void tg3_tx(struct tg3 *tp) |
| { |
| u32 hw_idx = tp->hw_status->idx[0].tx_consumer; |
| u32 sw_idx = tp->tx_cons; |
| |
| while (sw_idx != hw_idx) { |
| struct tx_ring_info *ri = &tp->tx_buffers[sw_idx]; |
| struct sk_buff *skb = ri->skb; |
| int i; |
| |
| if (unlikely(skb == NULL)) |
| BUG(); |
| |
| pci_unmap_single(tp->pdev, |
| pci_unmap_addr(ri, mapping), |
| skb_headlen(skb), |
| PCI_DMA_TODEVICE); |
| |
| ri->skb = NULL; |
| |
| sw_idx = NEXT_TX(sw_idx); |
| |
| for (i = 0; i < skb_shinfo(skb)->nr_frags; i++) { |
| if (unlikely(sw_idx == hw_idx)) |
| BUG(); |
| |
| ri = &tp->tx_buffers[sw_idx]; |
| if (unlikely(ri->skb != NULL)) |
| BUG(); |
| |
| pci_unmap_page(tp->pdev, |
| pci_unmap_addr(ri, mapping), |
| skb_shinfo(skb)->frags[i].size, |
| PCI_DMA_TODEVICE); |
| |
| sw_idx = NEXT_TX(sw_idx); |
| } |
| |
| dev_kfree_skb_irq(skb); |
| } |
| |
| tp->tx_cons = sw_idx; |
| |
| if (netif_queue_stopped(tp->dev) && |
| (TX_BUFFS_AVAIL(tp) > TG3_TX_WAKEUP_THRESH)) |
| netif_wake_queue(tp->dev); |
| } |
| |
| /* Returns size of skb allocated or < 0 on error. |
| * |
| * We only need to fill in the address because the other members |
| * of the RX descriptor are invariant, see tg3_init_rings. |
| * |
| * Note the purposeful assymetry of cpu vs. chip accesses. For |
| * posting buffers we only dirty the first cache line of the RX |
| * descriptor (containing the address). Whereas for the RX status |
| * buffers the cpu only reads the last cacheline of the RX descriptor |
| * (to fetch the error flags, vlan tag, checksum, and opaque cookie). |
| */ |
| static int tg3_alloc_rx_skb(struct tg3 *tp, u32 opaque_key, |
| int src_idx, u32 dest_idx_unmasked) |
| { |
| struct tg3_rx_buffer_desc *desc; |
| struct ring_info *map, *src_map; |
| struct sk_buff *skb; |
| dma_addr_t mapping; |
| int skb_size, dest_idx; |
| |
| src_map = NULL; |
| switch (opaque_key) { |
| case RXD_OPAQUE_RING_STD: |
| dest_idx = dest_idx_unmasked % TG3_RX_RING_SIZE; |
| desc = &tp->rx_std[dest_idx]; |
| map = &tp->rx_std_buffers[dest_idx]; |
| if (src_idx >= 0) |
| src_map = &tp->rx_std_buffers[src_idx]; |
| skb_size = RX_PKT_BUF_SZ; |
| break; |
| |
| case RXD_OPAQUE_RING_JUMBO: |
| dest_idx = dest_idx_unmasked % TG3_RX_JUMBO_RING_SIZE; |
| desc = &tp->rx_jumbo[dest_idx]; |
| map = &tp->rx_jumbo_buffers[dest_idx]; |
| if (src_idx >= 0) |
| src_map = &tp->rx_jumbo_buffers[src_idx]; |
| skb_size = RX_JUMBO_PKT_BUF_SZ; |
| break; |
| |
| default: |
| return -EINVAL; |
| }; |
| |
| /* Do not overwrite any of the map or rp information |
| * until we are sure we can commit to a new buffer. |
| * |
| * Callers depend upon this behavior and assume that |
| * we leave everything unchanged if we fail. |
| */ |
| skb = dev_alloc_skb(skb_size); |
| if (skb == NULL) |
| return -ENOMEM; |
| |
| skb->dev = tp->dev; |
| skb_reserve(skb, tp->rx_offset); |
| |
| mapping = pci_map_single(tp->pdev, skb->data, |
| skb_size - tp->rx_offset, |
| PCI_DMA_FROMDEVICE); |
| |
| map->skb = skb; |
| pci_unmap_addr_set(map, mapping, mapping); |
| |
| if (src_map != NULL) |
| src_map->skb = NULL; |
| |
| desc->addr_hi = ((u64)mapping >> 32); |
| desc->addr_lo = ((u64)mapping & 0xffffffff); |
| |
| return skb_size; |
| } |
| |
| /* We only need to move over in the address because the other |
| * members of the RX descriptor are invariant. See notes above |
| * tg3_alloc_rx_skb for full details. |
| */ |
| static void tg3_recycle_rx(struct tg3 *tp, u32 opaque_key, |
| int src_idx, u32 dest_idx_unmasked) |
| { |
| struct tg3_rx_buffer_desc *src_desc, *dest_desc; |
| struct ring_info *src_map, *dest_map; |
| int dest_idx; |
| |
| switch (opaque_key) { |
| case RXD_OPAQUE_RING_STD: |
| dest_idx = dest_idx_unmasked % TG3_RX_RING_SIZE; |
| dest_desc = &tp->rx_std[dest_idx]; |
| dest_map = &tp->rx_std_buffers[dest_idx]; |
| src_desc = &tp->rx_std[src_idx]; |
| src_map = &tp->rx_std_buffers[src_idx]; |
| break; |
| |
| case RXD_OPAQUE_RING_JUMBO: |
| dest_idx = dest_idx_unmasked % TG3_RX_JUMBO_RING_SIZE; |
| dest_desc = &tp->rx_jumbo[dest_idx]; |
| dest_map = &tp->rx_jumbo_buffers[dest_idx]; |
| src_desc = &tp->rx_jumbo[src_idx]; |
| src_map = &tp->rx_jumbo_buffers[src_idx]; |
| break; |
| |
| default: |
| return; |
| }; |
| |
| dest_map->skb = src_map->skb; |
| pci_unmap_addr_set(dest_map, mapping, |
| pci_unmap_addr(src_map, mapping)); |
| dest_desc->addr_hi = src_desc->addr_hi; |
| dest_desc->addr_lo = src_desc->addr_lo; |
| |
| src_map->skb = NULL; |
| } |
| |
| #if TG3_VLAN_TAG_USED |
| static int tg3_vlan_rx(struct tg3 *tp, struct sk_buff *skb, u16 vlan_tag) |
| { |
| return vlan_hwaccel_receive_skb(skb, tp->vlgrp, vlan_tag); |
| } |
| #endif |
| |
| /* The RX ring scheme is composed of multiple rings which post fresh |
| * buffers to the chip, and one special ring the chip uses to report |
|