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