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