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 /* Supported parent core device identifiers */ 72 static const struct bhnd_device pwrctl_devices[] = { 73 BHND_DEVICE(BCM, CC, "ChipCommon Power Control", pwrctl_quirks), 74 BHND_DEVICE_END 75 }; 76 77 /* Device quirks table */ 78 static struct bhnd_device_quirk pwrctl_quirks[] = { 79 BHND_CORE_QUIRK (HWREV_LTE(5), PWRCTL_QUIRK_PCICLK_CTL), 80 BHND_CORE_QUIRK (HWREV_RANGE(6, 9), PWRCTL_QUIRK_SLOWCLK_CTL), 81 BHND_CORE_QUIRK (HWREV_RANGE(10, 19), PWRCTL_QUIRK_INSTACLK_CTL), 82 83 BHND_DEVICE_QUIRK_END 84 }; 85 86 static int 87 bhnd_pwrctl_probe(device_t dev) 88 { 89 const struct bhnd_device *id; 90 struct chipc_caps *ccaps; 91 device_t chipc; 92 93 /* Look for compatible chipc parent */ 94 chipc = device_get_parent(dev); 95 if (device_get_devclass(chipc) != devclass_find("bhnd_chipc")) 96 return (ENXIO); 97 98 if (device_get_driver(chipc) != &bhnd_chipc_driver) 99 return (ENXIO); 100 101 /* Verify chipc capability flags */ 102 ccaps = BHND_CHIPC_GET_CAPS(chipc); 103 if (ccaps->pmu || !ccaps->pwr_ctrl) 104 return (ENXIO); 105 106 /* Check for chipc device match */ 107 id = bhnd_device_lookup(chipc, pwrctl_devices, 108 sizeof(pwrctl_devices[0])); 109 if (id == NULL) 110 return (ENXIO); 111 112 device_set_desc(dev, id->desc); 113 return (BUS_PROBE_NOWILDCARD); 114 } 115 116 static int 117 bhnd_pwrctl_attach(device_t dev) 118 { 119 struct bhnd_pwrctl_softc *sc; 120 const struct bhnd_chipid *cid; 121 struct chipc_softc *chipc_sc; 122 bhnd_devclass_t hostb_class; 123 device_t hostb_dev; 124 int error; 125 126 sc = device_get_softc(dev); 127 128 sc->dev = dev; 129 sc->chipc_dev = device_get_parent(dev); 130 sc->quirks = bhnd_device_quirks(sc->chipc_dev, pwrctl_devices, 131 sizeof(pwrctl_devices[0])); 132 133 /* On devices that lack a slow clock source, HT must always be 134 * enabled. */ 135 hostb_class = BHND_DEVCLASS_INVALID; 136 hostb_dev = bhnd_bus_find_hostb_device(device_get_parent(sc->chipc_dev)); 137 if (hostb_dev != NULL) 138 hostb_class = bhnd_get_class(hostb_dev); 139 140 cid = bhnd_get_chipid(sc->chipc_dev); 141 switch (cid->chip_id) { 142 case BHND_CHIPID_BCM4311: 143 if (cid->chip_rev <= 1 && hostb_class == BHND_DEVCLASS_PCI) 144 sc->quirks |= PWRCTL_QUIRK_FORCE_HT; 145 break; 146 147 case BHND_CHIPID_BCM4321: 148 if (hostb_class == BHND_DEVCLASS_PCIE || 149 hostb_class == BHND_DEVCLASS_PCI) 150 sc->quirks |= PWRCTL_QUIRK_FORCE_HT; 151 break; 152 153 case BHND_CHIPID_BCM4716: 154 if (hostb_class == BHND_DEVCLASS_PCIE) 155 sc->quirks |= PWRCTL_QUIRK_FORCE_HT; 156 break; 157 } 158 159 /* Fetch core register block from ChipCommon parent */ 160 chipc_sc = device_get_softc(sc->chipc_dev); 161 sc->res = chipc_sc->core; 162 163 PWRCTL_LOCK_INIT(sc); 164 STAILQ_INIT(&sc->clkres_list); 165 166 /* Initialize power control */ 167 PWRCTL_LOCK(sc); 168 169 if ((error = bhnd_pwrctl_init(sc))) { 170 PWRCTL_UNLOCK(sc); 171 goto cleanup; 172 } 173 174 /* Apply default clock transitions */ 175 if ((error = bhnd_pwrctl_updateclk(sc, BHND_PWRCTL_WAR_UP))) { 176 PWRCTL_UNLOCK(sc); 177 goto cleanup; 178 } 179 180 PWRCTL_UNLOCK(sc); 181 182 /* Register as the bus PWRCTL provider */ 183 if ((error = bhnd_register_provider(dev, BHND_SERVICE_PWRCTL))) { 184 device_printf(sc->dev, "failed to register PWRCTL with bus : " 185 "%d\n", error); 186 goto cleanup; 187 } 188 189 return (0); 190 191 cleanup: 192 PWRCTL_LOCK_DESTROY(sc); 193 return (error); 194 } 195 196 static int 197 bhnd_pwrctl_detach(device_t dev) 198 { 199 struct bhnd_pwrctl_softc *sc; 200 struct bhnd_pwrctl_clkres *clkres, *crnext; 201 int error; 202 203 sc = device_get_softc(dev); 204 205 if ((error = bhnd_deregister_provider(dev, BHND_SERVICE_ANY))) 206 return (error); 207 208 /* Update clock state */ 209 PWRCTL_LOCK(sc); 210 error = bhnd_pwrctl_updateclk(sc, BHND_PWRCTL_WAR_DOWN); 211 PWRCTL_UNLOCK(sc); 212 if (error) 213 return (error); 214 215 STAILQ_FOREACH_SAFE(clkres, &sc->clkres_list, cr_link, crnext) 216 free(clkres, M_DEVBUF); 217 218 PWRCTL_LOCK_DESTROY(sc); 219 return (0); 220 } 221 222 static int 223 bhnd_pwrctl_suspend(device_t dev) 224 { 225 struct bhnd_pwrctl_softc *sc; 226 int error; 227 228 sc = device_get_softc(dev); 229 230 /* Update clock state */ 231 PWRCTL_LOCK(sc); 232 error = bhnd_pwrctl_updateclk(sc, BHND_PWRCTL_WAR_DOWN); 233 PWRCTL_UNLOCK(sc); 234 235 return (error); 236 } 237 238 static int 239 bhnd_pwrctl_resume(device_t dev) 240 { 241 struct bhnd_pwrctl_softc *sc; 242 int error; 243 244 sc = device_get_softc(dev); 245 246 PWRCTL_LOCK(sc); 247 248 /* Re-initialize power control registers */ 249 if ((error = bhnd_pwrctl_init(sc))) { 250 device_printf(sc->dev, "PWRCTL init failed: %d\n", error); 251 goto cleanup; 252 } 253 254 /* Restore clock state */ 255 if ((error = bhnd_pwrctl_updateclk(sc, BHND_PWRCTL_WAR_UP))) { 256 device_printf(sc->dev, "clock state restore failed: %d\n", 257 error); 258 goto cleanup; 259 } 260 261 cleanup: 262 PWRCTL_UNLOCK(sc); 263 return (error); 264 } 265 266 static int 267 bhnd_pwrctl_get_clock_latency(device_t dev, bhnd_clock clock, 268 u_int *latency) 269 { 270 struct bhnd_pwrctl_softc *sc = device_get_softc(dev); 271 272 switch (clock) { 273 case BHND_CLOCK_HT: 274 PWRCTL_LOCK(sc); 275 *latency = bhnd_pwrctl_fast_pwrup_delay(sc); 276 PWRCTL_UNLOCK(sc); 277 278 return (0); 279 280 default: 281 return (ENODEV); 282 } 283 } 284 285 static int 286 bhnd_pwrctl_get_clock_freq(device_t dev, bhnd_clock clock, u_int *freq) 287 { 288 struct bhnd_pwrctl_softc *sc = device_get_softc(dev); 289 290 switch (clock) { 291 case BHND_CLOCK_ALP: 292 BPMU_LOCK(sc); 293 *freq = bhnd_pwrctl_getclk_speed(sc); 294 BPMU_UNLOCK(sc); 295 296 return (0); 297 298 case BHND_CLOCK_HT: 299 case BHND_CLOCK_ILP: 300 case BHND_CLOCK_DYN: 301 default: 302 return (ENODEV); 303 } 304 } 305 306 /** 307 * Find the clock reservation associated with @p owner, if any. 308 * 309 * @param sc Driver instance state. 310 * @param owner The owning device. 311 */ 312 static struct bhnd_pwrctl_clkres * 313 bhnd_pwrctl_find_res(struct bhnd_pwrctl_softc *sc, device_t owner) 314 { 315 struct bhnd_pwrctl_clkres *clkres; 316 317 PWRCTL_LOCK_ASSERT(sc, MA_OWNED); 318 319 STAILQ_FOREACH(clkres, &sc->clkres_list, cr_link) { 320 if (clkres->owner == owner) 321 return (clkres); 322 } 323 324 /* not found */ 325 return (NULL); 326 } 327 328 /** 329 * Enumerate all active clock requests, compute the minimum required clock, 330 * and issue any required clock transition. 331 * 332 * @param sc Driver instance state. 333 * @param wars Work-around state. 334 */ 335 static int 336 bhnd_pwrctl_updateclk(struct bhnd_pwrctl_softc *sc, bhnd_pwrctl_wars wars) 337 { 338 struct bhnd_pwrctl_clkres *clkres; 339 bhnd_clock clock; 340 341 PWRCTL_LOCK_ASSERT(sc, MA_OWNED); 342 343 /* Nothing to update on fixed clock devices */ 344 if (PWRCTL_QUIRK(sc, FIXED_CLK)) 345 return (0); 346 347 /* Default clock target */ 348 clock = BHND_CLOCK_DYN; 349 350 /* Apply quirk-specific overrides to the clock target */ 351 switch (wars) { 352 case BHND_PWRCTL_WAR_UP: 353 /* Force HT clock */ 354 if (PWRCTL_QUIRK(sc, FORCE_HT)) 355 clock = BHND_CLOCK_HT; 356 break; 357 358 case BHND_PWRCTL_WAR_RUN: 359 /* Cannot transition clock if FORCE_HT */ 360 if (PWRCTL_QUIRK(sc, FORCE_HT)) 361 return (0); 362 break; 363 364 case BHND_PWRCTL_WAR_DOWN: 365 /* Leave default clock unmodified to permit 366 * transition back to BHND_CLOCK_DYN on FORCE_HT devices. */ 367 break; 368 } 369 370 /* Determine required clock */ 371 STAILQ_FOREACH(clkres, &sc->clkres_list, cr_link) 372 clock = bhnd_clock_max(clock, clkres->clock); 373 374 /* Map to supported clock setting */ 375 switch (clock) { 376 case BHND_CLOCK_DYN: 377 case BHND_CLOCK_ILP: 378 clock = BHND_CLOCK_DYN; 379 break; 380 case BHND_CLOCK_ALP: 381 /* In theory FORCE_ALP is supported by the hardware, but 382 * there are currently no known use-cases for it; mapping 383 * to HT is still valid, and allows us to punt on determing 384 * where FORCE_ALP is supported and functional */ 385 clock = BHND_CLOCK_HT; 386 break; 387 case BHND_CLOCK_HT: 388 break; 389 default: 390 device_printf(sc->dev, "unknown clock: %#x\n", clock); 391 return (ENODEV); 392 } 393 394 /* Issue transition */ 395 return (bhnd_pwrctl_setclk(sc, clock)); 396 } 397 398 /* BHND_PWRCTL_REQUEST_CLOCK() */ 399 static int 400 bhnd_pwrctl_request_clock(device_t dev, device_t child, bhnd_clock clock) 401 { 402 struct bhnd_pwrctl_softc *sc; 403 struct bhnd_pwrctl_clkres *clkres; 404 int error; 405 406 sc = device_get_softc(dev); 407 error = 0; 408 409 PWRCTL_LOCK(sc); 410 411 clkres = bhnd_pwrctl_find_res(sc, child); 412 413 /* BHND_CLOCK_DYN discards the clock reservation entirely */ 414 if (clock == BHND_CLOCK_DYN) { 415 /* nothing to clean up? */ 416 if (clkres == NULL) { 417 PWRCTL_UNLOCK(sc); 418 return (0); 419 } 420 421 /* drop reservation and apply clock transition */ 422 STAILQ_REMOVE(&sc->clkres_list, clkres, 423 bhnd_pwrctl_clkres, cr_link); 424 425 if ((error = bhnd_pwrctl_updateclk(sc, BHND_PWRCTL_WAR_RUN))) { 426 device_printf(dev, "clock transition failed: %d\n", 427 error); 428 429 /* restore reservation */ 430 STAILQ_INSERT_TAIL(&sc->clkres_list, clkres, cr_link); 431 432 PWRCTL_UNLOCK(sc); 433 return (error); 434 } 435 436 /* deallocate orphaned reservation */ 437 free(clkres, M_DEVBUF); 438 439 PWRCTL_UNLOCK(sc); 440 return (0); 441 } 442 443 /* create (or update) reservation */ 444 if (clkres == NULL) { 445 clkres = malloc(sizeof(struct bhnd_pwrctl_clkres), M_DEVBUF, 446 M_NOWAIT); 447 if (clkres == NULL) 448 return (ENOMEM); 449 450 clkres->owner = child; 451 clkres->clock = clock; 452 453 STAILQ_INSERT_TAIL(&sc->clkres_list, clkres, cr_link); 454 } else { 455 KASSERT(clkres->owner == child, ("invalid owner")); 456 clkres->clock = clock; 457 } 458 459 /* apply clock transition */ 460 error = bhnd_pwrctl_updateclk(sc, BHND_PWRCTL_WAR_RUN); 461 if (error) { 462 STAILQ_REMOVE(&sc->clkres_list, clkres, bhnd_pwrctl_clkres, 463 cr_link); 464 free(clkres, M_DEVBUF); 465 } 466 467 PWRCTL_UNLOCK(sc); 468 return (error); 469 } 470 471 static device_method_t bhnd_pwrctl_methods[] = { 472 /* Device interface */ 473 DEVMETHOD(device_probe, bhnd_pwrctl_probe), 474 DEVMETHOD(device_attach, bhnd_pwrctl_attach), 475 DEVMETHOD(device_detach, bhnd_pwrctl_detach), 476 DEVMETHOD(device_suspend, bhnd_pwrctl_suspend), 477 DEVMETHOD(device_resume, bhnd_pwrctl_resume), 478 479 /* BHND PWRCTL interface */ 480 DEVMETHOD(bhnd_pwrctl_request_clock, bhnd_pwrctl_request_clock), 481 DEVMETHOD(bhnd_pwrctl_get_clock_freq, bhnd_pwrctl_get_clock_freq), 482 DEVMETHOD(bhnd_pwrctl_get_clock_latency, bhnd_pwrctl_get_clock_latency), 483 484 DEVMETHOD_END 485 }; 486 487 DEFINE_CLASS_0(bhnd_pwrctl, bhnd_pwrctl_driver, bhnd_pwrctl_methods, 488 sizeof(struct bhnd_pwrctl_softc)); 489 EARLY_DRIVER_MODULE(bhnd_pwrctl, bhnd_chipc, bhnd_pwrctl_driver, 490 NULL, NULL, BUS_PASS_TIMER + BUS_PASS_ORDER_MIDDLE); 491 492 MODULE_DEPEND(bhnd_pwrctl, bhnd, 1, 1, 1); 493 MODULE_VERSION(bhnd_pwrctl, 1); 494