Deleted Added
sdiff udiff text old ( 186288 ) new ( 209908 )
full compact
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 **)&reg);
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}