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