aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/sh
diff options
context:
space:
mode:
authorPaul Mundt <lethal@linux-sh.org>2012-05-25 03:34:48 -0400
committerPaul Mundt <lethal@linux-sh.org>2012-05-25 03:34:48 -0400
commit764f4e4e33d18cde4dcaf8a0d860b749c6d6d08b (patch)
tree1e498ea454a2392b557a0f562170946838dece39 /drivers/sh
parent0fa22168e00106797f28b2655aaefd0d16a6e67b (diff)
sh: clkfwk: Use shared sh_clk_div_enable/disable().
This introduces a new flag for clocks that need to have their divisor ratio set back to their initial mask at disable time to prevent interactivity problems with the clock stop bit (presently div6 only). With this in place it's possible to handle the corner case on top of the div4 op without any particular need for leaving things split out. Signed-off-by: Paul Mundt <lethal@linux-sh.org>
Diffstat (limited to 'drivers/sh')
-rw-r--r--drivers/sh/clk/cpg.c77
1 files changed, 34 insertions, 43 deletions
diff --git a/drivers/sh/clk/cpg.c b/drivers/sh/clk/cpg.c
index 29ee5f7072a4..06537f2b2fb8 100644
--- a/drivers/sh/clk/cpg.c
+++ b/drivers/sh/clk/cpg.c
@@ -14,6 +14,8 @@
14#include <linux/io.h> 14#include <linux/io.h>
15#include <linux/sh_clk.h> 15#include <linux/sh_clk.h>
16 16
17#define CPG_CKSTP_BIT BIT(8)
18
17static unsigned int sh_clk_read(struct clk *clk) 19static unsigned int sh_clk_read(struct clk *clk)
18{ 20{
19 if (clk->flags & CLK_ENABLE_REG_8BIT) 21 if (clk->flags & CLK_ENABLE_REG_8BIT)
@@ -122,6 +124,30 @@ static int sh_clk_div_set_rate(struct clk *clk, unsigned long rate)
122 return 0; 124 return 0;
123} 125}
124 126
127static int sh_clk_div_enable(struct clk *clk)
128{
129 sh_clk_write(sh_clk_read(clk) & ~CPG_CKSTP_BIT, clk);
130 return 0;
131}
132
133static void sh_clk_div_disable(struct clk *clk)
134{
135 unsigned int val;
136
137 val = sh_clk_read(clk);
138 val |= CPG_CKSTP_BIT;
139
140 /*
141 * div6 clocks require the divisor field to be non-zero or the
142 * above CKSTP toggle silently fails. Ensure that the divisor
143 * array is reset to its initial state on disable.
144 */
145 if (clk->flags & CLK_MASK_DIV_ON_DISABLE)
146 val |= clk->div_mask;
147
148 sh_clk_write(val, clk);
149}
150
125/* 151/*
126 * div6 support 152 * div6 support
127 */ 153 */
@@ -174,44 +200,20 @@ static int sh_clk_div6_set_parent(struct clk *clk, struct clk *parent)
174 return 0; 200 return 0;
175} 201}
176 202
177static int sh_clk_div6_enable(struct clk *clk)
178{
179 unsigned long value;
180 int ret;
181
182 ret = sh_clk_div_set_rate(clk, clk->rate);
183 if (ret == 0) {
184 value = sh_clk_read(clk);
185 value &= ~0x100; /* clear stop bit to enable clock */
186 sh_clk_write(value, clk);
187 }
188 return ret;
189}
190
191static void sh_clk_div6_disable(struct clk *clk)
192{
193 unsigned long value;
194
195 value = sh_clk_read(clk);
196 value |= 0x100; /* stop clock */
197 value |= clk->div_mask; /* VDIV bits must be non-zero, overwrite divider */
198 sh_clk_write(value, clk);
199}
200
201static struct sh_clk_ops sh_clk_div6_clk_ops = { 203static struct sh_clk_ops sh_clk_div6_clk_ops = {
202 .recalc = sh_clk_div_recalc, 204 .recalc = sh_clk_div_recalc,
203 .round_rate = sh_clk_div_round_rate, 205 .round_rate = sh_clk_div_round_rate,
204 .set_rate = sh_clk_div_set_rate, 206 .set_rate = sh_clk_div_set_rate,
205 .enable = sh_clk_div6_enable, 207 .enable = sh_clk_div_enable,
206 .disable = sh_clk_div6_disable, 208 .disable = sh_clk_div_disable,
207}; 209};
208 210
209static struct sh_clk_ops sh_clk_div6_reparent_clk_ops = { 211static struct sh_clk_ops sh_clk_div6_reparent_clk_ops = {
210 .recalc = sh_clk_div_recalc, 212 .recalc = sh_clk_div_recalc,
211 .round_rate = sh_clk_div_round_rate, 213 .round_rate = sh_clk_div_round_rate,
212 .set_rate = sh_clk_div_set_rate, 214 .set_rate = sh_clk_div_set_rate,
213 .enable = sh_clk_div6_enable, 215 .enable = sh_clk_div_enable,
214 .disable = sh_clk_div6_disable, 216 .disable = sh_clk_div_disable,
215 .set_parent = sh_clk_div6_set_parent, 217 .set_parent = sh_clk_div6_set_parent,
216}; 218};
217 219
@@ -325,17 +327,6 @@ static int sh_clk_div4_set_parent(struct clk *clk, struct clk *parent)
325 return 0; 327 return 0;
326} 328}
327 329
328static int sh_clk_div4_enable(struct clk *clk)
329{
330 sh_clk_write(sh_clk_read(clk) & ~(1 << 8), clk);
331 return 0;
332}
333
334static void sh_clk_div4_disable(struct clk *clk)
335{
336 sh_clk_write(sh_clk_read(clk) | (1 << 8), clk);
337}
338
339static struct sh_clk_ops sh_clk_div4_clk_ops = { 330static struct sh_clk_ops sh_clk_div4_clk_ops = {
340 .recalc = sh_clk_div_recalc, 331 .recalc = sh_clk_div_recalc,
341 .set_rate = sh_clk_div_set_rate, 332 .set_rate = sh_clk_div_set_rate,
@@ -346,16 +337,16 @@ static struct sh_clk_ops sh_clk_div4_enable_clk_ops = {
346 .recalc = sh_clk_div_recalc, 337 .recalc = sh_clk_div_recalc,
347 .set_rate = sh_clk_div_set_rate, 338 .set_rate = sh_clk_div_set_rate,
348 .round_rate = sh_clk_div_round_rate, 339 .round_rate = sh_clk_div_round_rate,
349 .enable = sh_clk_div4_enable, 340 .enable = sh_clk_div_enable,
350 .disable = sh_clk_div4_disable, 341 .disable = sh_clk_div_disable,
351}; 342};
352 343
353static struct sh_clk_ops sh_clk_div4_reparent_clk_ops = { 344static struct sh_clk_ops sh_clk_div4_reparent_clk_ops = {
354 .recalc = sh_clk_div_recalc, 345 .recalc = sh_clk_div_recalc,
355 .set_rate = sh_clk_div_set_rate, 346 .set_rate = sh_clk_div_set_rate,
356 .round_rate = sh_clk_div_round_rate, 347 .round_rate = sh_clk_div_round_rate,
357 .enable = sh_clk_div4_enable, 348 .enable = sh_clk_div_enable,
358 .disable = sh_clk_div4_disable, 349 .disable = sh_clk_div_disable,
359 .set_parent = sh_clk_div4_set_parent, 350 .set_parent = sh_clk_div4_set_parent,
360}; 351};
361 352