1/*- 2 * SPDX-License-Identifier: BSD-2-Clause-FreeBSD 3 * 4 * Copyright (c) 2012 Semihalf. 5 * All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 17 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 19 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 20 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 21 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 22 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 23 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 24 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 25 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 26 * SUCH DAMAGE. 27 */ 28 29#include <sys/cdefs.h> 30__FBSDID("$FreeBSD$"); 31 32#include "opt_platform.h" 33#include <sys/param.h> 34#include <sys/systm.h> 35#include <sys/ktr.h> 36#include <sys/kernel.h> 37#include <sys/module.h> 38#include <sys/bus.h> 39#include <sys/rman.h> 40#include <sys/malloc.h> 41#include <sys/devmap.h> 42 43#include <vm/vm.h> 44 45#include <machine/fdt.h> 46 47#include <dev/ofw/ofw_bus.h> 48#include <dev/ofw/ofw_bus_subr.h> 49#include <dev/ofw/openfirm.h> 50 51#include "dev/fdt/fdt_common.h" 52#include "ofw_bus_if.h" 53 54#include <arm/mv/mvvar.h> 55#include <arm/mv/mvwin.h> 56 57#ifdef DEBUG 58#define debugf(fmt, args...) do { printf("%s(): ", __func__); \ 59 printf(fmt,##args); } while (0) 60#else 61#define debugf(fmt, args...) 62#endif 63 64#define MV_LOCALBUS_MAX_BANKS 8 65#define MV_LOCALBUS_MAX_BANK_CELLS 4 66 67static MALLOC_DEFINE(M_LOCALBUS, "localbus", "localbus devices information"); 68 69struct localbus_bank { 70 vm_offset_t va; /* VA of the bank */ 71 vm_paddr_t pa; /* physical address of the bank */ 72 vm_size_t size; /* bank size */ 73 uint8_t mapped; /* device memory has mapping */ 74}; 75 76struct localbus_softc { 77 device_t sc_dev; 78 bus_space_handle_t sc_bsh; 79 bus_space_tag_t sc_bst; 80 int sc_rid; 81 82 struct localbus_bank *sc_banks; 83}; 84 85struct localbus_devinfo { 86 struct ofw_bus_devinfo di_ofw; 87 struct resource_list di_res; 88 int di_bank; 89}; 90 91struct localbus_va_entry { 92 int8_t bank; 93 vm_offset_t va; 94 vm_size_t size; 95}; 96 97/* 98 * Prototypes. 99 */ 100static int localbus_probe(device_t); 101static int localbus_attach(device_t); 102static int localbus_print_child(device_t, device_t); 103 104static struct resource *localbus_alloc_resource(device_t, device_t, int, 105 int *, rman_res_t, rman_res_t, rman_res_t, u_int); 106static struct resource_list *localbus_get_resource_list(device_t, device_t); 107 108static ofw_bus_get_devinfo_t localbus_get_devinfo; 109 110/* 111 * Bus interface definition. 112 */ 113static device_method_t localbus_methods[] = { 114 /* Device interface */ 115 DEVMETHOD(device_probe, localbus_probe), 116 DEVMETHOD(device_attach, localbus_attach), 117 DEVMETHOD(device_detach, bus_generic_detach), 118 DEVMETHOD(device_shutdown, bus_generic_shutdown), 119 DEVMETHOD(device_suspend, bus_generic_suspend), 120 DEVMETHOD(device_resume, bus_generic_resume), 121 122 /* Bus interface */ 123 DEVMETHOD(bus_print_child, localbus_print_child), 124 DEVMETHOD(bus_alloc_resource, localbus_alloc_resource), 125 DEVMETHOD(bus_release_resource, bus_generic_release_resource), 126 DEVMETHOD(bus_activate_resource, bus_generic_activate_resource), 127 DEVMETHOD(bus_deactivate_resource, bus_generic_deactivate_resource), 128 DEVMETHOD(bus_setup_intr, bus_generic_setup_intr), 129 DEVMETHOD(bus_teardown_intr, bus_generic_teardown_intr), 130 DEVMETHOD(bus_get_resource_list, localbus_get_resource_list), 131 132 /* OFW bus interface */ 133 DEVMETHOD(ofw_bus_get_devinfo, localbus_get_devinfo), 134 DEVMETHOD(ofw_bus_get_compat, ofw_bus_gen_get_compat), 135 DEVMETHOD(ofw_bus_get_model, ofw_bus_gen_get_model), 136 DEVMETHOD(ofw_bus_get_name, ofw_bus_gen_get_name), 137 DEVMETHOD(ofw_bus_get_node, ofw_bus_gen_get_node), 138 DEVMETHOD(ofw_bus_get_type, ofw_bus_gen_get_type), 139 { 0, 0 } 140}; 141 142static driver_t localbus_driver = { 143 "localbus", 144 localbus_methods, 145 sizeof(struct localbus_softc) 146}; 147 148const struct localbus_va_entry localbus_virtmap[] = { 149 { 0, MV_DEV_BOOT_BASE, MV_DEV_BOOT_SIZE }, 150 { 1, MV_DEV_CS0_BASE, MV_DEV_CS0_SIZE }, 151 { 2, MV_DEV_CS1_BASE, MV_DEV_CS1_SIZE }, 152 { 3, MV_DEV_CS2_BASE, MV_DEV_CS2_SIZE }, 153 { -1, 0, 0 } 154}; 155 156static struct localbus_bank localbus_banks[MV_LOCALBUS_MAX_BANKS]; 157 158devclass_t localbus_devclass; 159 160DRIVER_MODULE(localbus, ofwbus, localbus_driver, localbus_devclass, 0, 0); 161 162static int 163fdt_localbus_reg_decode(phandle_t node, struct localbus_softc *sc, 164 struct localbus_devinfo *di) 165{ 166 u_long start, end, count; 167 pcell_t *reg, *regptr; 168 pcell_t addr_cells, size_cells; 169 int tuple_size, tuples; 170 int i, rv, bank; 171 172 if (fdt_addrsize_cells(OF_parent(node), &addr_cells, &size_cells) != 0) 173 return (ENXIO); 174 175 tuple_size = sizeof(pcell_t) * (addr_cells + size_cells); 176 tuples = OF_getprop_alloc_multi(node, "reg", tuple_size, (void **)®); 177 debugf("addr_cells = %d, size_cells = %d\n", addr_cells, size_cells); 178 debugf("tuples = %d, tuple size = %d\n", tuples, tuple_size); 179 if (tuples <= 0) 180 /* No 'reg' property in this node. */ 181 return (0); 182 183 regptr = reg; 184 for (i = 0; i < tuples; i++) { 185 bank = fdt_data_get((void *)regptr, 1); 186 187 if (bank >= MV_LOCALBUS_MAX_BANKS) { 188 device_printf(sc->sc_dev, "bank number [%d] out of " 189 "range\n", bank); 190 continue; 191 } 192 193 /* 194 * If device doesn't have virtual to physical mapping don't add 195 * resources 196 */ 197 if (!(sc->sc_banks[bank].mapped)) { 198 device_printf(sc->sc_dev, "device [%d]: missing memory " 199 "mapping\n", bank); 200 continue; 201 } 202 203 di->di_bank = bank; 204 regptr += 1; 205 206 /* Get address/size. */ 207 rv = fdt_data_to_res(regptr, addr_cells - 1, size_cells, &start, 208 &count); 209 if (rv != 0) { 210 resource_list_free(&di->di_res); 211 goto out; 212 } 213 214 /* Check if enough amount of memory is mapped */ 215 if (sc->sc_banks[bank].size < count) { 216 device_printf(sc->sc_dev, "device [%d]: not enough " 217 "memory reserved\n", bank); 218 continue; 219 } 220 221 regptr += addr_cells - 1 + size_cells; 222 223 /* Calculate address range relative to VA base. */ 224 start = sc->sc_banks[bank].va + start; 225 end = start + count - 1; 226 227 debugf("reg addr bank = %d, start = %lx, end = %lx, " 228 "count = %lx\n", bank, start, end, count); 229 230 /* Use bank (CS) cell as rid. */ 231 resource_list_add(&di->di_res, SYS_RES_MEMORY, di->di_bank, 232 start, end, count); 233 } 234 rv = 0; 235out: 236 OF_prop_free(reg); 237 return (rv); 238} 239 240static int 241localbus_probe(device_t dev) 242{ 243 244 if (!ofw_bus_is_compatible_strict(dev, "mrvl,lbc")) 245 return (ENXIO); 246 247 device_set_desc(dev, "Marvell device bus"); 248 249 return (BUS_PROBE_DEFAULT); 250} 251 252static int 253localbus_attach(device_t dev) 254{ 255 device_t dev_child; 256 struct localbus_softc *sc; 257 struct localbus_devinfo *di; 258 phandle_t dt_node, dt_child; 259 260 sc = device_get_softc(dev); 261 sc->sc_dev = dev; 262 sc->sc_banks = localbus_banks; 263 264 /* 265 * Walk localbus and add direct subordinates as our children. 266 */ 267 dt_node = ofw_bus_get_node(dev); 268 for (dt_child = OF_child(dt_node); dt_child != 0; 269 dt_child = OF_peer(dt_child)) { 270 /* Check and process 'status' property. */ 271 if (!(ofw_bus_node_status_okay(dt_child))) 272 continue; 273 274 if (!(mv_fdt_pm(dt_child))) 275 continue; 276 277 di = malloc(sizeof(*di), M_LOCALBUS, M_WAITOK | M_ZERO); 278 if (ofw_bus_gen_setup_devinfo(&di->di_ofw, dt_child) != 0) { 279 free(di, M_LOCALBUS); 280 device_printf(dev, "could not set up devinfo\n"); 281 continue; 282 } 283 284 resource_list_init(&di->di_res); 285 if (fdt_localbus_reg_decode(dt_child, sc, di)) { 286 device_printf(dev, "could not process 'reg' " 287 "property\n"); 288 ofw_bus_gen_destroy_devinfo(&di->di_ofw); 289 free(di, M_LOCALBUS); 290 continue; 291 } 292 293 /* Add newbus device for this FDT node */ 294 dev_child = device_add_child(dev, NULL, -1); 295 if (dev_child == NULL) { 296 device_printf(dev, "could not add child: %s\n", 297 di->di_ofw.obd_name); 298 resource_list_free(&di->di_res); 299 ofw_bus_gen_destroy_devinfo(&di->di_ofw); 300 free(di, M_LOCALBUS); 301 continue; 302 } 303#ifdef DEBUG 304 device_printf(dev, "added child: %s\n\n", di->di_ofw.obd_name); 305#endif 306 device_set_ivars(dev_child, di); 307 } 308 309 return (bus_generic_attach(dev)); 310} 311 312static int 313localbus_print_child(device_t dev, device_t child) 314{ 315 struct localbus_devinfo *di; 316 struct resource_list *rl; 317 int rv; 318 319 di = device_get_ivars(child); 320 rl = &di->di_res; 321 322 rv = 0; 323 rv += bus_print_child_header(dev, child); 324 rv += resource_list_print_type(rl, "mem", SYS_RES_MEMORY, "%#jx"); 325 rv += resource_list_print_type(rl, "irq", SYS_RES_IRQ, "%jd"); 326 rv += bus_print_child_footer(dev, child); 327 328 return (rv); 329} 330 331static struct resource * 332localbus_alloc_resource(device_t bus, device_t child, int type, int *rid, 333 rman_res_t start, rman_res_t end, rman_res_t count, u_int flags) 334{ 335 struct localbus_devinfo *di; 336 struct resource_list_entry *rle; 337 338 /* 339 * Request for the default allocation with a given rid: use resource 340 * list stored in the local device info. 341 */ 342 if (RMAN_IS_DEFAULT_RANGE(start, end)) { 343 if ((di = device_get_ivars(child)) == NULL) 344 return (NULL); 345 346 if (type == SYS_RES_IOPORT) 347 type = SYS_RES_MEMORY; 348 349 rid = &di->di_bank; 350 rle = resource_list_find(&di->di_res, type, *rid); 351 if (rle == NULL) { 352 device_printf(bus, "no default resources for " 353 "rid = %d, type = %d\n", *rid, type); 354 return (NULL); 355 } 356 start = rle->start; 357 end = rle->end; 358 count = rle->count; 359 } 360 361 return (bus_generic_alloc_resource(bus, child, type, rid, start, end, 362 count, flags)); 363} 364 365static struct resource_list * 366localbus_get_resource_list(device_t bus, device_t child) 367{ 368 struct localbus_devinfo *di; 369 370 di = device_get_ivars(child); 371 return (&di->di_res); 372} 373 374static const struct ofw_bus_devinfo * 375localbus_get_devinfo(device_t bus, device_t child) 376{ 377 struct localbus_devinfo *di; 378 379 di = device_get_ivars(child); 380 return (&di->di_ofw); 381} 382 383int 384fdt_localbus_devmap(phandle_t dt_node, struct devmap_entry *fdt_devmap, 385 int banks_max_num, int *banks_added) 386{ 387 pcell_t ranges[MV_LOCALBUS_MAX_BANKS * MV_LOCALBUS_MAX_BANK_CELLS]; 388 pcell_t *rangesptr; 389 uint32_t tuple_size, bank; 390 vm_paddr_t offset; 391 vm_size_t size; 392 int dev_num, addr_cells, size_cells, par_addr_cells, va_index, i, j, k; 393 394 if ((fdt_addrsize_cells(dt_node, &addr_cells, &size_cells)) != 0) 395 return (EINVAL); 396 397 par_addr_cells = fdt_parent_addr_cells(dt_node); 398 if (par_addr_cells > 2) { 399 /* 400 * Localbus devmap initialization error: unsupported parent 401 * #addr-cells 402 */ 403 return (ERANGE); 404 } 405 406 tuple_size = (addr_cells + par_addr_cells + size_cells); 407 if (tuple_size > MV_LOCALBUS_MAX_BANK_CELLS) 408 return (ERANGE); 409 410 tuple_size *= sizeof(pcell_t); 411 412 dev_num = OF_getprop(dt_node, "ranges", ranges, sizeof(ranges)); 413 if (dev_num <= 0) 414 return (EINVAL); 415 416 /* Calculate number of devices attached to bus */ 417 dev_num = dev_num / tuple_size; 418 419 /* 420 * If number of ranges > max number of localbus devices, 421 * additional entries will not be processed 422 */ 423 dev_num = MIN(dev_num, banks_max_num); 424 425 rangesptr = &ranges[0]; 426 j = 0; 427 428 /* Process data from FDT */ 429 for (i = 0; i < dev_num; i++) { 430 /* First field is bank number */ 431 bank = fdt_data_get((void *)rangesptr, 1); 432 rangesptr += 1; 433 434 if (bank > MV_LOCALBUS_MAX_BANKS) { 435 /* Bank out of range */ 436 rangesptr += ((addr_cells - 1) + par_addr_cells + 437 size_cells); 438 continue; 439 } 440 441 /* Find virtmap entry for this bank */ 442 va_index = -1; 443 for (k = 0; localbus_virtmap[k].bank >= 0; k++) { 444 if (localbus_virtmap[k].bank == bank) { 445 va_index = k; 446 break; 447 } 448 } 449 450 /* Check if virtmap entry was found */ 451 if (va_index == -1) { 452 rangesptr += ((addr_cells - 1) + par_addr_cells + 453 size_cells); 454 continue; 455 } 456 457 /* Remaining child's address fields are unused */ 458 rangesptr += (addr_cells - 1); 459 460 /* Parent address offset */ 461 offset = fdt_data_get((void *)rangesptr, par_addr_cells); 462 rangesptr += par_addr_cells; 463 464 /* Last field is size */ 465 size = fdt_data_get((void *)rangesptr, size_cells); 466 rangesptr += size_cells; 467 468 if (size > localbus_virtmap[va_index].size) { 469 /* Not enough space reserved in virtual memory map */ 470 continue; 471 } 472 473 fdt_devmap[j].pd_va = localbus_virtmap[va_index].va; 474 fdt_devmap[j].pd_pa = offset; 475 fdt_devmap[j].pd_size = size; 476 477 /* Copy data to structure used by localbus driver */ 478 localbus_banks[bank].va = fdt_devmap[j].pd_va; 479 localbus_banks[bank].pa = fdt_devmap[j].pd_pa; 480 localbus_banks[bank].size = fdt_devmap[j].pd_size; 481 localbus_banks[bank].mapped = 1; 482 483 j++; 484 } 485 486 *banks_added = j; 487 return (0); 488} 489