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> 39235611Sgber 40259364Sian#include <vm/vm.h> 41259364Sian 42259364Sian#include <machine/devmap.h> 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 52235611Sgber#include <arm/mv/mvwin.h> 53235611Sgber 54235611Sgber#ifdef DEBUG 55235611Sgber#define debugf(fmt, args...) do { printf("%s(): ", __func__); \ 56235611Sgber printf(fmt,##args); } while (0) 57235611Sgber#else 58235611Sgber#define debugf(fmt, args...) 59235611Sgber#endif 60235611Sgber 61235611Sgber#define MV_LOCALBUS_MAX_BANKS 8 62235611Sgber#define MV_LOCALBUS_MAX_BANK_CELLS 4 63235611Sgber 64235611Sgberstatic MALLOC_DEFINE(M_LOCALBUS, "localbus", "localbus devices information"); 65235611Sgber 66235611Sgberstruct localbus_bank { 67235611Sgber vm_offset_t va; /* VA of the bank */ 68235611Sgber vm_paddr_t pa; /* physical address of the bank */ 69235611Sgber vm_size_t size; /* bank size */ 70235611Sgber uint8_t mapped; /* device memory has mapping */ 71235611Sgber}; 72235611Sgber 73235611Sgberstruct localbus_softc { 74235611Sgber device_t sc_dev; 75235611Sgber bus_space_handle_t sc_bsh; 76235611Sgber bus_space_tag_t sc_bst; 77235611Sgber int sc_rid; 78235611Sgber 79235611Sgber struct localbus_bank *sc_banks; 80235611Sgber}; 81235611Sgber 82235611Sgberstruct localbus_devinfo { 83235611Sgber struct ofw_bus_devinfo di_ofw; 84235611Sgber struct resource_list di_res; 85235611Sgber int di_bank; 86235611Sgber}; 87235611Sgber 88235611Sgberstruct localbus_va_entry { 89235611Sgber int8_t bank; 90235611Sgber vm_offset_t va; 91235611Sgber vm_size_t size; 92235611Sgber}; 93235611Sgber 94235611Sgber/* 95235611Sgber * Prototypes. 96235611Sgber */ 97235611Sgberstatic int localbus_probe(device_t); 98235611Sgberstatic int localbus_attach(device_t); 99235611Sgberstatic int localbus_print_child(device_t, device_t); 100235611Sgber 101235611Sgberstatic struct resource *localbus_alloc_resource(device_t, device_t, int, 102235611Sgber int *, u_long, u_long, u_long, u_int); 103235611Sgberstatic struct resource_list *localbus_get_resource_list(device_t, device_t); 104235611Sgber 105235611Sgberstatic ofw_bus_get_devinfo_t localbus_get_devinfo; 106235611Sgber 107235611Sgber/* 108235611Sgber * Bus interface definition. 109235611Sgber */ 110235611Sgberstatic device_method_t localbus_methods[] = { 111235611Sgber /* Device interface */ 112235611Sgber DEVMETHOD(device_probe, localbus_probe), 113235611Sgber DEVMETHOD(device_attach, localbus_attach), 114235611Sgber DEVMETHOD(device_detach, bus_generic_detach), 115235611Sgber DEVMETHOD(device_shutdown, bus_generic_shutdown), 116235611Sgber DEVMETHOD(device_suspend, bus_generic_suspend), 117235611Sgber DEVMETHOD(device_resume, bus_generic_resume), 118235611Sgber 119235611Sgber /* Bus interface */ 120235611Sgber DEVMETHOD(bus_print_child, localbus_print_child), 121235611Sgber DEVMETHOD(bus_alloc_resource, localbus_alloc_resource), 122235611Sgber DEVMETHOD(bus_release_resource, bus_generic_release_resource), 123235611Sgber DEVMETHOD(bus_activate_resource, bus_generic_activate_resource), 124235611Sgber DEVMETHOD(bus_deactivate_resource, bus_generic_deactivate_resource), 125235611Sgber DEVMETHOD(bus_setup_intr, bus_generic_setup_intr), 126235611Sgber DEVMETHOD(bus_teardown_intr, bus_generic_teardown_intr), 127235611Sgber DEVMETHOD(bus_get_resource_list, localbus_get_resource_list), 128235611Sgber 129235611Sgber /* OFW bus interface */ 130235611Sgber DEVMETHOD(ofw_bus_get_devinfo, localbus_get_devinfo), 131235611Sgber DEVMETHOD(ofw_bus_get_compat, ofw_bus_gen_get_compat), 132235611Sgber DEVMETHOD(ofw_bus_get_model, ofw_bus_gen_get_model), 133235611Sgber DEVMETHOD(ofw_bus_get_name, ofw_bus_gen_get_name), 134235611Sgber DEVMETHOD(ofw_bus_get_node, ofw_bus_gen_get_node), 135235611Sgber DEVMETHOD(ofw_bus_get_type, ofw_bus_gen_get_type), 136235611Sgber 137235611Sgber { 0, 0 } 138235611Sgber}; 139235611Sgber 140235611Sgberstatic driver_t localbus_driver = { 141235611Sgber "localbus", 142235611Sgber localbus_methods, 143235611Sgber sizeof(struct localbus_softc) 144235611Sgber}; 145235611Sgber 146235611Sgberconst struct localbus_va_entry localbus_virtmap[] = { 147235611Sgber { 0, MV_DEV_BOOT_BASE, MV_DEV_BOOT_SIZE }, 148235611Sgber { 1, MV_DEV_CS0_BASE, MV_DEV_CS0_SIZE }, 149235611Sgber { 2, MV_DEV_CS1_BASE, MV_DEV_CS1_SIZE }, 150235611Sgber { 3, MV_DEV_CS2_BASE, MV_DEV_CS2_SIZE }, 151235611Sgber 152235611Sgber { -1, 0, 0 } 153235611Sgber}; 154235611Sgber 155235611Sgberstatic struct localbus_bank localbus_banks[MV_LOCALBUS_MAX_BANKS]; 156235611Sgber 157235611Sgberdevclass_t localbus_devclass; 158235611Sgber 159266160SianDRIVER_MODULE(localbus, ofwbus, localbus_driver, localbus_devclass, 0, 0); 160235611Sgber 161235611Sgberstatic int 162235611Sgberfdt_localbus_reg_decode(phandle_t node, struct localbus_softc *sc, 163235611Sgber struct localbus_devinfo *di) 164235611Sgber{ 165235611Sgber u_long start, end, count; 166235611Sgber pcell_t *reg, *regptr; 167235611Sgber pcell_t addr_cells, size_cells; 168235611Sgber int tuple_size, tuples; 169235611Sgber int i, rv, bank; 170235611Sgber 171235611Sgber if (fdt_addrsize_cells(OF_parent(node), &addr_cells, &size_cells) != 0) 172235611Sgber return (ENXIO); 173235611Sgber 174235611Sgber tuple_size = sizeof(pcell_t) * (addr_cells + size_cells); 175235611Sgber tuples = OF_getprop_alloc(node, "reg", tuple_size, (void **)®); 176235611Sgber debugf("addr_cells = %d, size_cells = %d\n", addr_cells, size_cells); 177235611Sgber debugf("tuples = %d, tuple size = %d\n", tuples, tuple_size); 178235611Sgber if (tuples <= 0) 179235611Sgber /* No 'reg' property in this node. */ 180235611Sgber return (0); 181235611Sgber 182235611Sgber regptr = reg; 183235611Sgber for (i = 0; i < tuples; i++) { 184235611Sgber 185235611Sgber bank = fdt_data_get((void *)regptr, 1); 186235611Sgber 187235611Sgber if (bank >= MV_LOCALBUS_MAX_BANKS) { 188235611Sgber device_printf(sc->sc_dev, "bank number [%d] out of " 189235611Sgber "range\n", bank); 190235611Sgber continue; 191235611Sgber } 192235611Sgber 193235611Sgber /* 194235611Sgber * If device doesn't have virtual to physical mapping don't add 195235611Sgber * resources 196235611Sgber */ 197235611Sgber if (!(sc->sc_banks[bank].mapped)) { 198235611Sgber device_printf(sc->sc_dev, "device [%d]: missing memory " 199235611Sgber "mapping\n", bank); 200235611Sgber continue; 201235611Sgber } 202235611Sgber 203235611Sgber di->di_bank = bank; 204235611Sgber regptr += 1; 205235611Sgber 206235611Sgber /* Get address/size. */ 207235611Sgber rv = fdt_data_to_res(regptr, addr_cells - 1, size_cells, &start, 208235611Sgber &count); 209235611Sgber if (rv != 0) { 210235611Sgber resource_list_free(&di->di_res); 211235611Sgber goto out; 212235611Sgber } 213235611Sgber 214235611Sgber /* Check if enough amount of memory is mapped */ 215235611Sgber if (sc->sc_banks[bank].size < count) { 216235611Sgber device_printf(sc->sc_dev, "device [%d]: not enough " 217235611Sgber "memory reserved\n", bank); 218235611Sgber continue; 219235611Sgber } 220235611Sgber 221235611Sgber regptr += addr_cells - 1 + size_cells; 222235611Sgber 223235611Sgber /* Calculate address range relative to VA base. */ 224235611Sgber start = sc->sc_banks[bank].va + start; 225235611Sgber end = start + count - 1; 226235611Sgber 227235611Sgber debugf("reg addr bank = %d, start = %lx, end = %lx, " 228235611Sgber "count = %lx\n", bank, start, end, count); 229235611Sgber 230235611Sgber /* Use bank (CS) cell as rid. */ 231235611Sgber resource_list_add(&di->di_res, SYS_RES_MEMORY, di->di_bank, 232235611Sgber start, end, count); 233235611Sgber } 234235611Sgber rv = 0; 235235611Sgberout: 236235611Sgber free(reg, M_OFWPROP); 237235611Sgber return (rv); 238235611Sgber} 239235611Sgber 240235611Sgberstatic int 241235611Sgberlocalbus_probe(device_t dev) 242235611Sgber{ 243235611Sgber 244235611Sgber if (!ofw_bus_is_compatible_strict(dev, "mrvl,lbc")) 245235611Sgber return (ENXIO); 246235611Sgber 247235611Sgber device_set_desc(dev, "Marvell device bus"); 248235611Sgber 249235611Sgber return (BUS_PROBE_DEFAULT); 250235611Sgber} 251235611Sgber 252235611Sgberstatic int 253235611Sgberlocalbus_attach(device_t dev) 254235611Sgber{ 255235611Sgber device_t dev_child; 256235611Sgber struct localbus_softc *sc; 257235611Sgber struct localbus_devinfo *di; 258235611Sgber phandle_t dt_node, dt_child; 259235611Sgber 260235611Sgber sc = device_get_softc(dev); 261235611Sgber sc->sc_dev = dev; 262235611Sgber sc->sc_banks = localbus_banks; 263235611Sgber 264235611Sgber /* 265235611Sgber * Walk localbus and add direct subordinates as our children. 266235611Sgber */ 267235611Sgber dt_node = ofw_bus_get_node(dev); 268235611Sgber for (dt_child = OF_child(dt_node); dt_child != 0; 269235611Sgber dt_child = OF_peer(dt_child)) { 270235611Sgber 271235611Sgber /* Check and process 'status' property. */ 272235611Sgber if (!(fdt_is_enabled(dt_child))) 273235611Sgber continue; 274235611Sgber 275235611Sgber if (!(fdt_pm_is_enabled(dt_child))) 276235611Sgber continue; 277235611Sgber 278235611Sgber di = malloc(sizeof(*di), M_LOCALBUS, M_WAITOK | M_ZERO); 279235611Sgber if (ofw_bus_gen_setup_devinfo(&di->di_ofw, dt_child) != 0) { 280235611Sgber free(di, M_LOCALBUS); 281235611Sgber device_printf(dev, "could not set up devinfo\n"); 282235611Sgber continue; 283235611Sgber } 284235611Sgber 285235611Sgber resource_list_init(&di->di_res); 286235611Sgber if (fdt_localbus_reg_decode(dt_child, sc, di)) { 287235611Sgber device_printf(dev, "could not process 'reg' " 288235611Sgber "property\n"); 289235611Sgber ofw_bus_gen_destroy_devinfo(&di->di_ofw); 290235611Sgber free(di, M_LOCALBUS); 291235611Sgber continue; 292235611Sgber } 293235611Sgber 294235611Sgber /* Add newbus device for this FDT node */ 295235611Sgber dev_child = device_add_child(dev, NULL, -1); 296235611Sgber if (dev_child == NULL) { 297235611Sgber device_printf(dev, "could not add child: %s\n", 298235611Sgber di->di_ofw.obd_name); 299235611Sgber resource_list_free(&di->di_res); 300235611Sgber ofw_bus_gen_destroy_devinfo(&di->di_ofw); 301235611Sgber free(di, M_LOCALBUS); 302235611Sgber continue; 303235611Sgber } 304235611Sgber#ifdef DEBUG 305235611Sgber device_printf(dev, "added child: %s\n\n", di->di_ofw.obd_name); 306235611Sgber#endif 307235611Sgber device_set_ivars(dev_child, di); 308235611Sgber } 309235611Sgber 310235611Sgber return (bus_generic_attach(dev)); 311235611Sgber} 312235611Sgber 313235611Sgberstatic int 314235611Sgberlocalbus_print_child(device_t dev, device_t child) 315235611Sgber{ 316235611Sgber struct localbus_devinfo *di; 317235611Sgber struct resource_list *rl; 318235611Sgber int rv; 319235611Sgber 320235611Sgber di = device_get_ivars(child); 321235611Sgber rl = &di->di_res; 322235611Sgber 323235611Sgber rv = 0; 324235611Sgber rv += bus_print_child_header(dev, child); 325235611Sgber rv += resource_list_print_type(rl, "mem", SYS_RES_MEMORY, "%#lx"); 326235611Sgber rv += resource_list_print_type(rl, "irq", SYS_RES_IRQ, "%ld"); 327235611Sgber rv += bus_print_child_footer(dev, child); 328235611Sgber 329235611Sgber return (rv); 330235611Sgber} 331235611Sgber 332235611Sgberstatic struct resource * 333235611Sgberlocalbus_alloc_resource(device_t bus, device_t child, int type, int *rid, 334235611Sgber u_long start, u_long end, u_long count, u_int flags) 335235611Sgber{ 336235611Sgber struct localbus_devinfo *di; 337235611Sgber struct resource_list_entry *rle; 338235611Sgber 339235611Sgber /* 340235611Sgber * Request for the default allocation with a given rid: use resource 341235611Sgber * list stored in the local device info. 342235611Sgber */ 343235611Sgber if ((start == 0UL) && (end == ~0UL)) { 344235611Sgber if ((di = device_get_ivars(child)) == NULL) 345235611Sgber return (NULL); 346235611Sgber 347235611Sgber if (type == SYS_RES_IOPORT) 348235611Sgber type = SYS_RES_MEMORY; 349235611Sgber 350235611Sgber rid = &di->di_bank; 351235611Sgber rle = resource_list_find(&di->di_res, type, *rid); 352235611Sgber if (rle == NULL) { 353235611Sgber device_printf(bus, "no default resources for " 354235611Sgber "rid = %d, type = %d\n", *rid, type); 355235611Sgber return (NULL); 356235611Sgber } 357235611Sgber start = rle->start; 358235611Sgber end = rle->end; 359235611Sgber count = rle->count; 360235611Sgber } 361235611Sgber 362235611Sgber return (bus_generic_alloc_resource(bus, child, type, rid, start, end, 363235611Sgber count, flags)); 364235611Sgber} 365235611Sgber 366235611Sgber 367235611Sgberstatic struct resource_list * 368235611Sgberlocalbus_get_resource_list(device_t bus, device_t child) 369235611Sgber{ 370235611Sgber struct localbus_devinfo *di; 371235611Sgber 372235611Sgber di = device_get_ivars(child); 373235611Sgber return (&di->di_res); 374235611Sgber} 375235611Sgber 376235611Sgberstatic const struct ofw_bus_devinfo * 377235611Sgberlocalbus_get_devinfo(device_t bus, device_t child) 378235611Sgber{ 379235611Sgber struct localbus_devinfo *di; 380235611Sgber 381235611Sgber di = device_get_ivars(child); 382235611Sgber return (&di->di_ofw); 383235611Sgber} 384235611Sgber 385235611Sgberint 386259364Sianfdt_localbus_devmap(phandle_t dt_node, struct arm_devmap_entry *fdt_devmap, 387235611Sgber int banks_max_num, int *banks_added) 388235611Sgber{ 389235611Sgber pcell_t ranges[MV_LOCALBUS_MAX_BANKS * MV_LOCALBUS_MAX_BANK_CELLS]; 390235611Sgber pcell_t *rangesptr; 391235611Sgber uint32_t tuple_size, bank; 392235611Sgber vm_paddr_t offset; 393235611Sgber vm_size_t size; 394235611Sgber int dev_num, addr_cells, size_cells, par_addr_cells, va_index, i, j, k; 395235611Sgber 396235611Sgber if ((fdt_addrsize_cells(dt_node, &addr_cells, &size_cells)) != 0) 397235611Sgber return (EINVAL); 398235611Sgber 399235611Sgber par_addr_cells = fdt_parent_addr_cells(dt_node); 400235611Sgber if (par_addr_cells > 2) { 401235611Sgber /* 402235611Sgber * Localbus devmap initialization error: unsupported parent 403235611Sgber * #addr-cells 404235611Sgber */ 405235611Sgber return (ERANGE); 406235611Sgber } 407235611Sgber 408235611Sgber tuple_size = (addr_cells + par_addr_cells + size_cells); 409235611Sgber if (tuple_size > MV_LOCALBUS_MAX_BANK_CELLS) 410235611Sgber return (ERANGE); 411235611Sgber 412235611Sgber tuple_size *= sizeof(pcell_t); 413235611Sgber 414235611Sgber dev_num = OF_getprop(dt_node, "ranges", ranges, sizeof(ranges)); 415235611Sgber if (dev_num <= 0) 416235611Sgber return (EINVAL); 417235611Sgber 418235611Sgber /* Calculate number of devices attached to bus */ 419235611Sgber dev_num = dev_num / tuple_size; 420235611Sgber 421235611Sgber /* 422235611Sgber * If number of ranges > max number of localbus devices, 423235611Sgber * additional entries will not be processed 424235611Sgber */ 425235611Sgber dev_num = MIN(dev_num, banks_max_num); 426235611Sgber 427235611Sgber rangesptr = &ranges[0]; 428235611Sgber j = 0; 429235611Sgber 430235611Sgber /* Process data from FDT */ 431235611Sgber for (i = 0; i < dev_num; i++) { 432235611Sgber 433235611Sgber /* First field is bank number */ 434235611Sgber bank = fdt_data_get((void *)rangesptr, 1); 435235611Sgber rangesptr += 1; 436235611Sgber 437235611Sgber if (bank < 0 || bank > MV_LOCALBUS_MAX_BANKS) { 438235611Sgber /* Bank out of range */ 439235611Sgber rangesptr += ((addr_cells - 1) + par_addr_cells + 440235611Sgber size_cells); 441235611Sgber continue; 442235611Sgber } 443235611Sgber 444235611Sgber /* Find virtmap entry for this bank */ 445235611Sgber va_index = -1; 446235611Sgber for (k = 0; localbus_virtmap[k].bank >= 0; k++) { 447235611Sgber if (localbus_virtmap[k].bank == bank) { 448235611Sgber va_index = k; 449235611Sgber break; 450235611Sgber } 451235611Sgber } 452235611Sgber 453235611Sgber /* Check if virtmap entry was found */ 454235611Sgber if (va_index == -1) { 455235611Sgber rangesptr += ((addr_cells - 1) + par_addr_cells + 456235611Sgber size_cells); 457235611Sgber continue; 458235611Sgber } 459235611Sgber 460235611Sgber /* Remaining child's address fields are unused */ 461235611Sgber rangesptr += (addr_cells - 1); 462235611Sgber 463235611Sgber /* Parent address offset */ 464235611Sgber offset = fdt_data_get((void *)rangesptr, par_addr_cells); 465235611Sgber rangesptr += par_addr_cells; 466235611Sgber 467235611Sgber /* Last field is size */ 468235611Sgber size = fdt_data_get((void *)rangesptr, size_cells); 469235611Sgber rangesptr += size_cells; 470235611Sgber 471235611Sgber if (size > localbus_virtmap[va_index].size) { 472235611Sgber /* Not enough space reserved in virtual memory map */ 473235611Sgber continue; 474235611Sgber } 475235611Sgber 476235611Sgber fdt_devmap[j].pd_va = localbus_virtmap[va_index].va; 477235611Sgber fdt_devmap[j].pd_pa = offset; 478235611Sgber fdt_devmap[j].pd_size = size; 479235611Sgber fdt_devmap[j].pd_prot = VM_PROT_READ | VM_PROT_WRITE; 480266386Sian fdt_devmap[j].pd_cache = PTE_DEVICE; 481235611Sgber 482235611Sgber /* Copy data to structure used by localbus driver */ 483235611Sgber localbus_banks[bank].va = fdt_devmap[j].pd_va; 484235611Sgber localbus_banks[bank].pa = fdt_devmap[j].pd_pa; 485235611Sgber localbus_banks[bank].size = fdt_devmap[j].pd_size; 486235611Sgber localbus_banks[bank].mapped = 1; 487235611Sgber 488235611Sgber j++; 489235611Sgber } 490235611Sgber 491235611Sgber *banks_added = j; 492235611Sgber return (0); 493235611Sgber} 494