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