1/*- 2 * SPDX-License-Identifier: BSD-2-Clause-FreeBSD 3 * 4 * Copyright (c) 2016 Michael Zhilin <mizhka@gmail.com> 5 * Copyright (c) 2015-2016 Landon Fuller <landon@landonf.org> 6 * All rights reserved. 7 * 8 * Redistribution and use in source and binary forms, with or without 9 * modification, are permitted provided that the following conditions 10 * are met: 11 * 1. Redistributions of source code must retain the above copyright 12 * notice, this list of conditions and the following disclaimer, 13 * without modification. 14 * 2. Redistributions in binary form must reproduce at minimum a disclaimer 15 * similar to the "NO WARRANTY" disclaimer below ("Disclaimer") and any 16 * redistribution must be conditioned upon including a substantially 17 * similar Disclaimer requirement for further binary redistribution. 18 * 19 * NO WARRANTY 20 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 21 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 22 * LIMITED TO, THE IMPLIED WARRANTIES OF NONINFRINGEMENT, MERCHANTIBILITY 23 * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL 24 * THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, 25 * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 26 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 27 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER 28 * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 29 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF 30 * THE POSSIBILITY OF SUCH DAMAGES. 31 */ 32 33#include <sys/cdefs.h> 34__FBSDID("$FreeBSD$"); 35 36#include <sys/param.h> 37#include <sys/kernel.h> 38 39#include "chipc_private.h" 40#include "chipcvar.h" 41 42/** 43 * Return a human-readable name for the given flash @p type. 44 */ 45const char * 46chipc_flash_name(chipc_flash type) 47{ 48 switch (type) { 49 case CHIPC_PFLASH_CFI: 50 return ("CFI Flash"); 51 52 case CHIPC_SFLASH_ST: 53 case CHIPC_SFLASH_AT: 54 return ("SPI Flash"); 55 56 case CHIPC_QSFLASH_ST: 57 case CHIPC_QSFLASH_AT: 58 return ("QSPI Flash"); 59 60 case CHIPC_NFLASH: 61 case CHIPC_NFLASH_4706: 62 return ("NAND"); 63 64 case CHIPC_FLASH_NONE: 65 default: 66 return ("unknown"); 67 } 68} 69 70/** 71 * Return the name of the bus device class used by flash @p type, 72 * or NULL if @p type is unsupported. 73 */ 74const char * 75chipc_flash_bus_name(chipc_flash type) 76{ 77 switch (type) { 78 case CHIPC_PFLASH_CFI: 79 return ("cfi"); 80 81 case CHIPC_SFLASH_ST: 82 case CHIPC_SFLASH_AT: 83 return ("spi"); 84 85 case CHIPC_QSFLASH_ST: 86 case CHIPC_QSFLASH_AT: 87 /* unimplemented; spi? */ 88 return (NULL); 89 90 case CHIPC_NFLASH: 91 case CHIPC_NFLASH_4706: 92 /* unimplemented; nandbus? */ 93 return (NULL); 94 95 case CHIPC_FLASH_NONE: 96 default: 97 return (NULL); 98 } 99} 100 101/** 102 * Return the name of the flash device class for SPI flash @p type, 103 * or NULL if @p type does not use SPI, or is unsupported. 104 */ 105const char * 106chipc_sflash_device_name(chipc_flash type) 107{ 108 switch (type) { 109 case CHIPC_SFLASH_ST: 110 return ("mx25l"); 111 112 case CHIPC_SFLASH_AT: 113 return ("at45d"); 114 115 case CHIPC_QSFLASH_ST: 116 case CHIPC_QSFLASH_AT: 117 /* unimplemented */ 118 return (NULL); 119 120 case CHIPC_PFLASH_CFI: 121 case CHIPC_NFLASH: 122 case CHIPC_NFLASH_4706: 123 case CHIPC_FLASH_NONE: 124 default: 125 return (NULL); 126 } 127} 128 129/** 130 * Initialize child resource @p r with a virtual address, tag, and handle 131 * copied from @p parent, adjusted to contain only the range defined by 132 * @p offsize and @p size. 133 * 134 * @param r The register to be initialized. 135 * @param parent The parent bus resource that fully contains the subregion. 136 * @param offset The subregion offset within @p parent. 137 * @param size The subregion size. 138 */ 139int 140chipc_init_child_resource(struct resource *r, 141 struct resource *parent, bhnd_size_t offset, bhnd_size_t size) 142{ 143 bus_space_handle_t bh, child_bh; 144 bus_space_tag_t bt; 145 uintptr_t vaddr; 146 int error; 147 148 /* Fetch the parent resource's bus values */ 149 vaddr = (uintptr_t) rman_get_virtual(parent); 150 bt = rman_get_bustag(parent); 151 bh = rman_get_bushandle(parent); 152 153 /* Configure child resource with offset-adjusted values */ 154 vaddr += offset; 155 error = bus_space_subregion(bt, bh, offset, size, &child_bh); 156 if (error) 157 return (error); 158 159 rman_set_virtual(r, (void *) vaddr); 160 rman_set_bustag(r, bt); 161 rman_set_bushandle(r, child_bh); 162 163 return (0); 164} 165 166/** 167 * Map an interrupt line to an IRQ, and then register a corresponding SYS_RES_IRQ 168 * with @p child's resource list. 169 * 170 * @param sc chipc driver state. 171 * @param child The device to set the resource on. 172 * @param rid The resource ID. 173 * @param intr The interrupt line to be mapped. 174 * @param count The length of the resource. 175 * @param port The mapping port number (ignored if not SYS_RES_MEMORY). 176 * @param region The mapping region number (ignored if not SYS_RES_MEMORY). 177 */ 178int 179chipc_set_irq_resource(struct chipc_softc *sc, device_t child, int rid, 180 u_int intr) 181{ 182 struct chipc_devinfo *dinfo; 183 int error; 184 185 KASSERT(device_get_parent(child) == sc->dev, ("not a direct child")); 186 dinfo = device_get_ivars(child); 187 188 /* We currently only support a single IRQ mapping */ 189 if (dinfo->irq_mapped) { 190 device_printf(sc->dev, "irq already mapped for child\n"); 191 return (ENOMEM); 192 } 193 194 /* Map the IRQ */ 195 if ((error = bhnd_map_intr(sc->dev, intr, &dinfo->irq))) { 196 device_printf(sc->dev, "failed to map intr %u: %d\n", intr, 197 error); 198 return (error); 199 } 200 201 dinfo->irq_mapped = true; 202 203 /* Add to child's resource list */ 204 error = bus_set_resource(child, SYS_RES_IRQ, rid, dinfo->irq, 1); 205 if (error) { 206 device_printf(sc->dev, "failed to set child irq resource %d to " 207 "%ju: %d\n", rid, dinfo->irq, error); 208 209 bhnd_unmap_intr(sc->dev, dinfo->irq); 210 return (error); 211 } 212 213 return (0); 214} 215 216/** 217 * Add a SYS_RES_MEMORY resource with a given resource ID, relative to the 218 * given port and region, to @p child's resource list. 219 * 220 * The specified @p region's address and size will be fetched from the bhnd(4) 221 * bus, and bus_set_resource() will be called with @p start added the region's 222 * actual base address. 223 * 224 * To use the default region values for @p start and @p count, specify 225 * a @p start value of 0ul, and an end value of RMAN_MAX_END 226 * 227 * @param sc chipc driver state. 228 * @param child The device to set the resource on. 229 * @param rid The resource ID. 230 * @param start The resource start address (if SYS_RES_MEMORY, this is 231 * relative to @p region's base address). 232 * @param count The length of the resource. 233 * @param port The mapping port number (ignored if not SYS_RES_MEMORY). 234 * @param region The mapping region number (ignored if not SYS_RES_MEMORY). 235 */ 236int 237chipc_set_mem_resource(struct chipc_softc *sc, device_t child, int rid, 238 rman_res_t start, rman_res_t count, u_int port, u_int region) 239{ 240 bhnd_addr_t region_addr; 241 bhnd_size_t region_size; 242 bool isdefault; 243 int error; 244 245 KASSERT(device_get_parent(child) == sc->dev, ("not a direct child")); 246 isdefault = RMAN_IS_DEFAULT_RANGE(start, count); 247 248 /* Fetch region address and size */ 249 error = bhnd_get_region_addr(sc->dev, BHND_PORT_DEVICE, port, 250 region, ®ion_addr, ®ion_size); 251 if (error) { 252 device_printf(sc->dev, 253 "lookup of %s%u.%u failed: %d\n", 254 bhnd_port_type_name(BHND_PORT_DEVICE), port, region, error); 255 return (error); 256 } 257 258 /* Populate defaults */ 259 if (isdefault) { 260 start = 0; 261 count = region_size; 262 } 263 264 /* Verify requested range is mappable */ 265 if (start > region_size || region_size - start < count) { 266 device_printf(sc->dev, 267 "%s%u.%u region cannot map requested range %#jx+%#jx\n", 268 bhnd_port_type_name(BHND_PORT_DEVICE), port, region, start, 269 count); 270 return (ERANGE); 271 } 272 273 return (bus_set_resource(child, SYS_RES_MEMORY, rid, 274 region_addr + start, count)); 275} 276 277/* 278 * Print a capability structure. 279 */ 280void 281chipc_print_caps(device_t dev, struct chipc_caps *caps) 282{ 283#define CC_TFS(_flag) (caps->_flag ? "yes" : "no") 284 285 device_printf(dev, "MIPSEB: %-3s | BP64: %s\n", 286 CC_TFS(mipseb), CC_TFS(backplane_64)); 287 device_printf(dev, "UARTs: %-3hhu | UGPIO: %s\n", 288 caps->num_uarts, CC_TFS(uart_gpio)); 289 // XXX: hitting a kvprintf bug with '%#02x' not prefixing '0x' in 290 // some cases, and not apply the field width in others 291 device_printf(dev, "UARTClk: 0x%02x | Flash: %u\n", 292 caps->uart_clock, caps->flash_type); 293 device_printf(dev, "SPROM: %-3s | OTP: %s\n", 294 CC_TFS(sprom), CC_TFS(otp_size)); 295 device_printf(dev, "CFIsz: 0x%02x | OTPsz: 0x%02x\n", 296 caps->cfi_width, caps->otp_size); 297 device_printf(dev, "ExtBus: 0x%02x | PwrCtrl: %s\n", 298 caps->extbus_type, CC_TFS(pwr_ctrl)); 299 device_printf(dev, "PLL: 0x%02x | JTAGM: %s\n", 300 caps->pll_type, CC_TFS(jtag_master)); 301 device_printf(dev, "PMU: %-3s | ECI: %s\n", 302 CC_TFS(pmu), CC_TFS(eci)); 303 device_printf(dev, "SECI: %-3s | GSIO: %s\n", 304 CC_TFS(seci), CC_TFS(gsio)); 305 device_printf(dev, "AOB: %-3s | BootROM: %s\n", 306 CC_TFS(aob), CC_TFS(boot_rom)); 307 308#undef CC_TFS 309} 310 311/** 312 * Allocate and initialize new region record. 313 * 314 * @param sc Driver instance state. 315 * @param type The port type to query. 316 * @param port The port number to query. 317 * @param region The region number to query. 318 */ 319struct chipc_region * 320chipc_alloc_region(struct chipc_softc *sc, bhnd_port_type type, 321 u_int port, u_int region) 322{ 323 struct chipc_region *cr; 324 int error; 325 326 /* Don't bother allocating a chipc_region if init will fail */ 327 if (!bhnd_is_region_valid(sc->dev, type, port, region)) 328 return (NULL); 329 330 /* Allocate and initialize region info */ 331 cr = malloc(sizeof(*cr), M_BHND, M_NOWAIT); 332 if (cr == NULL) 333 return (NULL); 334 335 cr->cr_port_type = type; 336 cr->cr_port_num = port; 337 cr->cr_region_num = region; 338 cr->cr_res = NULL; 339 cr->cr_refs = 0; 340 cr->cr_act_refs = 0; 341 342 error = bhnd_get_region_addr(sc->dev, type, port, region, &cr->cr_addr, 343 &cr->cr_count); 344 if (error) { 345 device_printf(sc->dev, 346 "fetching chipc region address failed: %d\n", error); 347 goto failed; 348 } 349 350 cr->cr_end = cr->cr_addr + cr->cr_count - 1; 351 352 /* Fetch default resource ID for this region. Not all regions have an 353 * assigned rid, in which case this will return -1 */ 354 cr->cr_rid = bhnd_get_port_rid(sc->dev, type, port, region); 355 356 return (cr); 357 358failed: 359 device_printf(sc->dev, "chipc region alloc failed for %s%u.%u\n", 360 bhnd_port_type_name(type), port, region); 361 free(cr, M_BHND); 362 return (NULL); 363} 364 365/** 366 * Deallocate the given region record and its associated resource, if any. 367 * 368 * @param sc Driver instance state. 369 * @param cr Region record to be deallocated. 370 */ 371void 372chipc_free_region(struct chipc_softc *sc, struct chipc_region *cr) 373{ 374 KASSERT(cr->cr_refs == 0, 375 ("chipc %s%u.%u region has %u active references", 376 bhnd_port_type_name(cr->cr_port_type), cr->cr_port_num, 377 cr->cr_region_num, cr->cr_refs)); 378 379 if (cr->cr_res != NULL) { 380 bhnd_release_resource(sc->dev, SYS_RES_MEMORY, cr->cr_res_rid, 381 cr->cr_res); 382 } 383 384 free(cr, M_BHND); 385} 386 387/** 388 * Locate the region mapping the given range, if any. Returns NULL if no 389 * valid region is found. 390 * 391 * @param sc Driver instance state. 392 * @param start start of address range. 393 * @param end end of address range. 394 */ 395struct chipc_region * 396chipc_find_region(struct chipc_softc *sc, rman_res_t start, rman_res_t end) 397{ 398 struct chipc_region *cr; 399 400 if (start > end) 401 return (NULL); 402 403 STAILQ_FOREACH(cr, &sc->mem_regions, cr_link) { 404 if (start < cr->cr_addr || end > cr->cr_end) 405 continue; 406 407 /* Found */ 408 return (cr); 409 } 410 411 /* Not found */ 412 return (NULL); 413} 414 415/** 416 * Locate a region mapping by its bhnd-assigned resource id (as returned by 417 * bhnd_get_port_rid). 418 * 419 * @param sc Driver instance state. 420 * @param rid Resource ID to query for. 421 */ 422struct chipc_region * 423chipc_find_region_by_rid(struct chipc_softc *sc, int rid) 424{ 425 struct chipc_region *cr; 426 int port_rid; 427 428 STAILQ_FOREACH(cr, &sc->mem_regions, cr_link) { 429 port_rid = bhnd_get_port_rid(sc->dev, cr->cr_port_type, 430 cr->cr_port_num, cr->cr_region_num); 431 if (port_rid == -1 || port_rid != rid) 432 continue; 433 434 /* Found */ 435 return (cr); 436 } 437 438 /* Not found */ 439 return (NULL); 440} 441 442/** 443 * Retain a reference to a chipc_region, allocating and activating the 444 * backing resource as required. 445 * 446 * @param sc chipc driver instance state 447 * @param cr region to retain. 448 * @param flags specify RF_ALLOCATED to retain an allocation reference, 449 * RF_ACTIVE to retain an activation reference. 450 */ 451int 452chipc_retain_region(struct chipc_softc *sc, struct chipc_region *cr, int flags) 453{ 454 int error; 455 456 KASSERT(!(flags &~ (RF_ACTIVE|RF_ALLOCATED)), ("unsupported flags")); 457 458 CHIPC_LOCK(sc); 459 460 /* Handle allocation */ 461 if (flags & RF_ALLOCATED) { 462 /* If this is the first reference, allocate the resource */ 463 if (cr->cr_refs == 0) { 464 KASSERT(cr->cr_res == NULL, 465 ("non-NULL resource has refcount")); 466 467 /* Fetch initial resource ID */ 468 if ((cr->cr_res_rid = cr->cr_rid) == -1) { 469 CHIPC_UNLOCK(sc); 470 return (EINVAL); 471 } 472 473 /* Allocate resource */ 474 cr->cr_res = bhnd_alloc_resource(sc->dev, 475 SYS_RES_MEMORY, &cr->cr_res_rid, cr->cr_addr, 476 cr->cr_end, cr->cr_count, RF_SHAREABLE); 477 if (cr->cr_res == NULL) { 478 CHIPC_UNLOCK(sc); 479 return (ENXIO); 480 } 481 } 482 483 /* Increment allocation refcount */ 484 cr->cr_refs++; 485 } 486 487 /* Handle activation */ 488 if (flags & RF_ACTIVE) { 489 KASSERT(cr->cr_refs > 0, 490 ("cannot activate unallocated resource")); 491 492 /* If this is the first reference, activate the resource */ 493 if (cr->cr_act_refs == 0) { 494 error = bhnd_activate_resource(sc->dev, SYS_RES_MEMORY, 495 cr->cr_res_rid, cr->cr_res); 496 if (error) { 497 /* Drop any allocation reference acquired 498 * above */ 499 CHIPC_UNLOCK(sc); 500 chipc_release_region(sc, cr, 501 flags &~ RF_ACTIVE); 502 return (error); 503 } 504 } 505 506 /* Increment activation refcount */ 507 cr->cr_act_refs++; 508 } 509 510 CHIPC_UNLOCK(sc); 511 return (0); 512} 513 514/** 515 * Release a reference to a chipc_region, deactivating and releasing the 516 * backing resource if the reference count hits zero. 517 * 518 * @param sc chipc driver instance state 519 * @param cr region to retain. 520 * @param flags specify RF_ALLOCATED to release an allocation reference, 521 * RF_ACTIVE to release an activation reference. 522 */ 523int 524chipc_release_region(struct chipc_softc *sc, struct chipc_region *cr, 525 int flags) 526{ 527 int error; 528 529 CHIPC_LOCK(sc); 530 error = 0; 531 532 KASSERT(cr->cr_res != NULL, ("release on NULL region resource")); 533 534 if (flags & RF_ACTIVE) { 535 KASSERT(cr->cr_act_refs > 0, ("RF_ACTIVE over-released")); 536 KASSERT(cr->cr_act_refs <= cr->cr_refs, 537 ("RF_ALLOCATED released with RF_ACTIVE held")); 538 539 /* If this is the last reference, deactivate the resource */ 540 if (cr->cr_act_refs == 1) { 541 error = bhnd_deactivate_resource(sc->dev, 542 SYS_RES_MEMORY, cr->cr_res_rid, cr->cr_res); 543 if (error) 544 goto done; 545 } 546 547 /* Drop our activation refcount */ 548 cr->cr_act_refs--; 549 } 550 551 if (flags & RF_ALLOCATED) { 552 KASSERT(cr->cr_refs > 0, ("overrelease of refs")); 553 /* If this is the last reference, release the resource */ 554 if (cr->cr_refs == 1) { 555 error = bhnd_release_resource(sc->dev, SYS_RES_MEMORY, 556 cr->cr_res_rid, cr->cr_res); 557 if (error) 558 goto done; 559 560 cr->cr_res = NULL; 561 } 562 563 /* Drop our allocation refcount */ 564 cr->cr_refs--; 565 } 566 567done: 568 CHIPC_UNLOCK(sc); 569 return (error); 570} 571