fhc.c revision 152684
1/*- 2 * Copyright (c) 2003 Jake Burkholder. 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: head/sys/sparc64/fhc/fhc.c 152684 2005-11-22 16:39:44Z marius $"); 29 30#include <sys/param.h> 31#include <sys/systm.h> 32#include <sys/bus.h> 33#include <sys/kernel.h> 34#include <sys/malloc.h> 35#include <sys/pcpu.h> 36 37#include <dev/led/led.h> 38#include <dev/ofw/ofw_bus.h> 39#include <dev/ofw/ofw_bus_subr.h> 40#include <dev/ofw/openfirm.h> 41 42#include <machine/bus.h> 43#include <machine/bus_common.h> 44#include <machine/resource.h> 45 46#include <sys/rman.h> 47 48#include <sparc64/fhc/fhcreg.h> 49#include <sparc64/fhc/fhcvar.h> 50#include <sparc64/sbus/ofw_sbus.h> 51 52struct fhc_clr { 53 driver_intr_t *fc_func; 54 void *fc_arg; 55 void *fc_cookie; 56 bus_space_tag_t fc_bt; 57 bus_space_handle_t fc_bh; 58}; 59 60struct fhc_devinfo { 61 struct ofw_bus_devinfo fdi_obdinfo; 62 struct resource_list fdi_rl; 63}; 64 65static void fhc_intr_stub(void *); 66static void fhc_led_func(void *, int); 67static int fhc_print_res(struct fhc_devinfo *); 68 69int 70fhc_probe(device_t dev) 71{ 72 73 return (0); 74} 75 76int 77fhc_attach(device_t dev) 78{ 79 char ledname[sizeof("boardXX")]; 80 struct fhc_devinfo *fdi; 81 struct sbus_regs *reg; 82 struct fhc_softc *sc; 83 phandle_t child; 84 phandle_t node; 85 device_t cdev; 86 uint32_t ctrl; 87 uint32_t *intr; 88 uint32_t iv; 89 char *name; 90 int nintr; 91 int nreg; 92 int i; 93 94 sc = device_get_softc(dev); 95 node = sc->sc_node; 96 97 device_printf(dev, "board %d, ", sc->sc_board); 98 if (OF_getprop_alloc(node, "board-model", 1, (void **)&name) != -1) { 99 printf("model %s\n", name); 100 free(name, M_OFWPROP); 101 } else 102 printf("model unknown\n"); 103 104 for (i = FHC_FANFAIL; i <= FHC_TOD; i++) { 105 bus_space_write_4(sc->sc_bt[i], sc->sc_bh[i], FHC_ICLR, 0x0); 106 bus_space_read_4(sc->sc_bt[i], sc->sc_bh[i], FHC_ICLR); 107 } 108 109 sc->sc_ign = sc->sc_board << 1; 110 bus_space_write_4(sc->sc_bt[FHC_IGN], sc->sc_bh[FHC_IGN], 0x0, 111 sc->sc_ign); 112 sc->sc_ign = bus_space_read_4(sc->sc_bt[FHC_IGN], 113 sc->sc_bh[FHC_IGN], 0x0); 114 115 ctrl = bus_space_read_4(sc->sc_bt[FHC_INTERNAL], 116 sc->sc_bh[FHC_INTERNAL], FHC_CTRL); 117 if ((sc->sc_flags & FHC_CENTRAL) == 0) 118 ctrl |= FHC_CTRL_IXIST; 119 ctrl &= ~(FHC_CTRL_AOFF | FHC_CTRL_BOFF | FHC_CTRL_SLINE); 120 bus_space_write_4(sc->sc_bt[FHC_INTERNAL], sc->sc_bh[FHC_INTERNAL], 121 FHC_CTRL, ctrl); 122 bus_space_read_4(sc->sc_bt[FHC_INTERNAL], sc->sc_bh[FHC_INTERNAL], 123 FHC_CTRL); 124 125 sc->sc_nrange = OF_getprop_alloc(node, "ranges", 126 sizeof(*sc->sc_ranges), (void **)&sc->sc_ranges); 127 if (sc->sc_nrange == -1) { 128 device_printf(dev, "can't get ranges\n"); 129 return (ENXIO); 130 } 131 132 if ((sc->sc_flags & FHC_CENTRAL) == 0) { 133 snprintf(ledname, sizeof(ledname), "board%d", sc->sc_board); 134 sc->sc_led_dev = led_create(fhc_led_func, sc, ledname); 135 } 136 137 for (child = OF_child(node); child != 0; child = OF_peer(child)) { 138 fdi = malloc(sizeof(*fdi), M_DEVBUF, M_WAITOK | M_ZERO); 139 if (ofw_bus_gen_setup_devinfo(&fdi->fdi_obdinfo, child) != 0) { 140 free(fdi, M_DEVBUF); 141 continue; 142 } 143 nreg = OF_getprop_alloc(child, "reg", sizeof(*reg), 144 (void **)®); 145 if (nreg == -1) { 146 device_printf(dev, "<%s>: incomplete\n", 147 fdi->fdi_obdinfo.obd_name); 148 ofw_bus_gen_destroy_devinfo(&fdi->fdi_obdinfo); 149 free(fdi, M_DEVBUF); 150 continue; 151 } 152 resource_list_init(&fdi->fdi_rl); 153 for (i = 0; i < nreg; i++) 154 resource_list_add(&fdi->fdi_rl, SYS_RES_MEMORY, i, 155 reg[i].sbr_offset, reg[i].sbr_offset + 156 reg[i].sbr_size, reg[i].sbr_size); 157 free(reg, M_OFWPROP); 158 nintr = OF_getprop_alloc(child, "interrupts", sizeof(*intr), 159 (void **)&intr); 160 if (nintr != -1) { 161 for (i = 0; i < nintr; i++) { 162 iv = INTINO(intr[i]) | 163 (sc->sc_ign << INTMAP_IGN_SHIFT); 164 resource_list_add(&fdi->fdi_rl, SYS_RES_IRQ, i, 165 iv, iv, 1); 166 } 167 free(intr, M_OFWPROP); 168 } 169 cdev = device_add_child(dev, NULL, -1); 170 if (cdev == NULL) { 171 device_printf(dev, "<%s>: device_add_child failed\n", 172 fdi->fdi_obdinfo.obd_name); 173 resource_list_free(&fdi->fdi_rl); 174 ofw_bus_gen_destroy_devinfo(&fdi->fdi_obdinfo); 175 free(fdi, M_DEVBUF); 176 continue; 177 } 178 device_set_ivars(cdev, fdi); 179 } 180 181 return (bus_generic_attach(dev)); 182} 183 184int 185fhc_print_child(device_t dev, device_t child) 186{ 187 int rv; 188 189 rv = bus_print_child_header(dev, child); 190 rv += fhc_print_res(device_get_ivars(child)); 191 rv += bus_print_child_footer(dev, child); 192 return (rv); 193} 194 195void 196fhc_probe_nomatch(device_t dev, device_t child) 197{ 198 const char *type; 199 200 device_printf(dev, "<%s>", ofw_bus_get_name(child)); 201 fhc_print_res(device_get_ivars(child)); 202 type = ofw_bus_get_type(child); 203 printf(" type %s (no driver attached)\n", 204 type != NULL ? type : "unknown"); 205} 206 207int 208fhc_setup_intr(device_t bus, device_t child, struct resource *r, int flags, 209 driver_intr_t *func, void *arg, void **cookiep) 210{ 211 struct fhc_softc *sc; 212 struct fhc_clr *fc; 213 bus_space_tag_t bt; 214 bus_space_handle_t bh; 215 int error; 216 int i; 217 long vec; 218 uint32_t inr; 219 220 sc = device_get_softc(bus); 221 vec = rman_get_start(r); 222 223 bt = NULL; 224 bh = 0; 225 inr = 0; 226 for (i = FHC_FANFAIL; i <= FHC_TOD; i++) { 227 if (INTINO(bus_space_read_4(sc->sc_bt[i], sc->sc_bh[i], 228 FHC_IMAP)) == INTINO(vec)){ 229 bt = sc->sc_bt[i]; 230 bh = sc->sc_bh[i]; 231 inr = INTINO(vec) | (sc->sc_ign << INTMAP_IGN_SHIFT); 232 break; 233 } 234 } 235 if (inr == 0) 236 return (0); 237 238 fc = malloc(sizeof(*fc), M_DEVBUF, M_WAITOK | M_ZERO); 239 if (fc == NULL) 240 return (0); 241 fc->fc_func = func; 242 fc->fc_arg = arg; 243 fc->fc_bt = bt; 244 fc->fc_bh = bh; 245 246 bus_space_write_4(bt, bh, FHC_IMAP, inr); 247 bus_space_read_4(bt, bh, FHC_IMAP); 248 249 error = bus_generic_setup_intr(bus, child, r, flags, fhc_intr_stub, 250 fc, cookiep); 251 if (error != 0) { 252 free(fc, M_DEVBUF); 253 return (error); 254 } 255 fc->fc_cookie = *cookiep; 256 *cookiep = fc; 257 258 bus_space_write_4(bt, bh, FHC_ICLR, 0x0); 259 bus_space_write_4(bt, bh, FHC_IMAP, INTMAP_ENABLE(inr, PCPU_GET(mid))); 260 bus_space_read_4(bt, bh, FHC_IMAP); 261 262 return (error); 263} 264 265int 266fhc_teardown_intr(device_t bus, device_t child, struct resource *r, 267 void *cookie) 268{ 269 struct fhc_clr *fc; 270 int error; 271 272 fc = cookie; 273 error = bus_generic_teardown_intr(bus, child, r, fc->fc_cookie); 274 if (error != 0) 275 free(fc, M_DEVBUF); 276 return (error); 277} 278 279static void 280fhc_intr_stub(void *arg) 281{ 282 struct fhc_clr *fc = arg; 283 284 fc->fc_func(fc->fc_arg); 285 286 bus_space_write_4(fc->fc_bt, fc->fc_bh, FHC_ICLR, 0x0); 287 bus_space_read_4(fc->fc_bt, fc->fc_bh, FHC_ICLR); 288} 289 290struct resource * 291fhc_alloc_resource(device_t bus, device_t child, int type, int *rid, 292 u_long start, u_long end, u_long count, u_int flags) 293{ 294 struct resource_list *rl; 295 struct resource_list_entry *rle; 296 struct fhc_softc *sc; 297 struct resource *res; 298 bus_addr_t coffset; 299 bus_addr_t cend; 300 bus_addr_t phys; 301 int isdefault; 302 int passthrough; 303 int i; 304 305 isdefault = (start == 0UL && end == ~0UL); 306 passthrough = (device_get_parent(child) != bus); 307 res = NULL; 308 rle = NULL; 309 rl = BUS_GET_RESOURCE_LIST(bus, child); 310 sc = device_get_softc(bus); 311 rle = resource_list_find(rl, type, *rid); 312 switch (type) { 313 case SYS_RES_IRQ: 314 return (resource_list_alloc(rl, bus, child, type, rid, start, 315 end, count, flags)); 316 case SYS_RES_MEMORY: 317 if (!passthrough) { 318 if (rle == NULL) 319 return (NULL); 320 if (rle->res != NULL) 321 panic("%s: resource entry is busy", __func__); 322 if (isdefault) { 323 start = rle->start; 324 count = ulmax(count, rle->count); 325 end = ulmax(rle->end, start + count - 1); 326 } 327 } 328 for (i = 0; i < sc->sc_nrange; i++) { 329 coffset = sc->sc_ranges[i].coffset; 330 cend = coffset + sc->sc_ranges[i].size - 1; 331 if (start >= coffset && end <= cend) { 332 start -= coffset; 333 end -= coffset; 334 phys = sc->sc_ranges[i].poffset | 335 ((bus_addr_t)sc->sc_ranges[i].pspace << 32); 336 res = bus_generic_alloc_resource(bus, child, 337 type, rid, phys + start, phys + end, 338 count, flags); 339 if (!passthrough) 340 rle->res = res; 341 break; 342 } 343 } 344 break; 345 } 346 return (res); 347} 348 349struct resource_list * 350fhc_get_resource_list(device_t bus, device_t child) 351{ 352 struct fhc_devinfo *fdi; 353 354 fdi = device_get_ivars(child); 355 return (&fdi->fdi_rl); 356} 357 358const struct ofw_bus_devinfo * 359fhc_get_devinfo(device_t bus, device_t child) 360{ 361 struct fhc_devinfo *fdi; 362 363 fdi = device_get_ivars(child); 364 return (&fdi->fdi_obdinfo); 365} 366 367static void 368fhc_led_func(void *arg, int onoff) 369{ 370 struct fhc_softc *sc; 371 uint32_t ctrl; 372 373 sc = (struct fhc_softc *)arg; 374 375 ctrl = bus_space_read_4(sc->sc_bt[FHC_INTERNAL], 376 sc->sc_bh[FHC_INTERNAL], FHC_CTRL); 377 if (onoff) 378 ctrl |= FHC_CTRL_RLED; 379 else 380 ctrl &= ~FHC_CTRL_RLED; 381 ctrl &= ~(FHC_CTRL_AOFF | FHC_CTRL_BOFF | FHC_CTRL_SLINE); 382 bus_space_write_4(sc->sc_bt[FHC_INTERNAL], sc->sc_bh[FHC_INTERNAL], 383 FHC_CTRL, ctrl); 384 bus_space_read_4(sc->sc_bt[FHC_INTERNAL], sc->sc_bh[FHC_INTERNAL], 385 FHC_CTRL); 386} 387 388static int 389fhc_print_res(struct fhc_devinfo *fdi) 390{ 391 int rv; 392 393 rv = 0; 394 rv += resource_list_print_type(&fdi->fdi_rl, "mem", SYS_RES_MEMORY, 395 "%#lx"); 396 rv += resource_list_print_type(&fdi->fdi_rl, "irq", SYS_RES_IRQ, "%ld"); 397 return (rv); 398} 399