Module Name:    src
Committed By:   snj
Date:           Tue Aug  1 23:33:18 UTC 2017

Modified Files:
        src/sys/dev/mii [netbsd-8]: igphy.c ihphyreg.h inbmphyreg.h
        src/sys/dev/pci [netbsd-8]: if_wm.c if_wmreg.h if_wmvar.h

Log Message:
Pull up following revision(s) (requested by msaitoh in ticket #173):
        sys/dev/pci/if_wmreg.h: 1.99-1.103
        sys/dev/pci/if_wmvar.h: 1.34-1.37
        sys/dev/pci/if_wm.c: 1.510-1.537
        sys/dev/mii/ihphyreg.h: 1.2
        sys/dev/mii/inbmphyreg.h: 1.10
        sys/dev/mii/igphy.c: 1.27
Sync wm(4) up to if_wm.c rev. 1.537:
- Set WMREG_KABGTXD not in wm_init_locked() but in wm_reset().
- If a legacy interrupt is a spurious interrupt, don't print debug
  message.
- Don't print the Image Unique ID if an NVM is iNVM (i210 and I211).
- Fix a bug that a RAL was written at incorrect address when the index
  number is more than 16 on 82544 and newer.
- The layout of RAL on PCH* are different from others. Fix it.
- Flush every MTA write. Same as Linux.
- Move the location of calling wm_set_filter. Same as some other OSes.
- Flush writing WMREG_CTRL in wm_gmii_mediachange().
- Make new wm_phy_post_reset() and use this function at all location
  after resetting phy.
- Add I219 specific workaround for legacy interrupt. From OpenBSD.
- Move the location of calling wm_lplu_d0_disable().
- Disable D0 LPLU on 8257[12356], 82580, I350 and I21[01], too. Before
  this commit, above devices and non-PCIe devices accessed wrong
  register.
- Fix latency calculation in wm_platform_pm_pch_lpt().
- Set OBFF water mark and enable OBFF on PCH_LPT and newer.
- Fix a bug that wm_gmii_i82544_{read,write}reg() didn't take care of
  page select. PHY access from igphy() automatically did it, but
  accessing from wm(4) for workaround didn't work correctly. This
  change affects 8254[17], 8257[12] ICH8, ICH9 and ICH10.
- Call wm_kmrn_lock_loss_workaround_ich8lan() before any PHY access in
  wm_linkintr_gmii().
- Register access in wm_kmrn_lock_loss_workaround_ich8lan() now works
  correctly. Enable this function.
- IF TX is not required to flush, RX is also not required to flush in
  wm_flush_desc_rings(). Same as other OSes.
- Remove wrong semaphore access in wm_nvm_{read,write}_{ich8,spt} to
  prevent hangup. A semaphore is get/put in wm_nvm_{read,write}.
- Move some initialization stuff in wm_attach() before wm_reset().
  Some flags and callback function is required to set correctly before
  wm_reset() because wm_reset() and some helper functions refer them.
- Add wm_write_smbus_addr() to set SMBus address by software.
- Modify wm_gmii_hv_{read,write}reg_locked() to make them access
  HV_SMB_ADDR correctly.
- Configure the LCD with the extended configuration region in NVM if
  it's required. Tested with Thinkpad X220.
- 8257[12]: Don't directly access SPI but use EERD register.
- 82575-I354: If the size of SPI ROM >= 32K words, use direct SPI
  access instead of EERD register access.
- Add wm_nvm_eec_clock_raise() and wm_nvm_eec_clock_lower() and use
  them for Microwire/SPI bus control. Same as Linux and FreeBSD.
- Reduce timeout value for 80003 in wm_get_swfw_semaphore(). Same as
  Linux and FreeBSD.
- Change API of kmrn_{read,write}reg() and check the return value.
- Check whether it's required to use MDIC workaround for 80003 or not
  in wm_reset(). If the workaround isn't required, don't use the
  workaround code in wm_gmii_i80003_{read,write}reg.
- Add WM_F_WA_I210_CLSEM flag for a workaround. FreeBSD/Linux drivers
  say "In rare circumstances, the SW semaphore may already be held
  unintentionally." on I21[01]. PXE boot is one of the case.
- Qemu's e1000e emulation (82574L)'s SPI has only 64 words. I've never
  seen on real 82574 hardware with such small SPI ROM. Check
  NVM word size before accessing higher address words to prevent
  timeout.
- Check some wm_nvm_read()'s return vale.
- Use device_printf() instead of aprint_error_dev() for PHY read/write
  functions because those are used not only in device attach.
- Print internal driver flags when attaching.
- Add debug printf()s.
- Rename variables.
- Add comment, update comment and remove wrong comment.


To generate a diff of this commit:
cvs rdiff -u -r1.26 -r1.26.10.1 src/sys/dev/mii/igphy.c
cvs rdiff -u -r1.1 -r1.1.56.1 src/sys/dev/mii/ihphyreg.h
cvs rdiff -u -r1.9 -r1.9.8.1 src/sys/dev/mii/inbmphyreg.h
cvs rdiff -u -r1.508.4.1 -r1.508.4.2 src/sys/dev/pci/if_wm.c
cvs rdiff -u -r1.98 -r1.98.6.1 src/sys/dev/pci/if_wmreg.h
cvs rdiff -u -r1.33 -r1.33.6.1 src/sys/dev/pci/if_wmvar.h

Please note that diffs are not public domain; they are subject to the
copyright notices on the relevant files.

Modified files:

Index: src/sys/dev/mii/igphy.c
diff -u src/sys/dev/mii/igphy.c:1.26 src/sys/dev/mii/igphy.c:1.26.10.1
--- src/sys/dev/mii/igphy.c:1.26	Thu Jul  7 06:55:41 2016
+++ src/sys/dev/mii/igphy.c	Tue Aug  1 23:33:18 2017
@@ -1,8 +1,8 @@
-/*	$NetBSD: igphy.c,v 1.26 2016/07/07 06:55:41 msaitoh Exp $	*/
+/*	$NetBSD: igphy.c,v 1.26.10.1 2017/08/01 23:33:18 snj Exp $	*/
 
 /*
  * The Intel copyright applies to the analog register setup, and the
- * (currently disabled) SmartSpeed workaround code.
+ * SmartSpeed workaround code.
  */
 
 /*******************************************************************************
@@ -70,7 +70,7 @@
  */
 
 #include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: igphy.c,v 1.26 2016/07/07 06:55:41 msaitoh Exp $");
+__KERNEL_RCSID(0, "$NetBSD: igphy.c,v 1.26.10.1 2017/08/01 23:33:18 snj Exp $");
 
 #ifdef _KERNEL_OPT
 #include "opt_mii.h"

Index: src/sys/dev/mii/ihphyreg.h
diff -u src/sys/dev/mii/ihphyreg.h:1.1 src/sys/dev/mii/ihphyreg.h:1.1.56.1
--- src/sys/dev/mii/ihphyreg.h:1.1	Sat Nov 27 20:15:27 2010
+++ src/sys/dev/mii/ihphyreg.h	Tue Aug  1 23:33:18 2017
@@ -1,4 +1,4 @@
-/*	$NetBSD: ihphyreg.h,v 1.1 2010/11/27 20:15:27 christos Exp $	*/
+/*	$NetBSD: ihphyreg.h,v 1.1.56.1 2017/08/01 23:33:18 snj Exp $	*/
 
 /*-
  * Copyright (c) 2010 The NetBSD Foundation, Inc.
@@ -108,4 +108,15 @@
 /* Diagnostics Status Register */
 #define	IHPHY_MII_DSR		BME1000_REG(0, 31)
 
+/*
+ * XXX I21[789] documents say that the SMBus Address register is at
+ * PHY address 01, Page 0 (not 768), Register 26.
+ */
+#define HV_SMB_ADDR		BME1000_REG(768, 26)
+#define HV_SMB_ADDR_ADDR	0x007f
+#define HV_SMB_ADDR_VALID	(1 << 7)
+#define HV_SMB_ADDR_FREQ_LOW	(1 << 8)
+#define HV_SMB_ADDR_PEC_EN	(1 << 9)
+#define HV_SMB_ADDR_FREQ_HIGH	(1 << 12)
+
 #endif /* _DEV_IHPHY_MIIREG_H_ */

Index: src/sys/dev/mii/inbmphyreg.h
diff -u src/sys/dev/mii/inbmphyreg.h:1.9 src/sys/dev/mii/inbmphyreg.h:1.9.8.1
--- src/sys/dev/mii/inbmphyreg.h:1.9	Wed Nov 16 08:56:17 2016
+++ src/sys/dev/mii/inbmphyreg.h	Tue Aug  1 23:33:18 2017
@@ -1,4 +1,4 @@
-/*	$NetBSD: inbmphyreg.h,v 1.9 2016/11/16 08:56:17 msaitoh Exp $	*/
+/*	$NetBSD: inbmphyreg.h,v 1.9.8.1 2017/08/01 23:33:18 snj Exp $	*/
 /*******************************************************************************
 Copyright (c) 2001-2005, Intel Corporation 
 All rights reserved.
@@ -91,6 +91,8 @@ POSSIBILITY OF SUCH DAMAGE.
 #define HV_OEM_BITS_A1KDIS	(1 << 6)
 #define HV_OEM_BITS_ANEGNOW	(1 << 10)
 
+#define HV_LED_CONFIG		BME1000_REG(0, 30)
+
 #define	HV_KMRN_MODE_CTRL	BME1000_REG(BM_PORT_CTRL_PAGE, 16)
 #define	HV_KMRN_MDIO_SLOW	0x0400
 

Index: src/sys/dev/pci/if_wm.c
diff -u src/sys/dev/pci/if_wm.c:1.508.4.1 src/sys/dev/pci/if_wm.c:1.508.4.2
--- src/sys/dev/pci/if_wm.c:1.508.4.1	Tue Jul  4 14:57:19 2017
+++ src/sys/dev/pci/if_wm.c	Tue Aug  1 23:33:18 2017
@@ -1,4 +1,4 @@
-/*	$NetBSD: if_wm.c,v 1.508.4.1 2017/07/04 14:57:19 martin Exp $	*/
+/*	$NetBSD: if_wm.c,v 1.508.4.2 2017/08/01 23:33:18 snj Exp $	*/
 
 /*
  * Copyright (c) 2001, 2002, 2003, 2004 Wasabi Systems, Inc.
@@ -73,7 +73,6 @@
  * TODO (in order of importance):
  *
  *	- Check XXX'ed comments
- *	- Disable D0 LPLU on 8257[12356], 82580 and I350.
  *	- TX Multi queue improvement (refine queue selection logic)
  *	- Split header buffer for newer descriptors
  *	- EEE (Energy Efficiency Ethernet)
@@ -84,7 +83,7 @@
  */
 
 #include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: if_wm.c,v 1.508.4.1 2017/07/04 14:57:19 martin Exp $");
+__KERNEL_RCSID(0, "$NetBSD: if_wm.c,v 1.508.4.2 2017/08/01 23:33:18 snj Exp $");
 
 #ifdef _KERNEL_OPT
 #include "opt_net_mpsafe.h"
@@ -135,6 +134,7 @@ __KERNEL_RCSID(0, "$NetBSD: if_wm.c,v 1.
 #include <dev/mii/igphyreg.h>
 #include <dev/mii/igphyvar.h>
 #include <dev/mii/inbmphyreg.h>
+#include <dev/mii/ihphyreg.h>
 
 #include <dev/pci/pcireg.h>
 #include <dev/pci/pcivar.h>
@@ -442,6 +442,12 @@ struct wm_phyop {
 	int reset_delay_us;
 };
 
+struct wm_nvmop {
+	int (*acquire)(struct wm_softc *);
+	void (*release)(struct wm_softc *);
+	int (*read)(struct wm_softc *, int, int, uint16_t *);
+};
+
 /*
  * Software state per device.
  */
@@ -564,6 +570,7 @@ struct wm_softc {
 	kmutex_t *sc_ich_nvmmtx;	/* ICH/PCH specific NVM mutex */
 
 	struct wm_phyop phy;
+	struct wm_nvmop nvm;
 };
 
 #define WM_CORE_LOCK(_sc)	if ((_sc)->sc_core_lock) mutex_enter((_sc)->sc_core_lock)
@@ -679,6 +686,9 @@ static void	wm_set_pcie_completion_timeo
 static void	wm_get_auto_rd_done(struct wm_softc *);
 static void	wm_lan_init_done(struct wm_softc *);
 static void	wm_get_cfg_done(struct wm_softc *);
+static void	wm_phy_post_reset(struct wm_softc *);
+static void	wm_write_smbus_addr(struct wm_softc *);
+static void	wm_init_lcd_from_nvm(struct wm_softc *);
 static void	wm_initialize_hardware_bits(struct wm_softc *);
 static uint32_t	wm_rxpbs_adjust_82580(uint32_t);
 static void	wm_reset_phy(struct wm_softc *);
@@ -696,8 +706,8 @@ static int	wm_setup_legacy(struct wm_sof
 static int	wm_setup_msix(struct wm_softc *);
 static int	wm_init(struct ifnet *);
 static int	wm_init_locked(struct ifnet *);
-static void	wm_turnon(struct wm_softc *);
-static void	wm_turnoff(struct wm_softc *);
+static void	wm_unset_stopping_flags(struct wm_softc *);
+static void	wm_set_stopping_flags(struct wm_softc *);
 static void	wm_stop(struct ifnet *, int);
 static void	wm_stop_locked(struct ifnet *, int);
 static void	wm_dump_mbuf_chain(struct wm_softc *, struct mbuf *);
@@ -799,10 +809,10 @@ static void	wm_gmii_statchg(struct ifnet
  * These functions are not for accessing MII registers but for accessing
  * kumeran specific registers.
  */
-static int	wm_kmrn_readreg(struct wm_softc *, int);
-static int	wm_kmrn_readreg_locked(struct wm_softc *, int);
-static void	wm_kmrn_writereg(struct wm_softc *, int, int);
-static void	wm_kmrn_writereg_locked(struct wm_softc *, int, int);
+static int	wm_kmrn_readreg(struct wm_softc *, int, uint16_t *);
+static int	wm_kmrn_readreg_locked(struct wm_softc *, int, uint16_t *);
+static int	wm_kmrn_writereg(struct wm_softc *, int, uint16_t);
+static int	wm_kmrn_writereg_locked(struct wm_softc *, int, uint16_t);
 /* SGMII */
 static bool	wm_sgmii_uses_mdio(struct wm_softc *);
 static int	wm_sgmii_readreg(device_t, int, int);
@@ -854,8 +864,6 @@ static int	wm_nvm_read_spt(struct wm_sof
 static int	wm_nvm_read_word_invm(struct wm_softc *, uint16_t, uint16_t *);
 static int	wm_nvm_read_invm(struct wm_softc *, int, int, uint16_t *);
 /* Lock, detecting NVM type, validate checksum and read */
-static int	wm_nvm_acquire(struct wm_softc *);
-static void	wm_nvm_release(struct wm_softc *);
 static int	wm_nvm_is_onboard_eeprom(struct wm_softc *);
 static int	wm_nvm_get_flash_presence_i210(struct wm_softc *);
 static int	wm_nvm_validate_checksum(struct wm_softc *);
@@ -869,17 +877,23 @@ static int	wm_nvm_read(struct wm_softc *
  */
 static int	wm_get_null(struct wm_softc *);
 static void	wm_put_null(struct wm_softc *);
+static int	wm_get_eecd(struct wm_softc *);
+static void	wm_put_eecd(struct wm_softc *);
 static int	wm_get_swsm_semaphore(struct wm_softc *); /* 8257[123] */
 static void	wm_put_swsm_semaphore(struct wm_softc *);
 static int	wm_get_swfw_semaphore(struct wm_softc *, uint16_t);
 static void	wm_put_swfw_semaphore(struct wm_softc *, uint16_t);
+static int	wm_get_nvm_80003(struct wm_softc *);
+static void	wm_put_nvm_80003(struct wm_softc *);
+static int	wm_get_nvm_82571(struct wm_softc *);
+static void	wm_put_nvm_82571(struct wm_softc *);
 static int	wm_get_phy_82575(struct wm_softc *);
 static void	wm_put_phy_82575(struct wm_softc *);
 static int	wm_get_swfwhw_semaphore(struct wm_softc *); /* For 574/583 */
 static void	wm_put_swfwhw_semaphore(struct wm_softc *);
 static int	wm_get_swflag_ich8lan(struct wm_softc *);	/* For PHY */
 static void	wm_put_swflag_ich8lan(struct wm_softc *);
-static int	wm_get_nvm_ich8lan(struct wm_softc *);		/* For NVM */
+static int	wm_get_nvm_ich8lan(struct wm_softc *);
 static void	wm_put_nvm_ich8lan(struct wm_softc *);
 static int	wm_get_hw_semaphore_82573(struct wm_softc *);
 static void	wm_put_hw_semaphore_82573(struct wm_softc *);
@@ -909,7 +923,6 @@ static void	wm_igp3_phy_powerdown_workar
 static void	wm_enable_wakeup(struct wm_softc *);
 /* LPLU (Low Power Link Up) */
 static void	wm_lplu_d0_disable(struct wm_softc *);
-static void	wm_lplu_d0_disable_pch(struct wm_softc *);
 /* EEE */
 static void	wm_set_eee_i350(struct wm_softc *);
 
@@ -930,6 +943,7 @@ static bool	wm_phy_is_accessible_pchlan(
 static void	wm_toggle_lanphypc_pch_lpt(struct wm_softc *);
 static int	wm_platform_pm_pch_lpt(struct wm_softc *, bool);
 static void	wm_pll_workaround_i210(struct wm_softc *);
+static void	wm_legacy_irq_quirk_spt(struct wm_softc *);
 
 CFATTACH_DECL3_NEW(wm, sizeof(struct wm_softc),
     wm_match, wm_attach, wm_detach, NULL, NULL, NULL, DVF_DETACH_SHUTDOWN);
@@ -1674,6 +1688,7 @@ wm_attach(device_t parent, device_t self
 	prop_data_t ea;
 	prop_number_t pn;
 	uint8_t enaddr[ETHER_ADDR_LEN];
+	char buf[256];
 	uint16_t cfg1, cfg2, swdpin, nvmword;
 	pcireg_t preg, memtype;
 	uint16_t eeprom_data, apme_mask;
@@ -1709,8 +1724,8 @@ wm_attach(device_t parent, device_t self
 	sc->sc_type = wmp->wmp_type;
 
 	/* Set default function pointers */
-	sc->phy.acquire = wm_get_null;
-	sc->phy.release = wm_put_null;
+	sc->phy.acquire = sc->nvm.acquire = wm_get_null;
+	sc->phy.release = sc->nvm.release = wm_put_null;
 	sc->phy.reset_delay_us = (sc->sc_type >= WM_T_82571) ? 100 : 10000;
 
 	if (sc->sc_type < WM_T_82543) {
@@ -2035,6 +2050,7 @@ alloc_retry:
 	case WM_T_82543:
 	case WM_T_82544:
 		/* Microwire */
+		sc->nvm.read = wm_nvm_read_uwire;
 		sc->sc_nvm_wordsize = 64;
 		sc->sc_nvm_addrbits = 6;
 		break;
@@ -2044,6 +2060,7 @@ alloc_retry:
 	case WM_T_82546:
 	case WM_T_82546_3:
 		/* Microwire */
+		sc->nvm.read = wm_nvm_read_uwire;
 		reg = CSR_READ(sc, WMREG_EECD);
 		if (reg & EECD_EE_SIZE) {
 			sc->sc_nvm_wordsize = 256;
@@ -2053,19 +2070,29 @@ alloc_retry:
 			sc->sc_nvm_addrbits = 6;
 		}
 		sc->sc_flags |= WM_F_LOCK_EECD;
+		sc->nvm.acquire = wm_get_eecd;
+		sc->nvm.release = wm_put_eecd;
 		break;
 	case WM_T_82541:
 	case WM_T_82541_2:
 	case WM_T_82547:
 	case WM_T_82547_2:
-		sc->sc_flags |= WM_F_LOCK_EECD;
 		reg = CSR_READ(sc, WMREG_EECD);
+		/*
+		 * wm_nvm_set_addrbits_size_eecd() accesses SPI in it only
+		 * on 8254[17], so set flags and functios before calling it.
+		 */
+		sc->sc_flags |= WM_F_LOCK_EECD;
+		sc->nvm.acquire = wm_get_eecd;
+		sc->nvm.release = wm_put_eecd;
 		if (reg & EECD_EE_TYPE) {
 			/* SPI */
+			sc->nvm.read = wm_nvm_read_spi;
 			sc->sc_flags |= WM_F_EEPROM_SPI;
 			wm_nvm_set_addrbits_size_eecd(sc);
 		} else {
 			/* Microwire */
+			sc->nvm.read = wm_nvm_read_uwire;
 			if ((reg & EECD_EE_ABITS) != 0) {
 				sc->sc_nvm_wordsize = 256;
 				sc->sc_nvm_addrbits = 8;
@@ -2078,25 +2105,30 @@ alloc_retry:
 	case WM_T_82571:
 	case WM_T_82572:
 		/* SPI */
+		sc->nvm.read = wm_nvm_read_eerd;
+		/* Not use WM_F_LOCK_EECD because we use EERD */
 		sc->sc_flags |= WM_F_EEPROM_SPI;
 		wm_nvm_set_addrbits_size_eecd(sc);
-		sc->sc_flags |= WM_F_LOCK_EECD | WM_F_LOCK_SWSM;
 		sc->phy.acquire = wm_get_swsm_semaphore;
 		sc->phy.release = wm_put_swsm_semaphore;
+		sc->nvm.acquire = wm_get_nvm_82571;
+		sc->nvm.release = wm_put_nvm_82571;
 		break;
 	case WM_T_82573:
 	case WM_T_82574:
 	case WM_T_82583:
+		sc->nvm.read = wm_nvm_read_eerd;
+		/* Not use WM_F_LOCK_EECD because we use EERD */
 		if (sc->sc_type == WM_T_82573) {
-			sc->sc_flags |= WM_F_LOCK_SWSM;
 			sc->phy.acquire = wm_get_swsm_semaphore;
 			sc->phy.release = wm_put_swsm_semaphore;
+			sc->nvm.acquire = wm_get_nvm_82571;
+			sc->nvm.release = wm_put_nvm_82571;
 		} else {
-			sc->sc_flags |= WM_F_LOCK_EXTCNF;
 			/* Both PHY and NVM use the same semaphore. */
-			sc->phy.acquire
+			sc->phy.acquire = sc->nvm.acquire
 			    = wm_get_swfwhw_semaphore;
-			sc->phy.release
+			sc->phy.release = sc->nvm.release
 			    = wm_put_swfwhw_semaphore;
 		}
 		if (wm_nvm_is_onboard_eeprom(sc) == 0) {
@@ -2107,7 +2139,6 @@ alloc_retry:
 			sc->sc_flags |= WM_F_EEPROM_SPI;
 			wm_nvm_set_addrbits_size_eecd(sc);
 		}
-		sc->sc_flags |= WM_F_EEPROM_EERDEEWR;
 		break;
 	case WM_T_82575:
 	case WM_T_82576:
@@ -2118,10 +2149,18 @@ alloc_retry:
 		/* SPI */
 		sc->sc_flags |= WM_F_EEPROM_SPI;
 		wm_nvm_set_addrbits_size_eecd(sc);
-		sc->sc_flags |= WM_F_EEPROM_EERDEEWR | WM_F_LOCK_SWFW
-		    | WM_F_LOCK_SWSM;
+		if((sc->sc_type == WM_T_80003)
+		    || (sc->sc_nvm_wordsize < (1 << 15))) {
+			sc->nvm.read = wm_nvm_read_eerd;
+			/* Don't use WM_F_LOCK_EECD because we use EERD */
+		} else {
+			sc->nvm.read = wm_nvm_read_spi;
+			sc->sc_flags |= WM_F_LOCK_EECD;
+		}
 		sc->phy.acquire = wm_get_phy_82575;
 		sc->phy.release = wm_put_phy_82575;
+		sc->nvm.acquire = wm_get_nvm_80003;	
+		sc->nvm.release = wm_put_nvm_80003;	
 		break;
 	case WM_T_ICH8:
 	case WM_T_ICH9:
@@ -2129,8 +2168,9 @@ alloc_retry:
 	case WM_T_PCH:
 	case WM_T_PCH2:
 	case WM_T_PCH_LPT:
+		sc->nvm.read = wm_nvm_read_ich8;
 		/* FLASH */
-		sc->sc_flags |= WM_F_EEPROM_FLASH | WM_F_LOCK_EXTCNF;
+		sc->sc_flags |= WM_F_EEPROM_FLASH;
 		sc->sc_nvm_wordsize = 2048;
 		memtype = pci_mapreg_type(pa->pa_pc, pa->pa_tag,WM_ICH8_FLASH);
 		if (pci_mapreg_map(pa, WM_ICH8_FLASH, memtype, 0,
@@ -2150,10 +2190,13 @@ alloc_retry:
 		sc->sc_flashreg_offset = 0;
 		sc->phy.acquire = wm_get_swflag_ich8lan;
 		sc->phy.release = wm_put_swflag_ich8lan;
+		sc->nvm.acquire = wm_get_nvm_ich8lan;
+		sc->nvm.release = wm_put_nvm_ich8lan;
 		break;
 	case WM_T_PCH_SPT:
+		sc->nvm.read = wm_nvm_read_spt;
 		/* SPT has no GFPREG; flash registers mapped through BAR0 */
-		sc->sc_flags |= WM_F_EEPROM_FLASH | WM_F_LOCK_EXTCNF;
+		sc->sc_flags |= WM_F_EEPROM_FLASH;
 		sc->sc_flasht = sc->sc_st;
 		sc->sc_flashh = sc->sc_sh;
 		sc->sc_ich8_flash_base = 0;
@@ -2167,28 +2210,32 @@ alloc_retry:
 		sc->sc_flashreg_offset = WM_PCH_SPT_FLASHOFFSET;
 		sc->phy.acquire = wm_get_swflag_ich8lan;
 		sc->phy.release = wm_put_swflag_ich8lan;
+		sc->nvm.acquire = wm_get_nvm_ich8lan;
+		sc->nvm.release = wm_put_nvm_ich8lan;
 		break;
 	case WM_T_I210:
 	case WM_T_I211:
+		/* Allow a single clear of the SW semaphore on I210 and newer*/
+		sc->sc_flags |= WM_F_WA_I210_CLSEM;
 		if (wm_nvm_get_flash_presence_i210(sc)) {
-			wm_nvm_set_addrbits_size_eecd(sc);
+			sc->nvm.read = wm_nvm_read_eerd;
+			/* Don't use WM_F_LOCK_EECD because we use EERD */
 			sc->sc_flags |= WM_F_EEPROM_FLASH_HW;
-			sc->sc_flags |= WM_F_EEPROM_EERDEEWR;
+			wm_nvm_set_addrbits_size_eecd(sc);
 		} else {
-			sc->sc_nvm_wordsize = INVM_SIZE;
+			sc->nvm.read = wm_nvm_read_invm;
 			sc->sc_flags |= WM_F_EEPROM_INVM;
+			sc->sc_nvm_wordsize = INVM_SIZE;
 		}
-		sc->sc_flags |= WM_F_LOCK_SWFW | WM_F_LOCK_SWSM;
 		sc->phy.acquire = wm_get_phy_82575;
 		sc->phy.release = wm_put_phy_82575;
+		sc->nvm.acquire = wm_get_nvm_80003;
+		sc->nvm.release = wm_put_nvm_80003;
 		break;
 	default:
 		break;
 	}
 
-	/* Reset the chip to a known state. */
-	wm_reset(sc);
-
 	/* Ensure the SMBI bit is clear before first NVM or PHY access */
 	switch (sc->sc_type) {
 	case WM_T_82571:
@@ -2235,9 +2282,6 @@ alloc_retry:
 			sc->sc_flags |= WM_F_EEPROM_INVALID;
 	}
 
-	/* Set device properties (macflags) */
-	prop_dictionary_set_uint32(dict, "macflags", sc->sc_flags);
-
 	if (sc->sc_flags & WM_F_EEPROM_INVALID)
 		aprint_verbose_dev(sc->sc_dev, "No EEPROM");
 	else {
@@ -2261,6 +2305,15 @@ alloc_retry:
 	wm_nvm_version(sc);
 	aprint_verbose("\n");
 
+	/*
+	 * XXX The first call of wm_gmii_setup_phytype. The result might be
+	 * incorrect.
+	 */
+	wm_gmii_setup_phytype(sc, 0, 0);
+
+	/* Reset the chip to a known state. */
+	wm_reset(sc);
+
 	/* Check for I21[01] PLL workaround */
 	if (sc->sc_type == WM_T_I210)
 		sc->sc_flags |= WM_F_PLL_WA_I210;
@@ -2377,10 +2430,6 @@ alloc_retry:
 	/* Check for WM_F_WOL flag after the setting of the EEPROM stuff */
 	if ((eeprom_data & apme_mask) != 0)
 		sc->sc_flags |= WM_F_WOL;
-#ifdef WM_DEBUG
-	if ((sc->sc_flags & WM_F_WOL) != 0)
-		printf("WOL\n");
-#endif
 
 	if ((sc->sc_type == WM_T_82575) || (sc->sc_type == WM_T_82576)) {
 		/* Check NVM for autonegotiation */
@@ -2477,17 +2526,13 @@ alloc_retry:
 			sc->sc_nvm_k1_enabled = 0;
 	}
 
-	/*
-	 * Determine if we're TBI,GMII or SGMII mode, and initialize the
-	 * media structures accordingly.
-	 */
+	/* Determine if we're GMII, TBI, SERDES or SGMII mode */
 	if (sc->sc_type == WM_T_ICH8 || sc->sc_type == WM_T_ICH9
 	    || sc->sc_type == WM_T_ICH10 || sc->sc_type == WM_T_PCH
 	    || sc->sc_type == WM_T_PCH2 || sc->sc_type == WM_T_PCH_LPT
 	    || sc->sc_type == WM_T_PCH_SPT || sc->sc_type == WM_T_82573
 	    || sc->sc_type == WM_T_82574 || sc->sc_type == WM_T_82583) {
-		/* STATUS_TBIMODE reserved/reused, can't rely on it */
-		wm_gmii_mediainit(sc, wmp->wmp_product);
+		/* Copper only */
 	} else if ((sc->sc_type == WM_T_82575) || (sc->sc_type == WM_T_82576)
 	    || (sc->sc_type ==WM_T_82580) || (sc->sc_type ==WM_T_I350)
 	    || (sc->sc_type ==WM_T_I354) || (sc->sc_type ==WM_T_I210)
@@ -2553,11 +2598,6 @@ alloc_retry:
 		else
 			reg &= ~CTRL_EXT_I2C_ENA;
 		CSR_WRITE(sc, WMREG_CTRL_EXT, reg);
-
-		if (sc->sc_mediatype == WM_MEDIATYPE_COPPER)
-			wm_gmii_mediainit(sc, wmp->wmp_product);
-		else
-			wm_tbi_mediainit(sc);
 	} else if (sc->sc_type < WM_T_82543 ||
 	    (CSR_READ(sc, WMREG_STATUS) & STATUS_TBIMODE) != 0) {
 		if (sc->sc_mediatype == WM_MEDIATYPE_COPPER) {
@@ -2565,15 +2605,24 @@ alloc_retry:
 			    "WARNING: TBIMODE set on 1000BASE-T product!\n");
 			sc->sc_mediatype = WM_MEDIATYPE_FIBER;
 		}
-		wm_tbi_mediainit(sc);
 	} else {
 		if (sc->sc_mediatype == WM_MEDIATYPE_FIBER) {
 			aprint_error_dev(sc->sc_dev,
 			    "WARNING: TBIMODE clear on 1000BASE-X product!\n");
 			sc->sc_mediatype = WM_MEDIATYPE_COPPER;
 		}
-		wm_gmii_mediainit(sc, wmp->wmp_product);
 	}
+	snprintb(buf, sizeof(buf), WM_FLAGS, sc->sc_flags);
+	aprint_verbose_dev(sc->sc_dev, "%s\n", buf);
+
+	/* Set device properties (macflags) */
+	prop_dictionary_set_uint32(dict, "macflags", sc->sc_flags);
+
+	/* Initialize the media structures accordingly. */
+	if (sc->sc_mediatype == WM_MEDIATYPE_COPPER)
+		wm_gmii_mediainit(sc, wmp->wmp_product);
+	else
+		wm_tbi_mediainit(sc); /* All others */
 
 	ifp = &sc->sc_ethercom.ec_if;
 	xname = device_xname(sc->sc_dev);
@@ -2990,6 +3039,9 @@ wm_ifflags_cb(struct ethercom *ec)
 	struct wm_softc *sc = ifp->if_softc;
 	int rc = 0;
 
+	DPRINTF(WM_DEBUG_INIT, ("%s: %s called\n",
+		device_xname(sc->sc_dev), __func__));
+
 	WM_CORE_LOCK(sc);
 
 	int change = ifp->if_flags ^ sc->sc_if_flags;
@@ -3204,7 +3256,9 @@ wm_read_mac_addr(struct wm_softc *sc, ui
 static void
 wm_set_ral(struct wm_softc *sc, const uint8_t *enaddr, int idx)
 {
-	uint32_t ral_lo, ral_hi;
+	uint32_t ral_lo, ral_hi, addrl, addrh;
+	uint32_t wlock_mac;
+	int rv;
 
 	if (enaddr != NULL) {
 		ral_lo = enaddr[0] | (enaddr[1] << 8) | (enaddr[2] << 16) |
@@ -3216,14 +3270,54 @@ wm_set_ral(struct wm_softc *sc, const ui
 		ral_hi = 0;
 	}
 
-	if (sc->sc_type >= WM_T_82544) {
-		CSR_WRITE(sc, WMREG_RAL_LO(WMREG_CORDOVA_RAL_BASE, idx),
-		    ral_lo);
-		CSR_WRITE(sc, WMREG_RAL_HI(WMREG_CORDOVA_RAL_BASE, idx),
-		    ral_hi);
-	} else {
-		CSR_WRITE(sc, WMREG_RAL_LO(WMREG_RAL_BASE, idx), ral_lo);
-		CSR_WRITE(sc, WMREG_RAL_HI(WMREG_RAL_BASE, idx), ral_hi);
+	switch (sc->sc_type) {
+	case WM_T_82542_2_0:
+	case WM_T_82542_2_1:
+	case WM_T_82543:
+		CSR_WRITE(sc, WMREG_RAL(idx), ral_lo);
+		CSR_WRITE_FLUSH(sc);
+		CSR_WRITE(sc, WMREG_RAH(idx), ral_hi);
+		CSR_WRITE_FLUSH(sc);
+		break;
+	case WM_T_PCH2:
+	case WM_T_PCH_LPT:
+	case WM_T_PCH_SPT:
+		if (idx == 0) {
+			CSR_WRITE(sc, WMREG_CORDOVA_RAL(idx), ral_lo);
+			CSR_WRITE_FLUSH(sc);
+			CSR_WRITE(sc, WMREG_CORDOVA_RAH(idx), ral_hi);
+			CSR_WRITE_FLUSH(sc);
+			return;
+		}
+		if (sc->sc_type != WM_T_PCH2) {
+			wlock_mac = __SHIFTOUT(CSR_READ(sc, WMREG_FWSM),
+			    FWSM_WLOCK_MAC);
+			addrl = WMREG_SHRAL(idx - 1);
+			addrh = WMREG_SHRAH(idx - 1);
+		} else {
+			wlock_mac = 0;
+			addrl = WMREG_PCH_LPT_SHRAL(idx - 1);
+			addrh = WMREG_PCH_LPT_SHRAH(idx - 1);
+		}
+		
+		if ((wlock_mac == 0) || (idx <= wlock_mac)) {
+			rv = wm_get_swflag_ich8lan(sc);
+			if (rv != 0)
+				return;
+			CSR_WRITE(sc, addrl, ral_lo);
+			CSR_WRITE_FLUSH(sc);
+			CSR_WRITE(sc, addrh, ral_hi);
+			CSR_WRITE_FLUSH(sc);
+			wm_put_swflag_ich8lan(sc);
+		}
+
+		break;
+	default:
+		CSR_WRITE(sc, WMREG_CORDOVA_RAL(idx), ral_lo);
+		CSR_WRITE_FLUSH(sc);
+		CSR_WRITE(sc, WMREG_CORDOVA_RAH(idx), ral_hi);
+		CSR_WRITE_FLUSH(sc);
+		break;
 	}
 }
 
@@ -3342,8 +3436,10 @@ wm_set_filter(struct wm_softc *sc)
 	else
 		size = WM_MC_TABSIZE;
 	/* Clear out the multicast table. */
-	for (i = 0; i < size; i++)
+	for (i = 0; i < size; i++) {
 		CSR_WRITE(sc, mta_reg + (i << 2), 0);
+		CSR_WRITE_FLUSH(sc);
+	}
 
 	ETHER_LOCK(ec);
 	ETHER_FIRST_MULTI(step, ec, enm);
@@ -3385,9 +3481,13 @@ wm_set_filter(struct wm_softc *sc)
 			 */
 			bit = CSR_READ(sc, mta_reg + ((reg - 1) << 2));
 			CSR_WRITE(sc, mta_reg + (reg << 2), hash);
+			CSR_WRITE_FLUSH(sc);
 			CSR_WRITE(sc, mta_reg + ((reg - 1) << 2), bit);
-		} else
+			CSR_WRITE_FLUSH(sc);
+		} else {
 			CSR_WRITE(sc, mta_reg + (reg << 2), hash);
+			CSR_WRITE_FLUSH(sc);
+		}
 
 		ETHER_NEXT_MULTI(step, enm);
 	}
@@ -3609,6 +3709,180 @@ wm_get_cfg_done(struct wm_softc *sc)
 	}
 }
 
+void
+wm_phy_post_reset(struct wm_softc *sc)
+{
+	uint32_t reg;
+
+	/* This function is only for ICH8 and newer. */
+	if (sc->sc_type < WM_T_ICH8)
+		return;
+
+	if (wm_phy_resetisblocked(sc)) {
+		/* XXX */
+		device_printf(sc->sc_dev, "PHY is blocked\n");
+		return;
+	}
+
+	/* Allow time for h/w to get to quiescent state after reset */
+	delay(10*1000);
+
+	/* Perform any necessary post-reset workarounds */
+	if (sc->sc_type == WM_T_PCH)
+		wm_hv_phy_workaround_ich8lan(sc);
+	if (sc->sc_type == WM_T_PCH2)
+		wm_lv_phy_workaround_ich8lan(sc);
+
+	/* Clear the host wakeup bit after lcd reset */
+	if (sc->sc_type >= WM_T_PCH) {
+		reg = wm_gmii_hv_readreg(sc->sc_dev, 2,
+		    BM_PORT_GEN_CFG);
+		reg &= ~BM_WUC_HOST_WU_BIT;
+		wm_gmii_hv_writereg(sc->sc_dev, 2,
+		    BM_PORT_GEN_CFG, reg);
+	}
+
+	/* Configure the LCD with the extended configuration region in NVM */
+	wm_init_lcd_from_nvm(sc);
+
+	/* Configure the LCD with the OEM bits in NVM */
+}
+
+/* Only for PCH and newer */
+static void
+wm_write_smbus_addr(struct wm_softc *sc)
+{
+	uint32_t strap, freq;
+	uint32_t phy_data;
+
+	DPRINTF(WM_DEBUG_INIT, ("%s: %s called\n",
+		device_xname(sc->sc_dev), __func__));
+
+	strap = CSR_READ(sc, WMREG_STRAP);
+	freq = __SHIFTOUT(strap, STRAP_FREQ);
+
+	phy_data = wm_gmii_hv_readreg_locked(sc->sc_dev, 2, HV_SMB_ADDR);
+
+	phy_data &= ~HV_SMB_ADDR_ADDR;
+	phy_data |= __SHIFTOUT(strap, STRAP_SMBUSADDR);
+	phy_data |= HV_SMB_ADDR_PEC_EN | HV_SMB_ADDR_VALID;
+
+	if (sc->sc_phytype == WMPHY_I217) {
+		/* Restore SMBus frequency */
+		if (freq --) {
+			phy_data &= ~(HV_SMB_ADDR_FREQ_LOW
+			    | HV_SMB_ADDR_FREQ_HIGH);
+			phy_data |= __SHIFTIN((freq & 0x01) != 0,
+			    HV_SMB_ADDR_FREQ_LOW);
+			phy_data |= __SHIFTIN((freq & 0x02) != 0,
+			    HV_SMB_ADDR_FREQ_HIGH);
+		} else {
+			DPRINTF(WM_DEBUG_INIT,
+			    ("%s: %s Unsupported SMB frequency in PHY\n",
+				device_xname(sc->sc_dev), __func__));
+		}
+	}
+
+	wm_gmii_hv_writereg_locked(sc->sc_dev, 2, HV_SMB_ADDR, phy_data);
+}
+
+void
+wm_init_lcd_from_nvm(struct wm_softc *sc)
+{
+	uint32_t extcnfctr, sw_cfg_mask, cnf_size, word_addr, i, reg;
+	uint16_t phy_page = 0;
+
+	DPRINTF(WM_DEBUG_INIT, ("%s: %s called\n",
+		device_xname(sc->sc_dev), __func__));
+
+	switch (sc->sc_type) {
+	case WM_T_ICH8:
+		if ((sc->sc_phytype == WMPHY_UNKNOWN)
+		    || (sc->sc_phytype != WMPHY_IGP_3))
+			return;
+
+		if ((sc->sc_pcidevid == PCI_PRODUCT_INTEL_82801H_AMT)
+		    || (sc->sc_pcidevid == PCI_PRODUCT_INTEL_82801H_LAN)) {
+			sw_cfg_mask = FEXTNVM_SW_CONFIG;
+			break;
+		}
+		/* FALLTHROUGH */
+	case WM_T_PCH:
+	case WM_T_PCH2:
+	case WM_T_PCH_LPT:
+	case WM_T_PCH_SPT:
+		sw_cfg_mask = FEXTNVM_SW_CONFIG_ICH8M;
+		break;
+	default:
+		return;
+	}
+
+	sc->phy.acquire(sc);
+
+	reg = CSR_READ(sc, WMREG_FEXTNVM);
+	if ((reg & sw_cfg_mask) == 0)
+		goto release;
+
+	/*
+	 * Make sure HW does not configure LCD from PHY extended configuration
+	 * before SW configuration
+	 */
+	extcnfctr = CSR_READ(sc, WMREG_EXTCNFCTR);
+	if ((sc->sc_type < WM_T_PCH2)
+	    && ((extcnfctr & EXTCNFCTR_PCIE_WRITE_ENABLE) != 0))
+		goto release;
+
+	DPRINTF(WM_DEBUG_INIT, ("%s: %s: Configure LCD by software\n",
+		device_xname(sc->sc_dev), __func__));
+	/* word_addr is in DWORD */
+	word_addr = __SHIFTOUT(extcnfctr, EXTCNFCTR_EXT_CNF_POINTER) << 1;
+	
+	reg = CSR_READ(sc, WMREG_EXTCNFSIZE);
+	cnf_size = __SHIFTOUT(reg, EXTCNFSIZE_LENGTH);
+
+	if (((sc->sc_type == WM_T_PCH)
+		&& ((extcnfctr & EXTCNFCTR_OEM_WRITE_ENABLE) == 0))
+	    || (sc->sc_type > WM_T_PCH)) {
+		/*
+		 * HW configures the SMBus address and LEDs when the OEM and
+		 * LCD Write Enable bits are set in the NVM. When both NVM bits
+		 * are cleared, SW will configure them instead.
+		 */
+		DPRINTF(WM_DEBUG_INIT, ("%s: %s: Configure SMBus and LED\n",
+			device_xname(sc->sc_dev), __func__));
+		wm_write_smbus_addr(sc);
+
+		reg = CSR_READ(sc, WMREG_LEDCTL);
+		wm_gmii_hv_writereg_locked(sc->sc_dev, 1, HV_LED_CONFIG, reg);
+	}
+
+	/* Configure LCD from extended configuration region. */
+	for (i = 0; i < cnf_size; i++) {
+		uint16_t reg_data, reg_addr;
+
+		if (wm_nvm_read(sc, (word_addr + i * 2), 1, &reg_data) != 0)
+			goto release;
+
+		if (wm_nvm_read(sc, (word_addr + i * 2 + 1), 1, &reg_addr) !=0)
+			goto release;
+
+		if (reg_addr == MII_IGPHY_PAGE_SELECT)
+			phy_page = reg_data;
+
+		reg_addr &= IGPHY_MAXREGADDR;
+		reg_addr |= phy_page;
+
+		sc->phy.release(sc); /* XXX */
+		sc->sc_mii.mii_writereg(sc->sc_dev, 1, reg_addr, reg_data);
+		sc->phy.acquire(sc); /* XXX */
+	}
+
+release:	
+	sc->phy.release(sc);
+	return;
+}
+    
+
 /* Init hardware bits */
 void
 wm_initialize_hardware_bits(struct wm_softc *sc)
@@ -3888,6 +4162,7 @@ wm_reset_phy(struct wm_softc *sc)
 	sc->phy.release(sc);
 
 	wm_get_cfg_done(sc);
+	wm_phy_post_reset(sc);
 }
 
 static void
@@ -3895,7 +4170,10 @@ wm_flush_desc_rings(struct wm_softc *sc)
 {
 	pcireg_t preg;
 	uint32_t reg;
+	struct wm_txqueue *txq;
+	wiseman_txdesc_t *txd;
 	int nexttx;
+	uint32_t rctl;
 
 	/* First, disable MULR fix in FEXTNVM11 */
 	reg = CSR_READ(sc, WMREG_FEXTNVM11);
@@ -3904,66 +4182,64 @@ wm_flush_desc_rings(struct wm_softc *sc)
 
 	preg = pci_conf_read(sc->sc_pc, sc->sc_pcitag, WM_PCI_DESCRING_STATUS);
 	reg = CSR_READ(sc, WMREG_TDLEN(0));
-	if (((preg & DESCRING_STATUS_FLUSH_REQ) != 0) && (reg != 0)) {
-		struct wm_txqueue *txq;
-		wiseman_txdesc_t *txd;
-
-		/* TX */
-		printf("%s: Need TX flush (reg = %08x, len = %u)\n",
-		    device_xname(sc->sc_dev), preg, reg);
-		reg = CSR_READ(sc, WMREG_TCTL);
-		CSR_WRITE(sc, WMREG_TCTL, reg | TCTL_EN);
+	if (((preg & DESCRING_STATUS_FLUSH_REQ) == 0) || (reg == 0))
+		return;
 
-		txq = &sc->sc_queue[0].wmq_txq;
-		nexttx = txq->txq_next;
-		txd = &txq->txq_descs[nexttx];
-		wm_set_dma_addr(&txd->wtx_addr, WM_CDTXADDR(txq, nexttx));
-		txd->wtx_cmdlen = htole32(WTX_CMD_IFCS| 512);
-		txd->wtx_fields.wtxu_status = 0;
-		txd->wtx_fields.wtxu_options = 0;
-		txd->wtx_fields.wtxu_vlan = 0;
+	/* TX */
+	printf("%s: Need TX flush (reg = %08x, len = %u)\n",
+	    device_xname(sc->sc_dev), preg, reg);
+	reg = CSR_READ(sc, WMREG_TCTL);
+	CSR_WRITE(sc, WMREG_TCTL, reg | TCTL_EN);
+
+	txq = &sc->sc_queue[0].wmq_txq;
+	nexttx = txq->txq_next;
+	txd = &txq->txq_descs[nexttx];
+	wm_set_dma_addr(&txd->wtx_addr, WM_CDTXADDR(txq, nexttx));
+	txd->wtx_cmdlen = htole32(WTX_CMD_IFCS| 512);
+	txd->wtx_fields.wtxu_status = 0;
+	txd->wtx_fields.wtxu_options = 0;
+	txd->wtx_fields.wtxu_vlan = 0;
 
-		bus_space_barrier(sc->sc_st, sc->sc_sh, 0, 0,
-			BUS_SPACE_BARRIER_WRITE);
+	bus_space_barrier(sc->sc_st, sc->sc_sh, 0, 0,
+	    BUS_SPACE_BARRIER_WRITE);
 		
-		txq->txq_next = WM_NEXTTX(txq, txq->txq_next);
-		CSR_WRITE(sc, WMREG_TDT(0), txq->txq_next);
-		bus_space_barrier(sc->sc_st, sc->sc_sh, 0, 0,
-			BUS_SPACE_BARRIER_READ | BUS_SPACE_BARRIER_WRITE);
-		delay(250);
-	}
+	txq->txq_next = WM_NEXTTX(txq, txq->txq_next);
+	CSR_WRITE(sc, WMREG_TDT(0), txq->txq_next);
+	bus_space_barrier(sc->sc_st, sc->sc_sh, 0, 0,
+	    BUS_SPACE_BARRIER_READ | BUS_SPACE_BARRIER_WRITE);
+	delay(250);
+
 	preg = pci_conf_read(sc->sc_pc, sc->sc_pcitag, WM_PCI_DESCRING_STATUS);
-	if (preg & DESCRING_STATUS_FLUSH_REQ) {
-		uint32_t rctl;
+	if ((preg & DESCRING_STATUS_FLUSH_REQ) == 0)
+		return;
 
-		/* RX */
-		printf("%s: Need RX flush (reg = %08x)\n",
-		    device_xname(sc->sc_dev), preg);
-		rctl = CSR_READ(sc, WMREG_RCTL);
-		CSR_WRITE(sc, WMREG_RCTL, rctl & ~RCTL_EN);
-		CSR_WRITE_FLUSH(sc);
-		delay(150);
+	/* RX */
+	printf("%s: Need RX flush (reg = %08x)\n",
+	    device_xname(sc->sc_dev), preg);
+	rctl = CSR_READ(sc, WMREG_RCTL);
+	CSR_WRITE(sc, WMREG_RCTL, rctl & ~RCTL_EN);
+	CSR_WRITE_FLUSH(sc);
+	delay(150);
 
-		reg = CSR_READ(sc, WMREG_RXDCTL(0));
-		/* zero the lower 14 bits (prefetch and host thresholds) */
-		reg &= 0xffffc000;
-		/*
-		 * update thresholds: prefetch threshold to 31, host threshold
-		 * to 1 and make sure the granularity is "descriptors" and not
-		 * "cache lines"
-		 */
-		reg |= (0x1f | (1 << 8) | RXDCTL_GRAN);
-		CSR_WRITE(sc, WMREG_RXDCTL(0), reg);
+	reg = CSR_READ(sc, WMREG_RXDCTL(0));
+	/* zero the lower 14 bits (prefetch and host thresholds) */
+	reg &= 0xffffc000;
+	/*
+	 * update thresholds: prefetch threshold to 31, host threshold
+	 * to 1 and make sure the granularity is "descriptors" and not
+	 * "cache lines"
+	 */
+	reg |= (0x1f | (1 << 8) | RXDCTL_GRAN);
+	CSR_WRITE(sc, WMREG_RXDCTL(0), reg);
 
-		/*
-		 * momentarily enable the RX ring for the changes to take
-		 * effect
-		 */
-		CSR_WRITE(sc, WMREG_RCTL, rctl | RCTL_EN);
-		CSR_WRITE_FLUSH(sc);
-		delay(150);
-		CSR_WRITE(sc, WMREG_RCTL, rctl & ~RCTL_EN);
-	}
+	/*
+	 * momentarily enable the RX ring for the changes to take
+	 * effect
+	 */
+	CSR_WRITE(sc, WMREG_RCTL, rctl | RCTL_EN);
+	CSR_WRITE_FLUSH(sc);
+	delay(150);
+	CSR_WRITE(sc, WMREG_RCTL, rctl & ~RCTL_EN);
 }
 
 /*
@@ -3977,6 +4253,8 @@ wm_reset(struct wm_softc *sc)
 	int phy_reset = 0;
 	int i, error = 0;
 	uint32_t reg;
+	uint16_t kmreg;
+	int rv;
 
 	DPRINTF(WM_DEBUG_INIT, ("%s: %s called\n",
 		device_xname(sc->sc_dev), __func__));
@@ -4069,6 +4347,9 @@ wm_reset(struct wm_softc *sc)
 				break;
 			delay(100);
 		}
+		if (timeout == 0)
+			device_printf(sc->sc_dev,
+			    "failed to disable busmastering\n");
 	}
 
 	/* Set the completion timeout for interface */
@@ -4318,6 +4599,9 @@ wm_reset(struct wm_softc *sc)
 		break;
 	}
 
+	if (phy_reset != 0)
+		wm_phy_post_reset(sc);
+
 	if ((sc->sc_type == WM_T_82580)
 	    || (sc->sc_type == WM_T_I350) || (sc->sc_type == WM_T_I354)) {
 		/* clear global device reset status bit */
@@ -4335,21 +4619,21 @@ wm_reset(struct wm_softc *sc)
 			CSR_WRITE(sc, WMREG_EIAC_82574, 0);
 	}
 
+	if ((sc->sc_type == WM_T_ICH8) || (sc->sc_type == WM_T_ICH9)
+	    || (sc->sc_type == WM_T_ICH10) || (sc->sc_type == WM_T_PCH)
+	    || (sc->sc_type == WM_T_PCH2) || (sc->sc_type == WM_T_PCH_LPT)
+	    || (sc->sc_type == WM_T_PCH_SPT)) {
+		reg = CSR_READ(sc, WMREG_KABGTXD);
+		reg |= KABGTXD_BGSQLBIAS;
+		CSR_WRITE(sc, WMREG_KABGTXD, reg);
+	}
+
 	/* reload sc_ctrl */
 	sc->sc_ctrl = CSR_READ(sc, WMREG_CTRL);
 
 	if ((sc->sc_type >= WM_T_I350) && (sc->sc_type <= WM_T_I211))
 		wm_set_eee_i350(sc);
 
-	/* Clear the host wakeup bit after lcd reset */
-	if (sc->sc_type >= WM_T_PCH) {
-		reg = wm_gmii_hv_readreg(sc->sc_dev, 2,
-		    BM_PORT_GEN_CFG);
-		reg &= ~BM_WUC_HOST_WU_BIT;
-		wm_gmii_hv_writereg(sc->sc_dev, 2,
-		    BM_PORT_GEN_CFG, reg);
-	}
-
 	/*
 	 * 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
@@ -4365,6 +4649,21 @@ wm_reset(struct wm_softc *sc)
 
 	if ((sc->sc_flags & WM_F_PLL_WA_I210) != 0)
 		wm_pll_workaround_i210(sc);
+
+	if (sc->sc_type == WM_T_80003) {
+		/* default to TRUE to enable the MDIC W/A */
+		sc->sc_flags |= WM_F_80003_MDIC_WA;
+	
+		rv = wm_kmrn_readreg(sc,
+		    KUMCTRLSTA_OFFSET >> KUMCTRLSTA_OFFSET_SHIFT, &kmreg);
+		if (rv == 0) {
+			if ((kmreg & KUMCTRLSTA_OPMODE_MASK)
+			    == KUMCTRLSTA_OPMODE_INBAND_MDIO)
+				sc->sc_flags &= ~WM_F_80003_MDIC_WA;
+			else
+				sc->sc_flags |= WM_F_80003_MDIC_WA;
+		}
+	}
 }
 
 /*
@@ -4822,7 +5121,7 @@ wm_setup_msix(struct wm_softc *sc)
 }
 
 static void
-wm_turnon(struct wm_softc *sc)
+wm_unset_stopping_flags(struct wm_softc *sc)
 {
 	int i;
 
@@ -4848,7 +5147,7 @@ wm_turnon(struct wm_softc *sc)
 }
 
 static void
-wm_turnoff(struct wm_softc *sc)
+wm_set_stopping_flags(struct wm_softc *sc)
 {
 	int i;
 
@@ -5036,10 +5335,17 @@ wm_init_locked(struct ifnet *ifp)
 	/* Reset the chip to a known state. */
 	wm_reset(sc);
 
-	/* AMT based hardware can now take control from firmware */
+	/*
+	 * AMT based hardware can now take control from firmware
+	 * Do this after reset.
+	 */
 	if ((sc->sc_flags & WM_F_HAS_AMT) != 0)
 		wm_get_hw_control(sc);
 
+	if ((sc->sc_type == WM_T_PCH_SPT) &&
+	    pci_intr_type(sc->sc_pc, sc->sc_intrs[0]) == PCI_INTR_TYPE_INTX)
+		wm_legacy_irq_quirk_spt(sc);
+
 	/* Init hardware bits */
 	wm_initialize_hardware_bits(sc);
 
@@ -5131,7 +5437,7 @@ wm_init_locked(struct ifnet *ifp)
 	wm_set_vlan(sc);
 
 	if (sc->sc_flags & WM_F_HAS_MII) {
-		int val;
+		uint16_t kmreg;
 
 		switch (sc->sc_type) {
 		case WM_T_80003:
@@ -5150,19 +5456,20 @@ wm_init_locked(struct ifnet *ifp)
 			 */
 			wm_kmrn_writereg(sc, KUMCTRLSTA_OFFSET_TIMEOUTS,
 			    0xFFFF);
-			val = wm_kmrn_readreg(sc, KUMCTRLSTA_OFFSET_INB_PARAM);
-			val |= 0x3F;
-			wm_kmrn_writereg(sc,
-			    KUMCTRLSTA_OFFSET_INB_PARAM, val);
+			wm_kmrn_readreg(sc, KUMCTRLSTA_OFFSET_INB_PARAM,
+			    &kmreg);
+			kmreg |= 0x3F;
+			wm_kmrn_writereg(sc, KUMCTRLSTA_OFFSET_INB_PARAM,
+			    kmreg);
 			break;
 		default:
 			break;
 		}
 
 		if (sc->sc_type == WM_T_80003) {
-			val = CSR_READ(sc, WMREG_CTRL_EXT);
-			val &= ~CTRL_EXT_LINK_MODE_MASK;
-			CSR_WRITE(sc, WMREG_CTRL_EXT, val);
+			reg = CSR_READ(sc, WMREG_CTRL_EXT);
+			reg &= ~CTRL_EXT_LINK_MODE_MASK;
+			CSR_WRITE(sc, WMREG_CTRL_EXT, reg);
 
 			/* Bypass RX and TX FIFO's */
 			wm_kmrn_writereg(sc, KUMCTRLSTA_OFFSET_FIFO_CTRL,
@@ -5363,15 +5670,6 @@ wm_init_locked(struct ifnet *ifp)
 	} else
 		CSR_WRITE(sc, WMREG_IMS, sc->sc_icr);
 
-	if ((sc->sc_type == WM_T_ICH8) || (sc->sc_type == WM_T_ICH9)
-	    || (sc->sc_type == WM_T_ICH10) || (sc->sc_type == WM_T_PCH)
-	    || (sc->sc_type == WM_T_PCH2) || (sc->sc_type == WM_T_PCH_LPT)
-	    || (sc->sc_type == WM_T_PCH_SPT)) {
-		reg = CSR_READ(sc, WMREG_KABGTXD);
-		reg |= KABGTXD_BGSQLBIAS;
-		CSR_WRITE(sc, WMREG_KABGTXD, reg);
-	}
-
 	/* Set up the inter-packet gap. */
 	CSR_WRITE(sc, WMREG_TIPG, sc->sc_tipg);
 
@@ -5477,9 +5775,6 @@ wm_init_locked(struct ifnet *ifp)
 		} else panic("wm_init: i82542 requires MCLBYTES = 2048");
 	}
 
-	/* Set the receive filter. */
-	wm_set_filter(sc);
-
 	/* Enable ECC */
 	switch (sc->sc_type) {
 	case WM_T_82571:
@@ -5514,7 +5809,10 @@ wm_init_locked(struct ifnet *ifp)
 		}
 	}
 
-	wm_turnon(sc);
+	/* Set the receive filter. */
+	wm_set_filter(sc);
+
+	wm_unset_stopping_flags(sc);
 
 	/* Start the one second link check clock. */
 	callout_reset(&sc->sc_tick_ch, hz, wm_tick, sc);
@@ -5557,7 +5855,7 @@ wm_stop_locked(struct ifnet *ifp, int di
 		device_xname(sc->sc_dev), __func__));
 	KASSERT(WM_CORE_LOCKED(sc));
 
-	wm_turnoff(sc);
+	wm_set_stopping_flags(sc);
 
 	/* Stop the one second clock. */
 	callout_stop(&sc->sc_tick_ch);
@@ -8190,9 +8488,21 @@ wm_linkintr_gmii(struct wm_softc *sc, ui
 		uint32_t reg;
 		uint32_t status = CSR_READ(sc, WMREG_STATUS);
 
+		if ((status & STATUS_LU) != 0) {
+			DPRINTF(WM_DEBUG_LINK, ("%s: LINK: LSC -> up %s\n",
+				device_xname(sc->sc_dev),
+				(status & STATUS_FD) ? "FDX" : "HDX"));
+		} else {
+			DPRINTF(WM_DEBUG_LINK, ("%s: LINK: LSC -> down\n",
+				device_xname(sc->sc_dev)));
+		}
 		if ((sc->sc_type == WM_T_ICH8) && ((status & STATUS_LU) == 0))
 			wm_gig_downshift_workaround_ich8lan(sc);
 
+		if ((sc->sc_type == WM_T_ICH8)
+		    && (sc->sc_phytype == WMPHY_IGP_3)) {
+			wm_kmrn_lock_loss_workaround_ich8lan(sc);
+		}
 		DPRINTF(WM_DEBUG_LINK, ("%s: LINK: LSC -> mii_pollstat\n",
 			device_xname(sc->sc_dev)));
 		mii_pollstat(&sc->sc_mii);
@@ -8231,9 +8541,6 @@ wm_linkintr_gmii(struct wm_softc *sc, ui
 					sc->sc_ctrl |= CTRL_FD;
 				CSR_WRITE(sc, WMREG_CTRL, sc->sc_ctrl);
 			}
-		} else if ((sc->sc_type == WM_T_ICH8)
-		    && (sc->sc_phytype == WMPHY_IGP_3)) {
-			wm_kmrn_lock_loss_workaround_ich8lan(sc);
 		} else if (sc->sc_type == WM_T_PCH) {
 			wm_k1_gig_workaround_hv(sc,
 			    ((sc->sc_mii.mii_media_status & IFM_ACTIVE) != 0));
@@ -8469,13 +8776,15 @@ wm_intr_legacy(void *arg)
 	uint32_t icr, rndval = 0;
 	int handled = 0;
 
-	DPRINTF(WM_DEBUG_TX,
-	    ("%s: INTx: got intr\n", device_xname(sc->sc_dev)));
 	while (1 /* CONSTCOND */) {
 		icr = CSR_READ(sc, WMREG_ICR);
 		if ((icr & sc->sc_icr) == 0)
 			break;
-		if (rndval == 0)
+		if (handled == 0) {
+			DPRINTF(WM_DEBUG_TX,
+			    ("%s: INTx: got intr\n",device_xname(sc->sc_dev)));
+		}
+		if (rndval == 0)
 			rndval = icr;
 
 		mutex_enter(rxq->rxq_lock);
@@ -8496,6 +8805,11 @@ wm_intr_legacy(void *arg)
 			WM_Q_EVCNT_INCR(rxq, rxintr);
 		}
 #endif
+		/*
+		 * wm_rxeof() does *not* call upper layer functions directly,
+		 * as if_percpuq_enqueue() just call softint_schedule().
+		 * So, we can call wm_rxeof() in interrupt context.
+		 */
 		wm_rxeof(rxq, UINT_MAX);
 
 		mutex_exit(rxq->rxq_lock);
@@ -8854,6 +9168,8 @@ wm_gmii_reset(struct wm_softc *sc)
 	case WM_T_82571:
 	case WM_T_82572:
 	case WM_T_82573:
+	case WM_T_82574:
+	case WM_T_82583:
 	case WM_T_82575:
 	case WM_T_82576:
 	case WM_T_82580:
@@ -8864,10 +9180,6 @@ wm_gmii_reset(struct wm_softc *sc)
 	case WM_T_80003:
 		/* null */
 		break;
-	case WM_T_82574:
-	case WM_T_82583:
-		wm_lplu_d0_disable(sc);
-		break;
 	case WM_T_82541:
 	case WM_T_82547:
 		/* XXX Configure actively LED after PHY reset */
@@ -8879,34 +9191,7 @@ wm_gmii_reset(struct wm_softc *sc)
 	case WM_T_PCH2:
 	case WM_T_PCH_LPT:
 	case WM_T_PCH_SPT:
-		/* Allow time for h/w to get to a quiescent state afer reset */
-		delay(10*1000);
-
-		if (sc->sc_type == WM_T_PCH)
-			wm_hv_phy_workaround_ich8lan(sc);
-
-		if (sc->sc_type == WM_T_PCH2)
-			wm_lv_phy_workaround_ich8lan(sc);
-
-		/* Clear the host wakeup bit after lcd reset */
-		if (sc->sc_type >= WM_T_PCH) {
-			reg = wm_gmii_hv_readreg(sc->sc_dev, 2,
-			    BM_PORT_GEN_CFG);
-			reg &= ~BM_WUC_HOST_WU_BIT;
-			wm_gmii_hv_writereg(sc->sc_dev, 2,
-			    BM_PORT_GEN_CFG, reg);
-		}
-
-		/*
-		 * XXX Configure the LCD with th extended configuration region
-		 * in NVM
-		 */
-
-		/* Disable D0 LPLU. */
-		if (sc->sc_type >= WM_T_PCH)	/* PCH* */
-			wm_lplu_d0_disable_pch(sc);
-		else
-			wm_lplu_d0_disable(sc);	/* ICH* */
+		wm_phy_post_reset(sc);
 		break;
 	default:
 		panic("%s: unknown type\n", __func__);
@@ -8943,6 +9228,9 @@ wm_gmii_setup_phytype(struct wm_softc *s
 	mii_readreg_t new_readreg;
 	mii_writereg_t new_writereg;
 
+	DPRINTF(WM_DEBUG_INIT, ("%s: %s called\n",
+		device_xname(sc->sc_dev), __func__));
+
 	if (mii->mii_readreg == NULL) {
 		/*
 		 *  This is the first call of this function. For ICH and PCH
@@ -9243,12 +9531,6 @@ wm_gmii_mediainit(struct wm_softc *sc, p
 	/* Initialize our media structures and probe the GMII. */
 	mii->mii_ifp = ifp;
 
-	/*
-	 * The first call of wm_mii_setup_phytype. The result might be
-	 * incorrect.
-	 */
-	wm_gmii_setup_phytype(sc, 0, 0);
-
 	mii->mii_statchg = wm_gmii_statchg;
 
 	/* get PHY control from SMBus to PCIe */
@@ -9341,7 +9623,7 @@ wm_gmii_mediainit(struct wm_softc *sc, p
 
 		/*
 		 * PHY Found! Check PHY type again by the second call of
-		 * wm_mii_setup_phytype.
+		 * wm_gmii_setup_phytype.
 		 */
 		wm_gmii_setup_phytype(sc, child->mii_mpd_oui,
 		    child->mii_mpd_model);
@@ -9367,6 +9649,9 @@ wm_gmii_mediachange(struct ifnet *ifp)
 	if ((ifp->if_flags & IFF_UP) == 0)
 		return 0;
 
+	/* Disable D0 LPLU. */
+	wm_lplu_d0_disable(sc);
+
 	sc->sc_ctrl &= ~(CTRL_SPEED_MASK | CTRL_FD);
 	sc->sc_ctrl |= CTRL_SLU;
 	if ((IFM_SUBTYPE(ife->ifm_media) == IFM_AUTO)
@@ -9393,6 +9678,7 @@ wm_gmii_mediachange(struct ifnet *ifp)
 		}
 	}
 	CSR_WRITE(sc, WMREG_CTRL, sc->sc_ctrl);
+	CSR_WRITE_FLUSH(sc);
 	if (sc->sc_type <= WM_T_82543)
 		wm_gmii_reset(sc);
 
@@ -9497,9 +9783,9 @@ wm_i82543_mii_recvbits(struct wm_softc *
  *	Read a PHY register on the GMII (i82543 version).
  */
 static int
-wm_gmii_i82543_readreg(device_t self, int phy, int reg)
+wm_gmii_i82543_readreg(device_t dev, int phy, int reg)
 {
-	struct wm_softc *sc = device_private(self);
+	struct wm_softc *sc = device_private(dev);
 	int rv;
 
 	wm_i82543_mii_sendbits(sc, 0xffffffffU, 32);
@@ -9508,7 +9794,7 @@ wm_gmii_i82543_readreg(device_t self, in
 	rv = wm_i82543_mii_recvbits(sc) & 0xffff;
 
 	DPRINTF(WM_DEBUG_GMII, ("%s: GMII: read phy %d reg %d -> 0x%04x\n",
-	    device_xname(sc->sc_dev), phy, reg, rv));
+	    device_xname(dev), phy, reg, rv));
 
 	return rv;
 }
@@ -9519,9 +9805,9 @@ wm_gmii_i82543_readreg(device_t self, in
  *	Write a PHY register on the GMII (i82543 version).
  */
 static void
-wm_gmii_i82543_writereg(device_t self, int phy, int reg, int val)
+wm_gmii_i82543_writereg(device_t dev, int phy, int reg, int val)
 {
-	struct wm_softc *sc = device_private(self);
+	struct wm_softc *sc = device_private(dev);
 
 	wm_i82543_mii_sendbits(sc, 0xffffffffU, 32);
 	wm_i82543_mii_sendbits(sc, val | (MII_COMMAND_ACK << 16) |
@@ -9535,12 +9821,18 @@ wm_gmii_i82543_writereg(device_t self, i
  *	Read a PHY register on the GMII.
  */
 static int
-wm_gmii_mdic_readreg(device_t self, int phy, int reg)
+wm_gmii_mdic_readreg(device_t dev, int phy, int reg)
 {
-	struct wm_softc *sc = device_private(self);
+	struct wm_softc *sc = device_private(dev);
 	uint32_t mdic = 0;
 	int i, rv;
 
+	if (reg > MII_ADDRMASK) {
+		device_printf(dev, "%s: PHYTYPE = %d, addr 0x%x > 0x1f\n",
+		    __func__, sc->sc_phytype, reg);
+		reg &= MII_ADDRMASK;
+	}
+
 	CSR_WRITE(sc, WMREG_MDIC, MDIC_OP_READ | MDIC_PHYADD(phy) |
 	    MDIC_REGADD(reg));
 
@@ -9553,12 +9845,12 @@ wm_gmii_mdic_readreg(device_t self, int 
 
 	if ((mdic & MDIC_READY) == 0) {
 		log(LOG_WARNING, "%s: MDIC read timed out: phy %d reg %d\n",
-		    device_xname(sc->sc_dev), phy, reg);
+		    device_xname(dev), phy, reg);
 		rv = 0;
 	} else if (mdic & MDIC_E) {
 #if 0 /* This is normal if no PHY is present. */
 		log(LOG_WARNING, "%s: MDIC read error: phy %d reg %d\n",
-		    device_xname(sc->sc_dev), phy, reg);
+		    device_xname(dev), phy, reg);
 #endif
 		rv = 0;
 	} else {
@@ -9576,12 +9868,18 @@ wm_gmii_mdic_readreg(device_t self, int 
  *	Write a PHY register on the GMII.
  */
 static void
-wm_gmii_mdic_writereg(device_t self, int phy, int reg, int val)
+wm_gmii_mdic_writereg(device_t dev, int phy, int reg, int val)
 {
-	struct wm_softc *sc = device_private(self);
+	struct wm_softc *sc = device_private(dev);
 	uint32_t mdic = 0;
 	int i;
 
+	if (reg > MII_ADDRMASK) {
+		device_printf(dev, "%s: PHYTYPE = %d, addr 0x%x > 0x1f\n",
+		    __func__, sc->sc_phytype, reg);
+		reg &= MII_ADDRMASK;
+	}
+
 	CSR_WRITE(sc, WMREG_MDIC, MDIC_OP_WRITE | MDIC_PHYADD(phy) |
 	    MDIC_REGADD(reg) | MDIC_DATA(val));
 
@@ -9594,10 +9892,10 @@ wm_gmii_mdic_writereg(device_t self, int
 
 	if ((mdic & MDIC_READY) == 0)
 		log(LOG_WARNING, "%s: MDIC write timed out: phy %d reg %d\n",
-		    device_xname(sc->sc_dev), phy, reg);
+		    device_xname(dev), phy, reg);
 	else if (mdic & MDIC_E)
 		log(LOG_WARNING, "%s: MDIC write error: phy %d reg %d\n",
-		    device_xname(sc->sc_dev), phy, reg);
+		    device_xname(dev), phy, reg);
 }
 
 /*
@@ -9606,17 +9904,33 @@ wm_gmii_mdic_writereg(device_t self, int
  *	Read a PHY register on the GMII.
  */
 static int
-wm_gmii_i82544_readreg(device_t self, int phy, int reg)
+wm_gmii_i82544_readreg(device_t dev, int phy, int reg)
 {
-	struct wm_softc *sc = device_private(self);
+	struct wm_softc *sc = device_private(dev);
 	int rv;
 
 	if (sc->phy.acquire(sc)) {
-		aprint_error_dev(sc->sc_dev, "%s: failed to get semaphore\n",
-		    __func__);
+		device_printf(dev, "%s: failed to get semaphore\n", __func__);
 		return 0;
 	}
-	rv = wm_gmii_mdic_readreg(self, phy, reg);
+
+	if (reg > BME1000_MAX_MULTI_PAGE_REG) {
+		switch (sc->sc_phytype) {
+		case WMPHY_IGP:
+		case WMPHY_IGP_2:
+		case WMPHY_IGP_3:
+			wm_gmii_mdic_writereg(dev, phy, MII_IGPHY_PAGE_SELECT, reg);
+			break;
+		default:
+#ifdef WM_DEBUG
+			device_printf(dev, "%s: PHYTYPE = 0x%x, addr = %02x\n",
+			    __func__, sc->sc_phytype, reg);
+#endif
+			break;
+		}
+	}
+	
+	rv = wm_gmii_mdic_readreg(dev, phy, reg & MII_ADDRMASK);
 	sc->phy.release(sc);
 
 	return rv;
@@ -9628,15 +9942,32 @@ wm_gmii_i82544_readreg(device_t self, in
  *	Write a PHY register on the GMII.
  */
 static void
-wm_gmii_i82544_writereg(device_t self, int phy, int reg, int val)
+wm_gmii_i82544_writereg(device_t dev, int phy, int reg, int val)
 {
-	struct wm_softc *sc = device_private(self);
+	struct wm_softc *sc = device_private(dev);
 
 	if (sc->phy.acquire(sc)) {
-		aprint_error_dev(sc->sc_dev, "%s: failed to get semaphore\n",
-		    __func__);
+		device_printf(dev, "%s: failed to get semaphore\n", __func__);
+		return;
+	}
+
+	if (reg > BME1000_MAX_MULTI_PAGE_REG) {
+		switch (sc->sc_phytype) {
+		case WMPHY_IGP:
+		case WMPHY_IGP_2:
+		case WMPHY_IGP_3:
+			wm_gmii_mdic_writereg(dev, phy, MII_IGPHY_PAGE_SELECT, reg);
+			break;
+		default:
+#ifdef WM_DEBUG
+			device_printf(dev, "%s: PHYTYPE == 0x%x, addr = %02x",
+			    __func__, sc->sc_phytype, reg);
+#endif
+			break;
+		}
 	}
-	wm_gmii_mdic_writereg(self, phy, reg, val);
+			
+	wm_gmii_mdic_writereg(dev, phy, reg & MII_ADDRMASK, val);
 	sc->phy.release(sc);
 }
 
@@ -9648,33 +9979,49 @@ wm_gmii_i82544_writereg(device_t self, i
  * ressource ...
  */
 static int
-wm_gmii_i80003_readreg(device_t self, int phy, int reg)
+wm_gmii_i80003_readreg(device_t dev, int phy, int reg)
 {
-	struct wm_softc *sc = device_private(self);
+	struct wm_softc *sc = device_private(dev);
+	int page_select, temp;
 	int rv;
 
 	if (phy != 1) /* only one PHY on kumeran bus */
 		return 0;
 
 	if (sc->phy.acquire(sc)) {
-		aprint_error_dev(sc->sc_dev, "%s: failed to get semaphore\n",
-		    __func__);
+		device_printf(dev, "%s: failed to get semaphore\n", __func__);
 		return 0;
 	}
 
-	if ((reg & MII_ADDRMASK) < GG82563_MIN_ALT_REG) {
-		wm_gmii_mdic_writereg(self, phy, GG82563_PHY_PAGE_SELECT,
-		    reg >> GG82563_PAGE_SHIFT);
-	} else {
-		wm_gmii_mdic_writereg(self, phy, GG82563_PHY_PAGE_SELECT_ALT,
-		    reg >> GG82563_PAGE_SHIFT);
-	}
-	/* Wait more 200us for a bug of the ready bit in the MDIC register */
-	delay(200);
-	rv = wm_gmii_mdic_readreg(self, phy, reg & MII_ADDRMASK);
-	delay(200);
-	sc->phy.release(sc);
+	if ((reg & MII_ADDRMASK) < GG82563_MIN_ALT_REG)
+		page_select = GG82563_PHY_PAGE_SELECT;
+	else {
+		/*
+		 * Use Alternative Page Select register to access registers
+		 * 30 and 31.
+		 */
+		page_select = GG82563_PHY_PAGE_SELECT_ALT;
+	}
+	temp = (uint16_t)reg >> GG82563_PAGE_SHIFT;
+	wm_gmii_mdic_writereg(dev, phy, page_select, temp);
+	if ((sc->sc_flags & WM_F_80003_MDIC_WA) != 0) {
+		/*
+		 * Wait more 200us for a bug of the ready bit in the MDIC
+		 * register.
+		 */
+		delay(200);
+		if (wm_gmii_mdic_readreg(dev, phy, page_select) != temp) {
+			device_printf(dev, "%s failed\n", __func__);
+			rv = 0; /* XXX */
+			goto out;
+		}
+		rv = wm_gmii_mdic_readreg(dev, phy, reg & MII_ADDRMASK);
+		delay(200);
+	} else
+		rv = wm_gmii_mdic_readreg(dev, phy, reg & MII_ADDRMASK);
 
+out:
+	sc->phy.release(sc);
 	return rv;
 }
 
@@ -9686,31 +10033,46 @@ wm_gmii_i80003_readreg(device_t self, in
  * ressource ...
  */
 static void
-wm_gmii_i80003_writereg(device_t self, int phy, int reg, int val)
+wm_gmii_i80003_writereg(device_t dev, int phy, int reg, int val)
 {
-	struct wm_softc *sc = device_private(self);
+	struct wm_softc *sc = device_private(dev);
+	int page_select, temp;
 
 	if (phy != 1) /* only one PHY on kumeran bus */
 		return;
 
 	if (sc->phy.acquire(sc)) {
-		aprint_error_dev(sc->sc_dev, "%s: failed to get semaphore\n",
-		    __func__);
+		device_printf(dev, "%s: failed to get semaphore\n", __func__);
 		return;
 	}
 
-	if ((reg & MII_ADDRMASK) < GG82563_MIN_ALT_REG) {
-		wm_gmii_mdic_writereg(self, phy, GG82563_PHY_PAGE_SELECT,
-		    reg >> GG82563_PAGE_SHIFT);
-	} else {
-		wm_gmii_mdic_writereg(self, phy, GG82563_PHY_PAGE_SELECT_ALT,
-		    reg >> GG82563_PAGE_SHIFT);
-	}
-	/* Wait more 200us for a bug of the ready bit in the MDIC register */
-	delay(200);
-	wm_gmii_mdic_writereg(self, phy, reg & MII_ADDRMASK, val);
-	delay(200);
+	if ((reg & MII_ADDRMASK) < GG82563_MIN_ALT_REG)
+		page_select = GG82563_PHY_PAGE_SELECT;
+	else {
+		/*
+		 * Use Alternative Page Select register to access registers
+		 * 30 and 31.
+		 */
+		page_select = GG82563_PHY_PAGE_SELECT_ALT;
+	}
+	temp = (uint16_t)reg >> GG82563_PAGE_SHIFT;
+	wm_gmii_mdic_writereg(dev, phy, page_select, temp);
+	if ((sc->sc_flags & WM_F_80003_MDIC_WA) != 0) {
+		/*
+		 * Wait more 200us for a bug of the ready bit in the MDIC
+		 * register.
+		 */
+		delay(200);
+		if (wm_gmii_mdic_readreg(dev, phy, page_select) != temp) {
+			device_printf(dev, "%s failed\n", __func__);
+			goto out;
+		}
+		wm_gmii_mdic_writereg(dev, phy, reg & MII_ADDRMASK, val);
+		delay(200);
+	} else
+		wm_gmii_mdic_writereg(dev, phy, reg & MII_ADDRMASK, val);
 
+out:
 	sc->phy.release(sc);
 }
 
@@ -9722,16 +10084,15 @@ wm_gmii_i80003_writereg(device_t self, i
  * ressource ...
  */
 static int
-wm_gmii_bm_readreg(device_t self, int phy, int reg)
+wm_gmii_bm_readreg(device_t dev, int phy, int reg)
 {
-	struct wm_softc *sc = device_private(self);
+	struct wm_softc *sc = device_private(dev);
 	uint16_t page = reg >> BME1000_PAGE_SHIFT;
 	uint16_t val;
 	int rv;
 
 	if (sc->phy.acquire(sc)) {
-		aprint_error_dev(sc->sc_dev, "%s: failed to get semaphore\n",
-		    __func__);
+		device_printf(dev, "%s: failed to get semaphore\n", __func__);
 		return 0;
 	}
 
@@ -9740,7 +10101,7 @@ wm_gmii_bm_readreg(device_t self, int ph
 		    || (reg == 31)) ? 1 : phy;
 	/* 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);
+		wm_access_phy_wakeup_reg_bm(dev, reg, &val, 1);
 		rv = val;
 		goto release;
 	}
@@ -9748,14 +10109,14 @@ wm_gmii_bm_readreg(device_t self, int ph
 	if (reg > BME1000_MAX_MULTI_PAGE_REG) {
 		if ((phy == 1) && (sc->sc_type != WM_T_82574)
 		    && (sc->sc_type != WM_T_82583))
-			wm_gmii_mdic_writereg(self, phy,
+			wm_gmii_mdic_writereg(dev, phy,
 			    MII_IGPHY_PAGE_SELECT, page << BME1000_PAGE_SHIFT);
 		else
-			wm_gmii_mdic_writereg(self, phy,
+			wm_gmii_mdic_writereg(dev, phy,
 			    BME1000_PHY_PAGE_SELECT, page);
 	}
 
-	rv = wm_gmii_mdic_readreg(self, phy, reg & MII_ADDRMASK);
+	rv = wm_gmii_mdic_readreg(dev, phy, reg & MII_ADDRMASK);
 
 release:
 	sc->phy.release(sc);
@@ -9770,14 +10131,13 @@ release:
  * ressource ...
  */
 static void
-wm_gmii_bm_writereg(device_t self, int phy, int reg, int val)
+wm_gmii_bm_writereg(device_t dev, int phy, int reg, int val)
 {
-	struct wm_softc *sc = device_private(self);
+	struct wm_softc *sc = device_private(dev);
 	uint16_t page = reg >> BME1000_PAGE_SHIFT;
 
 	if (sc->phy.acquire(sc)) {
-		aprint_error_dev(sc->sc_dev, "%s: failed to get semaphore\n",
-		    __func__);
+		device_printf(dev, "%s: failed to get semaphore\n", __func__);
 		return;
 	}
 
@@ -9789,35 +10149,35 @@ wm_gmii_bm_writereg(device_t self, int p
 		uint16_t tmp;
 
 		tmp = val;
-		wm_access_phy_wakeup_reg_bm(self, reg, &tmp, 0);
+		wm_access_phy_wakeup_reg_bm(dev, reg, &tmp, 0);
 		goto release;
 	}
 
 	if (reg > BME1000_MAX_MULTI_PAGE_REG) {
 		if ((phy == 1) && (sc->sc_type != WM_T_82574)
 		    && (sc->sc_type != WM_T_82583))
-			wm_gmii_mdic_writereg(self, phy,
+			wm_gmii_mdic_writereg(dev, phy,
 			    MII_IGPHY_PAGE_SELECT, page << BME1000_PAGE_SHIFT);
 		else
-			wm_gmii_mdic_writereg(self, phy,
+			wm_gmii_mdic_writereg(dev, phy,
 			    BME1000_PHY_PAGE_SELECT, page);
 	}
 
-	wm_gmii_mdic_writereg(self, phy, reg & MII_ADDRMASK, val);
+	wm_gmii_mdic_writereg(dev, phy, reg & MII_ADDRMASK, val);
 
 release:
 	sc->phy.release(sc);
 }
 
 static void
-wm_access_phy_wakeup_reg_bm(device_t self, int offset, int16_t *val, int rd)
+wm_access_phy_wakeup_reg_bm(device_t dev, int offset, int16_t *val, int rd)
 {
-	struct wm_softc *sc = device_private(self);
+	struct wm_softc *sc = device_private(dev);
 	uint16_t regnum = BM_PHY_REG_NUM(offset);
 	uint16_t wuce, reg;
 
 	DPRINTF(WM_DEBUG_GMII, ("%s: %s called\n",
-		device_xname(sc->sc_dev), __func__));
+		device_xname(dev), __func__));
 	/* XXX Gig must be disabled for MDIO accesses to page 800 */
 	if (sc->sc_type == WM_T_PCH) {
 		/* XXX e1000 driver do nothing... why? */
@@ -9829,18 +10189,18 @@ wm_access_phy_wakeup_reg_bm(device_t sel
 	 */
 
 	/* Set page 769 */
-	wm_gmii_mdic_writereg(self, 1, MII_IGPHY_PAGE_SELECT,
+	wm_gmii_mdic_writereg(dev, 1, MII_IGPHY_PAGE_SELECT,
 	    BM_WUC_ENABLE_PAGE << BME1000_PAGE_SHIFT);
 
 	/* Read WUCE and save it */
-	wuce = wm_gmii_mdic_readreg(self, 1, BM_WUC_ENABLE_REG);
+	wuce = wm_gmii_mdic_readreg(dev, 1, BM_WUC_ENABLE_REG);
 
 	reg = wuce | BM_WUC_ENABLE_BIT;
 	reg &= ~(BM_WUC_ME_WU_BIT | BM_WUC_HOST_WU_BIT);
-	wm_gmii_mdic_writereg(self, 1, BM_WUC_ENABLE_REG, reg);
+	wm_gmii_mdic_writereg(dev, 1, BM_WUC_ENABLE_REG, reg);
 
 	/* Select page 800 */
-	wm_gmii_mdic_writereg(self, 1, MII_IGPHY_PAGE_SELECT,
+	wm_gmii_mdic_writereg(dev, 1, MII_IGPHY_PAGE_SELECT,
 	    BM_WUC_PAGE << BME1000_PAGE_SHIFT);
 
 	/*
@@ -9849,22 +10209,22 @@ wm_access_phy_wakeup_reg_bm(device_t sel
 	 */
 
 	/* Write page 800 */
-	wm_gmii_mdic_writereg(self, 1, BM_WUC_ADDRESS_OPCODE, regnum);
+	wm_gmii_mdic_writereg(dev, 1, BM_WUC_ADDRESS_OPCODE, regnum);
 
 	if (rd)
-		*val = wm_gmii_mdic_readreg(self, 1, BM_WUC_DATA_OPCODE);
+		*val = wm_gmii_mdic_readreg(dev, 1, BM_WUC_DATA_OPCODE);
 	else
-		wm_gmii_mdic_writereg(self, 1, BM_WUC_DATA_OPCODE, *val);
+		wm_gmii_mdic_writereg(dev, 1, BM_WUC_DATA_OPCODE, *val);
 
 	/*
 	 * 3) Disable PHY wakeup register.
 	 * See e1000_disable_phy_wakeup_reg_access_bm().
 	 */
 	/* Set page 769 */
-	wm_gmii_mdic_writereg(self, 1, MII_IGPHY_PAGE_SELECT,
+	wm_gmii_mdic_writereg(dev, 1, MII_IGPHY_PAGE_SELECT,
 	    BM_WUC_ENABLE_PAGE << BME1000_PAGE_SHIFT);
 
-	wm_gmii_mdic_writereg(self, 1, BM_WUC_ENABLE_REG, wuce);
+	wm_gmii_mdic_writereg(dev, 1, BM_WUC_ENABLE_REG, wuce);
 }
 
 /*
@@ -9875,26 +10235,25 @@ wm_access_phy_wakeup_reg_bm(device_t sel
  * ressource ...
  */
 static int
-wm_gmii_hv_readreg(device_t self, int phy, int reg)
+wm_gmii_hv_readreg(device_t dev, int phy, int reg)
 {
-	struct wm_softc *sc = device_private(self);
+	struct wm_softc *sc = device_private(dev);
 	int rv;
 
 	DPRINTF(WM_DEBUG_GMII, ("%s: %s called\n",
-		device_xname(sc->sc_dev), __func__));
+		device_xname(dev), __func__));
 	if (sc->phy.acquire(sc)) {
-		aprint_error_dev(sc->sc_dev, "%s: failed to get semaphore\n",
-		    __func__);
+		device_printf(dev, "%s: failed to get semaphore\n", __func__);
 		return 0;
 	}
 
-	rv = wm_gmii_hv_readreg_locked(self, phy, reg);
+	rv = wm_gmii_hv_readreg_locked(dev, phy, reg);
 	sc->phy.release(sc);
 	return rv;
 }
 
 static int
-wm_gmii_hv_readreg_locked(device_t self, int phy, int reg)
+wm_gmii_hv_readreg_locked(device_t dev, int phy, int reg)
 {
 	uint16_t page = BM_PHY_REG_PAGE(reg);
 	uint16_t regnum = BM_PHY_REG_NUM(reg);
@@ -9905,7 +10264,7 @@ wm_gmii_hv_readreg_locked(device_t self,
 
 	/* 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);
+		wm_access_phy_wakeup_reg_bm(dev, reg, &val, 1);
 		return val;
 	}
 
@@ -9918,12 +10277,19 @@ wm_gmii_hv_readreg_locked(device_t self,
 		return 0;
 	}
 
+	/*
+	 * XXX I21[789] documents say that the SMBus Address register is at
+	 * PHY address 01, Page 0 (not 768), Register 26.
+	 */
+	if (page == HV_INTC_FC_PAGE_START)
+		page = 0;
+
 	if (regnum > BME1000_MAX_MULTI_PAGE_REG) {
-		wm_gmii_mdic_writereg(self, 1, MII_IGPHY_PAGE_SELECT,
+		wm_gmii_mdic_writereg(dev, 1, MII_IGPHY_PAGE_SELECT,
 		    page << BME1000_PAGE_SHIFT);
 	}
 
-	rv = wm_gmii_mdic_readreg(self, phy, regnum & MII_ADDRMASK);
+	rv = wm_gmii_mdic_readreg(dev, phy, regnum & MII_ADDRMASK);
 	return rv;
 }
 
@@ -9935,27 +10301,26 @@ wm_gmii_hv_readreg_locked(device_t self,
  * ressource ...
  */
 static void
-wm_gmii_hv_writereg(device_t self, int phy, int reg, int val)
+wm_gmii_hv_writereg(device_t dev, int phy, int reg, int val)
 {
-	struct wm_softc *sc = device_private(self);
+	struct wm_softc *sc = device_private(dev);
 
 	DPRINTF(WM_DEBUG_GMII, ("%s: %s called\n",
-		device_xname(sc->sc_dev), __func__));
+		device_xname(dev), __func__));
 
 	if (sc->phy.acquire(sc)) {
-		aprint_error_dev(sc->sc_dev, "%s: failed to get semaphore\n",
-		    __func__);
+		device_printf(dev, "%s: failed to get semaphore\n", __func__);
 		return;
 	}
 
-	wm_gmii_hv_writereg_locked(self, phy, reg, val);
+	wm_gmii_hv_writereg_locked(dev, phy, reg, val);
 	sc->phy.release(sc);
 }
 
 static void
-wm_gmii_hv_writereg_locked(device_t self, int phy, int reg, int val)
+wm_gmii_hv_writereg_locked(device_t dev, int phy, int reg, int val)
 {
-	struct wm_softc *sc = device_private(self);
+	struct wm_softc *sc = device_private(dev);
 	uint16_t page = BM_PHY_REG_PAGE(reg);
 	uint16_t regnum = BM_PHY_REG_NUM(reg);
 
@@ -9966,7 +10331,7 @@ wm_gmii_hv_writereg_locked(device_t self
 		uint16_t tmp;
 
 		tmp = val;
-		wm_access_phy_wakeup_reg_bm(self, reg, &tmp, 0);
+		wm_access_phy_wakeup_reg_bm(dev, reg, &tmp, 0);
 		return;
 	}
 
@@ -9981,6 +10346,13 @@ wm_gmii_hv_writereg_locked(device_t self
 
 	{
 		/*
+		 * XXX I21[789] documents say that the SMBus Address register
+		 * is at PHY address 01, Page 0 (not 768), Register 26.
+		 */
+		if (page == HV_INTC_FC_PAGE_START)
+			page = 0;
+
+		/*
 		 * XXX Workaround MDIO accesses being disabled after entering
 		 * IEEE Power Down (whenever bit 11 of the PHY control
 		 * register is set)
@@ -9997,12 +10369,12 @@ wm_gmii_hv_writereg_locked(device_t self
 		}
 
 		if (regnum > BME1000_MAX_MULTI_PAGE_REG) {
-			wm_gmii_mdic_writereg(self, 1, MII_IGPHY_PAGE_SELECT,
+			wm_gmii_mdic_writereg(dev, 1, MII_IGPHY_PAGE_SELECT,
 			    page << BME1000_PAGE_SHIFT);
 		}
 	}
 
-	wm_gmii_mdic_writereg(self, phy, regnum & MII_ADDRMASK, val);
+	wm_gmii_mdic_writereg(dev, phy, regnum & MII_ADDRMASK, val);
 }
 
 /*
@@ -10013,18 +10385,24 @@ wm_gmii_hv_writereg_locked(device_t self
  * ressource ...
  */
 static int
-wm_gmii_82580_readreg(device_t self, int phy, int reg)
+wm_gmii_82580_readreg(device_t dev, int phy, int reg)
 {
-	struct wm_softc *sc = device_private(self);
+	struct wm_softc *sc = device_private(dev);
 	int rv;
 
 	if (sc->phy.acquire(sc) != 0) {
-		aprint_error_dev(sc->sc_dev, "%s: failed to get semaphore\n",
-		    __func__);
+		device_printf(dev, "%s: failed to get semaphore\n", __func__);
 		return 0;
 	}
 
-	rv = wm_gmii_mdic_readreg(self, phy, reg);
+#ifdef DIAGNOSTIC
+	if (reg > MII_ADDRMASK) {
+		device_printf(dev, "%s: PHYTYPE = %d, addr 0x%x > 0x1f\n",
+		    __func__, sc->sc_phytype, reg);
+		reg &= MII_ADDRMASK;
+	}
+#endif
+	rv = wm_gmii_mdic_readreg(dev, phy, reg);
 
 	sc->phy.release(sc);
 	return rv;
@@ -10038,17 +10416,23 @@ wm_gmii_82580_readreg(device_t self, int
  * ressource ...
  */
 static void
-wm_gmii_82580_writereg(device_t self, int phy, int reg, int val)
+wm_gmii_82580_writereg(device_t dev, int phy, int reg, int val)
 {
-	struct wm_softc *sc = device_private(self);
+	struct wm_softc *sc = device_private(dev);
 
 	if (sc->phy.acquire(sc) != 0) {
-		aprint_error_dev(sc->sc_dev, "%s: failed to get semaphore\n",
-		    __func__);
+		device_printf(dev, "%s: failed to get semaphore\n", __func__);
 		return;
 	}
 
-	wm_gmii_mdic_writereg(self, phy, reg, val);
+#ifdef DIAGNOSTIC
+	if (reg > MII_ADDRMASK) {
+		device_printf(dev, "%s: PHYTYPE = %d, addr 0x%x > 0x1f\n",
+		    __func__, sc->sc_phytype, reg);
+		reg &= MII_ADDRMASK;
+	}
+#endif
+	wm_gmii_mdic_writereg(dev, phy, reg, val);
 
 	sc->phy.release(sc);
 }
@@ -10061,26 +10445,25 @@ wm_gmii_82580_writereg(device_t self, in
  * ressource ...
  */
 static int
-wm_gmii_gs40g_readreg(device_t self, int phy, int reg)
+wm_gmii_gs40g_readreg(device_t dev, int phy, int reg)
 {
-	struct wm_softc *sc = device_private(self);
+	struct wm_softc *sc = device_private(dev);
 	int page, offset;
 	int rv;
 
 	/* Acquire semaphore */
 	if (sc->phy.acquire(sc)) {
-		aprint_error_dev(sc->sc_dev, "%s: failed to get semaphore\n",
-		    __func__);
+		device_printf(dev, "%s: failed to get semaphore\n", __func__);
 		return 0;
 	}
 
 	/* Page select */
 	page = reg >> GS40G_PAGE_SHIFT;
-	wm_gmii_mdic_writereg(self, phy, GS40G_PAGE_SELECT, page);
+	wm_gmii_mdic_writereg(dev, phy, GS40G_PAGE_SELECT, page);
 
 	/* Read reg */
 	offset = reg & GS40G_OFFSET_MASK;
-	rv = wm_gmii_mdic_readreg(self, phy, offset);
+	rv = wm_gmii_mdic_readreg(dev, phy, offset);
 
 	sc->phy.release(sc);
 	return rv;
@@ -10094,25 +10477,24 @@ wm_gmii_gs40g_readreg(device_t self, int
  * ressource ...
  */
 static void
-wm_gmii_gs40g_writereg(device_t self, int phy, int reg, int val)
+wm_gmii_gs40g_writereg(device_t dev, int phy, int reg, int val)
 {
-	struct wm_softc *sc = device_private(self);
+	struct wm_softc *sc = device_private(dev);
 	int page, offset;
 
 	/* Acquire semaphore */
 	if (sc->phy.acquire(sc)) {
-		aprint_error_dev(sc->sc_dev, "%s: failed to get semaphore\n",
-		    __func__);
+		device_printf(dev, "%s: failed to get semaphore\n", __func__);
 		return;
 	}
 
 	/* Page select */
 	page = reg >> GS40G_PAGE_SHIFT;
-	wm_gmii_mdic_writereg(self, phy, GS40G_PAGE_SELECT, page);
+	wm_gmii_mdic_writereg(dev, phy, GS40G_PAGE_SELECT, page);
 
 	/* Write reg */
 	offset = reg & GS40G_OFFSET_MASK;
-	wm_gmii_mdic_writereg(self, phy, offset, val);
+	wm_gmii_mdic_writereg(dev, phy, offset, val);
 
 	/* Release semaphore */
 	sc->phy.release(sc);
@@ -10190,7 +10572,7 @@ wm_gmii_statchg(struct ifnet *ifp)
  *	Read a kumeran register
  */
 static int
-wm_kmrn_readreg(struct wm_softc *sc, int reg)
+wm_kmrn_readreg(struct wm_softc *sc, int reg, uint16_t *val)
 {
 	int rv;
 
@@ -10199,12 +10581,12 @@ wm_kmrn_readreg(struct wm_softc *sc, int
 	else
 		rv = sc->phy.acquire(sc);
 	if (rv != 0) {
-		aprint_error_dev(sc->sc_dev,
-		    "%s: failed to get semaphore\n", __func__);
-		return 0;
+		device_printf(sc->sc_dev, "%s: failed to get semaphore\n",
+		    __func__);
+		return rv;
 	}
 
-	rv = wm_kmrn_readreg_locked(sc, reg);
+	rv = wm_kmrn_readreg_locked(sc, reg, val);
 
 	if (sc->sc_type == WM_T_80003)
 		wm_put_swfw_semaphore(sc, SWFW_MAC_CSR_SM);
@@ -10215,9 +10597,8 @@ wm_kmrn_readreg(struct wm_softc *sc, int
 }
 
 static int
-wm_kmrn_readreg_locked(struct wm_softc *sc, int reg)
+wm_kmrn_readreg_locked(struct wm_softc *sc, int reg, uint16_t *val)
 {
-	int rv;
 
 	CSR_WRITE(sc, WMREG_KUMCTRLSTA,
 	    ((reg << KUMCTRLSTA_OFFSET_SHIFT) & KUMCTRLSTA_OFFSET) |
@@ -10225,9 +10606,9 @@ wm_kmrn_readreg_locked(struct wm_softc *
 	CSR_WRITE_FLUSH(sc);
 	delay(2);
 
-	rv = CSR_READ(sc, WMREG_KUMCTRLSTA) & KUMCTRLSTA_MASK;
+	*val = CSR_READ(sc, WMREG_KUMCTRLSTA) & KUMCTRLSTA_MASK;
 
-	return rv;
+	return 0;
 }
 
 /*
@@ -10235,8 +10616,8 @@ wm_kmrn_readreg_locked(struct wm_softc *
  *
  *	Write a kumeran register
  */
-static void
-wm_kmrn_writereg(struct wm_softc *sc, int reg, int val)
+static int
+wm_kmrn_writereg(struct wm_softc *sc, int reg, uint16_t val)
 {
 	int rv;
 
@@ -10245,26 +10626,29 @@ wm_kmrn_writereg(struct wm_softc *sc, in
 	else
 		rv = sc->phy.acquire(sc);
 	if (rv != 0) {
-		aprint_error_dev(sc->sc_dev,
-		    "%s: failed to get semaphore\n", __func__);
-		return;
+		device_printf(sc->sc_dev, "%s: failed to get semaphore\n",
+		    __func__);
+		return rv;
 	}
 
-	wm_kmrn_writereg_locked(sc, reg, val);
+	rv = wm_kmrn_writereg_locked(sc, reg, val);
 
 	if (sc->sc_type == WM_T_80003)
 		wm_put_swfw_semaphore(sc, SWFW_MAC_CSR_SM);
 	else
 		sc->phy.release(sc);
+
+	return rv;
 }
 
-static void
-wm_kmrn_writereg_locked(struct wm_softc *sc, int reg, int val)
+static int
+wm_kmrn_writereg_locked(struct wm_softc *sc, int reg, uint16_t val)
 {
 
 	CSR_WRITE(sc, WMREG_KUMCTRLSTA,
-	    ((reg << KUMCTRLSTA_OFFSET_SHIFT) & KUMCTRLSTA_OFFSET) |
-	    (val & KUMCTRLSTA_MASK));
+	    ((reg << KUMCTRLSTA_OFFSET_SHIFT) & KUMCTRLSTA_OFFSET) | val);
+
+	return 0;
 }
 
 /* SGMII related */
@@ -10310,15 +10694,14 @@ wm_sgmii_uses_mdio(struct wm_softc *sc)
  * ressource ...
  */
 static int
-wm_sgmii_readreg(device_t self, int phy, int reg)
+wm_sgmii_readreg(device_t dev, int phy, int reg)
 {
-	struct wm_softc *sc = device_private(self);
+	struct wm_softc *sc = device_private(dev);
 	uint32_t i2ccmd;
 	int i, rv;
 
 	if (sc->phy.acquire(sc)) {
-		aprint_error_dev(sc->sc_dev, "%s: failed to get semaphore\n",
-		    __func__);
+		device_printf(dev, "%s: failed to get semaphore\n", __func__);
 		return 0;
 	}
 
@@ -10335,9 +10718,9 @@ wm_sgmii_readreg(device_t self, int phy,
 			break;
 	}
 	if ((i2ccmd & I2CCMD_READY) == 0)
-		aprint_error_dev(sc->sc_dev, "I2CCMD Read did not complete\n");
+		device_printf(dev, "I2CCMD Read did not complete\n");
 	if ((i2ccmd & I2CCMD_ERROR) != 0)
-		aprint_error_dev(sc->sc_dev, "I2CCMD Error bit set\n");
+		device_printf(dev, "I2CCMD Error bit set\n");
 
 	rv = ((i2ccmd >> 8) & 0x00ff) | ((i2ccmd << 8) & 0xff00);
 
@@ -10353,16 +10736,15 @@ wm_sgmii_readreg(device_t self, int phy,
  * ressource ...
  */
 static void
-wm_sgmii_writereg(device_t self, int phy, int reg, int val)
+wm_sgmii_writereg(device_t dev, int phy, int reg, int val)
 {
-	struct wm_softc *sc = device_private(self);
+	struct wm_softc *sc = device_private(dev);
 	uint32_t i2ccmd;
 	int i;
 	int val_swapped;
 
 	if (sc->phy.acquire(sc) != 0) {
-		aprint_error_dev(sc->sc_dev, "%s: failed to get semaphore\n",
-		    __func__);
+		device_printf(dev, "%s: failed to get semaphore\n", __func__);
 		return;
 	}
 	/* Swap the data bytes for the I2C interface */
@@ -10380,9 +10762,9 @@ wm_sgmii_writereg(device_t self, int phy
 			break;
 	}
 	if ((i2ccmd & I2CCMD_READY) == 0)
-		aprint_error_dev(sc->sc_dev, "I2CCMD Write did not complete\n");
+		device_printf(dev, "I2CCMD Write did not complete\n");
 	if ((i2ccmd & I2CCMD_ERROR) != 0)
-		aprint_error_dev(sc->sc_dev, "I2CCMD Error bit set\n");
+		device_printf(dev, "I2CCMD Error bit set\n");
 
 	sc->phy.release(sc);
 }
@@ -11144,6 +11526,9 @@ wm_nvm_read_uwire(struct wm_softc *sc, i
 	DPRINTF(WM_DEBUG_NVM, ("%s: %s called\n",
 		device_xname(sc->sc_dev), __func__));
 
+	if (sc->nvm.acquire(sc) != 0)
+		return -1;
+
 	for (i = 0; i < wordcnt; i++) {
 		/* Clear SK and DI. */
 		reg = CSR_READ(sc, WMREG_EECD) & ~(EECD_SK | EECD_DI);
@@ -11189,6 +11574,7 @@ wm_nvm_read_uwire(struct wm_softc *sc, i
 		delay(2);
 	}
 
+	sc->nvm.release(sc);
 	return 0;
 }
 
@@ -11217,7 +11603,10 @@ wm_nvm_set_addrbits_size_eecd(struct wm_
 	case WM_T_82547_2:
 		/* Set dummy value to access EEPROM */
 		sc->sc_nvm_wordsize = 64;
-		wm_nvm_read(sc, NVM_OFF_EEPROM_SIZE, 1, &data);
+		if (wm_nvm_read(sc, NVM_OFF_EEPROM_SIZE, 1, &data) != 0) {
+			aprint_error_dev(sc->sc_dev,
+			    "%s: failed to read EEPROM size\n", __func__);
+		}
 		reg = data;
 		size = __SHIFTOUT(reg, EECD_EE_SIZE_EX_MASK);
 		if (size == 0)
@@ -11280,7 +11669,7 @@ wm_nvm_ready_spi(struct wm_softc *sc)
 	}
 	if (usec >= SPI_MAX_RETRIES) {
 		aprint_error_dev(sc->sc_dev,"EEPROM failed to become ready\n");
-		return 1;
+		return -1;
 	}
 	return 0;
 }
@@ -11296,18 +11685,22 @@ wm_nvm_read_spi(struct wm_softc *sc, int
 	uint32_t reg, val;
 	int i;
 	uint8_t opc;
+	int rv = 0;
 
 	DPRINTF(WM_DEBUG_NVM, ("%s: %s called\n",
 		device_xname(sc->sc_dev), __func__));
 
+	if (sc->nvm.acquire(sc) != 0)
+		return -1;
+
 	/* Clear SK and CS. */
 	reg = CSR_READ(sc, WMREG_EECD) & ~(EECD_SK | EECD_CS);
 	CSR_WRITE(sc, WMREG_EECD, reg);
 	CSR_WRITE_FLUSH(sc);
 	delay(2);
 
-	if (wm_nvm_ready_spi(sc))
-		return 1;
+	if ((rv = wm_nvm_ready_spi(sc)) != 0)
+		goto out;
 
 	/* Toggle CS to flush commands. */
 	CSR_WRITE(sc, WMREG_EECD, reg | EECD_CS);
@@ -11335,7 +11728,9 @@ wm_nvm_read_spi(struct wm_softc *sc, int
 	CSR_WRITE_FLUSH(sc);
 	delay(2);
 
-	return 0;
+out:
+	sc->nvm.release(sc);
+	return rv;
 }
 
 /* Using with EERD */
@@ -11365,23 +11760,27 @@ wm_nvm_read_eerd(struct wm_softc *sc, in
     uint16_t *data)
 {
 	int i, eerd = 0;
-	int error = 0;
+	int rv = 0;
 
 	DPRINTF(WM_DEBUG_NVM, ("%s: %s called\n",
 		device_xname(sc->sc_dev), __func__));
 
+	if (sc->nvm.acquire(sc) != 0)
+		return -1;
+
 	for (i = 0; i < wordcnt; i++) {
 		eerd = ((offset + i) << EERD_ADDR_SHIFT) | EERD_START;
-
 		CSR_WRITE(sc, WMREG_EERD, eerd);
-		error = wm_poll_eerd_eewr_done(sc, WMREG_EERD);
-		if (error != 0)
+		rv = wm_poll_eerd_eewr_done(sc, WMREG_EERD);
+		if (rv != 0) {
+			aprint_error_dev(sc->sc_dev, "EERD polling failed\n");
 			break;
-
+		}
 		data[i] = (CSR_READ(sc, WMREG_EERD) >> EERD_DATA_SHIFT);
 	}
 
-	return error;
+	sc->nvm.release(sc);
+	return rv;
 }
 
 /* Flash */
@@ -11711,7 +12110,7 @@ wm_read_ich8_dword(struct wm_softc *sc, 
 static int
 wm_nvm_read_ich8(struct wm_softc *sc, int offset, int words, uint16_t *data)
 {
-	int32_t  error = 0;
+	int32_t  rv = 0;
 	uint32_t flash_bank = 0;
 	uint32_t act_offset = 0;
 	uint32_t bank_offset = 0;
@@ -11721,14 +12120,17 @@ wm_nvm_read_ich8(struct wm_softc *sc, in
 	DPRINTF(WM_DEBUG_NVM, ("%s: %s called\n",
 		device_xname(sc->sc_dev), __func__));
 
+	if (sc->nvm.acquire(sc) != 0)
+		return -1;
+
 	/*
 	 * We need to know which is the valid flash bank.  In the event
 	 * that we didn't allocate eeprom_shadow_ram, we may not be
 	 * managing flash_bank.  So it cannot be trusted and needs
 	 * to be updated with each read.
 	 */
-	error = wm_nvm_valid_bank_detect_ich8lan(sc, &flash_bank);
-	if (error) {
+	rv = wm_nvm_valid_bank_detect_ich8lan(sc, &flash_bank);
+	if (rv) {
 		DPRINTF(WM_DEBUG_NVM, ("%s: failed to detect NVM bank\n",
 			device_xname(sc->sc_dev)));
 		flash_bank = 0;
@@ -11740,18 +12142,11 @@ wm_nvm_read_ich8(struct wm_softc *sc, in
 	 */
 	bank_offset = flash_bank * (sc->sc_ich8_flash_bank_size * 2);
 
-	error = wm_get_swfwhw_semaphore(sc);
-	if (error) {
-		aprint_error_dev(sc->sc_dev, "%s: failed to get semaphore\n",
-		    __func__);
-		return error;
-	}
-
 	for (i = 0; i < words; i++) {
 		/* The NVM part needs a byte offset, hence * 2 */
 		act_offset = bank_offset + ((offset + i) * 2);
-		error = wm_read_ich8_word(sc, act_offset, &word);
-		if (error) {
+		rv = wm_read_ich8_word(sc, act_offset, &word);
+		if (rv) {
 			aprint_error_dev(sc->sc_dev,
 			    "%s: failed to read NVM\n", __func__);
 			break;
@@ -11759,8 +12154,8 @@ wm_nvm_read_ich8(struct wm_softc *sc, in
 		data[i] = word;
 	}
 
-	wm_put_swfwhw_semaphore(sc);
-	return error;
+	sc->nvm.release(sc);
+	return rv;
 }
 
 /******************************************************************************
@@ -11775,7 +12170,7 @@ wm_nvm_read_ich8(struct wm_softc *sc, in
 static int
 wm_nvm_read_spt(struct wm_softc *sc, int offset, int words, uint16_t *data)
 {
-	int32_t  error = 0;
+	int32_t  rv = 0;
 	uint32_t flash_bank = 0;
 	uint32_t act_offset = 0;
 	uint32_t bank_offset = 0;
@@ -11785,14 +12180,17 @@ wm_nvm_read_spt(struct wm_softc *sc, int
 	DPRINTF(WM_DEBUG_NVM, ("%s: %s called\n",
 		device_xname(sc->sc_dev), __func__));
 
+	if (sc->nvm.acquire(sc) != 0)
+		return -1;
+
 	/*
 	 * We need to know which is the valid flash bank.  In the event
 	 * that we didn't allocate eeprom_shadow_ram, we may not be
 	 * managing flash_bank.  So it cannot be trusted and needs
 	 * to be updated with each read.
 	 */
-	error = wm_nvm_valid_bank_detect_ich8lan(sc, &flash_bank);
-	if (error) {
+	rv = wm_nvm_valid_bank_detect_ich8lan(sc, &flash_bank);
+	if (rv) {
 		DPRINTF(WM_DEBUG_NVM, ("%s: failed to detect NVM bank\n",
 			device_xname(sc->sc_dev)));
 		flash_bank = 0;
@@ -11804,19 +12202,12 @@ wm_nvm_read_spt(struct wm_softc *sc, int
 	 */
 	bank_offset = flash_bank * (sc->sc_ich8_flash_bank_size * 2);
 
-	error = wm_get_swfwhw_semaphore(sc);
-	if (error) {
-		aprint_error_dev(sc->sc_dev, "%s: failed to get semaphore\n",
-		    __func__);
-		return error;
-	}
-
 	for (i = 0; i < words; i++) {
 		/* The NVM part needs a byte offset, hence * 2 */
 		act_offset = bank_offset + ((offset + i) * 2);
 		/* but we must read dword aligned, so mask ... */
-		error = wm_read_ich8_dword(sc, act_offset & ~0x3, &dword);
-		if (error) {
+		rv = wm_read_ich8_dword(sc, act_offset & ~0x3, &dword);
+		if (rv) {
 			aprint_error_dev(sc->sc_dev,
 			    "%s: failed to read NVM\n", __func__);
 			break;
@@ -11828,8 +12219,8 @@ wm_nvm_read_spt(struct wm_softc *sc, int
 			data[i] = (uint16_t)((dword >> 16) & 0xFFFF);
 	}
 
-	wm_put_swfwhw_semaphore(sc);
-	return error;
+	sc->nvm.release(sc);
+	return rv;
 }
 
 /* iNVM */
@@ -11877,6 +12268,9 @@ wm_nvm_read_invm(struct wm_softc *sc, in
 	DPRINTF(WM_DEBUG_NVM, ("%s: %s called\n",
 		device_xname(sc->sc_dev), __func__));
 
+	if (sc->nvm.acquire(sc) != 0)
+		return -1;
+
 	for (i = 0; i < words; i++) {
 		switch (offset + i) {
 		case NVM_OFF_MACADDR:
@@ -11931,142 +12325,50 @@ wm_nvm_read_invm(struct wm_softc *sc, in
 		}
 	}
 
+	sc->nvm.release(sc);
 	return rv;
 }
 
 /* Lock, detecting NVM type, validate checksum, version and read */
 
-/*
- * wm_nvm_acquire:
- *
- *	Perform the EEPROM handshake required on some chips.
- */
 static int
-wm_nvm_acquire(struct wm_softc *sc)
+wm_nvm_is_onboard_eeprom(struct wm_softc *sc)
 {
-	uint32_t reg;
-	int x;
-	int ret = 0;
+	uint32_t eecd = 0;
 
-	DPRINTF(WM_DEBUG_NVM, ("%s: %s called\n",
-		device_xname(sc->sc_dev), __func__));
+	if (sc->sc_type == WM_T_82573 || sc->sc_type == WM_T_82574
+	    || sc->sc_type == WM_T_82583) {
+		eecd = CSR_READ(sc, WMREG_EECD);
 
-	if (sc->sc_type >= WM_T_ICH8) {
-		ret = wm_get_nvm_ich8lan(sc);
-	} else if (sc->sc_flags & WM_F_LOCK_EXTCNF) {
-		ret = wm_get_swfwhw_semaphore(sc);
-	} else if (sc->sc_flags & WM_F_LOCK_SWFW) {
-		/* This will also do wm_get_swsm_semaphore() if needed */
-		ret = wm_get_swfw_semaphore(sc, SWFW_EEP_SM);
-	} else if (sc->sc_flags & WM_F_LOCK_SWSM) {
-		ret = wm_get_swsm_semaphore(sc);
-	}
+		/* Isolate bits 15 & 16 */
+		eecd = ((eecd >> 15) & 0x03);
 
-	if (ret) {
-		aprint_error_dev(sc->sc_dev, "%s: failed to get semaphore\n",
-			__func__);
-		return 1;
+		/* If both bits are set, device is Flash type */
+		if (eecd == 0x03)
+			return 0;
 	}
+	return 1;
+}
 
-	if (sc->sc_flags & WM_F_LOCK_EECD) {
-		reg = CSR_READ(sc, WMREG_EECD);
-
-		/* Request EEPROM access. */
-		reg |= EECD_EE_REQ;
-		CSR_WRITE(sc, WMREG_EECD, reg);
+static int
+wm_nvm_get_flash_presence_i210(struct wm_softc *sc)
+{
+	uint32_t eec;
 
-		/* ..and wait for it to be granted. */
-		for (x = 0; x < 1000; x++) {
-			reg = CSR_READ(sc, WMREG_EECD);
-			if (reg & EECD_EE_GNT)
-				break;
-			delay(5);
-		}
-		if ((reg & EECD_EE_GNT) == 0) {
-			aprint_error_dev(sc->sc_dev,
-			    "could not acquire EEPROM GNT\n");
-			reg &= ~EECD_EE_REQ;
-			CSR_WRITE(sc, WMREG_EECD, reg);
-			if (sc->sc_flags & WM_F_LOCK_EXTCNF)
-				wm_put_swfwhw_semaphore(sc);
-			if (sc->sc_flags & WM_F_LOCK_SWFW)
-				wm_put_swfw_semaphore(sc, SWFW_EEP_SM);
-			else if (sc->sc_flags & WM_F_LOCK_SWSM)
-				wm_put_swsm_semaphore(sc);
-			return 1;
-		}
-	}
+	eec = CSR_READ(sc, WMREG_EEC);
+	if ((eec & EEC_FLASH_DETECTED) != 0)
+		return 1;
 
 	return 0;
 }
 
 /*
- * wm_nvm_release:
+ * wm_nvm_validate_checksum
  *
- *	Release the EEPROM mutex.
+ * The checksum is defined as the sum of the first 64 (16 bit) words.
  */
-static void
-wm_nvm_release(struct wm_softc *sc)
-{
-	uint32_t reg;
-
-	DPRINTF(WM_DEBUG_NVM, ("%s: %s called\n",
-		device_xname(sc->sc_dev), __func__));
-
-	if (sc->sc_flags & WM_F_LOCK_EECD) {
-		reg = CSR_READ(sc, WMREG_EECD);
-		reg &= ~EECD_EE_REQ;
-		CSR_WRITE(sc, WMREG_EECD, reg);
-	}
-
-	if (sc->sc_type >= WM_T_ICH8) {
-		wm_put_nvm_ich8lan(sc);
-	} else if (sc->sc_flags & WM_F_LOCK_EXTCNF)
-		wm_put_swfwhw_semaphore(sc);
-	if (sc->sc_flags & WM_F_LOCK_SWFW)
-		wm_put_swfw_semaphore(sc, SWFW_EEP_SM);
-	else if (sc->sc_flags & WM_F_LOCK_SWSM)
-		wm_put_swsm_semaphore(sc);
-}
-
-static int
-wm_nvm_is_onboard_eeprom(struct wm_softc *sc)
-{
-	uint32_t eecd = 0;
-
-	if (sc->sc_type == WM_T_82573 || sc->sc_type == WM_T_82574
-	    || sc->sc_type == WM_T_82583) {
-		eecd = CSR_READ(sc, WMREG_EECD);
-
-		/* Isolate bits 15 & 16 */
-		eecd = ((eecd >> 15) & 0x03);
-
-		/* If both bits are set, device is Flash type */
-		if (eecd == 0x03)
-			return 0;
-	}
-	return 1;
-}
-
-static int
-wm_nvm_get_flash_presence_i210(struct wm_softc *sc)
-{
-	uint32_t eec;
-
-	eec = CSR_READ(sc, WMREG_EEC);
-	if ((eec & EEC_FLASH_DETECTED) != 0)
-		return 1;
-
-	return 0;
-}
-
-/*
- * wm_nvm_validate_checksum
- *
- * The checksum is defined as the sum of the first 64 (16 bit) words.
- */
-static int
-wm_nvm_validate_checksum(struct wm_softc *sc)
+static int
+wm_nvm_validate_checksum(struct wm_softc *sc)
 {
 	uint16_t checksum;
 	uint16_t eeprom_data;
@@ -12164,6 +12466,7 @@ wm_nvm_version(struct wm_softc *sc)
 	bool check_version = false;
 	bool check_optionrom = false;
 	bool have_build = false;
+	bool have_uid = true;
 
 	/*
 	 * Version format:
@@ -12182,7 +12485,16 @@ wm_nvm_version(struct wm_softc *sc)
 	 *		0x2013	2.1.3?
 	 *	82583	0x10a0	1.10.0? (document says it's default vaule)
 	 */
-	wm_nvm_read(sc, NVM_OFF_IMAGE_UID1, 1, &uid1);
+
+	/*
+	 * XXX
+	 * Qemu's e1000e emulation (82574L)'s SPI has only 64 words.
+	 * I've never seen on real 82574 hardware with such small SPI ROM.
+	 */
+	if ((sc->sc_nvm_wordsize < NVM_OFF_IMAGE_UID1)
+	    || (wm_nvm_read(sc, NVM_OFF_IMAGE_UID1, 1, &uid1) != 0))
+		have_uid = false;
+
 	switch (sc->sc_type) {
 	case WM_T_82571:
 	case WM_T_82572:
@@ -12200,10 +12512,12 @@ wm_nvm_version(struct wm_softc *sc)
 		break;
 	case WM_T_I211:
 		wm_nvm_version_invm(sc);
+		have_uid = false;
 		goto printver;
 	case WM_T_I210:
 		if (!wm_nvm_get_flash_presence_i210(sc)) {
 			wm_nvm_version_invm(sc);
+			have_uid = false;
 			goto printver;
 		}
 		/* FALLTHROUGH */
@@ -12215,8 +12529,8 @@ wm_nvm_version(struct wm_softc *sc)
 	default:
 		return;
 	}
-	if (check_version) {
-		wm_nvm_read(sc, NVM_OFF_VERSION, 1, &nvm_data);
+	if (check_version
+	    && (wm_nvm_read(sc, NVM_OFF_VERSION, 1, &nvm_data) == 0)) {
 		major = (nvm_data & NVM_MAJOR_MASK) >> NVM_MAJOR_SHIFT;
 		if (have_build || ((nvm_data & 0x0f00) != 0x0000)) {
 			minor = (nvm_data & NVM_MINOR_MASK) >> NVM_MINOR_SHIFT;
@@ -12238,14 +12552,18 @@ printver:
 			aprint_verbose(".%d", build);
 		}
 	}
-	if (check_optionrom) {
-		wm_nvm_read(sc, NVM_OFF_COMB_VER_PTR, 1, &off);
+
+	/* Assume the Option ROM area is at avove NVM_SIZE */
+	if ((sc->sc_nvm_wordsize >= NVM_SIZE) && check_optionrom
+	    && (wm_nvm_read(sc, NVM_OFF_COMB_VER_PTR, 1, &off) == 0)) {
 		/* Option ROM Version */
 		if ((off != 0x0000) && (off != 0xffff)) {
+			int rv;
+
 			off += NVM_COMBO_VER_OFF;
-			wm_nvm_read(sc, off + 1, 1, &uid1);
-			wm_nvm_read(sc, off, 1, &uid0);
-			if ((uid0 != 0) && (uid0 != 0xffff)
+			rv = wm_nvm_read(sc, off + 1, 1, &uid1);
+			rv |= wm_nvm_read(sc, off, 1, &uid0);
+			if ((rv == 0) && (uid0 != 0) && (uid0 != 0xffff)
 			    && (uid1 != 0) && (uid1 != 0xffff)) {
 				/* 16bits */
 				major = uid0 >> 8;
@@ -12257,8 +12575,8 @@ printver:
 		}
 	}
 
-	wm_nvm_read(sc, NVM_OFF_IMAGE_UID0, 1, &uid0);
-	aprint_verbose(", Image Unique ID %08x", (uid1 << 16) | uid0);
+	if (have_uid && (wm_nvm_read(sc, NVM_OFF_IMAGE_UID0, 1, &uid0) == 0))
+		aprint_verbose(", Image Unique ID %08x", (uid1 << 16) | uid0);
 }
 
 /*
@@ -12275,27 +12593,10 @@ wm_nvm_read(struct wm_softc *sc, int wor
 		device_xname(sc->sc_dev), __func__));
 
 	if (sc->sc_flags & WM_F_EEPROM_INVALID)
-		return 1;
-
-	if (wm_nvm_acquire(sc))
-		return 1;
-
-	if ((sc->sc_type == WM_T_ICH8) || (sc->sc_type == WM_T_ICH9)
-	    || (sc->sc_type == WM_T_ICH10) || (sc->sc_type == WM_T_PCH)
-	    || (sc->sc_type == WM_T_PCH2) || (sc->sc_type == WM_T_PCH_LPT))
-		rv = wm_nvm_read_ich8(sc, word, wordcnt, data);
-	else if (sc->sc_type == WM_T_PCH_SPT)
-		rv = wm_nvm_read_spt(sc, word, wordcnt, data);
-	else if (sc->sc_flags & WM_F_EEPROM_INVM)
-		rv = wm_nvm_read_invm(sc, word, wordcnt, data);
-	else if (sc->sc_flags & WM_F_EEPROM_EERDEEWR)
-		rv = wm_nvm_read_eerd(sc, word, wordcnt, data);
-	else if (sc->sc_flags & WM_F_EEPROM_SPI)
-		rv = wm_nvm_read_spi(sc, word, wordcnt, data);
-	else
-		rv = wm_nvm_read_uwire(sc, word, wordcnt, data);
+		return -1;
 
-	wm_nvm_release(sc);
+	rv = sc->nvm.read(sc, word, wordcnt, data);
+	
 	return rv;
 }
 
@@ -12322,6 +12623,94 @@ wm_put_null(struct wm_softc *sc)
 	return;
 }
 
+static int
+wm_get_eecd(struct wm_softc *sc)
+{
+	uint32_t reg;
+	int x;
+
+	DPRINTF(WM_DEBUG_LOCK | WM_DEBUG_NVM, ("%s: %s called\n",
+		device_xname(sc->sc_dev), __func__));
+
+	reg = CSR_READ(sc, WMREG_EECD);
+
+	/* Request EEPROM access. */
+	reg |= EECD_EE_REQ;
+	CSR_WRITE(sc, WMREG_EECD, reg);
+
+	/* ..and wait for it to be granted. */
+	for (x = 0; x < 1000; x++) {
+		reg = CSR_READ(sc, WMREG_EECD);
+		if (reg & EECD_EE_GNT)
+			break;
+		delay(5);
+	}
+	if ((reg & EECD_EE_GNT) == 0) {
+		aprint_error_dev(sc->sc_dev,
+		    "could not acquire EEPROM GNT\n");
+		reg &= ~EECD_EE_REQ;
+		CSR_WRITE(sc, WMREG_EECD, reg);
+		return -1;
+	}
+
+	return 0;
+}
+
+static void
+wm_nvm_eec_clock_raise(struct wm_softc *sc, uint32_t *eecd)
+{
+
+	*eecd |= EECD_SK;
+	CSR_WRITE(sc, WMREG_EECD, *eecd);
+	CSR_WRITE_FLUSH(sc);
+	if ((sc->sc_flags & WM_F_EEPROM_SPI) != 0)
+		delay(1);
+	else
+		delay(50);
+}
+
+static void
+wm_nvm_eec_clock_lower(struct wm_softc *sc, uint32_t *eecd)
+{
+
+	*eecd &= ~EECD_SK;
+	CSR_WRITE(sc, WMREG_EECD, *eecd);
+	CSR_WRITE_FLUSH(sc);
+	if ((sc->sc_flags & WM_F_EEPROM_SPI) != 0)
+		delay(1);
+	else
+		delay(50);
+}
+
+static void
+wm_put_eecd(struct wm_softc *sc)
+{
+	uint32_t reg;
+
+	DPRINTF(WM_DEBUG_LOCK, ("%s: %s called\n",
+		device_xname(sc->sc_dev), __func__));
+
+	/* Stop nvm */
+	reg = CSR_READ(sc, WMREG_EECD);
+	if ((sc->sc_flags & WM_F_EEPROM_SPI) != 0) {
+		/* Pull CS high */
+		reg |= EECD_CS;
+		wm_nvm_eec_clock_lower(sc, &reg);
+	} else {
+		/* CS on Microwire is active-high */
+		reg &= ~(EECD_CS | EECD_DI);
+		CSR_WRITE(sc, WMREG_EECD, reg);
+		wm_nvm_eec_clock_raise(sc, &reg);
+		wm_nvm_eec_clock_lower(sc, &reg);
+	}
+	
+	reg = CSR_READ(sc, WMREG_EECD);
+	reg &= ~EECD_EE_REQ;
+	CSR_WRITE(sc, WMREG_EECD, reg);
+
+	return;
+}
+
 /*
  * Get hardware semaphore.
  * Same as e1000_get_hw_semaphore_generic()
@@ -12336,6 +12725,7 @@ wm_get_swsm_semaphore(struct wm_softc *s
 		device_xname(sc->sc_dev), __func__));
 	KASSERT(sc->sc_nvm_wordsize > 0);
 
+retry:
 	/* Get the SW semaphore. */
 	timeout = sc->sc_nvm_wordsize + 1;
 	while (timeout) {
@@ -12349,6 +12739,16 @@ wm_get_swsm_semaphore(struct wm_softc *s
 	}
 
 	if (timeout == 0) {
+		if ((sc->sc_flags & WM_F_WA_I210_CLSEM) != 0) {
+			/*
+			 * In rare circumstances, the SW semaphore may already
+			 * be held unintentionally. Clear the semaphore once
+			 * before giving up.
+			 */
+			sc->sc_flags &= ~WM_F_WA_I210_CLSEM;
+			wm_put_swsm_semaphore(sc);
+			goto retry;
+		}
 		aprint_error_dev(sc->sc_dev,
 		    "could not acquire SWSM SMBI\n");
 		return 1;
@@ -12398,7 +12798,7 @@ wm_put_swsm_semaphore(struct wm_softc *s
 
 /*
  * Get SW/FW semaphore.
- * Same as e1000_acquire_swfw_sync_82575().
+ * Same as e1000_acquire_swfw_sync_{80003es2lan,82575}().
  */
 static int
 wm_get_swfw_semaphore(struct wm_softc *sc, uint16_t mask)
@@ -12406,31 +12806,31 @@ wm_get_swfw_semaphore(struct wm_softc *s
 	uint32_t swfw_sync;
 	uint32_t swmask = mask << SWFW_SOFT_SHIFT;
 	uint32_t fwmask = mask << SWFW_FIRM_SHIFT;
-	int timeout = 200;
+	int timeout;
 
 	DPRINTF(WM_DEBUG_LOCK, ("%s: %s called\n",
 		device_xname(sc->sc_dev), __func__));
-	KASSERT((sc->sc_flags & WM_F_LOCK_SWSM) != 0);
+
+	if (sc->sc_type == WM_T_80003)
+		timeout = 50;
+	else
+		timeout = 200;
 
 	for (timeout = 0; timeout < 200; timeout++) {
-		if (sc->sc_flags & WM_F_LOCK_SWSM) {
-			if (wm_get_swsm_semaphore(sc)) {
-				aprint_error_dev(sc->sc_dev,
-				    "%s: failed to get semaphore\n",
-				    __func__);
-				return 1;
-			}
+		if (wm_get_swsm_semaphore(sc)) {
+			aprint_error_dev(sc->sc_dev,
+			    "%s: failed to get semaphore\n",
+			    __func__);
+			return 1;
 		}
 		swfw_sync = CSR_READ(sc, WMREG_SW_FW_SYNC);
 		if ((swfw_sync & (swmask | fwmask)) == 0) {
 			swfw_sync |= swmask;
 			CSR_WRITE(sc, WMREG_SW_FW_SYNC, swfw_sync);
-			if (sc->sc_flags & WM_F_LOCK_SWSM)
-				wm_put_swsm_semaphore(sc);
+			wm_put_swsm_semaphore(sc);
 			return 0;
 		}
-		if (sc->sc_flags & WM_F_LOCK_SWSM)
-			wm_put_swsm_semaphore(sc);
+		wm_put_swsm_semaphore(sc);
 		delay(5000);
 	}
 	printf("%s: failed to get swfw semaphore mask 0x%x swfw 0x%x\n",
@@ -12445,17 +12845,103 @@ wm_put_swfw_semaphore(struct wm_softc *s
 
 	DPRINTF(WM_DEBUG_LOCK, ("%s: %s called\n",
 		device_xname(sc->sc_dev), __func__));
-	KASSERT((sc->sc_flags & WM_F_LOCK_SWSM) != 0);
 
-	if (sc->sc_flags & WM_F_LOCK_SWSM) {
-		while (wm_get_swsm_semaphore(sc) != 0)
-			continue;
-	}
+	while (wm_get_swsm_semaphore(sc) != 0)
+		continue;
+
 	swfw_sync = CSR_READ(sc, WMREG_SW_FW_SYNC);
 	swfw_sync &= ~(mask << SWFW_SOFT_SHIFT);
 	CSR_WRITE(sc, WMREG_SW_FW_SYNC, swfw_sync);
-	if (sc->sc_flags & WM_F_LOCK_SWSM)
+
+	wm_put_swsm_semaphore(sc);
+}
+
+static int
+wm_get_nvm_80003(struct wm_softc *sc)
+{
+	int rv;
+
+	DPRINTF(WM_DEBUG_LOCK | WM_DEBUG_NVM, ("%s: %s called\n",
+		device_xname(sc->sc_dev), __func__));
+
+	if ((rv = wm_get_swfw_semaphore(sc, SWFW_EEP_SM)) != 0) {
+		aprint_error_dev(sc->sc_dev,
+		    "%s: failed to get semaphore(SWFW)\n",
+		    __func__);
+		return rv;
+	}
+
+	if (((sc->sc_flags & WM_F_LOCK_EECD) != 0)
+	    && (rv = wm_get_eecd(sc)) != 0) {
+		aprint_error_dev(sc->sc_dev,
+		    "%s: failed to get semaphore(EECD)\n",
+		    __func__);
+		wm_put_swfw_semaphore(sc, SWFW_EEP_SM);
+		return rv;
+	}
+
+	return 0;
+}
+
+static void
+wm_put_nvm_80003(struct wm_softc *sc)
+{
+
+	DPRINTF(WM_DEBUG_LOCK, ("%s: %s called\n",
+		device_xname(sc->sc_dev), __func__));
+
+	if ((sc->sc_flags & WM_F_LOCK_EECD) != 0)
+		wm_put_eecd(sc);
+	wm_put_swfw_semaphore(sc, SWFW_EEP_SM);
+}
+
+static int
+wm_get_nvm_82571(struct wm_softc *sc)
+{
+	int rv;
+
+	DPRINTF(WM_DEBUG_LOCK, ("%s: %s called\n",
+		device_xname(sc->sc_dev), __func__));
+
+	if ((rv = wm_get_swsm_semaphore(sc)) != 0)
+		return rv;
+
+	switch (sc->sc_type) {
+	case WM_T_82573:
+		break;
+	default:
+		if ((sc->sc_flags & WM_F_LOCK_EECD) != 0)
+			rv = wm_get_eecd(sc);
+		break;
+	}
+
+	if (rv != 0) {
+		aprint_error_dev(sc->sc_dev,
+		    "%s: failed to get semaphore\n",
+		    __func__);
 		wm_put_swsm_semaphore(sc);
+	}
+
+	return rv;
+}
+
+static void
+wm_put_nvm_82571(struct wm_softc *sc)
+{
+
+	DPRINTF(WM_DEBUG_LOCK, ("%s: %s called\n",
+		device_xname(sc->sc_dev), __func__));
+
+	switch (sc->sc_type) {
+	case WM_T_82573:
+		break;
+	default:
+		if ((sc->sc_flags & WM_F_LOCK_EECD) != 0)
+			wm_put_eecd(sc);
+		break;
+	}
+
+	wm_put_swsm_semaphore(sc);
 }
 
 static int
@@ -13041,18 +13527,6 @@ wm_get_wakeup(struct wm_softc *sc)
 	if (wm_enable_mng_pass_thru(sc) != 0)
 		sc->sc_flags |= WM_F_HAS_MANAGE;
 
-#ifdef WM_DEBUG
-	printf("\n");
-	if ((sc->sc_flags & WM_F_HAS_AMT) != 0)
-		printf("HAS_AMT,");
-	if ((sc->sc_flags & WM_F_ARC_SUBSYS_VALID) != 0)
-		printf("ARC_SUBSYS_VALID,");
-	if ((sc->sc_flags & WM_F_ASF_FIRMWARE_PRES) != 0)
-		printf("ASF_FIRMWARE_PRES,");
-	if ((sc->sc_flags & WM_F_HAS_MANAGE) != 0)
-		printf("HAS_MANAGE,");
-	printf("\n");
-#endif
 	/*
 	 * Note that the WOL flags is set after the resetting of the eeprom
 	 * stuff
@@ -13308,28 +13782,56 @@ wm_enable_wakeup(struct wm_softc *sc)
 static void
 wm_lplu_d0_disable(struct wm_softc *sc)
 {
+	struct mii_data *mii = &sc->sc_mii;
 	uint32_t reg;
 
 	DPRINTF(WM_DEBUG_INIT, ("%s: %s called\n",
 		device_xname(sc->sc_dev), __func__));
 
-	reg = CSR_READ(sc, WMREG_PHY_CTRL);
-	reg &= ~(PHY_CTRL_GBE_DIS | PHY_CTRL_D0A_LPLU);
-	CSR_WRITE(sc, WMREG_PHY_CTRL, reg);
-}
-
-static void
-wm_lplu_d0_disable_pch(struct wm_softc *sc)
-{
-	uint32_t reg;
-
-	DPRINTF(WM_DEBUG_INIT, ("%s: %s called\n",
-		device_xname(sc->sc_dev), __func__));
+	if (sc->sc_phytype == WMPHY_IFE)
+		return;
 
-	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_ANEGNOW;
-	wm_gmii_hv_writereg(sc->sc_dev, 1, HV_OEM_BITS, reg);
+	switch (sc->sc_type) {
+	case WM_T_82571:
+	case WM_T_82572:
+	case WM_T_82573:
+	case WM_T_82575:
+	case WM_T_82576:
+		reg = mii->mii_readreg(sc->sc_dev, 1, MII_IGPHY_POWER_MGMT);
+		reg &= ~PMR_D0_LPLU;
+		mii->mii_writereg(sc->sc_dev, 1, MII_IGPHY_POWER_MGMT, reg);
+		break;
+	case WM_T_82580:
+	case WM_T_I350:
+	case WM_T_I210:
+	case WM_T_I211:
+		reg = CSR_READ(sc, WMREG_PHPM);
+		reg &= ~PHPM_D0A_LPLU;
+		CSR_WRITE(sc, WMREG_PHPM, reg);
+		break;
+	case WM_T_82574:
+	case WM_T_82583:
+	case WM_T_ICH8:
+	case WM_T_ICH9:
+	case WM_T_ICH10:
+		reg = CSR_READ(sc, WMREG_PHY_CTRL);
+		reg &= ~(PHY_CTRL_GBE_DIS | PHY_CTRL_D0A_LPLU);
+		CSR_WRITE(sc, WMREG_PHY_CTRL, reg);
+		CSR_WRITE_FLUSH(sc);
+		break;
+	case WM_T_PCH:
+	case WM_T_PCH2:
+	case WM_T_PCH_LPT:
+	case WM_T_PCH_SPT:
+		reg = wm_gmii_hv_readreg(sc->sc_dev, 1, HV_OEM_BITS);
+		reg &= ~(HV_OEM_BITS_A1KDIS | HV_OEM_BITS_LPLU);
+		if (wm_phy_resetisblocked(sc) == false)
+			reg |= HV_OEM_BITS_ANEGNOW;
+		wm_gmii_hv_writereg(sc->sc_dev, 1, HV_OEM_BITS, reg);
+		break;
+	default:
+		break;
+	}
 }
 
 /* EEE */
@@ -13368,31 +13870,32 @@ wm_set_eee_i350(struct wm_softc *sc)
 static void
 wm_kmrn_lock_loss_workaround_ich8lan(struct wm_softc *sc)
 {
-#if 0
-	int miistatus, active, i;
+	struct mii_data *mii = &sc->sc_mii;
+	uint32_t status = CSR_READ(sc, WMREG_STATUS);
+	int i;
 	int reg;
 
-	miistatus = sc->sc_mii.mii_media_status;
+	DPRINTF(WM_DEBUG_INIT, ("%s: %s called\n",
+		device_xname(sc->sc_dev), __func__));
 
 	/* If the link is not up, do nothing */
-	if ((miistatus & IFM_ACTIVE) == 0)
+	if ((status & STATUS_LU) == 0)
 		return;
 
-	active = sc->sc_mii.mii_media_active;
-
 	/* Nothing to do if the link is other than 1Gbps */
-	if (IFM_SUBTYPE(active) != IFM_1000_T)
+	if (__SHIFTOUT(status, STATUS_SPEED) != STATUS_SPEED_1000)
 		return;
 
+	reg = CSR_READ(sc, WMREG_PHY_CTRL);
 	for (i = 0; i < 10; i++) {
 		/* read twice */
-		reg = wm_gmii_i80003_readreg(sc->sc_dev, 1, IGP3_KMRN_DIAG);
-		reg = wm_gmii_i80003_readreg(sc->sc_dev, 1, IGP3_KMRN_DIAG);
+		reg = mii->mii_readreg(sc->sc_dev, 1, IGP3_KMRN_DIAG);
+		reg = mii->mii_readreg(sc->sc_dev, 1, IGP3_KMRN_DIAG);
 		if ((reg & IGP3_KMRN_DIAG_PCS_LOCK_LOSS) == 0)
 			goto out;	/* GOOD! */
 
 		/* Reset the PHY */
-		wm_gmii_reset(sc);
+		wm_reset_phy(sc);
 		delay(5*1000);
 	}
 
@@ -13409,22 +13912,23 @@ wm_kmrn_lock_loss_workaround_ich8lan(str
 
 out:
 	return;
-#endif
 }
 
 /* WOL from S5 stops working */
 static void
 wm_gig_downshift_workaround_ich8lan(struct wm_softc *sc)
 {
-	uint16_t kmrn_reg;
+	uint16_t kmreg;
 
 	/* Only for igp3 */
 	if (sc->sc_phytype == WMPHY_IGP_3) {
-		kmrn_reg = wm_kmrn_readreg(sc, KUMCTRLSTA_OFFSET_DIAG);
-		kmrn_reg |= KUMCTRLSTA_DIAG_NELPBK;
-		wm_kmrn_writereg(sc, KUMCTRLSTA_OFFSET_DIAG, kmrn_reg);
-		kmrn_reg &= ~KUMCTRLSTA_DIAG_NELPBK;
-		wm_kmrn_writereg(sc, KUMCTRLSTA_OFFSET_DIAG, kmrn_reg);
+		if (wm_kmrn_readreg(sc, KUMCTRLSTA_OFFSET_DIAG, &kmreg) != 0)
+			return;
+		kmreg |= KUMCTRLSTA_DIAG_NELPBK;
+		if (wm_kmrn_writereg(sc, KUMCTRLSTA_OFFSET_DIAG, kmreg) != 0)
+			return;
+		kmreg &= ~KUMCTRLSTA_DIAG_NELPBK;
+		wm_kmrn_writereg(sc, KUMCTRLSTA_OFFSET_DIAG, kmreg);
 	}
 }
 
@@ -13528,16 +14032,21 @@ static void
 wm_configure_k1_ich8lan(struct wm_softc *sc, int k1_enable)
 {
 	uint32_t ctrl, ctrl_ext, tmp;
-	uint16_t kmrn_reg;
+	uint16_t kmreg;
+	int rv;
 
-	kmrn_reg = wm_kmrn_readreg_locked(sc, KUMCTRLSTA_OFFSET_K1_CONFIG);
+	rv = wm_kmrn_readreg_locked(sc, KUMCTRLSTA_OFFSET_K1_CONFIG, &kmreg);
+	if (rv != 0)
+		return;
 
 	if (k1_enable)
-		kmrn_reg |= KUMCTRLSTA_K1_ENABLE;
+		kmreg |= KUMCTRLSTA_K1_ENABLE;
 	else
-		kmrn_reg &= ~KUMCTRLSTA_K1_ENABLE;
+		kmreg &= ~KUMCTRLSTA_K1_ENABLE;
 
-	wm_kmrn_writereg_locked(sc, KUMCTRLSTA_OFFSET_K1_CONFIG, kmrn_reg);
+	rv = wm_kmrn_writereg_locked(sc, KUMCTRLSTA_OFFSET_K1_CONFIG, kmreg);
+	if (rv != 0)
+		return;
 
 	delay(20);
 
@@ -13556,6 +14065,8 @@ wm_configure_k1_ich8lan(struct wm_softc 
 	CSR_WRITE(sc, WMREG_CTRL_EXT, ctrl_ext);
 	CSR_WRITE_FLUSH(sc);
 	delay(20);
+
+	return;
 }
 
 /* special case - for 82575 - need to do manual init ... */
@@ -13715,15 +14226,36 @@ wm_platform_pm_pch_lpt(struct wm_softc *
 	    | __SHIFTIN(link, LTRV_SNOOP_REQ) | LTRV_SEND;
 	uint32_t rxa;
 	uint16_t scale = 0, lat_enc = 0;
+	int32_t obff_hwm = 0;
 	int64_t lat_ns, value;
 	
 	DPRINTF(WM_DEBUG_INIT, ("%s: %s called\n",
 		device_xname(sc->sc_dev), __func__));
 
 	if (link) {
-		pcireg_t preg;
 		uint16_t max_snoop, max_nosnoop, max_ltr_enc;
+		uint32_t status;
+		uint16_t speed;
+		pcireg_t preg;
 
+		status = CSR_READ(sc, WMREG_STATUS);
+		switch (__SHIFTOUT(status, STATUS_SPEED)) {
+		case STATUS_SPEED_10:
+			speed = 10;
+			break;
+		case STATUS_SPEED_100:
+			speed = 100;
+			break;
+		case STATUS_SPEED_1000:
+			speed = 1000;
+			break;
+		default:
+			device_printf(sc->sc_dev, "Unknown speed "
+			    "(status = %08x)\n", status);
+			return -1;
+		}
+
+		/* Rx Packet Buffer Allocation size (KB) */
 		rxa = CSR_READ(sc, WMREG_PBA) & PBA_RXA_MASK;
 
 		/*
@@ -13736,31 +14268,12 @@ wm_platform_pm_pch_lpt(struct wm_softc *
 		 * 1=2^5ns, 2=2^10ns,...5=2^25ns.
 		 */
 		lat_ns = ((int64_t)rxa * 1024 -
-		    (2 * (int64_t)sc->sc_ethercom.ec_if.if_mtu)) * 8 * 1000;
+		    (2 * ((int64_t)sc->sc_ethercom.ec_if.if_mtu
+			+ ETHER_HDR_LEN))) * 8 * 1000;
 		if (lat_ns < 0)
 			lat_ns = 0;
-		else {
-			uint32_t status;
-			uint16_t speed;
-
-			status = CSR_READ(sc, WMREG_STATUS);
-			switch (__SHIFTOUT(status, STATUS_SPEED)) {
-			case STATUS_SPEED_10:
-				speed = 10;
-				break;
-			case STATUS_SPEED_100:
-				speed = 100;
-				break;
-			case STATUS_SPEED_1000:
-				speed = 1000;
-				break;
-			default:
-				printf("%s: Unknown speed (status = %08x)\n",
-				    device_xname(sc->sc_dev), status);
-				return -1;
-			}
+		else
 			lat_ns /= speed;
-		}
 		value = lat_ns;
 
 		while (value > LTRV_VALUE) {
@@ -13774,6 +14287,7 @@ wm_platform_pm_pch_lpt(struct wm_softc *
 		}
 		lat_enc = (uint16_t)(__SHIFTIN(scale, LTRV_SCALE) | value);
 
+		/* Determine the maximum latency tolerated by the platform */
 		preg = pci_conf_read(sc->sc_pc, sc->sc_pcitag,
 		    WM_PCI_LTR_CAP_LPT);
 		max_snoop = preg & 0xffff;
@@ -13783,12 +14297,39 @@ wm_platform_pm_pch_lpt(struct wm_softc *
 
 		if (lat_enc > max_ltr_enc) {
 			lat_enc = max_ltr_enc;
+			lat_ns = __SHIFTOUT(lat_enc, PCI_LTR_MAXSNOOPLAT_VAL)
+			    * PCI_LTR_SCALETONS(
+				    __SHIFTOUT(lat_enc,
+					PCI_LTR_MAXSNOOPLAT_SCALE));
+		}
+
+		if (lat_ns) {
+			lat_ns *= speed * 1000;
+			lat_ns /= 8;
+			lat_ns /= 1000000000;
+			obff_hwm = (int32_t)(rxa - lat_ns);
+		}
+		if ((obff_hwm < 0) || (obff_hwm > SVT_OFF_HWM)) {
+			device_printf(sc->sc_dev, "Invalid high water mark %d"
+			    "(rxa = %d, lat_ns = %d)\n",
+			    obff_hwm, (int32_t)rxa, (int32_t)lat_ns);
+			return -1;
 		}
 	}
 	/* Snoop and No-Snoop latencies the same */
 	reg |= lat_enc | __SHIFTIN(lat_enc, LTRV_NONSNOOP);
 	CSR_WRITE(sc, WMREG_LTRV, reg);
 
+	/* Set OBFF high water mark */
+	reg = CSR_READ(sc, WMREG_SVT) & ~SVT_OFF_HWM;
+	reg |= obff_hwm;
+	CSR_WRITE(sc, WMREG_SVT, reg);
+
+	/* Enable OBFF */
+	reg = CSR_READ(sc, WMREG_SVCR);
+	reg |= SVCR_OFF_EN | SVCR_OFF_MASKINT;
+	CSR_WRITE(sc, WMREG_SVCR, reg);
+	
 	return 0;
 }
 
@@ -13866,3 +14407,21 @@ wm_pll_workaround_i210(struct wm_softc *
 	if (wa_done)
 		aprint_verbose_dev(sc->sc_dev, "I210 workaround done\n");
 }
+
+static void
+wm_legacy_irq_quirk_spt(struct wm_softc *sc)
+{
+	uint32_t reg;
+
+	DPRINTF(WM_DEBUG_INIT, ("%s: %s called\n",
+		device_xname(sc->sc_dev), __func__));
+	KASSERT(sc->sc_type == WM_T_PCH_SPT);
+
+	reg = CSR_READ(sc, WMREG_FEXTNVM7);
+	reg |= FEXTNVM7_SIDE_CLK_UNGATE;
+	CSR_WRITE(sc, WMREG_FEXTNVM7, reg);
+
+	reg = CSR_READ(sc, WMREG_FEXTNVM9);
+	reg |= FEXTNVM9_IOSFSB_CLKGATE_DIS | FEXTNVM9_IOSFSB_CLKREQ_DIS;
+	CSR_WRITE(sc, WMREG_FEXTNVM9, reg);
+}

Index: src/sys/dev/pci/if_wmreg.h
diff -u src/sys/dev/pci/if_wmreg.h:1.98 src/sys/dev/pci/if_wmreg.h:1.98.6.1
--- src/sys/dev/pci/if_wmreg.h:1.98	Tue Feb 28 09:55:47 2017
+++ src/sys/dev/pci/if_wmreg.h	Tue Aug  1 23:33:18 2017
@@ -1,4 +1,4 @@
-/*	$NetBSD: if_wmreg.h,v 1.98 2017/02/28 09:55:47 knakahara Exp $	*/
+/*	$NetBSD: if_wmreg.h,v 1.98.6.1 2017/08/01 23:33:18 snj Exp $	*/
 
 /*
  * Copyright (c) 2001 Wasabi Systems, Inc.
@@ -589,6 +589,10 @@ struct livengood_tcpip_ctxdesc {
 #define	WMREG_FCAL	0x0028	/* Flow Control Address Low */
 #define	FCAL_CONST	0x00c28001	/* Flow Control MAC addr low */
 
+#define	WMREG_FEXTNVM	0x0028	/* Future Extended NVM register */
+#define	FEXTNVM_SW_CONFIG	__BIT(1)
+#define	FEXTNVM_SW_CONFIG_ICH8M	__BIT(27)
+
 #define	WMREG_FCAH	0x002c	/* Flow Control Address High */
 #define	FCAH_CONST	0x00000100	/* Flow Control MAC addr high */
 
@@ -612,12 +616,12 @@ struct livengood_tcpip_ctxdesc {
 #define	KUMCTRLSTA_OFFSET_M2P_MODES	0x0000001F
 
 /* FIFO Control */
-#define	KUMCTRLSTA_FIFO_CTRL_RX_BYPASS	0x00000008
-#define	KUMCTRLSTA_FIFO_CTRL_TX_BYPASS	0x00000800
+#define	KUMCTRLSTA_FIFO_CTRL_RX_BYPASS	0x0008
+#define	KUMCTRLSTA_FIFO_CTRL_TX_BYPASS	0x0800
 
 /* In-Band Control */
-#define	KUMCTRLSTA_INB_CTRL_LINK_TMOUT_DFLT 0x00000500
-#define	KUMCTRLSTA_INB_CTRL_DIS_PADDING	0x00000010
+#define	KUMCTRLSTA_INB_CTRL_LINK_TMOUT_DFLT 0x0500
+#define	KUMCTRLSTA_INB_CTRL_DIS_PADDING	0x0010
 
 /* Diag */
 #define	KUMCTRLSTA_DIAG_NELPBK	0x1000
@@ -626,8 +630,12 @@ struct livengood_tcpip_ctxdesc {
 #define	KUMCTRLSTA_K1_ENABLE	0x0002
 
 /* Half-Duplex Control */
-#define	KUMCTRLSTA_HD_CTRL_10_100_DEFAULT 0x00000004
-#define	KUMCTRLSTA_HD_CTRL_1000_DEFAULT	0x00000000
+#define	KUMCTRLSTA_HD_CTRL_10_100_DEFAULT 0x0004
+#define	KUMCTRLSTA_HD_CTRL_1000_DEFAULT	0x0000
+
+/* M2P Modes */
+#define	KUMCTRLSTA_OPMODE_MASK	0x000c
+#define	KUMCTRLSTA_OPMODE_INBAND_MDIO 0x0004
 
 #define	WMREG_VET	0x0038	/* VLAN Ethertype */
 #define	WMREG_MDPHYA	0x003C	/* PHY address - RW */
@@ -636,8 +644,15 @@ struct livengood_tcpip_ctxdesc {
 #define FEXTNVM3_PHY_CFG_COUNTER_MASK	__BITS(27, 26)
 #define FEXTNVM3_PHY_CFG_COUNTER_50MS	__BIT(27)
 
-#define	WMREG_RAL_BASE	0x0040	/* Receive Address List */
-#define	WMREG_CORDOVA_RAL_BASE 0x5400
+#define	WMREG_RAL(x)		(0x0040	+ ((x) * 8)) /* Receive Address List */
+#define	WMREG_RAH(x)		(WMREG_RAL(x) + 4)
+#define	WMREG_CORDOVA_RAL(x)	(((x) <= 15) ? (0x5400 + ((x) * 8)) : \
+	    (0x54e0 + (((x) - 16) * 8)))
+#define	WMREG_CORDOVA_RAH(x)	(WMREG_CORDOVA_RAL(x) + 4)
+#define	WMREG_SHRAL(x)		(0x5438 + ((x) * 8))
+#define	WMREG_SHRAH(x)		(WMREG_PCH_LPT_SHRAL(x) + 4)
+#define	WMREG_PCH_LPT_SHRAL(x)	(0x5408 + ((x) * 8))
+#define	WMREG_PCH_LPT_SHRAH(x)	(WMREG_PCH_LPT_SHRAL(x) + 4)
 #define	WMREG_RAL_LO(b, x) ((b) + ((x) << 3))
 #define	WMREG_RAL_HI(b, x) (WMREG_RAL_LO(b, x) + 4)
 	/*
@@ -721,6 +736,13 @@ struct livengood_tcpip_ctxdesc {
 #define IVAR_MISC_TCPTIMER __BITS(0, 7)
 #define IVAR_MISC_OTHER	__BITS(8, 15)
 
+#define	WMREG_SVCR	0x00f0
+#define	SVCR_OFF_EN		__BIT(0)
+#define	SVCR_OFF_MASKINT	__BIT(12)
+
+#define	WMREG_SVT	0x00f4
+#define	SVT_OFF_HWM		__BITS(4, 0)
+
 #define	WMREG_LTRV	0x00f8	/* Latency Tolerance Reporting */
 #define	LTRV_VALUE	__BITS(9, 0)
 #define	LTRV_SCALE	__BITS(12, 10)
@@ -926,6 +948,8 @@ struct livengood_tcpip_ctxdesc {
 #define	WMREG_AIT	0x0458	/* Adaptive IFS Throttle */
 #define	WMREG_VFTA	0x0600
 
+#define	WMREG_LEDCTL	0x0e00	/* LED Control - RW */
+
 #define	WMREG_MDICNFG	0x0e04	/* MDC/MDIO Configuration Register */
 #define MDICNFG_PHY_SHIFT	21
 #define MDICNFG_PHY_MASK	__BITS(25, 21)
@@ -937,6 +961,10 @@ struct livengood_tcpip_ctxdesc {
 #define	WM_VLAN_TABSIZE	128
 
 #define	WMREG_PHPM	0x0e14	/* PHY Power Management */
+#define	PHPM_SPD_EN		__BIT(0)	/* Smart Power Down */
+#define	PHPM_D0A_LPLU		__BIT(1)	/* D0 Low Power Link Up */
+#define	PHPM_NOND0A_LPLU	__BIT(2)	/* D0 Low Power Link Up */
+#define	PHPM_NOND0A_GBE_DIS	__BIT(3)	/* D0 Low Power Link Up */
 #define	PHPM_GO_LINK_D		__BIT(5)	/* Go Link Disconnect */
 
 #define WMREG_EEER	0x0e30	/* Energy Efficiency Ethernet "EEE" */
@@ -954,15 +982,15 @@ struct livengood_tcpip_ctxdesc {
 
 #define WMREG_EXTCNFCTR	0x0f00  /* Extended Configuration Control */
 #define EXTCNFCTR_PCIE_WRITE_ENABLE	0x00000001
-#define EXTCNFCTR_PHY_WRITE_ENABLE	0x00000002
-#define EXTCNFCTR_D_UD_ENABLE		0x00000004
-#define EXTCNFCTR_D_UD_LATENCY		0x00000008
-#define EXTCNFCTR_D_UD_OWNER		0x00000010
+#define EXTCNFCTR_OEM_WRITE_ENABLE	0x00000008
 #define EXTCNFCTR_MDIO_SW_OWNERSHIP	0x00000020
 #define EXTCNFCTR_MDIO_HW_OWNERSHIP	0x00000040
 #define EXTCNFCTR_GATE_PHY_CFG		0x00000080
 #define EXTCNFCTR_EXT_CNF_POINTER	0x0FFF0000
 
+#define WMREG_EXTCNFSIZE 0x0f08  /* Extended Configuration Size */
+#define EXTCNFSIZE_LENGTH	__BITS(23, 16)
+
 #define	WMREG_PHY_CTRL	0x0f10	/* PHY control */
 #define	PHY_CTRL_SPD_EN		(1 << 0)
 #define	PHY_CTRL_D0A_LPLU	(1 << 1)
@@ -1276,7 +1304,7 @@ struct livengood_tcpip_ctxdesc {
 #define	MNG_ICH_IAMT_MODE	0x2	/* PT mode? */
 #define	MNG_IAMT_MODE		0x3
 #define FWSM_RSPCIPHY		__BIT(6)  /* Reset PHY on PCI reset */
-#define FWSM_WLOCK_MAC		__BITS(7, 9)  /* Reset PHY on PCI reset */
+#define FWSM_WLOCK_MAC		__BITS(7, 9)
 #define FWSM_ULP_CFG_DONE	__BIT(10)
 #define FWSM_FW_VALID		__BIT(15) /* FW established a valid mode */
 
@@ -1295,6 +1323,8 @@ struct livengood_tcpip_ctxdesc {
 
 #define WMREG_GCR2	0x5b64	/* 3GPIO Control Register 2 */
 #define WMREG_FEXTNVM9	0x5bb4	/* Future Extended NVM 9 */
+#define FEXTNVM9_IOSFSB_CLKGATE_DIS __BIT(11)
+#define FEXTNVM9_IOSFSB_CLKREQ_DIS __BIT(12)
 #define WMREG_FEXTNVM11	0x5bbc	/* Future Extended NVM 11 */
 #define FEXTNVM11_DIS_MULRFIX	__BIT(13)	/* Disable MULR fix */
 

Index: src/sys/dev/pci/if_wmvar.h
diff -u src/sys/dev/pci/if_wmvar.h:1.33 src/sys/dev/pci/if_wmvar.h:1.33.6.1
--- src/sys/dev/pci/if_wmvar.h:1.33	Wed Feb  1 08:56:41 2017
+++ src/sys/dev/pci/if_wmvar.h	Tue Aug  1 23:33:18 2017
@@ -1,4 +1,4 @@
-/*	$NetBSD: if_wmvar.h,v 1.33 2017/02/01 08:56:41 msaitoh Exp $	*/
+/*	$NetBSD: if_wmvar.h,v 1.33.6.1 2017/08/01 23:33:18 snj Exp $	*/
 
 /*
  * Copyright (c) 2001, 2002, 2003, 2004 Wasabi Systems, Inc.
@@ -74,10 +74,7 @@
 /* sc_flags */
 #define	WM_F_HAS_MII		0x00000001 /* has MII */
 #define	WM_F_LOCK_EECD		0x00000002 /* Lock using with EECD register */
-#define	WM_F_LOCK_SWSM		0x00000004 /* Lock using with SWSM register */
-#define WM_F_LOCK_SWFW		0x00000008 /* Lock using with SWFW register */
-#define WM_F_LOCK_EXTCNF	0x00000010 /* Lock using with EXTCNF reg. */
-#define	WM_F_EEPROM_EERDEEWR	0x00000020 /* EEPROM access via EERD/EEWR */
+#define	WM_F_EEPROM_INVM	0x00000020 /* NVM is iNVM */
 #define	WM_F_EEPROM_SPI		0x00000040 /* EEPROM is SPI */
 #define	WM_F_EEPROM_FLASH	0x00000080 /* EEPROM is FLASH */
 #define	WM_F_EEPROM_FLASH_HW	0x00000100 /* EEPROM is FLASH */
@@ -96,9 +93,19 @@
 #define WM_F_WOL		0x00200000
 #define WM_F_EEE		0x00400000 /* Energy Efficiency Ethernet */
 #define WM_F_ATTACHED		0x00800000 /* attach() finished successfully */
-#define	WM_F_EEPROM_INVM	0x01000000 /* NVM is iNVM */
+#define WM_F_80003_MDIC_WA	0x01000000 /* 80003 MDIC workaround */
 #define	WM_F_PCS_DIS_AUTONEGO	0x02000000 /* PCS Disable Autonego */
 #define	WM_F_PLL_WA_I210	0x04000000 /* I21[01] PLL workaround */
+#define	WM_F_WA_I210_CLSEM	0x08000000 /* I21[01] Semaphore workaround */
+
+#define WM_FLAGS "\20" \
+	"\1" "HAS_MII"	"\2" "LOCK_EECD" "\3" "_B02"	"\4" "_B03"	\
+	"\5" "_B04"	"\6" "INVM"	"\7" "SPI"	"\10" "FLASH"	\
+	"\11" "FLASH_HW" "\12" "INVALID" "\13" "IOH_VALID" "\14" "BUS64" \
+	"\15" "PCIX"	"\16" "CSA"	"\17" "PCIE"	"\20" "SGMII"	\
+	"\21" "NEWQUEUE" "\22" "ASF_FIRM" "\23" "ARC_SUBSYS" "\24" "AMT" \
+	"\25" "MANAGE"	"\26" "WOL"	"\27" "EEE"	"\30" "ATTACHED" \
+	"\31" "_B24"	"\32" "PCS_DIS_AUTONEGO" "\33" "PLLWA"
 
 /*
  * Variations of Intel gigabit Ethernet controller:

Reply via email to