Linux Kernel  3.7.1
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Groups Pages
clockdomain2xxx_3xxx.c
Go to the documentation of this file.
1 /*
2  * OMAP2 and OMAP3 clockdomain control
3  *
4  * Copyright (C) 2008-2010 Texas Instruments, Inc.
5  * Copyright (C) 2008-2010 Nokia Corporation
6  *
7  * Derived from mach-omap2/clockdomain.c written by Paul Walmsley
8  * Rajendra Nayak <[email protected]>
9  *
10  * This program is free software; you can redistribute it and/or modify
11  * it under the terms of the GNU General Public License version 2 as
12  * published by the Free Software Foundation.
13  */
14 
15 #include <linux/types.h>
16 #include <plat/prcm.h>
17 #include "prm.h"
18 #include "prm2xxx_3xxx.h"
19 #include "cm.h"
20 #include "cm2xxx_3xxx.h"
21 #include "cm-regbits-24xx.h"
22 #include "cm-regbits-34xx.h"
23 #include "prm-regbits-24xx.h"
24 #include "clockdomain.h"
25 
26 static int omap2_clkdm_add_wkdep(struct clockdomain *clkdm1,
27  struct clockdomain *clkdm2)
28 {
29  omap2_prm_set_mod_reg_bits((1 << clkdm2->dep_bit),
30  clkdm1->pwrdm.ptr->prcm_offs, PM_WKDEP);
31  return 0;
32 }
33 
34 static int omap2_clkdm_del_wkdep(struct clockdomain *clkdm1,
35  struct clockdomain *clkdm2)
36 {
38  clkdm1->pwrdm.ptr->prcm_offs, PM_WKDEP);
39  return 0;
40 }
41 
42 static int omap2_clkdm_read_wkdep(struct clockdomain *clkdm1,
43  struct clockdomain *clkdm2)
44 {
45  return omap2_prm_read_mod_bits_shift(clkdm1->pwrdm.ptr->prcm_offs,
46  PM_WKDEP, (1 << clkdm2->dep_bit));
47 }
48 
49 static int omap2_clkdm_clear_all_wkdeps(struct clockdomain *clkdm)
50 {
51  struct clkdm_dep *cd;
52  u32 mask = 0;
53 
54  for (cd = clkdm->wkdep_srcs; cd && cd->clkdm_name; cd++) {
55  if (!cd->clkdm)
56  continue; /* only happens if data is erroneous */
57 
58  /* PRM accesses are slow, so minimize them */
59  mask |= 1 << cd->clkdm->dep_bit;
60  atomic_set(&cd->wkdep_usecount, 0);
61  }
62 
63  omap2_prm_clear_mod_reg_bits(mask, clkdm->pwrdm.ptr->prcm_offs,
64  PM_WKDEP);
65  return 0;
66 }
67 
68 static int omap3_clkdm_add_sleepdep(struct clockdomain *clkdm1,
69  struct clockdomain *clkdm2)
70 {
71  omap2_cm_set_mod_reg_bits((1 << clkdm2->dep_bit),
72  clkdm1->pwrdm.ptr->prcm_offs,
74  return 0;
75 }
76 
77 static int omap3_clkdm_del_sleepdep(struct clockdomain *clkdm1,
78  struct clockdomain *clkdm2)
79 {
80  omap2_cm_clear_mod_reg_bits((1 << clkdm2->dep_bit),
81  clkdm1->pwrdm.ptr->prcm_offs,
83  return 0;
84 }
85 
86 static int omap3_clkdm_read_sleepdep(struct clockdomain *clkdm1,
87  struct clockdomain *clkdm2)
88 {
89  return omap2_prm_read_mod_bits_shift(clkdm1->pwrdm.ptr->prcm_offs,
90  OMAP3430_CM_SLEEPDEP, (1 << clkdm2->dep_bit));
91 }
92 
93 static int omap3_clkdm_clear_all_sleepdeps(struct clockdomain *clkdm)
94 {
95  struct clkdm_dep *cd;
96  u32 mask = 0;
97 
98  for (cd = clkdm->sleepdep_srcs; cd && cd->clkdm_name; cd++) {
99  if (!cd->clkdm)
100  continue; /* only happens if data is erroneous */
101 
102  /* PRM accesses are slow, so minimize them */
103  mask |= 1 << cd->clkdm->dep_bit;
104  atomic_set(&cd->sleepdep_usecount, 0);
105  }
106  omap2_prm_clear_mod_reg_bits(mask, clkdm->pwrdm.ptr->prcm_offs,
108  return 0;
109 }
110 
111 static int omap2_clkdm_sleep(struct clockdomain *clkdm)
112 {
114  clkdm->pwrdm.ptr->prcm_offs,
116  return 0;
117 }
118 
119 static int omap2_clkdm_wakeup(struct clockdomain *clkdm)
120 {
122  clkdm->pwrdm.ptr->prcm_offs,
124  return 0;
125 }
126 
127 static void omap2_clkdm_allow_idle(struct clockdomain *clkdm)
128 {
129  if (atomic_read(&clkdm->usecount) > 0)
130  _clkdm_add_autodeps(clkdm);
131 
132  omap2xxx_cm_clkdm_enable_hwsup(clkdm->pwrdm.ptr->prcm_offs,
133  clkdm->clktrctrl_mask);
134 }
135 
136 static void omap2_clkdm_deny_idle(struct clockdomain *clkdm)
137 {
138  omap2xxx_cm_clkdm_disable_hwsup(clkdm->pwrdm.ptr->prcm_offs,
139  clkdm->clktrctrl_mask);
140 
141  if (atomic_read(&clkdm->usecount) > 0)
142  _clkdm_del_autodeps(clkdm);
143 }
144 
145 static void _enable_hwsup(struct clockdomain *clkdm)
146 {
147  if (cpu_is_omap24xx())
148  omap2xxx_cm_clkdm_enable_hwsup(clkdm->pwrdm.ptr->prcm_offs,
149  clkdm->clktrctrl_mask);
150  else if (cpu_is_omap34xx())
151  omap3xxx_cm_clkdm_enable_hwsup(clkdm->pwrdm.ptr->prcm_offs,
152  clkdm->clktrctrl_mask);
153 }
154 
155 static void _disable_hwsup(struct clockdomain *clkdm)
156 {
157  if (cpu_is_omap24xx())
158  omap2xxx_cm_clkdm_disable_hwsup(clkdm->pwrdm.ptr->prcm_offs,
159  clkdm->clktrctrl_mask);
160  else if (cpu_is_omap34xx())
161  omap3xxx_cm_clkdm_disable_hwsup(clkdm->pwrdm.ptr->prcm_offs,
162  clkdm->clktrctrl_mask);
163 }
164 
165 static int omap3_clkdm_sleep(struct clockdomain *clkdm)
166 {
167  omap3xxx_cm_clkdm_force_sleep(clkdm->pwrdm.ptr->prcm_offs,
168  clkdm->clktrctrl_mask);
169  return 0;
170 }
171 
172 static int omap3_clkdm_wakeup(struct clockdomain *clkdm)
173 {
174  omap3xxx_cm_clkdm_force_wakeup(clkdm->pwrdm.ptr->prcm_offs,
175  clkdm->clktrctrl_mask);
176  return 0;
177 }
178 
179 static int omap2_clkdm_clk_enable(struct clockdomain *clkdm)
180 {
181  bool hwsup = false;
182 
183  if (!clkdm->clktrctrl_mask)
184  return 0;
185 
186  hwsup = omap2_cm_is_clkdm_in_hwsup(clkdm->pwrdm.ptr->prcm_offs,
187  clkdm->clktrctrl_mask);
188 
189  if (hwsup) {
190  /* Disable HW transitions when we are changing deps */
191  _disable_hwsup(clkdm);
192  _clkdm_add_autodeps(clkdm);
193  _enable_hwsup(clkdm);
194  } else {
195  if (clkdm->flags & CLKDM_CAN_FORCE_WAKEUP)
196  omap2_clkdm_wakeup(clkdm);
197  }
198 
199  return 0;
200 }
201 
202 static int omap2_clkdm_clk_disable(struct clockdomain *clkdm)
203 {
204  bool hwsup = false;
205 
206  if (!clkdm->clktrctrl_mask)
207  return 0;
208 
209  hwsup = omap2_cm_is_clkdm_in_hwsup(clkdm->pwrdm.ptr->prcm_offs,
210  clkdm->clktrctrl_mask);
211 
212  if (hwsup) {
213  /* Disable HW transitions when we are changing deps */
214  _disable_hwsup(clkdm);
215  _clkdm_del_autodeps(clkdm);
216  _enable_hwsup(clkdm);
217  } else {
218  if (clkdm->flags & CLKDM_CAN_FORCE_SLEEP)
219  omap2_clkdm_sleep(clkdm);
220  }
221 
222  return 0;
223 }
224 
225 static void omap3_clkdm_allow_idle(struct clockdomain *clkdm)
226 {
227  if (atomic_read(&clkdm->usecount) > 0)
228  _clkdm_add_autodeps(clkdm);
229 
230  omap3xxx_cm_clkdm_enable_hwsup(clkdm->pwrdm.ptr->prcm_offs,
231  clkdm->clktrctrl_mask);
232 }
233 
234 static void omap3_clkdm_deny_idle(struct clockdomain *clkdm)
235 {
236  omap3xxx_cm_clkdm_disable_hwsup(clkdm->pwrdm.ptr->prcm_offs,
237  clkdm->clktrctrl_mask);
238 
239  if (atomic_read(&clkdm->usecount) > 0)
240  _clkdm_del_autodeps(clkdm);
241 }
242 
243 static int omap3xxx_clkdm_clk_enable(struct clockdomain *clkdm)
244 {
245  bool hwsup = false;
246 
247  if (!clkdm->clktrctrl_mask)
248  return 0;
249 
250  /*
251  * The CLKDM_MISSING_IDLE_REPORTING flag documentation has
252  * more details on the unpleasant problem this is working
253  * around
254  */
255  if ((clkdm->flags & CLKDM_MISSING_IDLE_REPORTING) &&
256  (clkdm->flags & CLKDM_CAN_FORCE_WAKEUP)) {
257  omap3_clkdm_wakeup(clkdm);
258  return 0;
259  }
260 
261  hwsup = omap2_cm_is_clkdm_in_hwsup(clkdm->pwrdm.ptr->prcm_offs,
262  clkdm->clktrctrl_mask);
263 
264  if (hwsup) {
265  /* Disable HW transitions when we are changing deps */
266  _disable_hwsup(clkdm);
267  _clkdm_add_autodeps(clkdm);
268  _enable_hwsup(clkdm);
269  } else {
270  if (clkdm->flags & CLKDM_CAN_FORCE_WAKEUP)
271  omap3_clkdm_wakeup(clkdm);
272  }
273 
274  return 0;
275 }
276 
277 static int omap3xxx_clkdm_clk_disable(struct clockdomain *clkdm)
278 {
279  bool hwsup = false;
280 
281  if (!clkdm->clktrctrl_mask)
282  return 0;
283 
284  /*
285  * The CLKDM_MISSING_IDLE_REPORTING flag documentation has
286  * more details on the unpleasant problem this is working
287  * around
288  */
289  if (clkdm->flags & CLKDM_MISSING_IDLE_REPORTING &&
290  !(clkdm->flags & CLKDM_CAN_FORCE_SLEEP)) {
291  _enable_hwsup(clkdm);
292  return 0;
293  }
294 
295  hwsup = omap2_cm_is_clkdm_in_hwsup(clkdm->pwrdm.ptr->prcm_offs,
296  clkdm->clktrctrl_mask);
297 
298  if (hwsup) {
299  /* Disable HW transitions when we are changing deps */
300  _disable_hwsup(clkdm);
301  _clkdm_del_autodeps(clkdm);
302  _enable_hwsup(clkdm);
303  } else {
304  if (clkdm->flags & CLKDM_CAN_FORCE_SLEEP)
305  omap3_clkdm_sleep(clkdm);
306  }
307 
308  return 0;
309 }
310 
312  .clkdm_add_wkdep = omap2_clkdm_add_wkdep,
313  .clkdm_del_wkdep = omap2_clkdm_del_wkdep,
314  .clkdm_read_wkdep = omap2_clkdm_read_wkdep,
315  .clkdm_clear_all_wkdeps = omap2_clkdm_clear_all_wkdeps,
316  .clkdm_sleep = omap2_clkdm_sleep,
317  .clkdm_wakeup = omap2_clkdm_wakeup,
318  .clkdm_allow_idle = omap2_clkdm_allow_idle,
319  .clkdm_deny_idle = omap2_clkdm_deny_idle,
320  .clkdm_clk_enable = omap2_clkdm_clk_enable,
321  .clkdm_clk_disable = omap2_clkdm_clk_disable,
322 };
323 
325  .clkdm_add_wkdep = omap2_clkdm_add_wkdep,
326  .clkdm_del_wkdep = omap2_clkdm_del_wkdep,
327  .clkdm_read_wkdep = omap2_clkdm_read_wkdep,
328  .clkdm_clear_all_wkdeps = omap2_clkdm_clear_all_wkdeps,
329  .clkdm_add_sleepdep = omap3_clkdm_add_sleepdep,
330  .clkdm_del_sleepdep = omap3_clkdm_del_sleepdep,
331  .clkdm_read_sleepdep = omap3_clkdm_read_sleepdep,
332  .clkdm_clear_all_sleepdeps = omap3_clkdm_clear_all_sleepdeps,
333  .clkdm_sleep = omap3_clkdm_sleep,
334  .clkdm_wakeup = omap3_clkdm_wakeup,
335  .clkdm_allow_idle = omap3_clkdm_allow_idle,
336  .clkdm_deny_idle = omap3_clkdm_deny_idle,
337  .clkdm_clk_enable = omap3xxx_clkdm_clk_enable,
338  .clkdm_clk_disable = omap3xxx_clkdm_clk_disable,
339 };