/*- * Copyright (C) 2012 Margarida Gouveia * 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, * without modification, immediately at the beginning of the file. * 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. * * 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 __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define WIIBUS_CSR_ADDR 0x0d800100 #define WIIBUS_CSR_LEN 0x300 #define WIIBUS_CSR_RESET 0x94 struct wiibus_softc { device_t sc_dev; struct rman sc_rman; bus_space_tag_t sc_tag; bus_space_handle_t sc_handle; }; static struct wiibus_softc *wiibus_sc = NULL; static uint32_t wiibus_csr_read(struct wiibus_softc *, uint16_t); static void wiibus_csr_write(struct wiibus_softc *, uint16_t, uint32_t); static void wiibus_identify(driver_t *, device_t); static int wiibus_probe(device_t); static int wiibus_attach(device_t); static int wiibus_print_child(device_t, device_t); static struct resource * wiibus_alloc_resource(device_t, device_t, int, int *, unsigned long, unsigned long, unsigned long, unsigned int); static int wiibus_activate_resource(device_t, device_t, int, int, struct resource *); void wiibus_reset_system(void); static device_method_t wiibus_methods[] = { /* Device interface */ DEVMETHOD(device_identify, wiibus_identify), DEVMETHOD(device_probe, wiibus_probe), DEVMETHOD(device_attach, wiibus_attach), /* Bus interface */ DEVMETHOD(bus_add_child, bus_generic_add_child), DEVMETHOD(bus_print_child, wiibus_print_child), DEVMETHOD(bus_setup_intr, bus_generic_setup_intr), DEVMETHOD(bus_teardown_intr, bus_generic_teardown_intr), DEVMETHOD(bus_alloc_resource, wiibus_alloc_resource), DEVMETHOD(bus_activate_resource,wiibus_activate_resource), DEVMETHOD_END }; static MALLOC_DEFINE(M_WIIBUS, "wiibus", "Nintendo Wii system bus"); struct wiibus_devinfo { struct resource_list di_resources; uint8_t di_init; }; static driver_t wiibus_driver = { "wiibus", wiibus_methods, sizeof(struct wiibus_softc) }; static devclass_t wiibus_devclass; DRIVER_MODULE(wiibus, nexus, wiibus_driver, wiibus_devclass, 0, 0); static uint32_t wiibus_csr_read(struct wiibus_softc *sc, uint16_t reg) { return (bus_space_read_4(sc->sc_tag, sc->sc_handle, reg)); } static void wiibus_csr_write(struct wiibus_softc *sc, uint16_t reg, uint32_t val) { bus_space_write_4(sc->sc_tag, sc->sc_handle, reg, val); } static void wiibus_identify(driver_t *driver, device_t parent) { if (strcmp(installed_platform(), "wii") != 0) return; if (device_find_child(parent, "wiibus", -1) == NULL) BUS_ADD_CHILD(parent, 0, "wiibus", 0); } static int wiibus_probe(device_t dev) { device_set_desc(dev, "Nintendo Wii System Bus"); return (BUS_PROBE_NOWILDCARD); } static void wiibus_init_device_resources(struct rman *rm, struct wiibus_devinfo *dinfo, unsigned int rid, uintptr_t addr, size_t len, unsigned int irq) { if (!dinfo->di_init) { resource_list_init(&dinfo->di_resources); dinfo->di_init++; } if (addr) { rman_manage_region(rm, addr, addr + len - 1); resource_list_add(&dinfo->di_resources, SYS_RES_MEMORY, rid, addr, addr + len, len); } if (irq) resource_list_add(&dinfo->di_resources, SYS_RES_IRQ, rid, irq, irq, 1); } static int wiibus_attach(device_t self) { struct wiibus_softc *sc; struct wiibus_devinfo *dinfo; device_t cdev; sc = device_get_softc(self); sc->sc_rman.rm_type = RMAN_ARRAY; sc->sc_rman.rm_descr = "Wii Bus Memory Mapped I/O"; rman_init(&sc->sc_rman); KASSERT(wiibus_sc == NULL, ("wiibus_sc already initialised")); wiibus_sc = sc; /* Nintendo PIC */ dinfo = malloc(sizeof(*dinfo), M_WIIBUS, M_WAITOK | M_ZERO); wiibus_init_device_resources(&sc->sc_rman, dinfo, 0, WIIPIC_REG_ADDR, WIIPIC_REG_LEN, 1); cdev = BUS_ADD_CHILD(self, 0, "wiipic", 0); device_set_ivars(cdev, dinfo); /* Framebuffer */ dinfo = malloc(sizeof(*dinfo), M_WIIBUS, M_WAITOK | M_ZERO); wiibus_init_device_resources(&sc->sc_rman, dinfo, 0, WIIFB_REG_ADDR, WIIFB_REG_LEN, 8); wiibus_init_device_resources(&sc->sc_rman, dinfo, 1, WIIFB_FB_ADDR, WIIFB_FB_LEN, 0); cdev = BUS_ADD_CHILD(self, 0, "wiifb", 0); device_set_ivars(cdev, dinfo); /* External Interface Bus */ dinfo = malloc(sizeof(*dinfo), M_WIIBUS, M_WAITOK | M_ZERO); wiibus_init_device_resources(&sc->sc_rman, dinfo, 0, WIIEXI_REG_ADDR, WIIEXI_REG_LEN, 4); cdev = BUS_ADD_CHILD(self, 0, "wiiexi", 0); device_set_ivars(cdev, dinfo); /* Nintendo IOS IPC */ dinfo = malloc(sizeof(*dinfo), M_WIIBUS, M_WAITOK | M_ZERO); wiibus_init_device_resources(&sc->sc_rman, dinfo, 0, WIIIPC_REG_ADDR, WIIIPC_REG_LEN, 14); wiibus_init_device_resources(&sc->sc_rman, dinfo, 1, WIIIPC_IOH_ADDR, WIIIPC_IOH_LEN, 0); cdev = BUS_ADD_CHILD(self, 0, "wiiipc", 0); device_set_ivars(cdev, dinfo); /* GPIO */ dinfo = malloc(sizeof(*dinfo), M_WIIBUS, M_WAITOK | M_ZERO); wiibus_init_device_resources(&sc->sc_rman, dinfo, 0, WIIGPIO_REG_ADDR, WIIGPIO_REG_LEN, 0); cdev = BUS_ADD_CHILD(self, 0, "wiigpio", 0); device_set_ivars(cdev, dinfo); /* The control registers */ sc->sc_tag = &bs_be_tag; sc->sc_handle = (bus_space_handle_t)pmap_mapdev(WIIBUS_CSR_ADDR, WIIBUS_CSR_LEN); return (bus_generic_attach(self)); } static int wiibus_print_child(device_t dev, device_t child) { struct wiibus_devinfo *dinfo = device_get_ivars(child); int retval = 0; retval += bus_print_child_header(dev, child); retval += resource_list_print_type(&dinfo->di_resources, "mem", SYS_RES_MEMORY, "%#lx"); retval += resource_list_print_type(&dinfo->di_resources, "irq", SYS_RES_IRQ, "%ld"); retval += bus_print_child_footer(dev, child); return (retval); } static struct resource * wiibus_alloc_resource(device_t bus, device_t child, int type, int *rid, unsigned long start, unsigned long end, unsigned long count, unsigned int flags) { struct wiibus_softc *sc; struct wiibus_devinfo *dinfo; struct resource_list_entry *rle; struct resource *rv; int needactivate; sc = device_get_softc(bus); dinfo = device_get_ivars(child); needactivate = flags & RF_ACTIVE; flags &= ~RF_ACTIVE; switch (type) { case SYS_RES_MEMORY: rle = resource_list_find(&dinfo->di_resources, SYS_RES_MEMORY, *rid); if (rle == NULL) { device_printf(bus, "no res entry for %s memory 0x%x\n", device_get_nameunit(child), *rid); return (NULL); } rv = rman_reserve_resource(&sc->sc_rman, rle->start, rle->end, rle->count, flags, child); if (rv == NULL) { device_printf(bus, "failed to reserve resource for %s\n", device_get_nameunit(child)); return (NULL); } rman_set_rid(rv, *rid); break; case SYS_RES_IRQ: return (resource_list_alloc(&dinfo->di_resources, bus, child, type, rid, start, end, count, flags)); default: device_printf(bus, "unknown resource request from %s\n", device_get_nameunit(child)); return (NULL); } if (needactivate) { if (bus_activate_resource(child, type, *rid, rv) != 0) { device_printf(bus, "failed to activate resource for %s\n", device_get_nameunit(child)); return (NULL); } } return (rv); } static int wiibus_activate_resource(device_t bus, device_t child, int type, int rid, struct resource *res) { void *p; switch (type) { case SYS_RES_MEMORY: p = pmap_mapdev(rman_get_start(res), rman_get_size(res)); if (p == NULL) return (ENOMEM); rman_set_virtual(res, p); rman_set_bustag(res, &bs_be_tag); rman_set_bushandle(res, (unsigned long)p); break; case SYS_RES_IRQ: return (bus_activate_resource(bus, type, rid, res)); default: device_printf(bus, "unknown activate resource request from %s\n", device_get_nameunit(child)); return (ENXIO); } return (rman_activate_resource(res)); } void wiibus_reset_system(void) { uint32_t r; r = wiibus_csr_read(wiibus_sc, WIIBUS_CSR_RESET); r &= ~1; wiibus_csr_write(wiibus_sc, WIIBUS_CSR_RESET, r); }