xref: /openbmc/linux/arch/arm/mach-omap2/clockdomain.c (revision b8bb76713ec50df2f11efee386e16f93d51e1076)
1 /*
2  * OMAP2/3 clockdomain framework functions
3  *
4  * Copyright (C) 2008 Texas Instruments, Inc.
5  * Copyright (C) 2008 Nokia Corporation
6  *
7  * Written by Paul Walmsley and Jouni Högander
8  *
9  * This program is free software; you can redistribute it and/or modify
10  * it under the terms of the GNU General Public License version 2 as
11  * published by the Free Software Foundation.
12  */
13 #ifdef CONFIG_OMAP_DEBUG_CLOCKDOMAIN
14 #  define DEBUG
15 #endif
16 
17 #include <linux/module.h>
18 #include <linux/kernel.h>
19 #include <linux/device.h>
20 #include <linux/list.h>
21 #include <linux/errno.h>
22 #include <linux/delay.h>
23 #include <linux/clk.h>
24 #include <linux/limits.h>
25 #include <linux/err.h>
26 
27 #include <linux/io.h>
28 
29 #include <linux/bitops.h>
30 
31 #include <mach/clock.h>
32 
33 #include "prm.h"
34 #include "prm-regbits-24xx.h"
35 #include "cm.h"
36 
37 #include <mach/powerdomain.h>
38 #include <mach/clockdomain.h>
39 
40 /* clkdm_list contains all registered struct clockdomains */
41 static LIST_HEAD(clkdm_list);
42 
43 /* clkdm_mutex protects clkdm_list add and del ops */
44 static DEFINE_MUTEX(clkdm_mutex);
45 
46 /* array of powerdomain deps to be added/removed when clkdm in hwsup mode */
47 static struct clkdm_pwrdm_autodep *autodeps;
48 
49 
50 /* Private functions */
51 
52 /*
53  * _autodep_lookup - resolve autodep pwrdm names to pwrdm pointers; store
54  * @autodep: struct clkdm_pwrdm_autodep * to resolve
55  *
56  * Resolve autodep powerdomain names to powerdomain pointers via
57  * pwrdm_lookup() and store the pointers in the autodep structure.  An
58  * "autodep" is a powerdomain sleep/wakeup dependency that is
59  * automatically added and removed whenever clocks in the associated
60  * clockdomain are enabled or disabled (respectively) when the
61  * clockdomain is in hardware-supervised mode.	Meant to be called
62  * once at clockdomain layer initialization, since these should remain
63  * fixed for a particular architecture.  No return value.
64  */
65 static void _autodep_lookup(struct clkdm_pwrdm_autodep *autodep)
66 {
67 	struct powerdomain *pwrdm;
68 
69 	if (!autodep)
70 		return;
71 
72 	if (!omap_chip_is(autodep->omap_chip))
73 		return;
74 
75 	pwrdm = pwrdm_lookup(autodep->pwrdm.name);
76 	if (!pwrdm) {
77 		pr_err("clockdomain: autodeps: powerdomain %s does not exist\n",
78 			 autodep->pwrdm.name);
79 		pwrdm = ERR_PTR(-ENOENT);
80 	}
81 	autodep->pwrdm.ptr = pwrdm;
82 }
83 
84 /*
85  * _clkdm_add_autodeps - add auto sleepdeps/wkdeps to clkdm upon clock enable
86  * @clkdm: struct clockdomain *
87  *
88  * Add the "autodep" sleep & wakeup dependencies to clockdomain 'clkdm'
89  * in hardware-supervised mode.  Meant to be called from clock framework
90  * when a clock inside clockdomain 'clkdm' is enabled.	No return value.
91  */
92 static void _clkdm_add_autodeps(struct clockdomain *clkdm)
93 {
94 	struct clkdm_pwrdm_autodep *autodep;
95 
96 	for (autodep = autodeps; autodep->pwrdm.ptr; autodep++) {
97 		if (IS_ERR(autodep->pwrdm.ptr))
98 			continue;
99 
100 		if (!omap_chip_is(autodep->omap_chip))
101 			continue;
102 
103 		pr_debug("clockdomain: adding %s sleepdep/wkdep for "
104 			 "pwrdm %s\n", autodep->pwrdm.ptr->name,
105 			 clkdm->pwrdm.ptr->name);
106 
107 		pwrdm_add_sleepdep(clkdm->pwrdm.ptr, autodep->pwrdm.ptr);
108 		pwrdm_add_wkdep(clkdm->pwrdm.ptr, autodep->pwrdm.ptr);
109 	}
110 }
111 
112 /*
113  * _clkdm_add_autodeps - remove auto sleepdeps/wkdeps from clkdm
114  * @clkdm: struct clockdomain *
115  *
116  * Remove the "autodep" sleep & wakeup dependencies from clockdomain 'clkdm'
117  * in hardware-supervised mode.  Meant to be called from clock framework
118  * when a clock inside clockdomain 'clkdm' is disabled.  No return value.
119  */
120 static void _clkdm_del_autodeps(struct clockdomain *clkdm)
121 {
122 	struct clkdm_pwrdm_autodep *autodep;
123 
124 	for (autodep = autodeps; autodep->pwrdm.ptr; autodep++) {
125 		if (IS_ERR(autodep->pwrdm.ptr))
126 			continue;
127 
128 		if (!omap_chip_is(autodep->omap_chip))
129 			continue;
130 
131 		pr_debug("clockdomain: removing %s sleepdep/wkdep for "
132 			 "pwrdm %s\n", autodep->pwrdm.ptr->name,
133 			 clkdm->pwrdm.ptr->name);
134 
135 		pwrdm_del_sleepdep(clkdm->pwrdm.ptr, autodep->pwrdm.ptr);
136 		pwrdm_del_wkdep(clkdm->pwrdm.ptr, autodep->pwrdm.ptr);
137 	}
138 }
139 
140 
141 static struct clockdomain *_clkdm_lookup(const char *name)
142 {
143 	struct clockdomain *clkdm, *temp_clkdm;
144 
145 	if (!name)
146 		return NULL;
147 
148 	clkdm = NULL;
149 
150 	list_for_each_entry(temp_clkdm, &clkdm_list, node) {
151 		if (!strcmp(name, temp_clkdm->name)) {
152 			clkdm = temp_clkdm;
153 			break;
154 		}
155 	}
156 
157 	return clkdm;
158 }
159 
160 
161 /* Public functions */
162 
163 /**
164  * clkdm_init - set up the clockdomain layer
165  * @clkdms: optional pointer to an array of clockdomains to register
166  * @init_autodeps: optional pointer to an array of autodeps to register
167  *
168  * Set up internal state.  If a pointer to an array of clockdomains
169  * was supplied, loop through the list of clockdomains, register all
170  * that are available on the current platform.	Similarly, if a
171  * pointer to an array of clockdomain-powerdomain autodependencies was
172  * provided, register those.  No return value.
173  */
174 void clkdm_init(struct clockdomain **clkdms,
175 		struct clkdm_pwrdm_autodep *init_autodeps)
176 {
177 	struct clockdomain **c = NULL;
178 	struct clkdm_pwrdm_autodep *autodep = NULL;
179 
180 	if (clkdms)
181 		for (c = clkdms; *c; c++)
182 			clkdm_register(*c);
183 
184 	autodeps = init_autodeps;
185 	if (autodeps)
186 		for (autodep = autodeps; autodep->pwrdm.ptr; autodep++)
187 			_autodep_lookup(autodep);
188 }
189 
190 /**
191  * clkdm_register - register a clockdomain
192  * @clkdm: struct clockdomain * to register
193  *
194  * Adds a clockdomain to the internal clockdomain list.
195  * Returns -EINVAL if given a null pointer, -EEXIST if a clockdomain is
196  * already registered by the provided name, or 0 upon success.
197  */
198 int clkdm_register(struct clockdomain *clkdm)
199 {
200 	int ret = -EINVAL;
201 	struct powerdomain *pwrdm;
202 
203 	if (!clkdm || !clkdm->name)
204 		return -EINVAL;
205 
206 	if (!omap_chip_is(clkdm->omap_chip))
207 		return -EINVAL;
208 
209 	pwrdm = pwrdm_lookup(clkdm->pwrdm.name);
210 	if (!pwrdm) {
211 		pr_err("clockdomain: %s: powerdomain %s does not exist\n",
212 			clkdm->name, clkdm->pwrdm.name);
213 		return -EINVAL;
214 	}
215 	clkdm->pwrdm.ptr = pwrdm;
216 
217 	mutex_lock(&clkdm_mutex);
218 	/* Verify that the clockdomain is not already registered */
219 	if (_clkdm_lookup(clkdm->name)) {
220 		ret = -EEXIST;
221 		goto cr_unlock;
222 	}
223 
224 	list_add(&clkdm->node, &clkdm_list);
225 
226 	pwrdm_add_clkdm(pwrdm, clkdm);
227 
228 	pr_debug("clockdomain: registered %s\n", clkdm->name);
229 	ret = 0;
230 
231 cr_unlock:
232 	mutex_unlock(&clkdm_mutex);
233 
234 	return ret;
235 }
236 
237 /**
238  * clkdm_unregister - unregister a clockdomain
239  * @clkdm: struct clockdomain * to unregister
240  *
241  * Removes a clockdomain from the internal clockdomain list.  Returns
242  * -EINVAL if clkdm argument is NULL.
243  */
244 int clkdm_unregister(struct clockdomain *clkdm)
245 {
246 	if (!clkdm)
247 		return -EINVAL;
248 
249 	pwrdm_del_clkdm(clkdm->pwrdm.ptr, clkdm);
250 
251 	mutex_lock(&clkdm_mutex);
252 	list_del(&clkdm->node);
253 	mutex_unlock(&clkdm_mutex);
254 
255 	pr_debug("clockdomain: unregistered %s\n", clkdm->name);
256 
257 	return 0;
258 }
259 
260 /**
261  * clkdm_lookup - look up a clockdomain by name, return a pointer
262  * @name: name of clockdomain
263  *
264  * Find a registered clockdomain by its name.  Returns a pointer to the
265  * struct clockdomain if found, or NULL otherwise.
266  */
267 struct clockdomain *clkdm_lookup(const char *name)
268 {
269 	struct clockdomain *clkdm, *temp_clkdm;
270 
271 	if (!name)
272 		return NULL;
273 
274 	clkdm = NULL;
275 
276 	mutex_lock(&clkdm_mutex);
277 	list_for_each_entry(temp_clkdm, &clkdm_list, node) {
278 		if (!strcmp(name, temp_clkdm->name)) {
279 			clkdm = temp_clkdm;
280 			break;
281 		}
282 	}
283 	mutex_unlock(&clkdm_mutex);
284 
285 	return clkdm;
286 }
287 
288 /**
289  * clkdm_for_each - call function on each registered clockdomain
290  * @fn: callback function *
291  *
292  * Call the supplied function for each registered clockdomain.
293  * The callback function can return anything but 0 to bail
294  * out early from the iterator.  The callback function is called with
295  * the clkdm_mutex held, so no clockdomain structure manipulation
296  * functions should be called from the callback, although hardware
297  * clockdomain control functions are fine.  Returns the last return
298  * value of the callback function, which should be 0 for success or
299  * anything else to indicate failure; or -EINVAL if the function pointer
300  * is null.
301  */
302 int clkdm_for_each(int (*fn)(struct clockdomain *clkdm))
303 {
304 	struct clockdomain *clkdm;
305 	int ret = 0;
306 
307 	if (!fn)
308 		return -EINVAL;
309 
310 	mutex_lock(&clkdm_mutex);
311 	list_for_each_entry(clkdm, &clkdm_list, node) {
312 		ret = (*fn)(clkdm);
313 		if (ret)
314 			break;
315 	}
316 	mutex_unlock(&clkdm_mutex);
317 
318 	return ret;
319 }
320 
321 
322 /**
323  * clkdm_get_pwrdm - return a ptr to the pwrdm that this clkdm resides in
324  * @clkdm: struct clockdomain *
325  *
326  * Return a pointer to the struct powerdomain that the specified clockdomain
327  * 'clkdm' exists in, or returns NULL if clkdm argument is NULL.
328  */
329 struct powerdomain *clkdm_get_pwrdm(struct clockdomain *clkdm)
330 {
331 	if (!clkdm)
332 		return NULL;
333 
334 	return clkdm->pwrdm.ptr;
335 }
336 
337 
338 /* Hardware clockdomain control */
339 
340 /**
341  * omap2_clkdm_clktrctrl_read - read the clkdm's current state transition mode
342  * @clk: struct clk * of a clockdomain
343  *
344  * Return the clockdomain's current state transition mode from the
345  * corresponding domain CM_CLKSTCTRL register.	Returns -EINVAL if clk
346  * is NULL or the current mode upon success.
347  */
348 static int omap2_clkdm_clktrctrl_read(struct clockdomain *clkdm)
349 {
350 	u32 v;
351 
352 	if (!clkdm)
353 		return -EINVAL;
354 
355 	v = cm_read_mod_reg(clkdm->pwrdm.ptr->prcm_offs, CM_CLKSTCTRL);
356 	v &= clkdm->clktrctrl_mask;
357 	v >>= __ffs(clkdm->clktrctrl_mask);
358 
359 	return v;
360 }
361 
362 /**
363  * omap2_clkdm_sleep - force clockdomain sleep transition
364  * @clkdm: struct clockdomain *
365  *
366  * Instruct the CM to force a sleep transition on the specified
367  * clockdomain 'clkdm'.  Returns -EINVAL if clk is NULL or if
368  * clockdomain does not support software-initiated sleep; 0 upon
369  * success.
370  */
371 int omap2_clkdm_sleep(struct clockdomain *clkdm)
372 {
373 	if (!clkdm)
374 		return -EINVAL;
375 
376 	if (!(clkdm->flags & CLKDM_CAN_FORCE_SLEEP)) {
377 		pr_debug("clockdomain: %s does not support forcing "
378 			 "sleep via software\n", clkdm->name);
379 		return -EINVAL;
380 	}
381 
382 	pr_debug("clockdomain: forcing sleep on %s\n", clkdm->name);
383 
384 	if (cpu_is_omap24xx()) {
385 
386 		cm_set_mod_reg_bits(OMAP24XX_FORCESTATE,
387 				    clkdm->pwrdm.ptr->prcm_offs, PM_PWSTCTRL);
388 
389 	} else if (cpu_is_omap34xx()) {
390 
391 		u32 v = (OMAP34XX_CLKSTCTRL_FORCE_SLEEP <<
392 			 __ffs(clkdm->clktrctrl_mask));
393 
394 		cm_rmw_mod_reg_bits(clkdm->clktrctrl_mask, v,
395 				    clkdm->pwrdm.ptr->prcm_offs, CM_CLKSTCTRL);
396 
397 	} else {
398 		BUG();
399 	};
400 
401 	return 0;
402 }
403 
404 /**
405  * omap2_clkdm_wakeup - force clockdomain wakeup transition
406  * @clkdm: struct clockdomain *
407  *
408  * Instruct the CM to force a wakeup transition on the specified
409  * clockdomain 'clkdm'.  Returns -EINVAL if clkdm is NULL or if the
410  * clockdomain does not support software-controlled wakeup; 0 upon
411  * success.
412  */
413 int omap2_clkdm_wakeup(struct clockdomain *clkdm)
414 {
415 	if (!clkdm)
416 		return -EINVAL;
417 
418 	if (!(clkdm->flags & CLKDM_CAN_FORCE_WAKEUP)) {
419 		pr_debug("clockdomain: %s does not support forcing "
420 			 "wakeup via software\n", clkdm->name);
421 		return -EINVAL;
422 	}
423 
424 	pr_debug("clockdomain: forcing wakeup on %s\n", clkdm->name);
425 
426 	if (cpu_is_omap24xx()) {
427 
428 		cm_clear_mod_reg_bits(OMAP24XX_FORCESTATE,
429 				      clkdm->pwrdm.ptr->prcm_offs, PM_PWSTCTRL);
430 
431 	} else if (cpu_is_omap34xx()) {
432 
433 		u32 v = (OMAP34XX_CLKSTCTRL_FORCE_WAKEUP <<
434 			 __ffs(clkdm->clktrctrl_mask));
435 
436 		cm_rmw_mod_reg_bits(clkdm->clktrctrl_mask, v,
437 				    clkdm->pwrdm.ptr->prcm_offs, CM_CLKSTCTRL);
438 
439 	} else {
440 		BUG();
441 	};
442 
443 	return 0;
444 }
445 
446 /**
447  * omap2_clkdm_allow_idle - enable hwsup idle transitions for clkdm
448  * @clkdm: struct clockdomain *
449  *
450  * Allow the hardware to automatically switch the clockdomain into
451  * active or idle states, as needed by downstream clocks.  If the
452  * clockdomain has any downstream clocks enabled in the clock
453  * framework, wkdep/sleepdep autodependencies are added; this is so
454  * device drivers can read and write to the device.  No return value.
455  */
456 void omap2_clkdm_allow_idle(struct clockdomain *clkdm)
457 {
458 	u32 v;
459 
460 	if (!clkdm)
461 		return;
462 
463 	if (!(clkdm->flags & CLKDM_CAN_ENABLE_AUTO)) {
464 		pr_debug("clock: automatic idle transitions cannot be enabled "
465 			 "on clockdomain %s\n", clkdm->name);
466 		return;
467 	}
468 
469 	pr_debug("clockdomain: enabling automatic idle transitions for %s\n",
470 		 clkdm->name);
471 
472 	if (atomic_read(&clkdm->usecount) > 0)
473 		_clkdm_add_autodeps(clkdm);
474 
475 	if (cpu_is_omap24xx())
476 		v = OMAP24XX_CLKSTCTRL_ENABLE_AUTO;
477 	else if (cpu_is_omap34xx())
478 		v = OMAP34XX_CLKSTCTRL_ENABLE_AUTO;
479 	else
480 		BUG();
481 
482 
483 	cm_rmw_mod_reg_bits(clkdm->clktrctrl_mask,
484 			    v << __ffs(clkdm->clktrctrl_mask),
485 			    clkdm->pwrdm.ptr->prcm_offs,
486 			    CM_CLKSTCTRL);
487 }
488 
489 /**
490  * omap2_clkdm_deny_idle - disable hwsup idle transitions for clkdm
491  * @clkdm: struct clockdomain *
492  *
493  * Prevent the hardware from automatically switching the clockdomain
494  * into inactive or idle states.  If the clockdomain has downstream
495  * clocks enabled in the clock framework, wkdep/sleepdep
496  * autodependencies are removed.  No return value.
497  */
498 void omap2_clkdm_deny_idle(struct clockdomain *clkdm)
499 {
500 	u32 v;
501 
502 	if (!clkdm)
503 		return;
504 
505 	if (!(clkdm->flags & CLKDM_CAN_DISABLE_AUTO)) {
506 		pr_debug("clockdomain: automatic idle transitions cannot be "
507 			 "disabled on %s\n", clkdm->name);
508 		return;
509 	}
510 
511 	pr_debug("clockdomain: disabling automatic idle transitions for %s\n",
512 		 clkdm->name);
513 
514 	if (cpu_is_omap24xx())
515 		v = OMAP24XX_CLKSTCTRL_DISABLE_AUTO;
516 	else if (cpu_is_omap34xx())
517 		v = OMAP34XX_CLKSTCTRL_DISABLE_AUTO;
518 	else
519 		BUG();
520 
521 	cm_rmw_mod_reg_bits(clkdm->clktrctrl_mask,
522 			    v << __ffs(clkdm->clktrctrl_mask),
523 			    clkdm->pwrdm.ptr->prcm_offs, CM_CLKSTCTRL);
524 
525 	if (atomic_read(&clkdm->usecount) > 0)
526 		_clkdm_del_autodeps(clkdm);
527 }
528 
529 
530 /* Clockdomain-to-clock framework interface code */
531 
532 /**
533  * omap2_clkdm_clk_enable - add an enabled downstream clock to this clkdm
534  * @clkdm: struct clockdomain *
535  * @clk: struct clk * of the enabled downstream clock
536  *
537  * Increment the usecount of this clockdomain 'clkdm' and ensure that
538  * it is awake.  Intended to be called by clk_enable() code.  If the
539  * clockdomain is in software-supervised idle mode, force the
540  * clockdomain to wake.  If the clockdomain is in hardware-supervised
541  * idle mode, add clkdm-pwrdm autodependencies, to ensure that devices
542  * in the clockdomain can be read from/written to by on-chip processors.
543  * Returns -EINVAL if passed null pointers; returns 0 upon success or
544  * if the clockdomain is in hwsup idle mode.
545  */
546 int omap2_clkdm_clk_enable(struct clockdomain *clkdm, struct clk *clk)
547 {
548 	int v;
549 
550 	/*
551 	 * XXX Rewrite this code to maintain a list of enabled
552 	 * downstream clocks for debugging purposes?
553 	 */
554 
555 	if (!clkdm || !clk)
556 		return -EINVAL;
557 
558 	if (atomic_inc_return(&clkdm->usecount) > 1)
559 		return 0;
560 
561 	/* Clockdomain now has one enabled downstream clock */
562 
563 	pr_debug("clockdomain: clkdm %s: clk %s now enabled\n", clkdm->name,
564 		 clk->name);
565 
566 	v = omap2_clkdm_clktrctrl_read(clkdm);
567 
568 	if ((cpu_is_omap34xx() && v == OMAP34XX_CLKSTCTRL_ENABLE_AUTO) ||
569 	    (cpu_is_omap24xx() && v == OMAP24XX_CLKSTCTRL_ENABLE_AUTO))
570 		_clkdm_add_autodeps(clkdm);
571 	else
572 		omap2_clkdm_wakeup(clkdm);
573 
574 	pwrdm_wait_transition(clkdm->pwrdm.ptr);
575 
576 	return 0;
577 }
578 
579 /**
580  * omap2_clkdm_clk_disable - remove an enabled downstream clock from this clkdm
581  * @clkdm: struct clockdomain *
582  * @clk: struct clk * of the disabled downstream clock
583  *
584  * Decrement the usecount of this clockdomain 'clkdm'. Intended to be
585  * called by clk_disable() code.  If the usecount goes to 0, put the
586  * clockdomain to sleep (software-supervised mode) or remove the
587  * clkdm-pwrdm autodependencies (hardware-supervised mode).  Returns
588  * -EINVAL if passed null pointers; -ERANGE if the clkdm usecount
589  * underflows and debugging is enabled; or returns 0 upon success or
590  * if the clockdomain is in hwsup idle mode.
591  */
592 int omap2_clkdm_clk_disable(struct clockdomain *clkdm, struct clk *clk)
593 {
594 	int v;
595 
596 	/*
597 	 * XXX Rewrite this code to maintain a list of enabled
598 	 * downstream clocks for debugging purposes?
599 	 */
600 
601 	if (!clkdm || !clk)
602 		return -EINVAL;
603 
604 #ifdef DEBUG
605 	if (atomic_read(&clkdm->usecount) == 0) {
606 		WARN_ON(1); /* underflow */
607 		return -ERANGE;
608 	}
609 #endif
610 
611 	if (atomic_dec_return(&clkdm->usecount) > 0)
612 		return 0;
613 
614 	/* All downstream clocks of this clockdomain are now disabled */
615 
616 	pr_debug("clockdomain: clkdm %s: clk %s now disabled\n", clkdm->name,
617 		 clk->name);
618 
619 	v = omap2_clkdm_clktrctrl_read(clkdm);
620 
621 	if ((cpu_is_omap34xx() && v == OMAP34XX_CLKSTCTRL_ENABLE_AUTO) ||
622 	    (cpu_is_omap24xx() && v == OMAP24XX_CLKSTCTRL_ENABLE_AUTO))
623 		_clkdm_del_autodeps(clkdm);
624 	else
625 		omap2_clkdm_sleep(clkdm);
626 
627 	return 0;
628 }
629 
630