sb_zbbus.c revision 203509
154359Sroberto/*-
2285612Sdelphij * Copyright (c) 2009 Neelkanth Natu
354359Sroberto * All rights reserved.
454359Sroberto *
554359Sroberto * Redistribution and use in source and binary forms, with or without
654359Sroberto * modification, are permitted provided that the following conditions
754359Sroberto * are met:
854359Sroberto * 1. Redistributions of source code must retain the above copyright
954359Sroberto *    notice, this list of conditions and the following disclaimer.
1054359Sroberto * 2. Redistributions in binary form must reproduce the above copyright
1154359Sroberto *    notice, this list of conditions and the following disclaimer in the
1254359Sroberto *    documentation and/or other materials provided with the distribution.
1354359Sroberto *
14285612Sdelphij * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15285612Sdelphij * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
1654359Sroberto * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
1754359Sroberto * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
1854359Sroberto * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19285612Sdelphij * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2054359Sroberto * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21285612Sdelphij * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2254359Sroberto * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23285612Sdelphij * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24285612Sdelphij * SUCH DAMAGE.
25285612Sdelphij */
2654359Sroberto
27285612Sdelphij#include <sys/param.h>
2854359Sroberto#include <sys/kernel.h>
29285612Sdelphij#include <sys/systm.h>
3082498Sroberto#include <sys/module.h>
31285612Sdelphij#include <sys/bus.h>
3282498Sroberto#include <sys/malloc.h>
3354359Sroberto#include <sys/rman.h>
3482498Sroberto
3582498Sroberto#include <machine/resource.h>
3682498Sroberto#include <machine/intr_machdep.h>
3782498Sroberto
3882498Sroberto#include "sb_scd.h"
3954359Sroberto
4054359Sroberto__FBSDID("$FreeBSD: head/sys/mips/sibyte/sb_zbbus.c 203509 2010-02-05 03:20:47Z neel $");
41285612Sdelphij
42285612Sdelphijstatic MALLOC_DEFINE(M_INTMAP, "sb1250 intmap", "Sibyte 1250 Interrupt Mapper");
43285612Sdelphij
44285612Sdelphij#define	NUM_HARD_IRQS	6
4554359Sroberto
4654359Srobertostruct sb_intmap {
47285612Sdelphij	int intsrc;		/* interrupt mapper register number (0 - 63) */
4854359Sroberto	int hardint;		/* cpu interrupt from 0 to NUM_HARD_IRQS - 1 */
4954359Sroberto
5054359Sroberto	/*
5154359Sroberto	 * The device that the interrupt belongs to. Note that multiple
5254359Sroberto	 * devices may share an interrupt. For e.g. PCI_INT_x lines.
5354359Sroberto	 *
5454359Sroberto	 * The device 'dev' in combination with the 'rid' uniquely
5554359Sroberto	 * identify this interrupt source.
5654359Sroberto	 */
5754359Sroberto	device_t dev;
5854359Sroberto	int rid;
59285612Sdelphij
60285612Sdelphij	SLIST_ENTRY(sb_intmap) next;
61285612Sdelphij};
62285612Sdelphij
63285612Sdelphijstatic SLIST_HEAD(, sb_intmap) sb_intmap_head;
6454359Sroberto
6554359Srobertostatic struct sb_intmap *
66285612Sdelphijsb_intmap_lookup(int intrnum, device_t dev, int rid)
67285612Sdelphij{
68285612Sdelphij	struct sb_intmap *map;
69285612Sdelphij
70285612Sdelphij	SLIST_FOREACH(map, &sb_intmap_head, next) {
71285612Sdelphij		if (dev == map->dev && rid == map->rid &&
72285612Sdelphij		    intrnum == map->hardint)
7354359Sroberto			break;
7454359Sroberto	}
7554359Sroberto	return (map);
7654359Sroberto}
7754359Sroberto
7854359Sroberto/*
7954359Sroberto * Keep track of which (dev,rid,hardint) tuple is using the interrupt source.
80285612Sdelphij *
81285612Sdelphij * We don't actually unmask the interrupt source until the device calls
82285612Sdelphij * a bus_setup_intr() on the resource.
83285612Sdelphij */
84285612Sdelphijstatic void
8554359Srobertosb_intmap_add(int intrnum, device_t dev, int rid, int intsrc)
86285612Sdelphij{
8754359Sroberto	struct sb_intmap *map;
88285612Sdelphij
89285612Sdelphij	KASSERT(intrnum >= 0 && intrnum < NUM_HARD_IRQS,
9054359Sroberto		("intrnum is out of range: %d", intrnum));
91285612Sdelphij
9254359Sroberto	map = sb_intmap_lookup(intrnum, dev, rid);
93285612Sdelphij	if (map) {
9454359Sroberto		KASSERT(intsrc == map->intsrc,
9554359Sroberto			("%s%d allocating SYS_RES_IRQ resource with rid %d "
96285612Sdelphij			 "with a different intsrc (%d versus %d)",
97285612Sdelphij			device_get_name(dev), device_get_unit(dev), rid,
98285612Sdelphij			intsrc, map->intsrc));
99285612Sdelphij		return;
100285612Sdelphij	}
101285612Sdelphij
102285612Sdelphij	map = malloc(sizeof(*map), M_INTMAP, M_WAITOK | M_ZERO);
103285612Sdelphij	map->intsrc = intsrc;
104285612Sdelphij	map->hardint = intrnum;
105285612Sdelphij	map->dev = dev;
106285612Sdelphij	map->rid = rid;
107285612Sdelphij
10854359Sroberto	SLIST_INSERT_HEAD(&sb_intmap_head, map, next);
109132451Sroberto}
110285612Sdelphij
111285612Sdelphijstatic void
112285612Sdelphijsb_intmap_activate(int intrnum, device_t dev, int rid)
113132451Sroberto{
114285612Sdelphij	struct sb_intmap *map;
115285612Sdelphij
116285612Sdelphij	KASSERT(intrnum >= 0 && intrnum < NUM_HARD_IRQS,
117285612Sdelphij		("intrnum is out of range: %d", intrnum));
118285612Sdelphij
119285612Sdelphij	map = sb_intmap_lookup(intrnum, dev, rid);
120285612Sdelphij	if (map) {
121285612Sdelphij		sb_enable_intsrc(0, map->intsrc);
122285612Sdelphij	} else {
123285612Sdelphij		/*
124285612Sdelphij		 * In zbbus_setup_intr() we blindly call sb_intmap_activate()
125285612Sdelphij		 * for every interrupt activation that comes our way.
126285612Sdelphij		 *
127285612Sdelphij		 * We might end up here if we did not "hijack" the SYS_RES_IRQ
128285612Sdelphij		 * resource in zbbus_alloc_resource().
129285612Sdelphij		 */
130285612Sdelphij		printf("sb_intmap_activate: unable to activate interrupt %d "
131285612Sdelphij		       "for device %s%d rid %d.\n", intrnum,
132285612Sdelphij		       device_get_name(dev), device_get_unit(dev), rid);
133285612Sdelphij	}
13454359Sroberto}
13554359Sroberto
136285612Sdelphijstruct zbbus_devinfo {
137285612Sdelphij	struct resource_list resources;
138285612Sdelphij};
139285612Sdelphij
140285612Sdelphijstatic MALLOC_DEFINE(M_ZBBUSDEV, "zbbusdev", "zbbusdev");
141285612Sdelphij
142285612Sdelphijstatic int
143285612Sdelphijzbbus_probe(device_t dev)
144285612Sdelphij{
145285612Sdelphij
146285612Sdelphij	device_set_desc(dev, "Broadcom/Sibyte ZBbus");
14754359Sroberto	return (0);
148285612Sdelphij}
149285612Sdelphij
150285612Sdelphijstatic int
151285612Sdelphijzbbus_attach(device_t dev)
15254359Sroberto{
153285612Sdelphij
154285612Sdelphij	if (bootverbose) {
15554359Sroberto		device_printf(dev, "attached.\n");
156285612Sdelphij	}
157285612Sdelphij
158285612Sdelphij	bus_generic_probe(dev);
159285612Sdelphij	bus_enumerate_hinted_children(dev);
160285612Sdelphij	bus_generic_attach(dev);
16154359Sroberto
16254359Sroberto	return (0);
163285612Sdelphij}
164285612Sdelphij
165285612Sdelphijstatic void
166285612Sdelphijzbbus_hinted_child(device_t bus, const char *dname, int dunit)
167285612Sdelphij{
168285612Sdelphij	device_t child;
169285612Sdelphij	long maddr, msize;
170285612Sdelphij	int err, irq;
171285612Sdelphij
17254359Sroberto	if (resource_disabled(dname, dunit))
173285612Sdelphij		return;
174285612Sdelphij
17554359Sroberto	child = BUS_ADD_CHILD(bus, 0, dname, dunit);
176285612Sdelphij	if (child == NULL) {
177285612Sdelphij		panic("zbbus: could not add child %s unit %d\n", dname, dunit);
178285612Sdelphij	}
17954359Sroberto
180285612Sdelphij	if (bootverbose)
181285612Sdelphij		device_printf(bus, "Adding hinted child %s%d\n", dname, dunit);
182285612Sdelphij
183285612Sdelphij	/*
18454359Sroberto	 * Assign any pre-defined resources to the child.
185285612Sdelphij	 */
186285612Sdelphij	if (resource_long_value(dname, dunit, "msize", &msize) == 0 &&
187285612Sdelphij	    resource_long_value(dname, dunit, "maddr", &maddr) == 0) {
188285612Sdelphij		if (bootverbose) {
18954359Sroberto			device_printf(bus, "Assigning memory resource "
19054359Sroberto					   "0x%0lx/%ld to child %s%d\n",
191285612Sdelphij					   maddr, msize, dname, dunit);
192285612Sdelphij		}
19354359Sroberto		err = bus_set_resource(child, SYS_RES_MEMORY, 0, maddr, msize);
194285612Sdelphij		if (err) {
195285612Sdelphij			device_printf(bus, "Unable to set memory resource "
196285612Sdelphij					   "0x%0lx/%ld for child %s%d: %d\n",
197285612Sdelphij					   maddr, msize, dname, dunit, err);
198285612Sdelphij		}
199285612Sdelphij	}
200285612Sdelphij
201285612Sdelphij	if (resource_int_value(dname, dunit, "irq", &irq) == 0) {
20254359Sroberto		if (bootverbose) {
203285612Sdelphij			device_printf(bus, "Assigning irq resource %d to "
204285612Sdelphij					   "child %s%d\n", irq, dname, dunit);
20554359Sroberto		}
206285612Sdelphij		err = bus_set_resource(child, SYS_RES_IRQ, 0, irq, 1);
207285612Sdelphij		if (err) {
208285612Sdelphij			device_printf(bus, "Unable to set irq resource %d"
20954359Sroberto					   "for child %s%d: %d\n",
210285612Sdelphij					   irq, dname, dunit, err);
211285612Sdelphij		}
21254359Sroberto	}
21354359Sroberto}
21454359Sroberto
21554359Srobertostatic struct resource *
21654359Srobertozbbus_alloc_resource(device_t bus, device_t child, int type, int *rid,
21754359Sroberto		     u_long start, u_long end, u_long count, u_int flags)
21854359Sroberto{
21954359Sroberto	struct resource *res;
22054359Sroberto	int intrnum, intsrc, isdefault;
221285612Sdelphij	struct resource_list *rl;
222285612Sdelphij	struct resource_list_entry *rle;
223285612Sdelphij	struct zbbus_devinfo *dinfo;
22454359Sroberto
225285612Sdelphij	isdefault = (start == 0UL && end == ~0UL && count == 1);
22654359Sroberto
22754359Sroberto	/*
22854359Sroberto	 * Our direct child is asking for a default resource allocation.
229285612Sdelphij	 */
230285612Sdelphij	if (device_get_parent(child) == bus) {
231285612Sdelphij		dinfo = device_get_ivars(child);
232285612Sdelphij		rl = &dinfo->resources;
23354359Sroberto		rle = resource_list_find(rl, type, *rid);
23454359Sroberto		if (rle) {
23554359Sroberto			if (rle->res)
23654359Sroberto				panic("zbbus_alloc_resource: resource is busy");
237285612Sdelphij			if (isdefault) {
238285612Sdelphij				start = rle->start;
239285612Sdelphij				count = ulmax(count, rle->count);
240285612Sdelphij				end = ulmax(rle->end, start + count - 1);
241285612Sdelphij			}
242285612Sdelphij		} else {
24354359Sroberto			if (isdefault) {
244285612Sdelphij				/*
245285612Sdelphij				 * Our child is requesting a default
246285612Sdelphij				 * resource allocation but we don't have the
247285612Sdelphij				 * 'type/rid' tuple in the resource list.
248285612Sdelphij				 *
249285612Sdelphij				 * We have to fail the resource allocation.
250285612Sdelphij				 */
25154359Sroberto				return (NULL);
25254359Sroberto			} else {
25354359Sroberto				/*
25454359Sroberto				 * The child is requesting a non-default
25554359Sroberto				 * resource. We just pass the request up
25654359Sroberto				 * to our parent. If the resource allocation
257285612Sdelphij				 * succeeds we will create a resource list
258285612Sdelphij				 * entry corresponding to that resource.
259285612Sdelphij				 */
260285612Sdelphij			}
261285612Sdelphij		}
262285612Sdelphij	} else {
263285612Sdelphij		rl = NULL;
264285612Sdelphij		rle = NULL;
265285612Sdelphij	}
266285612Sdelphij
267285612Sdelphij	/*
268285612Sdelphij	 * nexus doesn't know about the interrupt mapper and only wants to
269285612Sdelphij	 * see the hard irq numbers [0-6]. We translate from the interrupt
270285612Sdelphij	 * source presented to the mapper to the interrupt number presented
271285612Sdelphij	 * to the cpu.
272285612Sdelphij	 */
273285612Sdelphij	if ((count == 1) && (type == SYS_RES_IRQ)) {
274285612Sdelphij		intsrc = start;
275285612Sdelphij		intrnum = sb_route_intsrc(intsrc);
276285612Sdelphij		start = end = intrnum;
277285612Sdelphij	} else {
278285612Sdelphij		intsrc = -1;		/* satisfy gcc */
279285612Sdelphij		intrnum = -1;
280285612Sdelphij	}
281285612Sdelphij
282285612Sdelphij	res = bus_generic_alloc_resource(bus, child, type, rid,
283285612Sdelphij 					 start, end, count, flags);
284285612Sdelphij
285285612Sdelphij	/*
286285612Sdelphij	 * Keep track of the input into the interrupt mapper that maps
287285612Sdelphij	 * to the resource allocated by 'child' with resource id 'rid'.
288285612Sdelphij	 *
28954359Sroberto	 * If we don't record the mapping here then we won't be able to
29054359Sroberto	 * locate the interrupt source when bus_setup_intr(child,rid) is
29154359Sroberto	 * called.
29254359Sroberto	 */
29354359Sroberto	if (res != NULL && intrnum != -1)
29454359Sroberto		sb_intmap_add(intrnum, child, rman_get_rid(res), intsrc);
29554359Sroberto
29654359Sroberto	/*
297285612Sdelphij	 * If a non-default resource allocation by our child was successful
298285612Sdelphij	 * then keep track of the resource in the resource list associated
29954359Sroberto	 * with the child.
300285612Sdelphij	 */
301285612Sdelphij	if (res != NULL && rle == NULL && device_get_parent(child) == bus) {
302285612Sdelphij		resource_list_add(rl, type, *rid, start, end, count);
30354359Sroberto		rle = resource_list_find(rl, type, *rid);
304285612Sdelphij		if (rle == NULL)
305285612Sdelphij			panic("zbbus_alloc_resource: cannot find resource");
306285612Sdelphij	}
307285612Sdelphij
30854359Sroberto	if (rle != NULL) {
309285612Sdelphij		KASSERT(device_get_parent(child) == bus,
310285612Sdelphij			("rle should be NULL for passthru device"));
311285612Sdelphij		rle->res = res;
31254359Sroberto		if (rle->res) {
313285612Sdelphij			rle->start = rman_get_start(rle->res);
31454359Sroberto			rle->end = rman_get_end(rle->res);
31554359Sroberto			rle->count = count;
31654359Sroberto		}
31754359Sroberto	}
31854359Sroberto
31954359Sroberto	return (res);
32054359Sroberto}
32154359Sroberto
32254359Srobertostatic int
32354359Srobertozbbus_setup_intr(device_t dev, device_t child, struct resource *irq, int flags,
32454359Sroberto		 driver_filter_t *filter, driver_intr_t *intr, void *arg,
32554359Sroberto		 void **cookiep)
326285612Sdelphij{
327285612Sdelphij	int error;
328285612Sdelphij
32954359Sroberto	error = bus_generic_setup_intr(dev, child, irq, flags,
330285612Sdelphij				       filter, intr, arg, cookiep);
331285612Sdelphij	if (error == 0)
332285612Sdelphij		sb_intmap_activate(rman_get_start(irq), child,
333285612Sdelphij				   rman_get_rid(irq));
334285612Sdelphij
335285612Sdelphij	return (error);
336285612Sdelphij}
337285612Sdelphij
338285612Sdelphijstatic device_t
339285612Sdelphijzbbus_add_child(device_t bus, int order, const char *name, int unit)
340285612Sdelphij{
341285612Sdelphij	device_t child;
342285612Sdelphij	struct zbbus_devinfo *dinfo;
343285612Sdelphij
344285612Sdelphij	child = device_add_child_ordered(bus, order, name, unit);
345285612Sdelphij	if (child != NULL) {
34654359Sroberto		dinfo = malloc(sizeof(struct zbbus_devinfo), M_ZBBUSDEV,
347285612Sdelphij			       M_WAITOK | M_ZERO);
348285612Sdelphij		resource_list_init(&dinfo->resources);
349285612Sdelphij		device_set_ivars(child, dinfo);
350285612Sdelphij	}
351285612Sdelphij
352285612Sdelphij	return (child);
353285612Sdelphij}
354285612Sdelphij
355285612Sdelphijstatic struct resource_list *
356285612Sdelphijzbbus_get_resource_list(device_t dev, device_t child)
357285612Sdelphij{
358285612Sdelphij	struct zbbus_devinfo *dinfo = device_get_ivars(child);
359285612Sdelphij
360285612Sdelphij	return (&dinfo->resources);
361285612Sdelphij}
362285612Sdelphij
363285612Sdelphijstatic device_method_t zbbus_methods[] ={
364285612Sdelphij	/* Device interface */
365285612Sdelphij	DEVMETHOD(device_probe,		zbbus_probe),
366285612Sdelphij	DEVMETHOD(device_attach,	zbbus_attach),
367285612Sdelphij	DEVMETHOD(device_detach,	bus_generic_detach),
368285612Sdelphij	DEVMETHOD(device_shutdown,	bus_generic_shutdown),
369285612Sdelphij	DEVMETHOD(device_suspend,	bus_generic_suspend),
370285612Sdelphij	DEVMETHOD(device_resume,	bus_generic_resume),
371285612Sdelphij
372285612Sdelphij	/* Bus interface */
373285612Sdelphij	DEVMETHOD(bus_alloc_resource,	zbbus_alloc_resource),
374285612Sdelphij	DEVMETHOD(bus_activate_resource, bus_generic_activate_resource),
375285612Sdelphij	DEVMETHOD(bus_deactivate_resource, bus_generic_deactivate_resource),
376285612Sdelphij	DEVMETHOD(bus_release_resource,	bus_generic_release_resource),
377285612Sdelphij	DEVMETHOD(bus_get_resource_list,zbbus_get_resource_list),
378285612Sdelphij	DEVMETHOD(bus_set_resource,	bus_generic_rl_set_resource),
379285612Sdelphij	DEVMETHOD(bus_get_resource,	bus_generic_rl_get_resource),
380285612Sdelphij	DEVMETHOD(bus_delete_resource,	bus_generic_rl_delete_resource),
381285612Sdelphij	DEVMETHOD(bus_setup_intr,	zbbus_setup_intr),
382285612Sdelphij	DEVMETHOD(bus_teardown_intr,	bus_generic_teardown_intr),
383285612Sdelphij	DEVMETHOD(bus_add_child,	zbbus_add_child),
384285612Sdelphij	DEVMETHOD(bus_hinted_child,	zbbus_hinted_child),
385285612Sdelphij
386285612Sdelphij	{ 0, 0 }
387285612Sdelphij};
388285612Sdelphij
389285612Sdelphijstatic driver_t zbbus_driver = {
390285612Sdelphij	"zbbus",
391285612Sdelphij	zbbus_methods
392285612Sdelphij};
393285612Sdelphij
394285612Sdelphijstatic devclass_t zbbus_devclass;
395285612Sdelphij
396285612SdelphijDRIVER_MODULE(zbbus, nexus, zbbus_driver, zbbus_devclass, 0, 0);
397285612Sdelphij