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