1209908Sraj/*- 2209908Sraj * Copyright (C) 2007-2008 Semihalf, Rafal Jaworowski 3209908Sraj * Copyright (C) 2006-2007 Semihalf, Piotr Kruszynski 4209908Sraj * All rights reserved. 5209908Sraj * 6209908Sraj * Redistribution and use in source and binary forms, with or without 7209908Sraj * modification, are permitted provided that the following conditions 8209908Sraj * are met: 9209908Sraj * 1. Redistributions of source code must retain the above copyright 10209908Sraj * notice, this list of conditions and the following disclaimer. 11209908Sraj * 2. Redistributions in binary form must reproduce the above copyright 12209908Sraj * notice, this list of conditions and the following disclaimer in the 13209908Sraj * documentation and/or other materials provided with the distribution. 14209908Sraj * 15209908Sraj * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 16209908Sraj * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 17209908Sraj * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN 18209908Sraj * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 19209908Sraj * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED 20209908Sraj * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 21209908Sraj * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 22209908Sraj * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 23209908Sraj * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 24209908Sraj * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 25209908Sraj * 26209908Sraj * From: FreeBSD: head/sys/dev/tsec/if_tsec_ocp.c 188712 2009-02-17 14:59:47Z raj 27209908Sraj */ 28209908Sraj 29209908Sraj/* 30209908Sraj * FDT 'simple-bus' attachment for Freescale TSEC controller. 31209908Sraj */ 32209908Sraj#include <sys/cdefs.h> 33209908Sraj__FBSDID("$FreeBSD$"); 34209908Sraj 35209908Sraj#include <sys/param.h> 36209908Sraj#include <sys/systm.h> 37209908Sraj#include <sys/endian.h> 38209908Sraj#include <sys/mbuf.h> 39209908Sraj#include <sys/kernel.h> 40209908Sraj#include <sys/module.h> 41209908Sraj#include <sys/socket.h> 42209908Sraj#include <sys/sysctl.h> 43209908Sraj 44209908Sraj#include <sys/bus.h> 45209908Sraj#include <machine/bus.h> 46209908Sraj#include <sys/rman.h> 47209908Sraj#include <machine/resource.h> 48209908Sraj 49209908Sraj#include <net/ethernet.h> 50209908Sraj#include <net/if.h> 51209908Sraj#include <net/if_dl.h> 52209908Sraj#include <net/if_media.h> 53209908Sraj#include <net/if_arp.h> 54209908Sraj 55209908Sraj#include <dev/fdt/fdt_common.h> 56209908Sraj#include <dev/mii/mii.h> 57209908Sraj#include <dev/mii/miivar.h> 58209908Sraj#include <dev/ofw/ofw_bus.h> 59209908Sraj#include <dev/ofw/ofw_bus_subr.h> 60209908Sraj#include <dev/ofw/openfirm.h> 61209908Sraj 62209908Sraj#include <dev/tsec/if_tsec.h> 63209908Sraj#include <dev/tsec/if_tsecreg.h> 64209908Sraj 65209908Sraj#include "miibus_if.h" 66209908Sraj 67209908Sraj#define TSEC_RID_TXIRQ 0 68209908Sraj#define TSEC_RID_RXIRQ 1 69209908Sraj#define TSEC_RID_ERRIRQ 2 70209908Sraj 71209908Srajstatic int tsec_fdt_probe(device_t dev); 72209908Srajstatic int tsec_fdt_attach(device_t dev); 73209908Srajstatic int tsec_fdt_detach(device_t dev); 74209908Srajstatic int tsec_setup_intr(struct tsec_softc *sc, struct resource **ires, 75209908Sraj void **ihand, int *irid, driver_intr_t handler, const char *iname); 76209908Srajstatic void tsec_release_intr(struct tsec_softc *sc, struct resource *ires, 77209908Sraj void *ihand, int irid, const char *iname); 78209908Sraj 79209908Srajstatic device_method_t tsec_methods[] = { 80209908Sraj /* Device interface */ 81209908Sraj DEVMETHOD(device_probe, tsec_fdt_probe), 82209908Sraj DEVMETHOD(device_attach, tsec_fdt_attach), 83209908Sraj DEVMETHOD(device_detach, tsec_fdt_detach), 84209908Sraj 85209908Sraj DEVMETHOD(device_shutdown, tsec_shutdown), 86209908Sraj DEVMETHOD(device_suspend, tsec_suspend), 87209908Sraj DEVMETHOD(device_resume, tsec_resume), 88209908Sraj 89209908Sraj /* MII interface */ 90209908Sraj DEVMETHOD(miibus_readreg, tsec_miibus_readreg), 91209908Sraj DEVMETHOD(miibus_writereg, tsec_miibus_writereg), 92209908Sraj DEVMETHOD(miibus_statchg, tsec_miibus_statchg), 93229093Shselasky 94229093Shselasky DEVMETHOD_END 95209908Sraj}; 96209908Sraj 97209908Srajstatic driver_t tsec_fdt_driver = { 98209908Sraj "tsec", 99209908Sraj tsec_methods, 100209908Sraj sizeof(struct tsec_softc), 101209908Sraj}; 102209908Sraj 103209908SrajDRIVER_MODULE(tsec, simplebus, tsec_fdt_driver, tsec_devclass, 0, 0); 104209908SrajMODULE_DEPEND(tsec, simplebus, 1, 1, 1); 105209908SrajMODULE_DEPEND(tsec, ether, 1, 1, 1); 106209908Sraj 107209908Srajstatic int 108209908Srajtsec_fdt_probe(device_t dev) 109209908Sraj{ 110209908Sraj struct tsec_softc *sc; 111209908Sraj uint32_t id; 112209908Sraj 113209908Sraj if (!ofw_bus_is_compatible(dev, "gianfar")) 114209908Sraj return (ENXIO); 115209908Sraj 116209908Sraj sc = device_get_softc(dev); 117209908Sraj 118209908Sraj sc->sc_rrid = 0; 119209908Sraj sc->sc_rres = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &sc->sc_rrid, 120209908Sraj RF_ACTIVE); 121209908Sraj if (sc->sc_rres == NULL) 122209908Sraj return (ENXIO); 123209908Sraj 124209908Sraj sc->sc_bas.bsh = rman_get_bushandle(sc->sc_rres); 125209908Sraj sc->sc_bas.bst = rman_get_bustag(sc->sc_rres); 126209908Sraj 127209908Sraj /* Check if we are eTSEC (enhanced TSEC) */ 128209908Sraj id = TSEC_READ(sc, TSEC_REG_ID); 129209908Sraj sc->is_etsec = ((id >> 16) == TSEC_ETSEC_ID) ? 1 : 0; 130209908Sraj id |= TSEC_READ(sc, TSEC_REG_ID2); 131209908Sraj 132209908Sraj bus_release_resource(dev, SYS_RES_MEMORY, sc->sc_rrid, sc->sc_rres); 133209908Sraj 134209908Sraj if (id == 0) { 135209908Sraj device_printf(dev, "could not identify TSEC type\n"); 136209908Sraj return (ENXIO); 137209908Sraj } 138209908Sraj 139209908Sraj if (sc->is_etsec) 140209908Sraj device_set_desc(dev, "Enhanced Three-Speed Ethernet Controller"); 141209908Sraj else 142209908Sraj device_set_desc(dev, "Three-Speed Ethernet Controller"); 143209908Sraj 144209908Sraj return (BUS_PROBE_DEFAULT); 145209908Sraj} 146209908Sraj 147209908Srajstatic int 148209908Srajtsec_fdt_attach(device_t dev) 149209908Sraj{ 150209908Sraj struct tsec_softc *sc; 151209908Sraj int error = 0; 152209908Sraj 153209908Sraj sc = device_get_softc(dev); 154209908Sraj sc->dev = dev; 155209908Sraj sc->node = ofw_bus_get_node(dev); 156209908Sraj 157209908Sraj /* Get phy address from fdt */ 158233015Sraj if (fdt_get_phyaddr(sc->node, sc->dev, &sc->phyaddr, 159233015Sraj (void **)&sc->phy_sc) != 0) 160209908Sraj return (ENXIO); 161209908Sraj 162209908Sraj /* Init timer */ 163209908Sraj callout_init(&sc->tsec_callout, 1); 164209908Sraj 165209908Sraj /* Init locks */ 166209908Sraj mtx_init(&sc->transmit_lock, device_get_nameunit(dev), "TSEC TX lock", 167209908Sraj MTX_DEF); 168209908Sraj mtx_init(&sc->receive_lock, device_get_nameunit(dev), "TSEC RX lock", 169209908Sraj MTX_DEF); 170209908Sraj mtx_init(&sc->ic_lock, device_get_nameunit(dev), "TSEC IC lock", 171209908Sraj MTX_DEF); 172209908Sraj 173209908Sraj /* Allocate IO memory for TSEC registers */ 174209908Sraj sc->sc_rrid = 0; 175209908Sraj sc->sc_rres = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &sc->sc_rrid, 176209908Sraj RF_ACTIVE); 177209908Sraj if (sc->sc_rres == NULL) { 178209908Sraj device_printf(dev, "could not allocate IO memory range!\n"); 179209908Sraj goto fail1; 180209908Sraj } 181209908Sraj sc->sc_bas.bsh = rman_get_bushandle(sc->sc_rres); 182209908Sraj sc->sc_bas.bst = rman_get_bustag(sc->sc_rres); 183209908Sraj 184209908Sraj /* TSEC attach */ 185209908Sraj if (tsec_attach(sc) != 0) { 186209908Sraj device_printf(dev, "could not be configured\n"); 187209908Sraj goto fail2; 188209908Sraj } 189209908Sraj 190209908Sraj /* Set up interrupts (TX/RX/ERR) */ 191209908Sraj sc->sc_transmit_irid = TSEC_RID_TXIRQ; 192209908Sraj error = tsec_setup_intr(sc, &sc->sc_transmit_ires, 193209908Sraj &sc->sc_transmit_ihand, &sc->sc_transmit_irid, 194209908Sraj tsec_transmit_intr, "TX"); 195209908Sraj if (error) 196209908Sraj goto fail2; 197209908Sraj 198209908Sraj sc->sc_receive_irid = TSEC_RID_RXIRQ; 199209908Sraj error = tsec_setup_intr(sc, &sc->sc_receive_ires, 200209908Sraj &sc->sc_receive_ihand, &sc->sc_receive_irid, 201209908Sraj tsec_receive_intr, "RX"); 202209908Sraj if (error) 203209908Sraj goto fail3; 204209908Sraj 205209908Sraj sc->sc_error_irid = TSEC_RID_ERRIRQ; 206209908Sraj error = tsec_setup_intr(sc, &sc->sc_error_ires, 207209908Sraj &sc->sc_error_ihand, &sc->sc_error_irid, 208209908Sraj tsec_error_intr, "ERR"); 209209908Sraj if (error) 210209908Sraj goto fail4; 211209908Sraj 212209908Sraj return (0); 213209908Sraj 214209908Srajfail4: 215209908Sraj tsec_release_intr(sc, sc->sc_receive_ires, sc->sc_receive_ihand, 216209908Sraj sc->sc_receive_irid, "RX"); 217209908Srajfail3: 218209908Sraj tsec_release_intr(sc, sc->sc_transmit_ires, sc->sc_transmit_ihand, 219209908Sraj sc->sc_transmit_irid, "TX"); 220209908Srajfail2: 221209908Sraj bus_release_resource(dev, SYS_RES_MEMORY, sc->sc_rrid, sc->sc_rres); 222209908Srajfail1: 223209908Sraj mtx_destroy(&sc->receive_lock); 224209908Sraj mtx_destroy(&sc->transmit_lock); 225209908Sraj return (ENXIO); 226209908Sraj} 227209908Sraj 228209908Srajstatic int 229209908Srajtsec_setup_intr(struct tsec_softc *sc, struct resource **ires, void **ihand, 230209908Sraj int *irid, driver_intr_t handler, const char *iname) 231209908Sraj{ 232209908Sraj int error; 233209908Sraj 234209908Sraj *ires = bus_alloc_resource_any(sc->dev, SYS_RES_IRQ, irid, RF_ACTIVE); 235209908Sraj if (*ires == NULL) { 236209908Sraj device_printf(sc->dev, "could not allocate %s IRQ\n", iname); 237209908Sraj return (ENXIO); 238209908Sraj } 239209908Sraj error = bus_setup_intr(sc->dev, *ires, INTR_TYPE_NET | INTR_MPSAFE, 240209908Sraj NULL, handler, sc, ihand); 241209908Sraj if (error) { 242209908Sraj device_printf(sc->dev, "failed to set up %s IRQ\n", iname); 243209908Sraj if (bus_release_resource(sc->dev, SYS_RES_IRQ, *irid, *ires)) 244209908Sraj device_printf(sc->dev, "could not release %s IRQ\n", iname); 245209908Sraj *ires = NULL; 246209908Sraj return (error); 247209908Sraj } 248209908Sraj return (0); 249209908Sraj} 250209908Sraj 251209908Srajstatic void 252209908Srajtsec_release_intr(struct tsec_softc *sc, struct resource *ires, void *ihand, 253209908Sraj int irid, const char *iname) 254209908Sraj{ 255209908Sraj int error; 256209908Sraj 257209908Sraj if (ires == NULL) 258209908Sraj return; 259209908Sraj 260209908Sraj error = bus_teardown_intr(sc->dev, ires, ihand); 261209908Sraj if (error) 262209908Sraj device_printf(sc->dev, "bus_teardown_intr() failed for %s intr" 263209908Sraj ", error %d\n", iname, error); 264209908Sraj 265209908Sraj error = bus_release_resource(sc->dev, SYS_RES_IRQ, irid, ires); 266209908Sraj if (error) 267209908Sraj device_printf(sc->dev, "bus_release_resource() failed for %s " 268209908Sraj "intr, error %d\n", iname, error); 269209908Sraj} 270209908Sraj 271209908Srajstatic int 272209908Srajtsec_fdt_detach(device_t dev) 273209908Sraj{ 274209908Sraj struct tsec_softc *sc; 275209908Sraj int error; 276209908Sraj 277209908Sraj sc = device_get_softc(dev); 278209908Sraj 279209908Sraj /* Wait for stopping watchdog */ 280209908Sraj callout_drain(&sc->tsec_callout); 281209908Sraj 282209908Sraj /* Stop and release all interrupts */ 283209908Sraj tsec_release_intr(sc, sc->sc_transmit_ires, sc->sc_transmit_ihand, 284209908Sraj sc->sc_transmit_irid, "TX"); 285209908Sraj tsec_release_intr(sc, sc->sc_receive_ires, sc->sc_receive_ihand, 286209908Sraj sc->sc_receive_irid, "RX"); 287209908Sraj tsec_release_intr(sc, sc->sc_error_ires, sc->sc_error_ihand, 288209908Sraj sc->sc_error_irid, "ERR"); 289209908Sraj 290209908Sraj /* TSEC detach */ 291209908Sraj tsec_detach(sc); 292209908Sraj 293209908Sraj /* Free IO memory handler */ 294209908Sraj if (sc->sc_rres) { 295209908Sraj error = bus_release_resource(dev, SYS_RES_MEMORY, sc->sc_rrid, 296209908Sraj sc->sc_rres); 297209908Sraj if (error) 298209908Sraj device_printf(dev, "bus_release_resource() failed for" 299209908Sraj " IO memory, error %d\n", error); 300209908Sraj } 301209908Sraj 302209908Sraj /* Destroy locks */ 303209908Sraj mtx_destroy(&sc->receive_lock); 304209908Sraj mtx_destroy(&sc->transmit_lock); 305209908Sraj mtx_destroy(&sc->ic_lock); 306209908Sraj return (0); 307209908Sraj} 308209908Sraj 309209908Srajvoid 310209908Srajtsec_get_hwaddr(struct tsec_softc *sc, uint8_t *addr) 311209908Sraj{ 312209908Sraj union { 313209908Sraj uint32_t reg[2]; 314209908Sraj uint8_t addr[6]; 315218050Smarcel } hw; 316209908Sraj int i; 317209908Sraj 318218050Smarcel hw.reg[0] = hw.reg[1] = 0; 319218050Smarcel 320218050Smarcel /* Retrieve the hardware address from the device tree. */ 321218050Smarcel i = OF_getprop(sc->node, "local-mac-address", (void *)hw.addr, 6); 322218050Smarcel if (i == 6 && (hw.reg[0] != 0 || hw.reg[1] != 0)) { 323218050Smarcel bcopy(hw.addr, addr, 6); 324209908Sraj return; 325209908Sraj } 326209908Sraj 327209908Sraj /* 328209908Sraj * Fall back -- use the currently programmed address in the hope that 329209908Sraj * it was set be firmware... 330209908Sraj */ 331218050Smarcel hw.reg[0] = TSEC_READ(sc, TSEC_REG_MACSTNADDR1); 332218050Smarcel hw.reg[1] = TSEC_READ(sc, TSEC_REG_MACSTNADDR2); 333209908Sraj for (i = 0; i < 6; i++) 334218050Smarcel addr[5-i] = hw.addr[i]; 335209908Sraj} 336