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