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