diff options
author | Paul Mundt <lethal@linux-sh.org> | 2009-05-13 07:28:15 -0400 |
---|---|---|
committer | Paul Mundt <lethal@linux-sh.org> | 2009-05-13 07:28:15 -0400 |
commit | cc96eace48fdf0f8925a74c6c1f7ffa512e458d2 (patch) | |
tree | 7a922b498e387495b3665c5428a932d7d328810e /arch/sh | |
parent | a77b5ac0ea8e47c77008d3a9a9976dcfbc01c42a (diff) |
sh: clkfwk: rate table construction and rounding for SH7785.
This adds support for constructing a rate table by looking at potential
divisors for a specified clock. Each FQRMR clock is given its own table.
Presently each table is rebuilt when the parent propagates down a new
rate, so some more logic needs to be added to do this more intelligently.
Additionally, a fairly generic round_rate() implementation is then
layered on top of it, which subsequently provides us with cpufreq support.
Signed-off-by: Paul Mundt <lethal@linux-sh.org>
Diffstat (limited to 'arch/sh')
-rw-r--r-- | arch/sh/include/asm/clock.h | 1 | ||||
-rw-r--r-- | arch/sh/kernel/cpu/clock.c | 3 | ||||
-rw-r--r-- | arch/sh/kernel/cpu/sh4a/clock-sh7785.c | 109 |
3 files changed, 100 insertions, 13 deletions
diff --git a/arch/sh/include/asm/clock.h b/arch/sh/include/asm/clock.h index 40cf3c07d7e6..da681de1500a 100644 --- a/arch/sh/include/asm/clock.h +++ b/arch/sh/include/asm/clock.h | |||
@@ -16,6 +16,7 @@ struct clk_ops { | |||
16 | int (*set_rate)(struct clk *clk, unsigned long rate, int algo_id); | 16 | int (*set_rate)(struct clk *clk, unsigned long rate, int algo_id); |
17 | int (*set_parent)(struct clk *clk, struct clk *parent); | 17 | int (*set_parent)(struct clk *clk, struct clk *parent); |
18 | long (*round_rate)(struct clk *clk, unsigned long rate); | 18 | long (*round_rate)(struct clk *clk, unsigned long rate); |
19 | void (*build_rate_table)(struct clk *clk); | ||
19 | }; | 20 | }; |
20 | 21 | ||
21 | struct clk { | 22 | struct clk { |
diff --git a/arch/sh/kernel/cpu/clock.c b/arch/sh/kernel/cpu/clock.c index 033f4662b59d..56c6e11fa83b 100644 --- a/arch/sh/kernel/cpu/clock.c +++ b/arch/sh/kernel/cpu/clock.c | |||
@@ -63,6 +63,9 @@ void propagate_rate(struct clk *tclk) | |||
63 | list_for_each_entry(clkp, &tclk->children, sibling) { | 63 | list_for_each_entry(clkp, &tclk->children, sibling) { |
64 | if (clkp->ops && clkp->ops->recalc) | 64 | if (clkp->ops && clkp->ops->recalc) |
65 | clkp->rate = clkp->ops->recalc(clkp); | 65 | clkp->rate = clkp->ops->recalc(clkp); |
66 | if (clkp->ops && clkp->ops->build_rate_table) | ||
67 | clkp->ops->build_rate_table(clkp); | ||
68 | |||
66 | propagate_rate(clkp); | 69 | propagate_rate(clkp); |
67 | } | 70 | } |
68 | } | 71 | } |
diff --git a/arch/sh/kernel/cpu/sh4a/clock-sh7785.c b/arch/sh/kernel/cpu/sh4a/clock-sh7785.c index 87584dc81926..b7a32dd1b2db 100644 --- a/arch/sh/kernel/cpu/sh4a/clock-sh7785.c +++ b/arch/sh/kernel/cpu/sh4a/clock-sh7785.c | |||
@@ -13,28 +13,43 @@ | |||
13 | #include <linux/kernel.h> | 13 | #include <linux/kernel.h> |
14 | #include <linux/clk.h> | 14 | #include <linux/clk.h> |
15 | #include <linux/io.h> | 15 | #include <linux/io.h> |
16 | #include <linux/cpufreq.h> | ||
16 | #include <asm/clock.h> | 17 | #include <asm/clock.h> |
17 | #include <asm/freq.h> | 18 | #include <asm/freq.h> |
18 | 19 | ||
19 | static unsigned int div2[] = { 1, 2, 4, 6, 8, 12, 16, 18, | 20 | static unsigned int div2[] = { 1, 2, 4, 6, 8, 12, 16, 18, |
20 | 24, 32, 36, 48 }; | 21 | 24, 32, 36, 48 }; |
21 | struct clk_priv { | 22 | struct clk_priv { |
22 | unsigned int shift; | 23 | unsigned int shift; |
24 | |||
25 | /* allowable divisor bitmap */ | ||
26 | unsigned long div_bitmap; | ||
27 | |||
28 | /* Supportable frequencies + termination entry */ | ||
29 | struct cpufreq_frequency_table freq_table[ARRAY_SIZE(div2)+1]; | ||
23 | }; | 30 | }; |
24 | 31 | ||
25 | #define FRQMR_CLK_DATA(_name, _shift) \ | 32 | #define FRQMR_CLK_DATA(_name, _shift, _div_bitmap) \ |
26 | static struct clk_priv _name##_data = { .shift = _shift, } | 33 | static struct clk_priv _name##_data = { \ |
34 | .shift = _shift, \ | ||
35 | .div_bitmap = _div_bitmap, \ | ||
36 | \ | ||
37 | .freq_table[0] = { \ | ||
38 | .index = 0, \ | ||
39 | .frequency = CPUFREQ_TABLE_END, \ | ||
40 | }, \ | ||
41 | } | ||
27 | 42 | ||
28 | FRQMR_CLK_DATA(pfc, 0); | 43 | FRQMR_CLK_DATA(pfc, 0, 0x0f80); |
29 | FRQMR_CLK_DATA(s3fc, 4); | 44 | FRQMR_CLK_DATA(s3fc, 4, 0x0ff0); |
30 | FRQMR_CLK_DATA(s2fc, 8); | 45 | FRQMR_CLK_DATA(s2fc, 8, 0x0030); |
31 | FRQMR_CLK_DATA(mfc, 12); | 46 | FRQMR_CLK_DATA(mfc, 12, 0x000c); |
32 | FRQMR_CLK_DATA(bfc, 16); | 47 | FRQMR_CLK_DATA(bfc, 16, 0x0fe0); |
33 | FRQMR_CLK_DATA(sfc, 20); | 48 | FRQMR_CLK_DATA(sfc, 20, 0x000c); |
34 | FRQMR_CLK_DATA(ufc, 24); | 49 | FRQMR_CLK_DATA(ufc, 24, 0x000c); |
35 | FRQMR_CLK_DATA(ifc, 28); | 50 | FRQMR_CLK_DATA(ifc, 28, 0x000e); |
36 | 51 | ||
37 | static unsigned long frqmr_clk_recalc(struct clk *clk) | 52 | static unsigned long frqmr_recalc(struct clk *clk) |
38 | { | 53 | { |
39 | struct clk_priv *data = clk->priv; | 54 | struct clk_priv *data = clk->priv; |
40 | unsigned int idx; | 55 | unsigned int idx; |
@@ -49,8 +64,76 @@ static unsigned long frqmr_clk_recalc(struct clk *clk) | |||
49 | return clk->parent->rate * 36 / div2[idx]; | 64 | return clk->parent->rate * 36 / div2[idx]; |
50 | } | 65 | } |
51 | 66 | ||
67 | static void frqmr_build_rate_table(struct clk *clk) | ||
68 | { | ||
69 | struct clk_priv *data = clk->priv; | ||
70 | int i, entry; | ||
71 | |||
72 | for (i = entry = 0; i < ARRAY_SIZE(div2); i++) { | ||
73 | if ((data->div_bitmap & (1 << i)) == 0) | ||
74 | continue; | ||
75 | |||
76 | data->freq_table[entry].index = entry; | ||
77 | data->freq_table[entry].frequency = | ||
78 | clk->parent->rate * 36 / div2[i]; | ||
79 | |||
80 | entry++; | ||
81 | } | ||
82 | |||
83 | if (entry == 0) { | ||
84 | pr_warning("clkfwk: failed to build frequency table " | ||
85 | "for \"%s\" clk!\n", clk->name); | ||
86 | return; | ||
87 | } | ||
88 | |||
89 | /* Termination entry */ | ||
90 | data->freq_table[entry].index = entry; | ||
91 | data->freq_table[entry].frequency = CPUFREQ_TABLE_END; | ||
92 | } | ||
93 | |||
94 | static long frqmr_round_rate(struct clk *clk, unsigned long rate) | ||
95 | { | ||
96 | struct clk_priv *data = clk->priv; | ||
97 | unsigned long rate_error, rate_error_prev = ~0UL; | ||
98 | unsigned long rate_best_fit = rate; | ||
99 | unsigned long highest, lowest; | ||
100 | int i; | ||
101 | |||
102 | highest = lowest = 0; | ||
103 | |||
104 | for (i = 0; data->freq_table[i].frequency != CPUFREQ_TABLE_END; i++) { | ||
105 | unsigned long freq = data->freq_table[i].frequency; | ||
106 | |||
107 | if (freq == CPUFREQ_ENTRY_INVALID) | ||
108 | continue; | ||
109 | |||
110 | if (freq > highest) | ||
111 | highest = freq; | ||
112 | if (freq < lowest) | ||
113 | lowest = freq; | ||
114 | |||
115 | rate_error = abs(freq - rate); | ||
116 | if (rate_error < rate_error_prev) { | ||
117 | rate_best_fit = freq; | ||
118 | rate_error_prev = rate_error; | ||
119 | } | ||
120 | |||
121 | if (rate_error == 0) | ||
122 | break; | ||
123 | } | ||
124 | |||
125 | if (rate >= highest) | ||
126 | rate_best_fit = highest; | ||
127 | if (rate <= lowest) | ||
128 | rate_best_fit = lowest; | ||
129 | |||
130 | return rate_best_fit; | ||
131 | } | ||
132 | |||
52 | static struct clk_ops frqmr_clk_ops = { | 133 | static struct clk_ops frqmr_clk_ops = { |
53 | .recalc = frqmr_clk_recalc, | 134 | .recalc = frqmr_recalc, |
135 | .build_rate_table = frqmr_build_rate_table, | ||
136 | .round_rate = frqmr_round_rate, | ||
54 | }; | 137 | }; |
55 | 138 | ||
56 | /* | 139 | /* |