sb_zbbus.c revision 202090
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: head/sys/mips/sibyte/sb_zbbus.c 202090 2010-01-11 17:14:46Z 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 hardint; /* cpu interrupt from 0 to NUM_HARD_IRQS - 1 */ 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 63static SLIST_HEAD(, sb_intmap) sb_intmap_head; 64 65static struct sb_intmap * 66sb_intmap_lookup(int intrnum, device_t dev, int rid) 67{ 68 struct sb_intmap *map; 69 70 SLIST_FOREACH(map, &sb_intmap_head, next) { 71 if (dev == map->dev && rid == map->rid && 72 intrnum == map->hardint) 73 break; 74 } 75 return (map); 76} 77 78/* 79 * Keep track of which (dev,rid,hardint) tuple is using the interrupt source. 80 * 81 * We don't actually unmask the interrupt source until the device calls 82 * a bus_setup_intr() on the resource. 83 */ 84static void 85sb_intmap_add(int intrnum, device_t dev, int rid, int intsrc) 86{ 87 struct sb_intmap *map; 88 89 KASSERT(intrnum >= 0 && intrnum < NUM_HARD_IRQS, 90 ("intrnum is out of range: %d", intrnum)); 91 92 map = sb_intmap_lookup(intrnum, dev, rid); 93 if (map) { 94 KASSERT(intsrc == map->intsrc, 95 ("%s%d allocating SYS_RES_IRQ resource with rid %d " 96 "with a different intsrc (%d versus %d)", 97 device_get_name(dev), device_get_unit(dev), rid, 98 intsrc, map->intsrc)); 99 return; 100 } 101 102 map = malloc(sizeof(*map), M_INTMAP, M_WAITOK | M_ZERO); 103 map->intsrc = intsrc; 104 map->hardint = intrnum; 105 map->dev = dev; 106 map->rid = rid; 107 108 SLIST_INSERT_HEAD(&sb_intmap_head, map, next); 109} 110 111static void 112sb_intmap_activate(int intrnum, device_t dev, int rid) 113{ 114 struct sb_intmap *map; 115 116 KASSERT(intrnum >= 0 && intrnum < NUM_HARD_IRQS, 117 ("intrnum is out of range: %d", intrnum)); 118 119 map = sb_intmap_lookup(intrnum, dev, rid); 120 if (map) { 121 sb_enable_intsrc(map->intsrc); 122 } else { 123 /* 124 * In zbbus_setup_intr() we blindly call sb_intmap_activate() 125 * for every interrupt activation that comes our way. 126 * 127 * We might end up here if we did not "hijack" the SYS_RES_IRQ 128 * resource in zbbus_alloc_resource(). 129 */ 130 printf("sb_intmap_activate: unable to activate interrupt %d " 131 "for device %s%d rid %d.\n", intrnum, 132 device_get_name(dev), device_get_unit(dev), rid); 133 } 134} 135 136struct zbbus_devinfo { 137 struct resource_list resources; 138}; 139 140static MALLOC_DEFINE(M_ZBBUSDEV, "zbbusdev", "zbbusdev"); 141 142static int 143zbbus_probe(device_t dev) 144{ 145 146 device_set_desc(dev, "Broadcom/Sibyte ZBbus"); 147 return (0); 148} 149 150static int 151zbbus_attach(device_t dev) 152{ 153 154 if (bootverbose) { 155 device_printf(dev, "attached.\n"); 156 } 157 158 bus_generic_probe(dev); 159 bus_enumerate_hinted_children(dev); 160 bus_generic_attach(dev); 161 162 return (0); 163} 164 165static void 166zbbus_hinted_child(device_t bus, const char *dname, int dunit) 167{ 168 device_t child; 169 long maddr, msize; 170 int err, irq; 171 172 if (resource_disabled(dname, dunit)) 173 return; 174 175 child = BUS_ADD_CHILD(bus, 0, dname, dunit); 176 if (child == NULL) { 177 panic("zbbus: could not add child %s unit %d\n", dname, dunit); 178 } 179 180 if (bootverbose) 181 device_printf(bus, "Adding hinted child %s%d\n", dname, dunit); 182 183 /* 184 * Assign any pre-defined resources to the child. 185 */ 186 if (resource_long_value(dname, dunit, "msize", &msize) == 0 && 187 resource_long_value(dname, dunit, "maddr", &maddr) == 0) { 188 if (bootverbose) { 189 device_printf(bus, "Assigning memory resource " 190 "0x%0lx/%ld to child %s%d\n", 191 maddr, msize, dname, dunit); 192 } 193 err = bus_set_resource(child, SYS_RES_MEMORY, 0, maddr, msize); 194 if (err) { 195 device_printf(bus, "Unable to set memory resource " 196 "0x%0lx/%ld for child %s%d: %d\n", 197 maddr, msize, dname, dunit, err); 198 } 199 } 200 201 if (resource_int_value(dname, dunit, "irq", &irq) == 0) { 202 if (bootverbose) { 203 device_printf(bus, "Assigning irq resource %d to " 204 "child %s%d\n", irq, dname, dunit); 205 } 206 err = bus_set_resource(child, SYS_RES_IRQ, 0, irq, 1); 207 if (err) { 208 device_printf(bus, "Unable to set irq resource %d" 209 "for child %s%d: %d\n", 210 irq, dname, dunit, err); 211 } 212 } 213} 214 215static struct resource * 216zbbus_alloc_resource(device_t bus, device_t child, int type, int *rid, 217 u_long start, u_long end, u_long count, u_int flags) 218{ 219 struct resource *res; 220 int intrnum, intsrc, isdefault; 221 struct resource_list *rl; 222 struct resource_list_entry *rle; 223 struct zbbus_devinfo *dinfo; 224 225 isdefault = (start == 0UL && end == ~0UL && count == 1); 226 227 /* 228 * Our direct child is asking for a default resource allocation. 229 */ 230 if (device_get_parent(child) == bus) { 231 dinfo = device_get_ivars(child); 232 rl = &dinfo->resources; 233 rle = resource_list_find(rl, type, *rid); 234 if (rle) { 235 if (rle->res) 236 panic("zbbus_alloc_resource: resource is busy"); 237 if (isdefault) { 238 start = rle->start; 239 count = ulmax(count, rle->count); 240 end = ulmax(rle->end, start + count - 1); 241 } 242 } else { 243 if (isdefault) { 244 /* 245 * Our child is requesting a default 246 * resource allocation but we don't have the 247 * 'type/rid' tuple in the resource list. 248 * 249 * We have to fail the resource allocation. 250 */ 251 return (NULL); 252 } else { 253 /* 254 * The child is requesting a non-default 255 * resource. We just pass the request up 256 * to our parent. If the resource allocation 257 * succeeds we will create a resource list 258 * entry corresponding to that resource. 259 */ 260 } 261 } 262 } else { 263 rl = NULL; 264 rle = NULL; 265 } 266 267 /* 268 * nexus doesn't know about the interrupt mapper and only wants to 269 * see the hard irq numbers [0-6]. We translate from the interrupt 270 * source presented to the mapper to the interrupt number presented 271 * to the cpu. 272 */ 273 if ((count == 1) && (type == SYS_RES_IRQ)) { 274 intsrc = start; 275 intrnum = sb_route_intsrc(intsrc); 276 start = end = intrnum; 277 } else { 278 intsrc = -1; /* satisfy gcc */ 279 intrnum = -1; 280 } 281 282 res = bus_generic_alloc_resource(bus, child, type, rid, 283 start, end, count, flags); 284 285 /* 286 * Keep track of the input into the interrupt mapper that maps 287 * to the resource allocated by 'child' with resource id 'rid'. 288 * 289 * If we don't record the mapping here then we won't be able to 290 * locate the interrupt source when bus_setup_intr(child,rid) is 291 * called. 292 */ 293 if (res != NULL && intrnum != -1) 294 sb_intmap_add(intrnum, child, rman_get_rid(res), intsrc); 295 296 /* 297 * If a non-default resource allocation by our child was successful 298 * then keep track of the resource in the resource list associated 299 * with the child. 300 */ 301 if (res != NULL && rle == NULL && device_get_parent(child) == bus) { 302 resource_list_add(rl, type, *rid, start, end, count); 303 rle = resource_list_find(rl, type, *rid); 304 if (rle == NULL) 305 panic("zbbus_alloc_resource: cannot find resource"); 306 } 307 308 if (rle != NULL) { 309 KASSERT(device_get_parent(child) == bus, 310 ("rle should be NULL for passthru device")); 311 rle->res = res; 312 if (rle->res) { 313 rle->start = rman_get_start(rle->res); 314 rle->end = rman_get_end(rle->res); 315 rle->count = count; 316 } 317 } 318 319 return (res); 320} 321 322static int 323zbbus_setup_intr(device_t dev, device_t child, struct resource *irq, int flags, 324 driver_filter_t *filter, driver_intr_t *intr, void *arg, 325 void **cookiep) 326{ 327 int error; 328 329 error = bus_generic_setup_intr(dev, child, irq, flags, 330 filter, intr, arg, cookiep); 331 if (error == 0) 332 sb_intmap_activate(rman_get_start(irq), child, 333 rman_get_rid(irq)); 334 335 return (error); 336} 337 338static device_t 339zbbus_add_child(device_t bus, int order, const char *name, int unit) 340{ 341 device_t child; 342 struct zbbus_devinfo *dinfo; 343 344 child = device_add_child_ordered(bus, order, name, unit); 345 if (child != NULL) { 346 dinfo = malloc(sizeof(struct zbbus_devinfo), M_ZBBUSDEV, 347 M_WAITOK | M_ZERO); 348 resource_list_init(&dinfo->resources); 349 device_set_ivars(child, dinfo); 350 } 351 352 return (child); 353} 354 355static struct resource_list * 356zbbus_get_resource_list(device_t dev, device_t child) 357{ 358 struct zbbus_devinfo *dinfo = device_get_ivars(child); 359 360 return (&dinfo->resources); 361} 362 363static device_method_t zbbus_methods[] ={ 364 /* Device interface */ 365 DEVMETHOD(device_probe, zbbus_probe), 366 DEVMETHOD(device_attach, zbbus_attach), 367 DEVMETHOD(device_detach, bus_generic_detach), 368 DEVMETHOD(device_shutdown, bus_generic_shutdown), 369 DEVMETHOD(device_suspend, bus_generic_suspend), 370 DEVMETHOD(device_resume, bus_generic_resume), 371 372 /* Bus interface */ 373 DEVMETHOD(bus_alloc_resource, zbbus_alloc_resource), 374 DEVMETHOD(bus_activate_resource, bus_generic_activate_resource), 375 DEVMETHOD(bus_deactivate_resource, bus_generic_deactivate_resource), 376 DEVMETHOD(bus_release_resource, bus_generic_release_resource), 377 DEVMETHOD(bus_get_resource_list,zbbus_get_resource_list), 378 DEVMETHOD(bus_set_resource, bus_generic_rl_set_resource), 379 DEVMETHOD(bus_get_resource, bus_generic_rl_get_resource), 380 DEVMETHOD(bus_delete_resource, bus_generic_rl_delete_resource), 381 DEVMETHOD(bus_setup_intr, zbbus_setup_intr), 382 DEVMETHOD(bus_teardown_intr, bus_generic_teardown_intr), 383 DEVMETHOD(bus_add_child, zbbus_add_child), 384 DEVMETHOD(bus_hinted_child, zbbus_hinted_child), 385 386 { 0, 0 } 387}; 388 389static driver_t zbbus_driver = { 390 "zbbus", 391 zbbus_methods 392}; 393 394static devclass_t zbbus_devclass; 395 396DRIVER_MODULE(zbbus, nexus, zbbus_driver, zbbus_devclass, 0, 0); 397