fhc.c revision 111072
1230832Sgnn/*- 2230832Sgnn * Copyright (c) 2003 Jake Burkholder. 3230832Sgnn * All rights reserved. 4230832Sgnn * 5230832Sgnn * Redistribution and use in source and binary forms, with or without 6230832Sgnn * modification, are permitted provided that the following conditions 7230832Sgnn * are met: 8230832Sgnn * 1. Redistributions of source code must retain the above copyright 9230832Sgnn * notice, this list of conditions and the following disclaimer. 10230832Sgnn * 2. Redistributions in binary form must reproduce the above copyright 11230832Sgnn * notice, this list of conditions and the following disclaimer in the 12230832Sgnn * documentation and/or other materials provided with the distribution. 13230832Sgnn * 14230832Sgnn * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15230832Sgnn * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16230832Sgnn * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17230832Sgnn * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 18230832Sgnn * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19230832Sgnn * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20230832Sgnn * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21230832Sgnn * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22230832Sgnn * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23230832Sgnn * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24230832Sgnn * SUCH DAMAGE. 25230832Sgnn * 26230832Sgnn * $FreeBSD: head/sys/sparc64/fhc/fhc.c 111072 2003-02-18 09:01:01Z jake $ 27230832Sgnn */ 28230832Sgnn 29230832Sgnn#include <sys/param.h> 30230832Sgnn#include <sys/systm.h> 31230832Sgnn#include <sys/bus.h> 32230832Sgnn#include <sys/kernel.h> 33230832Sgnn#include <sys/malloc.h> 34230832Sgnn 35230832Sgnn#include <dev/ofw/openfirm.h> 36230832Sgnn 37230832Sgnn#include <machine/bus.h> 38230832Sgnn#include <machine/bus_common.h> 39230832Sgnn#include <machine/resource.h> 40230832Sgnn 41230832Sgnn#include <sys/rman.h> 42230832Sgnn 43230832Sgnn#include <sparc64/fhc/fhcreg.h> 44230832Sgnn#include <sparc64/fhc/fhcvar.h> 45230832Sgnn#include <sparc64/sbus/ofw_sbus.h> 46230832Sgnn 47230832Sgnn#define INTIGN(map) (((map) & INTMAP_IGN_MASK) >> INTMAP_IGN_SHIFT) 48230832Sgnn 49230832Sgnnstruct fhc_devinfo { 50230832Sgnn char *fdi_name; 51230832Sgnn char *fdi_type; 52230832Sgnn phandle_t fdi_node; 53230832Sgnn struct resource_list fdi_rl; 54230832Sgnn}; 55230832Sgnn 56230832Sgnnint 57230832Sgnnfhc_probe(device_t dev) 58230832Sgnn{ 59230832Sgnn 60230832Sgnn return (0); 61230832Sgnn} 62230832Sgnn 63230832Sgnnint 64230832Sgnnfhc_attach(device_t dev) 65230832Sgnn{ 66230832Sgnn struct fhc_devinfo *fdi; 67230832Sgnn struct sbus_regs *reg; 68230832Sgnn struct fhc_softc *sc; 69230832Sgnn phandle_t child; 70230832Sgnn phandle_t node; 71230832Sgnn bus_addr_t size; 72230832Sgnn bus_addr_t off; 73230832Sgnn device_t cdev; 74230832Sgnn uint64_t map; 75230832Sgnn char *name; 76230832Sgnn int nreg; 77230832Sgnn int i; 78230832Sgnn 79230832Sgnn sc = device_get_softc(dev); 80230832Sgnn node = sc->sc_node; 81230832Sgnn 82230832Sgnn sc->sc_ign = bus_space_read_4(sc->sc_bt[FHC_IGN], 83230832Sgnn sc->sc_bh[FHC_IGN], 0x0); 84230832Sgnn for (i = FHC_FANFAIL; i <= FHC_TOD; i++) { 85230832Sgnn bus_space_write_4(sc->sc_bt[i], sc->sc_bh[i], FHC_ICLR, 0x0); 86230832Sgnn map = bus_space_read_4(sc->sc_bt[i], sc->sc_bh[i], FHC_IMAP); 87230832Sgnn bus_space_write_4(sc->sc_bt[i], sc->sc_bh[i], FHC_IMAP, 88230832Sgnn map & ~INTMAP_V); 89230832Sgnn if (INTIGN(map) != sc->sc_ign) 90230832Sgnn panic("fhc_attach: map has wrong ign %#lx != %#x", 91230832Sgnn INTIGN(map), sc->sc_ign); 92230832Sgnn } 93230832Sgnn 94230832Sgnn sc->sc_nrange = OF_getprop_alloc(node, "ranges", 95230832Sgnn sizeof(*sc->sc_ranges), (void **)&sc->sc_ranges); 96230832Sgnn if (sc->sc_nrange == -1) { 97230832Sgnn device_printf(dev, "can't get ranges"); 98230832Sgnn return (ENXIO); 99230832Sgnn } 100230832Sgnn 101230832Sgnn for (child = OF_child(node); child != 0; child = OF_peer(child)) { 102230832Sgnn if ((OF_getprop_alloc(child, "name", 1, (void **)&name)) == -1) 103230832Sgnn continue; 104230832Sgnn cdev = device_add_child(dev, NULL, -1); 105230832Sgnn if (cdev != NULL) { 106230832Sgnn fdi = malloc(sizeof(*fdi), M_DEVBUF, M_ZERO); 107230832Sgnn fdi->fdi_name = name; 108230832Sgnn fdi->fdi_node = child; 109230832Sgnn OF_getprop_alloc(child, "device_type", 1, 110230832Sgnn (void **)&fdi->fdi_type); 111230832Sgnn resource_list_init(&fdi->fdi_rl); 112230832Sgnn nreg = OF_getprop_alloc(child, "reg", sizeof(*reg), 113230832Sgnn (void **)®); 114230832Sgnn if (nreg != -1) { 115230832Sgnn for (i = 0; i < nreg; i++) { 116230832Sgnn off = reg[i].sbr_offset; 117230832Sgnn size = reg[i].sbr_size; 118230832Sgnn resource_list_add(&fdi->fdi_rl, 119230832Sgnn SYS_RES_MEMORY, i, off, off + size, 120230832Sgnn size); 121230832Sgnn } 122230832Sgnn free(reg, M_OFWPROP); 123230832Sgnn } 124230832Sgnn device_set_ivars(cdev, fdi); 125230832Sgnn } else 126230832Sgnn free(name, M_OFWPROP); 127230832Sgnn } 128230832Sgnn 129230832Sgnn return (bus_generic_attach(dev)); 130230832Sgnn} 131230832Sgnn 132230832Sgnnint 133fhc_print_child(device_t dev, device_t child) 134{ 135 struct fhc_devinfo *fdi; 136 int rv; 137 138 fdi = device_get_ivars(child); 139 rv = bus_print_child_header(dev, child); 140 rv += resource_list_print_type(&fdi->fdi_rl, "mem", 141 SYS_RES_MEMORY, "%#lx"); 142 rv += bus_print_child_footer(dev, child); 143 return (rv); 144} 145 146void 147fhc_probe_nomatch(device_t dev, device_t child) 148{ 149 struct fhc_devinfo *fdi; 150 151 fdi = device_get_ivars(child); 152 device_printf(dev, "<%s>", fdi->fdi_name); 153 resource_list_print_type(&fdi->fdi_rl, "mem", SYS_RES_MEMORY, "%#lx"); 154 printf(" type %s (no driver attached)\n", 155 fdi->fdi_type != NULL ? fdi->fdi_type : "unknown"); 156} 157 158int 159fhc_read_ivar(device_t dev, device_t child, int which, uintptr_t *result) 160{ 161 struct fhc_devinfo *fdi; 162 163 if ((fdi = device_get_ivars(child)) == 0) 164 return (ENOENT); 165 switch (which) { 166 case FHC_IVAR_NAME: 167 *result = (uintptr_t)fdi->fdi_name; 168 break; 169 case FHC_IVAR_NODE: 170 *result = fdi->fdi_node; 171 break; 172 case FHC_IVAR_TYPE: 173 *result = (uintptr_t)fdi->fdi_type; 174 break; 175 default: 176 return (ENOENT); 177 } 178 return (0); 179} 180 181int 182fhc_write_ivar(device_t dev, device_t child, int which, uintptr_t value) 183{ 184 struct fhc_devinfo *fdi; 185 186 if ((fdi = device_get_ivars(child)) == 0) 187 return (ENOENT); 188 switch (which) { 189 case FHC_IVAR_NAME: 190 case FHC_IVAR_NODE: 191 case FHC_IVAR_TYPE: 192 return (EINVAL); 193 default: 194 return (ENOENT); 195 } 196 return (0); 197} 198 199int 200fhc_setup_intr(device_t bus, device_t child, struct resource *r, int flags, 201 driver_intr_t intr, void *arg, void **cookiep) 202{ 203 204 return (bus_generic_setup_intr(bus, child, r, flags, intr, arg, 205 cookiep)); 206} 207 208int 209fhc_teardown_intr(device_t bus, device_t child, struct resource *r, 210 void *cookie) 211{ 212 213 return (bus_generic_teardown_intr(bus, child, r, cookie)); 214} 215 216struct resource * 217fhc_alloc_resource(device_t bus, device_t child, int type, int *rid, 218 u_long start, u_long end, u_long count, u_int flags) 219{ 220 struct resource_list_entry *rle; 221 struct fhc_devinfo *fdi; 222 struct fhc_softc *sc; 223 struct resource *res; 224 bus_addr_t coffset; 225 bus_addr_t cend; 226 bus_addr_t phys; 227 int isdefault; 228 uint64_t map; 229 int i; 230 231 isdefault = (start == 0UL && end == ~0UL); 232 res = NULL; 233 sc = device_get_softc(bus); 234 switch (type) { 235 case SYS_RES_IRQ: 236 if (!isdefault || count != 1 || *rid < FHC_FANFAIL || 237 *rid > FHC_TOD) 238 break; 239 map = bus_space_read_4(sc->sc_bt[*rid], sc->sc_bh[*rid], 240 FHC_IMAP); 241 res = bus_generic_alloc_resource(bus, child, type, rid, 242 INTVEC(map), INTVEC(map), 1, flags); 243 if (res != NULL) 244 rman_set_rid(res, *rid); 245 break; 246 case SYS_RES_MEMORY: 247 fdi = device_get_ivars(child); 248 rle = resource_list_find(&fdi->fdi_rl, type, *rid); 249 if (rle == NULL) 250 return (NULL); 251 if (rle->res != NULL) 252 panic("fhc_alloc_resource: resource entry is busy"); 253 if (isdefault) { 254 start = rle->start; 255 count = ulmax(count, rle->count); 256 end = ulmax(rle->end, start + count - 1); 257 } 258 for (i = 0; i < sc->sc_nrange; i++) { 259 coffset = sc->sc_ranges[i].coffset; 260 cend = coffset + sc->sc_ranges[i].size - 1; 261 if (start >= coffset && end <= cend) { 262 start -= coffset; 263 end -= coffset; 264 phys = sc->sc_ranges[i].poffset | 265 ((bus_addr_t)sc->sc_ranges[i].pspace << 32); 266 res = bus_generic_alloc_resource(bus, child, 267 type, rid, phys + start, phys + end, 268 count, flags); 269 rle->res = res; 270 break; 271 } 272 } 273 break; 274 default: 275 break; 276 } 277 return (res); 278} 279 280int 281fhc_release_resource(device_t bus, device_t child, int type, int rid, 282 struct resource *r) 283{ 284 struct resource_list_entry *rle; 285 struct fhc_devinfo *fdi; 286 int error; 287 288 error = bus_generic_release_resource(bus, child, type, rid, r); 289 if (error != 0) 290 return (error); 291 fdi = device_get_ivars(child); 292 rle = resource_list_find(&fdi->fdi_rl, type, rid); 293 if (rle == NULL) 294 panic("fhc_release_resource: can't find resource"); 295 if (rle->res == NULL) 296 panic("fhc_release_resource: resource entry is not busy"); 297 rle->res = NULL; 298 return (error); 299} 300