📄 tg3.c
字号:
/* $Id: tg3.c,v 1.5 2003/03/19 21:26:20 gbaum Exp $ * tg3.c: Broadcom Tigon3 ethernet driver. * * Copyright (C) 2001, 2002 David S. Miller (davem@redhat.com) * Copyright (C) 2001, 2002 Jeff Garzik (jgarzik@mandrakesoft.com) * Copyright (C) 2003 Eric Biederman (ebiederman@lnxi.com) [etherboot port] *//* 11-13-2003 timlegge Fix Issue with NetGear GA302T * 11-18-2003 ebiederm Generalize NetGear Fix to what the code was supposed to be. */#include "etherboot.h"#include "nic.h"#include "pci.h"#include "timer.h"#include "string.h"#include "tg3.h"#define SUPPORT_COPPER_PHY 1#define SUPPORT_FIBER_PHY 1#define SUPPORT_LINK_REPORT 1#define SUPPORT_PARTNO_STR 1#define SUPPORT_PHY_STR 1struct tg3 tg3;/* Dummy defines for error handling */#define EBUSY 1#define ENODEV 2#define EINVAL 3#define ENOMEM 4/* 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 20 /* RX_RING_PENDING seems to be o.k. at 20 and 200 */#define TG3_RX_RCB_RING_SIZE 1024/* (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5705 ? \ 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_RCB_RING_BYTES (sizeof(struct tg3_rx_buffer_desc) * TG3_RX_RCB_RING_SIZE)#define TG3_TX_RING_BYTES (sizeof(struct tg3_tx_buffer_desc) * TG3_TX_RING_SIZE)#define NEXT_TX(N) (((N) + 1) & (TG3_TX_RING_SIZE - 1))#define PREV_TX(N) (((N) - 1) & (TG3_TX_RING_SIZE - 1))#define RX_PKT_BUF_SZ (1536 + 2 + 64)static struct bss { struct tg3_rx_buffer_desc rx_std[TG3_RX_RING_SIZE]; struct tg3_rx_buffer_desc rx_rcb[TG3_RX_RCB_RING_SIZE]; struct tg3_tx_buffer_desc tx_ring[TG3_TX_RING_SIZE]; struct tg3_hw_status hw_status; struct tg3_hw_stats hw_stats; unsigned char rx_bufs[TG3_DEF_RX_RING_PENDING][RX_PKT_BUF_SZ];} tg3_bss;/** * pci_save_state - save the PCI configuration space of a device before suspending * @dev: - PCI device that we're dealing with * @buffer: - buffer to hold config space context * * @buffer must be large enough to hold the entire PCI 2.2 config space * (>= 64 bytes). */static int pci_save_state(struct pci_device *dev, uint32_t *buffer){ int i; for (i = 0; i < 16; i++) pci_read_config_dword(dev, i * 4,&buffer[i]); return 0;}/** * pci_restore_state - Restore the saved state of a PCI device * @dev: - PCI device that we're dealing with * @buffer: - saved PCI config space * */static int pci_restore_state(struct pci_device *dev, uint32_t *buffer){ int i; for (i = 0; i < 16; i++) pci_write_config_dword(dev,i * 4, buffer[i]); return 0;}static void tg3_write_indirect_reg32(uint32_t off, uint32_t val){ pci_write_config_dword(tg3.pdev, TG3PCI_REG_BASE_ADDR, off); pci_write_config_dword(tg3.pdev, TG3PCI_REG_DATA, val);}#define tw32(reg,val) tg3_write_indirect_reg32((reg),(val))#define tw32_mailbox(reg, val) writel(((val) & 0xffffffff), tg3.regs + (reg))#define tw16(reg,val) writew(((val) & 0xffff), tg3.regs + (reg))#define tw8(reg,val) writeb(((val) & 0xff), tg3.regs + (reg))#define tr32(reg) readl(tg3.regs + (reg))#define tr16(reg) readw(tg3.regs + (reg))#define tr8(reg) readb(tg3.regs + (reg))static void tw32_carefully(uint32_t reg, uint32_t val){ tw32(reg, val); tr32(reg); udelay(100);}static void tw32_mailbox2(uint32_t reg, uint32_t val){ tw32_mailbox(reg, val); tr32(reg);}static void tg3_write_mem(uint32_t off, uint32_t val){ pci_write_config_dword(tg3.pdev, TG3PCI_MEM_WIN_BASE_ADDR, off); pci_write_config_dword(tg3.pdev, TG3PCI_MEM_WIN_DATA, val); /* Always leave this as zero. */ pci_write_config_dword(tg3.pdev, TG3PCI_MEM_WIN_BASE_ADDR, 0);}static void tg3_read_mem(uint32_t off, uint32_t *val){ pci_write_config_dword(tg3.pdev, TG3PCI_MEM_WIN_BASE_ADDR, off); pci_read_config_dword(tg3.pdev, TG3PCI_MEM_WIN_DATA, val); /* Always leave this as zero. */ pci_write_config_dword(tg3.pdev, TG3PCI_MEM_WIN_BASE_ADDR, 0);}static void tg3_disable_ints(struct tg3 *tp){ tw32(TG3PCI_MISC_HOST_CTRL, (tp->misc_host_ctrl | MISC_HOST_CTRL_MASK_PCI_INT)); tw32_mailbox2(MAILBOX_INTERRUPT_0 + TG3_64BIT_REG_LOW, 0x00000001);}static void tg3_switch_clocks(struct tg3 *tp){ uint32_t orig_clock_ctrl, clock_ctrl; clock_ctrl = tr32(TG3PCI_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 ((GET_ASIC_REV(tp->pci_chip_rev_id) != ASIC_REV_5705) && (orig_clock_ctrl & CLOCK_CTRL_44MHZ_CORE)!=0) { tw32_carefully(TG3PCI_CLOCK_CTRL, clock_ctrl | (CLOCK_CTRL_44MHZ_CORE | CLOCK_CTRL_ALTCLK)); tw32_carefully(TG3PCI_CLOCK_CTRL, clock_ctrl | (CLOCK_CTRL_ALTCLK)); } tw32_carefully(TG3PCI_CLOCK_CTRL, clock_ctrl);}#define PHY_BUSY_LOOPS 5000static int tg3_readphy(struct tg3 *tp, int reg, uint32_t *val){ uint32_t frame_val; int loops, ret; tw32_carefully(MAC_MI_MODE, tp->mi_mode & ~MAC_MI_MODE_AUTO_POLL); *val = 0xffffffff; 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_carefully(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; } } ret = -EBUSY; if (loops > 0) { *val = frame_val & MI_COM_DATA_MASK; ret = 0; } tw32_carefully(MAC_MI_MODE, tp->mi_mode); return ret;}static int tg3_writephy(struct tg3 *tp, int reg, uint32_t val){ uint32_t frame_val; int loops, ret; tw32_carefully(MAC_MI_MODE, tp->mi_mode & ~MAC_MI_MODE_AUTO_POLL); 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_carefully(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; } } ret = -EBUSY; if (loops > 0) ret = 0; tw32_carefully(MAC_MI_MODE, tp->mi_mode); return ret;}static int tg3_writedsp(struct tg3 *tp, uint16_t addr, uint16_t val){ int err; err = tg3_writephy(tp, MII_TG3_DSP_ADDRESS, addr); err |= tg3_writephy(tp, MII_TG3_DSP_RW_PORT, val); return err;}static void tg3_phy_set_wirespeed(struct tg3 *tp){ uint32_t val; if (tp->tg3_flags2 & TG3_FLG2_NO_ETH_WIRE_SPEED) return; 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){ uint32_t 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--) { uint32_t tmp32; 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 uint32_t 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) { uint32_t low, high; tg3_readphy(tp, MII_TG3_DSP_RW_PORT, &low); tg3_readphy(tp, MII_TG3_DSP_RW_PORT, &high); if (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){ uint32_t 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. */ tg3_readphy(tp, MII_TG3_EXT_CTRL, ®32); 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. */ tg3_readphy(tp, MII_TG3_CTRL, &phy9_orig); 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); tg3_writephy(tp, MII_TG3_AUX_CTRL, 0x0400); tg3_writephy(tp, MII_TG3_CTRL, phy9_orig); tg3_readphy(tp, MII_TG3_EXT_CTRL, ®32); reg32 &= ~0x3000; tg3_writephy(tp, MII_TG3_EXT_CTRL, reg32); return err;}/* This will reset the tigon3 PHY if there is no valid * link. */static int tg3_phy_reset(struct tg3 *tp){ uint32_t 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)
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -