summaryrefslogtreecommitdiffstats
path: root/sys/dev/hme
diff options
context:
space:
mode:
authormarius <marius@FreeBSD.org>2004-08-14 22:38:20 +0000
committermarius <marius@FreeBSD.org>2004-08-14 22:38:20 +0000
commit108ba104f10381a25c77f5b9bbacce9eed6b913a (patch)
tree9c2fc2072d5bff3917d91a002da47199de636186 /sys/dev/hme
parentda6b7d8015bfe072fdecf22c4958f40e135d3c2b (diff)
downloadFreeBSD-src-108ba104f10381a25c77f5b9bbacce9eed6b913a.zip
FreeBSD-src-108ba104f10381a25c77f5b9bbacce9eed6b913a.tar.gz
Make hme(4), i.e. the PCI-variant, MI by reading the MAC address on sytems
without Open Firmware directly instead of using OF_getetheraddr(). This is a bit painful though, as the MAC address is contained in the NA field of the VPD of the EBus bridge, which is is another function of the same chip. To make it worse, the VPD of the EBus bridge can't be accessed via the PCI capability pointer but has to be digged out from the Boot PROM and has a non-standard format. The PCI VPD struct and macros used here should be part of the FreeBSD PCI code nevertheless. Approved by: tmm Based on: NetBSD Tested with: Sun X1032A (hme(4)-isp(4)-combo card) on alpha and i386
Diffstat (limited to 'sys/dev/hme')
-rw-r--r--sys/dev/hme/if_hme_pci.c155
1 files changed, 152 insertions, 3 deletions
diff --git a/sys/dev/hme/if_hme_pci.c b/sys/dev/hme/if_hme_pci.c
index 419f554..fc203a9 100644
--- a/sys/dev/hme/if_hme_pci.c
+++ b/sys/dev/hme/if_hme_pci.c
@@ -25,7 +25,7 @@
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
- * from: NetBSD: if_hme_pci.c,v 1.7 2001/10/05 17:49:43 thorpej Exp
+ * from: NetBSD: if_hme_pci.c,v 1.14 2004/03/17 08:58:23 martin Exp
*/
#include <sys/cdefs.h>
@@ -44,8 +44,10 @@ __FBSDID("$FreeBSD$");
#include <sys/socket.h>
#include <machine/bus.h>
+#if defined(__powerpc__) || defined(__sparc64__)
#include <dev/ofw/openfirm.h>
#include <machine/ofw_machdep.h>
+#endif
#include <machine/resource.h>
#include <sys/rman.h>
@@ -116,12 +118,16 @@ DRIVER_MODULE(hme, pci, hme_pci_driver, hme_devclass, 0, 0);
MODULE_DEPEND(hme, pci, 1, 1, 1);
MODULE_DEPEND(hme, ether, 1, 1, 1);
+#define PCI_VENDOR_SUN 0x108e
+#define PCI_PRODUCT_SUN_EBUS 0x1000
+#define PCI_PRODUCT_SUN_HMENETWORK 0x1001
+
int
hme_pci_probe(device_t dev)
{
- if (pci_get_vendor(dev) == 0x108e &&
- pci_get_device(dev) == 0x1001) {
+ if (pci_get_vendor(dev) == PCI_VENDOR_SUN &&
+ pci_get_device(dev) == PCI_PRODUCT_SUN_HMENETWORK) {
device_set_desc(dev, "Sun HME 10/100 Ethernet");
return (0);
}
@@ -134,6 +140,40 @@ hme_pci_attach(device_t dev)
struct hme_pci_softc *hsc = device_get_softc(dev);
struct hme_softc *sc = &hsc->hsc_hme;
int error = 0;
+#if !(defined(__powerpc__) || defined(__sparc64__))
+ device_t *children, ebus_dev;
+ struct resource *ebus_rres;
+ bus_space_handle_t romh;
+ bus_space_tag_t romt;
+ int dataoff, ebus_rrid, slot, vpdoff;
+ int i, nchildren;
+ uint8_t buf[32];
+ static const uint8_t promhdr[] = { 0x55, 0xaa };
+#define PROMHDR_PTR_DATA 0x18
+ static const uint8_t promdat[] = {
+ 0x50, 0x43, 0x49, 0x52, /* "PCIR" */
+ PCI_VENDOR_SUN & 0xff, PCI_VENDOR_SUN >> 8,
+ PCI_PRODUCT_SUN_HMENETWORK & 0xff,
+ PCI_PRODUCT_SUN_HMENETWORK >> 8
+ };
+#define PROMDATA_PTR_VPD 0x08
+#define PROMDATA_DATA2 0x0a
+ static const uint8_t promdat2[] = {
+ 0x18, 0x00, /* structure length */
+ 0x00, /* structure revision */
+ 0x00, /* interface revision */
+ PCIS_NETWORK_ETHERNET, /* subclass code */
+ PCIC_NETWORK /* class code */
+ };
+#define PCI_VPDRES_ISLARGE(x) ((x) & 0x80)
+#define PCI_VPDRES_LARGE_NAME(x) ((x) & 0x7f)
+#define PCI_VPDRES_TYPE_VPD 0x10 /* large */
+ struct pci_vpd {
+ uint8_t vpd_key0;
+ uint8_t vpd_key1;
+ uint8_t vpd_len;
+ } *vpd;
+#endif
pci_enable_busmaster(dev);
/*
@@ -186,7 +226,116 @@ hme_pci_attach(device_t dev)
bus_space_subregion(hsc->hsc_memt, hsc->hsc_memh, 0x7000, 0x1000,
&sc->sc_mifh);
+#if defined(__powerpc__) || defined(__sparc64__)
OF_getetheraddr(dev, sc->sc_arpcom.ac_enaddr);
+#else
+ /*
+ * Dig out VPD (vital product data) and read NA (network address).
+ *
+ * The PCI HME is a PCIO chip, which is composed of two functions:
+ * function 0: PCI-EBus2 bridge, and
+ * function 1: HappyMeal Ethernet controller.
+ *
+ * The VPD of HME resides in the Boot PROM (PCI FCode) attached
+ * to the EBus bridge and can't be accessed via the PCI capability
+ * pointer.
+ * ``Writing FCode 3.x Programs'' (newer ones, dated 1997 and later)
+ * chapter 2 describes the data structure.
+ *
+ * We don't have a MI EBus driver since no EBus device exists
+ * (besides the FCode PROM) on add-on HME boards. The ``no driver
+ * attached'' message for function 0 therefore is what is expected.
+ */
+
+ /* Search accompanying EBus bridge. */
+ slot = pci_get_slot(dev);
+ if (device_get_children(device_get_parent(dev), &children,
+ &nchildren) != 0) {
+ device_printf(dev, "could not get children\n");
+ error = ENXIO;
+ goto fail_sres;
+ }
+ ebus_dev = NULL;
+ for (i = 0; i < nchildren; i++) {
+ if (pci_get_class(children[i]) == PCIC_BRIDGE &&
+ pci_get_vendor(children[i]) == PCI_VENDOR_SUN &&
+ pci_get_device(children[i]) == PCI_PRODUCT_SUN_EBUS &&
+ pci_get_slot(children[i]) == slot) {
+ ebus_dev = children[i];
+ break;
+ }
+ }
+ if (ebus_dev == NULL) {
+ device_printf(dev, "could not find EBus bridge\n");
+ error = ENXIO;
+ goto fail_children;
+ }
+
+ /* Map EBus bridge PROM registers. */
+#define PCI_EBUS2_BOOTROM 0x10
+ ebus_rrid = PCI_EBUS2_BOOTROM;
+ if ((ebus_rres = bus_alloc_resource_any(ebus_dev, SYS_RES_MEMORY,
+ &ebus_rrid, RF_ACTIVE)) == NULL) {
+ device_printf(dev, "could not map PROM registers\n");
+ error = ENXIO;
+ goto fail_children;
+ }
+ romt = rman_get_bustag(ebus_rres);
+ romh = rman_get_bushandle(ebus_rres);
+
+ /* Read PCI expansion PROM header. */
+ bus_space_read_region_1(romt, romh, 0, buf, sizeof(buf));
+ if (memcmp(buf, promhdr, sizeof(promhdr)) != 0 ||
+ (dataoff = (buf[PROMHDR_PTR_DATA] |
+ (buf[PROMHDR_PTR_DATA + 1] << 8))) < 0x1c) {
+ device_printf(dev, "unexpected PCI expansion PROM header\n");
+ error = ENXIO;
+ goto fail_rres;
+ }
+
+ /* Read PCI expansion PROM data. */
+ bus_space_read_region_1(romt, romh, dataoff, buf, sizeof(buf));
+ if (memcmp(buf, promdat, sizeof(promdat)) != 0 ||
+ memcmp(buf + PROMDATA_DATA2, promdat2, sizeof(promdat2)) != 0 ||
+ (vpdoff = (buf[PROMDATA_PTR_VPD] |
+ (buf[PROMDATA_PTR_VPD + 1] << 8))) < 0x1c) {
+ device_printf(dev, "unexpected PCI expansion PROM data\n");
+ error = ENXIO;
+ goto fail_rres;
+ }
+
+ /*
+ * Read PCI VPD.
+ * The VPD of HME is not in PCI 2.2 standard format. The length in
+ * the resource header is in big endian, and resources are not
+ * properly terminated (only one resource and no end tag).
+ */
+ bus_space_read_region_1(romt, romh, vpdoff, buf, sizeof(buf));
+ vpd = (void *)(buf + 3);
+ if (PCI_VPDRES_ISLARGE(buf[0]) == 0 ||
+ PCI_VPDRES_LARGE_NAME(buf[0]) != PCI_VPDRES_TYPE_VPD ||
+ /* buf[1] != 0 || buf[2] != 9 || */ /*len*/
+ vpd->vpd_key0 != 0x4e /* N */ ||
+ vpd->vpd_key1 != 0x41 /* A */ ||
+ vpd->vpd_len != ETHER_ADDR_LEN) {
+ device_printf(dev, "unexpected PCI VPD\n");
+ error = ENXIO;
+ goto fail_rres;
+ }
+ if (buf + 6 == NULL) {
+ device_printf(dev, "could not read network address\n");
+ error = ENXIO;
+ goto fail_rres;
+ }
+ bcopy(buf + 6, sc->sc_arpcom.ac_enaddr, ETHER_ADDR_LEN);
+
+fail_rres:
+ bus_release_resource(ebus_dev, SYS_RES_MEMORY, ebus_rrid, ebus_rres);
+fail_children:
+ free(children, M_TEMP);
+ if (error != 0)
+ goto fail_sres;
+#endif
sc->sc_burst = 64; /* XXX */
OpenPOWER on IntegriCloud