fhc.c revision 170387
1/*- 2 * Copyright (c) 2003 Jake Burkholder. 3 * Copyright (c) 2005 Marius Strobl <marius@FreeBSD.org> 4 * All rights reserved. 5 * 6 * Redistribution and use in source and binary forms, with or without 7 * modification, are permitted provided that the following conditions 8 * are met: 9 * 1. Redistributions of source code must retain the above copyright 10 * notice, this list of conditions and the following 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/sparc64/fhc/fhc.c 170387 2007-06-06 22:19:23Z piso $"); 30 31#include <sys/param.h> 32#include <sys/systm.h> 33#include <sys/bus.h> 34#include <sys/kernel.h> 35#include <sys/malloc.h> 36#include <sys/module.h> 37#include <sys/pcpu.h> 38 39#include <dev/led/led.h> 40#include <dev/ofw/ofw_bus.h> 41#include <dev/ofw/ofw_bus_subr.h> 42#include <dev/ofw/openfirm.h> 43 44#include <machine/bus.h> 45#include <machine/bus_common.h> 46#include <machine/resource.h> 47 48#include <sys/rman.h> 49 50#include <sparc64/fhc/fhcreg.h> 51#include <sparc64/sbus/ofw_sbus.h> 52 53struct fhc_clr { 54 driver_filter_t *fc_filter; 55 driver_intr_t *fc_func; 56 void *fc_arg; 57 void *fc_cookie; 58 bus_space_tag_t fc_bt; 59 bus_space_handle_t fc_bh; 60}; 61 62struct fhc_devinfo { 63 struct ofw_bus_devinfo fdi_obdinfo; 64 struct resource_list fdi_rl; 65}; 66 67struct fhc_softc { 68 struct resource * sc_memres[FHC_NREG]; 69 bus_space_handle_t sc_bh[FHC_NREG]; 70 bus_space_tag_t sc_bt[FHC_NREG]; 71 int sc_nrange; 72 struct sbus_ranges *sc_ranges; 73 uint32_t sc_board; 74 int sc_ign; 75 struct cdev *sc_led_dev; 76 int sc_flags; 77#define FHC_CENTRAL (1 << 0) 78}; 79 80static device_probe_t fhc_probe; 81static device_attach_t fhc_attach; 82static bus_print_child_t fhc_print_child; 83static bus_probe_nomatch_t fhc_probe_nomatch; 84static bus_setup_intr_t fhc_setup_intr; 85static bus_teardown_intr_t fhc_teardown_intr; 86static bus_alloc_resource_t fhc_alloc_resource; 87static bus_get_resource_list_t fhc_get_resource_list; 88static ofw_bus_get_devinfo_t fhc_get_devinfo; 89 90static driver_filter_t fhc_filter_stub; 91static driver_intr_t fhc_intr_stub; 92static void fhc_led_func(void *, int); 93static int fhc_print_res(struct fhc_devinfo *); 94 95static device_method_t fhc_methods[] = { 96 /* Device interface */ 97 DEVMETHOD(device_probe, fhc_probe), 98 DEVMETHOD(device_attach, fhc_attach), 99 DEVMETHOD(device_shutdown, bus_generic_shutdown), 100 DEVMETHOD(device_suspend, bus_generic_suspend), 101 DEVMETHOD(device_resume, bus_generic_resume), 102 103 /* Bus interface */ 104 DEVMETHOD(bus_print_child, fhc_print_child), 105 DEVMETHOD(bus_probe_nomatch, fhc_probe_nomatch), 106 DEVMETHOD(bus_setup_intr, fhc_setup_intr), 107 DEVMETHOD(bus_teardown_intr, fhc_teardown_intr), 108 DEVMETHOD(bus_alloc_resource, fhc_alloc_resource), 109 DEVMETHOD(bus_release_resource, bus_generic_rl_release_resource), 110 DEVMETHOD(bus_activate_resource, bus_generic_activate_resource), 111 DEVMETHOD(bus_deactivate_resource, bus_generic_deactivate_resource), 112 DEVMETHOD(bus_get_resource_list, fhc_get_resource_list), 113 DEVMETHOD(bus_get_resource, bus_generic_rl_get_resource), 114 115 /* ofw_bus interface */ 116 DEVMETHOD(ofw_bus_get_devinfo, fhc_get_devinfo), 117 DEVMETHOD(ofw_bus_get_compat, ofw_bus_gen_get_compat), 118 DEVMETHOD(ofw_bus_get_model, ofw_bus_gen_get_model), 119 DEVMETHOD(ofw_bus_get_name, ofw_bus_gen_get_name), 120 DEVMETHOD(ofw_bus_get_node, ofw_bus_gen_get_node), 121 DEVMETHOD(ofw_bus_get_type, ofw_bus_gen_get_type), 122 123 { NULL, NULL } 124}; 125 126static driver_t fhc_driver = { 127 "fhc", 128 fhc_methods, 129 sizeof(struct fhc_softc), 130}; 131 132static devclass_t fhc_devclass; 133 134DRIVER_MODULE(fhc, central, fhc_driver, fhc_devclass, 0, 0); 135DRIVER_MODULE(fhc, nexus, fhc_driver, fhc_devclass, 0, 0); 136 137static int 138fhc_probe(device_t dev) 139{ 140 141 if (strcmp(ofw_bus_get_name(dev), "fhc") == 0) { 142 device_set_desc(dev, "fhc"); 143 return (0); 144 } 145 return (ENXIO); 146} 147 148static int 149fhc_attach(device_t dev) 150{ 151 char ledname[sizeof("boardXX")]; 152 struct fhc_devinfo *fdi; 153 struct sbus_regs *reg; 154 struct fhc_softc *sc; 155 phandle_t child; 156 phandle_t node; 157 device_t cdev; 158 uint32_t board; 159 uint32_t ctrl; 160 uint32_t *intr; 161 uint32_t iv; 162 char *name; 163 int error; 164 int i; 165 int nintr; 166 int nreg; 167 int rid; 168 169 sc = device_get_softc(dev); 170 node = ofw_bus_get_node(dev); 171 172 if (strcmp(device_get_name(device_get_parent(dev)), "central") == 0) 173 sc->sc_flags |= FHC_CENTRAL; 174 175 for (i = 0; i < FHC_NREG; i++) { 176 rid = i; 177 sc->sc_memres[i] = bus_alloc_resource_any(dev, SYS_RES_MEMORY, 178 &rid, RF_ACTIVE); 179 if (sc->sc_memres[i] == NULL) { 180 device_printf(dev, "cannot allocate resource %d\n", i); 181 error = ENXIO; 182 goto fail_memres; 183 } 184 sc->sc_bt[i] = rman_get_bustag(sc->sc_memres[i]); 185 sc->sc_bh[i] = rman_get_bushandle(sc->sc_memres[i]); 186 } 187 188 if ((sc->sc_flags & FHC_CENTRAL) != 0) { 189 board = bus_space_read_4(sc->sc_bt[FHC_INTERNAL], 190 sc->sc_bh[FHC_INTERNAL], FHC_BSR); 191 sc->sc_board = ((board >> 16) & 0x1) | ((board >> 12) & 0xe); 192 } else { 193 if (OF_getprop(node, "board#", &sc->sc_board, 194 sizeof(sc->sc_board)) == -1) { 195 device_printf(dev, "cannot get board number\n"); 196 error = ENXIO; 197 goto fail_memres; 198 } 199 } 200 201 device_printf(dev, "board %d, ", sc->sc_board); 202 if (OF_getprop_alloc(node, "board-model", 1, (void **)&name) != -1) { 203 printf("model %s\n", name); 204 free(name, M_OFWPROP); 205 } else 206 printf("model unknown\n"); 207 208 for (i = FHC_FANFAIL; i <= FHC_TOD; i++) { 209 bus_space_write_4(sc->sc_bt[i], sc->sc_bh[i], FHC_ICLR, 0x0); 210 bus_space_read_4(sc->sc_bt[i], sc->sc_bh[i], FHC_ICLR); 211 } 212 213 sc->sc_ign = sc->sc_board << 1; 214 bus_space_write_4(sc->sc_bt[FHC_IGN], sc->sc_bh[FHC_IGN], 0x0, 215 sc->sc_ign); 216 sc->sc_ign = bus_space_read_4(sc->sc_bt[FHC_IGN], 217 sc->sc_bh[FHC_IGN], 0x0); 218 219 ctrl = bus_space_read_4(sc->sc_bt[FHC_INTERNAL], 220 sc->sc_bh[FHC_INTERNAL], FHC_CTRL); 221 if ((sc->sc_flags & FHC_CENTRAL) == 0) 222 ctrl |= FHC_CTRL_IXIST; 223 ctrl &= ~(FHC_CTRL_AOFF | FHC_CTRL_BOFF | FHC_CTRL_SLINE); 224 bus_space_write_4(sc->sc_bt[FHC_INTERNAL], sc->sc_bh[FHC_INTERNAL], 225 FHC_CTRL, ctrl); 226 bus_space_read_4(sc->sc_bt[FHC_INTERNAL], sc->sc_bh[FHC_INTERNAL], 227 FHC_CTRL); 228 229 sc->sc_nrange = OF_getprop_alloc(node, "ranges", 230 sizeof(*sc->sc_ranges), (void **)&sc->sc_ranges); 231 if (sc->sc_nrange == -1) { 232 device_printf(dev, "cannot get ranges\n"); 233 error = ENXIO; 234 goto fail_memres; 235 } 236 237 if ((sc->sc_flags & FHC_CENTRAL) == 0) { 238 snprintf(ledname, sizeof(ledname), "board%d", sc->sc_board); 239 sc->sc_led_dev = led_create(fhc_led_func, sc, ledname); 240 } 241 242 for (child = OF_child(node); child != 0; child = OF_peer(child)) { 243 fdi = malloc(sizeof(*fdi), M_DEVBUF, M_WAITOK | M_ZERO); 244 if (ofw_bus_gen_setup_devinfo(&fdi->fdi_obdinfo, child) != 0) { 245 free(fdi, M_DEVBUF); 246 continue; 247 } 248 nreg = OF_getprop_alloc(child, "reg", sizeof(*reg), 249 (void **)®); 250 if (nreg == -1) { 251 device_printf(dev, "<%s>: incomplete\n", 252 fdi->fdi_obdinfo.obd_name); 253 ofw_bus_gen_destroy_devinfo(&fdi->fdi_obdinfo); 254 free(fdi, M_DEVBUF); 255 continue; 256 } 257 resource_list_init(&fdi->fdi_rl); 258 for (i = 0; i < nreg; i++) 259 resource_list_add(&fdi->fdi_rl, SYS_RES_MEMORY, i, 260 reg[i].sbr_offset, reg[i].sbr_offset + 261 reg[i].sbr_size, reg[i].sbr_size); 262 free(reg, M_OFWPROP); 263 nintr = OF_getprop_alloc(child, "interrupts", sizeof(*intr), 264 (void **)&intr); 265 if (nintr != -1) { 266 for (i = 0; i < nintr; i++) { 267 iv = INTINO(intr[i]) | 268 (sc->sc_ign << INTMAP_IGN_SHIFT); 269 resource_list_add(&fdi->fdi_rl, SYS_RES_IRQ, i, 270 iv, iv, 1); 271 } 272 free(intr, M_OFWPROP); 273 } 274 cdev = device_add_child(dev, NULL, -1); 275 if (cdev == NULL) { 276 device_printf(dev, "<%s>: device_add_child failed\n", 277 fdi->fdi_obdinfo.obd_name); 278 resource_list_free(&fdi->fdi_rl); 279 ofw_bus_gen_destroy_devinfo(&fdi->fdi_obdinfo); 280 free(fdi, M_DEVBUF); 281 continue; 282 } 283 device_set_ivars(cdev, fdi); 284 } 285 286 return (bus_generic_attach(dev)); 287 288 fail_memres: 289 for (i = 0; i < FHC_NREG; i++) 290 if (sc->sc_memres[i] != NULL) 291 bus_release_resource(dev, SYS_RES_MEMORY, 292 rman_get_rid(sc->sc_memres[i]), sc->sc_memres[i]); 293 return (error); 294} 295 296static int 297fhc_print_child(device_t dev, device_t child) 298{ 299 int rv; 300 301 rv = bus_print_child_header(dev, child); 302 rv += fhc_print_res(device_get_ivars(child)); 303 rv += bus_print_child_footer(dev, child); 304 return (rv); 305} 306 307static void 308fhc_probe_nomatch(device_t dev, device_t child) 309{ 310 const char *type; 311 312 device_printf(dev, "<%s>", ofw_bus_get_name(child)); 313 fhc_print_res(device_get_ivars(child)); 314 type = ofw_bus_get_type(child); 315 printf(" type %s (no driver attached)\n", 316 type != NULL ? type : "unknown"); 317} 318 319static int 320fhc_setup_intr(device_t bus, device_t child, struct resource *r, int flags, 321 driver_filter_t *filt, driver_intr_t *func, void *arg, void **cookiep) 322{ 323 struct fhc_softc *sc; 324 struct fhc_clr *fc; 325 bus_space_tag_t bt; 326 bus_space_handle_t bh; 327 int error; 328 int i; 329 long vec; 330 uint32_t inr; 331 332 if (filt != NULL && func != NULL) 333 return (EINVAL); 334 335 sc = device_get_softc(bus); 336 vec = rman_get_start(r); 337 338 bt = NULL; 339 bh = 0; 340 inr = 0; 341 for (i = FHC_FANFAIL; i <= FHC_TOD; i++) { 342 if (INTINO(bus_space_read_4(sc->sc_bt[i], sc->sc_bh[i], 343 FHC_IMAP)) == INTINO(vec)){ 344 bt = sc->sc_bt[i]; 345 bh = sc->sc_bh[i]; 346 inr = INTINO(vec) | (sc->sc_ign << INTMAP_IGN_SHIFT); 347 break; 348 } 349 } 350 if (inr == 0) 351 return (0); 352 353 fc = malloc(sizeof(*fc), M_DEVBUF, M_WAITOK | M_ZERO); 354 if (fc == NULL) 355 return (0); 356 fc->fc_filter = filt; 357 fc->fc_func = func; 358 fc->fc_arg = arg; 359 fc->fc_bt = bt; 360 fc->fc_bh = bh; 361 362 bus_space_write_4(bt, bh, FHC_IMAP, inr); 363 bus_space_read_4(bt, bh, FHC_IMAP); 364 365 error = bus_generic_setup_intr(bus, child, r, flags, fhc_filter_stub, 366 fhc_intr_stub, fc, cookiep); 367 if (error != 0) { 368 free(fc, M_DEVBUF); 369 return (error); 370 } 371 fc->fc_cookie = *cookiep; 372 *cookiep = fc; 373 374 bus_space_write_4(bt, bh, FHC_ICLR, 0x0); 375 bus_space_write_4(bt, bh, FHC_IMAP, INTMAP_ENABLE(inr, PCPU_GET(mid))); 376 bus_space_read_4(bt, bh, FHC_IMAP); 377 378 return (error); 379} 380 381static int 382fhc_teardown_intr(device_t bus, device_t child, struct resource *r, 383 void *cookie) 384{ 385 struct fhc_clr *fc; 386 int error; 387 388 fc = cookie; 389 error = bus_generic_teardown_intr(bus, child, r, fc->fc_cookie); 390 if (error != 0) 391 free(fc, M_DEVBUF); 392 return (error); 393} 394 395static int 396fhc_filter_stub(void *arg) 397{ 398 struct fhc_clr *fc = arg; 399 int res; 400 401 if (fc->fc_filter != NULL) { 402 res = fc->fc_filter(fc->fc_arg); 403 bus_space_write_4(fc->fc_bt, fc->fc_bh, FHC_ICLR, 0x0); 404 bus_space_read_4(fc->fc_bt, fc->fc_bh, FHC_ICLR); 405 } else 406 res = FILTER_SCHEDULE_THREAD; 407 408 return (res); 409} 410 411static void 412fhc_intr_stub(void *arg) 413{ 414 struct fhc_clr *fc = arg; 415 416 fc->fc_func(fc->fc_arg); 417 if (fc->fc_filter == NULL) { 418 bus_space_write_4(fc->fc_bt, fc->fc_bh, FHC_ICLR, 0x0); 419 bus_space_read_4(fc->fc_bt, fc->fc_bh, FHC_ICLR); 420 } 421} 422 423static struct resource * 424fhc_alloc_resource(device_t bus, device_t child, int type, int *rid, 425 u_long start, u_long end, u_long count, u_int flags) 426{ 427 struct resource_list *rl; 428 struct resource_list_entry *rle; 429 struct fhc_softc *sc; 430 struct resource *res; 431 bus_addr_t coffset; 432 bus_addr_t cend; 433 bus_addr_t phys; 434 int isdefault; 435 int passthrough; 436 int i; 437 438 isdefault = (start == 0UL && end == ~0UL); 439 passthrough = (device_get_parent(child) != bus); 440 res = NULL; 441 rle = NULL; 442 rl = BUS_GET_RESOURCE_LIST(bus, child); 443 sc = device_get_softc(bus); 444 rle = resource_list_find(rl, type, *rid); 445 switch (type) { 446 case SYS_RES_IRQ: 447 return (resource_list_alloc(rl, bus, child, type, rid, start, 448 end, count, flags)); 449 case SYS_RES_MEMORY: 450 if (!passthrough) { 451 if (rle == NULL) 452 return (NULL); 453 if (rle->res != NULL) 454 panic("%s: resource entry is busy", __func__); 455 if (isdefault) { 456 start = rle->start; 457 count = ulmax(count, rle->count); 458 end = ulmax(rle->end, start + count - 1); 459 } 460 } 461 for (i = 0; i < sc->sc_nrange; i++) { 462 coffset = sc->sc_ranges[i].coffset; 463 cend = coffset + sc->sc_ranges[i].size - 1; 464 if (start >= coffset && end <= cend) { 465 start -= coffset; 466 end -= coffset; 467 phys = sc->sc_ranges[i].poffset | 468 ((bus_addr_t)sc->sc_ranges[i].pspace << 32); 469 res = bus_generic_alloc_resource(bus, child, 470 type, rid, phys + start, phys + end, 471 count, flags); 472 if (!passthrough) 473 rle->res = res; 474 break; 475 } 476 } 477 break; 478 } 479 return (res); 480} 481 482static struct resource_list * 483fhc_get_resource_list(device_t bus, device_t child) 484{ 485 struct fhc_devinfo *fdi; 486 487 fdi = device_get_ivars(child); 488 return (&fdi->fdi_rl); 489} 490 491static const struct ofw_bus_devinfo * 492fhc_get_devinfo(device_t bus, device_t child) 493{ 494 struct fhc_devinfo *fdi; 495 496 fdi = device_get_ivars(child); 497 return (&fdi->fdi_obdinfo); 498} 499 500static void 501fhc_led_func(void *arg, int onoff) 502{ 503 struct fhc_softc *sc; 504 uint32_t ctrl; 505 506 sc = (struct fhc_softc *)arg; 507 508 ctrl = bus_space_read_4(sc->sc_bt[FHC_INTERNAL], 509 sc->sc_bh[FHC_INTERNAL], FHC_CTRL); 510 if (onoff) 511 ctrl |= FHC_CTRL_RLED; 512 else 513 ctrl &= ~FHC_CTRL_RLED; 514 ctrl &= ~(FHC_CTRL_AOFF | FHC_CTRL_BOFF | FHC_CTRL_SLINE); 515 bus_space_write_4(sc->sc_bt[FHC_INTERNAL], sc->sc_bh[FHC_INTERNAL], 516 FHC_CTRL, ctrl); 517 bus_space_read_4(sc->sc_bt[FHC_INTERNAL], sc->sc_bh[FHC_INTERNAL], 518 FHC_CTRL); 519} 520 521static int 522fhc_print_res(struct fhc_devinfo *fdi) 523{ 524 int rv; 525 526 rv = 0; 527 rv += resource_list_print_type(&fdi->fdi_rl, "mem", SYS_RES_MEMORY, 528 "%#lx"); 529 rv += resource_list_print_type(&fdi->fdi_rl, "irq", SYS_RES_IRQ, "%ld"); 530 return (rv); 531} 532