1 /*- 2 * SPDX-License-Identifier: BSD-2-Clause 3 * 4 * Copyright (c) 2012 5 * Ben Gray <bgray@freebsd.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 * 2. Redistributions in binary form must reproduce the above copyright 14 * notice, this list of conditions and the following disclaimer in the 15 * documentation and/or other materials provided with the distribution. 16 * 17 * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND 18 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20 * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE 21 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 22 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 23 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 24 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 25 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 26 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 27 * SUCH DAMAGE. 28 */ 29 30 #include <sys/cdefs.h> 31 /* 32 * Texas Instruments TWL4030/TWL5030/TWL60x0/TPS659x0 Power Management. 33 * 34 * This driver covers the external clocks, allows for enabling & 35 * disabling their output. 36 * 37 * 38 * 39 * FLATTENED DEVICE TREE (FDT) 40 * Startup override settings can be specified in the FDT, if they are they 41 * should be under the twl parent device and take the following form: 42 * 43 * external-clocks = "name1", "state1", 44 * "name2", "state2", 45 * etc; 46 * 47 * Each override should be a pair, the first entry is the name of the clock 48 * the second is the state to set, possible strings are either "on" or "off". 49 * 50 */ 51 52 #include <sys/param.h> 53 #include <sys/systm.h> 54 #include <sys/kernel.h> 55 #include <sys/lock.h> 56 #include <sys/module.h> 57 #include <sys/bus.h> 58 #include <sys/resource.h> 59 #include <sys/rman.h> 60 #include <sys/sysctl.h> 61 #include <sys/sx.h> 62 #include <sys/malloc.h> 63 64 #include <machine/bus.h> 65 #include <machine/resource.h> 66 #include <machine/intr.h> 67 68 #include <dev/ofw/openfirm.h> 69 #include <dev/ofw/ofw_bus.h> 70 71 #include "twl.h" 72 #include "twl_clks.h" 73 74 static int twl_clks_debug = 1; 75 76 /* 77 * Power Groups bits for the 4030 and 6030 devices 78 */ 79 #define TWL4030_P3_GRP 0x80 /* Peripherals, power group */ 80 #define TWL4030_P2_GRP 0x40 /* Modem power group */ 81 #define TWL4030_P1_GRP 0x20 /* Application power group (FreeBSD control) */ 82 83 #define TWL6030_P3_GRP 0x04 /* Modem power group */ 84 #define TWL6030_P2_GRP 0x02 /* Connectivity power group */ 85 #define TWL6030_P1_GRP 0x01 /* Application power group (FreeBSD control) */ 86 87 /* 88 * Register offsets within a clk regulator register set 89 */ 90 #define TWL_CLKS_GRP 0x00 /* Regulator GRP register */ 91 #define TWL_CLKS_STATE 0x02 /* TWL6030 only */ 92 93 /** 94 * Support voltage regulators for the different IC's 95 */ 96 struct twl_clock { 97 const char *name; 98 uint8_t subdev; 99 uint8_t regbase; 100 }; 101 102 static const struct twl_clock twl4030_clocks[] = { 103 { "32kclkout", 0, 0x8e }, 104 { NULL, 0, 0x00 } 105 }; 106 107 static const struct twl_clock twl6030_clocks[] = { 108 { "clk32kg", 0, 0xbc }, 109 { "clk32kao", 0, 0xb9 }, 110 { "clk32kaudio", 0, 0xbf }, 111 { NULL, 0, 0x00 } 112 }; 113 114 #define TWL_CLKS_MAX_NAMELEN 32 115 116 struct twl_clk_entry { 117 LIST_ENTRY(twl_clk_entry) link; 118 struct sysctl_oid *oid; 119 char name[TWL_CLKS_MAX_NAMELEN]; 120 uint8_t sub_dev; /* the sub-device number for the clock */ 121 uint8_t reg_off; /* register base address of the clock */ 122 }; 123 124 struct twl_clks_softc { 125 device_t sc_dev; /* twl_clk device */ 126 device_t sc_pdev; /* parent device (twl) */ 127 struct sx sc_sx; /* internal locking */ 128 struct intr_config_hook sc_init_hook; 129 LIST_HEAD(twl_clk_list, twl_clk_entry) sc_clks_list; 130 }; 131 132 /** 133 * Macros for driver shared locking 134 */ 135 #define TWL_CLKS_XLOCK(_sc) sx_xlock(&(_sc)->sc_sx) 136 #define TWL_CLKS_XUNLOCK(_sc) sx_xunlock(&(_sc)->sc_sx) 137 #define TWL_CLKS_SLOCK(_sc) sx_slock(&(_sc)->sc_sx) 138 #define TWL_CLKS_SUNLOCK(_sc) sx_sunlock(&(_sc)->sc_sx) 139 #define TWL_CLKS_LOCK_INIT(_sc) sx_init(&(_sc)->sc_sx, "twl_clks") 140 #define TWL_CLKS_LOCK_DESTROY(_sc) sx_destroy(&(_sc)->sc_sx); 141 142 #define TWL_CLKS_ASSERT_LOCKED(_sc) sx_assert(&(_sc)->sc_sx, SA_LOCKED); 143 144 #define TWL_CLKS_LOCK_UPGRADE(_sc) \ 145 do { \ 146 while (!sx_try_upgrade(&(_sc)->sc_sx)) \ 147 pause("twl_clks_ex", (hz / 100)); \ 148 } while(0) 149 #define TWL_CLKS_LOCK_DOWNGRADE(_sc) sx_downgrade(&(_sc)->sc_sx); 150 151 /** 152 * twl_clks_read_1 - read single register from the TWL device 153 * twl_clks_write_1 - writes a single register in the TWL device 154 * @sc: device context 155 * @clk: the clock device we're reading from / writing to 156 * @off: offset within the clock's register set 157 * @val: the value to write or a pointer to a variable to store the result 158 * 159 * RETURNS: 160 * Zero on success or an error code on failure. 161 */ 162 static inline int 163 twl_clks_read_1(struct twl_clks_softc *sc, struct twl_clk_entry *clk, 164 uint8_t off, uint8_t *val) 165 { 166 return (twl_read(sc->sc_pdev, clk->sub_dev, clk->reg_off + off, val, 1)); 167 } 168 169 static inline int 170 twl_clks_write_1(struct twl_clks_softc *sc, struct twl_clk_entry *clk, 171 uint8_t off, uint8_t val) 172 { 173 return (twl_write(sc->sc_pdev, clk->sub_dev, clk->reg_off + off, &val, 1)); 174 } 175 176 /** 177 * twl_clks_is_enabled - determines if a clock is enabled 178 * @dev: TWL CLK device 179 * @name: the name of the clock 180 * @enabled: upon return will contain the 'enabled' state 181 * 182 * LOCKING: 183 * Internally the function takes and releases the TWL lock. 184 * 185 * RETURNS: 186 * Zero on success or a negative error code on failure. 187 */ 188 int 189 twl_clks_is_enabled(device_t dev, const char *name, int *enabled) 190 { 191 struct twl_clks_softc *sc = device_get_softc(dev); 192 struct twl_clk_entry *clk; 193 int found = 0; 194 int err; 195 uint8_t grp, state; 196 197 TWL_CLKS_SLOCK(sc); 198 199 LIST_FOREACH(clk, &sc->sc_clks_list, link) { 200 if (strcmp(clk->name, name) == 0) { 201 found = 1; 202 break; 203 } 204 } 205 206 if (!found) { 207 TWL_CLKS_SUNLOCK(sc); 208 return (EINVAL); 209 } 210 211 if (twl_is_4030(sc->sc_pdev)) { 212 err = twl_clks_read_1(sc, clk, TWL_CLKS_GRP, &grp); 213 if (!err) 214 *enabled = (grp & TWL4030_P1_GRP); 215 216 } else if (twl_is_6030(sc->sc_pdev) || twl_is_6025(sc->sc_pdev)) { 217 TWL_CLKS_LOCK_UPGRADE(sc); 218 219 /* Check the clock is in the application group */ 220 if (twl_is_6030(sc->sc_pdev)) { 221 err = twl_clks_read_1(sc, clk, TWL_CLKS_GRP, &grp); 222 if (err) { 223 TWL_CLKS_LOCK_DOWNGRADE(sc); 224 goto done; 225 } 226 227 if (!(grp & TWL6030_P1_GRP)) { 228 TWL_CLKS_LOCK_DOWNGRADE(sc); 229 *enabled = 0; /* disabled */ 230 goto done; 231 } 232 } 233 234 /* Read the application mode state and verify it's ON */ 235 err = twl_clks_read_1(sc, clk, TWL_CLKS_STATE, &state); 236 if (!err) 237 *enabled = ((state & 0x0C) == 0x04); 238 239 TWL_CLKS_LOCK_DOWNGRADE(sc); 240 241 } else { 242 err = EINVAL; 243 } 244 245 done: 246 TWL_CLKS_SUNLOCK(sc); 247 return (err); 248 } 249 250 /** 251 * twl_clks_set_state - enables/disables a clock output 252 * @sc: device context 253 * @clk: the clock entry to enable/disable 254 * @enable: non-zero the clock is enabled, zero the clock is disabled 255 * 256 * LOCKING: 257 * The TWL CLK lock must be held before this function is called. 258 * 259 * RETURNS: 260 * Zero on success or an error code on failure. 261 */ 262 static int 263 twl_clks_set_state(struct twl_clks_softc *sc, struct twl_clk_entry *clk, 264 int enable) 265 { 266 int xlocked; 267 int err; 268 uint8_t grp; 269 270 TWL_CLKS_ASSERT_LOCKED(sc); 271 272 /* Upgrade the lock to exclusive because about to perform read-mod-write */ 273 xlocked = sx_xlocked(&sc->sc_sx); 274 if (!xlocked) 275 TWL_CLKS_LOCK_UPGRADE(sc); 276 277 err = twl_clks_read_1(sc, clk, TWL_CLKS_GRP, &grp); 278 if (err) 279 goto done; 280 281 if (twl_is_4030(sc->sc_pdev)) { 282 /* On the TWL4030 we just need to ensure the clock is in the right 283 * power domain, don't need to turn on explicitly like TWL6030. 284 */ 285 if (enable) 286 grp |= TWL4030_P1_GRP; 287 else 288 grp &= ~(TWL4030_P1_GRP | TWL4030_P2_GRP | TWL4030_P3_GRP); 289 290 err = twl_clks_write_1(sc, clk, TWL_CLKS_GRP, grp); 291 292 } else if (twl_is_6030(sc->sc_pdev) || twl_is_6025(sc->sc_pdev)) { 293 /* Make sure the clock belongs to at least the APP power group */ 294 if (twl_is_6030(sc->sc_pdev) && !(grp & TWL6030_P1_GRP)) { 295 grp |= TWL6030_P1_GRP; 296 err = twl_clks_write_1(sc, clk, TWL_CLKS_GRP, grp); 297 if (err) 298 goto done; 299 } 300 301 /* On TWL6030 we need to make sure we disable power for all groups */ 302 if (twl_is_6030(sc->sc_pdev)) 303 grp = TWL6030_P1_GRP | TWL6030_P2_GRP | TWL6030_P3_GRP; 304 else 305 grp = 0x00; 306 307 /* Set the state of the clock */ 308 if (enable) 309 err = twl_clks_write_1(sc, clk, TWL_CLKS_STATE, (grp << 5) | 0x01); 310 else 311 err = twl_clks_write_1(sc, clk, TWL_CLKS_STATE, (grp << 5)); 312 313 } else { 314 315 err = EINVAL; 316 } 317 318 done: 319 if (!xlocked) 320 TWL_CLKS_LOCK_DOWNGRADE(sc); 321 322 if ((twl_clks_debug > 1) && !err) 323 device_printf(sc->sc_dev, "%s : %sabled\n", clk->name, 324 enable ? "en" : "dis"); 325 326 return (err); 327 } 328 329 /** 330 * twl_clks_disable - disables a clock output 331 * @dev: TWL clk device 332 * @name: the name of the clock 333 * 334 * LOCKING: 335 * Internally the function takes and releases the TWL lock. 336 * 337 * RETURNS: 338 * Zero on success or an error code on failure. 339 */ 340 int 341 twl_clks_disable(device_t dev, const char *name) 342 { 343 struct twl_clks_softc *sc = device_get_softc(dev); 344 struct twl_clk_entry *clk; 345 int err = EINVAL; 346 347 TWL_CLKS_SLOCK(sc); 348 349 LIST_FOREACH(clk, &sc->sc_clks_list, link) { 350 if (strcmp(clk->name, name) == 0) { 351 err = twl_clks_set_state(sc, clk, 0); 352 break; 353 } 354 } 355 356 TWL_CLKS_SUNLOCK(sc); 357 return (err); 358 } 359 360 /** 361 * twl_clks_enable - enables a clock output 362 * @dev: TWL clk device 363 * @name: the name of the clock 364 * 365 * LOCKING: 366 * Internally the function takes and releases the TWL CLKS lock. 367 * 368 * RETURNS: 369 * Zero on success or an error code on failure. 370 */ 371 int 372 twl_clks_enable(device_t dev, const char *name) 373 { 374 struct twl_clks_softc *sc = device_get_softc(dev); 375 struct twl_clk_entry *clk; 376 int err = EINVAL; 377 378 TWL_CLKS_SLOCK(sc); 379 380 LIST_FOREACH(clk, &sc->sc_clks_list, link) { 381 if (strcmp(clk->name, name) == 0) { 382 err = twl_clks_set_state(sc, clk, 1); 383 break; 384 } 385 } 386 387 TWL_CLKS_SUNLOCK(sc); 388 return (err); 389 } 390 391 /** 392 * twl_clks_sysctl_clock - reads the state of the clock 393 * @SYSCTL_HANDLER_ARGS: arguments for the callback 394 * 395 * Returns the clock status; disabled is zero and enabled is non-zero. 396 * 397 * LOCKING: 398 * It's expected the TWL lock is held while this function is called. 399 * 400 * RETURNS: 401 * EIO if device is not present, otherwise 0 is returned. 402 */ 403 static int 404 twl_clks_sysctl_clock(SYSCTL_HANDLER_ARGS) 405 { 406 struct twl_clks_softc *sc = (struct twl_clks_softc*)arg1; 407 int err; 408 int enabled = 0; 409 410 if ((err = twl_clks_is_enabled(sc->sc_dev, oidp->oid_name, &enabled)) != 0) 411 return err; 412 413 return sysctl_handle_int(oidp, &enabled, 0, req); 414 } 415 416 /** 417 * twl_clks_add_clock - adds single clock sysctls for the device 418 * @sc: device soft context 419 * @name: the name of the regulator 420 * @nsub: the number of the subdevice 421 * @regbase: the base address of the clocks registers 422 * 423 * Adds a single clock to the device and also a sysctl interface for 424 * querying it's status. 425 * 426 * LOCKING: 427 * It's expected the exclusive lock is held while this function is called. 428 * 429 * RETURNS: 430 * Pointer to the new clock entry on success, otherwise NULL on failure. 431 */ 432 static struct twl_clk_entry* 433 twl_clks_add_clock(struct twl_clks_softc *sc, const char *name, 434 uint8_t nsub, uint8_t regbase) 435 { 436 struct sysctl_ctx_list *ctx = device_get_sysctl_ctx(sc->sc_dev); 437 struct sysctl_oid *tree = device_get_sysctl_tree(sc->sc_dev); 438 struct twl_clk_entry *new; 439 440 TWL_CLKS_ASSERT_LOCKED(sc); 441 442 new = malloc(sizeof(struct twl_clk_entry), M_DEVBUF, M_NOWAIT | M_ZERO); 443 if (new == NULL) 444 return (NULL); 445 446 strncpy(new->name, name, TWL_CLKS_MAX_NAMELEN); 447 new->name[TWL_CLKS_MAX_NAMELEN - 1] = '\0'; 448 449 new->sub_dev = nsub; 450 new->reg_off = regbase; 451 452 /* Add a sysctl entry for the clock */ 453 new->oid = SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(tree), OID_AUTO, name, 454 CTLTYPE_INT | CTLFLAG_RD | CTLFLAG_NEEDGIANT, sc, 0, 455 twl_clks_sysctl_clock, "I", "external clock"); 456 457 /* Finally add the regulator to list of supported regulators */ 458 LIST_INSERT_HEAD(&sc->sc_clks_list, new, link); 459 460 return (new); 461 } 462 463 /** 464 * twl_clks_add_clocks - populates the internal list of clocks 465 * @sc: device soft context 466 * @chip: the name of the chip used in the hints 467 * @clks the list of clocks supported by the device 468 * 469 * Loops over the list of clocks and adds them to the device context. Also 470 * scans the FDT to determine if there are any clocks that should be 471 * enabled/disabled automatically. 472 * 473 * LOCKING: 474 * Internally takes the exclusive lock while adding the clocks to the 475 * device context. 476 * 477 * RETURNS: 478 * Always returns 0. 479 */ 480 static int 481 twl_clks_add_clocks(struct twl_clks_softc *sc, const struct twl_clock *clks) 482 { 483 int err; 484 const struct twl_clock *walker; 485 struct twl_clk_entry *entry; 486 phandle_t child; 487 char rnames[256]; 488 char *name, *state; 489 int len = 0, prop_len; 490 int enable; 491 492 TWL_CLKS_XLOCK(sc); 493 494 /* Add the regulators from the list */ 495 walker = &clks[0]; 496 while (walker->name != NULL) { 497 /* Add the regulator to the list */ 498 entry = twl_clks_add_clock(sc, walker->name, walker->subdev, 499 walker->regbase); 500 if (entry == NULL) 501 continue; 502 503 walker++; 504 } 505 506 /* Check for any FDT settings that need to be applied */ 507 child = ofw_bus_get_node(sc->sc_pdev); 508 if (child) { 509 prop_len = OF_getprop(child, "external-clocks", rnames, sizeof(rnames)); 510 while (len < prop_len) { 511 name = rnames + len; 512 len += strlen(name) + 1; 513 if ((len >= prop_len) || (name[0] == '\0')) 514 break; 515 516 state = rnames + len; 517 len += strlen(state) + 1; 518 if (state[0] == '\0') 519 break; 520 521 enable = !strncmp(state, "on", 2); 522 523 LIST_FOREACH(entry, &sc->sc_clks_list, link) { 524 if (strcmp(entry->name, name) == 0) { 525 twl_clks_set_state(sc, entry, enable); 526 break; 527 } 528 } 529 } 530 } 531 532 TWL_CLKS_XUNLOCK(sc); 533 534 if (twl_clks_debug) { 535 LIST_FOREACH(entry, &sc->sc_clks_list, link) { 536 err = twl_clks_is_enabled(sc->sc_dev, entry->name, &enable); 537 if (!err) 538 device_printf(sc->sc_dev, "%s : %s\n", entry->name, 539 enable ? "on" : "off"); 540 } 541 } 542 543 return (0); 544 } 545 546 /** 547 * twl_clks_init - initialises the list of clocks 548 * @dev: the twl_clks device 549 * 550 * This function is called as an intrhook once interrupts have been enabled, 551 * this is done so that the driver has the option to enable/disable a clock 552 * based on settings providied in the FDT. 553 * 554 * LOCKING: 555 * May takes the exclusive lock in the function. 556 */ 557 static void 558 twl_clks_init(void *dev) 559 { 560 struct twl_clks_softc *sc; 561 562 sc = device_get_softc((device_t)dev); 563 564 if (twl_is_4030(sc->sc_pdev)) 565 twl_clks_add_clocks(sc, twl4030_clocks); 566 else if (twl_is_6030(sc->sc_pdev) || twl_is_6025(sc->sc_pdev)) 567 twl_clks_add_clocks(sc, twl6030_clocks); 568 569 config_intrhook_disestablish(&sc->sc_init_hook); 570 } 571 572 static int 573 twl_clks_probe(device_t dev) 574 { 575 if (twl_is_4030(device_get_parent(dev))) 576 device_set_desc(dev, "TI TWL4030 PMIC External Clocks"); 577 else if (twl_is_6025(device_get_parent(dev)) || 578 twl_is_6030(device_get_parent(dev))) 579 device_set_desc(dev, "TI TWL6025/TWL6030 PMIC External Clocks"); 580 else 581 return (ENXIO); 582 583 return (0); 584 } 585 586 static int 587 twl_clks_attach(device_t dev) 588 { 589 struct twl_clks_softc *sc; 590 591 sc = device_get_softc(dev); 592 sc->sc_dev = dev; 593 sc->sc_pdev = device_get_parent(dev); 594 595 TWL_CLKS_LOCK_INIT(sc); 596 597 LIST_INIT(&sc->sc_clks_list); 598 599 sc->sc_init_hook.ich_func = twl_clks_init; 600 sc->sc_init_hook.ich_arg = dev; 601 602 if (config_intrhook_establish(&sc->sc_init_hook) != 0) 603 return (ENOMEM); 604 605 return (0); 606 } 607 608 static int 609 twl_clks_detach(device_t dev) 610 { 611 struct twl_clks_softc *sc; 612 struct twl_clk_entry *clk; 613 struct twl_clk_entry *tmp; 614 615 sc = device_get_softc(dev); 616 617 TWL_CLKS_XLOCK(sc); 618 619 LIST_FOREACH_SAFE(clk, &sc->sc_clks_list, link, tmp) { 620 LIST_REMOVE(clk, link); 621 sysctl_remove_oid(clk->oid, 1, 0); 622 free(clk, M_DEVBUF); 623 } 624 625 TWL_CLKS_XUNLOCK(sc); 626 627 TWL_CLKS_LOCK_DESTROY(sc); 628 629 return (0); 630 } 631 632 static device_method_t twl_clks_methods[] = { 633 DEVMETHOD(device_probe, twl_clks_probe), 634 DEVMETHOD(device_attach, twl_clks_attach), 635 DEVMETHOD(device_detach, twl_clks_detach), 636 637 {0, 0}, 638 }; 639 640 static driver_t twl_clks_driver = { 641 "twl_clks", 642 twl_clks_methods, 643 sizeof(struct twl_clks_softc), 644 }; 645 646 DRIVER_MODULE(twl_clks, twl, twl_clks_driver, 0, 0); 647 MODULE_VERSION(twl_clks, 1); 648