sb_zbbus.c revision 265999
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/cdefs.h> 28__FBSDID("$FreeBSD: stable/10/sys/mips/sibyte/sb_zbbus.c 265999 2014-05-14 01:35:43Z ian $"); 29 30#include <sys/param.h> 31#include <sys/kernel.h> 32#include <sys/systm.h> 33#include <sys/module.h> 34#include <sys/bus.h> 35#include <sys/malloc.h> 36#include <sys/rman.h> 37#include <sys/lock.h> 38#include <sys/mutex.h> 39 40#include <machine/resource.h> 41#include <machine/intr_machdep.h> 42 43#include "sb_scd.h" 44 45static MALLOC_DEFINE(M_INTMAP, "sb1250 intmap", "Sibyte 1250 Interrupt Mapper"); 46 47static struct mtx zbbus_intr_mtx; 48MTX_SYSINIT(zbbus_intr_mtx, &zbbus_intr_mtx, "zbbus_intr_mask/unmask lock", 49 MTX_SPIN); 50 51/* 52 * This array holds the mapping between a MIPS hard interrupt and the 53 * interrupt sources that feed into that it. 54 */ 55static uint64_t hardint_to_intsrc_mask[NHARD_IRQS]; 56 57struct sb_intmap { 58 int intsrc; /* interrupt mapper register number (0 - 63) */ 59 int hardint; /* cpu interrupt from 0 to NHARD_IRQS - 1 */ 60 61 /* 62 * The device that the interrupt belongs to. Note that multiple 63 * devices may share an interrupt. For e.g. PCI_INT_x lines. 64 * 65 * The device 'dev' in combination with the 'rid' uniquely 66 * identify this interrupt source. 67 */ 68 device_t dev; 69 int rid; 70 71 SLIST_ENTRY(sb_intmap) next; 72}; 73 74static SLIST_HEAD(, sb_intmap) sb_intmap_head; 75 76static struct sb_intmap * 77sb_intmap_lookup(int intrnum, device_t dev, int rid) 78{ 79 struct sb_intmap *map; 80 81 SLIST_FOREACH(map, &sb_intmap_head, next) { 82 if (dev == map->dev && rid == map->rid && 83 intrnum == map->hardint) 84 break; 85 } 86 return (map); 87} 88 89/* 90 * Keep track of which (dev,rid,hardint) tuple is using the interrupt source. 91 * 92 * We don't actually unmask the interrupt source until the device calls 93 * a bus_setup_intr() on the resource. 94 */ 95static void 96sb_intmap_add(int intrnum, device_t dev, int rid, int intsrc) 97{ 98 struct sb_intmap *map; 99 100 KASSERT(intrnum >= 0 && intrnum < NHARD_IRQS, 101 ("intrnum is out of range: %d", intrnum)); 102 103 map = sb_intmap_lookup(intrnum, dev, rid); 104 if (map) { 105 KASSERT(intsrc == map->intsrc, 106 ("%s%d allocating SYS_RES_IRQ resource with rid %d " 107 "with a different intsrc (%d versus %d)", 108 device_get_name(dev), device_get_unit(dev), rid, 109 intsrc, map->intsrc)); 110 return; 111 } 112 113 map = malloc(sizeof(*map), M_INTMAP, M_WAITOK | M_ZERO); 114 map->intsrc = intsrc; 115 map->hardint = intrnum; 116 map->dev = dev; 117 map->rid = rid; 118 119 SLIST_INSERT_HEAD(&sb_intmap_head, map, next); 120} 121 122static void 123sb_intmap_activate(int intrnum, device_t dev, int rid) 124{ 125 struct sb_intmap *map; 126 127 KASSERT(intrnum >= 0 && intrnum < NHARD_IRQS, 128 ("intrnum is out of range: %d", intrnum)); 129 130 map = sb_intmap_lookup(intrnum, dev, rid); 131 if (map) { 132 /* 133 * Deliver all interrupts to CPU0. 134 */ 135 mtx_lock_spin(&zbbus_intr_mtx); 136 hardint_to_intsrc_mask[intrnum] |= 1ULL << map->intsrc; 137 sb_enable_intsrc(0, map->intsrc); 138 mtx_unlock_spin(&zbbus_intr_mtx); 139 } else { 140 /* 141 * In zbbus_setup_intr() we blindly call sb_intmap_activate() 142 * for every interrupt activation that comes our way. 143 * 144 * We might end up here if we did not "hijack" the SYS_RES_IRQ 145 * resource in zbbus_alloc_resource(). 146 */ 147 printf("sb_intmap_activate: unable to activate interrupt %d " 148 "for device %s%d rid %d.\n", intrnum, 149 device_get_name(dev), device_get_unit(dev), rid); 150 } 151} 152 153/* 154 * Replace the default interrupt mask and unmask routines in intr_machdep.c 155 * with routines that are SMP-friendly. In contrast to the default mask/unmask 156 * routines in intr_machdep.c these routines do not change the SR.int_mask bits. 157 * 158 * Instead they use the interrupt mapper to either mask or unmask all 159 * interrupt sources feeding into a particular interrupt line of the processor. 160 * 161 * This means that these routines have an identical effect irrespective of 162 * which cpu is executing them. This is important because the ithread may 163 * be scheduled to run on either of the cpus. 164 */ 165static void 166zbbus_intr_mask(void *arg) 167{ 168 uint64_t mask; 169 int irq; 170 171 irq = (uintptr_t)arg; 172 173 mtx_lock_spin(&zbbus_intr_mtx); 174 175 mask = sb_read_intsrc_mask(0); 176 mask |= hardint_to_intsrc_mask[irq]; 177 sb_write_intsrc_mask(0, mask); 178 179 mtx_unlock_spin(&zbbus_intr_mtx); 180} 181 182static void 183zbbus_intr_unmask(void *arg) 184{ 185 uint64_t mask; 186 int irq; 187 188 irq = (uintptr_t)arg; 189 190 mtx_lock_spin(&zbbus_intr_mtx); 191 192 mask = sb_read_intsrc_mask(0); 193 mask &= ~hardint_to_intsrc_mask[irq]; 194 sb_write_intsrc_mask(0, mask); 195 196 mtx_unlock_spin(&zbbus_intr_mtx); 197} 198 199struct zbbus_devinfo { 200 struct resource_list resources; 201}; 202 203static MALLOC_DEFINE(M_ZBBUSDEV, "zbbusdev", "zbbusdev"); 204 205static int 206zbbus_probe(device_t dev) 207{ 208 209 device_set_desc(dev, "Broadcom/Sibyte ZBbus"); 210 return (BUS_PROBE_NOWILDCARD); 211} 212 213static int 214zbbus_attach(device_t dev) 215{ 216 217 if (bootverbose) { 218 device_printf(dev, "attached.\n"); 219 } 220 221 cpu_set_hardintr_mask_func(zbbus_intr_mask); 222 cpu_set_hardintr_unmask_func(zbbus_intr_unmask); 223 224 bus_generic_probe(dev); 225 bus_enumerate_hinted_children(dev); 226 bus_generic_attach(dev); 227 228 return (0); 229} 230 231static void 232zbbus_hinted_child(device_t bus, const char *dname, int dunit) 233{ 234 device_t child; 235 long maddr, msize; 236 int err, irq; 237 238 if (resource_disabled(dname, dunit)) 239 return; 240 241 child = BUS_ADD_CHILD(bus, 0, dname, dunit); 242 if (child == NULL) { 243 panic("zbbus: could not add child %s unit %d\n", dname, dunit); 244 } 245 246 if (bootverbose) 247 device_printf(bus, "Adding hinted child %s%d\n", dname, dunit); 248 249 /* 250 * Assign any pre-defined resources to the child. 251 */ 252 if (resource_long_value(dname, dunit, "msize", &msize) == 0 && 253 resource_long_value(dname, dunit, "maddr", &maddr) == 0) { 254 if (bootverbose) { 255 device_printf(bus, "Assigning memory resource " 256 "0x%0lx/%ld to child %s%d\n", 257 maddr, msize, dname, dunit); 258 } 259 err = bus_set_resource(child, SYS_RES_MEMORY, 0, maddr, msize); 260 if (err) { 261 device_printf(bus, "Unable to set memory resource " 262 "0x%0lx/%ld for child %s%d: %d\n", 263 maddr, msize, dname, dunit, err); 264 } 265 } 266 267 if (resource_int_value(dname, dunit, "irq", &irq) == 0) { 268 if (bootverbose) { 269 device_printf(bus, "Assigning irq resource %d to " 270 "child %s%d\n", irq, dname, dunit); 271 } 272 err = bus_set_resource(child, SYS_RES_IRQ, 0, irq, 1); 273 if (err) { 274 device_printf(bus, "Unable to set irq resource %d" 275 "for child %s%d: %d\n", 276 irq, dname, dunit, err); 277 } 278 } 279} 280 281static struct resource * 282zbbus_alloc_resource(device_t bus, device_t child, int type, int *rid, 283 u_long start, u_long end, u_long count, u_int flags) 284{ 285 struct resource *res; 286 int intrnum, intsrc, isdefault; 287 struct resource_list *rl; 288 struct resource_list_entry *rle; 289 struct zbbus_devinfo *dinfo; 290 291 isdefault = (start == 0UL && end == ~0UL && count == 1); 292 293 /* 294 * Our direct child is asking for a default resource allocation. 295 */ 296 if (device_get_parent(child) == bus) { 297 dinfo = device_get_ivars(child); 298 rl = &dinfo->resources; 299 rle = resource_list_find(rl, type, *rid); 300 if (rle) { 301 if (rle->res) 302 panic("zbbus_alloc_resource: resource is busy"); 303 if (isdefault) { 304 start = rle->start; 305 count = ulmax(count, rle->count); 306 end = ulmax(rle->end, start + count - 1); 307 } 308 } else { 309 if (isdefault) { 310 /* 311 * Our child is requesting a default 312 * resource allocation but we don't have the 313 * 'type/rid' tuple in the resource list. 314 * 315 * We have to fail the resource allocation. 316 */ 317 return (NULL); 318 } else { 319 /* 320 * The child is requesting a non-default 321 * resource. We just pass the request up 322 * to our parent. If the resource allocation 323 * succeeds we will create a resource list 324 * entry corresponding to that resource. 325 */ 326 } 327 } 328 } else { 329 rl = NULL; 330 rle = NULL; 331 } 332 333 /* 334 * nexus doesn't know about the interrupt mapper and only wants to 335 * see the hard irq numbers [0-6]. We translate from the interrupt 336 * source presented to the mapper to the interrupt number presented 337 * to the cpu. 338 */ 339 if ((count == 1) && (type == SYS_RES_IRQ)) { 340 intsrc = start; 341 intrnum = sb_route_intsrc(intsrc); 342 start = end = intrnum; 343 } else { 344 intsrc = -1; /* satisfy gcc */ 345 intrnum = -1; 346 } 347 348 res = bus_generic_alloc_resource(bus, child, type, rid, 349 start, end, count, flags); 350 351 /* 352 * Keep track of the input into the interrupt mapper that maps 353 * to the resource allocated by 'child' with resource id 'rid'. 354 * 355 * If we don't record the mapping here then we won't be able to 356 * locate the interrupt source when bus_setup_intr(child,rid) is 357 * called. 358 */ 359 if (res != NULL && intrnum != -1) 360 sb_intmap_add(intrnum, child, rman_get_rid(res), intsrc); 361 362 /* 363 * If a non-default resource allocation by our child was successful 364 * then keep track of the resource in the resource list associated 365 * with the child. 366 */ 367 if (res != NULL && rle == NULL && device_get_parent(child) == bus) { 368 resource_list_add(rl, type, *rid, start, end, count); 369 rle = resource_list_find(rl, type, *rid); 370 if (rle == NULL) 371 panic("zbbus_alloc_resource: cannot find resource"); 372 } 373 374 if (rle != NULL) { 375 KASSERT(device_get_parent(child) == bus, 376 ("rle should be NULL for passthru device")); 377 rle->res = res; 378 if (rle->res) { 379 rle->start = rman_get_start(rle->res); 380 rle->end = rman_get_end(rle->res); 381 rle->count = count; 382 } 383 } 384 385 return (res); 386} 387 388static int 389zbbus_setup_intr(device_t dev, device_t child, struct resource *irq, int flags, 390 driver_filter_t *filter, driver_intr_t *intr, void *arg, 391 void **cookiep) 392{ 393 int error; 394 395 error = bus_generic_setup_intr(dev, child, irq, flags, 396 filter, intr, arg, cookiep); 397 if (error == 0) 398 sb_intmap_activate(rman_get_start(irq), child, 399 rman_get_rid(irq)); 400 401 return (error); 402} 403 404static device_t 405zbbus_add_child(device_t bus, u_int order, const char *name, int unit) 406{ 407 device_t child; 408 struct zbbus_devinfo *dinfo; 409 410 child = device_add_child_ordered(bus, order, name, unit); 411 if (child != NULL) { 412 dinfo = malloc(sizeof(struct zbbus_devinfo), M_ZBBUSDEV, 413 M_WAITOK | M_ZERO); 414 resource_list_init(&dinfo->resources); 415 device_set_ivars(child, dinfo); 416 } 417 418 return (child); 419} 420 421static struct resource_list * 422zbbus_get_resource_list(device_t dev, device_t child) 423{ 424 struct zbbus_devinfo *dinfo = device_get_ivars(child); 425 426 return (&dinfo->resources); 427} 428 429static device_method_t zbbus_methods[] ={ 430 /* Device interface */ 431 DEVMETHOD(device_probe, zbbus_probe), 432 DEVMETHOD(device_attach, zbbus_attach), 433 DEVMETHOD(device_detach, bus_generic_detach), 434 DEVMETHOD(device_shutdown, bus_generic_shutdown), 435 DEVMETHOD(device_suspend, bus_generic_suspend), 436 DEVMETHOD(device_resume, bus_generic_resume), 437 438 /* Bus interface */ 439 DEVMETHOD(bus_alloc_resource, zbbus_alloc_resource), 440 DEVMETHOD(bus_activate_resource, bus_generic_activate_resource), 441 DEVMETHOD(bus_deactivate_resource, bus_generic_deactivate_resource), 442 DEVMETHOD(bus_release_resource, bus_generic_release_resource), 443 DEVMETHOD(bus_get_resource_list,zbbus_get_resource_list), 444 DEVMETHOD(bus_set_resource, bus_generic_rl_set_resource), 445 DEVMETHOD(bus_get_resource, bus_generic_rl_get_resource), 446 DEVMETHOD(bus_delete_resource, bus_generic_rl_delete_resource), 447 DEVMETHOD(bus_setup_intr, zbbus_setup_intr), 448 DEVMETHOD(bus_teardown_intr, bus_generic_teardown_intr), 449 DEVMETHOD(bus_add_child, zbbus_add_child), 450 DEVMETHOD(bus_hinted_child, zbbus_hinted_child), 451 452 { 0, 0 } 453}; 454 455static driver_t zbbus_driver = { 456 "zbbus", 457 zbbus_methods 458}; 459 460static devclass_t zbbus_devclass; 461 462DRIVER_MODULE(zbbus, nexus, zbbus_driver, zbbus_devclass, 0, 0); 463