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