apb.c revision 212413
1/*- 2 * Copyright (c) 2009, Oleksandr Tymoshenko <gonzo@FreeBSD.org> 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 unmodified, this list of conditions, and the following 10 * disclaimer. 11 * 2. Redistributions in binary form must reproduce the above copyright 12 * notice, this list of conditions and the following disclaimer in the 13 * documentation and/or other materials provided with the distribution. 14 * 15 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 16 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 17 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 18 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 19 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 20 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 21 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 22 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 23 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 24 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 25 * SUCH DAMAGE. 26 */ 27 28#include <sys/cdefs.h> 29__FBSDID("$FreeBSD: head/sys/mips/atheros/apb.c 212413 2010-09-10 11:19:03Z avg $"); 30 31#include <sys/param.h> 32#include <sys/systm.h> 33#include <sys/bus.h> 34#include <sys/interrupt.h> 35#include <sys/kernel.h> 36#include <sys/module.h> 37#include <sys/rman.h> 38#include <sys/malloc.h> 39 40#include <machine/bus.h> 41#include <machine/intr_machdep.h> 42 43#include <mips/atheros/apbvar.h> 44#include <mips/atheros/ar71xxreg.h> 45 46#undef APB_DEBUG 47#ifdef APB_DEBUG 48#define dprintf printf 49#else 50#define dprintf(x, arg...) 51#endif /* APB_DEBUG */ 52 53static int apb_activate_resource(device_t, device_t, int, int, 54 struct resource *); 55static device_t apb_add_child(device_t, u_int, const char *, int); 56static struct resource * 57 apb_alloc_resource(device_t, device_t, int, int *, u_long, 58 u_long, u_long, u_int); 59static int apb_attach(device_t); 60static int apb_deactivate_resource(device_t, device_t, int, int, 61 struct resource *); 62static struct resource_list * 63 apb_get_resource_list(device_t, device_t); 64static void apb_hinted_child(device_t, const char *, int); 65static int apb_intr(void *); 66static int apb_probe(device_t); 67static int apb_release_resource(device_t, device_t, int, int, 68 struct resource *); 69static int apb_setup_intr(device_t, device_t, struct resource *, int, 70 driver_filter_t *, driver_intr_t *, void *, void **); 71static int apb_teardown_intr(device_t, device_t, struct resource *, 72 void *); 73 74static void 75apb_mask_irq(void *source) 76{ 77 unsigned int irq = (unsigned int)source; 78 uint32_t reg; 79 80 reg = ATH_READ_REG(AR71XX_MISC_INTR_MASK); 81 ATH_WRITE_REG(AR71XX_MISC_INTR_MASK, reg & ~(1 << irq)); 82 83} 84 85static void 86apb_unmask_irq(void *source) 87{ 88 uint32_t reg; 89 unsigned int irq = (unsigned int)source; 90 91 reg = ATH_READ_REG(AR71XX_MISC_INTR_MASK); 92 ATH_WRITE_REG(AR71XX_MISC_INTR_MASK, reg | (1 << irq)); 93} 94 95static int 96apb_probe(device_t dev) 97{ 98 99 return (0); 100} 101 102static int 103apb_attach(device_t dev) 104{ 105 struct apb_softc *sc = device_get_softc(dev); 106 int rid = 0; 107 108 device_set_desc(dev, "APB Bus bridge"); 109 110 sc->apb_mem_rman.rm_type = RMAN_ARRAY; 111 sc->apb_mem_rman.rm_descr = "APB memory window"; 112 113 if (rman_init(&sc->apb_mem_rman) != 0 || 114 rman_manage_region(&sc->apb_mem_rman, 115 AR71XX_APB_BASE, 116 AR71XX_APB_BASE + AR71XX_APB_SIZE - 1) != 0) 117 panic("apb_attach: failed to set up memory rman"); 118 119 sc->apb_irq_rman.rm_type = RMAN_ARRAY; 120 sc->apb_irq_rman.rm_descr = "APB IRQ"; 121 122 if (rman_init(&sc->apb_irq_rman) != 0 || 123 rman_manage_region(&sc->apb_irq_rman, 124 APB_IRQ_BASE, APB_IRQ_END) != 0) 125 panic("apb_attach: failed to set up IRQ rman"); 126 127 if ((sc->sc_misc_irq = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid, 128 RF_SHAREABLE | RF_ACTIVE)) == NULL) { 129 device_printf(dev, "unable to allocate IRQ resource\n"); 130 return (ENXIO); 131 } 132 133 if ((bus_setup_intr(dev, sc->sc_misc_irq, INTR_TYPE_MISC, 134 apb_intr, NULL, sc, &sc->sc_misc_ih))) { 135 device_printf(dev, 136 "WARNING: unable to register interrupt handler\n"); 137 return (ENXIO); 138 } 139 140 bus_generic_probe(dev); 141 bus_enumerate_hinted_children(dev); 142 bus_generic_attach(dev); 143 144 return (0); 145} 146 147static struct resource * 148apb_alloc_resource(device_t bus, device_t child, int type, int *rid, 149 u_long start, u_long end, u_long count, u_int flags) 150{ 151 struct apb_softc *sc = device_get_softc(bus); 152 struct apb_ivar *ivar = device_get_ivars(child); 153 struct resource *rv; 154 struct resource_list_entry *rle; 155 struct rman *rm; 156 int isdefault, needactivate, passthrough; 157 158 isdefault = (start == 0UL && end == ~0UL); 159 needactivate = flags & RF_ACTIVE; 160 /* 161 * Pass memory requests to nexus device 162 */ 163 passthrough = (device_get_parent(child) != bus); 164 rle = NULL; 165 166 dprintf("%s: entry (%p, %p, %d, %d, %p, %p, %ld, %d)\n", 167 __func__, bus, child, type, *rid, (void *)(intptr_t)start, 168 (void *)(intptr_t)end, count, flags); 169 170 if (passthrough) 171 return (BUS_ALLOC_RESOURCE(device_get_parent(bus), child, type, 172 rid, start, end, count, flags)); 173 174 /* 175 * If this is an allocation of the "default" range for a given RID, 176 * and we know what the resources for this device are (ie. they aren't 177 * maintained by a child bus), then work out the start/end values. 178 */ 179 180 if (isdefault) { 181 rle = resource_list_find(&ivar->resources, type, *rid); 182 if (rle == NULL) { 183 return (NULL); 184 } 185 186 if (rle->res != NULL) { 187 panic("%s: resource entry is busy", __func__); 188 } 189 start = rle->start; 190 end = rle->end; 191 count = rle->count; 192 193 dprintf("%s: default resource (%p, %p, %ld)\n", 194 __func__, (void *)(intptr_t)start, 195 (void *)(intptr_t)end, count); 196 } 197 198 switch (type) { 199 case SYS_RES_IRQ: 200 rm = &sc->apb_irq_rman; 201 break; 202 case SYS_RES_MEMORY: 203 rm = &sc->apb_mem_rman; 204 break; 205 default: 206 printf("%s: unknown resource type %d\n", __func__, type); 207 return (0); 208 } 209 210 rv = rman_reserve_resource(rm, start, end, count, flags, child); 211 if (rv == 0) { 212 printf("%s: could not reserve resource\n", __func__); 213 return (0); 214 } 215 216 rman_set_rid(rv, *rid); 217 218 if (needactivate) { 219 if (bus_activate_resource(child, type, *rid, rv)) { 220 printf("%s: could not activate resource\n", __func__); 221 rman_release_resource(rv); 222 return (0); 223 } 224 } 225 226 return (rv); 227} 228 229static int 230apb_activate_resource(device_t bus, device_t child, int type, int rid, 231 struct resource *r) 232{ 233 234 /* XXX: should we mask/unmask IRQ here? */ 235 return (BUS_ACTIVATE_RESOURCE(device_get_parent(bus), child, 236 type, rid, r)); 237} 238 239static int 240apb_deactivate_resource(device_t bus, device_t child, int type, int rid, 241 struct resource *r) 242{ 243 244 /* XXX: should we mask/unmask IRQ here? */ 245 return (BUS_DEACTIVATE_RESOURCE(device_get_parent(bus), child, 246 type, rid, r)); 247} 248 249static int 250apb_release_resource(device_t dev, device_t child, int type, 251 int rid, struct resource *r) 252{ 253 struct resource_list *rl; 254 struct resource_list_entry *rle; 255 256 rl = apb_get_resource_list(dev, child); 257 if (rl == NULL) 258 return (EINVAL); 259 rle = resource_list_find(rl, type, rid); 260 if (rle == NULL) 261 return (EINVAL); 262 rman_release_resource(r); 263 rle->res = NULL; 264 265 return (0); 266} 267 268static int 269apb_setup_intr(device_t bus, device_t child, struct resource *ires, 270 int flags, driver_filter_t *filt, driver_intr_t *handler, 271 void *arg, void **cookiep) 272{ 273 struct apb_softc *sc = device_get_softc(bus); 274 struct intr_event *event; 275 int irq, error; 276 277 irq = rman_get_start(ires); 278 279 if (irq > APB_IRQ_END) 280 panic("%s: bad irq %d", __func__, irq); 281 282 event = sc->sc_eventstab[irq]; 283 if (event == NULL) { 284 error = intr_event_create(&event, (void *)irq, 0, irq, 285 apb_mask_irq, apb_unmask_irq, 286 NULL, NULL, 287 "apb intr%d:", irq); 288 289 if (error == 0) { 290 sc->sc_eventstab[irq] = event; 291 sc->sc_intr_counter[irq] = 292 mips_intrcnt_create(event->ie_name); 293 } 294 else 295 return (error); 296 } 297 298 intr_event_add_handler(event, device_get_nameunit(child), filt, 299 handler, arg, intr_priority(flags), flags, cookiep); 300 mips_intrcnt_setname(sc->sc_intr_counter[irq], event->ie_fullname); 301 302 apb_unmask_irq((void*)irq); 303 304 return (0); 305} 306 307static int 308apb_teardown_intr(device_t dev, device_t child, struct resource *ires, 309 void *cookie) 310{ 311 struct apb_softc *sc = device_get_softc(dev); 312 int irq, result; 313 314 irq = rman_get_start(ires); 315 if (irq > APB_IRQ_END) 316 panic("%s: bad irq %d", __func__, irq); 317 318 if (sc->sc_eventstab[irq] == NULL) 319 panic("Trying to teardown unoccupied IRQ"); 320 321 apb_mask_irq((void*)irq); 322 323 result = intr_event_remove_handler(cookie); 324 if (!result) 325 sc->sc_eventstab[irq] = NULL; 326 327 return (result); 328} 329 330static int 331apb_intr(void *arg) 332{ 333 struct apb_softc *sc = arg; 334 struct intr_event *event; 335 uint32_t reg, irq; 336 337 reg = ATH_READ_REG(AR71XX_MISC_INTR_STATUS); 338 for (irq = 0; irq < APB_NIRQS; irq++) { 339 if (reg & (1 << irq)) { 340 event = sc->sc_eventstab[irq]; 341 if (!event || TAILQ_EMPTY(&event->ie_handlers)) { 342 /* Ignore timer interrupts */ 343 if (irq != 0) 344 printf("Stray APB IRQ %d\n", irq); 345 continue; 346 } 347 348 /* TODO: frame instead of NULL? */ 349 intr_event_handle(event, NULL); 350 mips_intrcnt_inc(sc->sc_intr_counter[irq]); 351 } 352 } 353 354 return (FILTER_HANDLED); 355} 356 357static void 358apb_hinted_child(device_t bus, const char *dname, int dunit) 359{ 360 device_t child; 361 long maddr; 362 int msize; 363 int irq; 364 int result; 365 int mem_hints_count; 366 367 child = BUS_ADD_CHILD(bus, 0, dname, dunit); 368 369 /* 370 * Set hard-wired resources for hinted child using 371 * specific RIDs. 372 */ 373 mem_hints_count = 0; 374 if (resource_long_value(dname, dunit, "maddr", &maddr) == 0) 375 mem_hints_count++; 376 if (resource_int_value(dname, dunit, "msize", &msize) == 0) 377 mem_hints_count++; 378 379 /* check if all info for mem resource has been provided */ 380 if ((mem_hints_count > 0) && (mem_hints_count < 2)) { 381 printf("Either maddr or msize hint is missing for %s%d\n", 382 dname, dunit); 383 } else if (mem_hints_count) { 384 result = bus_set_resource(child, SYS_RES_MEMORY, 0, 385 maddr, msize); 386 if (result != 0) 387 device_printf(bus, 388 "warning: bus_set_resource() failed\n"); 389 } 390 391 if (resource_int_value(dname, dunit, "irq", &irq) == 0) { 392 result = bus_set_resource(child, SYS_RES_IRQ, 0, irq, 1); 393 if (result != 0) 394 device_printf(bus, 395 "warning: bus_set_resource() failed\n"); 396 } 397} 398 399static device_t 400apb_add_child(device_t bus, u_int order, const char *name, int unit) 401{ 402 device_t child; 403 struct apb_ivar *ivar; 404 405 ivar = malloc(sizeof(struct apb_ivar), M_DEVBUF, M_WAITOK | M_ZERO); 406 if (ivar == NULL) { 407 printf("Failed to allocate ivar\n"); 408 return (0); 409 } 410 resource_list_init(&ivar->resources); 411 412 child = device_add_child_ordered(bus, order, name, unit); 413 if (child == NULL) { 414 printf("Can't add child %s%d ordered\n", name, unit); 415 return (0); 416 } 417 418 device_set_ivars(child, ivar); 419 420 return (child); 421} 422 423/* 424 * Helper routine for bus_generic_rl_get_resource/bus_generic_rl_set_resource 425 * Provides pointer to resource_list for these routines 426 */ 427static struct resource_list * 428apb_get_resource_list(device_t dev, device_t child) 429{ 430 struct apb_ivar *ivar; 431 432 ivar = device_get_ivars(child); 433 return (&(ivar->resources)); 434} 435 436static device_method_t apb_methods[] = { 437 DEVMETHOD(bus_activate_resource, apb_activate_resource), 438 DEVMETHOD(bus_add_child, apb_add_child), 439 DEVMETHOD(bus_alloc_resource, apb_alloc_resource), 440 DEVMETHOD(bus_deactivate_resource, apb_deactivate_resource), 441 DEVMETHOD(bus_get_resource_list, apb_get_resource_list), 442 DEVMETHOD(bus_hinted_child, apb_hinted_child), 443 DEVMETHOD(bus_print_child, bus_generic_print_child), 444 DEVMETHOD(bus_release_resource, apb_release_resource), 445 DEVMETHOD(bus_setup_intr, apb_setup_intr), 446 DEVMETHOD(bus_teardown_intr, apb_teardown_intr), 447 DEVMETHOD(device_attach, apb_attach), 448 DEVMETHOD(device_probe, apb_probe), 449 DEVMETHOD(bus_get_resource, bus_generic_rl_get_resource), 450 DEVMETHOD(bus_set_resource, bus_generic_rl_set_resource), 451 452 {0, 0}, 453}; 454 455static driver_t apb_driver = { 456 "apb", 457 apb_methods, 458 sizeof(struct apb_softc), 459}; 460static devclass_t apb_devclass; 461 462DRIVER_MODULE(apb, nexus, apb_driver, apb_devclass, 0, 0); 463