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