twl_clks.c revision 259329
1118611Snjl/*- 2118611Snjl * Copyright (c) 2012 3118611Snjl * Ben Gray <bgray@freebsd.org>. 4118611Snjl * All rights reserved. 5118611Snjl * 6118611Snjl * Redistribution and use in source and binary forms, with or without 7217365Sjkim * modification, are permitted provided that the following conditions 8281075Sdim * are met: 9118611Snjl * 1. Redistributions of source code must retain the above copyright 10118611Snjl * notice, this list of conditions and the following disclaimer. 11217365Sjkim * 2. Redistributions in binary form must reproduce the above copyright 12217365Sjkim * notice, this list of conditions and the following disclaimer in the 13217365Sjkim * documentation and/or other materials provided with the distribution. 14217365Sjkim * 15217365Sjkim * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND 16217365Sjkim * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 17217365Sjkim * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 18217365Sjkim * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE 19217365Sjkim * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 20217365Sjkim * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 21217365Sjkim * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 22217365Sjkim * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 23217365Sjkim * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 24217365Sjkim * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 25118611Snjl * SUCH DAMAGE. 26217365Sjkim */ 27217365Sjkim 28217365Sjkim#include <sys/cdefs.h> 29118611Snjl__FBSDID("$FreeBSD: stable/10/sys/arm/ti/twl/twl_clks.c 259329 2013-12-13 20:43:11Z ian $"); 30217365Sjkim 31217365Sjkim/* 32217365Sjkim * Texas Instruments TWL4030/TWL5030/TWL60x0/TPS659x0 Power Management. 33217365Sjkim * 34217365Sjkim * This driver covers the external clocks, allows for enabling & 35217365Sjkim * disabling their output. 36217365Sjkim * 37217365Sjkim * 38217365Sjkim * 39217365Sjkim * FLATTENED DEVICE TREE (FDT) 40217365Sjkim * Startup override settings can be specified in the FDT, if they are they 41217365Sjkim * should be under the twl parent device and take the following form: 42217365Sjkim * 43118611Snjl * external-clocks = "name1", "state1", 44151937Sjkim * "name2", "state2", 45118611Snjl * etc; 46193529Sjkim * 47118611Snjl * Each override should be a pair, the first entry is the name of the clock 48118611Snjl * the second is the state to set, possible strings are either "on" or "off". 49118611Snjl * 50118611Snjl */ 51118611Snjl 52151937Sjkim#include <sys/param.h> 53118611Snjl#include <sys/systm.h> 54151937Sjkim#include <sys/kernel.h> 55151937Sjkim#include <sys/lock.h> 56151937Sjkim#include <sys/module.h> 57151937Sjkim#include <sys/bus.h> 58151937Sjkim#include <sys/resource.h> 59151937Sjkim#include <sys/rman.h> 60151937Sjkim#include <sys/sysctl.h> 61151937Sjkim#include <sys/sx.h> 62151937Sjkim#include <sys/malloc.h> 63151937Sjkim 64151937Sjkim#include <machine/bus.h> 65151937Sjkim#include <machine/cpu.h> 66151937Sjkim#include <machine/cpufunc.h> 67151937Sjkim#include <machine/resource.h> 68151937Sjkim#include <machine/intr.h> 69151937Sjkim 70151937Sjkim#include <dev/ofw/openfirm.h> 71151937Sjkim#include <dev/ofw/ofw_bus.h> 72118611Snjl 73118611Snjl#include "twl.h" 74118611Snjl#include "twl_clks.h" 75118611Snjl 76118611Snjl 77118611Snjlstatic int twl_clks_debug = 1; 78118611Snjl 79118611Snjl 80118611Snjl/* 81241973Sjkim * Power Groups bits for the 4030 and 6030 devices 82118611Snjl */ 83118611Snjl#define TWL4030_P3_GRP 0x80 /* Peripherals, power group */ 84118611Snjl#define TWL4030_P2_GRP 0x40 /* Modem power group */ 85118611Snjl#define TWL4030_P1_GRP 0x20 /* Application power group (FreeBSD control) */ 86118611Snjl 87118611Snjl#define TWL6030_P3_GRP 0x04 /* Modem power group */ 88118611Snjl#define TWL6030_P2_GRP 0x02 /* Connectivity power group */ 89118611Snjl#define TWL6030_P1_GRP 0x01 /* Application power group (FreeBSD control) */ 90118611Snjl 91118611Snjl/* 92118611Snjl * Register offsets within a clk regulator register set 93118611Snjl */ 94118611Snjl#define TWL_CLKS_GRP 0x00 /* Regulator GRP register */ 95118611Snjl#define TWL_CLKS_STATE 0x02 /* TWL6030 only */ 96118611Snjl 97118611Snjl 98118611Snjl 99118611Snjl/** 100118611Snjl * Support voltage regulators for the different IC's 101118611Snjl */ 102118611Snjlstruct twl_clock { 103118611Snjl const char *name; 104118611Snjl uint8_t subdev; 105118611Snjl uint8_t regbase; 106118611Snjl}; 107118611Snjl 108118611Snjlstatic const struct twl_clock twl4030_clocks[] = { 109118611Snjl { "32kclkout", 0, 0x8e }, 110118611Snjl { NULL, 0, 0x00 } 111118611Snjl}; 112118611Snjl 113241973Sjkimstatic const struct twl_clock twl6030_clocks[] = { 114118611Snjl { "clk32kg", 0, 0xbc }, 115118611Snjl { "clk32kao", 0, 0xb9 }, 116118611Snjl { "clk32kaudio", 0, 0xbf }, 117118611Snjl { NULL, 0, 0x00 } 118118611Snjl}; 119118611Snjl 120118611Snjl#define TWL_CLKS_MAX_NAMELEN 32 121118611Snjl 122118611Snjlstruct twl_clk_entry { 123118611Snjl LIST_ENTRY(twl_clk_entry) link; 124118611Snjl struct sysctl_oid *oid; 125118611Snjl char name[TWL_CLKS_MAX_NAMELEN]; 126118611Snjl uint8_t sub_dev; /* the sub-device number for the clock */ 127118611Snjl uint8_t reg_off; /* register base address of the clock */ 128118611Snjl}; 129118611Snjl 130118611Snjlstruct twl_clks_softc { 131118611Snjl device_t sc_dev; /* twl_clk device */ 132118611Snjl device_t sc_pdev; /* parent device (twl) */ 133118611Snjl struct sx sc_sx; /* internal locking */ 134118611Snjl struct intr_config_hook sc_init_hook; 135118611Snjl LIST_HEAD(twl_clk_list, twl_clk_entry) sc_clks_list; 136118611Snjl}; 137118611Snjl 138118611Snjl/** 139118611Snjl * Macros for driver shared locking 140118611Snjl */ 141118611Snjl#define TWL_CLKS_XLOCK(_sc) sx_xlock(&(_sc)->sc_sx) 142118611Snjl#define TWL_CLKS_XUNLOCK(_sc) sx_xunlock(&(_sc)->sc_sx) 143118611Snjl#define TWL_CLKS_SLOCK(_sc) sx_slock(&(_sc)->sc_sx) 144118611Snjl#define TWL_CLKS_SUNLOCK(_sc) sx_sunlock(&(_sc)->sc_sx) 145118611Snjl#define TWL_CLKS_LOCK_INIT(_sc) sx_init(&(_sc)->sc_sx, "twl_clks") 146118611Snjl#define TWL_CLKS_LOCK_DESTROY(_sc) sx_destroy(&(_sc)->sc_sx); 147118611Snjl 148151937Sjkim#define TWL_CLKS_ASSERT_LOCKED(_sc) sx_assert(&(_sc)->sc_sx, SA_LOCKED); 149118611Snjl 150118611Snjl#define TWL_CLKS_LOCK_UPGRADE(_sc) \ 151118611Snjl do { \ 152118611Snjl while (!sx_try_upgrade(&(_sc)->sc_sx)) \ 153118611Snjl pause("twl_clks_ex", (hz / 100)); \ 154118611Snjl } while(0) 155118611Snjl#define TWL_CLKS_LOCK_DOWNGRADE(_sc) sx_downgrade(&(_sc)->sc_sx); 156118611Snjl 157118611Snjl 158151937Sjkim 159118611Snjl 160118611Snjl/** 161118611Snjl * twl_clks_read_1 - read single register from the TWL device 162118611Snjl * twl_clks_write_1 - writes a single register in the TWL device 163118611Snjl * @sc: device context 164118611Snjl * @clk: the clock device we're reading from / writing to 165118611Snjl * @off: offset within the clock's register set 166118611Snjl * @val: the value to write or a pointer to a variable to store the result 167118611Snjl * 168118611Snjl * RETURNS: 169118611Snjl * Zero on success or an error code on failure. 170118611Snjl */ 171118611Snjlstatic inline int 172118611Snjltwl_clks_read_1(struct twl_clks_softc *sc, struct twl_clk_entry *clk, 173118611Snjl uint8_t off, uint8_t *val) 174118611Snjl{ 175118611Snjl return (twl_read(sc->sc_pdev, clk->sub_dev, clk->reg_off + off, val, 1)); 176118611Snjl} 177118611Snjl 178118611Snjlstatic inline int 179118611Snjltwl_clks_write_1(struct twl_clks_softc *sc, struct twl_clk_entry *clk, 180118611Snjl uint8_t off, uint8_t val) 181118611Snjl{ 182118611Snjl return (twl_write(sc->sc_pdev, clk->sub_dev, clk->reg_off + off, &val, 1)); 183118611Snjl} 184118611Snjl 185118611Snjl 186118611Snjl/** 187118611Snjl * twl_clks_is_enabled - determines if a clock is enabled 188118611Snjl * @dev: TWL CLK device 189118611Snjl * @name: the name of the clock 190118611Snjl * @enabled: upon return will contain the 'enabled' state 191118611Snjl * 192118611Snjl * LOCKING: 193118611Snjl * Internally the function takes and releases the TWL lock. 194118611Snjl * 195118611Snjl * RETURNS: 196118611Snjl * Zero on success or a negative error code on failure. 197118611Snjl */ 198118611Snjlint 199118611Snjltwl_clks_is_enabled(device_t dev, const char *name, int *enabled) 200151937Sjkim{ 201118611Snjl struct twl_clks_softc *sc = device_get_softc(dev); 202118611Snjl struct twl_clk_entry *clk; 203118611Snjl int found = 0; 204118611Snjl int err; 205118611Snjl uint8_t grp, state; 206118611Snjl 207118611Snjl TWL_CLKS_SLOCK(sc); 208118611Snjl 209118611Snjl LIST_FOREACH(clk, &sc->sc_clks_list, link) { 210151937Sjkim if (strcmp(clk->name, name) == 0) { 211118611Snjl found = 1; 212118611Snjl break; 213118611Snjl } 214118611Snjl } 215118611Snjl 216118611Snjl if (!found) { 217118611Snjl TWL_CLKS_SUNLOCK(sc); 218118611Snjl return (EINVAL); 219118611Snjl } 220118611Snjl 221118611Snjl 222118611Snjl if (twl_is_4030(sc->sc_pdev)) { 223118611Snjl 224118611Snjl err = twl_clks_read_1(sc, clk, TWL_CLKS_GRP, &grp); 225118611Snjl if (!err) 226118611Snjl *enabled = (grp & TWL4030_P1_GRP); 227118611Snjl 228118611Snjl } else if (twl_is_6030(sc->sc_pdev) || twl_is_6025(sc->sc_pdev)) { 229118611Snjl 230118611Snjl TWL_CLKS_LOCK_UPGRADE(sc); 231151937Sjkim 232151937Sjkim /* Check the clock is in the application group */ 233118611Snjl if (twl_is_6030(sc->sc_pdev)) { 234118611Snjl err = twl_clks_read_1(sc, clk, TWL_CLKS_GRP, &grp); 235118611Snjl if (err) { 236118611Snjl TWL_CLKS_LOCK_DOWNGRADE(sc); 237118611Snjl goto done; 238118611Snjl } 239118611Snjl 240118611Snjl if (!(grp & TWL6030_P1_GRP)) { 241118611Snjl TWL_CLKS_LOCK_DOWNGRADE(sc); 242118611Snjl *enabled = 0; /* disabled */ 243118611Snjl goto done; 244118611Snjl } 245118611Snjl } 246118611Snjl 247118611Snjl /* Read the application mode state and verify it's ON */ 248118611Snjl err = twl_clks_read_1(sc, clk, TWL_CLKS_STATE, &state); 249118611Snjl if (!err) 250118611Snjl *enabled = ((state & 0x0C) == 0x04); 251118611Snjl 252118611Snjl TWL_CLKS_LOCK_DOWNGRADE(sc); 253118611Snjl 254118611Snjl } else { 255118611Snjl err = EINVAL; 256118611Snjl } 257118611Snjl 258118611Snjldone: 259118611Snjl TWL_CLKS_SUNLOCK(sc); 260250838Sjkim return (err); 261118611Snjl} 262118611Snjl 263118611Snjl 264118611Snjl/** 265118611Snjl * twl_clks_set_state - enables/disables a clock output 266118611Snjl * @sc: device context 267118611Snjl * @clk: the clock entry to enable/disable 268118611Snjl * @enable: non-zero the clock is enabled, zero the clock is disabled 269118611Snjl * 270118611Snjl * LOCKING: 271118611Snjl * The TWL CLK lock must be held before this function is called. 272118611Snjl * 273118611Snjl * RETURNS: 274118611Snjl * Zero on success or an error code on failure. 275118611Snjl */ 276118611Snjlstatic int 277118611Snjltwl_clks_set_state(struct twl_clks_softc *sc, struct twl_clk_entry *clk, 278118611Snjl int enable) 279118611Snjl{ 280118611Snjl int xlocked; 281118611Snjl int err; 282118611Snjl uint8_t grp; 283118611Snjl 284118611Snjl TWL_CLKS_ASSERT_LOCKED(sc); 285118611Snjl 286118611Snjl /* Upgrade the lock to exclusive because about to perform read-mod-write */ 287118611Snjl xlocked = sx_xlocked(&sc->sc_sx); 288118611Snjl if (!xlocked) 289118611Snjl TWL_CLKS_LOCK_UPGRADE(sc); 290118611Snjl 291118611Snjl err = twl_clks_read_1(sc, clk, TWL_CLKS_GRP, &grp); 292118611Snjl if (err) 293118611Snjl goto done; 294118611Snjl 295118611Snjl if (twl_is_4030(sc->sc_pdev)) { 296118611Snjl 297118611Snjl /* On the TWL4030 we just need to ensure the clock is in the right 298118611Snjl * power domain, don't need to turn on explicitly like TWL6030. 299118611Snjl */ 300118611Snjl if (enable) 301118611Snjl grp |= TWL4030_P1_GRP; 302118611Snjl else 303118611Snjl grp &= ~(TWL4030_P1_GRP | TWL4030_P2_GRP | TWL4030_P3_GRP); 304118611Snjl 305118611Snjl err = twl_clks_write_1(sc, clk, TWL_CLKS_GRP, grp); 306118611Snjl 307118611Snjl } else if (twl_is_6030(sc->sc_pdev) || twl_is_6025(sc->sc_pdev)) { 308118611Snjl 309118611Snjl /* Make sure the clock belongs to at least the APP power group */ 310118611Snjl if (twl_is_6030(sc->sc_pdev) && !(grp & TWL6030_P1_GRP)) { 311118611Snjl grp |= TWL6030_P1_GRP; 312118611Snjl err = twl_clks_write_1(sc, clk, TWL_CLKS_GRP, grp); 313118611Snjl if (err) 314118611Snjl goto done; 315118611Snjl } 316118611Snjl 317118611Snjl /* On TWL6030 we need to make sure we disable power for all groups */ 318118611Snjl if (twl_is_6030(sc->sc_pdev)) 319118611Snjl grp = TWL6030_P1_GRP | TWL6030_P2_GRP | TWL6030_P3_GRP; 320118611Snjl else 321118611Snjl grp = 0x00; 322118611Snjl 323118611Snjl /* Set the state of the clock */ 324118611Snjl if (enable) 325118611Snjl err = twl_clks_write_1(sc, clk, TWL_CLKS_STATE, (grp << 5) | 0x01); 326118611Snjl else 327118611Snjl err = twl_clks_write_1(sc, clk, TWL_CLKS_STATE, (grp << 5)); 328118611Snjl 329250838Sjkim } else { 330118611Snjl 331118611Snjl err = EINVAL; 332118611Snjl } 333118611Snjl 334118611Snjldone: 335118611Snjl if (!xlocked) 336118611Snjl TWL_CLKS_LOCK_DOWNGRADE(sc); 337151937Sjkim 338151937Sjkim if ((twl_clks_debug > 1) && !err) 339118611Snjl device_printf(sc->sc_dev, "%s : %sabled\n", clk->name, 340118611Snjl enable ? "en" : "dis"); 341118611Snjl 342118611Snjl return (err); 343118611Snjl} 344118611Snjl 345118611Snjl 346118611Snjl/** 347118611Snjl * twl_clks_disable - disables a clock output 348118611Snjl * @dev: TWL clk device 349118611Snjl* @name: the name of the clock 350118611Snjl * 351118611Snjl * LOCKING: 352118611Snjl * Internally the function takes and releases the TWL lock. 353118611Snjl * 354118611Snjl * RETURNS: 355118611Snjl* Zero on success or an error code on failure. 356118611Snjl */ 357118611Snjlint 358118611Snjltwl_clks_disable(device_t dev, const char *name) 359118611Snjl{ 360118611Snjl struct twl_clks_softc *sc = device_get_softc(dev); 361118611Snjl struct twl_clk_entry *clk; 362118611Snjl int err = EINVAL; 363118611Snjl 364118611Snjl TWL_CLKS_SLOCK(sc); 365118611Snjl 366118611Snjl LIST_FOREACH(clk, &sc->sc_clks_list, link) { 367118611Snjl if (strcmp(clk->name, name) == 0) { 368118611Snjl err = twl_clks_set_state(sc, clk, 0); 369118611Snjl break; 370118611Snjl } 371118611Snjl } 372118611Snjl 373118611Snjl TWL_CLKS_SUNLOCK(sc); 374118611Snjl return (err); 375118611Snjl} 376118611Snjl 377118611Snjl/** 378118611Snjl * twl_clks_enable - enables a clock output 379118611Snjl * @dev: TWL clk device 380118611Snjl * @name: the name of the clock 381118611Snjl * 382118611Snjl * LOCKING: 383118611Snjl * Internally the function takes and releases the TWL CLKS lock. 384151937Sjkim * 385151937Sjkim * RETURNS: 386151937Sjkim * Zero on success or an error code on failure. 387151937Sjkim */ 388118611Snjlint 389118611Snjltwl_clks_enable(device_t dev, const char *name) 390118611Snjl{ 391118611Snjl struct twl_clks_softc *sc = device_get_softc(dev); 392118611Snjl struct twl_clk_entry *clk; 393151937Sjkim int err = EINVAL; 394151937Sjkim 395118611Snjl TWL_CLKS_SLOCK(sc); 396118611Snjl 397118611Snjl LIST_FOREACH(clk, &sc->sc_clks_list, link) { 398118611Snjl if (strcmp(clk->name, name) == 0) { 399118611Snjl err = twl_clks_set_state(sc, clk, 1); 400118611Snjl break; 401118611Snjl } 402118611Snjl } 403118611Snjl 404118611Snjl TWL_CLKS_SUNLOCK(sc); 405118611Snjl return (err); 406118611Snjl} 407118611Snjl 408118611Snjl/** 409118611Snjl * twl_clks_sysctl_clock - reads the state of the clock 410118611Snjl * @SYSCTL_HANDLER_ARGS: arguments for the callback 411118611Snjl * 412118611Snjl * Returns the clock status; disabled is zero and enabled is non-zero. 413118611Snjl * 414118611Snjl * LOCKING: 415118611Snjl * It's expected the TWL lock is held while this function is called. 416118611Snjl * 417118611Snjl * RETURNS: 418118611Snjl * EIO if device is not present, otherwise 0 is returned. 419151937Sjkim */ 420151937Sjkimstatic int 421151937Sjkimtwl_clks_sysctl_clock(SYSCTL_HANDLER_ARGS) 422151937Sjkim{ 423151937Sjkim struct twl_clks_softc *sc = (struct twl_clks_softc*)arg1; 424151937Sjkim int err; 425151937Sjkim int enabled = 0; 426151937Sjkim 427151937Sjkim if ((err = twl_clks_is_enabled(sc->sc_dev, oidp->oid_name, &enabled)) != 0) 428151937Sjkim return err; 429241973Sjkim 430151937Sjkim return sysctl_handle_int(oidp, &enabled, 0, req); 431151937Sjkim} 432151937Sjkim 433151937Sjkim/** 434151937Sjkim * twl_clks_add_clock - adds single clock sysctls for the device 435151937Sjkim * @sc: device soft context 436151937Sjkim * @name: the name of the regulator 437151937Sjkim * @nsub: the number of the subdevice 438151937Sjkim * @regbase: the base address of the clocks registers 439151937Sjkim * 440151937Sjkim * Adds a single clock to the device and also a sysctl interface for 441151937Sjkim * querying it's status. 442151937Sjkim * 443151937Sjkim * LOCKING: 444151937Sjkim * It's expected the exclusive lock is held while this function is called. 445151937Sjkim * 446151937Sjkim * RETURNS: 447151937Sjkim * Pointer to the new clock entry on success, otherwise NULL on failure. 448151937Sjkim */ 449151937Sjkimstatic struct twl_clk_entry* 450151937Sjkimtwl_clks_add_clock(struct twl_clks_softc *sc, const char *name, 451151937Sjkim uint8_t nsub, uint8_t regbase) 452151937Sjkim{ 453151937Sjkim struct sysctl_ctx_list *ctx = device_get_sysctl_ctx(sc->sc_dev); 454151937Sjkim struct sysctl_oid *tree = device_get_sysctl_tree(sc->sc_dev); 455151937Sjkim struct twl_clk_entry *new; 456151937Sjkim 457 TWL_CLKS_ASSERT_LOCKED(sc); 458 459 new = malloc(sizeof(struct twl_clk_entry), M_DEVBUF, M_NOWAIT | M_ZERO); 460 if (new == NULL) 461 return (NULL); 462 463 464 strncpy(new->name, name, TWL_CLKS_MAX_NAMELEN); 465 new->name[TWL_CLKS_MAX_NAMELEN - 1] = '\0'; 466 467 new->sub_dev = nsub; 468 new->reg_off = regbase; 469 470 471 472 /* Add a sysctl entry for the clock */ 473 new->oid = SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(tree), OID_AUTO, name, 474 CTLTYPE_INT | CTLFLAG_RD, sc, 0, 475 twl_clks_sysctl_clock, "I", "external clock"); 476 477 /* Finally add the regulator to list of supported regulators */ 478 LIST_INSERT_HEAD(&sc->sc_clks_list, new, link); 479 480 return (new); 481} 482 483/** 484 * twl_clks_add_clocks - populates the internal list of clocks 485 * @sc: device soft context 486 * @chip: the name of the chip used in the hints 487 * @clks the list of clocks supported by the device 488 * 489 * Loops over the list of clocks and adds them to the device context. Also 490 * scans the FDT to determine if there are any clocks that should be 491 * enabled/disabled automatically. 492 * 493 * LOCKING: 494 * Internally takes the exclusive lock while adding the clocks to the 495 * device context. 496 * 497 * RETURNS: 498 * Always returns 0. 499 */ 500static int 501twl_clks_add_clocks(struct twl_clks_softc *sc, const struct twl_clock *clks) 502{ 503 int err; 504 const struct twl_clock *walker; 505 struct twl_clk_entry *entry; 506 phandle_t child; 507 char rnames[256]; 508 char *name, *state; 509 int len = 0, prop_len; 510 int enable; 511 512 513 TWL_CLKS_XLOCK(sc); 514 515 /* Add the regulators from the list */ 516 walker = &clks[0]; 517 while (walker->name != NULL) { 518 519 /* Add the regulator to the list */ 520 entry = twl_clks_add_clock(sc, walker->name, walker->subdev, 521 walker->regbase); 522 if (entry == NULL) 523 continue; 524 525 walker++; 526 } 527 528 /* Check for any FDT settings that need to be applied */ 529 child = ofw_bus_get_node(sc->sc_pdev); 530 if (child) { 531 532 prop_len = OF_getprop(child, "external-clocks", rnames, sizeof(rnames)); 533 while (len < prop_len) { 534 name = rnames + len; 535 len += strlen(name) + 1; 536 if ((len >= prop_len) || (name[0] == '\0')) 537 break; 538 539 state = rnames + len; 540 len += strlen(state) + 1; 541 if (state[0] == '\0') 542 break; 543 544 enable = !strncmp(state, "on", 2); 545 546 LIST_FOREACH(entry, &sc->sc_clks_list, link) { 547 if (strcmp(entry->name, name) == 0) { 548 twl_clks_set_state(sc, entry, enable); 549 break; 550 } 551 } 552 } 553 } 554 555 TWL_CLKS_XUNLOCK(sc); 556 557 558 if (twl_clks_debug) { 559 LIST_FOREACH(entry, &sc->sc_clks_list, link) { 560 err = twl_clks_is_enabled(sc->sc_dev, entry->name, &enable); 561 if (!err) 562 device_printf(sc->sc_dev, "%s : %s\n", entry->name, 563 enable ? "on" : "off"); 564 } 565 } 566 567 return (0); 568} 569 570/** 571 * twl_clks_init - initialises the list of clocks 572 * @dev: the twl_clks device 573 * 574 * This function is called as an intrhook once interrupts have been enabled, 575 * this is done so that the driver has the option to enable/disable a clock 576 * based on settings providied in the FDT. 577 * 578 * LOCKING: 579 * May takes the exclusive lock in the function. 580 */ 581static void 582twl_clks_init(void *dev) 583{ 584 struct twl_clks_softc *sc; 585 586 sc = device_get_softc((device_t)dev); 587 588 if (twl_is_4030(sc->sc_pdev)) 589 twl_clks_add_clocks(sc, twl4030_clocks); 590 else if (twl_is_6030(sc->sc_pdev) || twl_is_6025(sc->sc_pdev)) 591 twl_clks_add_clocks(sc, twl6030_clocks); 592 593 config_intrhook_disestablish(&sc->sc_init_hook); 594} 595 596static int 597twl_clks_probe(device_t dev) 598{ 599 if (twl_is_4030(device_get_parent(dev))) 600 device_set_desc(dev, "TI TWL4030 PMIC External Clocks"); 601 else if (twl_is_6025(device_get_parent(dev)) || 602 twl_is_6030(device_get_parent(dev))) 603 device_set_desc(dev, "TI TWL6025/TWL6030 PMIC External Clocks"); 604 else 605 return (ENXIO); 606 607 return (0); 608} 609 610static int 611twl_clks_attach(device_t dev) 612{ 613 struct twl_clks_softc *sc; 614 615 sc = device_get_softc(dev); 616 sc->sc_dev = dev; 617 sc->sc_pdev = device_get_parent(dev); 618 619 TWL_CLKS_LOCK_INIT(sc); 620 621 LIST_INIT(&sc->sc_clks_list); 622 623 624 sc->sc_init_hook.ich_func = twl_clks_init; 625 sc->sc_init_hook.ich_arg = dev; 626 627 if (config_intrhook_establish(&sc->sc_init_hook) != 0) 628 return (ENOMEM); 629 630 return (0); 631} 632 633static int 634twl_clks_detach(device_t dev) 635{ 636 struct twl_clks_softc *sc; 637 struct twl_clk_entry *clk; 638 struct twl_clk_entry *tmp; 639 640 sc = device_get_softc(dev); 641 642 TWL_CLKS_XLOCK(sc); 643 644 LIST_FOREACH_SAFE(clk, &sc->sc_clks_list, link, tmp) { 645 LIST_REMOVE(clk, link); 646 sysctl_remove_oid(clk->oid, 1, 0); 647 free(clk, M_DEVBUF); 648 } 649 650 TWL_CLKS_XUNLOCK(sc); 651 652 TWL_CLKS_LOCK_DESTROY(sc); 653 654 return (0); 655} 656 657static device_method_t twl_clks_methods[] = { 658 DEVMETHOD(device_probe, twl_clks_probe), 659 DEVMETHOD(device_attach, twl_clks_attach), 660 DEVMETHOD(device_detach, twl_clks_detach), 661 662 {0, 0}, 663}; 664 665static driver_t twl_clks_driver = { 666 "twl_clks", 667 twl_clks_methods, 668 sizeof(struct twl_clks_softc), 669}; 670 671static devclass_t twl_clks_devclass; 672 673DRIVER_MODULE(twl_clks, twl, twl_clks_driver, twl_clks_devclass, 0, 0); 674MODULE_VERSION(twl_clks, 1); 675