summaryrefslogtreecommitdiff
path: root/sys/dev
diff options
context:
space:
mode:
authorDariusz Swiderski <dms@cvs.openbsd.org>2009-11-25 13:28:14 +0000
committerDariusz Swiderski <dms@cvs.openbsd.org>2009-11-25 13:28:14 +0000
commiteb5a74f8c9ec0ba900a6528150e0851226181793 (patch)
tree9fdace4d62b281ca9404b5967ad4097b119a4de9 /sys/dev
parent0a70633931ddb015bd0e7ad3b5e35439ff387229 (diff)
Add support for em(4) interfaces found on intel EP80579 SoC. The MAC part is
basicly 82545, but the PHY's are separated form the chip and they are accessed through a special PCI device called GCU which has the MDIO interface. Since there is no direct relationship between MAC and PHY, so for the moment they are assigned to each other the way its done on Axiomtek NA-200, that was danted to us by them. This also adds a device driver for the GCU. tested by me on Axiomtek board reviewed by claudio@, kettenis@, deraadt@ 'commit that as is' deraadt@
Diffstat (limited to 'sys/dev')
-rw-r--r--sys/dev/pci/files.pci9
-rw-r--r--sys/dev/pci/gcu.c78
-rw-r--r--sys/dev/pci/gcu.h27
-rw-r--r--sys/dev/pci/gcu_reg.h71
-rw-r--r--sys/dev/pci/if_em.c106
-rw-r--r--sys/dev/pci/if_em.h3
-rw-r--r--sys/dev/pci/if_em_hw.c185
-rw-r--r--sys/dev/pci/if_em_hw.h35
-rw-r--r--sys/dev/pci/if_em_soc.c136
-rw-r--r--sys/dev/pci/if_em_soc.h28
10 files changed, 628 insertions, 50 deletions
diff --git a/sys/dev/pci/files.pci b/sys/dev/pci/files.pci
index 973c8a6a495..2648aa63036 100644
--- a/sys/dev/pci/files.pci
+++ b/sys/dev/pci/files.pci
@@ -1,4 +1,4 @@
-# $OpenBSD: files.pci,v 1.266 2009/11/14 16:55:11 damien Exp $
+# $OpenBSD: files.pci,v 1.267 2009/11/25 13:28:13 dms Exp $
# $NetBSD: files.pci,v 1.20 1996/09/24 17:47:15 christos Exp $
#
# Config file and device description for machine-independent PCI code.
@@ -351,6 +351,7 @@ device em: ether, ifnet, ifmedia
attach em at pci
file dev/pci/if_em.c em
file dev/pci/if_em_hw.c em
+file dev/pci/if_em_soc.c em
# Intel Pro/10GbE
device ixgb: ether, ifnet, ifmedia
@@ -788,3 +789,9 @@ file dev/pci/kate.c kate
device km
attach km at pci
file dev/pci/km.c km
+
+# Intel SOC GCU
+device gcu
+attach gcu at pci
+file dev/pci/gcu.c gcu
+
diff --git a/sys/dev/pci/gcu.c b/sys/dev/pci/gcu.c
new file mode 100644
index 00000000000..a0df00effa2
--- /dev/null
+++ b/sys/dev/pci/gcu.c
@@ -0,0 +1,78 @@
+/* $OpenBSD: gcu.c,v 1.1 2009/11/25 13:28:13 dms Exp $ */
+
+/*
+ * Copyright (c) 2009 Dariusz Swiderski <sfires@sfires.net>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+/*
+ * Driver for a GCU device that apears on embeded intel systems, like 80579
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/device.h>
+
+#include <dev/pci/pcireg.h>
+#include <dev/pci/pcivar.h>
+
+#include <dev/pci/pcidevs.h>
+#include <dev/pci/gcu.h>
+
+int gcu_probe(struct device *, void *, void *);
+void gcu_attach(struct device *, struct device *, void *);
+int gcu_detach(struct device *, int);
+
+const struct pci_matchid gcu_devices[] = {
+ { PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_EP80579_GCU }
+};
+
+struct cfdriver gcu_cd = {
+ NULL, "gcu", DV_IFNET
+};
+
+struct cfattach gcu_ca = {
+ sizeof(struct gcu_softc), gcu_probe, gcu_attach
+};
+
+int
+gcu_probe(struct device *parent, void *match, void *aux)
+{
+ return (pci_matchbyid((struct pci_attach_args *)aux, gcu_devices,
+ sizeof(gcu_devices)/sizeof(gcu_devices[0])));
+}
+
+void
+gcu_attach(struct device *parent, struct device *self, void *aux)
+{
+ struct gcu_softc *sc = (struct gcu_softc *)self;
+ struct pci_attach_args *pa = aux;
+ int val;
+
+ val = pci_conf_read(pa->pa_pc, pa->pa_tag, 0x10);
+ if (PCI_MAPREG_TYPE(val) != PCI_MAPREG_TYPE_MEM) {
+ printf(": mmba is not mem space\n");
+ return;
+ }
+
+ if (pci_mapreg_map(pa, 0x10, PCI_MAPREG_MEM_TYPE(val), 0, &sc->tag,
+ &sc->handle, &sc->addr, &sc->size, 0)) {
+ printf(": cannot find mem space\n");
+ return;
+ }
+
+ mtx_init(&sc->mdio_mtx, IPL_NET);
+
+ printf("\n");
+}
diff --git a/sys/dev/pci/gcu.h b/sys/dev/pci/gcu.h
new file mode 100644
index 00000000000..9611e078e00
--- /dev/null
+++ b/sys/dev/pci/gcu.h
@@ -0,0 +1,27 @@
+/* $OpenBSD: gcu.h,v 1.1 2009/11/25 13:28:13 dms Exp $ */
+
+/*
+ * Copyright (c) 2009 Dariusz Swiderski <sfires@sfires.net>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+struct gcu_softc {
+ struct device sc_dev;
+
+ bus_addr_t addr;
+ bus_size_t size;
+ bus_space_tag_t tag;
+ bus_space_handle_t handle;
+ struct mutex mdio_mtx;
+};
diff --git a/sys/dev/pci/gcu_reg.h b/sys/dev/pci/gcu_reg.h
new file mode 100644
index 00000000000..970f84b3b62
--- /dev/null
+++ b/sys/dev/pci/gcu_reg.h
@@ -0,0 +1,71 @@
+/* $OpenBSD: gcu_reg.h,v 1.1 2009/11/25 13:28:13 dms Exp $ */
+
+/*
+ * Copyright(c) 2007,2008,2009 Intel Corporation. All rights reserved.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name of Intel Corporation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * version: Embedded.B.1.0.3-146
+ */
+
+/*
+ * gcu_reg.h
+ * Macros and constants related to the registers available on the GCU
+ */
+
+#ifndef GCU_REG_H
+#define GCU_REG_H
+
+/* Register Offsets within memory map register space */
+#define MDIO_STATUS_REG 0x00000010UL
+#define MDIO_COMMAND_REG 0x00000014UL
+
+/* MDIO_STATUS_REG fields */
+#define MDIO_STATUS_STATUS_MASK 0x80000000UL /* bit 31 = 1 on error */
+#define MDIO_STATUS_READ_DATA_MASK 0x0000FFFFUL
+
+/* MDIO_COMMAND_REG fields */
+#define MDIO_COMMAND_GO_MASK 0x80000000UL /* bit 31 = 1 during read or
+ * write, 0 on completion */
+#define MDIO_COMMAND_OPER_MASK 0x04000000UL /* bit = 1 is a write */
+#define MDIO_COMMAND_PHY_ADDR_MASK 0x03E00000UL
+#define MDIO_COMMAND_PHY_REG_MASK 0x001F0000UL
+#define MDIO_COMMAND_WRITE_DATA_MASK 0x0000FFFFUL
+
+#define MDIO_COMMAND_GO_OFFSET 31
+#define MDIO_COMMAND_OPER_OFFSET 26
+#define MDIO_COMMAND_PHY_ADDR_OFFSET 21
+#define MDIO_COMMAND_PHY_REG_OFFSET 16
+#define MDIO_COMMAND_WRITE_DATA_OFFSET 0
+
+#define MDIO_COMMAND_PHY_ADDR_MAX 2 /* total phys supported by GCU */
+#define MDIO_COMMAND_PHY_REG_MAX 31 /* total registers available on
+ * the M88 Phy used on truxton */
+
+#endif /* ifndef GCU_REG_H */
+
diff --git a/sys/dev/pci/if_em.c b/sys/dev/pci/if_em.c
index 4fe22eee806..8548434d964 100644
--- a/sys/dev/pci/if_em.c
+++ b/sys/dev/pci/if_em.c
@@ -31,10 +31,11 @@ POSSIBILITY OF SUCH DAMAGE.
***************************************************************************/
-/* $OpenBSD: if_em.c,v 1.228 2009/10/13 23:55:20 deraadt Exp $ */
+/* $OpenBSD: if_em.c,v 1.229 2009/11/25 13:28:13 dms Exp $ */
/* $FreeBSD: if_em.c,v 1.46 2004/09/29 18:28:28 mlaier Exp $ */
#include <dev/pci/if_em.h>
+#include <dev/pci/if_em_soc.h>
#ifdef EM_DEBUG
/*********************************************************************
@@ -145,7 +146,10 @@ const struct pci_matchid em_devices[] = {
{ PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_ICH10_D_BM_LM },
{ PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_ICH10_R_BM_LF },
{ PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_ICH10_R_BM_LM },
- { PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_ICH10_R_BM_V }
+ { PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_ICH10_R_BM_V },
+ { PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_EP80579_LAN_1 },
+ { PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_EP80579_LAN_2 },
+ { PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_EP80579_LAN_3 }
};
/*********************************************************************
@@ -153,6 +157,7 @@ const struct pci_matchid em_devices[] = {
*********************************************************************/
int em_probe(struct device *, void *, void *);
void em_attach(struct device *, struct device *, void *);
+void em_defer_attach(struct device*);
int em_detach(struct device *, int);
int em_intr(void *);
void em_power(int, void *);
@@ -248,6 +253,45 @@ em_probe(struct device *parent, void *match, void *aux)
sizeof(em_devices)/sizeof(em_devices[0])));
}
+void
+em_defer_attach(struct device *self)
+{
+ struct em_softc *sc = (struct em_softc *)self;
+ struct pci_attach_args *pa = &sc->osdep.em_pa;
+ pci_chipset_tag_t pc = pa->pa_pc;
+ void *gcu;
+
+ if ((gcu = em_lookup_gcu(self)) == 0) {
+ printf("%s: No GCU found, defered attachment failed\n",
+ sc->sc_dv.dv_xname);
+
+ if (sc->sc_intrhand)
+ pci_intr_disestablish(pc, sc->sc_intrhand);
+ sc->sc_intrhand = 0;
+
+ if (sc->sc_powerhook != NULL)
+ powerhook_disestablish(sc->sc_powerhook);
+
+ em_stop(sc, 1);
+
+ em_free_pci_resources(sc);
+ em_dma_free(sc, &sc->rxdma);
+ em_dma_free(sc, &sc->txdma);
+
+ return;
+ }
+
+ sc->hw.gcu = gcu;
+
+ em_attach_miibus(self);
+
+ em_setup_interface(sc);
+
+ em_update_link_status(sc);
+
+ em_setup_link(&sc->hw);
+}
+
/*********************************************************************
* Device initialization routine
*
@@ -262,8 +306,9 @@ em_attach(struct device *parent, struct device *self, void *aux)
{
struct pci_attach_args *pa = aux;
struct em_softc *sc;
- int tsize, rsize;
-
+ int tsize, rsize;
+ int defer = 0;
+
INIT_DEBUGOUT("em_attach: begin");
sc = (struct em_softc *)self;
@@ -393,10 +438,14 @@ em_attach(struct device *parent, struct device *self, void *aux)
sc->rx_desc_base = (struct em_rx_desc *) sc->rxdma.dma_vaddr;
/* Initialize the hardware */
- if (em_hardware_init(sc)) {
- printf("%s: Unable to initialize the hardware\n",
- sc->sc_dv.dv_xname);
- goto err_hw_init;
+ if ((defer = em_hardware_init(sc))) {
+ if (defer == EAGAIN)
+ config_defer(self, em_defer_attach);
+ else {
+ printf("%s: Unable to initialize the hardware\n",
+ sc->sc_dv.dv_xname);
+ goto err_hw_init;
+ }
}
/* Copy the permanent MAC address out of the EEPROM */
@@ -412,16 +461,18 @@ em_attach(struct device *parent, struct device *self, void *aux)
}
bcopy(sc->hw.mac_addr, sc->interface_data.ac_enaddr,
- ETHER_ADDR_LEN);
+ ETHER_ADDR_LEN);
/* Setup OS specific network interface */
- em_setup_interface(sc);
+ if (!defer)
+ em_setup_interface(sc);
/* Initialize statistics */
em_clear_hw_cntrs(&sc->hw);
em_update_stats_counters(sc);
sc->hw.get_link_status = 1;
- em_update_link_status(sc);
+ if (!defer)
+ em_update_link_status(sc);
printf(", address %s\n", ether_sprintf(sc->interface_data.ac_enaddr));
@@ -437,6 +488,9 @@ em_attach(struct device *parent, struct device *self, void *aux)
sc->pcix_82544 = TRUE;
else
sc->pcix_82544 = FALSE;
+
+ sc->hw.icp_xxxx_is_link_up = FALSE;
+
INIT_DEBUGOUT("em_attach: end");
sc->sc_powerhook = powerhook_establish(em_power, sc);
return;
@@ -628,7 +682,6 @@ em_watchdog(struct ifnet *ifp)
ifp->if_timer = EM_TX_TIMEOUT;
return;
}
-
printf("%s: watchdog timeout -- resetting\n", sc->sc_dv.dv_xname);
em_init(sc);
@@ -1552,6 +1605,7 @@ em_allocate_pci_resources(struct em_softc *sc)
return (ENXIO);
}
+ sc->osdep.dev = (struct device *)sc;
sc->hw.back = &sc->osdep;
intrstr = pci_intr_string(pc, ih);
@@ -1566,6 +1620,27 @@ em_allocate_pci_resources(struct em_softc *sc)
}
printf(": %s", intrstr);
+ /*
+ * the ICP_xxxx device has multiple, duplicate register sets for
+ * use when it is being used as a network processor. Disable those
+ * registers here, as they are not necessary in this context and
+ * can confuse the system
+ */
+ if(sc->hw.mac_type == em_icp_xxxx) {
+ uint8_t offset;
+ pcireg_t val;
+
+ if (!pci_get_capability(sc->osdep.em_pa.pa_pc,
+ sc->osdep.em_pa.pa_tag, PCI_CAP_ID_ST, (int*) &offset,
+ &val)) {
+ return (0);
+ }
+ offset += PCI_ST_SMIA_OFFSET;
+ pci_conf_write(sc->osdep.em_pa.pa_pc, sc->osdep.em_pa.pa_tag,
+ offset, 0x06);
+ E1000_WRITE_REG(&sc->hw, IMC1, ~0x0);
+ E1000_WRITE_REG(&sc->hw, IMC2, ~0x0);
+ }
return (0);
}
@@ -1606,6 +1681,7 @@ em_free_pci_resources(struct em_softc *sc)
int
em_hardware_init(struct em_softc *sc)
{
+ uint32_t ret_val;
u_int16_t rx_buffer_size;
INIT_DEBUGOUT("em_hardware_init: begin");
@@ -1674,7 +1750,11 @@ em_hardware_init(struct em_softc *sc)
sc->hw.fc_send_xon = TRUE;
sc->hw.fc = E1000_FC_FULL;
- if (em_init_hw(&sc->hw) < 0) {
+ if ((ret_val = em_init_hw(&sc->hw)) != 0) {
+ if (ret_val == E1000_DEFER_INIT) {
+ INIT_DEBUGOUT("\nHardware Initialization Deferred ");
+ return (EAGAIN);
+ }
printf("%s: Hardware Initialization Failed",
sc->sc_dv.dv_xname);
return (EIO);
diff --git a/sys/dev/pci/if_em.h b/sys/dev/pci/if_em.h
index 2173d461c5d..4c2355e6a21 100644
--- a/sys/dev/pci/if_em.h
+++ b/sys/dev/pci/if_em.h
@@ -32,7 +32,7 @@ POSSIBILITY OF SUCH DAMAGE.
***************************************************************************/
/* $FreeBSD: if_em.h,v 1.26 2004/09/01 23:22:41 pdeuskar Exp $ */
-/* $OpenBSD: if_em.h,v 1.45 2009/08/10 19:41:05 deraadt Exp $ */
+/* $OpenBSD: if_em.h,v 1.46 2009/11/25 13:28:13 dms Exp $ */
#ifndef _EM_H_DEFINED_
#define _EM_H_DEFINED_
@@ -417,7 +417,6 @@ struct em_softc {
/* For 82544 PCI-X Workaround */
boolean_t pcix_82544;
-
struct em_hw_stats stats;
};
diff --git a/sys/dev/pci/if_em_hw.c b/sys/dev/pci/if_em_hw.c
index 5c80164c0e1..43c5e9b7ee8 100644
--- a/sys/dev/pci/if_em_hw.c
+++ b/sys/dev/pci/if_em_hw.c
@@ -31,7 +31,7 @@
*******************************************************************************/
-/* $OpenBSD: if_em_hw.c,v 1.41 2009/10/11 23:54:49 dms Exp $ */
+/* $OpenBSD: if_em_hw.c,v 1.42 2009/11/25 13:28:13 dms Exp $ */
/* if_em_hw.c
* Shared functions for accessing and configuring the MAC
@@ -70,6 +70,7 @@ __FBSDID("$FreeBSD: if_em_hw.c,v 1.16 2005/05/26 23:32:02 tackerman Exp $");
#include <dev/pci/pcidevs.h>
#include <dev/pci/if_em_hw.h>
+#include <dev/pci/if_em_soc.h>
#define STATIC
@@ -221,6 +222,9 @@ em_set_phy_type(struct em_hw *hw)
case IFE_C_E_PHY_ID:
hw->phy_type = em_phy_ife;
break;
+ case M88E1141_E_PHY_ID:
+ hw->phy_type = em_phy_oem;
+ break;
case BME1000_E_PHY_ID:
if (hw->phy_revision == 1) {
hw->phy_type = em_phy_bm;
@@ -494,6 +498,18 @@ em_set_mac_type(struct em_hw *hw)
case E1000_DEV_ID_ICH10_D_BM_LM:
hw->mac_type = em_ich10lan;
break;
+ case E1000_DEV_ID_EP80579_LAN_1:
+ hw->mac_type = em_icp_xxxx;
+ hw->icp_xxxx_port_num = 0;
+ break;
+ case E1000_DEV_ID_EP80579_LAN_2:
+ hw->mac_type = em_icp_xxxx;
+ hw->icp_xxxx_port_num = 1;
+ break;
+ case E1000_DEV_ID_EP80579_LAN_3:
+ hw->mac_type = em_icp_xxxx;
+ hw->icp_xxxx_port_num = 2;
+ break;
default:
/* Should never have loaded on this device */
return -E1000_ERR_MAC_TYPE;
@@ -555,6 +571,11 @@ em_set_media_type(struct em_hw *hw)
case E1000_DEV_ID_80003ES2LAN_SERDES_DPT:
hw->media_type = em_media_type_internal_serdes;
break;
+ case E1000_DEV_ID_EP80579_LAN_1:
+ case E1000_DEV_ID_EP80579_LAN_2:
+ case E1000_DEV_ID_EP80579_LAN_3:
+ hw->media_type = em_media_type_oem;
+ break;
default:
switch (hw->mac_type) {
case em_82542_rev2_0:
@@ -756,16 +777,21 @@ em_reset_hw(struct em_hw *hw)
E1000_WRITE_FLUSH(hw);
}
/* FALLTHROUGH */
- default:
+
/* Auto read done will delay 5ms or poll based on mac type */
ret_val = em_get_auto_rd_done(hw);
if (ret_val)
return ret_val;
break;
+ default:
+ /* Wait for EEPROM reload (it happens automatically) */
+ msec_delay(5);
+ break;
}
/* Disable HW ARPs on ASF enabled adapters */
- if (hw->mac_type >= em_82540 && hw->mac_type <= em_82547_rev_2) {
+ if (hw->mac_type >= em_82540 && hw->mac_type <= em_82547_rev_2 &&
+ hw->mac_type != em_icp_xxxx) {
manc = E1000_READ_REG(hw, MANC);
manc &= ~(E1000_MANC_ARP_EN);
E1000_WRITE_REG(hw, MANC, manc);
@@ -1193,9 +1219,15 @@ em_setup_link(struct em_hw *hw)
uint32_t ctrl_ext;
int32_t ret_val;
uint16_t eeprom_data;
+ uint16_t eeprom_control2_reg_offset;
DEBUGFUNC("em_setup_link");
+ eeprom_control2_reg_offset =
+ (hw->mac_type != em_icp_xxxx)
+ ? EEPROM_INIT_CONTROL2_REG
+ : EEPROM_INIT_CONTROL3_ICP_xxxx(hw->icp_xxxx_port_num);
+
/* In the case of the phy reset being blocked, we already have a link.
* We do not have to set it up again. */
if (em_check_phy_reset_block(hw))
@@ -1219,7 +1251,7 @@ em_setup_link(struct em_hw *hw)
hw->fc = E1000_FC_FULL;
break;
default:
- ret_val = em_read_eeprom(hw, EEPROM_INIT_CONTROL2_REG,
+ ret_val = em_read_eeprom(hw, eeprom_control2_reg_offset,
1, &eeprom_data);
if (ret_val) {
DEBUGOUT("EEPROM Read Error\n");
@@ -1273,14 +1305,23 @@ em_setup_link(struct em_hw *hw)
ret_val = em_detect_gig_phy(hw);
if (ret_val) {
DEBUGOUT("Error, did not detect valid phy.\n");
- return ret_val;
+ if (hw->mac_type == em_icp_xxxx)
+ return E1000_DEFER_INIT;
+ else
+ return ret_val;
}
DEBUGOUT1("Phy ID = %x \n", hw->phy_id);
/* Call the necessary subroutine to configure the link. */
- ret_val = (hw->media_type == em_media_type_copper) ?
- em_setup_copper_link(hw) :
- em_setup_fiber_serdes_link(hw);
+ switch (hw->media_type) {
+ case em_media_type_copper:
+ case em_media_type_oem:
+ ret_val = em_setup_copper_link(hw);
+ break;
+ default:
+ ret_val = em_setup_fiber_serdes_link(hw);
+ break;
+ }
/* Initialize the flow control address, type, and PAUSE timer
* registers to their default values. This is done even if flow
@@ -1849,10 +1890,23 @@ em_copper_link_mgp_setup(struct em_hw *hw)
if (ret_val)
return ret_val;
+ if (hw->phy_id == M88E1141_E_PHY_ID) {
+ phy_data |= 0x00000008;
+ ret_val = em_write_phy_reg(hw, M88E1000_PHY_SPEC_CTRL, phy_data);
+ if (ret_val)
+ return ret_val;
+
+ ret_val = em_read_phy_reg(hw, M88E1000_PHY_SPEC_CTRL, &phy_data);
+ if (ret_val)
+ return ret_val;
+
+ phy_data &= ~M88E1000_PSCR_ASSERT_CRS_ON_TX;
+
+ }
/* For BM PHY this bit is downshift enable */
- if (hw->phy_type != em_phy_bm)
+ else if (hw->phy_type != em_phy_bm)
phy_data |= M88E1000_PSCR_ASSERT_CRS_ON_TX;
-
+
/* Options:
* MDI/MDI-X = 0 (default)
* 0 - Auto for all speeds
@@ -1896,9 +1950,11 @@ em_copper_link_mgp_setup(struct em_hw *hw)
if (ret_val)
return ret_val;
- if ((hw->phy_type == em_phy_m88) &&
+ if (((hw->phy_type == em_phy_m88) &&
(hw->phy_revision < M88E1011_I_REV_4) &&
- (hw->phy_id != BME1000_E_PHY_ID)) {
+ (hw->phy_id != BME1000_E_PHY_ID)) ||
+ (hw->phy_type == em_phy_oem)) {
+
/* Force TX_CLK in the Extended PHY Specific Control Register
* to 25MHz clock.
*/
@@ -1906,6 +1962,11 @@ em_copper_link_mgp_setup(struct em_hw *hw)
if (ret_val)
return ret_val;
+ if (hw->phy_type == em_phy_oem) {
+ phy_data |= M88E1000_EPSCR_TX_TIME_CTRL;
+ phy_data |= M88E1000_EPSCR_RX_TIME_CTRL;
+ }
+
phy_data |= M88E1000_EPSCR_TX_CLK_25;
if ((hw->phy_revision == E1000_REVISION_2) &&
@@ -1962,7 +2023,7 @@ em_copper_link_mgp_setup(struct em_hw *hw)
*
* hw - Struct containing variables accessed by shared code
*********************************************************************/
-static int32_t
+int32_t
em_copper_link_autoneg(struct em_hw *hw)
{
int32_t ret_val;
@@ -2033,13 +2094,14 @@ em_copper_link_autoneg(struct em_hw *hw)
*
* hw - Struct containing variables accessed by shared code
******************************************************************************/
-static int32_t
+int32_t
em_copper_link_postconfig(struct em_hw *hw)
{
int32_t ret_val;
DEBUGFUNC("em_copper_link_postconfig");
- if (hw->mac_type >= em_82544) {
+ if (hw->mac_type >= em_82544 &&
+ hw->mac_type != em_icp_xxxx) {
em_config_collision_dist(hw);
} else {
ret_val = em_config_mac_to_phy(hw);
@@ -2129,7 +2191,8 @@ em_setup_copper_link(struct em_hw *hw)
if (ret_val)
return ret_val;
} else if (hw->phy_type == em_phy_m88 ||
- hw->phy_type == em_phy_bm) {
+ hw->phy_type == em_phy_bm ||
+ hw->phy_type == em_phy_oem) {
ret_val = em_copper_link_mgp_setup(hw);
if (ret_val)
return ret_val;
@@ -2167,6 +2230,8 @@ em_setup_copper_link(struct em_hw *hw)
if (ret_val)
return ret_val;
+ hw->icp_xxxx_is_link_up = (phy_data & MII_SR_LINK_STATUS) != 0;
+
if (phy_data & MII_SR_LINK_STATUS) {
/* Config the MAC and PHY after link is up */
ret_val = em_copper_link_postconfig(hw);
@@ -2487,7 +2552,8 @@ em_phy_force_speed_duplex(struct em_hw *hw)
if ((hw->phy_type == em_phy_m88) ||
(hw->phy_type == em_phy_gg82563) ||
- (hw->phy_type == em_phy_bm)) {
+ (hw->phy_type == em_phy_bm) ||
+ (hw->phy_type == em_phy_oem)) {
ret_val = em_read_phy_reg(hw, M88E1000_PHY_SPEC_CTRL, &phy_data);
if (ret_val)
return ret_val;
@@ -2504,8 +2570,11 @@ em_phy_force_speed_duplex(struct em_hw *hw)
/* Need to reset the PHY or these changes will be ignored */
mii_ctrl_reg |= MII_CR_RESET;
+
+ }
+
/* Disable MDI-X support for 10/100 */
- } else if (hw->phy_type == em_phy_ife) {
+ else if (hw->phy_type == em_phy_ife) {
ret_val = em_read_phy_reg(hw, IFE_PHY_MDIX_CONTROL, &phy_data);
if (ret_val)
return ret_val;
@@ -2578,6 +2647,7 @@ em_phy_force_speed_duplex(struct em_hw *hw)
return ret_val;
}
}
+
/* This loop will early-out if the link condition has been met. */
for (i = PHY_FORCE_TIME; i > 0; i--) {
if (mii_status_reg & MII_SR_LINK_STATUS) break;
@@ -2596,7 +2666,8 @@ em_phy_force_speed_duplex(struct em_hw *hw)
}
if (hw->phy_type == em_phy_m88 ||
- hw->phy_type == em_phy_bm) {
+ hw->phy_type == em_phy_bm ||
+ hw->phy_type == em_phy_oem) {
/* Because we reset the PHY above, we need to re-force TX_CLK in the
* Extended PHY Specific Control Register to 25MHz clock. This value
* defaults back to a 2.5MHz clock when the PHY is reset.
@@ -2617,7 +2688,11 @@ em_phy_force_speed_duplex(struct em_hw *hw)
if (ret_val)
return ret_val;
- phy_data |= M88E1000_PSCR_ASSERT_CRS_ON_TX;
+ if ( hw->phy_id == M88E1141_E_PHY_ID)
+ phy_data &= ~M88E1000_PSCR_ASSERT_CRS_ON_TX;
+ else
+ phy_data |= M88E1000_PSCR_ASSERT_CRS_ON_TX;
+
ret_val = em_write_phy_reg(hw, M88E1000_PHY_SPEC_CTRL, phy_data);
if (ret_val)
return ret_val;
@@ -2703,7 +2778,8 @@ em_config_mac_to_phy(struct em_hw *hw)
/* 82544 or newer MAC, Auto Speed Detection takes care of
* MAC speed/duplex configuration.*/
- if (hw->mac_type >= em_82544)
+ if (hw->mac_type >= em_82544
+ && hw->mac_type != em_icp_xxxx)
return E1000_SUCCESS;
/* Read the Device Control Register and set the bits to Force Speed
@@ -2837,7 +2913,9 @@ em_config_fc_after_link_up(struct em_hw *hw)
if (((hw->media_type == em_media_type_fiber) && (hw->autoneg_failed)) ||
((hw->media_type == em_media_type_internal_serdes) &&
(hw->autoneg_failed)) ||
- ((hw->media_type == em_media_type_copper) && (!hw->autoneg))) {
+ ((hw->media_type == em_media_type_copper) && (!hw->autoneg)) ||
+ ((hw->media_type == em_media_type_oem) && (!hw->autoneg))
+ ) {
ret_val = em_force_mac_fc(hw);
if (ret_val) {
DEBUGOUT("Error forcing flow control settings\n");
@@ -2850,7 +2928,9 @@ em_config_fc_after_link_up(struct em_hw *hw)
* has completed, and if so, how the PHY and link partner has
* flow control configured.
*/
- if ((hw->media_type == em_media_type_copper) && hw->autoneg) {
+ if ((hw->media_type == em_media_type_copper ||
+ (hw->media_type == em_media_type_oem)) &&
+ hw->autoneg) {
/* Read the MII Status Register and check to see if AutoNeg
* has completed. We read this twice because this reg has
* some "sticky" (latched) bits.
@@ -3061,7 +3141,9 @@ em_check_for_link(struct em_hw *hw)
* receive a Link Status Change interrupt or we have Rx Sequence
* Errors.
*/
- if ((hw->media_type == em_media_type_copper) && hw->get_link_status) {
+ if ((hw->media_type == em_media_type_copper ||
+ (hw->media_type == em_media_type_oem)) &&
+ hw->get_link_status) {
/* First we want to see if the MII Status Register reports
* link. If so, then we want to get the current speed/duplex
* of the PHY.
@@ -3074,6 +3156,8 @@ em_check_for_link(struct em_hw *hw)
if (ret_val)
return ret_val;
+ hw->icp_xxxx_is_link_up = (phy_data & MII_SR_LINK_STATUS) != 0;
+
if (phy_data & MII_SR_LINK_STATUS) {
hw->get_link_status = FALSE;
/* Check if there was DownShift, must be checked immediately after
@@ -3121,7 +3205,7 @@ em_check_for_link(struct em_hw *hw)
* speed/duplex on the MAC to the current PHY speed/duplex
* settings.
*/
- if (hw->mac_type >= em_82544)
+ if (hw->mac_type >= em_82544 && hw->mac_type != em_icp_xxxx)
em_config_collision_dist(hw);
else {
ret_val = em_config_mac_to_phy(hw);
@@ -3672,6 +3756,12 @@ em_read_phy_reg_ex(struct em_hw *hw, uint32_t reg_addr,
return -E1000_ERR_PARAM;
}
+ if(hw->mac_type == em_icp_xxxx) {
+ *phy_data = gcu_miibus_readreg(hw, hw->icp_xxxx_port_num,
+ reg_addr);
+ return E1000_SUCCESS;
+ }
+
if (hw->mac_type > em_82543) {
/* Set up Op-code, Phy Address, and register address in the MDI
* Control register. The MAC will take care of interfacing with the
@@ -3816,6 +3906,11 @@ em_write_phy_reg_ex(struct em_hw *hw, uint32_t reg_addr,
return -E1000_ERR_PARAM;
}
+ if(hw->mac_type == em_icp_xxxx) {
+ gcu_miibus_writereg(hw, hw->icp_xxxx_port_num,
+ reg_addr, phy_data);
+ return E1000_SUCCESS;
+ }
if (hw->mac_type > em_82543) {
/* Set up Op-code, Phy Address, register address, and data intended
* for the PHY register in the MDI Control register. The MAC will take
@@ -3946,7 +4041,7 @@ em_phy_hw_reset(struct em_hw *hw)
DEBUGOUT("Resetting Phy...\n");
- if (hw->mac_type > em_82543) {
+ if (hw->mac_type > em_82543 && hw->mac_type != em_icp_xxxx ) {
if ((hw->mac_type == em_80003es2lan) &&
(E1000_READ_REG(hw, STATUS) & E1000_STATUS_FUNC_1)) {
swfw = E1000_SWFW_PHY1_SM;
@@ -3978,6 +4073,7 @@ em_phy_hw_reset(struct em_hw *hw)
if (hw->mac_type >= em_82571)
msec_delay_irq(10);
em_swfw_sync_release(hw, swfw);
+ /* the M88E1141_E_PHY_ID might need reset here, but nothing proves it */
} else {
/* Read the Extended Device Control Register, assert the PHY_RESET_DIR
* bit to put the PHY into reset. Then, take it out of reset.
@@ -4191,6 +4287,9 @@ em_match_gig_phy(struct em_hw *hw)
if (hw->phy_id == IFE_C_E_PHY_ID) match = TRUE;
if (hw->phy_id == BME1000_E_PHY_ID) match = TRUE;
break;
+ case em_icp_xxxx:
+ if (hw->phy_id == M88E1141_E_PHY_ID) match = TRUE;
+ break;
default:
DEBUGOUT1("Invalid MAC type %d\n", hw->mac_type);
return -E1000_ERR_CONFIG;
@@ -4221,7 +4320,7 @@ em_detect_gig_phy(struct em_hw *hw)
return E1000_SUCCESS;
/* default phy address, most phys reside here, but not all (ICH10) */
- hw->phy_addr = 1;
+ hw->phy_addr = 0;
/* The 82571 firmware may still be configuring the PHY. In this
* case, we cannot access the PHY until the configuration is done. So
@@ -4332,6 +4431,7 @@ em_init_eeprom_params(struct em_hw *hw)
case em_82540:
case em_82545:
case em_82545_rev_3:
+ case em_icp_xxxx:
case em_82546:
case em_82546_rev_3:
eeprom->type = em_eeprom_microwire;
@@ -5086,9 +5186,14 @@ em_validate_eeprom_checksum(struct em_hw *hw)
{
uint16_t checksum = 0;
uint16_t i, eeprom_data;
+ uint16_t checksum_reg;
DEBUGFUNC("em_validate_eeprom_checksum");
+ checksum_reg = hw->mac_type != em_icp_xxxx
+ ? EEPROM_CHECKSUM_REG
+ : EEPROM_CHECKSUM_REG_ICP_xxxx;
+
if (((hw->mac_type == em_82573) || (hw->mac_type == em_82574)) &&
(em_is_onboard_nvm_eeprom(hw) == FALSE)) {
/* Check bit 4 of word 10h. If it is 0, firmware is done updating
@@ -5124,7 +5229,7 @@ em_validate_eeprom_checksum(struct em_hw *hw)
}
}
- for (i = 0; i < (EEPROM_CHECKSUM_REG + 1); i++) {
+ for (i = 0; i < (checksum_reg + 1); i++) {
if (em_read_eeprom(hw, i, 1, &eeprom_data) < 0) {
DEBUGOUT("EEPROM Read Error\n");
return -E1000_ERR_EEPROM;
@@ -5607,12 +5712,18 @@ em_read_mac_addr(struct em_hw * hw)
{
uint16_t offset;
uint16_t eeprom_data, i;
+ uint16_t ia_base_addr = 0;
DEBUGFUNC("em_read_mac_addr");
+ if(hw->mac_type == em_icp_xxxx) {
+ ia_base_addr = (uint16_t)
+ EEPROM_IA_START_ICP_xxxx(hw->icp_xxxx_port_num);
+ }
+
for (i = 0; i < NODE_ADDRESS_SIZE; i += 2) {
offset = i >> 1;
- if (em_read_eeprom(hw, offset, 1, &eeprom_data) < 0) {
+ if (em_read_eeprom(hw, offset + ia_base_addr, 1, &eeprom_data) < 0) {
DEBUGOUT("EEPROM Read Error\n");
return -E1000_ERR_EEPROM;
}
@@ -6019,7 +6130,7 @@ em_id_led_init(struct em_hw * hw)
DEBUGFUNC("em_id_led_init");
- if (hw->mac_type < em_82540) {
+ if (hw->mac_type < em_82540 || hw->mac_type == em_icp_xxxx) {
/* Nothing to do */
return E1000_SUCCESS;
}
@@ -6168,7 +6279,8 @@ em_clear_hw_cntrs(struct em_hw *hw)
temp = E1000_READ_REG(hw, TSCTC);
temp = E1000_READ_REG(hw, TSCTFC);
- if (hw->mac_type <= em_82544) return;
+ if (hw->mac_type <= em_82544
+ || hw->mac_type == em_icp_xxxx) return;
temp = E1000_READ_REG(hw, MGTPRC);
temp = E1000_READ_REG(hw, MGTPDC);
@@ -6292,6 +6404,11 @@ em_get_bus_info(struct em_hw *hw)
hw->bus_speed = em_bus_speed_unknown;
hw->bus_width = em_bus_width_unknown;
break;
+ case em_icp_xxxx:
+ hw->bus_type = em_bus_type_cpp;
+ hw->bus_speed = em_bus_speed_unknown;
+ hw->bus_width = em_bus_width_unknown;
+ break;
case em_82571:
case em_82572:
case em_82573:
@@ -6399,7 +6516,8 @@ em_get_cable_length(struct em_hw *hw,
*min_length = *max_length = 0;
/* Use old method for Phy older than IGP */
- if (hw->phy_type == em_phy_m88) {
+ if (hw->phy_type == em_phy_m88 ||
+ hw->phy_type == em_phy_oem) {
ret_val = em_read_phy_reg(hw, M88E1000_PHY_SPEC_STATUS,
&phy_data);
@@ -6592,7 +6710,8 @@ em_check_downshift(struct em_hw *hw)
hw->speed_downgraded = (phy_data & IGP01E1000_PLHR_SS_DOWNGRADE) ? 1 : 0;
} else if ((hw->phy_type == em_phy_m88) ||
- (hw->phy_type == em_phy_gg82563)) {
+ (hw->phy_type == em_phy_gg82563) ||
+ (hw->phy_type == em_phy_oem)) {
ret_val = em_read_phy_reg(hw, M88E1000_PHY_SPEC_STATUS,
&phy_data);
if (ret_val)
diff --git a/sys/dev/pci/if_em_hw.h b/sys/dev/pci/if_em_hw.h
index 485e1cec2ce..4511dd2cd88 100644
--- a/sys/dev/pci/if_em_hw.h
+++ b/sys/dev/pci/if_em_hw.h
@@ -31,7 +31,7 @@
*******************************************************************************/
-/* $OpenBSD: if_em_hw.h,v 1.34 2009/10/31 12:26:36 sobrado Exp $ */
+/* $OpenBSD: if_em_hw.h,v 1.35 2009/11/25 13:28:13 dms Exp $ */
/* $FreeBSD: if_em_hw.h,v 1.15 2005/05/26 23:32:02 tackerman Exp $ */
/* if_em_hw.h
@@ -58,6 +58,7 @@ typedef enum {
em_82540,
em_82545,
em_82545_rev_3,
+ em_icp_xxxx,
em_82546,
em_82546_rev_3,
em_82541,
@@ -91,6 +92,7 @@ typedef enum {
em_media_type_copper = 0,
em_media_type_fiber = 1,
em_media_type_internal_serdes = 2,
+ em_media_type_oem = 3,
em_num_media_types
} em_media_type;
@@ -112,6 +114,7 @@ typedef enum {
em_bus_type_pci,
em_bus_type_pcix,
em_bus_type_pci_express,
+ em_bus_type_cpp,
em_bus_type_reserved
} em_bus_type;
@@ -231,6 +234,7 @@ typedef enum {
em_phy_igp_3,
em_phy_ife,
em_phy_bm, /* phy used in i82574L, ICH10 and some ICH9 */
+ em_phy_oem,
em_phy_undefined = 0xFF
} em_phy_type;
@@ -304,6 +308,7 @@ typedef enum {
#define E1000_BLK_PHY_RESET 12
#define E1000_ERR_SWFW_SYNC 13
#define E1000_NOT_IMPLEMENTED 14
+#define E1000_DEFER_INIT 15
#define E1000_BYTE_SWAP_WORD(_value) ((((_value) & 0x00ff) << 8) | \
(((_value) & 0xff00) >> 8))
@@ -322,6 +327,8 @@ void em_config_collision_dist(struct em_hw *hw);
int32_t em_check_for_link(struct em_hw *hw);
int32_t em_get_speed_and_duplex(struct em_hw *hw, uint16_t *speed, uint16_t *duplex);
int32_t em_force_mac_fc(struct em_hw *hw);
+int32_t em_copper_link_autoneg(struct em_hw *hw);
+int32_t em_copper_link_postconfig(struct em_hw *hw);
/* PHY */
int32_t em_read_phy_reg(struct em_hw *hw, uint32_t reg_addr, uint16_t *phy_data);
@@ -522,6 +529,9 @@ int32_t em_check_phy_reset_block(struct em_hw *hw);
#define E1000_DEV_ID_82576_QUAD_COPPER 0x10E8
#define E1000_DEV_ID_82576_NS 0x150A
#define E1000_DEV_ID_82574L 0x10D3
+#define E1000_DEV_ID_EP80579_LAN_1 0x5040 /* EP80579 LAN */
+#define E1000_DEV_ID_EP80579_LAN_2 0x5044 /* EP80579 LAN */
+#define E1000_DEV_ID_EP80579_LAN_3 0x5048 /* EP80579 LAN */
#define NODE_ADDRESS_SIZE 6
#define ETH_LENGTH_OF_ADDRESS 6
@@ -1481,6 +1491,9 @@ struct em_hw {
boolean_t mng_reg_access_disabled;
boolean_t leave_av_bit_off;
boolean_t kmrn_lock_loss_workaround_disabled;
+ boolean_t icp_xxxx_is_link_up;
+ uint32_t icp_xxxx_port_num;
+ struct gcu_softc * gcu;
};
#define E1000_EEPROM_SWDPIN0 0x0001 /* SWDPIN 0 EEPROM Value */
@@ -2954,6 +2967,10 @@ struct em_host_command_info {
#define M88EC018_EPSCR_DOWNSHIFT_COUNTER_7X 0x0C00
#define M88EC018_EPSCR_DOWNSHIFT_COUNTER_8X 0x0E00
+/* M88E1141 specific */
+#define M88E1000_EPSCR_TX_TIME_CTRL 0x0002 /* Add Delay */
+#define M88E1000_EPSCR_RX_TIME_CTRL 0x0080 /* Add Delay */
+
/* IGP01E1000 Specific Port Config Register - R/W */
#define IGP01E1000_PSCFR_AUTO_MDIX_PAR_DETECT 0x0010
#define IGP01E1000_PSCFR_PRE_EN 0x0020
@@ -3180,6 +3197,7 @@ struct em_host_command_info {
#define L1LXT971A_PHY_ID 0x001378E0
#define GG82563_E_PHY_ID 0x01410CA0
#define BME1000_E_PHY_ID 0x01410CB0
+#define M88E1141_E_PHY_ID 0x01410CD0
/* Bits...
* 15-5: page
@@ -3399,4 +3417,19 @@ union ich8_hws_flash_regacc {
#define AUTONEG_ADVERTISE_10_100_ALL 0x000F /* All 10/100 speeds*/
#define AUTONEG_ADVERTISE_10_ALL 0x0003 /* 10Mbps Full & Half speeds*/
+/* ICP PCI Dev ID xxxx macros to calculate word offsets for IA, IPv4 and IPv6 */
+#define EEPROM_MGMT_CONTROL_ICP_xxxx(device_num) (((device_num) + 1) << 4)
+#define EEPROM_INIT_CONTROL3_ICP_xxxx(device_num) ((((device_num) + 1) << 4) + 1)
+#define EEPROM_IA_START_ICP_xxxx(device_num) ((((device_num) + 1) << 4) + 2)
+#define EEPROM_IPV4_START_ICP_xxxx(device_num) ((((device_num) + 1) << 4) + 5)
+#define EEPROM_IPV6_START_ICP_xxxx(device_num) ((((device_num) + 1) << 4) + 7)
+#define EEPROM_CHECKSUM_REG_ICP_xxxx EEPROM_CHECKSUM_REG
+#define PCI_CAP_ID_ST 0x09
+#define PCI_ST_SMIA_OFFSET 0x04
+
+#define E1000_IMC1 0x008D8 /* Interrupt Mask Clear 1 - RW */
+#define E1000_IMC2 0x008F8 /* Interrupt Mask Clear 2 - RW */
+#define E1000_82542_IMC1 E1000_IMC1
+#define E1000_82542_IMC2 E1000_IMC2
+
#endif /* _EM_HW_H_ */
diff --git a/sys/dev/pci/if_em_soc.c b/sys/dev/pci/if_em_soc.c
new file mode 100644
index 00000000000..7932e9cacd2
--- /dev/null
+++ b/sys/dev/pci/if_em_soc.c
@@ -0,0 +1,136 @@
+/* $OpenBSD: if_em_soc.c,v 1.1 2009/11/25 13:28:13 dms Exp $ */
+
+/*
+ * Copyright (c) 2009 Dariusz Swiderski <sfires@sfires.net>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <dev/pci/if_em.h>
+#include <dev/pci/if_em_hw.h>
+#include <dev/pci/if_em_soc.h>
+#include <dev/pci/gcu.h>
+#include <dev/pci/gcu_reg.h>
+
+void em_media_status(struct ifnet *, struct ifmediareq *);
+int em_media_change(struct ifnet *);
+
+void *
+em_lookup_gcu(struct device *self)
+{
+ struct device *dev;
+
+ INIT_DEBUGOUT("em_lookup_gcu");
+ TAILQ_FOREACH(dev, &alldevs, dv_list) {
+ if (strcmp(dev->dv_xname, "gcu0") == NULL) {
+ return dev;
+ }
+ }
+ return 0;
+}
+
+int
+em_attach_miibus(struct device *self)
+{
+ return 0;
+}
+
+int
+gcu_miibus_readreg(struct em_hw *hw, int phy, int reg)
+{
+ struct em_softc *sc = (struct em_softc *)
+ ((struct em_osdep *)hw->back)->dev;
+ struct gcu_softc *gcu = hw->gcu;
+ uint32_t data = 0;
+ uint32_t done = 0;
+ int i = 0;
+
+ if (gcu == 0)
+ return 0;
+
+ /* format the data to be written to MDIO_COMMAND_REG */
+ data |= (reg << MDIO_COMMAND_PHY_REG_OFFSET);
+ data |= (phy << MDIO_COMMAND_PHY_ADDR_OFFSET);
+ data |= MDIO_COMMAND_GO_MASK;
+
+ mtx_enter(&gcu->mdio_mtx);
+ bus_space_write_4(gcu->tag, gcu->handle, MDIO_COMMAND_REG, data);
+
+ while (!done && (i++ < GCU_MAX_ATTEMPTS)) {
+ DELAY(GCU_CMD_DELAY);
+ data = bus_space_read_4(gcu->tag, gcu->handle,
+ MDIO_COMMAND_REG);
+ done = !((data & MDIO_COMMAND_GO_MASK) >>
+ MDIO_COMMAND_GO_OFFSET);
+ }
+ mtx_leave(&gcu->mdio_mtx);
+
+ if (i >= GCU_MAX_ATTEMPTS) {
+ printf("%s: phy read timeout: phy %d, reg %d\n",
+ sc->sc_dv.dv_xname, phy, reg);
+ return (0);
+ }
+
+ mtx_enter(&gcu->mdio_mtx);
+ data = bus_space_read_4(gcu->tag, gcu->handle, MDIO_STATUS_REG);
+ mtx_leave(&gcu->mdio_mtx);
+
+ if((data & MDIO_STATUS_STATUS_MASK) != 0) {
+ printf("%s: unable to read phy %d reg %d\n",
+ sc->sc_dv.dv_xname, phy, reg);
+ return (0);
+ }
+ return (uint16_t) (data & MDIO_STATUS_READ_DATA_MASK);
+}
+
+void
+gcu_miibus_writereg(struct em_hw *hw, int phy, int reg, int val)
+{
+ struct em_softc *sc = (struct em_softc *)
+ ((struct em_osdep *)hw->back)->dev;
+ struct gcu_softc *gcu = hw->gcu;
+ uint32_t data, done = 0;
+ int i = 0;
+
+ if (gcu == 0)
+ return;
+
+ /* format the data to be written to the MDIO_COMMAND_REG */
+ data = val;
+ data |= (reg << MDIO_COMMAND_PHY_REG_OFFSET);
+ data |= (phy << MDIO_COMMAND_PHY_ADDR_OFFSET);
+ data |= MDIO_COMMAND_OPER_MASK | MDIO_COMMAND_GO_MASK;
+
+ mtx_enter(&gcu->mdio_mtx);
+ bus_space_write_4(gcu->tag, gcu->handle, MDIO_COMMAND_REG, data);
+
+ while (!done && (i++ < GCU_MAX_ATTEMPTS)) {
+ DELAY(GCU_CMD_DELAY);
+ data = bus_space_read_4(gcu->tag, gcu->handle,
+ MDIO_COMMAND_REG);
+ done = !((data & MDIO_COMMAND_GO_MASK) >>
+ MDIO_COMMAND_GO_OFFSET);
+ }
+ mtx_leave(&gcu->mdio_mtx);
+
+ if (i >= GCU_MAX_ATTEMPTS) {
+ printf("%s: phy read timeout: phy %d, reg %d\n",
+ sc->sc_dv.dv_xname, phy, reg);
+ return;
+ }
+}
+
+void
+gcu_miibus_statchg(struct device *dev)
+{
+}
diff --git a/sys/dev/pci/if_em_soc.h b/sys/dev/pci/if_em_soc.h
new file mode 100644
index 00000000000..ddadf55130e
--- /dev/null
+++ b/sys/dev/pci/if_em_soc.h
@@ -0,0 +1,28 @@
+/* $OpenBSD: if_em_soc.h,v 1.1 2009/11/25 13:28:13 dms Exp $ */
+
+/*
+ * Copyright (c) 2009 Dariusz Swiderski <sfires@sfires.net>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+
+void *em_lookup_gcu(struct device *);
+int em_attach_miibus(struct device *self);
+
+int gcu_miibus_readreg(struct em_hw *, int, int);
+void gcu_miibus_writereg(struct em_hw *, int, int, int);
+void gcu_miibus_statchg(struct device *);
+
+#define GCU_MAX_ATTEMPTS 64
+#define GCU_CMD_DELAY 50 /* microseconds */