1/* $NetBSD: podulebus.c,v 1.18 2009/03/18 16:00:08 cegger Exp $ */ 2 3/*- 4 * Copyright (c) 2000 Ben Harris 5 * All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 3. The name of the author may not be used to endorse or promote products 16 * derived from this software without specific prior written permission. 17 * 18 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 19 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 20 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 21 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 22 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 23 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 24 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 25 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 26 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 27 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 */ 29 30#include <sys/cdefs.h> 31__KERNEL_RCSID(0, "$NetBSD: podulebus.c,v 1.18 2009/03/18 16:00:08 cegger Exp $"); 32 33#include <sys/param.h> 34#include <sys/device.h> 35#include <sys/malloc.h> 36#include <sys/systm.h> 37#include <sys/bus.h> 38 39#include <machine/intr.h> 40#include <machine/irq.h> 41#include <machine/machdep.h> 42#include <machine/memcreg.h> 43 44#include <arch/acorn26/iobus/iocreg.h> 45#include <arch/acorn26/iobus/iocvar.h> 46#include <dev/podulebus/podulebus.h> 47#include <arch/acorn26/podulebus/podulebusreg.h> 48 49#include "locators.h" 50 51#include "podloader.h" 52#include "unixbp.h" 53 54#if NUNIXBP > 0 55#include <arch/acorn26/podulebus/unixbpvar.h> 56#endif 57 58static int podulebus_match(device_t, cfdata_t, void *); 59static void podulebus_attach(device_t, device_t , void *); 60static void podulebus_probe_podule(device_t, int); 61static int podulebus_print(void *, char const *); 62static int podulebus_submatch(device_t, cfdata_t, const int *, void *); 63static void podulebus_read_chunks(struct podulebus_attach_args *, int); 64static u_int8_t *podulebus_get_chunk(struct podulebus_attach_args *, int); 65#if NPODLOADER > 0 66void podloader_read_region(struct podulebus_attach_args *pa, u_int src, 67 u_int8_t *dest, size_t length); 68extern register_t _podloader_call(register_t, register_t, register_t, 69 void *, int); 70#endif 71 72struct podulebus_softc { 73 device_t sc_dev; 74 struct ioc_attach_args sc_ioc; 75}; 76 77CFATTACH_DECL_NEW(podulebus, sizeof(struct podulebus_softc), 78 podulebus_match, podulebus_attach, NULL, NULL); 79 80/* ARGSUSED */ 81static int 82podulebus_match(device_t parent, cfdata_t cf, void *aux) 83{ 84 85 /* We can't usefully probe for this */ 86 return 1; 87} 88 89static void 90podulebus_attach(device_t parent, device_t self, void *aux) 91{ 92 int i; 93 struct podulebus_softc *sc = device_private(self); 94 struct ioc_attach_args *ioc = aux; 95 96 sc->sc_dev = self; 97 sc->sc_ioc = *ioc; 98 aprint_normal("\n"); 99 100 /* Iterate over the podules attaching them */ 101 for (i = 0; i < MAX_PODULES; i++) 102 podulebus_probe_podule(self, i); 103} 104 105static void 106podulebus_probe_podule(device_t self, int slotnum) 107{ 108 struct podulebus_softc *sc = device_private(self); 109 bus_space_tag_t id_bst; 110 bus_space_handle_t id_bsh; 111 int ecid, w; 112 u_int8_t extecid[EXTECID_SIZE]; 113 struct podulebus_attach_args pa; 114 115 memset(&pa, 0, sizeof(pa)); 116 id_bst = sc->sc_ioc.ioc_sync_t; 117 bus_space_subregion(id_bst, sc->sc_ioc.ioc_sync_h, 118 slotnum * PODULE_GAP, PODULE_GAP, &id_bsh); 119 ecid = bus_space_read_1(id_bst, id_bsh, 0); 120 /* Skip empty or strange slots */ 121 if (ecid & (ECID_NOTPRESENT | ECID_NONCONF)) 122 return; 123 if ((ecid & ECID_ID_MASK) == ECID_ID_EXTEND) { 124 pa.pa_fast_t = sc->sc_ioc.ioc_fast_t; 125 bus_space_subregion(pa.pa_fast_t, sc->sc_ioc.ioc_fast_h, 126 slotnum * PODULE_GAP, PODULE_GAP, 127 &pa.pa_fast_h); 128 pa.pa_medium_t = sc->sc_ioc.ioc_medium_t; 129 bus_space_subregion(pa.pa_medium_t, sc->sc_ioc.ioc_medium_h, 130 slotnum * PODULE_GAP, PODULE_GAP, 131 &pa.pa_medium_h); 132 pa.pa_slow_t = sc->sc_ioc.ioc_slow_t; 133 bus_space_subregion(pa.pa_slow_t, sc->sc_ioc.ioc_slow_h, 134 slotnum * PODULE_GAP, PODULE_GAP, 135 &pa.pa_slow_h); 136 pa.pa_sync_t = sc->sc_ioc.ioc_sync_t; 137 bus_space_subregion(pa.pa_sync_t, sc->sc_ioc.ioc_sync_h, 138 slotnum * PODULE_GAP, PODULE_GAP, 139 &pa.pa_sync_h); 140 /* XXX This is a hack! */ 141 pa.pa_mod_t = &iobus_bs_tag; 142 bus_space_map(pa.pa_mod_t, 143 (bus_addr_t)MEMC_IO_BASE + slotnum * (PODULE_GAP << 2), 144 (PODULE_GAP << 2), 0, &pa.pa_mod_h); 145 bus_space_read_region_1(id_bst, id_bsh, 0, 146 extecid, EXTECID_SIZE); 147 /* XXX If you thought that was a hack... */ 148 pa.pa_mod_base = pa.pa_mod_h; 149 pa.pa_fast_base = pa.pa_fast_h; 150 pa.pa_medium_base = pa.pa_medium_h; 151 pa.pa_slow_base = pa.pa_slow_h; 152 pa.pa_sync_base = pa.pa_sync_h; 153 pa.pa_ecid = ecid; 154 pa.pa_flags1 = extecid[EXTECID_F1]; 155 pa.pa_manufacturer = (extecid[EXTECID_MLO] | 156 extecid[EXTECID_MHI] << 8); 157 pa.pa_product = (extecid[EXTECID_PLO] | 158 extecid[EXTECID_PHI] << 8); 159 pa.pa_slotnum = slotnum; 160 pa.pa_ih = slotnum; 161 if (pa.pa_flags1 & EXTECID_F1_CD) { 162 w = pa.pa_flags1 & EXTECID_F1_W_MASK; 163 if (w != EXTECID_F1_W_8BIT) { 164 /* RISC OS 3 can't handle this either. */ 165 aprint_error("%s:%d: ROM is not 8 bits wide; " 166 "ignoring it\n", 167 device_xname(self), pa.pa_slotnum); 168 } else { 169 podulebus_read_chunks(&pa, 0); 170 pa.pa_descr = podulebus_get_chunk(&pa, 171 CHUNK_DEV_DESCR); 172 } 173 } 174 pa.pa_slotflags = 0; 175 config_found_sm_loc(self, "podulebus", NULL, &pa, 176 podulebus_print, podulebus_submatch); 177 if (pa.pa_chunks) 178 free(pa.pa_chunks, M_DEVBUF); 179 if (pa.pa_descr) 180 free(pa.pa_descr, M_DEVBUF); 181 if (pa.pa_loader) 182 free(pa.pa_loader, M_DEVBUF); 183 } else 184 aprint_normal("%s:%d: non-extended podule ignored.\n", 185 device_xname(self), slotnum); 186} 187 188static void 189podulebus_read_chunks(struct podulebus_attach_args *pa, int useloader) 190{ 191 u_int8_t chunk[8]; 192 u_int ptr, nchunks, type, length, offset; 193 194 nchunks = pa->pa_nchunks; 195 if (useloader) 196 ptr = 0; 197 else 198 ptr = EXTECID_SIZE + IRQPTR_SIZE; 199 for (;;) { 200#if NPODLOADER > 0 201 if (useloader) 202 podloader_read_region(pa, ptr, chunk, 8); 203 else 204#endif 205 bus_space_read_region_1(pa->pa_sync_t, pa->pa_sync_h, 206 ptr, chunk, 8); 207 ptr += 8; 208 type = chunk[0]; 209 length = chunk[1] | chunk[2] << 8 | chunk[3] << 16; 210 if (length == 0) 211 break; 212 offset = chunk[4] | chunk[5] << 8 | 213 chunk[6] << 16 | chunk[7] << 24; 214 if ((offset + length) > PODULE_GAP) 215 continue; 216 nchunks++; 217 pa->pa_chunks = realloc(pa->pa_chunks, 218 nchunks * sizeof(*pa->pa_chunks), 219 M_DEVBUF, M_WAITOK); 220 pa->pa_chunks[nchunks-1].pc_type = type; 221 pa->pa_chunks[nchunks-1].pc_length = length; 222 pa->pa_chunks[nchunks-1].pc_offset = offset; 223 pa->pa_chunks[nchunks-1].pc_useloader = useloader; 224 } 225 pa->pa_nchunks = nchunks; 226} 227 228static u_int8_t * 229podulebus_get_chunk(struct podulebus_attach_args *pa, int type) 230{ 231 int i; 232 struct podulebus_chunk *pc; 233 u_int8_t *chunk; 234 235 for (i = 0; i < pa->pa_nchunks; i++) { 236 pc = &pa->pa_chunks[i]; 237 if (pc->pc_type == type) { 238 chunk = malloc( pc->pc_length + 1, M_DEVBUF, M_WAITOK); 239#if NPODLOADER > 0 240 if (pc->pc_useloader) 241 podloader_read_region(pa, pc->pc_offset, chunk, 242 pc->pc_length); 243 else 244#endif 245 bus_space_read_region_1(pa->pa_sync_t, 246 pa->pa_sync_h, pc->pc_offset, chunk, 247 pc->pc_length); 248 chunk[pc->pc_length] = 0; 249 return chunk; 250 } 251 } 252 return NULL; 253} 254 255#if NPODLOADER > 0 256int 257podulebus_initloader(struct podulebus_attach_args *pa) 258{ 259 260 if (pa->pa_loader != NULL) 261 return 0; 262 pa->pa_loader = podulebus_get_chunk(pa, CHUNK_RISCOS_LOADER); 263 if (pa->pa_loader == NULL) 264 return -1; 265 podulebus_read_chunks(pa, 1); 266 if (pa->pa_descr) 267 free(pa->pa_descr, M_DEVBUF); 268 pa->pa_descr = podulebus_get_chunk(pa, CHUNK_DEV_DESCR); 269 return 0; 270} 271 272static register_t 273podloader_call(register_t r0, register_t r1, register_t r11, void *loader, 274 int entry) 275{ 276 int s; 277 register_t result; 278 279 s = splhigh(); 280 result = _podloader_call(r0, r1, r11, loader, entry); 281 splx(s); 282 return result; 283} 284 285int 286podloader_readbyte(struct podulebus_attach_args *pa, u_int addr) 287{ 288 289 return podloader_call(0, addr, pa->pa_sync_base, pa->pa_loader, 0); 290} 291 292void 293podloader_writebyte(struct podulebus_attach_args *pa, u_int addr, int val) 294{ 295 296 podloader_call(val, addr, pa->pa_sync_base, pa->pa_loader, 1); 297} 298 299void 300podloader_reset(struct podulebus_attach_args *pa) 301{ 302 303 podloader_call(0, 0, pa->pa_sync_base, pa->pa_loader, 2); 304} 305 306int 307podloader_callloader(struct podulebus_attach_args *pa, u_int r0, u_int r1) 308{ 309 310 return podloader_call(r0, r1, pa->pa_sync_base, pa->pa_loader, 3); 311} 312 313void 314podloader_read_region(struct podulebus_attach_args *pa, u_int src, 315 u_int8_t *dest, size_t length) 316{ 317 318 while (length--) 319 *dest++ = podloader_readbyte(pa, src++); 320 podloader_reset(pa); 321} 322#endif 323 324static int 325podulebus_print(void *aux, char const *pnp) 326{ 327 struct podulebus_attach_args *pa = aux; 328 char *p, *q; 329 330 if (pnp) { 331 if (pa->pa_descr) { 332 /* Restrict description to ASCII graphic characters. */ 333 for (p = q = pa->pa_descr; *p != '\0'; p++) 334 if (*p >= 32 && *p < 126) 335 *q++ = *p; 336 *q++ = 0; 337 aprint_normal("%s", pa->pa_descr); 338 } else 339 aprint_normal("podule"); 340 aprint_normal(" <%04x:%04x> at %s", 341 pa->pa_manufacturer, pa->pa_product, pnp); 342 } 343 aprint_normal(" slot %d", pa->pa_slotnum); 344 return UNCONF; 345} 346 347static int 348podulebus_submatch(device_t parent, cfdata_t cf, const int *ldesc, void *aux) 349{ 350 struct podulebus_attach_args *pa = aux; 351 352 if (cf->cf_loc[PODULEBUSCF_SLOT] == PODULEBUSCF_SLOT_DEFAULT || 353 cf->cf_loc[PODULEBUSCF_SLOT] == pa->pa_slotnum) 354 return config_match(parent, cf, aux); 355 return 0; 356} 357 358void * 359podulebus_irq_establish(podulebus_intr_handle_t slot, int ipl, 360 int (*func)(void *), void *arg, struct evcnt *ev) 361{ 362 363 /* XXX: support for checking IRQ bit on podule? */ 364#if NUNIXBP > 0 365 if (the_unixbp != NULL) 366 return irq_establish(IRQ_UNIXBP_BASE + slot, ipl, func, arg, 367 ev); 368#endif 369 return irq_establish(IRQ_PIRQ, ipl, func, arg, ev); 370} 371 372void 373podulebus_readcmos(struct podulebus_attach_args *pa, u_int8_t *c) 374{ 375 int i; 376 377 for (i = 0; i < 4; i++) 378 c[i] = cmos_read(PODULE_CMOS_BASE + 379 pa->pa_slotnum * PODULE_CMOS_PERSLOT + i); 380} 381