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
74static 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 */
96struct twl_clock {
97	const char	*name;
98	uint8_t		subdev;
99	uint8_t		regbase;
100};
101
102static const struct twl_clock twl4030_clocks[] = {
103	{ "32kclkout", 0, 0x8e },
104	{ NULL, 0, 0x00 }
105};
106
107static 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
116struct 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
124struct 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 */
162static inline int
163twl_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
169static inline int
170twl_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 */
188int
189twl_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
245done:
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 */
262static int
263twl_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
318done:
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 */
340int
341twl_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 */
371int
372twl_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 */
403static int
404twl_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 */
432static struct twl_clk_entry*
433twl_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 */
480static int
481twl_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 */
557static void
558twl_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
572static int
573twl_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
586static int
587twl_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
608static int
609twl_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
632static 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
640static driver_t twl_clks_driver = {
641	"twl_clks",
642	twl_clks_methods,
643	sizeof(struct twl_clks_softc),
644};
645
646DRIVER_MODULE(twl_clks, twl, twl_clks_driver, 0, 0);
647MODULE_VERSION(twl_clks, 1);
648