1139749Simp/*- 291398Stmm * Copyright (C) 2001 Eduardo Horvath. 3174987Smarius * Copyright (c) 2007 Marius Strobl <marius@FreeBSD.org> 491398Stmm * All rights reserved. 591398Stmm * 691398Stmm * 791398Stmm * Redistribution and use in source and binary forms, with or without 891398Stmm * modification, are permitted provided that the following conditions 991398Stmm * are met: 1091398Stmm * 1. Redistributions of source code must retain the above copyright 1191398Stmm * notice, this list of conditions and the following disclaimer. 1291398Stmm * 2. Redistributions in binary form must reproduce the above copyright 1391398Stmm * notice, this list of conditions and the following disclaimer in the 1491398Stmm * documentation and/or other materials provided with the distribution. 1591398Stmm * 1691398Stmm * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND 1791398Stmm * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 1891398Stmm * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 1991398Stmm * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE 2091398Stmm * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 2191398Stmm * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 2291398Stmm * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 2391398Stmm * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 2491398Stmm * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 2591398Stmm * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 2691398Stmm * SUCH DAMAGE. 2791398Stmm * 2891398Stmm * from: NetBSD: if_gem_pci.c,v 1.7 2001/10/18 15:09:15 thorpej Exp 2991398Stmm */ 3091398Stmm 31119418Sobrien#include <sys/cdefs.h> 32119418Sobrien__FBSDID("$FreeBSD: releng/11.0/sys/dev/gem/if_gem_pci.c 242625 2012-11-05 19:16:27Z dim $"); 33119418Sobrien 3491398Stmm/* 35172334Smarius * PCI bindings for Apple GMAC, Sun ERI and Sun GEM Ethernet controllers 3691398Stmm */ 3791398Stmm 3891398Stmm#include <sys/param.h> 3991398Stmm#include <sys/systm.h> 4091398Stmm#include <sys/bus.h> 4191398Stmm#include <sys/kernel.h> 42148369Smarius#include <sys/lock.h> 43177560Smarius#include <sys/malloc.h> 44130026Sphk#include <sys/module.h> 45148369Smarius#include <sys/mutex.h> 4691398Stmm#include <sys/resource.h> 47172334Smarius#include <sys/rman.h> 4891398Stmm#include <sys/socket.h> 4991398Stmm 5091398Stmm#include <net/ethernet.h> 5191398Stmm#include <net/if.h> 5291398Stmm 5391398Stmm#include <machine/bus.h> 54172334Smarius#if defined(__powerpc__) || defined(__sparc64__) 55212725Smarius#include <dev/ofw/ofw_bus.h> 56119696Smarcel#include <dev/ofw/openfirm.h> 5791398Stmm#include <machine/ofw_machdep.h> 58172334Smarius#endif 59172334Smarius#include <machine/resource.h> 6091398Stmm 6191398Stmm#include <dev/gem/if_gemreg.h> 6291398Stmm#include <dev/gem/if_gemvar.h> 6391398Stmm 64172334Smarius#include <dev/pci/pcireg.h> 6591398Stmm#include <dev/pci/pcivar.h> 6691398Stmm 6791398Stmm#include "miibus_if.h" 6891398Stmm 69174987Smariusstatic int gem_pci_attach(device_t dev); 70174987Smariusstatic int gem_pci_detach(device_t dev); 71174987Smariusstatic int gem_pci_probe(device_t dev); 72174987Smariusstatic int gem_pci_resume(device_t dev); 73174987Smariusstatic int gem_pci_suspend(device_t dev); 7491398Stmm 7591398Stmmstatic device_method_t gem_pci_methods[] = { 7691398Stmm /* Device interface */ 7791398Stmm DEVMETHOD(device_probe, gem_pci_probe), 7891398Stmm DEVMETHOD(device_attach, gem_pci_attach), 79108964Stmm DEVMETHOD(device_detach, gem_pci_detach), 80108964Stmm DEVMETHOD(device_suspend, gem_pci_suspend), 81108964Stmm DEVMETHOD(device_resume, gem_pci_resume), 82108964Stmm /* Use the suspend handler here, it is all that is required. */ 83108964Stmm DEVMETHOD(device_shutdown, gem_pci_suspend), 8491398Stmm 8591398Stmm /* MII interface */ 8691398Stmm DEVMETHOD(miibus_readreg, gem_mii_readreg), 8791398Stmm DEVMETHOD(miibus_writereg, gem_mii_writereg), 8891398Stmm DEVMETHOD(miibus_statchg, gem_mii_statchg), 8991398Stmm 90227843Smarius DEVMETHOD_END 9191398Stmm}; 9291398Stmm 9391398Stmmstatic driver_t gem_pci_driver = { 9491398Stmm "gem", 9591398Stmm gem_pci_methods, 96169269Sphk sizeof(struct gem_softc) 9791398Stmm}; 9891398Stmm 99113506SmdoddDRIVER_MODULE(gem, pci, gem_pci_driver, gem_devclass, 0, 0); 100113506SmdoddMODULE_DEPEND(gem, pci, 1, 1, 1); 101113506SmdoddMODULE_DEPEND(gem, ether, 1, 1, 1); 10291398Stmm 103172334Smariusstatic const struct gem_pci_dev { 104172334Smarius uint32_t gpd_devid; 105172334Smarius int gpd_variant; 106172334Smarius const char *gpd_desc; 107242625Sdim} gem_pci_devlist[] = { 108172334Smarius { 0x1101108e, GEM_SUN_ERI, "Sun ERI 10/100 Ethernet" }, 109172334Smarius { 0x2bad108e, GEM_SUN_GEM, "Sun GEM Gigabit Ethernet" }, 110172334Smarius { 0x0021106b, GEM_APPLE_GMAC, "Apple UniNorth GMAC Ethernet" }, 111172334Smarius { 0x0024106b, GEM_APPLE_GMAC, "Apple Pangea GMAC Ethernet" }, 112172334Smarius { 0x0032106b, GEM_APPLE_GMAC, "Apple UniNorth2 GMAC Ethernet" }, 113172334Smarius { 0x004c106b, GEM_APPLE_K2_GMAC,"Apple K2 GMAC Ethernet" }, 114172334Smarius { 0x0051106b, GEM_APPLE_GMAC, "Apple Shasta GMAC Ethernet" }, 115172334Smarius { 0x006b106b, GEM_APPLE_GMAC, "Apple Intrepid 2 GMAC Ethernet" }, 116123851Sobrien { 0, 0, NULL } 11791398Stmm}; 11891398Stmm 11991398Stmmstatic int 120174987Smariusgem_pci_probe(device_t dev) 12191398Stmm{ 12291398Stmm int i; 12391398Stmm 12491398Stmm for (i = 0; gem_pci_devlist[i].gpd_desc != NULL; i++) { 125172334Smarius if (pci_get_devid(dev) == gem_pci_devlist[i].gpd_devid) { 12691398Stmm device_set_desc(dev, gem_pci_devlist[i].gpd_desc); 127143161Simp return (BUS_PROBE_DEFAULT); 12891398Stmm } 12991398Stmm } 13091398Stmm 13191398Stmm return (ENXIO); 13291398Stmm} 13391398Stmm 134169269Sphkstatic struct resource_spec gem_pci_res_spec[] = { 135177560Smarius { SYS_RES_IRQ, 0, RF_SHAREABLE | RF_ACTIVE }, /* GEM_RES_INTR */ 136177560Smarius { SYS_RES_MEMORY, PCIR_BAR(0), RF_ACTIVE }, /* GEM_RES_BANK1 */ 137169269Sphk { -1, 0 } 138169269Sphk}; 139169269Sphk 140212725Smarius#define GEM_SHARED_PINS "shared-pins" 141212725Smarius#define GEM_SHARED_PINS_SERDES "serdes" 142212725Smarius 14391398Stmmstatic int 144174987Smariusgem_pci_attach(device_t dev) 14591398Stmm{ 146172334Smarius struct gem_softc *sc; 147172334Smarius int i; 148212725Smarius#if defined(__powerpc__) || defined(__sparc64__) 149212725Smarius char buf[sizeof(GEM_SHARED_PINS)]; 150212725Smarius#else 151172334Smarius int j; 152172334Smarius#endif 15391398Stmm 154172334Smarius sc = device_get_softc(dev); 155172334Smarius sc->sc_variant = GEM_UNKNOWN; 156172334Smarius for (i = 0; gem_pci_devlist[i].gpd_desc != NULL; i++) { 157172334Smarius if (pci_get_devid(dev) == gem_pci_devlist[i].gpd_devid) { 158172334Smarius sc->sc_variant = gem_pci_devlist[i].gpd_variant; 159172334Smarius break; 160172334Smarius } 161172334Smarius } 162172334Smarius if (sc->sc_variant == GEM_UNKNOWN) { 163172334Smarius device_printf(dev, "unknown adaptor\n"); 164172334Smarius return (ENXIO); 165172334Smarius } 166172334Smarius 167117116Stmm pci_enable_busmaster(dev); 168117116Stmm 16991963Stmm /* 170117116Stmm * Some Sun GEMs/ERIs do have their intpin register bogusly set to 0, 171174987Smarius * although it should be 1. Correct that. 17291963Stmm */ 173117116Stmm if (pci_get_intpin(dev) == 0) 174117116Stmm pci_set_intpin(dev, 1); 17591963Stmm 176223944Smarius /* Set the PCI latency timer for Sun ERIs. */ 177223944Smarius if (sc->sc_variant == GEM_SUN_ERI) 178223944Smarius pci_write_config(dev, PCIR_LATTIMER, GEM_ERI_LATENCY_TIMER, 1); 179223944Smarius 18091398Stmm sc->sc_dev = dev; 181174987Smarius sc->sc_flags |= GEM_PCI; 18291398Stmm 183169269Sphk if (bus_alloc_resources(dev, gem_pci_res_spec, sc->sc_res)) { 184169269Sphk device_printf(dev, "failed to allocate resources\n"); 185169269Sphk bus_release_resources(dev, gem_pci_res_spec, sc->sc_res); 186169269Sphk return (ENXIO); 18791398Stmm } 18891398Stmm 189169269Sphk GEM_LOCK_INIT(sc, device_get_nameunit(dev)); 19091398Stmm 191177560Smarius /* 192177560Smarius * Derive GEM_RES_BANK2 from GEM_RES_BANK1. This seemed cleaner 193177560Smarius * with the old way of using copies of the bus tag and handle in 194177560Smarius * the softc along with bus_space_*()... 195177560Smarius */ 196177560Smarius sc->sc_res[GEM_RES_BANK2] = malloc(sizeof(*sc->sc_res[GEM_RES_BANK2]), 197177560Smarius M_DEVBUF, M_NOWAIT | M_ZERO); 198177560Smarius if (sc->sc_res[GEM_RES_BANK2] == NULL) { 199177560Smarius device_printf(dev, "failed to allocate bank2 resource\n"); 200177560Smarius goto fail; 201177560Smarius } 202177560Smarius rman_set_bustag(sc->sc_res[GEM_RES_BANK2], 203177560Smarius rman_get_bustag(sc->sc_res[GEM_RES_BANK1])); 204177560Smarius bus_space_subregion(rman_get_bustag(sc->sc_res[GEM_RES_BANK1]), 205177560Smarius rman_get_bushandle(sc->sc_res[GEM_RES_BANK1]), 206177560Smarius GEM_PCI_BANK2_OFFSET, GEM_PCI_BANK2_SIZE, 207177560Smarius &sc->sc_res[GEM_RES_BANK2]->r_bushandle); 208177560Smarius 209194763Smarius /* Determine whether we're running at 66MHz. */ 210194763Smarius if ((GEM_BANK2_READ_4(sc, GEM_PCI_BIF_CONFIG) & 211194763Smarius GEM_PCI_BIF_CNF_M66EN) != 0) 212194763Smarius sc->sc_flags |= GEM_PCI66; 213194763Smarius 214172334Smarius#if defined(__powerpc__) || defined(__sparc64__) 215147256Sbrooks OF_getetheraddr(dev, sc->sc_enaddr); 216212725Smarius if (OF_getprop(ofw_bus_get_node(dev), GEM_SHARED_PINS, buf, 217212725Smarius sizeof(buf)) > 0) { 218212725Smarius buf[sizeof(buf) - 1] = '\0'; 219212725Smarius if (strcmp(buf, GEM_SHARED_PINS_SERDES) == 0) 220212725Smarius sc->sc_flags |= GEM_SERDES; 221212725Smarius } 222172334Smarius#else 223172334Smarius /* 224172334Smarius * Dig out VPD (vital product data) and read NA (network address). 225194763Smarius * The VPD resides in the PCI Expansion ROM (PCI FCode) and can't 226194763Smarius * be accessed via the PCI capability pointer. 227172334Smarius * ``Writing FCode 3.x Programs'' (newer ones, dated 1997 and later) 228172334Smarius * chapter 2 describes the data structure. 229172334Smarius */ 23091398Stmm 231172334Smarius#define PCI_ROMHDR_SIZE 0x1c 232172334Smarius#define PCI_ROMHDR_SIG 0x00 233172334Smarius#define PCI_ROMHDR_SIG_MAGIC 0xaa55 /* little endian */ 234172334Smarius#define PCI_ROMHDR_PTR_DATA 0x18 235172334Smarius#define PCI_ROM_SIZE 0x18 236172334Smarius#define PCI_ROM_SIG 0x00 237172334Smarius#define PCI_ROM_SIG_MAGIC 0x52494350 /* "PCIR", endian */ 238172334Smarius /* reversed */ 239172334Smarius#define PCI_ROM_VENDOR 0x04 240172334Smarius#define PCI_ROM_DEVICE 0x06 241172334Smarius#define PCI_ROM_PTR_VPD 0x08 242172334Smarius#define PCI_VPDRES_BYTE0 0x00 243172334Smarius#define PCI_VPDRES_ISLARGE(x) ((x) & 0x80) 244172334Smarius#define PCI_VPDRES_LARGE_NAME(x) ((x) & 0x7f) 245172334Smarius#define PCI_VPDRES_LARGE_LEN_LSB 0x01 246172334Smarius#define PCI_VPDRES_LARGE_LEN_MSB 0x02 247194763Smarius#define PCI_VPDRES_LARGE_SIZE 0x03 248194763Smarius#define PCI_VPDRES_TYPE_VPD 0x10 /* large */ 249172334Smarius#define PCI_VPD_KEY0 0x00 250172334Smarius#define PCI_VPD_KEY1 0x01 251172334Smarius#define PCI_VPD_LEN 0x02 252194763Smarius#define PCI_VPD_SIZE 0x03 253172334Smarius 254177560Smarius#define GEM_ROM_READ_1(sc, offs) \ 255194763Smarius GEM_BANK1_READ_1((sc), GEM_PCI_ROM_OFFSET + (offs)) 256177560Smarius#define GEM_ROM_READ_2(sc, offs) \ 257194763Smarius GEM_BANK1_READ_2((sc), GEM_PCI_ROM_OFFSET + (offs)) 258177560Smarius#define GEM_ROM_READ_4(sc, offs) \ 259194763Smarius GEM_BANK1_READ_4((sc), GEM_PCI_ROM_OFFSET + (offs)) 260172334Smarius 261172334Smarius /* Read PCI Expansion ROM header. */ 262172334Smarius if (GEM_ROM_READ_2(sc, PCI_ROMHDR_SIG) != PCI_ROMHDR_SIG_MAGIC || 263174987Smarius (i = GEM_ROM_READ_2(sc, PCI_ROMHDR_PTR_DATA)) < 264174987Smarius PCI_ROMHDR_SIZE) { 265172334Smarius device_printf(dev, "unexpected PCI Expansion ROM header\n"); 266172334Smarius goto fail; 267172334Smarius } 268172334Smarius 269172334Smarius /* Read PCI Expansion ROM data. */ 270172334Smarius if (GEM_ROM_READ_4(sc, i + PCI_ROM_SIG) != PCI_ROM_SIG_MAGIC || 271172334Smarius GEM_ROM_READ_2(sc, i + PCI_ROM_VENDOR) != pci_get_vendor(dev) || 272172334Smarius GEM_ROM_READ_2(sc, i + PCI_ROM_DEVICE) != pci_get_device(dev) || 273174987Smarius (j = GEM_ROM_READ_2(sc, i + PCI_ROM_PTR_VPD)) < 274174987Smarius i + PCI_ROM_SIZE) { 275172334Smarius device_printf(dev, "unexpected PCI Expansion ROM data\n"); 276172334Smarius goto fail; 277172334Smarius } 278172334Smarius 27991398Stmm /* 280172334Smarius * Read PCI VPD. 281172334Smarius * SUNW,pci-gem cards have a single large resource VPD-R tag 282174987Smarius * containing one NA. The VPD used is not in PCI 2.2 standard 283174987Smarius * format however. The length in the resource header is in big 284172334Smarius * endian and the end tag is non-standard (0x79) and followed 285174987Smarius * by an all-zero "checksum" byte. Sun calls this a "Fresh 286172334Smarius * Choice Ethernet" VPD... 287172334Smarius */ 288174987Smarius if (PCI_VPDRES_ISLARGE(GEM_ROM_READ_1(sc, 289174987Smarius j + PCI_VPDRES_BYTE0)) == 0 || 290174987Smarius PCI_VPDRES_LARGE_NAME(GEM_ROM_READ_1(sc, 291177560Smarius j + PCI_VPDRES_BYTE0)) != PCI_VPDRES_TYPE_VPD || 292194763Smarius ((GEM_ROM_READ_1(sc, j + PCI_VPDRES_LARGE_LEN_LSB) << 8) | 293172334Smarius GEM_ROM_READ_1(sc, j + PCI_VPDRES_LARGE_LEN_MSB)) != 294172334Smarius PCI_VPD_SIZE + ETHER_ADDR_LEN || 295194763Smarius GEM_ROM_READ_1(sc, j + PCI_VPDRES_LARGE_SIZE + PCI_VPD_KEY0) != 296172334Smarius 0x4e /* N */ || 297194763Smarius GEM_ROM_READ_1(sc, j + PCI_VPDRES_LARGE_SIZE + PCI_VPD_KEY1) != 298172334Smarius 0x41 /* A */ || 299194763Smarius GEM_ROM_READ_1(sc, j + PCI_VPDRES_LARGE_SIZE + PCI_VPD_LEN) != 300172334Smarius ETHER_ADDR_LEN || 301194763Smarius GEM_ROM_READ_1(sc, j + PCI_VPDRES_LARGE_SIZE + PCI_VPD_SIZE + 302172334Smarius ETHER_ADDR_LEN) != 0x79) { 303172334Smarius device_printf(dev, "unexpected PCI VPD\n"); 304172334Smarius goto fail; 305172334Smarius } 306177560Smarius bus_read_region_1(sc->sc_res[GEM_RES_BANK1], 307194763Smarius GEM_PCI_ROM_OFFSET + j + PCI_VPDRES_LARGE_SIZE + PCI_VPD_SIZE, 308177560Smarius sc->sc_enaddr, ETHER_ADDR_LEN); 309172334Smarius#endif 310198211Snwhitehorn /* 311198211Snwhitehorn * The Xserve G5 has a fake GMAC with an all-zero MAC address. 312198211Snwhitehorn * Check for this, and don't attach in this case. 313198211Snwhitehorn */ 314172334Smarius 315198211Snwhitehorn for (i = 0; i < ETHER_ADDR_LEN && sc->sc_enaddr[i] == 0; i++) {} 316198211Snwhitehorn if (i == ETHER_ADDR_LEN) { 317198211Snwhitehorn device_printf(dev, "invalid MAC address\n"); 318198211Snwhitehorn goto fail; 319198211Snwhitehorn } 320198211Snwhitehorn 32191398Stmm if (gem_attach(sc) != 0) { 322174987Smarius device_printf(dev, "could not be attached\n"); 323169269Sphk goto fail; 32491398Stmm } 32591398Stmm 326177560Smarius if (bus_setup_intr(dev, sc->sc_res[GEM_RES_INTR], INTR_TYPE_NET | 327177560Smarius INTR_MPSAFE, NULL, gem_intr, sc, &sc->sc_ih) != 0) { 32891398Stmm device_printf(dev, "failed to set up interrupt\n"); 329109650Stmm gem_detach(sc); 330169269Sphk goto fail; 33191398Stmm } 33291398Stmm return (0); 33391398Stmm 334174987Smarius fail: 335177560Smarius if (sc->sc_res[GEM_RES_BANK2] != NULL) 336177560Smarius free(sc->sc_res[GEM_RES_BANK2], M_DEVBUF); 337172334Smarius GEM_LOCK_DESTROY(sc); 338169269Sphk bus_release_resources(dev, gem_pci_res_spec, sc->sc_res); 33991398Stmm return (ENXIO); 34091398Stmm} 341108964Stmm 342108964Stmmstatic int 343174987Smariusgem_pci_detach(device_t dev) 344108964Stmm{ 345174987Smarius struct gem_softc *sc; 346108964Stmm 347174987Smarius sc = device_get_softc(dev); 348177560Smarius bus_teardown_intr(dev, sc->sc_res[GEM_RES_INTR], sc->sc_ih); 349108964Stmm gem_detach(sc); 350177560Smarius free(sc->sc_res[GEM_RES_BANK2], M_DEVBUF); 351148369Smarius GEM_LOCK_DESTROY(sc); 352169269Sphk bus_release_resources(dev, gem_pci_res_spec, sc->sc_res); 353108964Stmm return (0); 354108964Stmm} 355108964Stmm 356108964Stmmstatic int 357174987Smariusgem_pci_suspend(device_t dev) 358108964Stmm{ 359108964Stmm 360194763Smarius gem_suspend(device_get_softc(dev)); 361108964Stmm return (0); 362108964Stmm} 363108964Stmm 364108964Stmmstatic int 365174987Smariusgem_pci_resume(device_t dev) 366108964Stmm{ 367108964Stmm 368194763Smarius gem_resume(device_get_softc(dev)); 369108964Stmm return (0); 370108964Stmm} 371