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