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