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.
18 * 3. The name of the author may not be used to endorse or promote products
19 *    derived from this software without specific prior written permission.
20 *
21 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
22 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
23 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
24 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
25 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
26 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
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 "opt_platform.h"
35
36#include <sys/cdefs.h>
37__FBSDID("$FreeBSD$");
38
39#include <sys/param.h>
40#include <sys/systm.h>
41#include <sys/ktr.h>
42#include <sys/kernel.h>
43#include <sys/malloc.h>
44#include <sys/module.h>
45#include <sys/bus.h>
46#include <sys/rman.h>
47#include <machine/bus.h>
48
49#include <vm/vm.h>
50#include <vm/pmap.h>
51
52#include <dev/fdt/fdt_common.h>
53#include <dev/ofw/ofw_bus.h>
54#include <dev/ofw/ofw_bus_subr.h>
55
56#include <powerpc/mpc85xx/mpc85xx.h>
57
58#include "ofw_bus_if.h"
59#include "lbc.h"
60
61#ifdef DEBUG
62#define debugf(fmt, args...) do { printf("%s(): ", __func__);	\
63    printf(fmt,##args); } while (0)
64#else
65#define debugf(fmt, args...)
66#endif
67
68static MALLOC_DEFINE(M_LBC, "localbus", "localbus devices information");
69
70static int lbc_probe(device_t);
71static int lbc_attach(device_t);
72static int lbc_shutdown(device_t);
73static int lbc_activate_resource(device_t bus __unused, device_t child __unused,
74    int type, int rid __unused, struct resource *r);
75static int lbc_deactivate_resource(device_t bus __unused,
76    device_t child __unused, int type __unused, int rid __unused,
77    struct resource *r);
78static struct resource *lbc_alloc_resource(device_t, device_t, int, int *,
79    rman_res_t, rman_res_t, rman_res_t, u_int);
80static int lbc_print_child(device_t, device_t);
81static int lbc_release_resource(device_t, device_t, int, int,
82    struct resource *);
83static const struct ofw_bus_devinfo *lbc_get_devinfo(device_t, device_t);
84
85/*
86 * Bus interface definition
87 */
88static device_method_t lbc_methods[] = {
89	/* Device interface */
90	DEVMETHOD(device_probe,		lbc_probe),
91	DEVMETHOD(device_attach,	lbc_attach),
92	DEVMETHOD(device_shutdown,	lbc_shutdown),
93
94	/* Bus interface */
95	DEVMETHOD(bus_print_child,	lbc_print_child),
96	DEVMETHOD(bus_setup_intr,	bus_generic_setup_intr),
97	DEVMETHOD(bus_teardown_intr,	NULL),
98
99	DEVMETHOD(bus_alloc_resource,	lbc_alloc_resource),
100	DEVMETHOD(bus_release_resource,	lbc_release_resource),
101	DEVMETHOD(bus_activate_resource, lbc_activate_resource),
102	DEVMETHOD(bus_deactivate_resource, lbc_deactivate_resource),
103
104	/* OFW bus interface */
105	DEVMETHOD(ofw_bus_get_devinfo,	lbc_get_devinfo),
106	DEVMETHOD(ofw_bus_get_compat,	ofw_bus_gen_get_compat),
107	DEVMETHOD(ofw_bus_get_model,	ofw_bus_gen_get_model),
108	DEVMETHOD(ofw_bus_get_name,	ofw_bus_gen_get_name),
109	DEVMETHOD(ofw_bus_get_node,	ofw_bus_gen_get_node),
110	DEVMETHOD(ofw_bus_get_type,	ofw_bus_gen_get_type),
111
112	{ 0, 0 }
113};
114
115static driver_t lbc_driver = {
116	"lbc",
117	lbc_methods,
118	sizeof(struct lbc_softc)
119};
120
121devclass_t lbc_devclass;
122
123EARLY_DRIVER_MODULE(lbc, ofwbus, lbc_driver, lbc_devclass,
124    0, 0, BUS_PASS_BUS);
125
126/*
127 * Calculate address mask used by OR(n) registers. Use memory region size to
128 * determine mask value. The size must be a power of two and within the range
129 * of 32KB - 4GB. Otherwise error code is returned. Value representing
130 * 4GB size can be passed as 0xffffffff.
131 */
132static uint32_t
133lbc_address_mask(uint32_t size)
134{
135	int n = 15;
136
137	if (size == ~0)
138		return (0);
139
140	while (n < 32) {
141		if (size == (1U << n))
142			break;
143		n++;
144	}
145
146	if (n == 32)
147		return (EINVAL);
148
149	return (0xffff8000 << (n - 15));
150}
151
152static void
153lbc_banks_unmap(struct lbc_softc *sc)
154{
155	int r;
156
157	r = 0;
158	while (r < LBC_DEV_MAX) {
159		if (sc->sc_range[r].size == 0)
160			return;
161
162		pmap_unmapdev(sc->sc_range[r].kva, sc->sc_range[r].size);
163		law_disable(OCP85XX_TGTIF_LBC, sc->sc_range[r].addr,
164		    sc->sc_range[r].size);
165		r++;
166	}
167}
168
169static int
170lbc_banks_map(struct lbc_softc *sc)
171{
172	vm_paddr_t end, start;
173	vm_size_t size;
174	u_int i, r, ranges, s;
175	int error;
176
177	bzero(sc->sc_range, sizeof(sc->sc_range));
178
179	/*
180	 * Determine number of discontiguous address ranges to program.
181	 */
182	ranges = 0;
183	for (i = 0; i < LBC_DEV_MAX; i++) {
184		size = sc->sc_banks[i].size;
185		if (size == 0)
186			continue;
187
188		start = sc->sc_banks[i].addr;
189		for (r = 0; r < ranges; r++) {
190			/* Avoid wrap-around bugs. */
191			end = sc->sc_range[r].addr - 1 + sc->sc_range[r].size;
192			if (start > 0 && end == start - 1) {
193				sc->sc_range[r].size += size;
194				break;
195			}
196			/* Avoid wrap-around bugs. */
197			end = start - 1 + size;
198			if (sc->sc_range[r].addr > 0 &&
199			    end == sc->sc_range[r].addr - 1) {
200				sc->sc_range[r].addr = start;
201				sc->sc_range[r].size += size;
202				break;
203			}
204		}
205		if (r == ranges) {
206			/* New range; add using insertion sort */
207			r = 0;
208			while (r < ranges && sc->sc_range[r].addr < start)
209				r++;
210			for (s = ranges; s > r; s--)
211				sc->sc_range[s] = sc->sc_range[s-1];
212			sc->sc_range[r].addr = start;
213			sc->sc_range[r].size = size;
214			ranges++;
215		}
216	}
217
218	/*
219	 * Ranges are sorted so quickly go over the list to merge ranges
220	 * that grew toward each other while building the ranges.
221	 */
222	r = 0;
223	while (r < ranges - 1) {
224		end = sc->sc_range[r].addr + sc->sc_range[r].size;
225		if (end != sc->sc_range[r+1].addr) {
226			r++;
227			continue;
228		}
229		sc->sc_range[r].size += sc->sc_range[r+1].size;
230		for (s = r + 1; s < ranges - 1; s++)
231			sc->sc_range[s] = sc->sc_range[s+1];
232		bzero(&sc->sc_range[s], sizeof(sc->sc_range[s]));
233		ranges--;
234	}
235
236	/*
237	 * Configure LAW for the LBC ranges and map the physical memory
238	 * range into KVA.
239	 */
240	for (r = 0; r < ranges; r++) {
241		start = sc->sc_range[r].addr;
242		size = sc->sc_range[r].size;
243		error = law_enable(OCP85XX_TGTIF_LBC, start, size);
244		if (error)
245			return (error);
246		sc->sc_range[r].kva = (vm_offset_t)pmap_mapdev(start, size);
247	}
248
249	/* XXX: need something better here? */
250	if (ranges == 0)
251		return (EINVAL);
252
253	/* Assign KVA to banks based on the enclosing range. */
254	for (i = 0; i < LBC_DEV_MAX; i++) {
255		size = sc->sc_banks[i].size;
256		if (size == 0)
257			continue;
258
259		start = sc->sc_banks[i].addr;
260		for (r = 0; r < ranges; r++) {
261			end = sc->sc_range[r].addr - 1 + sc->sc_range[r].size;
262			if (start >= sc->sc_range[r].addr &&
263			    start - 1 + size <= end)
264				break;
265		}
266		if (r < ranges) {
267			sc->sc_banks[i].kva = sc->sc_range[r].kva +
268			    (start - sc->sc_range[r].addr);
269		}
270	}
271
272	return (0);
273}
274
275static int
276lbc_banks_enable(struct lbc_softc *sc)
277{
278	uint32_t size;
279	uint32_t regval;
280	int error, i;
281
282	for (i = 0; i < LBC_DEV_MAX; i++) {
283		size = sc->sc_banks[i].size;
284		if (size == 0)
285			continue;
286
287		/*
288		 * Compute and program BR value.
289		 */
290		regval = sc->sc_banks[i].addr;
291		switch (sc->sc_banks[i].width) {
292		case 8:
293			regval |= (1 << 11);
294			break;
295		case 16:
296			regval |= (2 << 11);
297			break;
298		case 32:
299			regval |= (3 << 11);
300			break;
301		default:
302			error = EINVAL;
303			goto fail;
304		}
305		regval |= (sc->sc_banks[i].decc << 9);
306		regval |= (sc->sc_banks[i].wp << 8);
307		regval |= (sc->sc_banks[i].msel << 5);
308		regval |= (sc->sc_banks[i].atom << 2);
309		regval |= 1;
310		bus_space_write_4(sc->sc_bst, sc->sc_bsh,
311		    LBC85XX_BR(i), regval);
312
313		/*
314		 * Compute and program OR value.
315		 */
316		regval = lbc_address_mask(size);
317		switch (sc->sc_banks[i].msel) {
318		case LBCRES_MSEL_GPCM:
319			/* TODO Add flag support for option registers */
320			regval |= 0x0ff7;
321			break;
322		case LBCRES_MSEL_FCM:
323			/* TODO Add flag support for options register */
324			regval |= 0x0796;
325			break;
326		case LBCRES_MSEL_UPMA:
327		case LBCRES_MSEL_UPMB:
328		case LBCRES_MSEL_UPMC:
329			printf("UPM mode not supported yet!");
330			error = ENOSYS;
331			goto fail;
332		}
333		bus_space_write_4(sc->sc_bst, sc->sc_bsh,
334		    LBC85XX_OR(i), regval);
335	}
336
337	return (0);
338
339fail:
340	lbc_banks_unmap(sc);
341	return (error);
342}
343
344static void
345fdt_lbc_fixup(phandle_t node, struct lbc_softc *sc, struct lbc_devinfo *di)
346{
347	pcell_t width;
348	int bank;
349
350	if (OF_getprop(node, "bank-width", (void *)&width, sizeof(width)) <= 0)
351		return;
352
353	bank = di->di_bank;
354	if (sc->sc_banks[bank].size == 0)
355		return;
356
357	/* Express width in bits. */
358	sc->sc_banks[bank].width = width * 8;
359}
360
361static int
362fdt_lbc_reg_decode(phandle_t node, struct lbc_softc *sc,
363    struct lbc_devinfo *di)
364{
365	rman_res_t start, end, count;
366	pcell_t *reg, *regptr;
367	pcell_t addr_cells, size_cells;
368	int tuple_size, tuples;
369	int i, j, rv, bank;
370
371	if (fdt_addrsize_cells(OF_parent(node), &addr_cells, &size_cells) != 0)
372		return (ENXIO);
373
374	tuple_size = sizeof(pcell_t) * (addr_cells + size_cells);
375	tuples = OF_getencprop_alloc(node, "reg", tuple_size, (void **)&reg);
376	debugf("addr_cells = %d, size_cells = %d\n", addr_cells, size_cells);
377	debugf("tuples = %d, tuple size = %d\n", tuples, tuple_size);
378	if (tuples <= 0)
379		/* No 'reg' property in this node. */
380		return (0);
381
382	regptr = reg;
383	for (i = 0; i < tuples; i++) {
384
385		bank = fdt_data_get((void *)reg, 1);
386		di->di_bank = bank;
387		reg += 1;
388
389		/* Get address/size. */
390		start = count = 0;
391		for (j = 0; j < addr_cells; j++) {
392			start <<= 32;
393			start |= reg[j];
394		}
395		for (j = 0; j < size_cells; j++) {
396			count <<= 32;
397			count |= reg[addr_cells + j - 1];
398		}
399		reg += addr_cells - 1 + size_cells;
400
401		/* Calculate address range relative to VA base. */
402		start = sc->sc_banks[bank].kva + start;
403		end = start + count - 1;
404
405		debugf("reg addr bank = %d, start = %jx, end = %jx, "
406		    "count = %jx\n", bank, start, end, count);
407
408		/* Use bank (CS) cell as rid. */
409		resource_list_add(&di->di_res, SYS_RES_MEMORY, bank, start,
410		    end, count);
411	}
412	rv = 0;
413	OF_prop_free(regptr);
414	return (rv);
415}
416
417static void
418lbc_intr(void *arg)
419{
420	struct lbc_softc *sc = arg;
421	uint32_t ltesr;
422
423	ltesr = bus_space_read_4(sc->sc_bst, sc->sc_bsh, LBC85XX_LTESR);
424	sc->sc_ltesr = ltesr;
425	bus_space_write_4(sc->sc_bst, sc->sc_bsh, LBC85XX_LTESR, ltesr);
426	wakeup(sc->sc_dev);
427}
428
429static int
430lbc_probe(device_t dev)
431{
432
433	if (!(ofw_bus_is_compatible(dev, "fsl,lbc") ||
434	    ofw_bus_is_compatible(dev, "fsl,elbc")))
435		return (ENXIO);
436
437	device_set_desc(dev, "Freescale Local Bus Controller");
438	return (BUS_PROBE_DEFAULT);
439}
440
441static int
442lbc_attach(device_t dev)
443{
444	struct lbc_softc *sc;
445	struct lbc_devinfo *di;
446	struct rman *rm;
447	uintmax_t offset, size;
448	vm_paddr_t start;
449	device_t cdev;
450	phandle_t node, child;
451	pcell_t *ranges, *rangesptr;
452	int tuple_size, tuples;
453	int par_addr_cells;
454	int bank, error, i, j;
455
456	sc = device_get_softc(dev);
457	sc->sc_dev = dev;
458
459	sc->sc_mrid = 0;
460	sc->sc_mres = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &sc->sc_mrid,
461	    RF_ACTIVE);
462	if (sc->sc_mres == NULL)
463		return (ENXIO);
464
465	sc->sc_bst = rman_get_bustag(sc->sc_mres);
466	sc->sc_bsh = rman_get_bushandle(sc->sc_mres);
467
468	for (bank = 0; bank < LBC_DEV_MAX; bank++) {
469		bus_space_write_4(sc->sc_bst, sc->sc_bsh, LBC85XX_BR(bank), 0);
470		bus_space_write_4(sc->sc_bst, sc->sc_bsh, LBC85XX_OR(bank), 0);
471	}
472
473	/*
474	 * Initialize configuration register:
475	 * - enable Local Bus
476	 * - set data buffer control signal function
477	 * - disable parity byte select
478	 * - set ECC parity type
479	 * - set bus monitor timing and timer prescale
480	 */
481	bus_space_write_4(sc->sc_bst, sc->sc_bsh, LBC85XX_LBCR, 0);
482
483	/*
484	 * Initialize clock ratio register:
485	 * - disable PLL bypass mode
486	 * - configure LCLK delay cycles for the assertion of LALE
487	 * - set system clock divider
488	 */
489	bus_space_write_4(sc->sc_bst, sc->sc_bsh, LBC85XX_LCRR, 0x00030008);
490
491	bus_space_write_4(sc->sc_bst, sc->sc_bsh, LBC85XX_LTEDR, 0);
492	bus_space_write_4(sc->sc_bst, sc->sc_bsh, LBC85XX_LTESR, ~0);
493	bus_space_write_4(sc->sc_bst, sc->sc_bsh, LBC85XX_LTEIR, 0x64080001);
494
495	sc->sc_irid = 0;
496	sc->sc_ires = bus_alloc_resource_any(dev, SYS_RES_IRQ, &sc->sc_irid,
497	    RF_ACTIVE | RF_SHAREABLE);
498	if (sc->sc_ires != NULL) {
499		error = bus_setup_intr(dev, sc->sc_ires,
500		    INTR_TYPE_MISC | INTR_MPSAFE, NULL, lbc_intr, sc,
501		    &sc->sc_icookie);
502		if (error) {
503			device_printf(dev, "could not activate interrupt\n");
504			bus_release_resource(dev, SYS_RES_IRQ, sc->sc_irid,
505			    sc->sc_ires);
506			sc->sc_ires = NULL;
507		}
508	}
509
510	sc->sc_ltesr = ~0;
511
512	rangesptr = NULL;
513
514	rm = &sc->sc_rman;
515	rm->rm_type = RMAN_ARRAY;
516	rm->rm_descr = "Local Bus Space";
517	error = rman_init(rm);
518	if (error)
519		goto fail;
520
521	error = rman_manage_region(rm, rm->rm_start, rm->rm_end);
522	if (error) {
523		rman_fini(rm);
524		goto fail;
525	}
526
527	/*
528	 * Process 'ranges' property.
529	 */
530	node = ofw_bus_get_node(dev);
531	if ((fdt_addrsize_cells(node, &sc->sc_addr_cells,
532	    &sc->sc_size_cells)) != 0) {
533		error = ENXIO;
534		goto fail;
535	}
536
537	par_addr_cells = fdt_parent_addr_cells(node);
538	if (par_addr_cells > 2) {
539		device_printf(dev, "unsupported parent #addr-cells\n");
540		error = ERANGE;
541		goto fail;
542	}
543	tuple_size = sizeof(pcell_t) * (sc->sc_addr_cells + par_addr_cells +
544	    sc->sc_size_cells);
545
546	tuples = OF_getencprop_alloc(node, "ranges", tuple_size,
547	    (void **)&ranges);
548	if (tuples < 0) {
549		device_printf(dev, "could not retrieve 'ranges' property\n");
550		error = ENXIO;
551		goto fail;
552	}
553	rangesptr = ranges;
554
555	debugf("par addr_cells = %d, addr_cells = %d, size_cells = %d, "
556	    "tuple_size = %d, tuples = %d\n", par_addr_cells,
557	    sc->sc_addr_cells, sc->sc_size_cells, tuple_size, tuples);
558
559	start = 0;
560	size = 0;
561	for (i = 0; i < tuples; i++) {
562
563		/* The first cell is the bank (chip select) number. */
564		bank = fdt_data_get(ranges, 1);
565		if (bank < 0 || bank > LBC_DEV_MAX) {
566			device_printf(dev, "bank out of range: %d\n", bank);
567			error = ERANGE;
568			goto fail;
569		}
570		ranges += 1;
571
572		/*
573		 * Remaining cells of the child address define offset into
574		 * this CS.
575		 */
576		offset = 0;
577		for (j = 0; j < sc->sc_addr_cells - 1; j++) {
578			offset <<= sizeof(pcell_t) * 8;
579			offset |= *ranges;
580			ranges++;
581		}
582
583		/* Parent bus start address of this bank. */
584		start = 0;
585		for (j = 0; j < par_addr_cells; j++) {
586			start <<= sizeof(pcell_t) * 8;
587			start |= *ranges;
588			ranges++;
589		}
590
591		size = fdt_data_get((void *)ranges, sc->sc_size_cells);
592		ranges += sc->sc_size_cells;
593		debugf("bank = %d, start = %jx, size = %jx\n", bank,
594		    (uintmax_t)start, size);
595
596		sc->sc_banks[bank].addr = start + offset;
597		sc->sc_banks[bank].size = size;
598
599		/*
600		 * Attributes for the bank.
601		 *
602		 * XXX Note there are no DT bindings defined for them at the
603		 * moment, so we need to provide some defaults.
604		 */
605		sc->sc_banks[bank].width = 16;
606		sc->sc_banks[bank].msel = LBCRES_MSEL_GPCM;
607		sc->sc_banks[bank].decc = LBCRES_DECC_DISABLED;
608		sc->sc_banks[bank].atom = LBCRES_ATOM_DISABLED;
609		sc->sc_banks[bank].wp = 0;
610	}
611
612	/*
613	 * Initialize mem-mappings for the LBC banks (i.e. chip selects).
614	 */
615	error = lbc_banks_map(sc);
616	if (error)
617		goto fail;
618
619	/*
620	 * Walk the localbus and add direct subordinates as our children.
621	 */
622	for (child = OF_child(node); child != 0; child = OF_peer(child)) {
623
624		di = malloc(sizeof(*di), M_LBC, M_WAITOK | M_ZERO);
625
626		if (ofw_bus_gen_setup_devinfo(&di->di_ofw, child) != 0) {
627			free(di, M_LBC);
628			device_printf(dev, "could not set up devinfo\n");
629			continue;
630		}
631
632		resource_list_init(&di->di_res);
633
634		if (fdt_lbc_reg_decode(child, sc, di)) {
635			device_printf(dev, "could not process 'reg' "
636			    "property\n");
637			ofw_bus_gen_destroy_devinfo(&di->di_ofw);
638			free(di, M_LBC);
639			continue;
640		}
641
642		fdt_lbc_fixup(child, sc, di);
643
644		/* Add newbus device for this FDT node */
645		cdev = device_add_child(dev, NULL, -1);
646		if (cdev == NULL) {
647			device_printf(dev, "could not add child: %s\n",
648			    di->di_ofw.obd_name);
649			resource_list_free(&di->di_res);
650			ofw_bus_gen_destroy_devinfo(&di->di_ofw);
651			free(di, M_LBC);
652			continue;
653		}
654		debugf("added child name='%s', node=%p\n", di->di_ofw.obd_name,
655		    (void *)child);
656		device_set_ivars(cdev, di);
657	}
658
659	/*
660	 * Enable the LBC.
661	 */
662	lbc_banks_enable(sc);
663
664	OF_prop_free(rangesptr);
665	return (bus_generic_attach(dev));
666
667fail:
668	OF_prop_free(rangesptr);
669	bus_release_resource(dev, SYS_RES_MEMORY, sc->sc_mrid, sc->sc_mres);
670	return (error);
671}
672
673static int
674lbc_shutdown(device_t dev)
675{
676
677	/* TODO */
678	return(0);
679}
680
681static struct resource *
682lbc_alloc_resource(device_t bus, device_t child, int type, int *rid,
683    rman_res_t start, rman_res_t end, rman_res_t count, u_int flags)
684{
685	struct lbc_softc *sc;
686	struct lbc_devinfo *di;
687	struct resource_list_entry *rle;
688	struct resource *res;
689	struct rman *rm;
690	int needactivate;
691
692	/* We only support default allocations. */
693	if (!RMAN_IS_DEFAULT_RANGE(start, end))
694		return (NULL);
695
696	sc = device_get_softc(bus);
697	if (type == SYS_RES_IRQ)
698		return (bus_alloc_resource(bus, type, rid, start, end, count,
699		    flags));
700
701	/*
702	 * Request for the default allocation with a given rid: use resource
703	 * list stored in the local device info.
704	 */
705	if ((di = device_get_ivars(child)) == NULL)
706		return (NULL);
707
708	if (type == SYS_RES_IOPORT)
709		type = SYS_RES_MEMORY;
710
711	rid = &di->di_bank;
712
713	rle = resource_list_find(&di->di_res, type, *rid);
714	if (rle == NULL) {
715		device_printf(bus, "no default resources for "
716		    "rid = %d, type = %d\n", *rid, type);
717		return (NULL);
718	}
719	start = rle->start;
720	count = rle->count;
721	end = start + count - 1;
722
723	sc = device_get_softc(bus);
724
725	needactivate = flags & RF_ACTIVE;
726	flags &= ~RF_ACTIVE;
727
728	rm = &sc->sc_rman;
729
730	res = rman_reserve_resource(rm, start, end, count, flags, child);
731	if (res == NULL) {
732		device_printf(bus, "failed to reserve resource %#jx - %#jx "
733		    "(%#jx)\n", start, end, count);
734		return (NULL);
735	}
736
737	rman_set_rid(res, *rid);
738	rman_set_bustag(res, &bs_be_tag);
739	rman_set_bushandle(res, rman_get_start(res));
740
741	if (needactivate)
742		if (bus_activate_resource(child, type, *rid, res)) {
743			device_printf(child, "resource activation failed\n");
744			rman_release_resource(res);
745			return (NULL);
746		}
747
748	return (res);
749}
750
751static int
752lbc_print_child(device_t dev, device_t child)
753{
754	struct lbc_devinfo *di;
755	struct resource_list *rl;
756	int rv;
757
758	di = device_get_ivars(child);
759	rl = &di->di_res;
760
761	rv = 0;
762	rv += bus_print_child_header(dev, child);
763	rv += resource_list_print_type(rl, "mem", SYS_RES_MEMORY, "%#jx");
764	rv += resource_list_print_type(rl, "irq", SYS_RES_IRQ, "%jd");
765	rv += bus_print_child_footer(dev, child);
766
767	return (rv);
768}
769
770static int
771lbc_release_resource(device_t dev, device_t child, int type, int rid,
772    struct resource *res)
773{
774	int err;
775
776	if (rman_get_flags(res) & RF_ACTIVE) {
777		err = bus_deactivate_resource(child, type, rid, res);
778		if (err)
779			return (err);
780	}
781
782	return (rman_release_resource(res));
783}
784
785static int
786lbc_activate_resource(device_t bus __unused, device_t child __unused,
787    int type __unused, int rid __unused, struct resource *r)
788{
789
790	/* Child resources were already mapped, just activate. */
791	return (rman_activate_resource(r));
792}
793
794static int
795lbc_deactivate_resource(device_t bus __unused, device_t child __unused,
796    int type __unused, int rid __unused, struct resource *r)
797{
798
799	return (rman_deactivate_resource(r));
800}
801
802static const struct ofw_bus_devinfo *
803lbc_get_devinfo(device_t bus, device_t child)
804{
805	struct lbc_devinfo *di;
806
807	di = device_get_ivars(child);
808	return (&di->di_ofw);
809}
810
811void
812lbc_write_reg(device_t child, u_int off, uint32_t val)
813{
814	device_t dev;
815	struct lbc_softc *sc;
816
817	dev = device_get_parent(child);
818
819	if (off >= 0x1000) {
820		device_printf(dev, "%s(%s): invalid offset %#x\n",
821		    __func__, device_get_nameunit(child), off);
822		return;
823	}
824
825	sc = device_get_softc(dev);
826
827	if (off == LBC85XX_LTESR && sc->sc_ltesr != ~0u) {
828		sc->sc_ltesr ^= (val & sc->sc_ltesr);
829		return;
830	}
831
832	if (off == LBC85XX_LTEATR && (val & 1) == 0)
833		sc->sc_ltesr = ~0u;
834	bus_space_write_4(sc->sc_bst, sc->sc_bsh, off, val);
835}
836
837uint32_t
838lbc_read_reg(device_t child, u_int off)
839{
840	device_t dev;
841	struct lbc_softc *sc;
842	uint32_t val;
843
844	dev = device_get_parent(child);
845
846	if (off >= 0x1000) {
847		device_printf(dev, "%s(%s): invalid offset %#x\n",
848		    __func__, device_get_nameunit(child), off);
849		return (~0U);
850	}
851
852	sc = device_get_softc(dev);
853
854	if (off == LBC85XX_LTESR && sc->sc_ltesr != ~0U)
855		val = sc->sc_ltesr;
856	else
857		val = bus_space_read_4(sc->sc_bst, sc->sc_bsh, off);
858	return (val);
859}
860