if_le_lebuffer.c revision 183337
1116742Ssam/*- 2116904Ssam * Copyright (c) 2006 Marius Strobl <marius@FreeBSD.org> 3186904Ssam * All rights reserved. 4116742Ssam * 5116742Ssam * Redistribution and use in source and binary forms, with or without 6116742Ssam * modification, are permitted provided that the following conditions 7116742Ssam * are met: 8116742Ssam * 1. Redistributions of source code must retain the above copyright 9116742Ssam * notice, this list of conditions and the following disclaimer. 10116904Ssam * 2. Redistributions in binary form must reproduce the above copyright 11116904Ssam * notice, this list of conditions and the following disclaimer in the 12116904Ssam * documentation and/or other materials provided with the distribution. 13116904Ssam * 14116742Ssam * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15116904Ssam * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16116904Ssam * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17116904Ssam * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 18116904Ssam * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19116904Ssam * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20116904Ssam * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21116904Ssam * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22116904Ssam * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23116904Ssam * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24116904Ssam * SUCH DAMAGE. 25116742Ssam */ 26116742Ssam 27116742Ssam#include <sys/cdefs.h> 28116742Ssam__FBSDID("$FreeBSD: head/sys/dev/le/if_le_lebuffer.c 183337 2008-09-24 21:26:46Z marius $"); 29116742Ssam 30116742Ssam#include <sys/param.h> 31116742Ssam#include <sys/systm.h> 32116742Ssam#include <sys/bus.h> 33178354Ssam#include <sys/endian.h> 34116742Ssam#include <sys/kernel.h> 35116742Ssam#include <sys/lock.h> 36191746Sthompsa#include <sys/module.h> 37116742Ssam#include <sys/mutex.h> 38295126Sglebius#include <sys/resource.h> 39116742Ssam#include <sys/rman.h> 40287197Sglebius#include <sys/socket.h> 41116742Ssam 42283529Sglebius#include <dev/ofw/ofw_bus.h> 43283529Sglebius 44116742Ssam#include <machine/bus.h> 45257176Sglebius#include <machine/ofw_machdep.h> 46178354Ssam#include <machine/resource.h> 47116742Ssam 48178354Ssam#include <net/ethernet.h> 49116742Ssam#include <net/if.h> 50116742Ssam#include <net/if_media.h> 51116742Ssam 52178354Ssam#include <dev/le/lancereg.h> 53190391Ssam#include <dev/le/lancevar.h> 54190391Ssam#include <dev/le/am7990reg.h> 55190391Ssam#include <dev/le/am7990var.h> 56206358Srpaulo 57116742Ssam/* 58116742Ssam * LANCE registers 59116742Ssam */ 60178955Ssam#define LEREG1_RDP 0 /* Register Data port */ 61178955Ssam#define LEREG1_RAP 2 /* Register Address port */ 62178955Ssam 63178955Ssamstruct le_lebuffer_softc { 64178955Ssam struct am7990_softc sc_am7990; /* glue to MI code */ 65178955Ssam 66178955Ssam struct resource *sc_bres; 67178955Ssam 68178955Ssam struct resource *sc_rres; 69188782Ssam 70188782Ssam struct resource *sc_ires; 71178955Ssam void *sc_ih; 72178955Ssam}; 73116742Ssam 74178957Ssamstatic devclass_t le_lebuffer_devclass; 75178957Ssam 76178957Ssamstatic device_probe_t le_lebuffer_probe; 77178957Ssamstatic device_attach_t le_lebuffer_attach; 78178957Ssamstatic device_detach_t le_lebuffer_detach; 79178957Ssamstatic device_resume_t le_buffer_resume; 80178957Ssamstatic device_suspend_t le_buffer_suspend; 81178957Ssam 82195618Srpaulostatic device_method_t le_lebuffer_methods[] = { 83195618Srpaulo /* Device interface */ 84195618Srpaulo DEVMETHOD(device_probe, le_lebuffer_probe), 85178957Ssam DEVMETHOD(device_attach, le_lebuffer_attach), 86178957Ssam DEVMETHOD(device_detach, le_lebuffer_detach), 87283566Sglebius /* We can just use the suspend method here. */ 88178354Ssam DEVMETHOD(device_shutdown, le_buffer_suspend), 89116742Ssam DEVMETHOD(device_suspend, le_buffer_suspend), 90178354Ssam DEVMETHOD(device_resume, le_buffer_resume), 91193655Ssam 92178354Ssam { 0, 0 } 93178354Ssam}; 94178354Ssam 95178354SsamDEFINE_CLASS_0(le, le_lebuffer_driver, le_lebuffer_methods, 96178354Ssam sizeof(struct le_lebuffer_softc)); 97178354SsamDRIVER_MODULE(le, lebuffer, le_lebuffer_driver, le_lebuffer_devclass, 0, 0); 98283568SglebiusMODULE_DEPEND(le, ether, 1, 1, 1); 99178354SsamMODULE_DEPEND(le, lebuffer, 1, 1, 1); 100178354Ssam 101178354Ssam/* 102164645Ssam * Media types supported 103164645Ssam */ 104164645Ssamstatic const int le_lebuffer_media[] = { 105164645Ssam IFM_MAKEWORD(IFM_ETHER, IFM_10_T, 0, 0) 106164645Ssam}; 107164645Ssam#define NLEMEDIA \ 108165569Ssam (sizeof(le_lebuffer_media) / sizeof(le_lebuffer_media[0])) 109165569Ssam 110165569Ssamstatic void le_lebuffer_wrcsr(struct lance_softc *, uint16_t, uint16_t); 111165569Ssamstatic uint16_t le_lebuffer_rdcsr(struct lance_softc *, uint16_t); 112164645Ssamstatic void le_lebuffer_copytodesc(struct lance_softc *, void *, int, int); 113164645Ssamstatic void le_lebuffer_copyfromdesc(struct lance_softc *, void *, int, int); 114164645Ssamstatic void le_lebuffer_copytobuf(struct lance_softc *, void *, int, int); 115164645Ssamstatic void le_lebuffer_copyfrombuf(struct lance_softc *, void *, int, int); 116164645Ssamstatic void le_lebuffer_zerobuf(struct lance_softc *, int, int); 117164645Ssam 118164645Ssamstatic void 119140915Ssamle_lebuffer_wrcsr(struct lance_softc *sc, uint16_t port, uint16_t val) 120165569Ssam{ 121165569Ssam struct le_lebuffer_softc *lesc = (struct le_lebuffer_softc *)sc; 122165569Ssam 123165569Ssam bus_write_2(lesc->sc_rres, LEREG1_RAP, port); 124287197Sglebius bus_barrier(lesc->sc_rres, LEREG1_RAP, 2, BUS_SPACE_BARRIER_WRITE); 125165569Ssam bus_write_2(lesc->sc_rres, LEREG1_RDP, val); 126116742Ssam} 127165569Ssam 128188782Ssamstatic uint16_t 129165574Ssamle_lebuffer_rdcsr(struct lance_softc *sc, uint16_t port) 130165569Ssam{ 131116742Ssam struct le_lebuffer_softc *lesc = (struct le_lebuffer_softc *)sc; 132116742Ssam 133116742Ssam bus_write_2(lesc->sc_rres, LEREG1_RAP, port); 134186107Ssam bus_barrier(lesc->sc_rres, LEREG1_RAP, 2, BUS_SPACE_BARRIER_WRITE); 135170530Ssam return (bus_read_2(lesc->sc_rres, LEREG1_RDP)); 136116742Ssam} 137178354Ssam 138167468Ssam/* 139170530Ssam * It turns out that using bus_space(9) to access the buffers and the 140116742Ssam * descriptors yields way more throughput than accessing them via the 141170530Ssam * KVA returned by rman_get_virtual(9). The descriptor rings can be 142187796Ssam * accessed using 8-bit up to 64-bit operations while the buffers can 143187796Ssam * be only accessed using 8-bit and 16-bit operations. 144187796Ssam * NB: For whatever reason setting LE_C3_BSWP has no effect with at 145187796Ssam * least the 501-2981 (although their 'busmaster-regval' property 146187796Ssam * indicates to set LE_C3_BSWP also for these cards), so we need 147187796Ssam * to manually byte swap access to the buffers, i.e. the accesses 148187796Ssam * going through the RX/TX FIFOs. 149187796Ssam */ 150187796Ssam 151187796Ssamstatic void 152187796Ssamle_lebuffer_copytodesc(struct lance_softc *sc, void *fromv, int off, int len) 153187796Ssam{ 154187796Ssam struct le_lebuffer_softc *lesc = (struct le_lebuffer_softc *)sc; 155187796Ssam caddr_t from = fromv; 156187796Ssam 157170530Ssam for (; len >= 8; len -= 8, off += 8, from += 8) 158170530Ssam bus_write_8(lesc->sc_bres, off, be64dec(from)); 159170530Ssam for (; len >= 4; len -= 4, off += 4, from += 4) 160170530Ssam bus_write_4(lesc->sc_bres, off, be32dec(from)); 161170530Ssam for (; len >= 2; len -= 2, off += 2, from += 2) 162170530Ssam bus_write_2(lesc->sc_bres, off, be16dec(from)); 163170530Ssam if (len == 1) 164170530Ssam bus_write_1(lesc->sc_bres, off, *from); 165170530Ssam} 166170530Ssam 167170530Ssamstatic void 168170530Ssamle_lebuffer_copyfromdesc(struct lance_softc *sc, void *tov, int off, int len) 169170530Ssam{ 170170530Ssam struct le_lebuffer_softc *lesc = (struct le_lebuffer_softc *)sc; 171170530Ssam caddr_t to = tov; 172170530Ssam 173170530Ssam for (; len >= 8; len -= 8, off += 8, to += 8) 174170530Ssam be64enc(to, 175188782Ssam bus_read_8(lesc->sc_bres, off)); 176188782Ssam for (; len >= 4; len -= 4, off += 4, to += 4) 177188782Ssam be32enc(to, 178188782Ssam bus_read_4(lesc->sc_bres, off)); 179170530Ssam for (; len >= 2; len -= 2, off += 2, to += 2) 180170530Ssam be16enc(to, 181170530Ssam bus_read_2(lesc->sc_bres, off)); 182170530Ssam if (len == 1) 183116742Ssam *to = bus_read_1(lesc->sc_bres, off); 184170530Ssam} 185170530Ssam 186170530Ssamstatic void 187164645Ssamle_lebuffer_copytobuf(struct lance_softc *sc, void *fromv, int off, int len) 188178354Ssam{ 189178354Ssam struct le_lebuffer_softc *lesc = (struct le_lebuffer_softc *)sc; 190178354Ssam caddr_t from = fromv; 191178354Ssam 192170530Ssam for (; len >= 2; len -= 2, off += 2, from += 2) 193172233Ssam bus_write_2(lesc->sc_bres, off, le16dec(from)); 194178354Ssam if (len == 1) 195170530Ssam bus_write_1(lesc->sc_bres, off + 1, *from); 196170530Ssam} 197190532Ssam 198170530Ssamstatic void 199164645Ssamle_lebuffer_copyfrombuf(struct lance_softc *sc, void *tov, int off, int len) 200165569Ssam{ 201165569Ssam struct le_lebuffer_softc *lesc = (struct le_lebuffer_softc *)sc; 202165569Ssam caddr_t to = tov; 203165569Ssam 204165569Ssam for (; len >= 2; len -= 2, off += 2, to += 2) 205187897Ssam le16enc(to, 206188782Ssam bus_read_2(lesc->sc_bres, off)); 207188782Ssam if (len == 1) 208188774Ssam *to = bus_read_1(lesc->sc_bres, off + 1); 209188774Ssam} 210165569Ssam 211165569Ssamstatic void 212219596Sbschmidtle_lebuffer_zerobuf(struct lance_softc *sc, int off, int len) 213219596Sbschmidt{ 214219596Sbschmidt struct le_lebuffer_softc *lesc = (struct le_lebuffer_softc *)sc; 215219596Sbschmidt 216219596Sbschmidt for (; len >= 2; len -= 2, off += 2) 217219596Sbschmidt bus_write_2(lesc->sc_bres, off, 0); 218219596Sbschmidt if (len == 1) 219219596Sbschmidt bus_write_1(lesc->sc_bres, off + 1, 0); 220219596Sbschmidt} 221165569Ssam 222165569Ssamstatic int 223165569Ssamle_lebuffer_probe(device_t dev) 224165569Ssam{ 225165569Ssam 226165569Ssam if (strcmp(ofw_bus_get_name(dev), "le") == 0) { 227178354Ssam device_set_desc(dev, "LANCE Ethernet"); 228283540Sglebius return (BUS_PROBE_DEFAULT); 229178354Ssam } 230283540Sglebius return (ENXIO); 231283540Sglebius} 232178354Ssam 233178354Ssamstatic int 234178354Ssamle_lebuffer_attach(device_t dev) 235283540Sglebius{ 236178354Ssam struct le_lebuffer_softc *lesc; 237283540Sglebius struct lance_softc *sc; 238283540Sglebius int error, i; 239178354Ssam 240178354Ssam lesc = device_get_softc(dev); 241178521Ssam sc = &lesc->sc_am7990.lsc; 242233452Sadrian 243233452Sadrian LE_LOCK_INIT(sc, device_get_nameunit(dev)); 244233452Sadrian 245283529Sglebius /* 246233452Sadrian * The "register space" of the parent is just a buffer where the 247233452Sadrian * the LANCE descriptor rings and the RX/TX buffers can be stored. 248283529Sglebius */ 249283529Sglebius i = 0; 250283529Sglebius lesc->sc_bres = bus_alloc_resource_any(device_get_parent(dev), 251283529Sglebius SYS_RES_MEMORY, &i, RF_ACTIVE); 252283529Sglebius if (lesc->sc_bres == NULL) { 253283529Sglebius device_printf(dev, "cannot allocate LANCE buffer\n"); 254283529Sglebius error = ENXIO; 255283529Sglebius goto fail_mtx; 256283529Sglebius } 257283529Sglebius 258283529Sglebius /* Allocate LANCE registers. */ 259283529Sglebius i = 0; 260283529Sglebius lesc->sc_rres = bus_alloc_resource_any(dev, SYS_RES_MEMORY, 261287197Sglebius &i, RF_ACTIVE); 262287197Sglebius if (lesc->sc_rres == NULL) { 263287197Sglebius device_printf(dev, "cannot allocate LANCE registers\n"); 264287197Sglebius error = ENXIO; 265287197Sglebius goto fail_bres; 266287197Sglebius } 267287197Sglebius 268287197Sglebius /* Allocate LANCE interrupt. */ 269296114Savos i = 0; 270287197Sglebius if ((lesc->sc_ires = bus_alloc_resource_any(dev, SYS_RES_IRQ, 271287197Sglebius &i, RF_SHAREABLE | RF_ACTIVE)) == NULL) { 272287197Sglebius device_printf(dev, "cannot allocate interrupt\n"); 273296114Savos error = ENXIO; 274296114Savos goto fail_rres; 275296114Savos } 276296114Savos 277296114Savos /* 278287197Sglebius * LANCE view is offset by buffer location. 279287197Sglebius * Note that we don't use sc->sc_mem. 280287197Sglebius */ 281296114Savos sc->sc_addr = 0; 282287197Sglebius sc->sc_memsize = rman_get_size(lesc->sc_bres); 283287197Sglebius sc->sc_flags = 0; 284287197Sglebius 285296114Savos /* That old black magic... */ 286296114Savos if (OF_getprop(ofw_bus_get_node(dev), "busmaster-regval", 287287197Sglebius &sc->sc_conf3, sizeof(sc->sc_conf3)) == -1) 288287197Sglebius sc->sc_conf3 = LE_C3_ACON | LE_C3_BCON; 289287197Sglebius /* 290287197Sglebius * Make sure LE_C3_BSWP is cleared so that for cards where 291287197Sglebius * that flag actually works le_lebuffer_copy{from,to}buf() 292287197Sglebius * don't fail... 293287197Sglebius */ 294178354Ssam sc->sc_conf3 &= ~LE_C3_BSWP; 295178354Ssam 296178354Ssam OF_getetheraddr(dev, sc->sc_enaddr); 297178354Ssam 298165569Ssam sc->sc_copytodesc = le_lebuffer_copytodesc; 299287197Sglebius sc->sc_copyfromdesc = le_lebuffer_copyfromdesc; 300165569Ssam sc->sc_copytobuf = le_lebuffer_copytobuf; 301165569Ssam sc->sc_copyfrombuf = le_lebuffer_copyfrombuf; 302283529Sglebius sc->sc_zerobuf = le_lebuffer_zerobuf; 303283529Sglebius 304178354Ssam sc->sc_rdcsr = le_lebuffer_rdcsr; 305191746Sthompsa sc->sc_wrcsr = le_lebuffer_wrcsr; 306191746Sthompsa sc->sc_hwreset = NULL; 307191746Sthompsa sc->sc_hwinit = NULL; 308191746Sthompsa sc->sc_hwintr = NULL; 309230447Sadrian sc->sc_nocarrier = NULL; 310283565Sglebius sc->sc_mediachange = NULL; 311283568Sglebius sc->sc_mediastatus = NULL; 312283568Sglebius sc->sc_supmedia = le_lebuffer_media; 313165569Ssam sc->sc_nsupmedia = NLEMEDIA; 314165569Ssam sc->sc_defaultmedia = le_lebuffer_media[0]; 315165569Ssam 316165569Ssam error = am7990_config(&lesc->sc_am7990, device_get_name(dev), 317165569Ssam device_get_unit(dev)); 318287197Sglebius if (error != 0) { 319170530Ssam device_printf(dev, "cannot attach Am7990\n"); 320178354Ssam goto fail_ires; 321178354Ssam } 322233452Sadrian 323116742Ssam error = bus_setup_intr(dev, lesc->sc_ires, INTR_TYPE_NET | INTR_MPSAFE, 324195379Ssam NULL, am7990_intr, sc, &lesc->sc_ih); 325155688Ssam if (error != 0) { 326155688Ssam device_printf(dev, "cannot set up interrupt\n"); 327138568Ssam goto fail_am7990; 328138568Ssam } 329170530Ssam 330138568Ssam return (0); 331170530Ssam 332138568Ssam fail_am7990: 333190391Ssam am7990_detach(&lesc->sc_am7990); 334190391Ssam fail_ires: 335190391Ssam bus_release_resource(dev, SYS_RES_IRQ, 336170530Ssam rman_get_rid(lesc->sc_ires), lesc->sc_ires); 337170530Ssam fail_rres: 338178354Ssam bus_release_resource(dev, SYS_RES_MEMORY, 339193843Ssam rman_get_rid(lesc->sc_rres), lesc->sc_rres); 340138568Ssam fail_bres: 341178354Ssam bus_release_resource(device_get_parent(dev), SYS_RES_MEMORY, 342138568Ssam rman_get_rid(lesc->sc_bres), lesc->sc_bres); 343287197Sglebius fail_mtx: 344287197Sglebius LE_LOCK_DESTROY(sc); 345287197Sglebius return (error); 346116742Ssam} 347116742Ssam 348178354Ssamstatic int 349178354Ssamle_lebuffer_detach(device_t dev) 350178354Ssam{ 351178354Ssam struct le_lebuffer_softc *lesc; 352178354Ssam struct lance_softc *sc; 353178354Ssam 354116742Ssam lesc = device_get_softc(dev); 355138568Ssam sc = &lesc->sc_am7990.lsc; 356116742Ssam 357178354Ssam bus_teardown_intr(dev, lesc->sc_ires, lesc->sc_ih); 358116742Ssam am7990_detach(&lesc->sc_am7990); 359287197Sglebius bus_release_resource(dev, SYS_RES_IRQ, 360287197Sglebius rman_get_rid(lesc->sc_ires), lesc->sc_ires); 361287197Sglebius bus_release_resource(dev, SYS_RES_MEMORY, 362193337Ssam rman_get_rid(lesc->sc_rres), lesc->sc_rres); 363290058Savos bus_release_resource(device_get_parent(dev), SYS_RES_MEMORY, 364290058Savos rman_get_rid(lesc->sc_bres), lesc->sc_bres); 365242149Sadrian LE_LOCK_DESTROY(sc); 366242149Sadrian 367242149Sadrian return (0); 368242149Sadrian} 369178354Ssam 370178354Ssamstatic int 371188533Sthompsale_buffer_suspend(device_t dev) 372138568Ssam{ 373138568Ssam struct le_lebuffer_softc *lesc; 374193843Ssam 375178354Ssam lesc = device_get_softc(dev); 376170530Ssam 377190391Ssam lance_suspend(&lesc->sc_am7990.lsc); 378190391Ssam 379190391Ssam return (0); 380170530Ssam} 381166012Ssam 382138568Ssamstatic int 383138568Ssamle_buffer_resume(device_t dev) 384170530Ssam{ 385138568Ssam struct le_lebuffer_softc *lesc; 386193337Ssam 387283568Sglebius lesc = device_get_softc(dev); 388283568Sglebius 389242149Sadrian lance_resume(&lesc->sc_am7990.lsc); 390191746Sthompsa 391248069Sadrian return (0); 392170530Ssam} 393178354Ssam