3 * Ben Gray <bgray@freebsd.org>.
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
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.
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
28 #include <sys/cdefs.h>
29 __FBSDID("$FreeBSD$");
32 * Texas Instruments TWL4030/TWL5030/TWL60x0/TPS659x0 Power Management.
34 * This driver covers the external clocks, allows for enabling &
35 * disabling their output.
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:
43 * external-clocks = "name1", "state1",
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".
52 #include <sys/param.h>
53 #include <sys/systm.h>
54 #include <sys/kernel.h>
56 #include <sys/module.h>
58 #include <sys/resource.h>
60 #include <sys/sysctl.h>
62 #include <sys/malloc.h>
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>
71 #include <dev/ofw/openfirm.h>
72 #include <dev/ofw/ofw_bus.h>
78 static int twl_clks_debug = 1;
82 * Power Groups bits for the 4030 and 6030 devices
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) */
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) */
93 * Register offsets within a clk regulator register set
95 #define TWL_CLKS_GRP 0x00 /* Regulator GRP register */
96 #define TWL_CLKS_STATE 0x02 /* TWL6030 only */
101 * Support voltage regulators for the different IC's
109 static const struct twl_clock twl4030_clocks[] = {
110 { "32kclkout", 0, 0x8e },
114 static const struct twl_clock twl6030_clocks[] = {
115 { "clk32kg", 0, 0xbc },
116 { "clk32kao", 0, 0xb9 },
117 { "clk32kaudio", 0, 0xbf },
121 #define TWL_CLKS_MAX_NAMELEN 32
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 */
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;
140 * Macros for driver shared locking
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);
149 #define TWL_CLKS_ASSERT_LOCKED(_sc) sx_assert(&(_sc)->sc_sx, SA_LOCKED);
151 #define TWL_CLKS_LOCK_UPGRADE(_sc) \
153 while (!sx_try_upgrade(&(_sc)->sc_sx)) \
154 pause("twl_clks_ex", (hz / 100)); \
156 #define TWL_CLKS_LOCK_DOWNGRADE(_sc) sx_downgrade(&(_sc)->sc_sx);
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
170 * Zero on success or an error code on failure.
173 twl_clks_read_1(struct twl_clks_softc *sc, struct twl_clk_entry *clk,
174 uint8_t off, uint8_t *val)
176 return (twl_read(sc->sc_pdev, clk->sub_dev, clk->reg_off + off, val, 1));
180 twl_clks_write_1(struct twl_clks_softc *sc, struct twl_clk_entry *clk,
181 uint8_t off, uint8_t val)
183 return (twl_write(sc->sc_pdev, clk->sub_dev, clk->reg_off + off, &val, 1));
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
194 * Internally the function takes and releases the TWL lock.
197 * Zero on success or a negative error code on failure.
200 twl_clks_is_enabled(device_t dev, const char *name, int *enabled)
202 struct twl_clks_softc *sc = device_get_softc(dev);
203 struct twl_clk_entry *clk;
210 LIST_FOREACH(clk, &sc->sc_clks_list, link) {
211 if (strcmp(clk->name, name) == 0) {
218 TWL_CLKS_SUNLOCK(sc);
223 if (twl_is_4030(sc->sc_pdev)) {
225 err = twl_clks_read_1(sc, clk, TWL_CLKS_GRP, &grp);
227 *enabled = (grp & TWL4030_P1_GRP);
229 } else if (twl_is_6030(sc->sc_pdev) || twl_is_6025(sc->sc_pdev)) {
231 TWL_CLKS_LOCK_UPGRADE(sc);
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);
237 TWL_CLKS_LOCK_DOWNGRADE(sc);
241 if (!(grp & TWL6030_P1_GRP)) {
242 TWL_CLKS_LOCK_DOWNGRADE(sc);
243 *enabled = 0; /* disabled */
248 /* Read the application mode state and verify it's ON */
249 err = twl_clks_read_1(sc, clk, TWL_CLKS_STATE, &state);
251 *enabled = ((state & 0x0C) == 0x04);
253 TWL_CLKS_LOCK_DOWNGRADE(sc);
260 TWL_CLKS_SUNLOCK(sc);
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
272 * The TWL CLK lock must be held before this function is called.
275 * Zero on success or an error code on failure.
278 twl_clks_set_state(struct twl_clks_softc *sc, struct twl_clk_entry *clk,
285 TWL_CLKS_ASSERT_LOCKED(sc);
287 /* Upgrade the lock to exclusive because about to perform read-mod-write */
288 xlocked = sx_xlocked(&sc->sc_sx);
290 TWL_CLKS_LOCK_UPGRADE(sc);
292 err = twl_clks_read_1(sc, clk, TWL_CLKS_GRP, &grp);
296 if (twl_is_4030(sc->sc_pdev)) {
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.
302 grp |= TWL4030_P1_GRP;
304 grp &= ~(TWL4030_P1_GRP | TWL4030_P2_GRP | TWL4030_P3_GRP);
306 err = twl_clks_write_1(sc, clk, TWL_CLKS_GRP, grp);
308 } else if (twl_is_6030(sc->sc_pdev) || twl_is_6025(sc->sc_pdev)) {
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);
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;
324 /* Set the state of the clock */
326 err = twl_clks_write_1(sc, clk, TWL_CLKS_STATE, (grp << 5) | 0x01);
328 err = twl_clks_write_1(sc, clk, TWL_CLKS_STATE, (grp << 5));
337 TWL_CLKS_LOCK_DOWNGRADE(sc);
339 if ((twl_clks_debug > 1) && !err)
340 device_printf(sc->sc_dev, "%s : %sabled\n", clk->name,
341 enable ? "en" : "dis");
348 * twl_clks_disable - disables a clock output
349 * @dev: TWL clk device
350 * @name: the name of the clock
353 * Internally the function takes and releases the TWL lock.
356 * Zero on success or an error code on failure.
359 twl_clks_disable(device_t dev, const char *name)
361 struct twl_clks_softc *sc = device_get_softc(dev);
362 struct twl_clk_entry *clk;
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);
374 TWL_CLKS_SUNLOCK(sc);
379 * twl_clks_enable - enables a clock output
380 * @dev: TWL clk device
381 * @name: the name of the clock
384 * Internally the function takes and releases the TWL CLKS lock.
387 * Zero on success or an error code on failure.
390 twl_clks_enable(device_t dev, const char *name)
392 struct twl_clks_softc *sc = device_get_softc(dev);
393 struct twl_clk_entry *clk;
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);
405 TWL_CLKS_SUNLOCK(sc);
410 * twl_clks_sysctl_clock - reads the state of the clock
411 * @SYSCTL_HANDLER_ARGS: arguments for the callback
413 * Returns the clock status; disabled is zero and enabled is non-zero.
416 * It's expected the TWL lock is held while this function is called.
419 * EIO if device is not present, otherwise 0 is returned.
422 twl_clks_sysctl_clock(SYSCTL_HANDLER_ARGS)
424 struct twl_clks_softc *sc = (struct twl_clks_softc*)arg1;
428 if ((err = twl_clks_is_enabled(sc->sc_dev, oidp->oid_name, &enabled)) != 0)
431 return sysctl_handle_int(oidp, &enabled, 0, req);
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
441 * Adds a single clock to the device and also a sysctl interface for
442 * querying it's status.
445 * It's expected the exclusive lock is held while this function is called.
448 * Pointer to the new clock entry on success, otherwise NULL on failure.
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)
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;
458 TWL_CLKS_ASSERT_LOCKED(sc);
460 new = malloc(sizeof(struct twl_clk_entry), M_DEVBUF, M_NOWAIT | M_ZERO);
465 strncpy(new->name, name, TWL_CLKS_MAX_NAMELEN);
466 new->name[TWL_CLKS_MAX_NAMELEN - 1] = '\0';
469 new->reg_off = regbase;
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");
478 /* Finally add the regulator to list of supported regulators */
479 LIST_INSERT_HEAD(&sc->sc_clks_list, new, link);
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
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.
495 * Internally takes the exclusive lock while adding the clocks to the
502 twl_clks_add_clocks(struct twl_clks_softc *sc, const struct twl_clock *clks)
505 const struct twl_clock *walker;
506 struct twl_clk_entry *entry;
510 int len = 0, prop_len;
516 /* Add the regulators from the list */
518 while (walker->name != NULL) {
520 /* Add the regulator to the list */
521 entry = twl_clks_add_clock(sc, walker->name, walker->subdev,
529 /* Check for any FDT settings that need to be applied */
530 child = ofw_bus_get_node(sc->sc_pdev);
533 prop_len = OF_getprop(child, "external-clocks", rnames, sizeof(rnames));
534 while (len < prop_len) {
536 len += strlen(name) + 1;
537 if ((len >= prop_len) || (name[0] == '\0'))
540 state = rnames + len;
541 len += strlen(state) + 1;
542 if (state[0] == '\0')
545 enable = !strncmp(state, "on", 2);
547 LIST_FOREACH(entry, &sc->sc_clks_list, link) {
548 if (strcmp(entry->name, name) == 0) {
549 twl_clks_set_state(sc, entry, enable);
556 TWL_CLKS_XUNLOCK(sc);
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);
563 device_printf(sc->sc_dev, "%s : %s\n", entry->name,
564 enable ? "on" : "off");
572 * twl_clks_init - initialises the list of clocks
573 * @dev: the twl_clks device
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.
580 * May takes the exclusive lock in the function.
583 twl_clks_init(void *dev)
585 struct twl_clks_softc *sc;
587 sc = device_get_softc((device_t)dev);
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);
594 config_intrhook_disestablish(&sc->sc_init_hook);
598 twl_clks_probe(device_t dev)
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");
612 twl_clks_attach(device_t dev)
614 struct twl_clks_softc *sc;
616 sc = device_get_softc(dev);
618 sc->sc_pdev = device_get_parent(dev);
620 TWL_CLKS_LOCK_INIT(sc);
622 LIST_INIT(&sc->sc_clks_list);
625 sc->sc_init_hook.ich_func = twl_clks_init;
626 sc->sc_init_hook.ich_arg = dev;
628 if (config_intrhook_establish(&sc->sc_init_hook) != 0)
635 twl_clks_detach(device_t dev)
637 struct twl_clks_softc *sc;
638 struct twl_clk_entry *clk;
639 struct twl_clk_entry *tmp;
641 sc = device_get_softc(dev);
645 LIST_FOREACH_SAFE(clk, &sc->sc_clks_list, link, tmp) {
646 LIST_REMOVE(clk, link);
647 sysctl_remove_oid(clk->oid, 1, 0);
651 TWL_CLKS_XUNLOCK(sc);
653 TWL_CLKS_LOCK_DESTROY(sc);
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),
666 static driver_t twl_clks_driver = {
669 sizeof(struct twl_clks_softc),
672 static devclass_t twl_clks_devclass;
674 DRIVER_MODULE(twl_clks, twl, twl_clks_driver, twl_clks_devclass, 0, 0);
675 MODULE_VERSION(twl_clks, 1);