summaryrefslogtreecommitdiff
path: root/sys
diff options
context:
space:
mode:
Diffstat (limited to 'sys')
-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 */