aboutsummaryrefslogtreecommitdiffstats
path: root/arch
diff options
context:
space:
mode:
authorPaul Mundt <lethal@linux-sh.org>2009-05-13 07:28:15 -0400
committerPaul Mundt <lethal@linux-sh.org>2009-05-13 07:28:15 -0400
commitcc96eace48fdf0f8925a74c6c1f7ffa512e458d2 (patch)
tree7a922b498e387495b3665c5428a932d7d328810e /arch
parenta77b5ac0ea8e47c77008d3a9a9976dcfbc01c42a (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')
-rw-r--r--arch/sh/include/asm/clock.h1
-rw-r--r--arch/sh/kernel/cpu/clock.c3
-rw-r--r--arch/sh/kernel/cpu/sh4a/clock-sh7785.c109
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
21struct clk { 22struct 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
19static unsigned int div2[] = { 1, 2, 4, 6, 8, 12, 16, 18, 20static unsigned int div2[] = { 1, 2, 4, 6, 8, 12, 16, 18,
20 24, 32, 36, 48 }; 21 24, 32, 36, 48 };
21struct clk_priv { 22struct 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) \
26static struct clk_priv _name##_data = { .shift = _shift, } 33static 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
28FRQMR_CLK_DATA(pfc, 0); 43FRQMR_CLK_DATA(pfc, 0, 0x0f80);
29FRQMR_CLK_DATA(s3fc, 4); 44FRQMR_CLK_DATA(s3fc, 4, 0x0ff0);
30FRQMR_CLK_DATA(s2fc, 8); 45FRQMR_CLK_DATA(s2fc, 8, 0x0030);
31FRQMR_CLK_DATA(mfc, 12); 46FRQMR_CLK_DATA(mfc, 12, 0x000c);
32FRQMR_CLK_DATA(bfc, 16); 47FRQMR_CLK_DATA(bfc, 16, 0x0fe0);
33FRQMR_CLK_DATA(sfc, 20); 48FRQMR_CLK_DATA(sfc, 20, 0x000c);
34FRQMR_CLK_DATA(ufc, 24); 49FRQMR_CLK_DATA(ufc, 24, 0x000c);
35FRQMR_CLK_DATA(ifc, 28); 50FRQMR_CLK_DATA(ifc, 28, 0x000e);
36 51
37static unsigned long frqmr_clk_recalc(struct clk *clk) 52static 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
67static 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
94static 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
52static struct clk_ops frqmr_clk_ops = { 133static 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/*