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