aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/clk/shmobile/clk-div6.c
diff options
context:
space:
mode:
authorUlrich Hecht <ulrich.hecht+renesas@gmail.com>2014-11-07 10:51:07 -0500
committerGeert Uytterhoeven <geert+renesas@glider.be>2014-11-12 08:24:08 -0500
commitc6d67fb037f4eaafeec495611dd909c51d33f6a2 (patch)
tree14244adf6580d69d550d6fb763b8c17f5be63e42 /drivers/clk/shmobile/clk-div6.c
parent206c5f60a3d902bc4b56dab2de3e88de5eb06108 (diff)
clk: shmobile: div6: support selectable-input clocks
Support for setting the parent at initialization time based on the current hardware configuration in DIV6 clocks with selectable parents as found in the r8a73a4, r8a7740, sh73a0, and other SoCs. Signed-off-by: Ulrich Hecht <ulrich.hecht+renesas@gmail.com> Signed-off-by: Geert Uytterhoeven <geert+renesas@glider.be>
Diffstat (limited to 'drivers/clk/shmobile/clk-div6.c')
-rw-r--r--drivers/clk/shmobile/clk-div6.c113
1 files changed, 101 insertions, 12 deletions
diff --git a/drivers/clk/shmobile/clk-div6.c b/drivers/clk/shmobile/clk-div6.c
index f065f694cb65..639241e31e03 100644
--- a/drivers/clk/shmobile/clk-div6.c
+++ b/drivers/clk/shmobile/clk-div6.c
@@ -32,6 +32,9 @@ struct div6_clock {
32 struct clk_hw hw; 32 struct clk_hw hw;
33 void __iomem *reg; 33 void __iomem *reg;
34 unsigned int div; 34 unsigned int div;
35 u32 src_shift;
36 u32 src_width;
37 u8 *parents;
35}; 38};
36 39
37#define to_div6_clock(_hw) container_of(_hw, struct div6_clock, hw) 40#define to_div6_clock(_hw) container_of(_hw, struct div6_clock, hw)
@@ -39,8 +42,11 @@ struct div6_clock {
39static int cpg_div6_clock_enable(struct clk_hw *hw) 42static int cpg_div6_clock_enable(struct clk_hw *hw)
40{ 43{
41 struct div6_clock *clock = to_div6_clock(hw); 44 struct div6_clock *clock = to_div6_clock(hw);
45 u32 val;
42 46
43 clk_writel(CPG_DIV6_DIV(clock->div - 1), clock->reg); 47 val = (clk_readl(clock->reg) & ~(CPG_DIV6_DIV_MASK | CPG_DIV6_CKSTP))
48 | CPG_DIV6_DIV(clock->div - 1);
49 clk_writel(val, clock->reg);
44 50
45 return 0; 51 return 0;
46} 52}
@@ -52,7 +58,7 @@ static void cpg_div6_clock_disable(struct clk_hw *hw)
52 /* DIV6 clocks require the divisor field to be non-zero when stopping 58 /* DIV6 clocks require the divisor field to be non-zero when stopping
53 * the clock. 59 * the clock.
54 */ 60 */
55 clk_writel(CPG_DIV6_CKSTP | CPG_DIV6_DIV(CPG_DIV6_DIV_MASK), 61 clk_writel(clk_readl(clock->reg) | CPG_DIV6_CKSTP | CPG_DIV6_DIV_MASK,
56 clock->reg); 62 clock->reg);
57} 63}
58 64
@@ -94,12 +100,53 @@ static int cpg_div6_clock_set_rate(struct clk_hw *hw, unsigned long rate,
94{ 100{
95 struct div6_clock *clock = to_div6_clock(hw); 101 struct div6_clock *clock = to_div6_clock(hw);
96 unsigned int div = cpg_div6_clock_calc_div(rate, parent_rate); 102 unsigned int div = cpg_div6_clock_calc_div(rate, parent_rate);
103 u32 val;
97 104
98 clock->div = div; 105 clock->div = div;
99 106
107 val = clk_readl(clock->reg) & ~CPG_DIV6_DIV_MASK;
100 /* Only program the new divisor if the clock isn't stopped. */ 108 /* Only program the new divisor if the clock isn't stopped. */
101 if (!(clk_readl(clock->reg) & CPG_DIV6_CKSTP)) 109 if (!(val & CPG_DIV6_CKSTP))
102 clk_writel(CPG_DIV6_DIV(clock->div - 1), clock->reg); 110 clk_writel(val | CPG_DIV6_DIV(clock->div - 1), clock->reg);
111
112 return 0;
113}
114
115static u8 cpg_div6_clock_get_parent(struct clk_hw *hw)
116{
117 struct div6_clock *clock = to_div6_clock(hw);
118 unsigned int i;
119 u8 hw_index;
120
121 if (clock->src_width == 0)
122 return 0;
123
124 hw_index = (clk_readl(clock->reg) >> clock->src_shift) &
125 (BIT(clock->src_width) - 1);
126 for (i = 0; i < __clk_get_num_parents(hw->clk); i++) {
127 if (clock->parents[i] == hw_index)
128 return i;
129 }
130
131 pr_err("%s: %s DIV6 clock set to invalid parent %u\n",
132 __func__, __clk_get_name(hw->clk), hw_index);
133 return 0;
134}
135
136static int cpg_div6_clock_set_parent(struct clk_hw *hw, u8 index)
137{
138 struct div6_clock *clock = to_div6_clock(hw);
139 u8 hw_index;
140 u32 mask;
141
142 if (index >= __clk_get_num_parents(hw->clk))
143 return -EINVAL;
144
145 mask = ~((BIT(clock->src_width) - 1) << clock->src_shift);
146 hw_index = clock->parents[index];
147
148 clk_writel((clk_readl(clock->reg) & mask) |
149 (hw_index << clock->src_shift), clock->reg);
103 150
104 return 0; 151 return 0;
105} 152}
@@ -108,6 +155,8 @@ static const struct clk_ops cpg_div6_clock_ops = {
108 .enable = cpg_div6_clock_enable, 155 .enable = cpg_div6_clock_enable,
109 .disable = cpg_div6_clock_disable, 156 .disable = cpg_div6_clock_disable,
110 .is_enabled = cpg_div6_clock_is_enabled, 157 .is_enabled = cpg_div6_clock_is_enabled,
158 .get_parent = cpg_div6_clock_get_parent,
159 .set_parent = cpg_div6_clock_set_parent,
111 .recalc_rate = cpg_div6_clock_recalc_rate, 160 .recalc_rate = cpg_div6_clock_recalc_rate,
112 .round_rate = cpg_div6_clock_round_rate, 161 .round_rate = cpg_div6_clock_round_rate,
113 .set_rate = cpg_div6_clock_set_rate, 162 .set_rate = cpg_div6_clock_set_rate,
@@ -115,20 +164,33 @@ static const struct clk_ops cpg_div6_clock_ops = {
115 164
116static void __init cpg_div6_clock_init(struct device_node *np) 165static void __init cpg_div6_clock_init(struct device_node *np)
117{ 166{
167 unsigned int num_parents, valid_parents;
168 const char **parent_names;
118 struct clk_init_data init; 169 struct clk_init_data init;
119 struct div6_clock *clock; 170 struct div6_clock *clock;
120 const char *parent_name;
121 const char *name; 171 const char *name;
122 struct clk *clk; 172 struct clk *clk;
173 unsigned int i;
123 int ret; 174 int ret;
124 175
125 clock = kzalloc(sizeof(*clock), GFP_KERNEL); 176 clock = kzalloc(sizeof(*clock), GFP_KERNEL);
126 if (!clock) { 177 if (!clock)
127 pr_err("%s: failed to allocate %s DIV6 clock\n", 178 return;
179
180 num_parents = of_clk_get_parent_count(np);
181 if (num_parents < 1) {
182 pr_err("%s: no parent found for %s DIV6 clock\n",
128 __func__, np->name); 183 __func__, np->name);
129 return; 184 return;
130 } 185 }
131 186
187 clock->parents = kmalloc_array(num_parents, sizeof(*clock->parents),
188 GFP_KERNEL);
189 parent_names = kmalloc_array(num_parents, sizeof(*parent_names),
190 GFP_KERNEL);
191 if (!parent_names)
192 return;
193
132 /* Remap the clock register and read the divisor. Disabling the 194 /* Remap the clock register and read the divisor. Disabling the
133 * clock overwrites the divisor, so we need to cache its value for the 195 * clock overwrites the divisor, so we need to cache its value for the
134 * enable operation. 196 * enable operation.
@@ -150,9 +212,34 @@ static void __init cpg_div6_clock_init(struct device_node *np)
150 goto error; 212 goto error;
151 } 213 }
152 214
153 parent_name = of_clk_get_parent_name(np, 0); 215
154 if (parent_name == NULL) { 216 for (i = 0, valid_parents = 0; i < num_parents; i++) {
155 pr_err("%s: failed to get %s DIV6 clock parent name\n", 217 const char *name = of_clk_get_parent_name(np, i);
218
219 if (name) {
220 parent_names[valid_parents] = name;
221 clock->parents[valid_parents] = i;
222 valid_parents++;
223 }
224 }
225
226 switch (num_parents) {
227 case 1:
228 /* fixed parent clock */
229 clock->src_shift = clock->src_width = 0;
230 break;
231 case 4:
232 /* clock with EXSRC bits 6-7 */
233 clock->src_shift = 6;
234 clock->src_width = 2;
235 break;
236 case 8:
237 /* VCLK with EXSRC bits 12-14 */
238 clock->src_shift = 12;
239 clock->src_width = 3;
240 break;
241 default:
242 pr_err("%s: invalid number of parents for DIV6 clock %s\n",
156 __func__, np->name); 243 __func__, np->name);
157 goto error; 244 goto error;
158 } 245 }
@@ -161,8 +248,8 @@ static void __init cpg_div6_clock_init(struct device_node *np)
161 init.name = name; 248 init.name = name;
162 init.ops = &cpg_div6_clock_ops; 249 init.ops = &cpg_div6_clock_ops;
163 init.flags = CLK_IS_BASIC; 250 init.flags = CLK_IS_BASIC;
164 init.parent_names = &parent_name; 251 init.parent_names = parent_names;
165 init.num_parents = 1; 252 init.num_parents = valid_parents;
166 253
167 clock->hw.init = &init; 254 clock->hw.init = &init;
168 255
@@ -175,11 +262,13 @@ static void __init cpg_div6_clock_init(struct device_node *np)
175 262
176 of_clk_add_provider(np, of_clk_src_simple_get, clk); 263 of_clk_add_provider(np, of_clk_src_simple_get, clk);
177 264
265 kfree(parent_names);
178 return; 266 return;
179 267
180error: 268error:
181 if (clock->reg) 269 if (clock->reg)
182 iounmap(clock->reg); 270 iounmap(clock->reg);
271 kfree(parent_names);
183 kfree(clock); 272 kfree(clock);
184} 273}
185CLK_OF_DECLARE(cpg_div6_clk, "renesas,cpg-div6-clock", cpg_div6_clock_init); 274CLK_OF_DECLARE(cpg_div6_clk, "renesas,cpg-div6-clock", cpg_div6_clock_init);