1235611Sgber/*- 2235611Sgber * Copyright (c) 2012 Semihalf. 3235611Sgber * All rights reserved. 4235611Sgber * 5235611Sgber * Redistribution and use in source and binary forms, with or without 6235611Sgber * modification, are permitted provided that the following conditions 7235611Sgber * are met: 8235611Sgber * 1. Redistributions of source code must retain the above copyright 9235611Sgber * notice, this list of conditions and the following disclaimer. 10235611Sgber * 2. Redistributions in binary form must reproduce the above copyright 11235611Sgber * notice, this list of conditions and the following disclaimer in the 12235611Sgber * documentation and/or other materials provided with the distribution. 13235611Sgber * 14235611Sgber * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15235611Sgber * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16235611Sgber * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17235611Sgber * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 18235611Sgber * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19235611Sgber * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20235611Sgber * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21235611Sgber * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22235611Sgber * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23235611Sgber * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24235611Sgber * SUCH DAMAGE. 25235611Sgber */ 26235611Sgber 27235611Sgber#include <sys/cdefs.h> 28235611Sgber__FBSDID("$FreeBSD$"); 29235611Sgber 30235611Sgber#include "opt_platform.h" 31235611Sgber#include <sys/param.h> 32235611Sgber#include <sys/systm.h> 33235611Sgber#include <sys/ktr.h> 34235611Sgber#include <sys/kernel.h> 35235611Sgber#include <sys/module.h> 36235611Sgber#include <sys/bus.h> 37235611Sgber#include <sys/rman.h> 38235611Sgber#include <sys/malloc.h> 39298627Sbr#include <sys/devmap.h> 40235611Sgber 41257660Sian#include <vm/vm.h> 42257660Sian 43235611Sgber#include <machine/fdt.h> 44235611Sgber 45235611Sgber#include <dev/ofw/ofw_bus.h> 46235611Sgber#include <dev/ofw/ofw_bus_subr.h> 47235611Sgber#include <dev/ofw/openfirm.h> 48235611Sgber 49235611Sgber#include "dev/fdt/fdt_common.h" 50235611Sgber#include "ofw_bus_if.h" 51235611Sgber 52281087Sandrew#include <arm/mv/mvvar.h> 53235611Sgber#include <arm/mv/mvwin.h> 54235611Sgber 55235611Sgber#ifdef DEBUG 56235611Sgber#define debugf(fmt, args...) do { printf("%s(): ", __func__); \ 57235611Sgber printf(fmt,##args); } while (0) 58235611Sgber#else 59235611Sgber#define debugf(fmt, args...) 60235611Sgber#endif 61235611Sgber 62235611Sgber#define MV_LOCALBUS_MAX_BANKS 8 63235611Sgber#define MV_LOCALBUS_MAX_BANK_CELLS 4 64235611Sgber 65235611Sgberstatic MALLOC_DEFINE(M_LOCALBUS, "localbus", "localbus devices information"); 66235611Sgber 67235611Sgberstruct localbus_bank { 68235611Sgber vm_offset_t va; /* VA of the bank */ 69235611Sgber vm_paddr_t pa; /* physical address of the bank */ 70235611Sgber vm_size_t size; /* bank size */ 71235611Sgber uint8_t mapped; /* device memory has mapping */ 72235611Sgber}; 73235611Sgber 74235611Sgberstruct localbus_softc { 75235611Sgber device_t sc_dev; 76235611Sgber bus_space_handle_t sc_bsh; 77235611Sgber bus_space_tag_t sc_bst; 78235611Sgber int sc_rid; 79235611Sgber 80235611Sgber struct localbus_bank *sc_banks; 81235611Sgber}; 82235611Sgber 83235611Sgberstruct localbus_devinfo { 84235611Sgber struct ofw_bus_devinfo di_ofw; 85235611Sgber struct resource_list di_res; 86235611Sgber int di_bank; 87235611Sgber}; 88235611Sgber 89235611Sgberstruct localbus_va_entry { 90235611Sgber int8_t bank; 91235611Sgber vm_offset_t va; 92235611Sgber vm_size_t size; 93235611Sgber}; 94235611Sgber 95235611Sgber/* 96235611Sgber * Prototypes. 97235611Sgber */ 98235611Sgberstatic int localbus_probe(device_t); 99235611Sgberstatic int localbus_attach(device_t); 100235611Sgberstatic int localbus_print_child(device_t, device_t); 101235611Sgber 102235611Sgberstatic struct resource *localbus_alloc_resource(device_t, device_t, int, 103294883Sjhibbits int *, rman_res_t, rman_res_t, rman_res_t, u_int); 104235611Sgberstatic struct resource_list *localbus_get_resource_list(device_t, device_t); 105235611Sgber 106235611Sgberstatic ofw_bus_get_devinfo_t localbus_get_devinfo; 107235611Sgber 108235611Sgber/* 109235611Sgber * Bus interface definition. 110235611Sgber */ 111235611Sgberstatic device_method_t localbus_methods[] = { 112235611Sgber /* Device interface */ 113235611Sgber DEVMETHOD(device_probe, localbus_probe), 114235611Sgber DEVMETHOD(device_attach, localbus_attach), 115235611Sgber DEVMETHOD(device_detach, bus_generic_detach), 116235611Sgber DEVMETHOD(device_shutdown, bus_generic_shutdown), 117235611Sgber DEVMETHOD(device_suspend, bus_generic_suspend), 118235611Sgber DEVMETHOD(device_resume, bus_generic_resume), 119235611Sgber 120235611Sgber /* Bus interface */ 121235611Sgber DEVMETHOD(bus_print_child, localbus_print_child), 122235611Sgber DEVMETHOD(bus_alloc_resource, localbus_alloc_resource), 123235611Sgber DEVMETHOD(bus_release_resource, bus_generic_release_resource), 124235611Sgber DEVMETHOD(bus_activate_resource, bus_generic_activate_resource), 125235611Sgber DEVMETHOD(bus_deactivate_resource, bus_generic_deactivate_resource), 126235611Sgber DEVMETHOD(bus_setup_intr, bus_generic_setup_intr), 127235611Sgber DEVMETHOD(bus_teardown_intr, bus_generic_teardown_intr), 128235611Sgber DEVMETHOD(bus_get_resource_list, localbus_get_resource_list), 129235611Sgber 130235611Sgber /* OFW bus interface */ 131235611Sgber DEVMETHOD(ofw_bus_get_devinfo, localbus_get_devinfo), 132235611Sgber DEVMETHOD(ofw_bus_get_compat, ofw_bus_gen_get_compat), 133235611Sgber DEVMETHOD(ofw_bus_get_model, ofw_bus_gen_get_model), 134235611Sgber DEVMETHOD(ofw_bus_get_name, ofw_bus_gen_get_name), 135235611Sgber DEVMETHOD(ofw_bus_get_node, ofw_bus_gen_get_node), 136235611Sgber DEVMETHOD(ofw_bus_get_type, ofw_bus_gen_get_type), 137235611Sgber 138235611Sgber { 0, 0 } 139235611Sgber}; 140235611Sgber 141235611Sgberstatic driver_t localbus_driver = { 142235611Sgber "localbus", 143235611Sgber localbus_methods, 144235611Sgber sizeof(struct localbus_softc) 145235611Sgber}; 146235611Sgber 147235611Sgberconst struct localbus_va_entry localbus_virtmap[] = { 148235611Sgber { 0, MV_DEV_BOOT_BASE, MV_DEV_BOOT_SIZE }, 149235611Sgber { 1, MV_DEV_CS0_BASE, MV_DEV_CS0_SIZE }, 150235611Sgber { 2, MV_DEV_CS1_BASE, MV_DEV_CS1_SIZE }, 151235611Sgber { 3, MV_DEV_CS2_BASE, MV_DEV_CS2_SIZE }, 152235611Sgber 153235611Sgber { -1, 0, 0 } 154235611Sgber}; 155235611Sgber 156235611Sgberstatic struct localbus_bank localbus_banks[MV_LOCALBUS_MAX_BANKS]; 157235611Sgber 158235611Sgberdevclass_t localbus_devclass; 159235611Sgber 160261513SnwhitehornDRIVER_MODULE(localbus, ofwbus, localbus_driver, localbus_devclass, 0, 0); 161235611Sgber 162235611Sgberstatic int 163235611Sgberfdt_localbus_reg_decode(phandle_t node, struct localbus_softc *sc, 164235611Sgber struct localbus_devinfo *di) 165235611Sgber{ 166235611Sgber u_long start, end, count; 167235611Sgber pcell_t *reg, *regptr; 168235611Sgber pcell_t addr_cells, size_cells; 169235611Sgber int tuple_size, tuples; 170235611Sgber int i, rv, bank; 171235611Sgber 172235611Sgber if (fdt_addrsize_cells(OF_parent(node), &addr_cells, &size_cells) != 0) 173235611Sgber return (ENXIO); 174235611Sgber 175235611Sgber tuple_size = sizeof(pcell_t) * (addr_cells + size_cells); 176235611Sgber tuples = OF_getprop_alloc(node, "reg", tuple_size, (void **)®); 177235611Sgber debugf("addr_cells = %d, size_cells = %d\n", addr_cells, size_cells); 178235611Sgber debugf("tuples = %d, tuple size = %d\n", tuples, tuple_size); 179235611Sgber if (tuples <= 0) 180235611Sgber /* No 'reg' property in this node. */ 181235611Sgber return (0); 182235611Sgber 183235611Sgber regptr = reg; 184235611Sgber for (i = 0; i < tuples; i++) { 185235611Sgber 186235611Sgber bank = fdt_data_get((void *)regptr, 1); 187235611Sgber 188235611Sgber if (bank >= MV_LOCALBUS_MAX_BANKS) { 189235611Sgber device_printf(sc->sc_dev, "bank number [%d] out of " 190235611Sgber "range\n", bank); 191235611Sgber continue; 192235611Sgber } 193235611Sgber 194235611Sgber /* 195235611Sgber * If device doesn't have virtual to physical mapping don't add 196235611Sgber * resources 197235611Sgber */ 198235611Sgber if (!(sc->sc_banks[bank].mapped)) { 199235611Sgber device_printf(sc->sc_dev, "device [%d]: missing memory " 200235611Sgber "mapping\n", bank); 201235611Sgber continue; 202235611Sgber } 203235611Sgber 204235611Sgber di->di_bank = bank; 205235611Sgber regptr += 1; 206235611Sgber 207235611Sgber /* Get address/size. */ 208235611Sgber rv = fdt_data_to_res(regptr, addr_cells - 1, size_cells, &start, 209235611Sgber &count); 210235611Sgber if (rv != 0) { 211235611Sgber resource_list_free(&di->di_res); 212235611Sgber goto out; 213235611Sgber } 214235611Sgber 215235611Sgber /* Check if enough amount of memory is mapped */ 216235611Sgber if (sc->sc_banks[bank].size < count) { 217235611Sgber device_printf(sc->sc_dev, "device [%d]: not enough " 218235611Sgber "memory reserved\n", bank); 219235611Sgber continue; 220235611Sgber } 221235611Sgber 222235611Sgber regptr += addr_cells - 1 + size_cells; 223235611Sgber 224235611Sgber /* Calculate address range relative to VA base. */ 225235611Sgber start = sc->sc_banks[bank].va + start; 226235611Sgber end = start + count - 1; 227235611Sgber 228235611Sgber debugf("reg addr bank = %d, start = %lx, end = %lx, " 229235611Sgber "count = %lx\n", bank, start, end, count); 230235611Sgber 231235611Sgber /* Use bank (CS) cell as rid. */ 232235611Sgber resource_list_add(&di->di_res, SYS_RES_MEMORY, di->di_bank, 233235611Sgber start, end, count); 234235611Sgber } 235235611Sgber rv = 0; 236235611Sgberout: 237299702Sgonzo OF_prop_free(reg); 238235611Sgber return (rv); 239235611Sgber} 240235611Sgber 241235611Sgberstatic int 242235611Sgberlocalbus_probe(device_t dev) 243235611Sgber{ 244235611Sgber 245235611Sgber if (!ofw_bus_is_compatible_strict(dev, "mrvl,lbc")) 246235611Sgber return (ENXIO); 247235611Sgber 248235611Sgber device_set_desc(dev, "Marvell device bus"); 249235611Sgber 250235611Sgber return (BUS_PROBE_DEFAULT); 251235611Sgber} 252235611Sgber 253235611Sgberstatic int 254235611Sgberlocalbus_attach(device_t dev) 255235611Sgber{ 256235611Sgber device_t dev_child; 257235611Sgber struct localbus_softc *sc; 258235611Sgber struct localbus_devinfo *di; 259235611Sgber phandle_t dt_node, dt_child; 260235611Sgber 261235611Sgber sc = device_get_softc(dev); 262235611Sgber sc->sc_dev = dev; 263235611Sgber sc->sc_banks = localbus_banks; 264235611Sgber 265235611Sgber /* 266235611Sgber * Walk localbus and add direct subordinates as our children. 267235611Sgber */ 268235611Sgber dt_node = ofw_bus_get_node(dev); 269235611Sgber for (dt_child = OF_child(dt_node); dt_child != 0; 270235611Sgber dt_child = OF_peer(dt_child)) { 271235611Sgber 272235611Sgber /* Check and process 'status' property. */ 273235611Sgber if (!(fdt_is_enabled(dt_child))) 274235611Sgber continue; 275235611Sgber 276235611Sgber if (!(fdt_pm_is_enabled(dt_child))) 277235611Sgber continue; 278235611Sgber 279235611Sgber di = malloc(sizeof(*di), M_LOCALBUS, M_WAITOK | M_ZERO); 280235611Sgber if (ofw_bus_gen_setup_devinfo(&di->di_ofw, dt_child) != 0) { 281235611Sgber free(di, M_LOCALBUS); 282235611Sgber device_printf(dev, "could not set up devinfo\n"); 283235611Sgber continue; 284235611Sgber } 285235611Sgber 286235611Sgber resource_list_init(&di->di_res); 287235611Sgber if (fdt_localbus_reg_decode(dt_child, sc, di)) { 288235611Sgber device_printf(dev, "could not process 'reg' " 289235611Sgber "property\n"); 290235611Sgber ofw_bus_gen_destroy_devinfo(&di->di_ofw); 291235611Sgber free(di, M_LOCALBUS); 292235611Sgber continue; 293235611Sgber } 294235611Sgber 295235611Sgber /* Add newbus device for this FDT node */ 296235611Sgber dev_child = device_add_child(dev, NULL, -1); 297235611Sgber if (dev_child == NULL) { 298235611Sgber device_printf(dev, "could not add child: %s\n", 299235611Sgber di->di_ofw.obd_name); 300235611Sgber resource_list_free(&di->di_res); 301235611Sgber ofw_bus_gen_destroy_devinfo(&di->di_ofw); 302235611Sgber free(di, M_LOCALBUS); 303235611Sgber continue; 304235611Sgber } 305235611Sgber#ifdef DEBUG 306235611Sgber device_printf(dev, "added child: %s\n\n", di->di_ofw.obd_name); 307235611Sgber#endif 308235611Sgber device_set_ivars(dev_child, di); 309235611Sgber } 310235611Sgber 311235611Sgber return (bus_generic_attach(dev)); 312235611Sgber} 313235611Sgber 314235611Sgberstatic int 315235611Sgberlocalbus_print_child(device_t dev, device_t child) 316235611Sgber{ 317235611Sgber struct localbus_devinfo *di; 318235611Sgber struct resource_list *rl; 319235611Sgber int rv; 320235611Sgber 321235611Sgber di = device_get_ivars(child); 322235611Sgber rl = &di->di_res; 323235611Sgber 324235611Sgber rv = 0; 325235611Sgber rv += bus_print_child_header(dev, child); 326297199Sjhibbits rv += resource_list_print_type(rl, "mem", SYS_RES_MEMORY, "%#jx"); 327297199Sjhibbits rv += resource_list_print_type(rl, "irq", SYS_RES_IRQ, "%jd"); 328235611Sgber rv += bus_print_child_footer(dev, child); 329235611Sgber 330235611Sgber return (rv); 331235611Sgber} 332235611Sgber 333235611Sgberstatic struct resource * 334235611Sgberlocalbus_alloc_resource(device_t bus, device_t child, int type, int *rid, 335294883Sjhibbits rman_res_t start, rman_res_t end, rman_res_t count, u_int flags) 336235611Sgber{ 337235611Sgber struct localbus_devinfo *di; 338235611Sgber struct resource_list_entry *rle; 339235611Sgber 340235611Sgber /* 341235611Sgber * Request for the default allocation with a given rid: use resource 342235611Sgber * list stored in the local device info. 343235611Sgber */ 344295832Sjhibbits if (RMAN_IS_DEFAULT_RANGE(start, end)) { 345235611Sgber if ((di = device_get_ivars(child)) == NULL) 346235611Sgber return (NULL); 347235611Sgber 348235611Sgber if (type == SYS_RES_IOPORT) 349235611Sgber type = SYS_RES_MEMORY; 350235611Sgber 351235611Sgber rid = &di->di_bank; 352235611Sgber rle = resource_list_find(&di->di_res, type, *rid); 353235611Sgber if (rle == NULL) { 354235611Sgber device_printf(bus, "no default resources for " 355235611Sgber "rid = %d, type = %d\n", *rid, type); 356235611Sgber return (NULL); 357235611Sgber } 358235611Sgber start = rle->start; 359235611Sgber end = rle->end; 360235611Sgber count = rle->count; 361235611Sgber } 362235611Sgber 363235611Sgber return (bus_generic_alloc_resource(bus, child, type, rid, start, end, 364235611Sgber count, flags)); 365235611Sgber} 366235611Sgber 367235611Sgber 368235611Sgberstatic struct resource_list * 369235611Sgberlocalbus_get_resource_list(device_t bus, device_t child) 370235611Sgber{ 371235611Sgber struct localbus_devinfo *di; 372235611Sgber 373235611Sgber di = device_get_ivars(child); 374235611Sgber return (&di->di_res); 375235611Sgber} 376235611Sgber 377235611Sgberstatic const struct ofw_bus_devinfo * 378235611Sgberlocalbus_get_devinfo(device_t bus, device_t child) 379235611Sgber{ 380235611Sgber struct localbus_devinfo *di; 381235611Sgber 382235611Sgber di = device_get_ivars(child); 383235611Sgber return (&di->di_ofw); 384235611Sgber} 385235611Sgber 386235611Sgberint 387298627Sbrfdt_localbus_devmap(phandle_t dt_node, struct devmap_entry *fdt_devmap, 388235611Sgber int banks_max_num, int *banks_added) 389235611Sgber{ 390235611Sgber pcell_t ranges[MV_LOCALBUS_MAX_BANKS * MV_LOCALBUS_MAX_BANK_CELLS]; 391235611Sgber pcell_t *rangesptr; 392235611Sgber uint32_t tuple_size, bank; 393235611Sgber vm_paddr_t offset; 394235611Sgber vm_size_t size; 395235611Sgber int dev_num, addr_cells, size_cells, par_addr_cells, va_index, i, j, k; 396235611Sgber 397235611Sgber if ((fdt_addrsize_cells(dt_node, &addr_cells, &size_cells)) != 0) 398235611Sgber return (EINVAL); 399235611Sgber 400235611Sgber par_addr_cells = fdt_parent_addr_cells(dt_node); 401235611Sgber if (par_addr_cells > 2) { 402235611Sgber /* 403235611Sgber * Localbus devmap initialization error: unsupported parent 404235611Sgber * #addr-cells 405235611Sgber */ 406235611Sgber return (ERANGE); 407235611Sgber } 408235611Sgber 409235611Sgber tuple_size = (addr_cells + par_addr_cells + size_cells); 410235611Sgber if (tuple_size > MV_LOCALBUS_MAX_BANK_CELLS) 411235611Sgber return (ERANGE); 412235611Sgber 413235611Sgber tuple_size *= sizeof(pcell_t); 414235611Sgber 415235611Sgber dev_num = OF_getprop(dt_node, "ranges", ranges, sizeof(ranges)); 416235611Sgber if (dev_num <= 0) 417235611Sgber return (EINVAL); 418235611Sgber 419235611Sgber /* Calculate number of devices attached to bus */ 420235611Sgber dev_num = dev_num / tuple_size; 421235611Sgber 422235611Sgber /* 423235611Sgber * If number of ranges > max number of localbus devices, 424235611Sgber * additional entries will not be processed 425235611Sgber */ 426235611Sgber dev_num = MIN(dev_num, banks_max_num); 427235611Sgber 428235611Sgber rangesptr = &ranges[0]; 429235611Sgber j = 0; 430235611Sgber 431235611Sgber /* Process data from FDT */ 432235611Sgber for (i = 0; i < dev_num; i++) { 433235611Sgber 434235611Sgber /* First field is bank number */ 435235611Sgber bank = fdt_data_get((void *)rangesptr, 1); 436235611Sgber rangesptr += 1; 437235611Sgber 438256511Skevlo if (bank > MV_LOCALBUS_MAX_BANKS) { 439235611Sgber /* Bank out of range */ 440235611Sgber rangesptr += ((addr_cells - 1) + par_addr_cells + 441235611Sgber size_cells); 442235611Sgber continue; 443235611Sgber } 444235611Sgber 445235611Sgber /* Find virtmap entry for this bank */ 446235611Sgber va_index = -1; 447235611Sgber for (k = 0; localbus_virtmap[k].bank >= 0; k++) { 448235611Sgber if (localbus_virtmap[k].bank == bank) { 449235611Sgber va_index = k; 450235611Sgber break; 451235611Sgber } 452235611Sgber } 453235611Sgber 454235611Sgber /* Check if virtmap entry was found */ 455235611Sgber if (va_index == -1) { 456235611Sgber rangesptr += ((addr_cells - 1) + par_addr_cells + 457235611Sgber size_cells); 458235611Sgber continue; 459235611Sgber } 460235611Sgber 461235611Sgber /* Remaining child's address fields are unused */ 462235611Sgber rangesptr += (addr_cells - 1); 463235611Sgber 464235611Sgber /* Parent address offset */ 465235611Sgber offset = fdt_data_get((void *)rangesptr, par_addr_cells); 466235611Sgber rangesptr += par_addr_cells; 467235611Sgber 468235611Sgber /* Last field is size */ 469235611Sgber size = fdt_data_get((void *)rangesptr, size_cells); 470235611Sgber rangesptr += size_cells; 471235611Sgber 472235611Sgber if (size > localbus_virtmap[va_index].size) { 473235611Sgber /* Not enough space reserved in virtual memory map */ 474235611Sgber continue; 475235611Sgber } 476235611Sgber 477235611Sgber fdt_devmap[j].pd_va = localbus_virtmap[va_index].va; 478235611Sgber fdt_devmap[j].pd_pa = offset; 479235611Sgber fdt_devmap[j].pd_size = size; 480235611Sgber 481235611Sgber /* Copy data to structure used by localbus driver */ 482235611Sgber localbus_banks[bank].va = fdt_devmap[j].pd_va; 483235611Sgber localbus_banks[bank].pa = fdt_devmap[j].pd_pa; 484235611Sgber localbus_banks[bank].size = fdt_devmap[j].pd_size; 485235611Sgber localbus_banks[bank].mapped = 1; 486235611Sgber 487235611Sgber j++; 488235611Sgber } 489235611Sgber 490235611Sgber *banks_added = j; 491235611Sgber return (0); 492235611Sgber} 493