1195333Simp/*- 2195333Simp * Copyright (c) 2009 Neelkanth Natu 3195333Simp * All rights reserved. 4195333Simp * 5195333Simp * Redistribution and use in source and binary forms, with or without 6195333Simp * modification, are permitted provided that the following conditions 7195333Simp * are met: 8195333Simp * 1. Redistributions of source code must retain the above copyright 9195333Simp * notice, this list of conditions and the following disclaimer. 10195333Simp * 2. Redistributions in binary form must reproduce the above copyright 11195333Simp * notice, this list of conditions and the following disclaimer in the 12195333Simp * documentation and/or other materials provided with the distribution. 13195333Simp * 14195333Simp * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15195333Simp * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16195333Simp * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17195333Simp * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 18195333Simp * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19195333Simp * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20195333Simp * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21195333Simp * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22195333Simp * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23195333Simp * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24195333Simp * SUCH DAMAGE. 25195333Simp */ 26195333Simp 27203697Sneel#include <sys/cdefs.h> 28203697Sneel__FBSDID("$FreeBSD$"); 29203697Sneel 30195333Simp#include <sys/param.h> 31195333Simp#include <sys/kernel.h> 32195333Simp#include <sys/systm.h> 33195333Simp#include <sys/module.h> 34195333Simp#include <sys/bus.h> 35195333Simp#include <sys/malloc.h> 36195333Simp#include <sys/rman.h> 37203697Sneel#include <sys/lock.h> 38203697Sneel#include <sys/mutex.h> 39195333Simp 40195333Simp#include <machine/resource.h> 41195333Simp#include <machine/intr_machdep.h> 42195333Simp 43195333Simp#include "sb_scd.h" 44195333Simp 45195333Simpstatic MALLOC_DEFINE(M_INTMAP, "sb1250 intmap", "Sibyte 1250 Interrupt Mapper"); 46195333Simp 47203697Sneelstatic struct mtx zbbus_intr_mtx; 48203697SneelMTX_SYSINIT(zbbus_intr_mtx, &zbbus_intr_mtx, "zbbus_intr_mask/unmask lock", 49203697Sneel MTX_SPIN); 50195333Simp 51203697Sneel/* 52203697Sneel * This array holds the mapping between a MIPS hard interrupt and the 53203697Sneel * interrupt sources that feed into that it. 54203697Sneel */ 55203697Sneelstatic uint64_t hardint_to_intsrc_mask[NHARD_IRQS]; 56203697Sneel 57195333Simpstruct sb_intmap { 58195333Simp int intsrc; /* interrupt mapper register number (0 - 63) */ 59203697Sneel int hardint; /* cpu interrupt from 0 to NHARD_IRQS - 1 */ 60195333Simp 61195333Simp /* 62195333Simp * The device that the interrupt belongs to. Note that multiple 63195333Simp * devices may share an interrupt. For e.g. PCI_INT_x lines. 64195333Simp * 65195333Simp * The device 'dev' in combination with the 'rid' uniquely 66195333Simp * identify this interrupt source. 67195333Simp */ 68195333Simp device_t dev; 69195333Simp int rid; 70195333Simp 71195333Simp SLIST_ENTRY(sb_intmap) next; 72195333Simp}; 73195333Simp 74202090Simpstatic SLIST_HEAD(, sb_intmap) sb_intmap_head; 75195333Simp 76195333Simpstatic struct sb_intmap * 77195333Simpsb_intmap_lookup(int intrnum, device_t dev, int rid) 78195333Simp{ 79195333Simp struct sb_intmap *map; 80195333Simp 81202090Simp SLIST_FOREACH(map, &sb_intmap_head, next) { 82202090Simp if (dev == map->dev && rid == map->rid && 83202090Simp intrnum == map->hardint) 84195333Simp break; 85195333Simp } 86195333Simp return (map); 87195333Simp} 88195333Simp 89195333Simp/* 90202090Simp * Keep track of which (dev,rid,hardint) tuple is using the interrupt source. 91195333Simp * 92195333Simp * We don't actually unmask the interrupt source until the device calls 93195333Simp * a bus_setup_intr() on the resource. 94195333Simp */ 95195333Simpstatic void 96195333Simpsb_intmap_add(int intrnum, device_t dev, int rid, int intsrc) 97195333Simp{ 98195333Simp struct sb_intmap *map; 99195333Simp 100203697Sneel KASSERT(intrnum >= 0 && intrnum < NHARD_IRQS, 101195333Simp ("intrnum is out of range: %d", intrnum)); 102195333Simp 103195333Simp map = sb_intmap_lookup(intrnum, dev, rid); 104195333Simp if (map) { 105195333Simp KASSERT(intsrc == map->intsrc, 106195333Simp ("%s%d allocating SYS_RES_IRQ resource with rid %d " 107195333Simp "with a different intsrc (%d versus %d)", 108195333Simp device_get_name(dev), device_get_unit(dev), rid, 109195333Simp intsrc, map->intsrc)); 110195333Simp return; 111195333Simp } 112195333Simp 113195333Simp map = malloc(sizeof(*map), M_INTMAP, M_WAITOK | M_ZERO); 114195333Simp map->intsrc = intsrc; 115202090Simp map->hardint = intrnum; 116195333Simp map->dev = dev; 117195333Simp map->rid = rid; 118195333Simp 119202090Simp SLIST_INSERT_HEAD(&sb_intmap_head, map, next); 120195333Simp} 121195333Simp 122195333Simpstatic void 123195333Simpsb_intmap_activate(int intrnum, device_t dev, int rid) 124195333Simp{ 125195333Simp struct sb_intmap *map; 126195333Simp 127203697Sneel KASSERT(intrnum >= 0 && intrnum < NHARD_IRQS, 128195333Simp ("intrnum is out of range: %d", intrnum)); 129195333Simp 130195333Simp map = sb_intmap_lookup(intrnum, dev, rid); 131195333Simp if (map) { 132203697Sneel /* 133203697Sneel * Deliver all interrupts to CPU0. 134203697Sneel */ 135203697Sneel mtx_lock_spin(&zbbus_intr_mtx); 136203697Sneel hardint_to_intsrc_mask[intrnum] |= 1ULL << map->intsrc; 137203509Sneel sb_enable_intsrc(0, map->intsrc); 138203697Sneel mtx_unlock_spin(&zbbus_intr_mtx); 139195333Simp } else { 140195333Simp /* 141195333Simp * In zbbus_setup_intr() we blindly call sb_intmap_activate() 142195333Simp * for every interrupt activation that comes our way. 143195333Simp * 144195333Simp * We might end up here if we did not "hijack" the SYS_RES_IRQ 145195333Simp * resource in zbbus_alloc_resource(). 146195333Simp */ 147195333Simp printf("sb_intmap_activate: unable to activate interrupt %d " 148195333Simp "for device %s%d rid %d.\n", intrnum, 149195333Simp device_get_name(dev), device_get_unit(dev), rid); 150195333Simp } 151195333Simp} 152195333Simp 153203697Sneel/* 154203697Sneel * Replace the default interrupt mask and unmask routines in intr_machdep.c 155203697Sneel * with routines that are SMP-friendly. In contrast to the default mask/unmask 156203697Sneel * routines in intr_machdep.c these routines do not change the SR.int_mask bits. 157203697Sneel * 158203697Sneel * Instead they use the interrupt mapper to either mask or unmask all 159203697Sneel * interrupt sources feeding into a particular interrupt line of the processor. 160203697Sneel * 161203697Sneel * This means that these routines have an identical effect irrespective of 162203697Sneel * which cpu is executing them. This is important because the ithread may 163203697Sneel * be scheduled to run on either of the cpus. 164203697Sneel */ 165203697Sneelstatic void 166203697Sneelzbbus_intr_mask(void *arg) 167203697Sneel{ 168203697Sneel uint64_t mask; 169203697Sneel int irq; 170203697Sneel 171203697Sneel irq = (uintptr_t)arg; 172203697Sneel 173203697Sneel mtx_lock_spin(&zbbus_intr_mtx); 174203697Sneel 175203697Sneel mask = sb_read_intsrc_mask(0); 176203697Sneel mask |= hardint_to_intsrc_mask[irq]; 177203697Sneel sb_write_intsrc_mask(0, mask); 178203697Sneel 179203697Sneel mtx_unlock_spin(&zbbus_intr_mtx); 180203697Sneel} 181203697Sneel 182203697Sneelstatic void 183203697Sneelzbbus_intr_unmask(void *arg) 184203697Sneel{ 185203697Sneel uint64_t mask; 186203697Sneel int irq; 187203697Sneel 188203697Sneel irq = (uintptr_t)arg; 189203697Sneel 190203697Sneel mtx_lock_spin(&zbbus_intr_mtx); 191203697Sneel 192203697Sneel mask = sb_read_intsrc_mask(0); 193203697Sneel mask &= ~hardint_to_intsrc_mask[irq]; 194203697Sneel sb_write_intsrc_mask(0, mask); 195203697Sneel 196203697Sneel mtx_unlock_spin(&zbbus_intr_mtx); 197203697Sneel} 198203697Sneel 199195333Simpstruct zbbus_devinfo { 200195333Simp struct resource_list resources; 201195333Simp}; 202195333Simp 203195333Simpstatic MALLOC_DEFINE(M_ZBBUSDEV, "zbbusdev", "zbbusdev"); 204195333Simp 205195333Simpstatic int 206195333Simpzbbus_probe(device_t dev) 207195333Simp{ 208195333Simp 209195333Simp device_set_desc(dev, "Broadcom/Sibyte ZBbus"); 210195333Simp return (0); 211195333Simp} 212195333Simp 213195333Simpstatic int 214195333Simpzbbus_attach(device_t dev) 215195333Simp{ 216195333Simp 217195333Simp if (bootverbose) { 218195333Simp device_printf(dev, "attached.\n"); 219195333Simp } 220195333Simp 221203697Sneel cpu_set_hardintr_mask_func(zbbus_intr_mask); 222203697Sneel cpu_set_hardintr_unmask_func(zbbus_intr_unmask); 223203697Sneel 224195333Simp bus_generic_probe(dev); 225195333Simp bus_enumerate_hinted_children(dev); 226195333Simp bus_generic_attach(dev); 227195333Simp 228195333Simp return (0); 229195333Simp} 230195333Simp 231195333Simpstatic void 232195333Simpzbbus_hinted_child(device_t bus, const char *dname, int dunit) 233195333Simp{ 234195333Simp device_t child; 235195333Simp long maddr, msize; 236195333Simp int err, irq; 237195333Simp 238195333Simp if (resource_disabled(dname, dunit)) 239195333Simp return; 240195333Simp 241195333Simp child = BUS_ADD_CHILD(bus, 0, dname, dunit); 242195333Simp if (child == NULL) { 243195333Simp panic("zbbus: could not add child %s unit %d\n", dname, dunit); 244195333Simp } 245195333Simp 246195333Simp if (bootverbose) 247195333Simp device_printf(bus, "Adding hinted child %s%d\n", dname, dunit); 248195333Simp 249195333Simp /* 250195333Simp * Assign any pre-defined resources to the child. 251195333Simp */ 252195333Simp if (resource_long_value(dname, dunit, "msize", &msize) == 0 && 253195333Simp resource_long_value(dname, dunit, "maddr", &maddr) == 0) { 254195333Simp if (bootverbose) { 255195333Simp device_printf(bus, "Assigning memory resource " 256195333Simp "0x%0lx/%ld to child %s%d\n", 257195333Simp maddr, msize, dname, dunit); 258195333Simp } 259195333Simp err = bus_set_resource(child, SYS_RES_MEMORY, 0, maddr, msize); 260195333Simp if (err) { 261195333Simp device_printf(bus, "Unable to set memory resource " 262195333Simp "0x%0lx/%ld for child %s%d: %d\n", 263195333Simp maddr, msize, dname, dunit, err); 264195333Simp } 265195333Simp } 266195333Simp 267195333Simp if (resource_int_value(dname, dunit, "irq", &irq) == 0) { 268195333Simp if (bootverbose) { 269195333Simp device_printf(bus, "Assigning irq resource %d to " 270195333Simp "child %s%d\n", irq, dname, dunit); 271195333Simp } 272195333Simp err = bus_set_resource(child, SYS_RES_IRQ, 0, irq, 1); 273195333Simp if (err) { 274195333Simp device_printf(bus, "Unable to set irq resource %d" 275195333Simp "for child %s%d: %d\n", 276195333Simp irq, dname, dunit, err); 277195333Simp } 278195333Simp } 279195333Simp} 280195333Simp 281195333Simpstatic struct resource * 282195333Simpzbbus_alloc_resource(device_t bus, device_t child, int type, int *rid, 283195333Simp u_long start, u_long end, u_long count, u_int flags) 284195333Simp{ 285195333Simp struct resource *res; 286195333Simp int intrnum, intsrc, isdefault; 287195333Simp struct resource_list *rl; 288195333Simp struct resource_list_entry *rle; 289195333Simp struct zbbus_devinfo *dinfo; 290195333Simp 291195333Simp isdefault = (start == 0UL && end == ~0UL && count == 1); 292195333Simp 293195333Simp /* 294195333Simp * Our direct child is asking for a default resource allocation. 295195333Simp */ 296195333Simp if (device_get_parent(child) == bus) { 297195333Simp dinfo = device_get_ivars(child); 298195333Simp rl = &dinfo->resources; 299195333Simp rle = resource_list_find(rl, type, *rid); 300195333Simp if (rle) { 301195333Simp if (rle->res) 302195333Simp panic("zbbus_alloc_resource: resource is busy"); 303195333Simp if (isdefault) { 304195333Simp start = rle->start; 305195333Simp count = ulmax(count, rle->count); 306195333Simp end = ulmax(rle->end, start + count - 1); 307195333Simp } 308195333Simp } else { 309195333Simp if (isdefault) { 310195333Simp /* 311195333Simp * Our child is requesting a default 312195333Simp * resource allocation but we don't have the 313195333Simp * 'type/rid' tuple in the resource list. 314195333Simp * 315195333Simp * We have to fail the resource allocation. 316195333Simp */ 317195333Simp return (NULL); 318195333Simp } else { 319195333Simp /* 320195333Simp * The child is requesting a non-default 321195333Simp * resource. We just pass the request up 322195333Simp * to our parent. If the resource allocation 323195333Simp * succeeds we will create a resource list 324195333Simp * entry corresponding to that resource. 325195333Simp */ 326195333Simp } 327195333Simp } 328195333Simp } else { 329195333Simp rl = NULL; 330195333Simp rle = NULL; 331195333Simp } 332195333Simp 333195333Simp /* 334195333Simp * nexus doesn't know about the interrupt mapper and only wants to 335195333Simp * see the hard irq numbers [0-6]. We translate from the interrupt 336195333Simp * source presented to the mapper to the interrupt number presented 337195333Simp * to the cpu. 338195333Simp */ 339195333Simp if ((count == 1) && (type == SYS_RES_IRQ)) { 340195333Simp intsrc = start; 341195333Simp intrnum = sb_route_intsrc(intsrc); 342195333Simp start = end = intrnum; 343195333Simp } else { 344195333Simp intsrc = -1; /* satisfy gcc */ 345195333Simp intrnum = -1; 346195333Simp } 347195333Simp 348195333Simp res = bus_generic_alloc_resource(bus, child, type, rid, 349195333Simp start, end, count, flags); 350195333Simp 351195333Simp /* 352195333Simp * Keep track of the input into the interrupt mapper that maps 353195333Simp * to the resource allocated by 'child' with resource id 'rid'. 354195333Simp * 355195333Simp * If we don't record the mapping here then we won't be able to 356195333Simp * locate the interrupt source when bus_setup_intr(child,rid) is 357195333Simp * called. 358195333Simp */ 359195333Simp if (res != NULL && intrnum != -1) 360195333Simp sb_intmap_add(intrnum, child, rman_get_rid(res), intsrc); 361195333Simp 362195333Simp /* 363195333Simp * If a non-default resource allocation by our child was successful 364195333Simp * then keep track of the resource in the resource list associated 365195333Simp * with the child. 366195333Simp */ 367195333Simp if (res != NULL && rle == NULL && device_get_parent(child) == bus) { 368195333Simp resource_list_add(rl, type, *rid, start, end, count); 369195333Simp rle = resource_list_find(rl, type, *rid); 370195333Simp if (rle == NULL) 371195333Simp panic("zbbus_alloc_resource: cannot find resource"); 372195333Simp } 373195333Simp 374195333Simp if (rle != NULL) { 375195333Simp KASSERT(device_get_parent(child) == bus, 376195333Simp ("rle should be NULL for passthru device")); 377195333Simp rle->res = res; 378195333Simp if (rle->res) { 379195333Simp rle->start = rman_get_start(rle->res); 380195333Simp rle->end = rman_get_end(rle->res); 381195333Simp rle->count = count; 382195333Simp } 383195333Simp } 384195333Simp 385195333Simp return (res); 386195333Simp} 387195333Simp 388195333Simpstatic int 389195333Simpzbbus_setup_intr(device_t dev, device_t child, struct resource *irq, int flags, 390195333Simp driver_filter_t *filter, driver_intr_t *intr, void *arg, 391195333Simp void **cookiep) 392195333Simp{ 393195333Simp int error; 394195333Simp 395195333Simp error = bus_generic_setup_intr(dev, child, irq, flags, 396195333Simp filter, intr, arg, cookiep); 397195333Simp if (error == 0) 398195333Simp sb_intmap_activate(rman_get_start(irq), child, 399195333Simp rman_get_rid(irq)); 400195333Simp 401195333Simp return (error); 402195333Simp} 403195333Simp 404195333Simpstatic device_t 405212413Savgzbbus_add_child(device_t bus, u_int order, const char *name, int unit) 406195333Simp{ 407195333Simp device_t child; 408195333Simp struct zbbus_devinfo *dinfo; 409195333Simp 410195333Simp child = device_add_child_ordered(bus, order, name, unit); 411195333Simp if (child != NULL) { 412195333Simp dinfo = malloc(sizeof(struct zbbus_devinfo), M_ZBBUSDEV, 413195333Simp M_WAITOK | M_ZERO); 414195333Simp resource_list_init(&dinfo->resources); 415195333Simp device_set_ivars(child, dinfo); 416195333Simp } 417195333Simp 418195333Simp return (child); 419195333Simp} 420195333Simp 421195333Simpstatic struct resource_list * 422195333Simpzbbus_get_resource_list(device_t dev, device_t child) 423195333Simp{ 424195333Simp struct zbbus_devinfo *dinfo = device_get_ivars(child); 425195333Simp 426195333Simp return (&dinfo->resources); 427195333Simp} 428195333Simp 429195333Simpstatic device_method_t zbbus_methods[] ={ 430195333Simp /* Device interface */ 431195333Simp DEVMETHOD(device_probe, zbbus_probe), 432195333Simp DEVMETHOD(device_attach, zbbus_attach), 433195333Simp DEVMETHOD(device_detach, bus_generic_detach), 434195333Simp DEVMETHOD(device_shutdown, bus_generic_shutdown), 435195333Simp DEVMETHOD(device_suspend, bus_generic_suspend), 436195333Simp DEVMETHOD(device_resume, bus_generic_resume), 437195333Simp 438195333Simp /* Bus interface */ 439195333Simp DEVMETHOD(bus_alloc_resource, zbbus_alloc_resource), 440195333Simp DEVMETHOD(bus_activate_resource, bus_generic_activate_resource), 441195333Simp DEVMETHOD(bus_deactivate_resource, bus_generic_deactivate_resource), 442195333Simp DEVMETHOD(bus_release_resource, bus_generic_release_resource), 443195333Simp DEVMETHOD(bus_get_resource_list,zbbus_get_resource_list), 444195333Simp DEVMETHOD(bus_set_resource, bus_generic_rl_set_resource), 445195333Simp DEVMETHOD(bus_get_resource, bus_generic_rl_get_resource), 446195333Simp DEVMETHOD(bus_delete_resource, bus_generic_rl_delete_resource), 447195333Simp DEVMETHOD(bus_setup_intr, zbbus_setup_intr), 448195333Simp DEVMETHOD(bus_teardown_intr, bus_generic_teardown_intr), 449195333Simp DEVMETHOD(bus_add_child, zbbus_add_child), 450195333Simp DEVMETHOD(bus_hinted_child, zbbus_hinted_child), 451195333Simp 452195333Simp { 0, 0 } 453195333Simp}; 454195333Simp 455195333Simpstatic driver_t zbbus_driver = { 456195333Simp "zbbus", 457195333Simp zbbus_methods 458195333Simp}; 459195333Simp 460195333Simpstatic devclass_t zbbus_devclass; 461195333Simp 462195333SimpDRIVER_MODULE(zbbus, nexus, zbbus_driver, zbbus_devclass, 0, 0); 463