1 /*- 2 * Copyright (c) 2016 Landon Fuller <landonf@FreeBSD.org> 3 * Copyright (c) 2010 Broadcom Corporation. 4 * Copyright (c) 2017 The FreeBSD Foundation 5 * All rights reserved. 6 * 7 * Portions of this software were developed by Landon Fuller 8 * under sponsorship from the FreeBSD Foundation. 9 * 10 * Portions of this file were derived from the siutils.c source distributed with 11 * the Asus RT-N16 firmware source code release. 12 * 13 * Permission to use, copy, modify, and/or distribute this software for any 14 * purpose with or without fee is hereby granted, provided that the above 15 * copyright notice and this permission notice appear in all copies. 16 * 17 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 18 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 19 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY 20 * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 21 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION 22 * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN 23 * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 24 * 25 * $Id: siutils.c,v 1.821.2.48 2011-02-11 20:59:28 Exp $ 26 */ 27 28 #include <sys/cdefs.h> 29 __FBSDID("$FreeBSD$"); 30 31 #include <sys/param.h> 32 #include <sys/kernel.h> 33 #include <sys/bus.h> 34 #include <sys/limits.h> 35 #include <sys/malloc.h> 36 #include <sys/module.h> 37 #include <sys/systm.h> 38 39 #include <dev/bhnd/bhnd.h> 40 41 #include <dev/bhnd/cores/chipc/chipcreg.h> 42 #include <dev/bhnd/cores/chipc/chipcvar.h> 43 44 #include <dev/bhnd/cores/pmu/bhnd_pmuvar.h> 45 #include <dev/bhnd/cores/pmu/bhnd_pmureg.h> 46 47 #include "bhnd_chipc_if.h" 48 #include "bhnd_pwrctl_if.h" 49 #include "bhnd_pwrctl_hostb_if.h" 50 51 #include "bhnd_pwrctl_private.h" 52 53 /* 54 * ChipCommon Power Control. 55 * 56 * Provides a runtime interface to device clocking and power management on 57 * legacy non-PMU chipsets. 58 */ 59 60 typedef enum { 61 BHND_PWRCTL_WAR_UP, /**< apply attach/resume workarounds */ 62 BHND_PWRCTL_WAR_RUN, /**< apply running workarounds */ 63 BHND_PWRCTL_WAR_DOWN, /**< apply detach/suspend workarounds */ 64 } bhnd_pwrctl_wars; 65 66 static int bhnd_pwrctl_updateclk(struct bhnd_pwrctl_softc *sc, 67 bhnd_pwrctl_wars wars); 68 69 static struct bhnd_device_quirk pwrctl_quirks[]; 70 71 72 /* Supported parent core device identifiers */ 73 static const struct bhnd_device pwrctl_devices[] = { 74 BHND_DEVICE(BCM, CC, "ChipCommon Power Control", pwrctl_quirks), 75 BHND_DEVICE_END 76 }; 77 78 /* Device quirks table */ 79 static struct bhnd_device_quirk pwrctl_quirks[] = { 80 BHND_CORE_QUIRK (HWREV_LTE(5), PWRCTL_QUIRK_PCICLK_CTL), 81 BHND_CORE_QUIRK (HWREV_RANGE(6, 9), PWRCTL_QUIRK_SLOWCLK_CTL), 82 BHND_CORE_QUIRK (HWREV_RANGE(10, 19), PWRCTL_QUIRK_INSTACLK_CTL), 83 84 BHND_DEVICE_QUIRK_END 85 }; 86 87 static int 88 bhnd_pwrctl_probe(device_t dev) 89 { 90 const struct bhnd_device *id; 91 struct chipc_caps *ccaps; 92 device_t chipc; 93 94 /* Look for compatible chipc parent */ 95 chipc = device_get_parent(dev); 96 if (device_get_devclass(chipc) != devclass_find("bhnd_chipc")) 97 return (ENXIO); 98 99 if (device_get_driver(chipc) != &bhnd_chipc_driver) 100 return (ENXIO); 101 102 /* Verify chipc capability flags */ 103 ccaps = BHND_CHIPC_GET_CAPS(chipc); 104 if (ccaps->pmu || !ccaps->pwr_ctrl) 105 return (ENXIO); 106 107 /* Check for chipc device match */ 108 id = bhnd_device_lookup(chipc, pwrctl_devices, 109 sizeof(pwrctl_devices[0])); 110 if (id == NULL) 111 return (ENXIO); 112 113 device_set_desc(dev, id->desc); 114 return (BUS_PROBE_NOWILDCARD); 115 } 116 117 static int 118 bhnd_pwrctl_attach(device_t dev) 119 { 120 struct bhnd_pwrctl_softc *sc; 121 const struct bhnd_chipid *cid; 122 struct chipc_softc *chipc_sc; 123 bhnd_devclass_t hostb_class; 124 device_t hostb_dev; 125 device_t bus; 126 int error; 127 128 sc = device_get_softc(dev); 129 130 sc->dev = dev; 131 sc->chipc_dev = device_get_parent(dev); 132 sc->quirks = bhnd_device_quirks(sc->chipc_dev, pwrctl_devices, 133 sizeof(pwrctl_devices[0])); 134 135 bus = device_get_parent(sc->chipc_dev); 136 137 /* On devices that lack a slow clock source, HT must always be 138 * enabled. */ 139 hostb_class = BHND_DEVCLASS_INVALID; 140 hostb_dev = bhnd_bus_find_hostb_device(device_get_parent(sc->chipc_dev)); 141 if (hostb_dev != NULL) 142 hostb_class = bhnd_get_class(hostb_dev); 143 144 cid = bhnd_get_chipid(sc->chipc_dev); 145 switch (cid->chip_id) { 146 case BHND_CHIPID_BCM4311: 147 if (cid->chip_rev <= 1 && hostb_class == BHND_DEVCLASS_PCI) 148 sc->quirks |= PWRCTL_QUIRK_FORCE_HT; 149 break; 150 151 case BHND_CHIPID_BCM4321: 152 if (hostb_class == BHND_DEVCLASS_PCIE || 153 hostb_class == BHND_DEVCLASS_PCI) 154 sc->quirks |= PWRCTL_QUIRK_FORCE_HT; 155 break; 156 157 case BHND_CHIPID_BCM4716: 158 if (hostb_class == BHND_DEVCLASS_PCIE) 159 sc->quirks |= PWRCTL_QUIRK_FORCE_HT; 160 break; 161 } 162 163 /* Fetch core register block from ChipCommon parent */ 164 chipc_sc = device_get_softc(sc->chipc_dev); 165 sc->res = chipc_sc->core; 166 167 PWRCTL_LOCK_INIT(sc); 168 STAILQ_INIT(&sc->clkres_list); 169 170 /* Initialize power control */ 171 PWRCTL_LOCK(sc); 172 173 if ((error = bhnd_pwrctl_init(sc))) { 174 PWRCTL_UNLOCK(sc); 175 goto cleanup; 176 } 177 178 /* Apply default clock transitions */ 179 if ((error = bhnd_pwrctl_updateclk(sc, BHND_PWRCTL_WAR_UP))) { 180 PWRCTL_UNLOCK(sc); 181 goto cleanup; 182 } 183 184 PWRCTL_UNLOCK(sc); 185 186 /* Register as the bus PWRCTL provider */ 187 if ((error = bhnd_register_provider(dev, BHND_SERVICE_PWRCTL))) { 188 device_printf(sc->dev, "failed to register PWRCTL with bus : " 189 "%d\n", error); 190 goto cleanup; 191 } 192 193 return (0); 194 195 cleanup: 196 PWRCTL_LOCK_DESTROY(sc); 197 return (error); 198 } 199 200 static int 201 bhnd_pwrctl_detach(device_t dev) 202 { 203 struct bhnd_pwrctl_softc *sc; 204 struct bhnd_pwrctl_clkres *clkres, *crnext; 205 int error; 206 207 sc = device_get_softc(dev); 208 209 if ((error = bhnd_deregister_provider(dev, BHND_SERVICE_ANY))) 210 return (error); 211 212 /* Update clock state */ 213 PWRCTL_LOCK(sc); 214 error = bhnd_pwrctl_updateclk(sc, BHND_PWRCTL_WAR_DOWN); 215 PWRCTL_UNLOCK(sc); 216 if (error) 217 return (error); 218 219 STAILQ_FOREACH_SAFE(clkres, &sc->clkres_list, cr_link, crnext) 220 free(clkres, M_DEVBUF); 221 222 PWRCTL_LOCK_DESTROY(sc); 223 return (0); 224 } 225 226 static int 227 bhnd_pwrctl_suspend(device_t dev) 228 { 229 struct bhnd_pwrctl_softc *sc; 230 int error; 231 232 sc = device_get_softc(dev); 233 234 /* Update clock state */ 235 PWRCTL_LOCK(sc); 236 error = bhnd_pwrctl_updateclk(sc, BHND_PWRCTL_WAR_DOWN); 237 PWRCTL_UNLOCK(sc); 238 239 return (error); 240 } 241 242 static int 243 bhnd_pwrctl_resume(device_t dev) 244 { 245 struct bhnd_pwrctl_softc *sc; 246 int error; 247 248 sc = device_get_softc(dev); 249 250 PWRCTL_LOCK(sc); 251 252 /* Re-initialize power control registers */ 253 if ((error = bhnd_pwrctl_init(sc))) { 254 device_printf(sc->dev, "PWRCTL init failed: %d\n", error); 255 goto cleanup; 256 } 257 258 /* Restore clock state */ 259 if ((error = bhnd_pwrctl_updateclk(sc, BHND_PWRCTL_WAR_UP))) { 260 device_printf(sc->dev, "clock state restore failed: %d\n", 261 error); 262 goto cleanup; 263 } 264 265 cleanup: 266 PWRCTL_UNLOCK(sc); 267 return (error); 268 } 269 270 static int 271 bhnd_pwrctl_get_clock_latency(device_t dev, bhnd_clock clock, 272 u_int *latency) 273 { 274 struct bhnd_pwrctl_softc *sc = device_get_softc(dev); 275 276 switch (clock) { 277 case BHND_CLOCK_HT: 278 PWRCTL_LOCK(sc); 279 *latency = bhnd_pwrctl_fast_pwrup_delay(sc); 280 PWRCTL_UNLOCK(sc); 281 282 return (0); 283 284 default: 285 return (ENODEV); 286 } 287 } 288 289 static int 290 bhnd_pwrctl_get_clock_freq(device_t dev, bhnd_clock clock, u_int *freq) 291 { 292 struct bhnd_pwrctl_softc *sc = device_get_softc(dev); 293 294 switch (clock) { 295 case BHND_CLOCK_ALP: 296 BPMU_LOCK(sc); 297 *freq = bhnd_pwrctl_getclk_speed(sc); 298 BPMU_UNLOCK(sc); 299 300 return (0); 301 302 case BHND_CLOCK_HT: 303 case BHND_CLOCK_ILP: 304 case BHND_CLOCK_DYN: 305 default: 306 return (ENODEV); 307 } 308 } 309 310 /** 311 * Find the clock reservation associated with @p owner, if any. 312 * 313 * @param sc Driver instance state. 314 * @param owner The owning device. 315 */ 316 static struct bhnd_pwrctl_clkres * 317 bhnd_pwrctl_find_res(struct bhnd_pwrctl_softc *sc, device_t owner) 318 { 319 struct bhnd_pwrctl_clkres *clkres; 320 321 PWRCTL_LOCK_ASSERT(sc, MA_OWNED); 322 323 STAILQ_FOREACH(clkres, &sc->clkres_list, cr_link) { 324 if (clkres->owner == owner) 325 return (clkres); 326 } 327 328 /* not found */ 329 return (NULL); 330 } 331 332 /** 333 * Enumerate all active clock requests, compute the minimum required clock, 334 * and issue any required clock transition. 335 * 336 * @param sc Driver instance state. 337 * @param wars Work-around state. 338 */ 339 static int 340 bhnd_pwrctl_updateclk(struct bhnd_pwrctl_softc *sc, bhnd_pwrctl_wars wars) 341 { 342 struct bhnd_pwrctl_clkres *clkres; 343 bhnd_clock clock; 344 345 PWRCTL_LOCK_ASSERT(sc, MA_OWNED); 346 347 /* Nothing to update on fixed clock devices */ 348 if (PWRCTL_QUIRK(sc, FIXED_CLK)) 349 return (0); 350 351 /* Default clock target */ 352 clock = BHND_CLOCK_DYN; 353 354 /* Apply quirk-specific overrides to the clock target */ 355 switch (wars) { 356 case BHND_PWRCTL_WAR_UP: 357 /* Force HT clock */ 358 if (PWRCTL_QUIRK(sc, FORCE_HT)) 359 clock = BHND_CLOCK_HT; 360 break; 361 362 case BHND_PWRCTL_WAR_RUN: 363 /* Cannot transition clock if FORCE_HT */ 364 if (PWRCTL_QUIRK(sc, FORCE_HT)) 365 return (0); 366 break; 367 368 case BHND_PWRCTL_WAR_DOWN: 369 /* Leave default clock unmodified to permit 370 * transition back to BHND_CLOCK_DYN on FORCE_HT devices. */ 371 break; 372 } 373 374 /* Determine required clock */ 375 STAILQ_FOREACH(clkres, &sc->clkres_list, cr_link) 376 clock = bhnd_clock_max(clock, clkres->clock); 377 378 /* Map to supported clock setting */ 379 switch (clock) { 380 case BHND_CLOCK_DYN: 381 case BHND_CLOCK_ILP: 382 clock = BHND_CLOCK_DYN; 383 break; 384 case BHND_CLOCK_ALP: 385 /* In theory FORCE_ALP is supported by the hardware, but 386 * there are currently no known use-cases for it; mapping 387 * to HT is still valid, and allows us to punt on determing 388 * where FORCE_ALP is supported and functional */ 389 clock = BHND_CLOCK_HT; 390 break; 391 case BHND_CLOCK_HT: 392 break; 393 default: 394 device_printf(sc->dev, "unknown clock: %#x\n", clock); 395 return (ENODEV); 396 } 397 398 /* Issue transition */ 399 return (bhnd_pwrctl_setclk(sc, clock)); 400 } 401 402 /* BHND_PWRCTL_REQUEST_CLOCK() */ 403 static int 404 bhnd_pwrctl_request_clock(device_t dev, device_t child, bhnd_clock clock) 405 { 406 struct bhnd_pwrctl_softc *sc; 407 struct bhnd_pwrctl_clkres *clkres; 408 int error; 409 410 sc = device_get_softc(dev); 411 error = 0; 412 413 PWRCTL_LOCK(sc); 414 415 clkres = bhnd_pwrctl_find_res(sc, child); 416 417 /* BHND_CLOCK_DYN discards the clock reservation entirely */ 418 if (clock == BHND_CLOCK_DYN) { 419 /* nothing to clean up? */ 420 if (clkres == NULL) { 421 PWRCTL_UNLOCK(sc); 422 return (0); 423 } 424 425 /* drop reservation and apply clock transition */ 426 STAILQ_REMOVE(&sc->clkres_list, clkres, 427 bhnd_pwrctl_clkres, cr_link); 428 429 if ((error = bhnd_pwrctl_updateclk(sc, BHND_PWRCTL_WAR_RUN))) { 430 device_printf(dev, "clock transition failed: %d\n", 431 error); 432 433 /* restore reservation */ 434 STAILQ_INSERT_TAIL(&sc->clkres_list, clkres, cr_link); 435 436 PWRCTL_UNLOCK(sc); 437 return (error); 438 } 439 440 /* deallocate orphaned reservation */ 441 free(clkres, M_DEVBUF); 442 443 PWRCTL_UNLOCK(sc); 444 return (0); 445 } 446 447 /* create (or update) reservation */ 448 if (clkres == NULL) { 449 clkres = malloc(sizeof(struct bhnd_pwrctl_clkres), M_DEVBUF, 450 M_NOWAIT); 451 if (clkres == NULL) 452 return (ENOMEM); 453 454 clkres->owner = child; 455 clkres->clock = clock; 456 457 STAILQ_INSERT_TAIL(&sc->clkres_list, clkres, cr_link); 458 } else { 459 KASSERT(clkres->owner == child, ("invalid owner")); 460 clkres->clock = clock; 461 } 462 463 /* apply clock transition */ 464 error = bhnd_pwrctl_updateclk(sc, BHND_PWRCTL_WAR_RUN); 465 if (error) { 466 STAILQ_REMOVE(&sc->clkres_list, clkres, bhnd_pwrctl_clkres, 467 cr_link); 468 free(clkres, M_DEVBUF); 469 } 470 471 PWRCTL_UNLOCK(sc); 472 return (error); 473 } 474 475 476 static device_method_t bhnd_pwrctl_methods[] = { 477 /* Device interface */ 478 DEVMETHOD(device_probe, bhnd_pwrctl_probe), 479 DEVMETHOD(device_attach, bhnd_pwrctl_attach), 480 DEVMETHOD(device_detach, bhnd_pwrctl_detach), 481 DEVMETHOD(device_suspend, bhnd_pwrctl_suspend), 482 DEVMETHOD(device_resume, bhnd_pwrctl_resume), 483 484 /* BHND PWRCTL interface */ 485 DEVMETHOD(bhnd_pwrctl_request_clock, bhnd_pwrctl_request_clock), 486 DEVMETHOD(bhnd_pwrctl_get_clock_freq, bhnd_pwrctl_get_clock_freq), 487 DEVMETHOD(bhnd_pwrctl_get_clock_latency, bhnd_pwrctl_get_clock_latency), 488 489 DEVMETHOD_END 490 }; 491 492 DEFINE_CLASS_0(bhnd_pwrctl, bhnd_pwrctl_driver, bhnd_pwrctl_methods, 493 sizeof(struct bhnd_pwrctl_softc)); 494 EARLY_DRIVER_MODULE(bhnd_pwrctl, bhnd_chipc, bhnd_pwrctl_driver, 495 bhnd_pmu_devclass, NULL, NULL, BUS_PASS_TIMER + BUS_PASS_ORDER_MIDDLE); 496 497 MODULE_DEPEND(bhnd_pwrctl, bhnd, 1, 1, 1); 498 MODULE_VERSION(bhnd_pwrctl, 1); 499