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