108077f58SMarcel Moolenaar /*- 208077f58SMarcel Moolenaar * Copyright (c) 2006-2008, Juniper Networks, Inc. 325c22eb4SRafal Jaworowski * Copyright (c) 2008 Semihalf, Rafal Czubak 4d1d3233eSRafal Jaworowski * Copyright (c) 2009 The FreeBSD Foundation 508077f58SMarcel Moolenaar * All rights reserved. 608077f58SMarcel Moolenaar * 7d1d3233eSRafal Jaworowski * Portions of this software were developed by Semihalf 8d1d3233eSRafal Jaworowski * under sponsorship from the FreeBSD Foundation. 9d1d3233eSRafal Jaworowski * 1008077f58SMarcel Moolenaar * Redistribution and use in source and binary forms, with or without 1108077f58SMarcel Moolenaar * modification, are permitted provided that the following conditions 1208077f58SMarcel Moolenaar * are met: 1308077f58SMarcel Moolenaar * 1. Redistributions of source code must retain the above copyright 1408077f58SMarcel Moolenaar * notice, this list of conditions and the following disclaimer. 1508077f58SMarcel Moolenaar * 2. Redistributions in binary form must reproduce the above copyright 1608077f58SMarcel Moolenaar * notice, this list of conditions and the following disclaimer in the 1708077f58SMarcel Moolenaar * documentation and/or other materials provided with the distribution. 1808077f58SMarcel Moolenaar * 3. The name of the author may not be used to endorse or promote products 1908077f58SMarcel Moolenaar * derived from this software without specific prior written permission. 2008077f58SMarcel Moolenaar * 2108077f58SMarcel Moolenaar * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 2208077f58SMarcel Moolenaar * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 2308077f58SMarcel Moolenaar * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 2408077f58SMarcel Moolenaar * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 2508077f58SMarcel Moolenaar * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 2608077f58SMarcel Moolenaar * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 2708077f58SMarcel Moolenaar * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED 2808077f58SMarcel Moolenaar * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 2908077f58SMarcel Moolenaar * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 3008077f58SMarcel Moolenaar * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 3108077f58SMarcel Moolenaar * SUCH DAMAGE. 3208077f58SMarcel Moolenaar */ 3308077f58SMarcel Moolenaar 3408077f58SMarcel Moolenaar #include <sys/cdefs.h> 3508077f58SMarcel Moolenaar __FBSDID("$FreeBSD$"); 3608077f58SMarcel Moolenaar 3708077f58SMarcel Moolenaar #include <sys/param.h> 3808077f58SMarcel Moolenaar #include <sys/systm.h> 3908077f58SMarcel Moolenaar #include <sys/ktr.h> 4008077f58SMarcel Moolenaar #include <sys/kernel.h> 4108077f58SMarcel Moolenaar #include <sys/malloc.h> 4208077f58SMarcel Moolenaar #include <sys/module.h> 4308077f58SMarcel Moolenaar #include <sys/bus.h> 4408077f58SMarcel Moolenaar #include <sys/rman.h> 4508077f58SMarcel Moolenaar #include <machine/bus.h> 4608077f58SMarcel Moolenaar 4708077f58SMarcel Moolenaar #include <vm/vm.h> 4808077f58SMarcel Moolenaar #include <vm/pmap.h> 4908077f58SMarcel Moolenaar 50d1d3233eSRafal Jaworowski #include <dev/fdt/fdt_common.h> 51d1d3233eSRafal Jaworowski #include <dev/ofw/ofw_bus.h> 52d1d3233eSRafal Jaworowski #include <dev/ofw/ofw_bus_subr.h> 53d1d3233eSRafal Jaworowski 5425c22eb4SRafal Jaworowski #include <powerpc/mpc85xx/mpc85xx.h> 5508077f58SMarcel Moolenaar 56d1d3233eSRafal Jaworowski #include "ofw_bus_if.h" 57d1d3233eSRafal Jaworowski #include "lbc.h" 5808077f58SMarcel Moolenaar 59d1d3233eSRafal Jaworowski #define DEBUG 60d1d3233eSRafal Jaworowski #undef DEBUG 6108077f58SMarcel Moolenaar 62d1d3233eSRafal Jaworowski #ifdef DEBUG 63d1d3233eSRafal Jaworowski #define debugf(fmt, args...) do { printf("%s(): ", __func__); \ 64d1d3233eSRafal Jaworowski printf(fmt,##args); } while (0) 65d1d3233eSRafal Jaworowski #else 66d1d3233eSRafal Jaworowski #define debugf(fmt, args...) 67d1d3233eSRafal Jaworowski #endif 6808077f58SMarcel Moolenaar 6925c22eb4SRafal Jaworowski static __inline void 7025c22eb4SRafal Jaworowski lbc_write_reg(struct lbc_softc *sc, bus_size_t off, uint32_t val) 7125c22eb4SRafal Jaworowski { 7225c22eb4SRafal Jaworowski 7325c22eb4SRafal Jaworowski bus_space_write_4(sc->sc_bst, sc->sc_bsh, off, val); 7425c22eb4SRafal Jaworowski } 7525c22eb4SRafal Jaworowski 7625c22eb4SRafal Jaworowski static __inline uint32_t 7725c22eb4SRafal Jaworowski lbc_read_reg(struct lbc_softc *sc, bus_size_t off) 7825c22eb4SRafal Jaworowski { 7925c22eb4SRafal Jaworowski 8025c22eb4SRafal Jaworowski return (bus_space_read_4(sc->sc_bst, sc->sc_bsh, off)); 8125c22eb4SRafal Jaworowski } 8225c22eb4SRafal Jaworowski 83d1d3233eSRafal Jaworowski static MALLOC_DEFINE(M_LBC, "localbus", "localbus devices information"); 84d1d3233eSRafal Jaworowski 85d1d3233eSRafal Jaworowski static int lbc_probe(device_t); 86d1d3233eSRafal Jaworowski static int lbc_attach(device_t); 87d1d3233eSRafal Jaworowski static int lbc_shutdown(device_t); 88d1d3233eSRafal Jaworowski static struct resource *lbc_alloc_resource(device_t, device_t, int, int *, 89d1d3233eSRafal Jaworowski u_long, u_long, u_long, u_int); 90d1d3233eSRafal Jaworowski static int lbc_print_child(device_t, device_t); 91d1d3233eSRafal Jaworowski static int lbc_release_resource(device_t, device_t, int, int, 92d1d3233eSRafal Jaworowski struct resource *); 93d1d3233eSRafal Jaworowski static const struct ofw_bus_devinfo *lbc_get_devinfo(device_t, device_t); 94d1d3233eSRafal Jaworowski 95d1d3233eSRafal Jaworowski /* 96d1d3233eSRafal Jaworowski * Bus interface definition 97d1d3233eSRafal Jaworowski */ 98d1d3233eSRafal Jaworowski static device_method_t lbc_methods[] = { 99d1d3233eSRafal Jaworowski /* Device interface */ 100d1d3233eSRafal Jaworowski DEVMETHOD(device_probe, lbc_probe), 101d1d3233eSRafal Jaworowski DEVMETHOD(device_attach, lbc_attach), 102d1d3233eSRafal Jaworowski DEVMETHOD(device_shutdown, lbc_shutdown), 103d1d3233eSRafal Jaworowski 104d1d3233eSRafal Jaworowski /* Bus interface */ 105d1d3233eSRafal Jaworowski DEVMETHOD(bus_print_child, lbc_print_child), 106d1d3233eSRafal Jaworowski DEVMETHOD(bus_setup_intr, bus_generic_setup_intr), 107d1d3233eSRafal Jaworowski DEVMETHOD(bus_teardown_intr, NULL), 108d1d3233eSRafal Jaworowski 109d1d3233eSRafal Jaworowski DEVMETHOD(bus_alloc_resource, lbc_alloc_resource), 110d1d3233eSRafal Jaworowski DEVMETHOD(bus_release_resource, lbc_release_resource), 111d1d3233eSRafal Jaworowski DEVMETHOD(bus_activate_resource, bus_generic_activate_resource), 112d1d3233eSRafal Jaworowski DEVMETHOD(bus_deactivate_resource, bus_generic_deactivate_resource), 113d1d3233eSRafal Jaworowski 114d1d3233eSRafal Jaworowski /* OFW bus interface */ 115d1d3233eSRafal Jaworowski DEVMETHOD(ofw_bus_get_devinfo, lbc_get_devinfo), 116d1d3233eSRafal Jaworowski DEVMETHOD(ofw_bus_get_compat, ofw_bus_gen_get_compat), 117d1d3233eSRafal Jaworowski DEVMETHOD(ofw_bus_get_model, ofw_bus_gen_get_model), 118d1d3233eSRafal Jaworowski DEVMETHOD(ofw_bus_get_name, ofw_bus_gen_get_name), 119d1d3233eSRafal Jaworowski DEVMETHOD(ofw_bus_get_node, ofw_bus_gen_get_node), 120d1d3233eSRafal Jaworowski DEVMETHOD(ofw_bus_get_type, ofw_bus_gen_get_type), 121d1d3233eSRafal Jaworowski 122d1d3233eSRafal Jaworowski { 0, 0 } 123d1d3233eSRafal Jaworowski }; 124d1d3233eSRafal Jaworowski 125d1d3233eSRafal Jaworowski static driver_t lbc_driver = { 126d1d3233eSRafal Jaworowski "lbc", 127d1d3233eSRafal Jaworowski lbc_methods, 128d1d3233eSRafal Jaworowski sizeof(struct lbc_softc) 129d1d3233eSRafal Jaworowski }; 130d1d3233eSRafal Jaworowski 131d1d3233eSRafal Jaworowski devclass_t lbc_devclass; 132d1d3233eSRafal Jaworowski 133d1d3233eSRafal Jaworowski DRIVER_MODULE(lbc, fdtbus, lbc_driver, lbc_devclass, 0, 0); 134d1d3233eSRafal Jaworowski 13525c22eb4SRafal Jaworowski /* 13625c22eb4SRafal Jaworowski * Calculate address mask used by OR(n) registers. Use memory region size to 13725c22eb4SRafal Jaworowski * determine mask value. The size must be a power of two and within the range 13825c22eb4SRafal Jaworowski * of 32KB - 4GB. Otherwise error code is returned. Value representing 13925c22eb4SRafal Jaworowski * 4GB size can be passed as 0xffffffff. 14025c22eb4SRafal Jaworowski */ 14125c22eb4SRafal Jaworowski static uint32_t 14225c22eb4SRafal Jaworowski lbc_address_mask(uint32_t size) 14325c22eb4SRafal Jaworowski { 14425c22eb4SRafal Jaworowski int n = 15; 14525c22eb4SRafal Jaworowski 14625c22eb4SRafal Jaworowski if (size == ~0UL) 14725c22eb4SRafal Jaworowski return (0); 14825c22eb4SRafal Jaworowski 14925c22eb4SRafal Jaworowski while (n < 32) { 15025c22eb4SRafal Jaworowski if (size == (1UL << n)) 15125c22eb4SRafal Jaworowski break; 15225c22eb4SRafal Jaworowski n++; 15325c22eb4SRafal Jaworowski } 15425c22eb4SRafal Jaworowski 15525c22eb4SRafal Jaworowski if (n == 32) 15625c22eb4SRafal Jaworowski return (EINVAL); 15725c22eb4SRafal Jaworowski 15825c22eb4SRafal Jaworowski return (0xffff8000 << (n - 15)); 15925c22eb4SRafal Jaworowski } 16025c22eb4SRafal Jaworowski 161d1d3233eSRafal Jaworowski static void 162d1d3233eSRafal Jaworowski lbc_banks_unmap(struct lbc_softc *sc) 16308077f58SMarcel Moolenaar { 164d1d3233eSRafal Jaworowski int i; 16508077f58SMarcel Moolenaar 166d1d3233eSRafal Jaworowski for (i = 0; i < LBC_DEV_MAX; i++) { 167d1d3233eSRafal Jaworowski if (sc->sc_banks[i].size == 0) 168d1d3233eSRafal Jaworowski continue; 16925c22eb4SRafal Jaworowski 170d1d3233eSRafal Jaworowski law_disable(OCP85XX_TGTIF_LBC, sc->sc_banks[i].pa, 171d1d3233eSRafal Jaworowski sc->sc_banks[i].size); 172d1d3233eSRafal Jaworowski pmap_unmapdev(sc->sc_banks[i].va, sc->sc_banks[i].size); 17308077f58SMarcel Moolenaar } 17408077f58SMarcel Moolenaar } 17508077f58SMarcel Moolenaar 17608077f58SMarcel Moolenaar static int 177d1d3233eSRafal Jaworowski lbc_banks_map(struct lbc_softc *sc) 17825c22eb4SRafal Jaworowski { 17925c22eb4SRafal Jaworowski u_long start, size; 180d1d3233eSRafal Jaworowski int error, i; 18125c22eb4SRafal Jaworowski 182d1d3233eSRafal Jaworowski for (i = 0; i < LBC_DEV_MAX; i++) { 183d1d3233eSRafal Jaworowski if (sc->sc_banks[i].size == 0) 18425c22eb4SRafal Jaworowski continue; 18525c22eb4SRafal Jaworowski 186d1d3233eSRafal Jaworowski /* Physical address start/size. */ 187d1d3233eSRafal Jaworowski start = sc->sc_banks[i].pa; 188d1d3233eSRafal Jaworowski size = sc->sc_banks[i].size; 18925c22eb4SRafal Jaworowski 19025c22eb4SRafal Jaworowski /* 191d1d3233eSRafal Jaworowski * Configure LAW for this LBC bank (CS) and map its physical 192d1d3233eSRafal Jaworowski * memory region into KVA. 19325c22eb4SRafal Jaworowski */ 19425c22eb4SRafal Jaworowski error = law_enable(OCP85XX_TGTIF_LBC, start, size); 19525c22eb4SRafal Jaworowski if (error) 19625c22eb4SRafal Jaworowski return (error); 19725c22eb4SRafal Jaworowski 198d1d3233eSRafal Jaworowski sc->sc_banks[i].va = (vm_offset_t)pmap_mapdev(start, size); 199d1d3233eSRafal Jaworowski if (sc->sc_banks[i].va == 0) { 200d1d3233eSRafal Jaworowski lbc_banks_unmap(sc); 20125c22eb4SRafal Jaworowski return (ENOSPC); 20225c22eb4SRafal Jaworowski } 203d1d3233eSRafal Jaworowski } 204d1d3233eSRafal Jaworowski return (0); 205d1d3233eSRafal Jaworowski } 20625c22eb4SRafal Jaworowski 207d1d3233eSRafal Jaworowski static int 208d1d3233eSRafal Jaworowski lbc_banks_enable(struct lbc_softc *sc) 209d1d3233eSRafal Jaworowski { 210d1d3233eSRafal Jaworowski u_long size; 211d1d3233eSRafal Jaworowski uint32_t regval; 212d1d3233eSRafal Jaworowski int error, i; 213d1d3233eSRafal Jaworowski 214d1d3233eSRafal Jaworowski for (i = 0; i < LBC_DEV_MAX; i++) { 215d1d3233eSRafal Jaworowski size = sc->sc_banks[i].size; 216d1d3233eSRafal Jaworowski if (size == 0) 217d1d3233eSRafal Jaworowski continue; 21825c22eb4SRafal Jaworowski /* 219d1d3233eSRafal Jaworowski * Compute and program BR value. 22025c22eb4SRafal Jaworowski */ 221d1d3233eSRafal Jaworowski regval = 0; 222d1d3233eSRafal Jaworowski regval |= sc->sc_banks[i].pa; 22325c22eb4SRafal Jaworowski 224d1d3233eSRafal Jaworowski switch (sc->sc_banks[i].width) { 22525c22eb4SRafal Jaworowski case 8: 226d1d3233eSRafal Jaworowski regval |= (1 << 11); 22725c22eb4SRafal Jaworowski break; 22825c22eb4SRafal Jaworowski case 16: 229d1d3233eSRafal Jaworowski regval |= (2 << 11); 23025c22eb4SRafal Jaworowski break; 23125c22eb4SRafal Jaworowski case 32: 232d1d3233eSRafal Jaworowski regval |= (3 << 11); 23325c22eb4SRafal Jaworowski break; 23425c22eb4SRafal Jaworowski default: 23525c22eb4SRafal Jaworowski error = EINVAL; 23625c22eb4SRafal Jaworowski goto fail; 23725c22eb4SRafal Jaworowski } 238d1d3233eSRafal Jaworowski regval |= (sc->sc_banks[i].decc << 9); 239d1d3233eSRafal Jaworowski regval |= (sc->sc_banks[i].wp << 8); 240d1d3233eSRafal Jaworowski regval |= (sc->sc_banks[i].msel << 5); 241d1d3233eSRafal Jaworowski regval |= (sc->sc_banks[i].atom << 2); 242d1d3233eSRafal Jaworowski regval |= 1; 24325c22eb4SRafal Jaworowski 244d1d3233eSRafal Jaworowski lbc_write_reg(sc, LBC85XX_BR(i), regval); 24525c22eb4SRafal Jaworowski 24625c22eb4SRafal Jaworowski /* 247d1d3233eSRafal Jaworowski * Compute and program OR value. 24825c22eb4SRafal Jaworowski */ 249d1d3233eSRafal Jaworowski regval = 0; 250d1d3233eSRafal Jaworowski regval |= lbc_address_mask(size); 25125c22eb4SRafal Jaworowski 252d1d3233eSRafal Jaworowski switch (sc->sc_banks[i].msel) { 25325c22eb4SRafal Jaworowski case LBCRES_MSEL_GPCM: 25425c22eb4SRafal Jaworowski /* TODO Add flag support for option registers */ 255d1d3233eSRafal Jaworowski regval |= 0x00000ff7; 25625c22eb4SRafal Jaworowski break; 25725c22eb4SRafal Jaworowski case LBCRES_MSEL_FCM: 25825c22eb4SRafal Jaworowski printf("FCM mode not supported yet!"); 25925c22eb4SRafal Jaworowski error = ENOSYS; 26025c22eb4SRafal Jaworowski goto fail; 26125c22eb4SRafal Jaworowski case LBCRES_MSEL_UPMA: 26225c22eb4SRafal Jaworowski case LBCRES_MSEL_UPMB: 26325c22eb4SRafal Jaworowski case LBCRES_MSEL_UPMC: 26425c22eb4SRafal Jaworowski printf("UPM mode not supported yet!"); 26525c22eb4SRafal Jaworowski error = ENOSYS; 26625c22eb4SRafal Jaworowski goto fail; 26725c22eb4SRafal Jaworowski } 268d1d3233eSRafal Jaworowski lbc_write_reg(sc, LBC85XX_OR(i), regval); 269d1d3233eSRafal Jaworowski } 27025c22eb4SRafal Jaworowski 271d1d3233eSRafal Jaworowski /* 272d1d3233eSRafal Jaworowski * Initialize configuration register: 273d1d3233eSRafal Jaworowski * - enable Local Bus 274d1d3233eSRafal Jaworowski * - set data buffer control signal function 275d1d3233eSRafal Jaworowski * - disable parity byte select 276d1d3233eSRafal Jaworowski * - set ECC parity type 277d1d3233eSRafal Jaworowski * - set bus monitor timing and timer prescale 278d1d3233eSRafal Jaworowski */ 279d1d3233eSRafal Jaworowski lbc_write_reg(sc, LBC85XX_LBCR, 0); 280d1d3233eSRafal Jaworowski 281d1d3233eSRafal Jaworowski /* 282d1d3233eSRafal Jaworowski * Initialize clock ratio register: 283d1d3233eSRafal Jaworowski * - disable PLL bypass mode 284d1d3233eSRafal Jaworowski * - configure LCLK delay cycles for the assertion of LALE 285d1d3233eSRafal Jaworowski * - set system clock divider 286d1d3233eSRafal Jaworowski */ 287d1d3233eSRafal Jaworowski lbc_write_reg(sc, LBC85XX_LCRR, 0x00030008); 28825c22eb4SRafal Jaworowski 28925c22eb4SRafal Jaworowski return (0); 290d1d3233eSRafal Jaworowski 29125c22eb4SRafal Jaworowski fail: 292d1d3233eSRafal Jaworowski lbc_banks_unmap(sc); 29325c22eb4SRafal Jaworowski return (error); 294d1d3233eSRafal Jaworowski } 295d1d3233eSRafal Jaworowski 296d1d3233eSRafal Jaworowski static void 297d1d3233eSRafal Jaworowski fdt_lbc_fixup(phandle_t node, struct lbc_softc *sc, struct lbc_devinfo *di) 298d1d3233eSRafal Jaworowski { 299d1d3233eSRafal Jaworowski pcell_t width; 300d1d3233eSRafal Jaworowski int bank; 301d1d3233eSRafal Jaworowski 302d1d3233eSRafal Jaworowski if (OF_getprop(node, "bank-width", (void *)&width, sizeof(width)) <= 0) 303d1d3233eSRafal Jaworowski return; 304d1d3233eSRafal Jaworowski 305d1d3233eSRafal Jaworowski bank = di->di_bank; 306d1d3233eSRafal Jaworowski if (sc->sc_banks[bank].size == 0) 307d1d3233eSRafal Jaworowski return; 308d1d3233eSRafal Jaworowski 309d1d3233eSRafal Jaworowski /* Express width in bits. */ 310d1d3233eSRafal Jaworowski sc->sc_banks[bank].width = width * 8; 311d1d3233eSRafal Jaworowski } 312d1d3233eSRafal Jaworowski 313d1d3233eSRafal Jaworowski static int 314d1d3233eSRafal Jaworowski fdt_lbc_reg_decode(phandle_t node, struct lbc_softc *sc, 315d1d3233eSRafal Jaworowski struct lbc_devinfo *di) 316d1d3233eSRafal Jaworowski { 317d1d3233eSRafal Jaworowski u_long start, end, count; 318d1d3233eSRafal Jaworowski pcell_t *reg, *regptr; 319d1d3233eSRafal Jaworowski pcell_t addr_cells, size_cells; 320d1d3233eSRafal Jaworowski int tuple_size, tuples; 321d1d3233eSRafal Jaworowski int i, rv, bank; 322d1d3233eSRafal Jaworowski 323d1d3233eSRafal Jaworowski if (fdt_addrsize_cells(OF_parent(node), &addr_cells, &size_cells) != 0) 324d1d3233eSRafal Jaworowski return (ENXIO); 325d1d3233eSRafal Jaworowski 326d1d3233eSRafal Jaworowski tuple_size = sizeof(pcell_t) * (addr_cells + size_cells); 327d1d3233eSRafal Jaworowski tuples = OF_getprop_alloc(node, "reg", tuple_size, (void **)®); 328d1d3233eSRafal Jaworowski debugf("addr_cells = %d, size_cells = %d\n", addr_cells, size_cells); 329d1d3233eSRafal Jaworowski debugf("tuples = %d, tuple size = %d\n", tuples, tuple_size); 330d1d3233eSRafal Jaworowski if (tuples <= 0) 331d1d3233eSRafal Jaworowski /* No 'reg' property in this node. */ 332d1d3233eSRafal Jaworowski return (0); 333d1d3233eSRafal Jaworowski 334d1d3233eSRafal Jaworowski regptr = reg; 335d1d3233eSRafal Jaworowski for (i = 0; i < tuples; i++) { 336d1d3233eSRafal Jaworowski 337d1d3233eSRafal Jaworowski bank = fdt_data_get((void *)reg, 1); 338d1d3233eSRafal Jaworowski di->di_bank = bank; 339d1d3233eSRafal Jaworowski reg += 1; 340d1d3233eSRafal Jaworowski 341d1d3233eSRafal Jaworowski /* Get address/size. */ 342d1d3233eSRafal Jaworowski rv = fdt_data_to_res(reg, addr_cells - 1, size_cells, &start, 343d1d3233eSRafal Jaworowski &count); 344d1d3233eSRafal Jaworowski if (rv != 0) { 345d1d3233eSRafal Jaworowski resource_list_free(&di->di_res); 346d1d3233eSRafal Jaworowski goto out; 347d1d3233eSRafal Jaworowski } 348d1d3233eSRafal Jaworowski reg += addr_cells - 1 + size_cells; 349d1d3233eSRafal Jaworowski 350d1d3233eSRafal Jaworowski /* Calculate address range relative to VA base. */ 351d1d3233eSRafal Jaworowski start = sc->sc_banks[bank].va + start; 352d1d3233eSRafal Jaworowski end = start + count - 1; 353d1d3233eSRafal Jaworowski 354d1d3233eSRafal Jaworowski debugf("reg addr bank = %d, start = %lx, end = %lx, " 355d1d3233eSRafal Jaworowski "count = %lx\n", bank, start, end, count); 356d1d3233eSRafal Jaworowski 357d1d3233eSRafal Jaworowski /* Use bank (CS) cell as rid. */ 358d1d3233eSRafal Jaworowski resource_list_add(&di->di_res, SYS_RES_MEMORY, bank, start, 359d1d3233eSRafal Jaworowski end, count); 360d1d3233eSRafal Jaworowski } 361d1d3233eSRafal Jaworowski rv = 0; 362d1d3233eSRafal Jaworowski out: 363d1d3233eSRafal Jaworowski free(regptr, M_OFWPROP); 364d1d3233eSRafal Jaworowski return (rv); 36525c22eb4SRafal Jaworowski } 36625c22eb4SRafal Jaworowski 36725c22eb4SRafal Jaworowski static int 36808077f58SMarcel Moolenaar lbc_probe(device_t dev) 36908077f58SMarcel Moolenaar { 37008077f58SMarcel Moolenaar 371d1d3233eSRafal Jaworowski if (!(ofw_bus_is_compatible(dev, "fsl,lbc") || 372d1d3233eSRafal Jaworowski ofw_bus_is_compatible(dev, "fsl,elbc"))) 37308077f58SMarcel Moolenaar return (ENXIO); 37408077f58SMarcel Moolenaar 375d1d3233eSRafal Jaworowski device_set_desc(dev, "Freescale Local Bus Controller"); 37608077f58SMarcel Moolenaar return (BUS_PROBE_DEFAULT); 37708077f58SMarcel Moolenaar } 37808077f58SMarcel Moolenaar 37908077f58SMarcel Moolenaar static int 38008077f58SMarcel Moolenaar lbc_attach(device_t dev) 38108077f58SMarcel Moolenaar { 38208077f58SMarcel Moolenaar struct lbc_softc *sc; 383d1d3233eSRafal Jaworowski struct lbc_devinfo *di; 38408077f58SMarcel Moolenaar struct rman *rm; 385d1d3233eSRafal Jaworowski u_long offset, start, size; 386d1d3233eSRafal Jaworowski device_t cdev; 387d1d3233eSRafal Jaworowski phandle_t node, child; 388d1d3233eSRafal Jaworowski pcell_t *ranges, *rangesptr; 389d1d3233eSRafal Jaworowski int tuple_size, tuples; 390d1d3233eSRafal Jaworowski int par_addr_cells; 391d1d3233eSRafal Jaworowski int bank, error, i; 39208077f58SMarcel Moolenaar 39308077f58SMarcel Moolenaar sc = device_get_softc(dev); 39408077f58SMarcel Moolenaar sc->sc_dev = dev; 39508077f58SMarcel Moolenaar 39608077f58SMarcel Moolenaar sc->sc_rid = 0; 39708077f58SMarcel Moolenaar sc->sc_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &sc->sc_rid, 39808077f58SMarcel Moolenaar RF_ACTIVE); 39908077f58SMarcel Moolenaar if (sc->sc_res == NULL) 40008077f58SMarcel Moolenaar return (ENXIO); 40108077f58SMarcel Moolenaar 40208077f58SMarcel Moolenaar sc->sc_bst = rman_get_bustag(sc->sc_res); 40308077f58SMarcel Moolenaar sc->sc_bsh = rman_get_bushandle(sc->sc_res); 404d1d3233eSRafal Jaworowski rangesptr = NULL; 40508077f58SMarcel Moolenaar 40608077f58SMarcel Moolenaar rm = &sc->sc_rman; 40708077f58SMarcel Moolenaar rm->rm_type = RMAN_ARRAY; 408d1d3233eSRafal Jaworowski rm->rm_descr = "Local Bus Space"; 40925c22eb4SRafal Jaworowski rm->rm_start = 0UL; 41025c22eb4SRafal Jaworowski rm->rm_end = ~0UL; 41108077f58SMarcel Moolenaar error = rman_init(rm); 41208077f58SMarcel Moolenaar if (error) 41308077f58SMarcel Moolenaar goto fail; 41408077f58SMarcel Moolenaar 41508077f58SMarcel Moolenaar error = rman_manage_region(rm, rm->rm_start, rm->rm_end); 41608077f58SMarcel Moolenaar if (error) { 41708077f58SMarcel Moolenaar rman_fini(rm); 41808077f58SMarcel Moolenaar goto fail; 41908077f58SMarcel Moolenaar } 42008077f58SMarcel Moolenaar 42125c22eb4SRafal Jaworowski /* 422d1d3233eSRafal Jaworowski * Process 'ranges' property. 42325c22eb4SRafal Jaworowski */ 424d1d3233eSRafal Jaworowski node = ofw_bus_get_node(dev); 425d1d3233eSRafal Jaworowski if ((fdt_addrsize_cells(node, &sc->sc_addr_cells, 426d1d3233eSRafal Jaworowski &sc->sc_size_cells)) != 0) { 42725c22eb4SRafal Jaworowski error = ENXIO; 42825c22eb4SRafal Jaworowski goto fail; 42925c22eb4SRafal Jaworowski } 43008077f58SMarcel Moolenaar 431d1d3233eSRafal Jaworowski par_addr_cells = fdt_parent_addr_cells(node); 432d1d3233eSRafal Jaworowski if (par_addr_cells > 2) { 433d1d3233eSRafal Jaworowski device_printf(dev, "unsupported parent #addr-cells\n"); 434d1d3233eSRafal Jaworowski error = ERANGE; 435d1d3233eSRafal Jaworowski goto fail; 436d1d3233eSRafal Jaworowski } 437d1d3233eSRafal Jaworowski tuple_size = sizeof(pcell_t) * (sc->sc_addr_cells + par_addr_cells + 438d1d3233eSRafal Jaworowski sc->sc_size_cells); 439d1d3233eSRafal Jaworowski 440d1d3233eSRafal Jaworowski tuples = OF_getprop_alloc(node, "ranges", tuple_size, 441d1d3233eSRafal Jaworowski (void **)&ranges); 442d1d3233eSRafal Jaworowski if (tuples < 0) { 443d1d3233eSRafal Jaworowski device_printf(dev, "could not retrieve 'ranges' property\n"); 444d1d3233eSRafal Jaworowski error = ENXIO; 445d1d3233eSRafal Jaworowski goto fail; 446d1d3233eSRafal Jaworowski } 447d1d3233eSRafal Jaworowski rangesptr = ranges; 448d1d3233eSRafal Jaworowski 449d1d3233eSRafal Jaworowski debugf("par addr_cells = %d, addr_cells = %d, size_cells = %d, " 450d1d3233eSRafal Jaworowski "tuple_size = %d, tuples = %d\n", par_addr_cells, 451d1d3233eSRafal Jaworowski sc->sc_addr_cells, sc->sc_size_cells, tuple_size, tuples); 452d1d3233eSRafal Jaworowski 453d1d3233eSRafal Jaworowski start = 0; 454d1d3233eSRafal Jaworowski size = 0; 455d1d3233eSRafal Jaworowski for (i = 0; i < tuples; i++) { 456d1d3233eSRafal Jaworowski 457d1d3233eSRafal Jaworowski /* The first cell is the bank (chip select) number. */ 458d1d3233eSRafal Jaworowski bank = fdt_data_get((void *)ranges, 1); 459d1d3233eSRafal Jaworowski if (bank < 0 || bank > LBC_DEV_MAX) { 460d1d3233eSRafal Jaworowski device_printf(dev, "bank out of range: %d\n", bank); 461d1d3233eSRafal Jaworowski error = ERANGE; 462d1d3233eSRafal Jaworowski goto fail; 463d1d3233eSRafal Jaworowski } 464d1d3233eSRafal Jaworowski ranges += 1; 465d1d3233eSRafal Jaworowski 466d1d3233eSRafal Jaworowski /* 467d1d3233eSRafal Jaworowski * Remaining cells of the child address define offset into 468d1d3233eSRafal Jaworowski * this CS. 469d1d3233eSRafal Jaworowski */ 470d1d3233eSRafal Jaworowski offset = fdt_data_get((void *)ranges, sc->sc_addr_cells - 1); 471d1d3233eSRafal Jaworowski ranges += sc->sc_addr_cells - 1; 472d1d3233eSRafal Jaworowski 473d1d3233eSRafal Jaworowski /* Parent bus start address of this bank. */ 474d1d3233eSRafal Jaworowski start = fdt_data_get((void *)ranges, par_addr_cells); 475d1d3233eSRafal Jaworowski ranges += par_addr_cells; 476d1d3233eSRafal Jaworowski 477d1d3233eSRafal Jaworowski size = fdt_data_get((void *)ranges, sc->sc_size_cells); 478d1d3233eSRafal Jaworowski ranges += sc->sc_size_cells; 479d1d3233eSRafal Jaworowski debugf("bank = %d, start = %lx, size = %lx\n", bank, 480d1d3233eSRafal Jaworowski start, size); 481d1d3233eSRafal Jaworowski 482d1d3233eSRafal Jaworowski sc->sc_banks[bank].pa = start + offset; 483d1d3233eSRafal Jaworowski sc->sc_banks[bank].size = size; 484d1d3233eSRafal Jaworowski 485d1d3233eSRafal Jaworowski /* 486d1d3233eSRafal Jaworowski * Attributes for the bank. 487d1d3233eSRafal Jaworowski * 488d1d3233eSRafal Jaworowski * XXX Note there are no DT bindings defined for them at the 489d1d3233eSRafal Jaworowski * moment, so we need to provide some defaults. 490d1d3233eSRafal Jaworowski */ 491d1d3233eSRafal Jaworowski sc->sc_banks[bank].width = 16; 492d1d3233eSRafal Jaworowski sc->sc_banks[bank].msel = LBCRES_MSEL_GPCM; 493d1d3233eSRafal Jaworowski sc->sc_banks[bank].decc = LBCRES_DECC_DISABLED; 494d1d3233eSRafal Jaworowski sc->sc_banks[bank].atom = LBCRES_ATOM_DISABLED; 495d1d3233eSRafal Jaworowski sc->sc_banks[bank].wp = 0; 496d1d3233eSRafal Jaworowski } 497d1d3233eSRafal Jaworowski 498d1d3233eSRafal Jaworowski /* 499d1d3233eSRafal Jaworowski * Initialize mem-mappings for the LBC banks (i.e. chip selects). 500d1d3233eSRafal Jaworowski */ 501d1d3233eSRafal Jaworowski error = lbc_banks_map(sc); 502d1d3233eSRafal Jaworowski if (error) 503d1d3233eSRafal Jaworowski goto fail; 504d1d3233eSRafal Jaworowski 505d1d3233eSRafal Jaworowski /* 506d1d3233eSRafal Jaworowski * Walk the localbus and add direct subordinates as our children. 507d1d3233eSRafal Jaworowski */ 508d1d3233eSRafal Jaworowski for (child = OF_child(node); child != 0; child = OF_peer(child)) { 509d1d3233eSRafal Jaworowski 510d1d3233eSRafal Jaworowski di = malloc(sizeof(*di), M_LBC, M_WAITOK | M_ZERO); 511d1d3233eSRafal Jaworowski 512d1d3233eSRafal Jaworowski if (ofw_bus_gen_setup_devinfo(&di->di_ofw, child) != 0) { 513d1d3233eSRafal Jaworowski free(di, M_LBC); 514d1d3233eSRafal Jaworowski device_printf(dev, "could not set up devinfo\n"); 515d1d3233eSRafal Jaworowski continue; 516d1d3233eSRafal Jaworowski } 517d1d3233eSRafal Jaworowski 518d1d3233eSRafal Jaworowski resource_list_init(&di->di_res); 519d1d3233eSRafal Jaworowski 520d1d3233eSRafal Jaworowski if (fdt_lbc_reg_decode(child, sc, di)) { 521d1d3233eSRafal Jaworowski device_printf(dev, "could not process 'reg' " 522d1d3233eSRafal Jaworowski "property\n"); 523d1d3233eSRafal Jaworowski ofw_bus_gen_destroy_devinfo(&di->di_ofw); 524d1d3233eSRafal Jaworowski free(di, M_LBC); 525d1d3233eSRafal Jaworowski continue; 526d1d3233eSRafal Jaworowski } 527d1d3233eSRafal Jaworowski 528d1d3233eSRafal Jaworowski fdt_lbc_fixup(child, sc, di); 529d1d3233eSRafal Jaworowski 530d1d3233eSRafal Jaworowski /* Add newbus device for this FDT node */ 531d1d3233eSRafal Jaworowski cdev = device_add_child(dev, NULL, -1); 532d1d3233eSRafal Jaworowski if (cdev == NULL) { 533d1d3233eSRafal Jaworowski device_printf(dev, "could not add child: %s\n", 534d1d3233eSRafal Jaworowski di->di_ofw.obd_name); 535d1d3233eSRafal Jaworowski resource_list_free(&di->di_res); 536d1d3233eSRafal Jaworowski ofw_bus_gen_destroy_devinfo(&di->di_ofw); 537d1d3233eSRafal Jaworowski free(di, M_LBC); 538d1d3233eSRafal Jaworowski continue; 539d1d3233eSRafal Jaworowski } 540d1d3233eSRafal Jaworowski debugf("added child name='%s', node=%p\n", di->di_ofw.obd_name, 541d1d3233eSRafal Jaworowski (void *)child); 542d1d3233eSRafal Jaworowski device_set_ivars(cdev, di); 543d1d3233eSRafal Jaworowski } 544d1d3233eSRafal Jaworowski 545d1d3233eSRafal Jaworowski /* 546d1d3233eSRafal Jaworowski * Enable the LBC. 547d1d3233eSRafal Jaworowski */ 548d1d3233eSRafal Jaworowski lbc_banks_enable(sc); 549d1d3233eSRafal Jaworowski 550d1d3233eSRafal Jaworowski free(rangesptr, M_OFWPROP); 55108077f58SMarcel Moolenaar return (bus_generic_attach(dev)); 55208077f58SMarcel Moolenaar 55308077f58SMarcel Moolenaar fail: 554d1d3233eSRafal Jaworowski free(rangesptr, M_OFWPROP); 55508077f58SMarcel Moolenaar bus_release_resource(dev, SYS_RES_MEMORY, sc->sc_rid, sc->sc_res); 55608077f58SMarcel Moolenaar return (error); 55708077f58SMarcel Moolenaar } 55808077f58SMarcel Moolenaar 55908077f58SMarcel Moolenaar static int 56008077f58SMarcel Moolenaar lbc_shutdown(device_t dev) 56108077f58SMarcel Moolenaar { 56208077f58SMarcel Moolenaar 56308077f58SMarcel Moolenaar /* TODO */ 56408077f58SMarcel Moolenaar return(0); 56508077f58SMarcel Moolenaar } 56608077f58SMarcel Moolenaar 56708077f58SMarcel Moolenaar static struct resource * 568d1d3233eSRafal Jaworowski lbc_alloc_resource(device_t bus, device_t child, int type, int *rid, 56908077f58SMarcel Moolenaar u_long start, u_long end, u_long count, u_int flags) 57008077f58SMarcel Moolenaar { 57108077f58SMarcel Moolenaar struct lbc_softc *sc; 572d1d3233eSRafal Jaworowski struct lbc_devinfo *di; 573d1d3233eSRafal Jaworowski struct resource_list_entry *rle; 574d1d3233eSRafal Jaworowski struct resource *res; 57508077f58SMarcel Moolenaar struct rman *rm; 576d1d3233eSRafal Jaworowski int needactivate; 57708077f58SMarcel Moolenaar 57808077f58SMarcel Moolenaar /* We only support default allocations. */ 57908077f58SMarcel Moolenaar if (start != 0ul || end != ~0ul) 58008077f58SMarcel Moolenaar return (NULL); 58108077f58SMarcel Moolenaar 582d1d3233eSRafal Jaworowski sc = device_get_softc(bus); 58308077f58SMarcel Moolenaar if (type == SYS_RES_IRQ) 584d1d3233eSRafal Jaworowski return (bus_alloc_resource(bus, type, rid, start, end, count, 58508077f58SMarcel Moolenaar flags)); 58608077f58SMarcel Moolenaar 587d1d3233eSRafal Jaworowski /* 588d1d3233eSRafal Jaworowski * Request for the default allocation with a given rid: use resource 589d1d3233eSRafal Jaworowski * list stored in the local device info. 590d1d3233eSRafal Jaworowski */ 591d1d3233eSRafal Jaworowski if ((di = device_get_ivars(child)) == NULL) 592d1d3233eSRafal Jaworowski return (NULL); 593d1d3233eSRafal Jaworowski 594d1d3233eSRafal Jaworowski if (type == SYS_RES_IOPORT) 595d1d3233eSRafal Jaworowski type = SYS_RES_MEMORY; 596d1d3233eSRafal Jaworowski 597d1d3233eSRafal Jaworowski rid = &di->di_bank; 598d1d3233eSRafal Jaworowski 599d1d3233eSRafal Jaworowski rle = resource_list_find(&di->di_res, type, *rid); 600d1d3233eSRafal Jaworowski if (rle == NULL) { 601d1d3233eSRafal Jaworowski device_printf(bus, "no default resources for " 602d1d3233eSRafal Jaworowski "rid = %d, type = %d\n", *rid, type); 60325c22eb4SRafal Jaworowski return (NULL); 60425c22eb4SRafal Jaworowski } 605d1d3233eSRafal Jaworowski start = rle->start; 606d1d3233eSRafal Jaworowski count = rle->count; 607d1d3233eSRafal Jaworowski end = start + count - 1; 60825c22eb4SRafal Jaworowski 609d1d3233eSRafal Jaworowski sc = device_get_softc(bus); 610d1d3233eSRafal Jaworowski 611d1d3233eSRafal Jaworowski needactivate = flags & RF_ACTIVE; 612d1d3233eSRafal Jaworowski flags &= ~RF_ACTIVE; 61308077f58SMarcel Moolenaar 61408077f58SMarcel Moolenaar rm = &sc->sc_rman; 615d1d3233eSRafal Jaworowski 616d1d3233eSRafal Jaworowski res = rman_reserve_resource(rm, start, end, count, flags, child); 617d1d3233eSRafal Jaworowski if (res == NULL) { 618d1d3233eSRafal Jaworowski device_printf(bus, "failed to reserve resource %#lx - %#lx " 619d1d3233eSRafal Jaworowski "(%#lx)\n", start, end, count); 620d1d3233eSRafal Jaworowski return (NULL); 62108077f58SMarcel Moolenaar } 622d1d3233eSRafal Jaworowski 623d1d3233eSRafal Jaworowski rman_set_rid(res, *rid); 624d1d3233eSRafal Jaworowski rman_set_bustag(res, &bs_be_tag); 625d1d3233eSRafal Jaworowski rman_set_bushandle(res, rman_get_start(res)); 626d1d3233eSRafal Jaworowski 627d1d3233eSRafal Jaworowski if (needactivate) 628d1d3233eSRafal Jaworowski if (bus_activate_resource(child, type, *rid, res)) { 629d1d3233eSRafal Jaworowski device_printf(child, "resource activation failed\n"); 630d1d3233eSRafal Jaworowski rman_release_resource(res); 631d1d3233eSRafal Jaworowski return (NULL); 632d1d3233eSRafal Jaworowski } 633d1d3233eSRafal Jaworowski 634d1d3233eSRafal Jaworowski return (res); 63508077f58SMarcel Moolenaar } 63608077f58SMarcel Moolenaar 63708077f58SMarcel Moolenaar static int 63808077f58SMarcel Moolenaar lbc_print_child(device_t dev, device_t child) 63908077f58SMarcel Moolenaar { 640d1d3233eSRafal Jaworowski struct lbc_devinfo *di; 641d1d3233eSRafal Jaworowski struct resource_list *rl; 642d1d3233eSRafal Jaworowski int rv; 64308077f58SMarcel Moolenaar 644d1d3233eSRafal Jaworowski di = device_get_ivars(child); 645d1d3233eSRafal Jaworowski rl = &di->di_res; 64608077f58SMarcel Moolenaar 647d1d3233eSRafal Jaworowski rv = 0; 648d1d3233eSRafal Jaworowski rv += bus_print_child_header(dev, child); 649d1d3233eSRafal Jaworowski rv += resource_list_print_type(rl, "mem", SYS_RES_MEMORY, "%#lx"); 650d1d3233eSRafal Jaworowski rv += resource_list_print_type(rl, "irq", SYS_RES_IRQ, "%ld"); 651d1d3233eSRafal Jaworowski rv += bus_print_child_footer(dev, child); 65208077f58SMarcel Moolenaar 653d1d3233eSRafal Jaworowski return (rv); 65408077f58SMarcel Moolenaar } 65508077f58SMarcel Moolenaar 65608077f58SMarcel Moolenaar static int 65708077f58SMarcel Moolenaar lbc_release_resource(device_t dev, device_t child, int type, int rid, 65808077f58SMarcel Moolenaar struct resource *res) 65908077f58SMarcel Moolenaar { 660d1d3233eSRafal Jaworowski int err; 661d1d3233eSRafal Jaworowski 662d1d3233eSRafal Jaworowski if (rman_get_flags(res) & RF_ACTIVE) { 663d1d3233eSRafal Jaworowski err = bus_deactivate_resource(child, type, rid, res); 664d1d3233eSRafal Jaworowski if (err) 665d1d3233eSRafal Jaworowski return (err); 666d1d3233eSRafal Jaworowski } 66708077f58SMarcel Moolenaar 66808077f58SMarcel Moolenaar return (rman_release_resource(res)); 66908077f58SMarcel Moolenaar } 67008077f58SMarcel Moolenaar 671d1d3233eSRafal Jaworowski static const struct ofw_bus_devinfo * 672d1d3233eSRafal Jaworowski lbc_get_devinfo(device_t bus, device_t child) 67308077f58SMarcel Moolenaar { 674d1d3233eSRafal Jaworowski struct lbc_devinfo *di; 67508077f58SMarcel Moolenaar 676d1d3233eSRafal Jaworowski di = device_get_ivars(child); 677d1d3233eSRafal Jaworowski return (&di->di_ofw); 67808077f58SMarcel Moolenaar } 679