1/*- 2 * Copyright (c) 2006-2008, Juniper Networks, Inc. 3 * Copyright (c) 2008 Semihalf, Rafal Czubak |
4 * Copyright (c) 2009 The FreeBSD Foundation |
5 * All rights reserved. 6 * |
7 * Portions of this software were developed by Semihalf 8 * under sponsorship from the FreeBSD Foundation. 9 * |
10 * Redistribution and use in source and binary forms, with or without 11 * modification, are permitted provided that the following conditions 12 * are met: 13 * 1. Redistributions of source code must retain the above copyright 14 * notice, this list of conditions and the following disclaimer. 15 * 2. Redistributions in binary form must reproduce the above copyright 16 * notice, this list of conditions and the following disclaimer in the 17 * documentation and/or other materials provided with the distribution. --- 9 unchanged lines hidden (view full) --- 27 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED 28 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 29 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 30 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 31 * SUCH DAMAGE. 32 */ 33 34#include <sys/cdefs.h> |
35__FBSDID("$FreeBSD: head/sys/powerpc/mpc85xx/lbc.c 209908 2010-07-11 21:08:29Z raj $"); |
36 37#include <sys/param.h> 38#include <sys/systm.h> 39#include <sys/ktr.h> 40#include <sys/kernel.h> 41#include <sys/malloc.h> 42#include <sys/module.h> 43#include <sys/bus.h> 44#include <sys/rman.h> 45#include <machine/bus.h> |
46 47#include <vm/vm.h> 48#include <vm/pmap.h> 49 |
50#include <dev/fdt/fdt_common.h> 51#include <dev/ofw/ofw_bus.h> 52#include <dev/ofw/ofw_bus_subr.h> 53 |
54#include <powerpc/mpc85xx/mpc85xx.h> |
55 |
56#include "ofw_bus_if.h" 57#include "lbc.h" |
58 |
59#define DEBUG 60#undef DEBUG |
61 |
62#ifdef DEBUG 63#define debugf(fmt, args...) do { printf("%s(): ", __func__); \ 64 printf(fmt,##args); } while (0) 65#else 66#define debugf(fmt, args...) 67#endif |
68 |
69static __inline void 70lbc_write_reg(struct lbc_softc *sc, bus_size_t off, uint32_t val) 71{ |
72 |
73 bus_space_write_4(sc->sc_bst, sc->sc_bsh, off, val); 74} |
75 |
76static __inline uint32_t 77lbc_read_reg(struct lbc_softc *sc, bus_size_t off) 78{ |
79 |
80 return (bus_space_read_4(sc->sc_bst, sc->sc_bsh, off)); 81} |
82 |
83static MALLOC_DEFINE(M_LBC, "localbus", "localbus devices information"); |
84 85static int lbc_probe(device_t); 86static int lbc_attach(device_t); 87static int lbc_shutdown(device_t); |
88static struct resource *lbc_alloc_resource(device_t, device_t, int, int *, 89 u_long, u_long, u_long, u_int); 90static int lbc_print_child(device_t, device_t); 91static int lbc_release_resource(device_t, device_t, int, int, 92 struct resource *); |
93static const struct ofw_bus_devinfo *lbc_get_devinfo(device_t, device_t); |
94 95/* 96 * Bus interface definition 97 */ 98static device_method_t lbc_methods[] = { 99 /* Device interface */ 100 DEVMETHOD(device_probe, lbc_probe), 101 DEVMETHOD(device_attach, lbc_attach), 102 DEVMETHOD(device_shutdown, lbc_shutdown), 103 104 /* Bus interface */ 105 DEVMETHOD(bus_print_child, lbc_print_child), |
106 DEVMETHOD(bus_setup_intr, bus_generic_setup_intr), 107 DEVMETHOD(bus_teardown_intr, NULL), 108 |
109 DEVMETHOD(bus_alloc_resource, lbc_alloc_resource), 110 DEVMETHOD(bus_release_resource, lbc_release_resource), 111 DEVMETHOD(bus_activate_resource, bus_generic_activate_resource), 112 DEVMETHOD(bus_deactivate_resource, bus_generic_deactivate_resource), 113 |
114 /* OFW bus interface */ 115 DEVMETHOD(ofw_bus_get_devinfo, lbc_get_devinfo), 116 DEVMETHOD(ofw_bus_get_compat, ofw_bus_gen_get_compat), 117 DEVMETHOD(ofw_bus_get_model, ofw_bus_gen_get_model), 118 DEVMETHOD(ofw_bus_get_name, ofw_bus_gen_get_name), 119 DEVMETHOD(ofw_bus_get_node, ofw_bus_gen_get_node), 120 DEVMETHOD(ofw_bus_get_type, ofw_bus_gen_get_type), 121 |
122 { 0, 0 } 123}; 124 125static driver_t lbc_driver = { 126 "lbc", 127 lbc_methods, 128 sizeof(struct lbc_softc) 129}; |
130 |
131devclass_t lbc_devclass; |
132 |
133DRIVER_MODULE(lbc, fdtbus, lbc_driver, lbc_devclass, 0, 0); |
134 |
135/* 136 * Calculate address mask used by OR(n) registers. Use memory region size to 137 * determine mask value. The size must be a power of two and within the range 138 * of 32KB - 4GB. Otherwise error code is returned. Value representing 139 * 4GB size can be passed as 0xffffffff. 140 */ 141static uint32_t 142lbc_address_mask(uint32_t size) --- 10 unchanged lines hidden (view full) --- 153 } 154 155 if (n == 32) 156 return (EINVAL); 157 158 return (0xffff8000 << (n - 15)); 159} 160 |
161static void 162lbc_banks_unmap(struct lbc_softc *sc) |
163{ |
164 int i; |
165 |
166 for (i = 0; i < LBC_DEV_MAX; i++) { 167 if (sc->sc_banks[i].size == 0) 168 continue; |
169 |
170 law_disable(OCP85XX_TGTIF_LBC, sc->sc_banks[i].pa, 171 sc->sc_banks[i].size); 172 pmap_unmapdev(sc->sc_banks[i].va, sc->sc_banks[i].size); |
173 } |
174} 175 176static int |
177lbc_banks_map(struct lbc_softc *sc) |
178{ |
179 u_long start, size; |
180 int error, i; |
181 |
182 for (i = 0; i < LBC_DEV_MAX; i++) { 183 if (sc->sc_banks[i].size == 0) |
184 continue; 185 |
186 /* Physical address start/size. */ 187 start = sc->sc_banks[i].pa; 188 size = sc->sc_banks[i].size; |
189 190 /* |
191 * Configure LAW for this LBC bank (CS) and map its physical 192 * memory region into KVA. |
193 */ 194 error = law_enable(OCP85XX_TGTIF_LBC, start, size); 195 if (error) 196 return (error); 197 |
198 sc->sc_banks[i].va = (vm_offset_t)pmap_mapdev(start, size); 199 if (sc->sc_banks[i].va == 0) { 200 lbc_banks_unmap(sc); |
201 return (ENOSPC); 202 } |
203 } 204 return (0); 205} |
206 |
207static int 208lbc_banks_enable(struct lbc_softc *sc) 209{ 210 u_long size; 211 uint32_t regval; 212 int error, i; 213 214 for (i = 0; i < LBC_DEV_MAX; i++) { 215 size = sc->sc_banks[i].size; 216 if (size == 0) 217 continue; |
218 /* |
219 * Compute and program BR value. |
220 */ |
221 regval = 0; 222 regval |= sc->sc_banks[i].pa; |
223 |
224 switch (sc->sc_banks[i].width) { |
225 case 8: |
226 regval |= (1 << 11); |
227 break; 228 case 16: |
229 regval |= (2 << 11); |
230 break; 231 case 32: |
232 regval |= (3 << 11); |
233 break; 234 default: 235 error = EINVAL; 236 goto fail; 237 } |
238 regval |= (sc->sc_banks[i].decc << 9); 239 regval |= (sc->sc_banks[i].wp << 8); 240 regval |= (sc->sc_banks[i].msel << 5); 241 regval |= (sc->sc_banks[i].atom << 2); 242 regval |= 1; |
243 |
244 lbc_write_reg(sc, LBC85XX_BR(i), regval); |
245 246 /* |
247 * Compute and program OR value. |
248 */ |
249 regval = 0; 250 regval |= lbc_address_mask(size); |
251 |
252 switch (sc->sc_banks[i].msel) { |
253 case LBCRES_MSEL_GPCM: 254 /* TODO Add flag support for option registers */ |
255 regval |= 0x00000ff7; |
256 break; 257 case LBCRES_MSEL_FCM: 258 printf("FCM mode not supported yet!"); 259 error = ENOSYS; 260 goto fail; 261 case LBCRES_MSEL_UPMA: 262 case LBCRES_MSEL_UPMB: 263 case LBCRES_MSEL_UPMC: 264 printf("UPM mode not supported yet!"); 265 error = ENOSYS; 266 goto fail; 267 } |
268 lbc_write_reg(sc, LBC85XX_OR(i), regval); 269 } |
270 |
271 /* 272 * Initialize configuration register: 273 * - enable Local Bus 274 * - set data buffer control signal function 275 * - disable parity byte select 276 * - set ECC parity type 277 * - set bus monitor timing and timer prescale 278 */ 279 lbc_write_reg(sc, LBC85XX_LBCR, 0); |
280 |
281 /* 282 * Initialize clock ratio register: 283 * - disable PLL bypass mode 284 * - configure LCLK delay cycles for the assertion of LALE 285 * - set system clock divider 286 */ 287 lbc_write_reg(sc, LBC85XX_LCRR, 0x00030008); 288 289 return (0); 290 291fail: 292 lbc_banks_unmap(sc); 293 return (error); 294} 295 296static void 297fdt_lbc_fixup(phandle_t node, struct lbc_softc *sc, struct lbc_devinfo *di) 298{ 299 pcell_t width; 300 int bank; 301 302 if (OF_getprop(node, "bank-width", (void *)&width, sizeof(width)) <= 0) 303 return; 304 305 bank = di->di_bank; 306 if (sc->sc_banks[bank].size == 0) 307 return; 308 309 /* Express width in bits. */ 310 sc->sc_banks[bank].width = width * 8; 311} 312 313static int 314fdt_lbc_reg_decode(phandle_t node, struct lbc_softc *sc, 315 struct lbc_devinfo *di) 316{ 317 u_long start, end, count; 318 pcell_t *reg, *regptr; 319 pcell_t addr_cells, size_cells; 320 int tuple_size, tuples; 321 int i, rv, bank; 322 323 if (fdt_addrsize_cells(OF_parent(node), &addr_cells, &size_cells) != 0) 324 return (ENXIO); 325 326 tuple_size = sizeof(pcell_t) * (addr_cells + size_cells); 327 tuples = OF_getprop_alloc(node, "reg", tuple_size, (void **)®); 328 debugf("addr_cells = %d, size_cells = %d\n", addr_cells, size_cells); 329 debugf("tuples = %d, tuple size = %d\n", tuples, tuple_size); 330 if (tuples <= 0) 331 /* No 'reg' property in this node. */ |
332 return (0); |
333 334 regptr = reg; 335 for (i = 0; i < tuples; i++) { 336 337 bank = fdt_data_get((void *)reg, 1); 338 di->di_bank = bank; 339 reg += 1; 340 341 /* Get address/size. */ 342 rv = fdt_data_to_res(reg, addr_cells - 1, size_cells, &start, 343 &count); 344 if (rv != 0) { 345 resource_list_free(&di->di_res); 346 goto out; 347 } 348 reg += addr_cells - 1 + size_cells; 349 350 /* Calculate address range relative to VA base. */ 351 start = sc->sc_banks[bank].va + start; 352 end = start + count - 1; 353 354 debugf("reg addr bank = %d, start = %lx, end = %lx, " 355 "count = %lx\n", bank, start, end, count); 356 357 /* Use bank (CS) cell as rid. */ 358 resource_list_add(&di->di_res, SYS_RES_MEMORY, bank, start, 359 end, count); |
360 } |
361 rv = 0; 362out: 363 free(regptr, M_OFWPROP); 364 return (rv); |
365} 366 367static int 368lbc_probe(device_t dev) 369{ |
370 |
371 if (!(ofw_bus_is_compatible(dev, "fsl,lbc") || 372 ofw_bus_is_compatible(dev, "fsl,elbc"))) |
373 return (ENXIO); 374 |
375 device_set_desc(dev, "Freescale Local Bus Controller"); |
376 return (BUS_PROBE_DEFAULT); 377} 378 379static int 380lbc_attach(device_t dev) 381{ 382 struct lbc_softc *sc; |
383 struct lbc_devinfo *di; |
384 struct rman *rm; |
385 u_long offset, start, size; 386 device_t cdev; 387 phandle_t node, child; 388 pcell_t *ranges, *rangesptr; 389 int tuple_size, tuples; 390 int par_addr_cells; 391 int bank, error, i; |
392 393 sc = device_get_softc(dev); 394 sc->sc_dev = dev; 395 396 sc->sc_rid = 0; 397 sc->sc_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &sc->sc_rid, 398 RF_ACTIVE); 399 if (sc->sc_res == NULL) 400 return (ENXIO); 401 402 sc->sc_bst = rman_get_bustag(sc->sc_res); 403 sc->sc_bsh = rman_get_bushandle(sc->sc_res); |
404 rangesptr = NULL; |
405 406 rm = &sc->sc_rman; 407 rm->rm_type = RMAN_ARRAY; |
408 rm->rm_descr = "Local Bus Space"; |
409 rm->rm_start = 0UL; 410 rm->rm_end = ~0UL; 411 error = rman_init(rm); 412 if (error) 413 goto fail; 414 415 error = rman_manage_region(rm, rm->rm_start, rm->rm_end); 416 if (error) { 417 rman_fini(rm); 418 goto fail; 419 } 420 421 /* |
422 * Process 'ranges' property. |
423 */ |
424 node = ofw_bus_get_node(dev); 425 if ((fdt_addrsize_cells(node, &sc->sc_addr_cells, 426 &sc->sc_size_cells)) != 0) { 427 error = ENXIO; 428 goto fail; 429 } |
430 |
431 par_addr_cells = fdt_parent_addr_cells(node); 432 if (par_addr_cells > 2) { 433 device_printf(dev, "unsupported parent #addr-cells\n"); 434 error = ERANGE; 435 goto fail; 436 } 437 tuple_size = sizeof(pcell_t) * (sc->sc_addr_cells + par_addr_cells + 438 sc->sc_size_cells); 439 440 tuples = OF_getprop_alloc(node, "ranges", tuple_size, 441 (void **)&ranges); 442 if (tuples < 0) { 443 device_printf(dev, "could not retrieve 'ranges' property\n"); 444 error = ENXIO; 445 goto fail; 446 } 447 rangesptr = ranges; 448 449 debugf("par addr_cells = %d, addr_cells = %d, size_cells = %d, " 450 "tuple_size = %d, tuples = %d\n", par_addr_cells, 451 sc->sc_addr_cells, sc->sc_size_cells, tuple_size, tuples); 452 453 start = 0; 454 size = 0; 455 for (i = 0; i < tuples; i++) { 456 457 /* The first cell is the bank (chip select) number. */ 458 bank = fdt_data_get((void *)ranges, 1); 459 if (bank < 0 || bank > LBC_DEV_MAX) { 460 device_printf(dev, "bank out of range: %d\n", bank); 461 error = ERANGE; 462 goto fail; 463 } 464 ranges += 1; 465 466 /* 467 * Remaining cells of the child address define offset into 468 * this CS. 469 */ 470 offset = fdt_data_get((void *)ranges, sc->sc_addr_cells - 1); 471 ranges += sc->sc_addr_cells - 1; 472 473 /* Parent bus start address of this bank. */ 474 start = fdt_data_get((void *)ranges, par_addr_cells); 475 ranges += par_addr_cells; 476 477 size = fdt_data_get((void *)ranges, sc->sc_size_cells); 478 ranges += sc->sc_size_cells; 479 debugf("bank = %d, start = %lx, size = %lx\n", bank, 480 start, size); 481 482 sc->sc_banks[bank].pa = start + offset; 483 sc->sc_banks[bank].size = size; 484 485 /* 486 * Attributes for the bank. 487 * 488 * XXX Note there are no DT bindings defined for them at the 489 * moment, so we need to provide some defaults. 490 */ 491 sc->sc_banks[bank].width = 16; 492 sc->sc_banks[bank].msel = LBCRES_MSEL_GPCM; 493 sc->sc_banks[bank].decc = LBCRES_DECC_DISABLED; 494 sc->sc_banks[bank].atom = LBCRES_ATOM_DISABLED; 495 sc->sc_banks[bank].wp = 0; 496 } 497 |
498 /* |
499 * Initialize mem-mappings for the LBC banks (i.e. chip selects). |
500 */ |
501 error = lbc_banks_map(sc); 502 if (error) 503 goto fail; |
504 |
505 /* 506 * Walk the localbus and add direct subordinates as our children. 507 */ 508 for (child = OF_child(node); child != 0; child = OF_peer(child)) { |
509 |
510 di = malloc(sizeof(*di), M_LBC, M_WAITOK | M_ZERO); 511 512 if (ofw_bus_gen_setup_devinfo(&di->di_ofw, child) != 0) { 513 free(di, M_LBC); 514 device_printf(dev, "could not set up devinfo\n"); 515 continue; |
516 } 517 |
518 resource_list_init(&di->di_res); 519 520 if (fdt_lbc_reg_decode(child, sc, di)) { 521 device_printf(dev, "could not process 'reg' " 522 "property\n"); 523 ofw_bus_gen_destroy_devinfo(&di->di_ofw); 524 free(di, M_LBC); 525 continue; 526 } 527 528 fdt_lbc_fixup(child, sc, di); 529 530 /* Add newbus device for this FDT node */ 531 cdev = device_add_child(dev, NULL, -1); 532 if (cdev == NULL) { 533 device_printf(dev, "could not add child: %s\n", 534 di->di_ofw.obd_name); 535 resource_list_free(&di->di_res); 536 ofw_bus_gen_destroy_devinfo(&di->di_ofw); 537 free(di, M_LBC); 538 continue; 539 } 540 debugf("added child name='%s', node=%p\n", di->di_ofw.obd_name, 541 (void *)child); 542 device_set_ivars(cdev, di); 543 } 544 545 /* 546 * Enable the LBC. 547 */ 548 lbc_banks_enable(sc); 549 550 free(rangesptr, M_OFWPROP); |
551 return (bus_generic_attach(dev)); 552 553fail: |
554 free(rangesptr, M_OFWPROP); |
555 bus_release_resource(dev, SYS_RES_MEMORY, sc->sc_rid, sc->sc_res); 556 return (error); 557} 558 559static int 560lbc_shutdown(device_t dev) 561{ 562 563 /* TODO */ 564 return(0); 565} 566 567static struct resource * |
568lbc_alloc_resource(device_t bus, device_t child, int type, int *rid, |
569 u_long start, u_long end, u_long count, u_int flags) 570{ 571 struct lbc_softc *sc; |
572 struct lbc_devinfo *di; 573 struct resource_list_entry *rle; 574 struct resource *res; |
575 struct rman *rm; |
576 int needactivate; |
577 |
578 /* We only support default allocations. */ 579 if (start != 0ul || end != ~0ul) 580 return (NULL); 581 |
582 sc = device_get_softc(bus); |
583 if (type == SYS_RES_IRQ) |
584 return (bus_alloc_resource(bus, type, rid, start, end, count, |
585 flags)); 586 |
587 /* 588 * Request for the default allocation with a given rid: use resource 589 * list stored in the local device info. 590 */ 591 if ((di = device_get_ivars(child)) == NULL) |
592 return (NULL); 593 |
594 if (type == SYS_RES_IOPORT) 595 type = SYS_RES_MEMORY; 596 597 rid = &di->di_bank; 598 599 rle = resource_list_find(&di->di_res, type, *rid); 600 if (rle == NULL) { 601 device_printf(bus, "no default resources for " 602 "rid = %d, type = %d\n", *rid, type); 603 return (NULL); |
604 } |
605 start = rle->start; 606 count = rle->count; 607 end = start + count - 1; |
608 |
609 sc = device_get_softc(bus); |
610 |
611 needactivate = flags & RF_ACTIVE; 612 flags &= ~RF_ACTIVE; |
613 |
614 rm = &sc->sc_rman; 615 616 res = rman_reserve_resource(rm, start, end, count, flags, child); 617 if (res == NULL) { 618 device_printf(bus, "failed to reserve resource %#lx - %#lx " 619 "(%#lx)\n", start, end, count); 620 return (NULL); |
621 } 622 |
623 rman_set_rid(res, *rid); 624 rman_set_bustag(res, &bs_be_tag); 625 rman_set_bushandle(res, rman_get_start(res)); 626 627 if (needactivate) 628 if (bus_activate_resource(child, type, *rid, res)) { 629 device_printf(child, "resource activation failed\n"); 630 rman_release_resource(res); 631 return (NULL); 632 } 633 634 return (res); |
635} 636 637static int |
638lbc_print_child(device_t dev, device_t child) |
639{ |
640 struct lbc_devinfo *di; 641 struct resource_list *rl; 642 int rv; |
643 |
644 di = device_get_ivars(child); 645 rl = &di->di_res; |
646 |
647 rv = 0; 648 rv += bus_print_child_header(dev, child); 649 rv += resource_list_print_type(rl, "mem", SYS_RES_MEMORY, "%#lx"); 650 rv += resource_list_print_type(rl, "irq", SYS_RES_IRQ, "%ld"); 651 rv += bus_print_child_footer(dev, child); |
652 |
653 return (rv); |
654} 655 656static int 657lbc_release_resource(device_t dev, device_t child, int type, int rid, 658 struct resource *res) 659{ |
660 int err; |
661 |
662 if (rman_get_flags(res) & RF_ACTIVE) { 663 err = bus_deactivate_resource(child, type, rid, res); 664 if (err) 665 return (err); 666 } 667 |
668 return (rman_release_resource(res)); 669} 670 |
671static const struct ofw_bus_devinfo * 672lbc_get_devinfo(device_t bus, device_t child) |
673{ |
674 struct lbc_devinfo *di; |
675 |
676 di = device_get_ivars(child); 677 return (&di->di_ofw); |
678} |