if_le_ledma.c revision 158663
1/* $NetBSD: if_le_ledma.c,v 1.26 2005/12/11 12:23:44 christos Exp $ */ 2 3/*- 4 * Copyright (c) 1997, 1998 The NetBSD Foundation, Inc. 5 * All rights reserved. 6 * 7 * This code is derived from software contributed to The NetBSD Foundation 8 * by Charles M. Hannum; Jason R. Thorpe of the Numerical Aerospace 9 * Simulation Facility, NASA Ames Research Center; Paul Kranenburg. 10 * 11 * Redistribution and use in source and binary forms, with or without 12 * modification, are permitted provided that the following conditions 13 * are met: 14 * 1. Redistributions of source code must retain the above copyright 15 * notice, this list of conditions and the following disclaimer. 16 * 2. Redistributions in binary form must reproduce the above copyright 17 * notice, this list of conditions and the following disclaimer in the 18 * documentation and/or other materials provided with the distribution. 19 * 3. All advertising materials mentioning features or use of this software 20 * must display the following acknowledgement: 21 * This product includes software developed by the NetBSD 22 * Foundation, Inc. and its contributors. 23 * 4. Neither the name of The NetBSD Foundation nor the names of its 24 * contributors may be used to endorse or promote products derived 25 * from this software without specific prior written permission. 26 * 27 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 28 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 29 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 30 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 31 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 32 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 33 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 34 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 35 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 36 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 37 * POSSIBILITY OF SUCH DAMAGE. 38 */ 39 40#include <sys/cdefs.h> 41__FBSDID("$FreeBSD: head/sys/dev/le/if_le_ledma.c 158663 2006-05-16 21:04:01Z marius $"); 42 43#include <sys/param.h> 44#include <sys/systm.h> 45#include <sys/bus.h> 46#include <sys/endian.h> 47#include <sys/kernel.h> 48#include <sys/lock.h> 49#include <sys/module.h> 50#include <sys/mutex.h> 51#include <sys/resource.h> 52#include <sys/rman.h> 53#include <sys/socket.h> 54 55#include <dev/ofw/ofw_bus.h> 56 57#include <machine/bus.h> 58#include <machine/ofw_machdep.h> 59#include <machine/resource.h> 60 61#include <net/ethernet.h> 62#include <net/if.h> 63#include <net/if_media.h> 64 65#include <sparc64/sbus/lsi64854reg.h> 66#include <sparc64/sbus/lsi64854var.h> 67 68#include <dev/le/lancereg.h> 69#include <dev/le/lancevar.h> 70#include <dev/le/am7990var.h> 71 72#define LEDMA_ALIGNMENT 8 /* ring desc. alignmet for NCR92C990 */ 73#define LEDMA_BOUNDARY (16*1024*1024) /* must not cross 16MB boundary */ 74#define LEDMA_MEMSIZE (16*1024) /* LANCE memory size */ 75#define LEREG1_RDP 0 /* Register Data Port */ 76#define LEREG1_RAP 2 /* Register Address Port */ 77 78struct le_dma_softc { 79 struct am7990_softc sc_am7990; /* glue to MI code */ 80 81 int sc_rrid; 82 struct resource *sc_rres; 83 bus_space_tag_t sc_regt; 84 bus_space_handle_t sc_regh; 85 86 int sc_irid; 87 struct resource *sc_ires; 88 void *sc_ih; 89 90 bus_dma_tag_t sc_dmat; 91 bus_dmamap_t sc_dmam; 92 bus_addr_t sc_laddr; /* LANCE DMA address */ 93 94 struct lsi64854_softc *sc_dma; /* pointer to DMA engine */ 95}; 96 97static device_probe_t le_dma_probe; 98static device_attach_t le_dma_attach; 99static device_detach_t le_dma_detach; 100static device_resume_t le_dma_resume; 101static device_suspend_t le_dma_suspend; 102 103static device_method_t le_dma_methods[] = { 104 /* Device interface */ 105 DEVMETHOD(device_probe, le_dma_probe), 106 DEVMETHOD(device_attach, le_dma_attach), 107 DEVMETHOD(device_detach, le_dma_detach), 108 /* We can just use the suspend method here. */ 109 DEVMETHOD(device_shutdown, le_dma_suspend), 110 DEVMETHOD(device_suspend, le_dma_suspend), 111 DEVMETHOD(device_resume, le_dma_resume), 112 113 { 0, 0 } 114}; 115 116DEFINE_CLASS_0(le, le_dma_driver, le_dma_methods, sizeof(struct le_dma_softc)); 117DRIVER_MODULE(le, dma, le_dma_driver, le_devclass, 0, 0); 118MODULE_DEPEND(le, ether, 1, 1, 1); 119 120/* 121 * Media types supported 122 */ 123static const int le_dma_supmedia[] = { 124 IFM_MAKEWORD(IFM_ETHER, IFM_AUTO, 0, 0), 125 IFM_MAKEWORD(IFM_ETHER, IFM_10_T, 0, 0), 126 IFM_MAKEWORD(IFM_ETHER, IFM_10_5, 0, 0) 127}; 128 129static void le_dma_wrcsr(struct lance_softc *, uint16_t, uint16_t); 130static uint16_t le_dma_rdcsr(struct lance_softc *, uint16_t); 131static void le_dma_setutp(struct lance_softc *); 132static void le_dma_setaui(struct lance_softc *); 133static int le_dma_supmediachange(struct lance_softc *); 134static void le_dma_supmediastatus(struct lance_softc *, struct ifmediareq *); 135static void le_dma_hwreset(struct lance_softc *); 136static int le_dma_hwintr(struct lance_softc *); 137static void le_dma_nocarrier(struct lance_softc *); 138static bus_dmamap_callback_t le_dma_dma_callback; 139 140static void 141le_dma_wrcsr(struct lance_softc *sc, uint16_t port, uint16_t val) 142{ 143 struct le_dma_softc *lesc = (struct le_dma_softc *)sc; 144 145 bus_space_write_2(lesc->sc_regt, lesc->sc_regh, LEREG1_RAP, port); 146 bus_space_barrier(lesc->sc_regt, lesc->sc_regh, LEREG1_RAP, 2, 147 BUS_SPACE_BARRIER_WRITE); 148 bus_space_write_2(lesc->sc_regt, lesc->sc_regh, LEREG1_RDP, val); 149} 150 151static uint16_t 152le_dma_rdcsr(struct lance_softc *sc, uint16_t port) 153{ 154 struct le_dma_softc *lesc = (struct le_dma_softc *)sc; 155 156 bus_space_write_2(lesc->sc_regt, lesc->sc_regh, LEREG1_RAP, port); 157 bus_space_barrier(lesc->sc_regt, lesc->sc_regh, LEREG1_RAP, 2, 158 BUS_SPACE_BARRIER_WRITE); 159 return (bus_space_read_2(lesc->sc_regt, lesc->sc_regh, LEREG1_RDP)); 160} 161 162static void 163le_dma_setutp(struct lance_softc *sc) 164{ 165 struct lsi64854_softc *dma = ((struct le_dma_softc *)sc)->sc_dma; 166 167 L64854_SCSR(dma, L64854_GCSR(dma) | E_TP_AUI); 168 DELAY(20000); /* We must not touch the LANCE chip for 20ms. */ 169} 170 171static void 172le_dma_setaui(struct lance_softc *sc) 173{ 174 struct lsi64854_softc *dma = ((struct le_dma_softc *)sc)->sc_dma; 175 176 L64854_SCSR(dma, L64854_GCSR(dma) & ~E_TP_AUI); 177 DELAY(20000); /* We must not touch the LANCE chip for 20ms. */ 178} 179 180static int 181le_dma_supmediachange(struct lance_softc *sc) 182{ 183 struct ifmedia *ifm = &sc->sc_media; 184 185 if (IFM_TYPE(ifm->ifm_media) != IFM_ETHER) 186 return (EINVAL); 187 188 /* 189 * Switch to the selected media. If autoselect is set, we don't 190 * really have to do anything. We'll switch to the other media 191 * when we detect loss of carrier. 192 */ 193 switch (IFM_SUBTYPE(ifm->ifm_media)) { 194 case IFM_10_T: 195 le_dma_setutp(sc); 196 break; 197 198 case IFM_10_5: 199 le_dma_setaui(sc); 200 break; 201 202 case IFM_AUTO: 203 break; 204 205 default: 206 return (EINVAL); 207 } 208 209 return (0); 210} 211 212static void 213le_dma_supmediastatus(struct lance_softc *sc, struct ifmediareq *ifmr) 214{ 215 struct lsi64854_softc *dma = ((struct le_dma_softc *)sc)->sc_dma; 216 217 /* 218 * Notify the world which media we're currently using. 219 */ 220 if (L64854_GCSR(dma) & E_TP_AUI) 221 ifmr->ifm_active = IFM_MAKEWORD(IFM_ETHER, IFM_10_T, 0, 0); 222 else 223 ifmr->ifm_active = IFM_MAKEWORD(IFM_ETHER, IFM_10_5, 0, 0); 224} 225 226static void 227le_dma_hwreset(struct lance_softc *sc) 228{ 229 struct le_dma_softc *lesc = (struct le_dma_softc *)sc; 230 struct lsi64854_softc *dma = lesc->sc_dma; 231 uint32_t aui_bit, csr; 232 233 /* 234 * Reset DMA channel. 235 */ 236 csr = L64854_GCSR(dma); 237 aui_bit = csr & E_TP_AUI; 238 DMA_RESET(dma); 239 240 /* Write bits 24-31 of Lance address. */ 241 bus_space_write_4(dma->sc_regt, dma->sc_regh, L64854_REG_ENBAR, 242 lesc->sc_laddr & 0xff000000); 243 244 DMA_ENINTR(dma); 245 246 /* 247 * Disable E-cache invalidates on chip writes. 248 * Retain previous cable selection bit. 249 */ 250 csr = L64854_GCSR(dma); 251 csr |= (E_DSBL_WR_INVAL | aui_bit); 252 L64854_SCSR(dma, csr); 253 DELAY(20000); /* We must not touch the LANCE chip for 20ms. */ 254} 255 256static int 257le_dma_hwintr(struct lance_softc *sc) 258{ 259 struct le_dma_softc *lesc = (struct le_dma_softc *)sc; 260 struct lsi64854_softc *dma = lesc->sc_dma; 261 262 return (DMA_INTR(dma)); 263} 264 265static void 266le_dma_nocarrier(struct lance_softc *sc) 267{ 268 struct le_dma_softc *lesc = (struct le_dma_softc *)sc; 269 270 /* 271 * Check if the user has requested a certain cable type, and 272 * if so, honor that request. 273 */ 274 275 if (L64854_GCSR(lesc->sc_dma) & E_TP_AUI) { 276 switch (IFM_SUBTYPE(sc->sc_media.ifm_media)) { 277 case IFM_10_5: 278 case IFM_AUTO: 279 if_printf(sc->sc_ifp, "lost carrier on UTP port, " 280 "switching to AUI port\n"); 281 le_dma_setaui(sc); 282 } 283 } else { 284 switch (IFM_SUBTYPE(sc->sc_media.ifm_media)) { 285 case IFM_10_T: 286 case IFM_AUTO: 287 if_printf(sc->sc_ifp, "lost carrier on AUI port, " 288 "switching to UTP port\n"); 289 le_dma_setutp(sc); 290 } 291 } 292} 293 294static void 295le_dma_dma_callback(void *xsc, bus_dma_segment_t *segs, int nsegs, int error) 296{ 297 struct le_dma_softc *lesc = (struct le_dma_softc *)xsc; 298 299 if (error != 0) 300 return; 301 KASSERT(nsegs == 1, ("%s: bad DMA segment count", __func__)); 302 lesc->sc_laddr = segs[0].ds_addr; 303} 304 305static int 306le_dma_probe(device_t dev) 307{ 308 309 if (strcmp(ofw_bus_get_name(dev), "le") == 0) { 310 device_set_desc(dev, "LANCE Ethernet"); 311 return (BUS_PROBE_DEFAULT); 312 } 313 return (ENXIO); 314} 315 316static int 317le_dma_attach(device_t dev) 318{ 319 struct le_dma_softc *lesc; 320 struct lsi64854_softc *dma; 321 struct lance_softc *sc; 322 int error; 323 324 lesc = device_get_softc(dev); 325 sc = &lesc->sc_am7990.lsc; 326 327 LE_LOCK_INIT(sc, device_get_nameunit(dev)); 328 329 /* 330 * Establish link to `ledma' device. 331 * XXX hackery. 332 */ 333 dma = (struct lsi64854_softc *)device_get_softc(device_get_parent(dev)); 334 lesc->sc_dma = dma; 335 lesc->sc_dma->sc_client = lesc; 336 337 lesc->sc_rrid = 0; 338 lesc->sc_rres = bus_alloc_resource_any(dev, SYS_RES_MEMORY, 339 &lesc->sc_rrid, RF_ACTIVE); 340 if (lesc->sc_rres == NULL) { 341 device_printf(dev, "cannot allocate registers\n"); 342 error = ENXIO; 343 goto fail_mtx; 344 } 345 lesc->sc_regt = rman_get_bustag(lesc->sc_rres); 346 lesc->sc_regh = rman_get_bushandle(lesc->sc_rres); 347 348 lesc->sc_irid = 0; 349 if ((lesc->sc_ires = bus_alloc_resource_any(dev, SYS_RES_IRQ, 350 &lesc->sc_irid, RF_SHAREABLE | RF_ACTIVE)) == NULL) { 351 device_printf(dev, "cannot allocate interrupt\n"); 352 error = ENXIO; 353 goto fail_rres; 354 } 355 356 sc->sc_memsize = LEDMA_MEMSIZE; 357 error = bus_dma_tag_create( 358 dma->sc_parent_dmat, /* parent */ 359 LEDMA_ALIGNMENT, /* alignment */ 360 LEDMA_BOUNDARY, /* boundary */ 361 BUS_SPACE_MAXADDR_32BIT, /* lowaddr */ 362 BUS_SPACE_MAXADDR, /* highaddr */ 363 NULL, NULL, /* filter, filterarg */ 364 sc->sc_memsize, /* maxsize */ 365 1, /* nsegments */ 366 sc->sc_memsize, /* maxsegsize */ 367 BUS_DMA_WAITOK, /* flags */ 368 NULL, NULL, /* lockfunc, lockarg */ 369 &lesc->sc_dmat); 370 if (error != 0) { 371 device_printf(dev, "cannot allocate buffer DMA tag\n"); 372 goto fail_ires; 373 } 374 375 error = bus_dmamem_alloc(lesc->sc_dmat, (void **)&sc->sc_mem, 376 BUS_DMA_WAITOK | BUS_DMA_COHERENT, &lesc->sc_dmam); 377 if (error != 0) { 378 device_printf(dev, "cannot allocate DMA buffer memory\n"); 379 goto fail_dtag; 380 } 381 382 lesc->sc_laddr = 0; 383 error = bus_dmamap_load(lesc->sc_dmat, lesc->sc_dmam, sc->sc_mem, 384 sc->sc_memsize, le_dma_dma_callback, lesc, 0); 385 if (error != 0 || lesc->sc_laddr == 0) { 386 device_printf(dev, "cannot load DMA buffer map\n"); 387 goto fail_dmem; 388 } 389 390 sc->sc_addr = lesc->sc_laddr & 0xffffff; 391 sc->sc_flags = 0; 392 sc->sc_conf3 = LE_C3_BSWP | LE_C3_ACON | LE_C3_BCON; 393 394 sc->sc_mediachange = le_dma_supmediachange; 395 sc->sc_mediastatus = le_dma_supmediastatus; 396 sc->sc_supmedia = le_dma_supmedia; 397 sc->sc_nsupmedia = sizeof(le_dma_supmedia) / sizeof(le_dma_supmedia[0]); 398 sc->sc_defaultmedia = le_dma_supmedia[0]; 399 400 OF_getetheraddr(dev, sc->sc_enaddr); 401 402 sc->sc_copytodesc = lance_copytobuf_contig; 403 sc->sc_copyfromdesc = lance_copyfrombuf_contig; 404 sc->sc_copytobuf = lance_copytobuf_contig; 405 sc->sc_copyfrombuf = lance_copyfrombuf_contig; 406 sc->sc_zerobuf = lance_zerobuf_contig; 407 408 sc->sc_rdcsr = le_dma_rdcsr; 409 sc->sc_wrcsr = le_dma_wrcsr; 410 sc->sc_hwreset = le_dma_hwreset; 411 sc->sc_hwintr = le_dma_hwintr; 412 sc->sc_nocarrier = le_dma_nocarrier; 413 414 error = am7990_config(&lesc->sc_am7990, device_get_name(dev), 415 device_get_unit(dev)); 416 if (error != 0) { 417 device_printf(dev, "cannot attach Am7990\n"); 418 goto fail_dmap; 419 } 420 421 error = bus_setup_intr(dev, lesc->sc_ires, INTR_TYPE_NET | INTR_MPSAFE, 422 am7990_intr, sc, &lesc->sc_ih); 423 if (error != 0) { 424 device_printf(dev, "cannot set up interrupt\n"); 425 goto fail_am7990; 426 } 427 428 return (0); 429 430 fail_am7990: 431 am7990_detach(&lesc->sc_am7990); 432 fail_dmap: 433 bus_dmamap_unload(lesc->sc_dmat, lesc->sc_dmam); 434 fail_dmem: 435 bus_dmamem_free(lesc->sc_dmat, sc->sc_mem, lesc->sc_dmam); 436 fail_dtag: 437 bus_dma_tag_destroy(lesc->sc_dmat); 438 fail_ires: 439 bus_release_resource(dev, SYS_RES_IRQ, lesc->sc_irid, lesc->sc_ires); 440 fail_rres: 441 bus_release_resource(dev, SYS_RES_MEMORY, lesc->sc_rrid, lesc->sc_rres); 442 fail_mtx: 443 LE_LOCK_DESTROY(sc); 444 return (error); 445} 446 447static int 448le_dma_detach(device_t dev) 449{ 450 struct le_dma_softc *lesc; 451 struct lance_softc *sc; 452 453 lesc = device_get_softc(dev); 454 sc = &lesc->sc_am7990.lsc; 455 456 bus_teardown_intr(dev, lesc->sc_ires, lesc->sc_ih); 457 am7990_detach(&lesc->sc_am7990); 458 bus_dmamap_unload(lesc->sc_dmat, lesc->sc_dmam); 459 bus_dmamem_free(lesc->sc_dmat, sc->sc_mem, lesc->sc_dmam); 460 bus_dma_tag_destroy(lesc->sc_dmat); 461 bus_release_resource(dev, SYS_RES_IRQ, lesc->sc_irid, lesc->sc_ires); 462 bus_release_resource(dev, SYS_RES_MEMORY, lesc->sc_rrid, lesc->sc_rres); 463 LE_LOCK_DESTROY(sc); 464 465 return (0); 466} 467 468static int 469le_dma_suspend(device_t dev) 470{ 471 struct le_dma_softc *lesc; 472 473 lesc = device_get_softc(dev); 474 475 lance_suspend(&lesc->sc_am7990.lsc); 476 477 return (0); 478} 479 480static int 481le_dma_resume(device_t dev) 482{ 483 struct le_dma_softc *lesc; 484 485 lesc = device_get_softc(dev); 486 487 lance_resume(&lesc->sc_am7990.lsc); 488 489 return (0); 490} 491