2 * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
5 * Ben Gray <bgray@freebsd.org>.
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
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.
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
30 #include <sys/cdefs.h>
31 __FBSDID("$FreeBSD$");
34 * Texas Instruments TWL4030/TWL5030/TWL60x0/TPS659x0 Power Management.
36 * This driver covers the external clocks, allows for enabling &
37 * disabling their output.
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:
45 * external-clocks = "name1", "state1",
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".
54 #include <sys/param.h>
55 #include <sys/systm.h>
56 #include <sys/kernel.h>
58 #include <sys/module.h>
60 #include <sys/resource.h>
62 #include <sys/sysctl.h>
64 #include <sys/malloc.h>
66 #include <machine/bus.h>
67 #include <machine/resource.h>
68 #include <machine/intr.h>
70 #include <dev/ofw/openfirm.h>
71 #include <dev/ofw/ofw_bus.h>
77 static int twl_clks_debug = 1;
81 * Power Groups bits for the 4030 and 6030 devices
83 #define TWL4030_P3_GRP 0x80 /* Peripherals, power group */
84 #define TWL4030_P2_GRP 0x40 /* Modem power group */
85 #define TWL4030_P1_GRP 0x20 /* Application power group (FreeBSD control) */
87 #define TWL6030_P3_GRP 0x04 /* Modem power group */
88 #define TWL6030_P2_GRP 0x02 /* Connectivity power group */
89 #define TWL6030_P1_GRP 0x01 /* Application power group (FreeBSD control) */
92 * Register offsets within a clk regulator register set
94 #define TWL_CLKS_GRP 0x00 /* Regulator GRP register */
95 #define TWL_CLKS_STATE 0x02 /* TWL6030 only */
100 * Support voltage regulators for the different IC's
108 static const struct twl_clock twl4030_clocks[] = {
109 { "32kclkout", 0, 0x8e },
113 static const struct twl_clock twl6030_clocks[] = {
114 { "clk32kg", 0, 0xbc },
115 { "clk32kao", 0, 0xb9 },
116 { "clk32kaudio", 0, 0xbf },
120 #define TWL_CLKS_MAX_NAMELEN 32
122 struct twl_clk_entry {
123 LIST_ENTRY(twl_clk_entry) link;
124 struct sysctl_oid *oid;
125 char name[TWL_CLKS_MAX_NAMELEN];
126 uint8_t sub_dev; /* the sub-device number for the clock */
127 uint8_t reg_off; /* register base address of the clock */
130 struct twl_clks_softc {
131 device_t sc_dev; /* twl_clk device */
132 device_t sc_pdev; /* parent device (twl) */
133 struct sx sc_sx; /* internal locking */
134 struct intr_config_hook sc_init_hook;
135 LIST_HEAD(twl_clk_list, twl_clk_entry) sc_clks_list;
139 * Macros for driver shared locking
141 #define TWL_CLKS_XLOCK(_sc) sx_xlock(&(_sc)->sc_sx)
142 #define TWL_CLKS_XUNLOCK(_sc) sx_xunlock(&(_sc)->sc_sx)
143 #define TWL_CLKS_SLOCK(_sc) sx_slock(&(_sc)->sc_sx)
144 #define TWL_CLKS_SUNLOCK(_sc) sx_sunlock(&(_sc)->sc_sx)
145 #define TWL_CLKS_LOCK_INIT(_sc) sx_init(&(_sc)->sc_sx, "twl_clks")
146 #define TWL_CLKS_LOCK_DESTROY(_sc) sx_destroy(&(_sc)->sc_sx);
148 #define TWL_CLKS_ASSERT_LOCKED(_sc) sx_assert(&(_sc)->sc_sx, SA_LOCKED);
150 #define TWL_CLKS_LOCK_UPGRADE(_sc) \
152 while (!sx_try_upgrade(&(_sc)->sc_sx)) \
153 pause("twl_clks_ex", (hz / 100)); \
155 #define TWL_CLKS_LOCK_DOWNGRADE(_sc) sx_downgrade(&(_sc)->sc_sx);
161 * twl_clks_read_1 - read single register from the TWL device
162 * twl_clks_write_1 - writes a single register in the TWL device
163 * @sc: device context
164 * @clk: the clock device we're reading from / writing to
165 * @off: offset within the clock's register set
166 * @val: the value to write or a pointer to a variable to store the result
169 * Zero on success or an error code on failure.
172 twl_clks_read_1(struct twl_clks_softc *sc, struct twl_clk_entry *clk,
173 uint8_t off, uint8_t *val)
175 return (twl_read(sc->sc_pdev, clk->sub_dev, clk->reg_off + off, val, 1));
179 twl_clks_write_1(struct twl_clks_softc *sc, struct twl_clk_entry *clk,
180 uint8_t off, uint8_t val)
182 return (twl_write(sc->sc_pdev, clk->sub_dev, clk->reg_off + off, &val, 1));
187 * twl_clks_is_enabled - determines if a clock is enabled
188 * @dev: TWL CLK device
189 * @name: the name of the clock
190 * @enabled: upon return will contain the 'enabled' state
193 * Internally the function takes and releases the TWL lock.
196 * Zero on success or a negative error code on failure.
199 twl_clks_is_enabled(device_t dev, const char *name, int *enabled)
201 struct twl_clks_softc *sc = device_get_softc(dev);
202 struct twl_clk_entry *clk;
209 LIST_FOREACH(clk, &sc->sc_clks_list, link) {
210 if (strcmp(clk->name, name) == 0) {
217 TWL_CLKS_SUNLOCK(sc);
222 if (twl_is_4030(sc->sc_pdev)) {
224 err = twl_clks_read_1(sc, clk, TWL_CLKS_GRP, &grp);
226 *enabled = (grp & TWL4030_P1_GRP);
228 } else if (twl_is_6030(sc->sc_pdev) || twl_is_6025(sc->sc_pdev)) {
230 TWL_CLKS_LOCK_UPGRADE(sc);
232 /* Check the clock is in the application group */
233 if (twl_is_6030(sc->sc_pdev)) {
234 err = twl_clks_read_1(sc, clk, TWL_CLKS_GRP, &grp);
236 TWL_CLKS_LOCK_DOWNGRADE(sc);
240 if (!(grp & TWL6030_P1_GRP)) {
241 TWL_CLKS_LOCK_DOWNGRADE(sc);
242 *enabled = 0; /* disabled */
247 /* Read the application mode state and verify it's ON */
248 err = twl_clks_read_1(sc, clk, TWL_CLKS_STATE, &state);
250 *enabled = ((state & 0x0C) == 0x04);
252 TWL_CLKS_LOCK_DOWNGRADE(sc);
259 TWL_CLKS_SUNLOCK(sc);
265 * twl_clks_set_state - enables/disables a clock output
266 * @sc: device context
267 * @clk: the clock entry to enable/disable
268 * @enable: non-zero the clock is enabled, zero the clock is disabled
271 * The TWL CLK lock must be held before this function is called.
274 * Zero on success or an error code on failure.
277 twl_clks_set_state(struct twl_clks_softc *sc, struct twl_clk_entry *clk,
284 TWL_CLKS_ASSERT_LOCKED(sc);
286 /* Upgrade the lock to exclusive because about to perform read-mod-write */
287 xlocked = sx_xlocked(&sc->sc_sx);
289 TWL_CLKS_LOCK_UPGRADE(sc);
291 err = twl_clks_read_1(sc, clk, TWL_CLKS_GRP, &grp);
295 if (twl_is_4030(sc->sc_pdev)) {
297 /* On the TWL4030 we just need to ensure the clock is in the right
298 * power domain, don't need to turn on explicitly like TWL6030.
301 grp |= TWL4030_P1_GRP;
303 grp &= ~(TWL4030_P1_GRP | TWL4030_P2_GRP | TWL4030_P3_GRP);
305 err = twl_clks_write_1(sc, clk, TWL_CLKS_GRP, grp);
307 } else if (twl_is_6030(sc->sc_pdev) || twl_is_6025(sc->sc_pdev)) {
309 /* Make sure the clock belongs to at least the APP power group */
310 if (twl_is_6030(sc->sc_pdev) && !(grp & TWL6030_P1_GRP)) {
311 grp |= TWL6030_P1_GRP;
312 err = twl_clks_write_1(sc, clk, TWL_CLKS_GRP, grp);
317 /* On TWL6030 we need to make sure we disable power for all groups */
318 if (twl_is_6030(sc->sc_pdev))
319 grp = TWL6030_P1_GRP | TWL6030_P2_GRP | TWL6030_P3_GRP;
323 /* Set the state of the clock */
325 err = twl_clks_write_1(sc, clk, TWL_CLKS_STATE, (grp << 5) | 0x01);
327 err = twl_clks_write_1(sc, clk, TWL_CLKS_STATE, (grp << 5));
336 TWL_CLKS_LOCK_DOWNGRADE(sc);
338 if ((twl_clks_debug > 1) && !err)
339 device_printf(sc->sc_dev, "%s : %sabled\n", clk->name,
340 enable ? "en" : "dis");
347 * twl_clks_disable - disables a clock output
348 * @dev: TWL clk device
349 * @name: the name of the clock
352 * Internally the function takes and releases the TWL lock.
355 * Zero on success or an error code on failure.
358 twl_clks_disable(device_t dev, const char *name)
360 struct twl_clks_softc *sc = device_get_softc(dev);
361 struct twl_clk_entry *clk;
366 LIST_FOREACH(clk, &sc->sc_clks_list, link) {
367 if (strcmp(clk->name, name) == 0) {
368 err = twl_clks_set_state(sc, clk, 0);
373 TWL_CLKS_SUNLOCK(sc);
378 * twl_clks_enable - enables a clock output
379 * @dev: TWL clk device
380 * @name: the name of the clock
383 * Internally the function takes and releases the TWL CLKS lock.
386 * Zero on success or an error code on failure.
389 twl_clks_enable(device_t dev, const char *name)
391 struct twl_clks_softc *sc = device_get_softc(dev);
392 struct twl_clk_entry *clk;
397 LIST_FOREACH(clk, &sc->sc_clks_list, link) {
398 if (strcmp(clk->name, name) == 0) {
399 err = twl_clks_set_state(sc, clk, 1);
404 TWL_CLKS_SUNLOCK(sc);
409 * twl_clks_sysctl_clock - reads the state of the clock
410 * @SYSCTL_HANDLER_ARGS: arguments for the callback
412 * Returns the clock status; disabled is zero and enabled is non-zero.
415 * It's expected the TWL lock is held while this function is called.
418 * EIO if device is not present, otherwise 0 is returned.
421 twl_clks_sysctl_clock(SYSCTL_HANDLER_ARGS)
423 struct twl_clks_softc *sc = (struct twl_clks_softc*)arg1;
427 if ((err = twl_clks_is_enabled(sc->sc_dev, oidp->oid_name, &enabled)) != 0)
430 return sysctl_handle_int(oidp, &enabled, 0, req);
434 * twl_clks_add_clock - adds single clock sysctls for the device
435 * @sc: device soft context
436 * @name: the name of the regulator
437 * @nsub: the number of the subdevice
438 * @regbase: the base address of the clocks registers
440 * Adds a single clock to the device and also a sysctl interface for
441 * querying it's status.
444 * It's expected the exclusive lock is held while this function is called.
447 * Pointer to the new clock entry on success, otherwise NULL on failure.
449 static struct twl_clk_entry*
450 twl_clks_add_clock(struct twl_clks_softc *sc, const char *name,
451 uint8_t nsub, uint8_t regbase)
453 struct sysctl_ctx_list *ctx = device_get_sysctl_ctx(sc->sc_dev);
454 struct sysctl_oid *tree = device_get_sysctl_tree(sc->sc_dev);
455 struct twl_clk_entry *new;
457 TWL_CLKS_ASSERT_LOCKED(sc);
459 new = malloc(sizeof(struct twl_clk_entry), M_DEVBUF, M_NOWAIT | M_ZERO);
464 strncpy(new->name, name, TWL_CLKS_MAX_NAMELEN);
465 new->name[TWL_CLKS_MAX_NAMELEN - 1] = '\0';
468 new->reg_off = regbase;
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");
477 /* Finally add the regulator to list of supported regulators */
478 LIST_INSERT_HEAD(&sc->sc_clks_list, new, link);
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
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.
494 * Internally takes the exclusive lock while adding the clocks to the
501 twl_clks_add_clocks(struct twl_clks_softc *sc, const struct twl_clock *clks)
504 const struct twl_clock *walker;
505 struct twl_clk_entry *entry;
509 int len = 0, prop_len;
515 /* Add the regulators from the list */
517 while (walker->name != NULL) {
519 /* Add the regulator to the list */
520 entry = twl_clks_add_clock(sc, walker->name, walker->subdev,
528 /* Check for any FDT settings that need to be applied */
529 child = ofw_bus_get_node(sc->sc_pdev);
532 prop_len = OF_getprop(child, "external-clocks", rnames, sizeof(rnames));
533 while (len < prop_len) {
535 len += strlen(name) + 1;
536 if ((len >= prop_len) || (name[0] == '\0'))
539 state = rnames + len;
540 len += strlen(state) + 1;
541 if (state[0] == '\0')
544 enable = !strncmp(state, "on", 2);
546 LIST_FOREACH(entry, &sc->sc_clks_list, link) {
547 if (strcmp(entry->name, name) == 0) {
548 twl_clks_set_state(sc, entry, enable);
555 TWL_CLKS_XUNLOCK(sc);
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);
562 device_printf(sc->sc_dev, "%s : %s\n", entry->name,
563 enable ? "on" : "off");
571 * twl_clks_init - initialises the list of clocks
572 * @dev: the twl_clks device
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.
579 * May takes the exclusive lock in the function.
582 twl_clks_init(void *dev)
584 struct twl_clks_softc *sc;
586 sc = device_get_softc((device_t)dev);
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);
593 config_intrhook_disestablish(&sc->sc_init_hook);
597 twl_clks_probe(device_t dev)
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");
611 twl_clks_attach(device_t dev)
613 struct twl_clks_softc *sc;
615 sc = device_get_softc(dev);
617 sc->sc_pdev = device_get_parent(dev);
619 TWL_CLKS_LOCK_INIT(sc);
621 LIST_INIT(&sc->sc_clks_list);
624 sc->sc_init_hook.ich_func = twl_clks_init;
625 sc->sc_init_hook.ich_arg = dev;
627 if (config_intrhook_establish(&sc->sc_init_hook) != 0)
634 twl_clks_detach(device_t dev)
636 struct twl_clks_softc *sc;
637 struct twl_clk_entry *clk;
638 struct twl_clk_entry *tmp;
640 sc = device_get_softc(dev);
644 LIST_FOREACH_SAFE(clk, &sc->sc_clks_list, link, tmp) {
645 LIST_REMOVE(clk, link);
646 sysctl_remove_oid(clk->oid, 1, 0);
650 TWL_CLKS_XUNLOCK(sc);
652 TWL_CLKS_LOCK_DESTROY(sc);
657 static 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),
665 static driver_t twl_clks_driver = {
668 sizeof(struct twl_clks_softc),
671 static devclass_t twl_clks_devclass;
673 DRIVER_MODULE(twl_clks, twl, twl_clks_driver, twl_clks_devclass, 0, 0);
674 MODULE_VERSION(twl_clks, 1);