sb_zbbus.c revision 195333
1/*-
2 * Copyright (c) 2009 Neelkanth Natu
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 *    notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 *    notice, this list of conditions and the following disclaimer in the
12 *    documentation and/or other materials provided with the distribution.
13 *
14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24 * SUCH DAMAGE.
25 */
26
27#include <sys/param.h>
28#include <sys/kernel.h>
29#include <sys/systm.h>
30#include <sys/module.h>
31#include <sys/bus.h>
32#include <sys/malloc.h>
33#include <sys/rman.h>
34
35#include <machine/resource.h>
36#include <machine/intr_machdep.h>
37
38#include "sb_scd.h"
39
40__FBSDID("$FreeBSD: projects/mips/sys/mips/sibyte/sb_zbbus.c 195333 2009-07-04 03:05:48Z imp $");
41
42static MALLOC_DEFINE(M_INTMAP, "sb1250 intmap", "Sibyte 1250 Interrupt Mapper");
43
44#define	NUM_HARD_IRQS	6
45
46struct sb_intmap {
47	int intsrc;		/* interrupt mapper register number (0 - 63) */
48	int active;		/* Does this source generate interrupts? */
49
50	/*
51	 * The device that the interrupt belongs to. Note that multiple
52	 * devices may share an interrupt. For e.g. PCI_INT_x lines.
53	 *
54	 * The device 'dev' in combination with the 'rid' uniquely
55	 * identify this interrupt source.
56	 */
57	device_t dev;
58	int rid;
59
60	SLIST_ENTRY(sb_intmap) next;
61};
62
63/*
64 * We register 'sb_intsrc.isrc' using cpu_register_hard_intsrc() for each
65 * hard interrupt source [0-5].
66 *
67 * The mask/unmask callbacks use the information in 'sb_intmap' to figure
68 * out the corresponding interrupt sources to mask/unmask.
69 */
70struct sb_intsrc {
71	struct intsrc isrc;
72	SLIST_HEAD(, sb_intmap) sb_intmap_head;
73};
74
75static struct sb_intsrc sb_intsrc[NUM_HARD_IRQS];
76
77static struct sb_intmap *
78sb_intmap_lookup(int intrnum, device_t dev, int rid)
79{
80	struct sb_intsrc *isrc;
81	struct sb_intmap *map;
82
83	isrc = &sb_intsrc[intrnum];
84	SLIST_FOREACH(map, &isrc->sb_intmap_head, next) {
85		if (dev == map->dev && rid == map->rid)
86			break;
87	}
88	return (map);
89}
90
91/*
92 * Keep track of which (dev,rid) tuple is using the interrupt source.
93 *
94 * We don't actually unmask the interrupt source until the device calls
95 * a bus_setup_intr() on the resource.
96 */
97static void
98sb_intmap_add(int intrnum, device_t dev, int rid, int intsrc)
99{
100	struct sb_intsrc *isrc;
101	struct sb_intmap *map;
102	register_t sr;
103
104	KASSERT(intrnum >= 0 && intrnum < NUM_HARD_IRQS,
105		("intrnum is out of range: %d", intrnum));
106
107	isrc = &sb_intsrc[intrnum];
108	map = sb_intmap_lookup(intrnum, dev, rid);
109	if (map) {
110		KASSERT(intsrc == map->intsrc,
111			("%s%d allocating SYS_RES_IRQ resource with rid %d "
112			 "with a different intsrc (%d versus %d)",
113			device_get_name(dev), device_get_unit(dev), rid,
114			intsrc, map->intsrc));
115		return;
116	}
117
118	map = malloc(sizeof(*map), M_INTMAP, M_WAITOK | M_ZERO);
119	map->intsrc = intsrc;
120	map->dev = dev;
121	map->rid = rid;
122
123	sr = intr_disable();
124	SLIST_INSERT_HEAD(&isrc->sb_intmap_head, map, next);
125	intr_restore(sr);
126}
127
128static void
129sb_intmap_activate(int intrnum, device_t dev, int rid)
130{
131	struct sb_intmap *map;
132	register_t sr;
133
134	KASSERT(intrnum >= 0 && intrnum < NUM_HARD_IRQS,
135		("intrnum is out of range: %d", intrnum));
136
137	map = sb_intmap_lookup(intrnum, dev, rid);
138	if (map) {
139		/*
140		 * See comments in sb_unmask_func() about disabling cpu intr
141		 */
142		sr = intr_disable();
143		map->active = 1;
144		sb_enable_intsrc(map->intsrc);
145		intr_restore(sr);
146	} else {
147		/*
148		 * In zbbus_setup_intr() we blindly call sb_intmap_activate()
149		 * for every interrupt activation that comes our way.
150		 *
151		 * We might end up here if we did not "hijack" the SYS_RES_IRQ
152		 * resource in zbbus_alloc_resource().
153		 */
154		printf("sb_intmap_activate: unable to activate interrupt %d "
155		       "for device %s%d rid %d.\n", intrnum,
156		       device_get_name(dev), device_get_unit(dev), rid);
157	}
158}
159
160static void
161sb_mask_func(struct intsrc *arg)
162{
163	struct sb_intmap *map;
164	struct sb_intsrc *isrc;
165	uint64_t isrc_bitmap;
166
167	isrc_bitmap = 0;
168	isrc = (struct sb_intsrc *)arg;
169	SLIST_FOREACH(map, &isrc->sb_intmap_head, next) {
170		if (map->active == 0)
171			continue;
172		/*
173		 * If we have already disabled this interrupt source then don't
174		 * do it again. This can happen when multiple devices share
175		 * an interrupt source (e.g. PCI_INT_x).
176		 */
177		if (isrc_bitmap & (1ULL << map->intsrc))
178			continue;
179		sb_disable_intsrc(map->intsrc);
180		isrc_bitmap |= 1ULL << map->intsrc;
181	}
182}
183
184static void
185sb_unmask_func(struct intsrc *arg)
186{
187	struct sb_intmap *map;
188	struct sb_intsrc *sb_isrc;
189	uint64_t isrc_bitmap;
190	register_t sr;
191
192	isrc_bitmap = 0;
193	sb_isrc = (struct sb_intsrc *)arg;
194
195	/*
196	 * Make sure we disable the cpu interrupts when enabling the
197	 * interrupt sources.
198	 *
199	 * This is to prevent a condition where some interrupt sources have
200	 * been enabled (but not all) and one of those interrupt sources
201	 * triggers an interrupt.
202	 *
203	 * If any of the interrupt handlers executes in an ithread then
204	 * cpu_intr() will return with all interrupt sources feeding into
205	 * that cpu irq masked. But when the loop below picks up where it
206	 * left off it will enable the remaining interrupt sources!!!
207	 *
208	 * If the disable the cpu interrupts then this race does not happen.
209	 */
210	sr = intr_disable();
211
212	SLIST_FOREACH(map, &sb_isrc->sb_intmap_head, next) {
213		if (map->active == 0)
214			continue;
215		/*
216		 * If we have already enabled this interrupt source then don't
217		 * do it again. This can happen when multiple devices share
218		 * an interrupt source (e.g. PCI_INT_x).
219		 */
220		if (isrc_bitmap & (1ULL << map->intsrc))
221			continue;
222		sb_enable_intsrc(map->intsrc);
223		isrc_bitmap |= 1ULL << map->intsrc;
224	}
225
226	intr_restore(sr);
227}
228
229struct zbbus_devinfo {
230	struct resource_list resources;
231};
232
233static MALLOC_DEFINE(M_ZBBUSDEV, "zbbusdev", "zbbusdev");
234
235static int
236zbbus_probe(device_t dev)
237{
238
239	device_set_desc(dev, "Broadcom/Sibyte ZBbus");
240	return (0);
241}
242
243static int
244zbbus_attach(device_t dev)
245{
246	int i, error;
247	struct intsrc *isrc;
248
249	if (bootverbose) {
250		device_printf(dev, "attached.\n");
251	}
252
253	for (i = 0; i < NUM_HARD_IRQS; ++i) {
254		isrc = &sb_intsrc[i].isrc;
255		isrc->intrnum = i;
256		isrc->mask_func = sb_mask_func;
257		isrc->unmask_func = sb_unmask_func;
258		error = cpu_register_hard_intsrc(isrc);
259		if (error)
260			panic("Error %d registering intsrc %d", error, i);
261	}
262
263	bus_generic_probe(dev);
264	bus_enumerate_hinted_children(dev);
265	bus_generic_attach(dev);
266
267	return (0);
268}
269
270static void
271zbbus_hinted_child(device_t bus, const char *dname, int dunit)
272{
273	device_t child;
274	long maddr, msize;
275	int err, irq;
276
277	if (resource_disabled(dname, dunit))
278		return;
279
280	child = BUS_ADD_CHILD(bus, 0, dname, dunit);
281	if (child == NULL) {
282		panic("zbbus: could not add child %s unit %d\n", dname, dunit);
283	}
284
285	if (bootverbose)
286		device_printf(bus, "Adding hinted child %s%d\n", dname, dunit);
287
288	/*
289	 * Assign any pre-defined resources to the child.
290	 */
291	if (resource_long_value(dname, dunit, "msize", &msize) == 0 &&
292	    resource_long_value(dname, dunit, "maddr", &maddr) == 0) {
293		if (bootverbose) {
294			device_printf(bus, "Assigning memory resource "
295					   "0x%0lx/%ld to child %s%d\n",
296					   maddr, msize, dname, dunit);
297		}
298		err = bus_set_resource(child, SYS_RES_MEMORY, 0, maddr, msize);
299		if (err) {
300			device_printf(bus, "Unable to set memory resource "
301					   "0x%0lx/%ld for child %s%d: %d\n",
302					   maddr, msize, dname, dunit, err);
303		}
304	}
305
306	if (resource_int_value(dname, dunit, "irq", &irq) == 0) {
307		if (bootverbose) {
308			device_printf(bus, "Assigning irq resource %d to "
309					   "child %s%d\n", irq, dname, dunit);
310		}
311		err = bus_set_resource(child, SYS_RES_IRQ, 0, irq, 1);
312		if (err) {
313			device_printf(bus, "Unable to set irq resource %d"
314					   "for child %s%d: %d\n",
315					   irq, dname, dunit, err);
316		}
317	}
318}
319
320static struct resource *
321zbbus_alloc_resource(device_t bus, device_t child, int type, int *rid,
322		     u_long start, u_long end, u_long count, u_int flags)
323{
324	struct resource *res;
325	int intrnum, intsrc, isdefault;
326	struct resource_list *rl;
327	struct resource_list_entry *rle;
328	struct zbbus_devinfo *dinfo;
329
330	isdefault = (start == 0UL && end == ~0UL && count == 1);
331
332	/*
333	 * Our direct child is asking for a default resource allocation.
334	 */
335	if (device_get_parent(child) == bus) {
336		dinfo = device_get_ivars(child);
337		rl = &dinfo->resources;
338		rle = resource_list_find(rl, type, *rid);
339		if (rle) {
340			if (rle->res)
341				panic("zbbus_alloc_resource: resource is busy");
342			if (isdefault) {
343				start = rle->start;
344				count = ulmax(count, rle->count);
345				end = ulmax(rle->end, start + count - 1);
346			}
347		} else {
348			if (isdefault) {
349				/*
350				 * Our child is requesting a default
351				 * resource allocation but we don't have the
352				 * 'type/rid' tuple in the resource list.
353				 *
354				 * We have to fail the resource allocation.
355				 */
356				return (NULL);
357			} else {
358				/*
359				 * The child is requesting a non-default
360				 * resource. We just pass the request up
361				 * to our parent. If the resource allocation
362				 * succeeds we will create a resource list
363				 * entry corresponding to that resource.
364				 */
365			}
366		}
367	} else {
368		rl = NULL;
369		rle = NULL;
370	}
371
372	/*
373	 * nexus doesn't know about the interrupt mapper and only wants to
374	 * see the hard irq numbers [0-6]. We translate from the interrupt
375	 * source presented to the mapper to the interrupt number presented
376	 * to the cpu.
377	 */
378	if ((count == 1) && (type == SYS_RES_IRQ)) {
379		intsrc = start;
380		intrnum = sb_route_intsrc(intsrc);
381		start = end = intrnum;
382	} else {
383		intsrc = -1;		/* satisfy gcc */
384		intrnum = -1;
385	}
386
387	res = bus_generic_alloc_resource(bus, child, type, rid,
388 					 start, end, count, flags);
389
390	/*
391	 * Keep track of the input into the interrupt mapper that maps
392	 * to the resource allocated by 'child' with resource id 'rid'.
393	 *
394	 * If we don't record the mapping here then we won't be able to
395	 * locate the interrupt source when bus_setup_intr(child,rid) is
396	 * called.
397	 */
398	if (res != NULL && intrnum != -1)
399		sb_intmap_add(intrnum, child, rman_get_rid(res), intsrc);
400
401	/*
402	 * If a non-default resource allocation by our child was successful
403	 * then keep track of the resource in the resource list associated
404	 * with the child.
405	 */
406	if (res != NULL && rle == NULL && device_get_parent(child) == bus) {
407		resource_list_add(rl, type, *rid, start, end, count);
408		rle = resource_list_find(rl, type, *rid);
409		if (rle == NULL)
410			panic("zbbus_alloc_resource: cannot find resource");
411	}
412
413	if (rle != NULL) {
414		KASSERT(device_get_parent(child) == bus,
415			("rle should be NULL for passthru device"));
416		rle->res = res;
417		if (rle->res) {
418			rle->start = rman_get_start(rle->res);
419			rle->end = rman_get_end(rle->res);
420			rle->count = count;
421		}
422	}
423
424	return (res);
425}
426
427static int
428zbbus_setup_intr(device_t dev, device_t child, struct resource *irq, int flags,
429		 driver_filter_t *filter, driver_intr_t *intr, void *arg,
430		 void **cookiep)
431{
432	int error;
433
434	error = bus_generic_setup_intr(dev, child, irq, flags,
435				       filter, intr, arg, cookiep);
436	if (error == 0)
437		sb_intmap_activate(rman_get_start(irq), child,
438				   rman_get_rid(irq));
439
440	return (error);
441}
442
443static device_t
444zbbus_add_child(device_t bus, int order, const char *name, int unit)
445{
446	device_t child;
447	struct zbbus_devinfo *dinfo;
448
449	child = device_add_child_ordered(bus, order, name, unit);
450	if (child != NULL) {
451		dinfo = malloc(sizeof(struct zbbus_devinfo), M_ZBBUSDEV,
452			       M_WAITOK | M_ZERO);
453		resource_list_init(&dinfo->resources);
454		device_set_ivars(child, dinfo);
455	}
456
457	return (child);
458}
459
460static struct resource_list *
461zbbus_get_resource_list(device_t dev, device_t child)
462{
463	struct zbbus_devinfo *dinfo = device_get_ivars(child);
464
465	return (&dinfo->resources);
466}
467
468static device_method_t zbbus_methods[] ={
469	/* Device interface */
470	DEVMETHOD(device_probe,		zbbus_probe),
471	DEVMETHOD(device_attach,	zbbus_attach),
472	DEVMETHOD(device_detach,	bus_generic_detach),
473	DEVMETHOD(device_shutdown,	bus_generic_shutdown),
474	DEVMETHOD(device_suspend,	bus_generic_suspend),
475	DEVMETHOD(device_resume,	bus_generic_resume),
476
477	/* Bus interface */
478	DEVMETHOD(bus_alloc_resource,	zbbus_alloc_resource),
479	DEVMETHOD(bus_activate_resource, bus_generic_activate_resource),
480	DEVMETHOD(bus_deactivate_resource, bus_generic_deactivate_resource),
481	DEVMETHOD(bus_release_resource,	bus_generic_release_resource),
482	DEVMETHOD(bus_get_resource_list,zbbus_get_resource_list),
483	DEVMETHOD(bus_set_resource,	bus_generic_rl_set_resource),
484	DEVMETHOD(bus_get_resource,	bus_generic_rl_get_resource),
485	DEVMETHOD(bus_delete_resource,	bus_generic_rl_delete_resource),
486	DEVMETHOD(bus_setup_intr,	zbbus_setup_intr),
487	DEVMETHOD(bus_teardown_intr,	bus_generic_teardown_intr),
488	DEVMETHOD(bus_add_child,	zbbus_add_child),
489	DEVMETHOD(bus_hinted_child,	zbbus_hinted_child),
490
491	{ 0, 0 }
492};
493
494static driver_t zbbus_driver = {
495	"zbbus",
496	zbbus_methods
497};
498
499static devclass_t zbbus_devclass;
500
501DRIVER_MODULE(zbbus, nexus, zbbus_driver, zbbus_devclass, 0, 0);
502