Fixes the rx stall problem on 82578 by MANY workaround code.

We need more work for 82577.
This commit is contained in:
msaitoh 2010-01-14 18:56:01 +00:00
parent 79f027bbd6
commit 86ff2cac75
4 changed files with 500 additions and 118 deletions

110
sys/dev/mii/inbmphyreg.h Normal file
View File

@ -0,0 +1,110 @@
/* $NetBSD: inbmphyreg.h,v 1.1 2010/01/14 18:56:01 msaitoh Exp $ */
/*******************************************************************************
Copyright (c) 2001-2005, Intel Corporation
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice,
this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
3. Neither the name of the Intel Corporation nor the names of its
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.
*******************************************************************************/
/*
* Copied from the Intel code, and then modified to match NetBSD
* style for MII registers more.
*/
#ifndef _DEV_MII_INBMPHYREG_H_
#define _DEV_MII_INBMPHYREG_H_
/* Bits...
* 15-5: page
* 4-0: register offset
*/
#define BME1000_PAGE_SHIFT 5
#define BME1000_REG(page, reg) \
(((page) << BME1000_PAGE_SHIFT) | ((reg) & BME1000_MAX_REG_ADDRESS))
#define BME1000_MAX_REG_ADDRESS 0x1f /* 5 bit address bus (0-0x1f) */
#define BME1000_MAX_MULTI_PAGE_REG 0xf /* Registers equal on all pages */
#define BM_PHY_REG_PAGE(offset) \
((uint16_t)(((offset) >> BME1000_PAGE_SHIFT) & 0xffff))
#define BM_PHY_REG_NUM(offset) \
((uint16_t)((offset) & BME1000_MAX_REG_ADDRESS) \
| (((offset) >> (21 - BME1000_PAGE_SHIFT)) & ~BME1000_MAX_REG_ADDRESS))
/* BME1000 Specific Registers */
#define BME1000_PHY_SPEC_CTRL BME1000_REG(0, 16) /* PHY Specific Control */
#define BME1000_PSCR_DISABLE_JABBER 0x0001 /* 1=Disable Jabber */
#define BME1000_PSCR_POLARITY_REVERSAL_DISABLE 0x0002 /* 1=Polarity Reversal Disabled */
#define BME1000_PSCR_POWER_DOWN 0x0004 /* 1=Power Down */
#define BME1000_PSCR_COPPER_TRANSMITER_DISABLE 0x0008 /* 1=Transmitter Disabled */
#define BME1000_PSCR_CROSSOVER_MODE_MASK 0x0060
#define BME1000_PSCR_CROSSOVER_MODE_MDI 0x0000 /* 00=Manual MDI configuration */
#define BME1000_PSCR_CROSSOVER_MODE_MDIX 0x0020 /* 01=Manual MDIX configuration */
#define BME1000_PSCR_CROSSOVER_MODE_AUTO 0x0060 /* 11=Automatic crossover */
#define BME1000_PSCR_ENALBE_EXTENDED_DISTANCE 0x0080 /* 1=Enable Extended Distance */
#define BME1000_PSCR_ENERGY_DETECT_MASK 0x0300
#define BME1000_PSCR_ENERGY_DETECT_OFF 0x0000 /* 00,01=Off */
#define BME1000_PSCR_ENERGY_DETECT_RX 0x0200 /* 10=Sense on Rx only (Energy Detect) */
#define BME1000_PSCR_ENERGY_DETECT_RX_TM 0x0300 /* 11=Sense and Tx NLP */
#define BME1000_PSCR_FORCE_LINK_GOOD 0x0400 /* 1=Force Link Good */
#define BME1000_PSCR_DOWNSHIFT_ENABLE 0x0800 /* 1=Enable Downshift */
#define BME1000_PSCR_DOWNSHIFT_COUNTER_MASK 0x7000
#define BME1000_PSCR_DOWNSHIFT_COUNTER_SHIFT 12
#define BME1000_PHY_PAGE_SELECT BME1000_REG(0, 22) /* Page Select */
#define BME1000_BIAS_SETTING 29
#define BME1000_BIAS_SETTING2 30
#define I82578_ADDR_REG 29
#define I82577_ADDR_REG 16
#define I82577_CFG_REG 22
#define HV_OEM_BITS BME1000_REG(0, 25)
#define HV_OEM_BITS_LPLU (1 << 2)
#define HV_OEM_BITS_A1KDIS (1 << 6)
#define HV_OEM_BITS_ANEGNOW (1 << 10)
#define HV_INTC_FC_PAGE_START 768
#define BM_PORT_CTRL_PAGE 769
#define IGP3_KMRN_DIAG BME1000_REG(770, 19)
#define HV_MUX_DATA_CTRL BME1000_REG(776, 16)
#define HV_MUX_DATA_CTRL_FORCE_SPEED (1 << 2)
#define HV_MUX_DATA_CTRL_GEN_TO_MAC (1 << 10)
#define BM_WUC_PAGE 800
#define BM_WUC BME1000_REG(BM_WUC_PAGE, 1)
#define BM_WUC_ADDRESS_OPCODE 0x11
#define BM_WUC_DATA_OPCODE 0x12
#define BM_WUC_ENABLE_PAGE BM_PORT_CTRL_PAGE
#define BM_WUC_ENABLE_REG 17
#define BM_WUC_ENABLE_BIT (1 << 2)
#define BM_WUC_HOST_WU_BIT (1 << 4)
#endif /* _DEV_MII_INBMPHYREG_H_ */

View File

@ -1,4 +1,4 @@
/* $NetBSD: if_wm.c,v 1.191 2010/01/12 22:26:30 msaitoh Exp $ */ /* $NetBSD: if_wm.c,v 1.192 2010/01/14 18:56:02 msaitoh Exp $ */
/* /*
* Copyright (c) 2001, 2002, 2003, 2004 Wasabi Systems, Inc. * Copyright (c) 2001, 2002, 2003, 2004 Wasabi Systems, Inc.
@ -76,7 +76,7 @@
*/ */
#include <sys/cdefs.h> #include <sys/cdefs.h>
__KERNEL_RCSID(0, "$NetBSD: if_wm.c,v 1.191 2010/01/12 22:26:30 msaitoh Exp $"); __KERNEL_RCSID(0, "$NetBSD: if_wm.c,v 1.192 2010/01/14 18:56:02 msaitoh Exp $");
#include "bpfilter.h" #include "bpfilter.h"
#include "rnd.h" #include "rnd.h"
@ -124,6 +124,7 @@ __KERNEL_RCSID(0, "$NetBSD: if_wm.c,v 1.191 2010/01/12 22:26:30 msaitoh Exp $");
#include <dev/mii/mii_bitbang.h> #include <dev/mii/mii_bitbang.h>
#include <dev/mii/ikphyreg.h> #include <dev/mii/ikphyreg.h>
#include <dev/mii/igphyreg.h> #include <dev/mii/igphyreg.h>
#include <dev/mii/inbmphyreg.h>
#include <dev/pci/pcireg.h> #include <dev/pci/pcireg.h>
#include <dev/pci/pcivar.h> #include <dev/pci/pcivar.h>
@ -249,7 +250,9 @@ struct wm_softc {
pci_chipset_tag_t sc_pc; pci_chipset_tag_t sc_pc;
pcitag_t sc_pcitag; pcitag_t sc_pcitag;
wm_chip_type sc_type; /* chip type */ wm_chip_type sc_type; /* MAC type */
int sc_rev; /* MAC revision */
wm_phy_type sc_phytype; /* PHY type */
int sc_flags; /* flags; see below */ int sc_flags; /* flags; see below */
int sc_if_flags; /* last if_flags */ int sc_if_flags; /* last if_flags */
int sc_bus_speed; /* PCI/PCIX bus speed */ int sc_bus_speed; /* PCI/PCIX bus speed */
@ -366,6 +369,7 @@ struct wm_softc {
#endif #endif
int sc_ich8_flash_base; int sc_ich8_flash_base;
int sc_ich8_flash_bank_size; int sc_ich8_flash_bank_size;
int sc_nvm_k1_enabled;
}; };
#define WM_RXCHAIN_RESET(sc) \ #define WM_RXCHAIN_RESET(sc) \
@ -520,8 +524,8 @@ static int wm_gmii_i80003_readreg(device_t, int, int);
static void wm_gmii_i80003_writereg(device_t, int, int, int); static void wm_gmii_i80003_writereg(device_t, int, int, int);
static int wm_gmii_bm_readreg(device_t, int, int); static int wm_gmii_bm_readreg(device_t, int, int);
static void wm_gmii_bm_writereg(device_t, int, int, int); static void wm_gmii_bm_writereg(device_t, int, int, int);
static int wm_gmii_kv_readreg(device_t, int, int); static int wm_gmii_hv_readreg(device_t, int, int);
static void wm_gmii_kv_writereg(device_t, int, int, int); static void wm_gmii_hv_writereg(device_t, int, int, int);
static void wm_gmii_statchg(device_t); static void wm_gmii_statchg(device_t);
@ -562,6 +566,9 @@ static int wm_check_mng_mode_generic(struct wm_softc *);
static int wm_check_reset_block(struct wm_softc *); static int wm_check_reset_block(struct wm_softc *);
static void wm_get_hw_control(struct wm_softc *); static void wm_get_hw_control(struct wm_softc *);
static int wm_check_for_link(struct wm_softc *); static int wm_check_for_link(struct wm_softc *);
static void wm_hv_phy_workaround_ich8lan(struct wm_softc *);
static void wm_k1_gig_workaround_hv(struct wm_softc *, int);
static void wm_configure_k1_ich8lan(struct wm_softc *, int);
CFATTACH_DECL_NEW(wm, sizeof(struct wm_softc), CFATTACH_DECL_NEW(wm, sizeof(struct wm_softc),
wm_match, wm_attach, NULL, NULL); wm_match, wm_attach, NULL, NULL);
@ -991,18 +998,18 @@ wm_attach(device_t parent, device_t self, void *aux)
else else
sc->sc_dmat = pa->pa_dmat; sc->sc_dmat = pa->pa_dmat;
preg = PCI_REVISION(pci_conf_read(pc, pa->pa_tag, PCI_CLASS_REG)); sc->sc_rev = PCI_REVISION(pci_conf_read(pc, pa->pa_tag, PCI_CLASS_REG));
aprint_naive(": Ethernet controller\n"); aprint_naive(": Ethernet controller\n");
aprint_normal(": %s, rev. %d\n", wmp->wmp_name, preg); aprint_normal(": %s, rev. %d\n", wmp->wmp_name, sc->sc_rev);
sc->sc_type = wmp->wmp_type; sc->sc_type = wmp->wmp_type;
if (sc->sc_type < WM_T_82543) { if (sc->sc_type < WM_T_82543) {
if (preg < 2) { if (sc->sc_rev < 2) {
aprint_error_dev(sc->sc_dev, aprint_error_dev(sc->sc_dev,
"i82542 must be at least rev. 2\n"); "i82542 must be at least rev. 2\n");
return; return;
} }
if (preg < 3) if (sc->sc_rev < 3)
sc->sc_type = WM_T_82542_2_0; sc->sc_type = WM_T_82542_2_0;
} }
@ -1575,6 +1582,18 @@ wm_attach(device_t parent, device_t self, void *aux)
sc->sc_tdt_reg = WMREG_TDT; sc->sc_tdt_reg = WMREG_TDT;
} }
if (sc->sc_type == WM_T_PCH) {
uint16_t val;
/* Save the NVM K1 bit setting */
wm_read_eeprom(sc, EEPROM_OFF_K1_CONFIG, 1, &val);
if ((val & EEPROM_K1_CONFIG_ENABLE) != 0)
sc->sc_nvm_k1_enabled = 1;
else
sc->sc_nvm_k1_enabled = 0;
}
/* /*
* Determine if we're TBI or GMII mode, and initialize the * Determine if we're TBI or GMII mode, and initialize the
* media structures accordingly. * media structures accordingly.
@ -2896,74 +2915,101 @@ wm_rxintr(struct wm_softc *sc)
} }
/* /*
* wm_linkintr: * wm_linkintr_gmii:
* *
* Helper; handle link interrupts. * Helper; handle link interrupts for GMII.
*/ */
static void static void
wm_linkintr(struct wm_softc *sc, uint32_t icr) wm_linkintr_gmii(struct wm_softc *sc, uint32_t icr)
{
DPRINTF(WM_DEBUG_LINK, ("%s: %s:\n", device_xname(sc->sc_dev),
__func__));
if (icr & ICR_LSC) {
DPRINTF(WM_DEBUG_LINK,
("%s: LINK: LSC -> mii_tick\n",
device_xname(sc->sc_dev)));
mii_tick(&sc->sc_mii);
if (sc->sc_type == WM_T_82543) {
int miistatus, active;
/*
* With 82543, we need to force speed and
* duplex on the MAC equal to what the PHY
* speed and duplex configuration is.
*/
miistatus = sc->sc_mii.mii_media_status;
if (miistatus & IFM_ACTIVE) {
active = sc->sc_mii.mii_media_active;
sc->sc_ctrl &= ~(CTRL_SPEED_MASK | CTRL_FD);
switch (IFM_SUBTYPE(active)) {
case IFM_10_T:
sc->sc_ctrl |= CTRL_SPEED_10;
break;
case IFM_100_TX:
sc->sc_ctrl |= CTRL_SPEED_100;
break;
case IFM_1000_T:
sc->sc_ctrl |= CTRL_SPEED_1000;
break;
default:
/*
* fiber?
* Shoud not enter here.
*/
printf("unknown media (%x)\n",
active);
break;
}
if (active & IFM_FDX)
sc->sc_ctrl |= CTRL_FD;
CSR_WRITE(sc, WMREG_CTRL, sc->sc_ctrl);
}
} else if (sc->sc_type == WM_T_PCH) {
wm_k1_gig_workaround_hv(sc,
((sc->sc_mii.mii_media_status & IFM_ACTIVE) != 0));
}
if ((sc->sc_phytype == WMPHY_82578)
&& (IFM_SUBTYPE(sc->sc_mii.mii_media_active)
== IFM_1000_T)) {
if ((sc->sc_mii.mii_media_status & IFM_ACTIVE) != 0) {
printf("XXX link sall wa\n");
delay(200*1000); /* XXX too big */
/* Link stall fix for link up */
wm_gmii_hv_writereg(sc->sc_dev, 1,
HV_MUX_DATA_CTRL,
HV_MUX_DATA_CTRL_GEN_TO_MAC
| HV_MUX_DATA_CTRL_FORCE_SPEED);
wm_gmii_hv_writereg(sc->sc_dev, 1,
HV_MUX_DATA_CTRL,
HV_MUX_DATA_CTRL_GEN_TO_MAC);
}
}
} else if (icr & ICR_RXSEQ) {
DPRINTF(WM_DEBUG_LINK,
("%s: LINK Receive sequence error\n",
device_xname(sc->sc_dev)));
}
}
/*
* wm_linkintr_tbi:
*
* Helper; handle link interrupts for TBI mode.
*/
static void
wm_linkintr_tbi(struct wm_softc *sc, uint32_t icr)
{ {
uint32_t status; uint32_t status;
DPRINTF(WM_DEBUG_LINK, ("%s: %s:\n", device_xname(sc->sc_dev), DPRINTF(WM_DEBUG_LINK, ("%s: %s:\n", device_xname(sc->sc_dev),
__func__)); __func__));
/*
* If we get a link status interrupt on a 1000BASE-T
* device, just fall into the normal MII tick path.
*/
if (sc->sc_flags & WM_F_HAS_MII) {
if (icr & ICR_LSC) {
DPRINTF(WM_DEBUG_LINK,
("%s: LINK: LSC -> mii_tick\n",
device_xname(sc->sc_dev)));
mii_tick(&sc->sc_mii);
if (sc->sc_type == WM_T_82543) {
int miistatus, active;
/*
* With 82543, we need to force speed and
* duplex on the MAC equal to what the PHY
* speed and duplex configuration is.
*/
miistatus = sc->sc_mii.mii_media_status;
if (miistatus & IFM_ACTIVE) {
active = sc->sc_mii.mii_media_active;
sc->sc_ctrl &= ~(CTRL_SPEED_MASK
| CTRL_FD);
switch (IFM_SUBTYPE(active)) {
case IFM_10_T:
sc->sc_ctrl |= CTRL_SPEED_10;
break;
case IFM_100_TX:
sc->sc_ctrl |= CTRL_SPEED_100;
break;
case IFM_1000_T:
sc->sc_ctrl |= CTRL_SPEED_1000;
break;
default:
/*
* fiber?
* Shoud not enter here.
*/
printf("unknown media (%x)\n",
active);
break;
}
if (active & IFM_FDX)
sc->sc_ctrl |= CTRL_FD;
CSR_WRITE(sc, WMREG_CTRL, sc->sc_ctrl);
}
}
} else if (icr & ICR_RXSEQ) {
DPRINTF(WM_DEBUG_LINK,
("%s: LINK Receive sequence error\n",
device_xname(sc->sc_dev)));
}
return;
}
/* TBI mode */
status = CSR_READ(sc, WMREG_STATUS); status = CSR_READ(sc, WMREG_STATUS);
if (icr & ICR_LSC) { if (icr & ICR_LSC) {
if (status & STATUS_LU) { if (status & STATUS_LU) {
@ -3009,6 +3055,21 @@ wm_linkintr(struct wm_softc *sc, uint32_t icr)
} }
} }
/*
* wm_linkintr:
*
* Helper; handle link interrupts.
*/
static void
wm_linkintr(struct wm_softc *sc, uint32_t icr)
{
if (sc->sc_flags & WM_F_HAS_MII)
wm_linkintr_gmii(sc, icr);
else
wm_linkintr_tbi(sc, icr);
}
/* /*
* wm_tick: * wm_tick:
* *
@ -3159,10 +3220,6 @@ wm_reset(struct wm_softc *sc)
delay(5000); delay(5000);
} }
if (sc->sc_type == WM_T_PCH) {
/* Save K1 */
}
switch (sc->sc_type) { switch (sc->sc_type) {
case WM_T_82544: /* XXX check whether WM_F_IOH_VALID is set */ case WM_T_82544: /* XXX check whether WM_F_IOH_VALID is set */
case WM_T_82541: case WM_T_82541:
@ -3303,6 +3360,9 @@ wm_reset(struct wm_softc *sc)
/* reload sc_ctrl */ /* reload sc_ctrl */
sc->sc_ctrl = CSR_READ(sc, WMREG_CTRL); sc->sc_ctrl = CSR_READ(sc, WMREG_CTRL);
/* dummy read from WUC */
if (sc->sc_type == WM_T_PCH)
reg = wm_gmii_hv_readreg(sc->sc_dev, 1, BM_WUC);
/* /*
* For PCH, this write will make sure that any noise will be detected * For PCH, this write will make sure that any noise will be detected
* as a CRC error and be dropped rather than show up as a bad packet * as a CRC error and be dropped rather than show up as a bad packet
@ -3390,6 +3450,11 @@ wm_init(struct ifnet *ifp)
if (sc->sc_flags & WM_F_HAS_MII) if (sc->sc_flags & WM_F_HAS_MII)
wm_gmii_reset(sc); wm_gmii_reset(sc);
reg = CSR_READ(sc, WMREG_CTRL_EXT);
/* Enable PHY low-power state when MAC is at D3 w/o WoL */
if (sc->sc_type == WM_T_PCH)
CSR_WRITE(sc, WMREG_CTRL_EXT, reg | CTRL_EXT_PHYPDEN);
/* Initialize the transmit descriptor ring. */ /* Initialize the transmit descriptor ring. */
memset(sc->sc_txdescs, 0, WM_TXDESCSIZE(sc)); memset(sc->sc_txdescs, 0, WM_TXDESCSIZE(sc));
WM_CDTXSYNC(sc, 0, WM_NTXDESC(sc), WM_CDTXSYNC(sc, 0, WM_NTXDESC(sc),
@ -4779,7 +4844,7 @@ wm_gmii_reset(struct wm_softc *sc)
case WM_T_82573: case WM_T_82573:
case WM_T_82574: case WM_T_82574:
case WM_T_82583: case WM_T_82583:
/* XXX sould get sw semaphore, too */ /* XXX should get sw semaphore, too */
rv = wm_get_swsm_semaphore(sc); rv = wm_get_swsm_semaphore(sc);
break; break;
case WM_T_80003: case WM_T_80003:
@ -4874,11 +4939,6 @@ wm_gmii_reset(struct wm_softc *sc)
delay(100); delay(100);
CSR_WRITE(sc, WMREG_CTRL, sc->sc_ctrl); CSR_WRITE(sc, WMREG_CTRL, sc->sc_ctrl);
delay(150); delay(150);
/* Allow time for h/w to get to a quiescent state afer reset */
delay(10*1000);
/* XXX add code to set LED after phy reset */
break; break;
default: default:
panic("%s: %s: unknown type\n", device_xname(sc->sc_dev), panic("%s: %s: unknown type\n", device_xname(sc->sc_dev),
@ -4943,36 +5003,34 @@ wm_gmii_reset(struct wm_softc *sc)
case WM_T_ICH9: case WM_T_ICH9:
case WM_T_ICH10: case WM_T_ICH10:
case WM_T_PCH: case WM_T_PCH:
/* Allow time for h/w to get to a quiescent state afer reset */
delay(10*1000); delay(10*1000);
if (sc->sc_type == WM_T_PCH) { if (sc->sc_type == WM_T_PCH) {
/* XXX hv_phy_workaround */ wm_hv_phy_workaround_ich8lan(sc);
/* dummy read from WUC */
}
/* XXX SW LCD configuration from NVM */
if (sc->sc_type == WM_T_PCH) {
/* XXX Configure the LCD with the OEM bits in NVM */
#if 1
/* /*
* We shlould make the new driver for 8257[78] and * dummy read to clear the phy wakeup bit after lcd
* move these code into it. * reset
*/ */
#define HV_OEM_BITS ((0 << 5) | 25) reg = wm_gmii_hv_readreg(sc->sc_dev, 1, BM_WUC);
#define HV_OEM_BITS_LPLU (1 << 2) }
#define HV_OEM_BITS_A1KDIS (1 << 6)
#define HV_OEM_BITS_ANEGNOW (1 << 10) /*
#endif * XXX Configure the LCD with th extended configuration region
* in NVM
*/
/* Configure the LCD with the OEM bits in NVM */
if (sc->sc_type == WM_T_PCH) {
/* /*
* Disable LPLU. * Disable LPLU.
* XXX It seems that 82567 has LPLU, too. * XXX It seems that 82567 has LPLU, too.
*/ */
reg = wm_gmii_kv_readreg(sc->sc_dev, 1, HV_OEM_BITS); reg = wm_gmii_hv_readreg(sc->sc_dev, 1, HV_OEM_BITS);
reg &= ~(HV_OEM_BITS_A1KDIS| HV_OEM_BITS_LPLU); reg &= ~(HV_OEM_BITS_A1KDIS| HV_OEM_BITS_LPLU);
reg |= HV_OEM_BITS_ANEGNOW; reg |= HV_OEM_BITS_ANEGNOW;
wm_gmii_kv_writereg(sc->sc_dev, 1, HV_OEM_BITS, reg); wm_gmii_hv_writereg(sc->sc_dev, 1, HV_OEM_BITS, reg);
} }
break; break;
default: default:
@ -5014,11 +5072,17 @@ wm_gmii_mediainit(struct wm_softc *sc, pci_product_id_t prodid)
switch (prodid) { switch (prodid) {
case PCI_PRODUCT_INTEL_PCH_M_LM: case PCI_PRODUCT_INTEL_PCH_M_LM:
case PCI_PRODUCT_INTEL_PCH_M_LC: case PCI_PRODUCT_INTEL_PCH_M_LC:
/* 82577 */
sc->sc_phytype = WMPHY_82577;
sc->sc_mii.mii_readreg = wm_gmii_hv_readreg;
sc->sc_mii.mii_writereg = wm_gmii_hv_writereg;
break;
case PCI_PRODUCT_INTEL_PCH_D_DM: case PCI_PRODUCT_INTEL_PCH_D_DM:
case PCI_PRODUCT_INTEL_PCH_D_DC: case PCI_PRODUCT_INTEL_PCH_D_DC:
/* 82577 or 82578 */ /* 82578 */
sc->sc_mii.mii_readreg = wm_gmii_kv_readreg; sc->sc_phytype = WMPHY_82578;
sc->sc_mii.mii_writereg = wm_gmii_kv_writereg; sc->sc_mii.mii_readreg = wm_gmii_hv_readreg;
sc->sc_mii.mii_writereg = wm_gmii_hv_writereg;
break; break;
case PCI_PRODUCT_INTEL_82801I_BM: case PCI_PRODUCT_INTEL_82801I_BM:
case PCI_PRODUCT_INTEL_82801J_R_BM_LM: case PCI_PRODUCT_INTEL_82801J_R_BM_LM:
@ -5027,6 +5091,7 @@ wm_gmii_mediainit(struct wm_softc *sc, pci_product_id_t prodid)
case PCI_PRODUCT_INTEL_82801J_D_BM_LF: case PCI_PRODUCT_INTEL_82801J_D_BM_LF:
case PCI_PRODUCT_INTEL_82801J_R_BM_V: case PCI_PRODUCT_INTEL_82801J_R_BM_V:
/* 82567 */ /* 82567 */
sc->sc_phytype = WMPHY_BM;
sc->sc_mii.mii_readreg = wm_gmii_bm_readreg; sc->sc_mii.mii_readreg = wm_gmii_bm_readreg;
sc->sc_mii.mii_writereg = wm_gmii_bm_writereg; sc->sc_mii.mii_writereg = wm_gmii_bm_writereg;
break; break;
@ -5066,8 +5131,10 @@ wm_gmii_mediainit(struct wm_softc *sc, pci_product_id_t prodid)
if (LIST_FIRST(&sc->sc_mii.mii_phys) == NULL) { if (LIST_FIRST(&sc->sc_mii.mii_phys) == NULL) {
ifmedia_add(&sc->sc_mii.mii_media, IFM_ETHER|IFM_NONE, 0, NULL); ifmedia_add(&sc->sc_mii.mii_media, IFM_ETHER|IFM_NONE, 0, NULL);
ifmedia_set(&sc->sc_mii.mii_media, IFM_ETHER|IFM_NONE); ifmedia_set(&sc->sc_mii.mii_media, IFM_ETHER|IFM_NONE);
} else sc->sc_phytype = WMPHY_NONE;
} else {
ifmedia_set(&sc->sc_mii.mii_media, IFM_ETHER|IFM_AUTO); ifmedia_set(&sc->sc_mii.mii_media, IFM_ETHER|IFM_AUTO);
}
} }
/* /*
@ -5405,7 +5472,7 @@ wm_gmii_bm_readreg(device_t self, int phy, int reg)
return 0; return 0;
} }
if (reg > GG82563_MAX_REG_ADDRESS) { if (reg > BME1000_MAX_MULTI_PAGE_REG) {
if (phy == 1) if (phy == 1)
wm_gmii_i82544_writereg(self, phy, 0x1f, wm_gmii_i82544_writereg(self, phy, 0x1f,
reg); reg);
@ -5439,7 +5506,7 @@ wm_gmii_bm_writereg(device_t self, int phy, int reg, int val)
return; return;
} }
if (reg > GG82563_MAX_REG_ADDRESS) { if (reg > BME1000_MAX_MULTI_PAGE_REG) {
if (phy == 1) if (phy == 1)
wm_gmii_i82544_writereg(self, phy, 0x1f, wm_gmii_i82544_writereg(self, phy, 0x1f,
reg); reg);
@ -5453,17 +5520,61 @@ wm_gmii_bm_writereg(device_t self, int phy, int reg, int val)
wm_put_swfw_semaphore(sc, func ? SWFW_PHY1_SM : SWFW_PHY0_SM); wm_put_swfw_semaphore(sc, func ? SWFW_PHY1_SM : SWFW_PHY0_SM);
} }
static void
wm_access_phy_wakeup_reg_bm(device_t self, int offset, int16_t *val, int rd)
{
struct wm_softc *sc = device_private(self);
uint16_t regnum = BM_PHY_REG_NUM(offset);
uint16_t wuce;
/* XXX Gig must be disabled for MDIO accesses to page 800 */
if (sc->sc_type == WM_T_PCH) {
/* XXX e1000 driver do nothing... why? */
}
/* Set page 769 */
wm_gmii_i82544_writereg(self, 1, MII_IGPHY_PAGE_SELECT,
BM_WUC_ENABLE_PAGE << BME1000_PAGE_SHIFT);
wuce = wm_gmii_i82544_readreg(self, 1, BM_WUC_ENABLE_REG);
wuce &= ~BM_WUC_HOST_WU_BIT;
wm_gmii_i82544_writereg(self, 1, BM_WUC_ENABLE_REG,
wuce | BM_WUC_ENABLE_BIT);
/* Select page 800 */
wm_gmii_i82544_writereg(self, 1, MII_IGPHY_PAGE_SELECT,
BM_WUC_PAGE << BME1000_PAGE_SHIFT);
/* Write page 800 */
wm_gmii_i82544_writereg(self, 1, BM_WUC_ADDRESS_OPCODE, regnum);
if (rd)
*val = wm_gmii_i82544_readreg(self, 1, BM_WUC_DATA_OPCODE);
else
wm_gmii_i82544_writereg(self, 1, BM_WUC_DATA_OPCODE, *val);
/* Set page 769 */
wm_gmii_i82544_writereg(self, 1, MII_IGPHY_PAGE_SELECT,
BM_WUC_ENABLE_PAGE << BME1000_PAGE_SHIFT);
wm_gmii_i82544_writereg(self, 1, BM_WUC_ENABLE_REG, wuce);
}
/* /*
* wm_gmii_kv_readreg: [mii interface function] * wm_gmii_hv_readreg: [mii interface function]
* *
* Read a PHY register on the kumeran * Read a PHY register on the kumeran
* This could be handled by the PHY layer if we didn't have to lock the * This could be handled by the PHY layer if we didn't have to lock the
* ressource ... * ressource ...
*/ */
static int static int
wm_gmii_kv_readreg(device_t self, int phy, int reg) wm_gmii_hv_readreg(device_t self, int phy, int reg)
{ {
struct wm_softc *sc = device_private(self); struct wm_softc *sc = device_private(self);
uint16_t page = BM_PHY_REG_PAGE(reg);
uint16_t regnum = BM_PHY_REG_NUM(reg);
uint16_t val;
int rv; int rv;
if (wm_get_swfw_semaphore(sc, SWFW_PHY0_SM)) { if (wm_get_swfw_semaphore(sc, SWFW_PHY0_SM)) {
@ -5472,28 +5583,49 @@ wm_gmii_kv_readreg(device_t self, int phy, int reg)
return 0; return 0;
} }
if (reg > GG82563_MAX_REG_ADDRESS) { /* XXX Workaround failure in MDIO access while cable is disconnected */
printf("XXX rd pagesel\n"); if (sc->sc_phytype == WMPHY_82577) {
wm_gmii_i82544_writereg(self, 1, MII_IGPHY_PAGE_SELECT, /* XXX must write */
reg & IGPHY_PAGEMASK);
} }
rv = wm_gmii_i82544_readreg(self, phy, reg & IGPHY_MAXREGADDR); /* Page 800 works differently than the rest so it has its own func */
if (page == BM_WUC_PAGE) {
wm_access_phy_wakeup_reg_bm(self, reg, &val, 1);
return val;
}
/*
* Lower than page 768 works differently than the rest so it has its
* own func
*/
if ((page > 0) && (page < HV_INTC_FC_PAGE_START)) {
printf("gmii_hv_readreg!!!\n");
return 0;
}
if (regnum > BME1000_MAX_MULTI_PAGE_REG) {
wm_gmii_i82544_writereg(self, 1, MII_IGPHY_PAGE_SELECT,
page << BME1000_PAGE_SHIFT);
}
rv = wm_gmii_i82544_readreg(self, phy, regnum & IGPHY_MAXREGADDR);
wm_put_swfw_semaphore(sc, SWFW_PHY0_SM); wm_put_swfw_semaphore(sc, SWFW_PHY0_SM);
return (rv); return (rv);
} }
/* /*
* wm_gmii_kv_writereg: [mii interface function] * wm_gmii_hv_writereg: [mii interface function]
* *
* Write a PHY register on the kumeran. * Write a PHY register on the kumeran.
* This could be handled by the PHY layer if we didn't have to lock the * This could be handled by the PHY layer if we didn't have to lock the
* ressource ... * ressource ...
*/ */
static void static void
wm_gmii_kv_writereg(device_t self, int phy, int reg, int val) wm_gmii_hv_writereg(device_t self, int phy, int reg, int val)
{ {
struct wm_softc *sc = device_private(self); struct wm_softc *sc = device_private(self);
uint16_t page = BM_PHY_REG_PAGE(reg);
uint16_t regnum = BM_PHY_REG_NUM(reg);
if (wm_get_swfw_semaphore(sc, SWFW_PHY0_SM)) { if (wm_get_swfw_semaphore(sc, SWFW_PHY0_SM)) {
aprint_error_dev(sc->sc_dev, "%s: failed to get semaphore\n", aprint_error_dev(sc->sc_dev, "%s: failed to get semaphore\n",
@ -5501,13 +5633,37 @@ wm_gmii_kv_writereg(device_t self, int phy, int reg, int val)
return; return;
} }
if (reg > GG82563_MAX_REG_ADDRESS) { /* XXX Workaround failure in MDIO access while cable is disconnected */
printf("XXX wr pagesel\n");
wm_gmii_i82544_writereg(self, 1, MII_IGPHY_PAGE_SELECT, /* Page 800 works differently than the rest so it has its own func */
reg & IGPHY_PAGEMASK); if (page == BM_WUC_PAGE) {
uint16_t tmp;
tmp = val;
wm_access_phy_wakeup_reg_bm(self, reg, &tmp, 0);
return;
} }
wm_gmii_i82544_writereg(self, phy, reg & IGPHY_MAXREGADDR, val); /*
* Lower than page 768 works differently than the rest so it has its
* own func
*/
if ((page > 0) && (page < HV_INTC_FC_PAGE_START)) {
printf("gmii_hv_writereg!!!\n");
return;
}
/*
* XXX Workaround MDIO accesses being disabled after entering IEEE
* Power Down (whenever bit 11 of the PHY control register is set)
*/
if (regnum > BME1000_MAX_MULTI_PAGE_REG) {
wm_gmii_i82544_writereg(self, 1, MII_IGPHY_PAGE_SELECT,
page << BME1000_PAGE_SHIFT);
}
wm_gmii_i82544_writereg(self, phy, regnum & IGPHY_MAXREGADDR, val);
wm_put_swfw_semaphore(sc, SWFW_PHY0_SM); wm_put_swfw_semaphore(sc, SWFW_PHY0_SM);
} }
@ -6289,3 +6445,96 @@ wm_check_for_link(struct wm_softc *sc)
return 0; return 0;
} }
/*
* Workaround for pch's PHYs
* XXX should be moved to new PHY driver?
*/
static void
wm_hv_phy_workaround_ich8lan(struct wm_softc *sc)
{
/* (PCH rev.2) && (82577 && (phy rev 2 or 3)) */
/* (82577 && (phy rev 1 or 2)) || (82578 & phy rev 1)*/
/* 82578 */
if (sc->sc_phytype == WMPHY_82578) {
/* PCH rev. < 3 */
if (sc->sc_rev < 3) {
/* XXX 6 bit shift? Why? Is it page2? */
wm_gmii_hv_writereg(sc->sc_dev, 1, ((1 << 6) | 0x29),
0x66c0);
wm_gmii_hv_writereg(sc->sc_dev, 1, ((1 << 6) | 0x1e),
0xffff);
}
/* XXX phy rev. < 2 */
}
/* Select page 0 */
/* XXX acquire semaphore */
wm_gmii_i82544_writereg(sc->sc_dev, 1, MII_IGPHY_PAGE_SELECT, 0);
/* XXX release semaphore */
/*
* Configure the K1 Si workaround during phy reset assuming there is
* link so that it disables K1 if link is in 1Gbps.
*/
wm_k1_gig_workaround_hv(sc, 1);
}
static void
wm_k1_gig_workaround_hv(struct wm_softc *sc, int link)
{
int k1_enable = sc->sc_nvm_k1_enabled;
/* XXX acquire semaphore */
if (link) {
k1_enable = 0;
/* Link stall fix for link up */
wm_gmii_hv_writereg(sc->sc_dev, 1, IGP3_KMRN_DIAG, 0x0100);
} else {
/* Link stall fix for link down */
wm_gmii_hv_writereg(sc->sc_dev, 1, IGP3_KMRN_DIAG, 0x4100);
}
wm_configure_k1_ich8lan(sc, k1_enable);
/* XXX release semaphore */
}
static void
wm_configure_k1_ich8lan(struct wm_softc *sc, int k1_enable)
{
uint32_t ctrl, ctrl_ext, tmp;
uint16_t kmrn_reg;
kmrn_reg = wm_kmrn_readreg(sc, KUMCTRLSTA_OFFSET_K1_CONFIG);
if (k1_enable)
kmrn_reg |= KUMCTRLSTA_K1_ENABLE;
else
kmrn_reg &= ~KUMCTRLSTA_K1_ENABLE;
wm_kmrn_writereg(sc, KUMCTRLSTA_OFFSET_K1_CONFIG, kmrn_reg);
delay(20);
ctrl = CSR_READ(sc, WMREG_CTRL);
ctrl_ext = CSR_READ(sc, WMREG_CTRL_EXT);
tmp = ctrl & ~(CTRL_SPEED_1000 | CTRL_SPEED_100);
tmp |= CTRL_FRCSPD;
CSR_WRITE(sc, WMREG_CTRL, tmp);
CSR_WRITE(sc, WMREG_CTRL_EXT, ctrl_ext | CTRL_EXT_SPD_BYPS);
delay(20);
CSR_WRITE(sc, WMREG_CTRL, ctrl);
CSR_WRITE(sc, WMREG_CTRL_EXT, ctrl_ext);
delay(20);
}

View File

@ -1,4 +1,4 @@
/* $NetBSD: if_wmreg.h,v 1.34 2010/01/12 22:26:30 msaitoh Exp $ */ /* $NetBSD: if_wmreg.h,v 1.35 2010/01/14 18:56:02 msaitoh Exp $ */
/* /*
* Copyright (c) 2001 Wasabi Systems, Inc. * Copyright (c) 2001 Wasabi Systems, Inc.
@ -276,6 +276,7 @@ struct livengood_tcpip_ctxdesc {
#define EEPROM_OFF_CFG1 0x0a /* config word 1 */ #define EEPROM_OFF_CFG1 0x0a /* config word 1 */
#define EEPROM_OFF_CFG2 0x0f /* config word 2 */ #define EEPROM_OFF_CFG2 0x0f /* config word 2 */
#define EEPROM_INIT_3GIO_3 0x1a /* PCIe Initial Configuration Word 3 */ #define EEPROM_INIT_3GIO_3 0x1a /* PCIe Initial Configuration Word 3 */
#define EEPROM_OFF_K1_CONFIG 0x1b /* NVM K1 Config */
#define EEPROM_OFF_SWDPIN 0x20 /* SWD Pins (Cordova) */ #define EEPROM_OFF_SWDPIN 0x20 /* SWD Pins (Cordova) */
#define EEPROM_CFG1_LVDID (1U << 0) #define EEPROM_CFG1_LVDID (1U << 0)
@ -309,6 +310,8 @@ struct livengood_tcpip_ctxdesc {
#define EEPROM_CFG2_SWDPIO_MASK (0xf << EEPROM_CFG2_SWDPIO_SHIFT) #define EEPROM_CFG2_SWDPIO_MASK (0xf << EEPROM_CFG2_SWDPIO_SHIFT)
#define EEPROM_CFG2_MNGM_MASK (3U << 13) /* Manageability Operation mode */ #define EEPROM_CFG2_MNGM_MASK (3U << 13) /* Manageability Operation mode */
#define EEPROM_K1_CONFIG_ENABLE 0x01
#define EEPROM_SWDPIN_MASK 0xdf #define EEPROM_SWDPIN_MASK 0xdf
#define EEPROM_SWDPIN_SWDPIN_SHIFT 0 #define EEPROM_SWDPIN_SWDPIN_SHIFT 0
#define EEPROM_SWDPIN_SWDPIO_SHIFT 8 #define EEPROM_SWDPIN_SWDPIO_SHIFT 8
@ -340,6 +343,7 @@ struct livengood_tcpip_ctxdesc {
#define CTRL_EXT_LINK_MODE_TBI 0x00C00000 #define CTRL_EXT_LINK_MODE_TBI 0x00C00000
#define CTRL_EXT_LINK_MODE_KMRN 0x00000000 #define CTRL_EXT_LINK_MODE_KMRN 0x00000000
#define CTRL_EXT_LINK_MODE_SERDES 0x00C00000 #define CTRL_EXT_LINK_MODE_SERDES 0x00C00000
#define CTRL_EXT_PHYPDEN 0x00100000
#define CTRL_EXT_DRV_LOAD 0x10000000 #define CTRL_EXT_DRV_LOAD 0x10000000
@ -657,6 +661,7 @@ struct livengood_tcpip_ctxdesc {
#define KUMCTRLSTA_OFFSET_INB_CTRL 0x00000002 #define KUMCTRLSTA_OFFSET_INB_CTRL 0x00000002
#define KUMCTRLSTA_OFFSET_DIAG 0x00000003 #define KUMCTRLSTA_OFFSET_DIAG 0x00000003
#define KUMCTRLSTA_OFFSET_TIMEOUTS 0x00000004 #define KUMCTRLSTA_OFFSET_TIMEOUTS 0x00000004
#define KUMCTRLSTA_OFFSET_K1_CONFIG 0x00000007
#define KUMCTRLSTA_OFFSET_INB_PARAM 0x00000009 #define KUMCTRLSTA_OFFSET_INB_PARAM 0x00000009
#define KUMCTRLSTA_OFFSET_HD_CTRL 0x00000010 #define KUMCTRLSTA_OFFSET_HD_CTRL 0x00000010
#define KUMCTRLSTA_OFFSET_M2P_SERDES 0x0000001E #define KUMCTRLSTA_OFFSET_M2P_SERDES 0x0000001E
@ -670,6 +675,9 @@ struct livengood_tcpip_ctxdesc {
#define KUMCTRLSTA_INB_CTRL_LINK_TMOUT_DFLT 0x00000500 #define KUMCTRLSTA_INB_CTRL_LINK_TMOUT_DFLT 0x00000500
#define KUMCTRLSTA_INB_CTRL_DIS_PADDING 0x00000010 #define KUMCTRLSTA_INB_CTRL_DIS_PADDING 0x00000010
/* K1 Config */
#define KUMCTRLSTA_K1_ENABLE 0x0002
/* Half-Duplex Control */ /* Half-Duplex Control */
#define KUMCTRLSTA_HD_CTRL_10_100_DEFAULT 0x00000004 #define KUMCTRLSTA_HD_CTRL_10_100_DEFAULT 0x00000004
#define KUMCTRLSTA_HD_CTRL_1000_DEFAULT 0x00000000 #define KUMCTRLSTA_HD_CTRL_1000_DEFAULT 0x00000000

View File

@ -1,4 +1,4 @@
/* $NetBSD: if_wmvar.h,v 1.7 2010/01/11 12:29:28 msaitoh Exp $ */ /* $NetBSD: if_wmvar.h,v 1.8 2010/01/14 18:56:02 msaitoh Exp $ */
/* /*
* Copyright (c) 2001, 2002, 2003, 2004 Wasabi Systems, Inc. * Copyright (c) 2001, 2002, 2003, 2004 Wasabi Systems, Inc.
@ -114,6 +114,21 @@ typedef enum {
WM_T_PCH, /* PCH LAN */ WM_T_PCH, /* PCH LAN */
} wm_chip_type; } wm_chip_type;
typedef enum {
WMPHY_UNKNOWN = 0,
WMPHY_NONE,
WMPHY_M88,
WMPHY_IGP,
WMPHY_IGP_2,
WMPHY_GG82563,
WMPHY_IGP_3,
WMPHY_IFE,
WMPHY_BM,
WMPHY_82578,
WMPHY_82577
} wm_phy_type;
#define WM_PHY_CFG_TIMEOUT 100 #define WM_PHY_CFG_TIMEOUT 100
#define WM_ICH8_LAN_INIT_TIMEOUT 1500 #define WM_ICH8_LAN_INIT_TIMEOUT 1500
#define WM_MDIO_OWNERSHIP_TIMEOUT 10 #define WM_MDIO_OWNERSHIP_TIMEOUT 10