/* $NetBSD: podulebus.c,v 1.21 2013/08/18 16:08:42 matt Exp $ */ /*- * Copyright (c) 2000 Ben Harris * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. The name of the author may not be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include __KERNEL_RCSID(0, "$NetBSD: podulebus.c,v 1.21 2013/08/18 16:08:42 matt Exp $"); #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "locators.h" #include "podloader.h" #include "unixbp.h" #if NUNIXBP > 0 #include #endif static int podulebus_match(device_t, cfdata_t, void *); static void podulebus_attach(device_t, device_t , void *); static void podulebus_probe_podule(device_t, int); static int podulebus_print(void *, char const *); static int podulebus_submatch(device_t, cfdata_t, const int *, void *); static void podulebus_read_chunks(struct podulebus_attach_args *, int); static uint8_t *podulebus_get_chunk(struct podulebus_attach_args *, int); #if NPODLOADER > 0 void podloader_read_region(struct podulebus_attach_args *pa, u_int src, uint8_t *dest, size_t length); extern register_t _podloader_call(register_t, register_t, register_t, void *, int); #endif struct podulebus_softc { device_t sc_dev; struct ioc_attach_args sc_ioc; }; CFATTACH_DECL_NEW(podulebus, sizeof(struct podulebus_softc), podulebus_match, podulebus_attach, NULL, NULL); /* ARGSUSED */ static int podulebus_match(device_t parent, cfdata_t cf, void *aux) { /* We can't usefully probe for this */ return 1; } static void podulebus_attach(device_t parent, device_t self, void *aux) { int i; struct podulebus_softc *sc = device_private(self); struct ioc_attach_args *ioc = aux; sc->sc_dev = self; sc->sc_ioc = *ioc; aprint_normal("\n"); /* Iterate over the podules attaching them */ for (i = 0; i < MAX_PODULES; i++) podulebus_probe_podule(self, i); } static void podulebus_probe_podule(device_t self, int slotnum) { struct podulebus_softc *sc = device_private(self); bus_space_tag_t id_bst; bus_space_handle_t id_bsh; int ecid, w; uint8_t extecid[EXTECID_SIZE]; struct podulebus_attach_args pa; memset(&pa, 0, sizeof(pa)); id_bst = sc->sc_ioc.ioc_sync_t; bus_space_subregion(id_bst, sc->sc_ioc.ioc_sync_h, slotnum * PODULE_GAP, PODULE_GAP, &id_bsh); ecid = bus_space_read_1(id_bst, id_bsh, 0); /* Skip empty or strange slots */ if (ecid & (ECID_NOTPRESENT | ECID_NONCONF)) return; if ((ecid & ECID_ID_MASK) == ECID_ID_EXTEND) { pa.pa_fast_t = sc->sc_ioc.ioc_fast_t; bus_space_subregion(pa.pa_fast_t, sc->sc_ioc.ioc_fast_h, slotnum * PODULE_GAP, PODULE_GAP, &pa.pa_fast_h); pa.pa_medium_t = sc->sc_ioc.ioc_medium_t; bus_space_subregion(pa.pa_medium_t, sc->sc_ioc.ioc_medium_h, slotnum * PODULE_GAP, PODULE_GAP, &pa.pa_medium_h); pa.pa_slow_t = sc->sc_ioc.ioc_slow_t; bus_space_subregion(pa.pa_slow_t, sc->sc_ioc.ioc_slow_h, slotnum * PODULE_GAP, PODULE_GAP, &pa.pa_slow_h); pa.pa_sync_t = sc->sc_ioc.ioc_sync_t; bus_space_subregion(pa.pa_sync_t, sc->sc_ioc.ioc_sync_h, slotnum * PODULE_GAP, PODULE_GAP, &pa.pa_sync_h); /* XXX This is a hack! */ pa.pa_mod_t = &iobus_bs_tag; bus_space_map(pa.pa_mod_t, (bus_addr_t)MEMC_IO_BASE + slotnum * (PODULE_GAP << 2), (PODULE_GAP << 2), 0, &pa.pa_mod_h); bus_space_read_region_1(id_bst, id_bsh, 0, extecid, EXTECID_SIZE); /* XXX If you thought that was a hack... */ pa.pa_mod_base = pa.pa_mod_h; pa.pa_fast_base = pa.pa_fast_h; pa.pa_medium_base = pa.pa_medium_h; pa.pa_slow_base = pa.pa_slow_h; pa.pa_sync_base = pa.pa_sync_h; pa.pa_ecid = ecid; pa.pa_flags1 = extecid[EXTECID_F1]; pa.pa_manufacturer = (extecid[EXTECID_MLO] | extecid[EXTECID_MHI] << 8); pa.pa_product = (extecid[EXTECID_PLO] | extecid[EXTECID_PHI] << 8); pa.pa_slotnum = slotnum; pa.pa_ih = slotnum; if (pa.pa_flags1 & EXTECID_F1_CD) { w = pa.pa_flags1 & EXTECID_F1_W_MASK; if (w != EXTECID_F1_W_8BIT) { /* RISC OS 3 can't handle this either. */ aprint_error("%s:%d: ROM is not 8 bits wide; " "ignoring it\n", device_xname(self), pa.pa_slotnum); } else { podulebus_read_chunks(&pa, 0); pa.pa_descr = podulebus_get_chunk(&pa, CHUNK_DEV_DESCR); } } pa.pa_slotflags = 0; config_found_sm_loc(self, "podulebus", NULL, &pa, podulebus_print, podulebus_submatch); if (pa.pa_chunks) free(pa.pa_chunks, M_DEVBUF); if (pa.pa_descr) free(pa.pa_descr, M_DEVBUF); if (pa.pa_loader) free(pa.pa_loader, M_DEVBUF); } else aprint_normal("%s:%d: non-extended podule ignored.\n", device_xname(self), slotnum); } static void podulebus_read_chunks(struct podulebus_attach_args *pa, int useloader) { uint8_t chunk[8]; u_int ptr, nchunks, type, length, offset; nchunks = pa->pa_nchunks; if (useloader) ptr = 0; else ptr = EXTECID_SIZE + IRQPTR_SIZE; for (;;) { #if NPODLOADER > 0 if (useloader) podloader_read_region(pa, ptr, chunk, 8); else #endif bus_space_read_region_1(pa->pa_sync_t, pa->pa_sync_h, ptr, chunk, 8); ptr += 8; type = chunk[0]; length = chunk[1] | chunk[2] << 8 | chunk[3] << 16; if (length == 0) break; offset = chunk[4] | chunk[5] << 8 | chunk[6] << 16 | chunk[7] << 24; if ((offset + length) > PODULE_GAP) continue; nchunks++; pa->pa_chunks = realloc(pa->pa_chunks, nchunks * sizeof(*pa->pa_chunks), M_DEVBUF, M_WAITOK); pa->pa_chunks[nchunks-1].pc_type = type; pa->pa_chunks[nchunks-1].pc_length = length; pa->pa_chunks[nchunks-1].pc_offset = offset; pa->pa_chunks[nchunks-1].pc_useloader = useloader; } pa->pa_nchunks = nchunks; } static uint8_t * podulebus_get_chunk(struct podulebus_attach_args *pa, int type) { int i; struct podulebus_chunk *pc; uint8_t *chunk; for (i = 0; i < pa->pa_nchunks; i++) { pc = &pa->pa_chunks[i]; if (pc->pc_type == type) { chunk = malloc( pc->pc_length + 1, M_DEVBUF, M_WAITOK); #if NPODLOADER > 0 if (pc->pc_useloader) podloader_read_region(pa, pc->pc_offset, chunk, pc->pc_length); else #endif bus_space_read_region_1(pa->pa_sync_t, pa->pa_sync_h, pc->pc_offset, chunk, pc->pc_length); chunk[pc->pc_length] = 0; return chunk; } } return NULL; } #if NPODLOADER > 0 int podulebus_initloader(struct podulebus_attach_args *pa) { if (pa->pa_loader != NULL) return 0; pa->pa_loader = podulebus_get_chunk(pa, CHUNK_RISCOS_LOADER); if (pa->pa_loader == NULL) return -1; podulebus_read_chunks(pa, 1); if (pa->pa_descr) free(pa->pa_descr, M_DEVBUF); pa->pa_descr = podulebus_get_chunk(pa, CHUNK_DEV_DESCR); return 0; } static register_t podloader_call(register_t r0, register_t r1, register_t r11, void *loader, int entry) { int s; register_t result; s = splhigh(); result = _podloader_call(r0, r1, r11, loader, entry); splx(s); return result; } int podloader_readbyte(struct podulebus_attach_args *pa, u_int addr) { return podloader_call(0, addr, pa->pa_sync_base, pa->pa_loader, 0); } void podloader_writebyte(struct podulebus_attach_args *pa, u_int addr, int val) { podloader_call(val, addr, pa->pa_sync_base, pa->pa_loader, 1); } void podloader_reset(struct podulebus_attach_args *pa) { podloader_call(0, 0, pa->pa_sync_base, pa->pa_loader, 2); } int podloader_callloader(struct podulebus_attach_args *pa, u_int r0, u_int r1) { return podloader_call(r0, r1, pa->pa_sync_base, pa->pa_loader, 3); } void podloader_read_region(struct podulebus_attach_args *pa, u_int src, uint8_t *dest, size_t length) { while (length--) *dest++ = podloader_readbyte(pa, src++); podloader_reset(pa); } #endif static int podulebus_print(void *aux, char const *pnp) { struct podulebus_attach_args *pa = aux; char *p, *q; if (pnp) { if (pa->pa_descr) { /* Restrict description to ASCII graphic characters. */ for (p = q = pa->pa_descr; *p != '\0'; p++) if (*p >= 32 && *p < 126) *q++ = *p; *q++ = 0; aprint_normal("%s", pa->pa_descr); } else aprint_normal("podule"); aprint_normal(" <%04x:%04x> at %s", pa->pa_manufacturer, pa->pa_product, pnp); } aprint_normal(" slot %d", pa->pa_slotnum); return UNCONF; } static int podulebus_submatch(device_t parent, cfdata_t cf, const int *ldesc, void *aux) { struct podulebus_attach_args *pa = aux; if (cf->cf_loc[PODULEBUSCF_SLOT] == PODULEBUSCF_SLOT_DEFAULT || cf->cf_loc[PODULEBUSCF_SLOT] == pa->pa_slotnum) return config_match(parent, cf, aux); return 0; } void * podulebus_irq_establish(podulebus_intr_handle_t slot, int ipl, int (*func)(void *), void *arg, struct evcnt *ev) { /* XXX: support for checking IRQ bit on podule? */ #if NUNIXBP > 0 if (the_unixbp != NULL) return irq_establish(IRQ_UNIXBP_BASE + slot, ipl, func, arg, ev); #endif return irq_establish(IRQ_PIRQ, ipl, func, arg, ev); } void podulebus_readcmos(struct podulebus_attach_args *pa, uint8_t *c) { int i; for (i = 0; i < 4; i++) c[i] = cmos_read(PODULE_CMOS_BASE + pa->pa_slotnum * PODULE_CMOS_PERSLOT + i); }