aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/clk/rockchip/clk.c
diff options
context:
space:
mode:
authorHeiko Stuebner <heiko@sntech.de>2015-12-22 16:27:59 -0500
committerMichael Turquette <mturquette@baylibre.com>2015-12-23 15:57:29 -0500
commit8ca1ca8f6039f19673fb61552f276b848539dbd6 (patch)
treeb1cdc1c0584c8301aa1a13b3855dfbf414281988 /drivers/clk/rockchip/clk.c
parent8ad0df33c62d706f925a5910701255759a68c2e5 (diff)
clk: rockchip: handle mux dependency of fractional dividers
The fractional dividers of Rockchip SoCs contain an "auto-gating-feature" that requires the downstream mux to actually point to the fractional divider and the fractional divider gate to be enabled, for it to really accept changes to the divider ratio. The downstream muxes themselfs are not generic enough to include them directly into the fractional divider, as they have varying sources of parent clocks including not only clocks related to the fractional dividers but other clocks as well. To solve this, allow our clock branches to specify direct child clock- branches in the new child property, let the fractional divider register its downstream mux through this and add a clock notifier that temporarily switches the mux setting when it notices rate changes to the fractional divider. Signed-off-by: Heiko Stuebner <heiko@sntech.de> Tested-by: Sjoerd Simons <sjoerd.simons@collabora.co.uk> Reviewed-by: Sjoerd Simons <sjoerd.simons@collabora.co.uk> Signed-off-by: Michael Turquette <mturquette@baylibre.com>
Diffstat (limited to 'drivers/clk/rockchip/clk.c')
-rw-r--r--drivers/clk/rockchip/clk.c137
1 files changed, 123 insertions, 14 deletions
diff --git a/drivers/clk/rockchip/clk.c b/drivers/clk/rockchip/clk.c
index be6c7fd8315d..f6d147b15c2b 100644
--- a/drivers/clk/rockchip/clk.c
+++ b/drivers/clk/rockchip/clk.c
@@ -102,22 +102,82 @@ static struct clk *rockchip_clk_register_branch(const char *name,
102 return clk; 102 return clk;
103} 103}
104 104
105struct rockchip_clk_frac {
106 struct notifier_block clk_nb;
107 struct clk_fractional_divider div;
108 struct clk_gate gate;
109
110 struct clk_mux mux;
111 const struct clk_ops *mux_ops;
112 int mux_frac_idx;
113
114 bool rate_change_remuxed;
115 int rate_change_idx;
116};
117
118#define to_rockchip_clk_frac_nb(nb) \
119 container_of(nb, struct rockchip_clk_frac, clk_nb)
120
121static int rockchip_clk_frac_notifier_cb(struct notifier_block *nb,
122 unsigned long event, void *data)
123{
124 struct clk_notifier_data *ndata = data;
125 struct rockchip_clk_frac *frac = to_rockchip_clk_frac_nb(nb);
126 struct clk_mux *frac_mux = &frac->mux;
127 int ret = 0;
128
129 pr_debug("%s: event %lu, old_rate %lu, new_rate: %lu\n",
130 __func__, event, ndata->old_rate, ndata->new_rate);
131 if (event == PRE_RATE_CHANGE) {
132 frac->rate_change_idx = frac->mux_ops->get_parent(&frac_mux->hw);
133 if (frac->rate_change_idx != frac->mux_frac_idx) {
134 frac->mux_ops->set_parent(&frac_mux->hw, frac->mux_frac_idx);
135 frac->rate_change_remuxed = 1;
136 }
137 } else if (event == POST_RATE_CHANGE) {
138 /*
139 * The POST_RATE_CHANGE notifier runs directly after the
140 * divider clock is set in clk_change_rate, so we'll have
141 * remuxed back to the original parent before clk_change_rate
142 * reaches the mux itself.
143 */
144 if (frac->rate_change_remuxed) {
145 frac->mux_ops->set_parent(&frac_mux->hw, frac->rate_change_idx);
146 frac->rate_change_remuxed = 0;
147 }
148 }
149
150 return notifier_from_errno(ret);
151}
152
105static struct clk *rockchip_clk_register_frac_branch(const char *name, 153static struct clk *rockchip_clk_register_frac_branch(const char *name,
106 const char *const *parent_names, u8 num_parents, 154 const char *const *parent_names, u8 num_parents,
107 void __iomem *base, int muxdiv_offset, u8 div_flags, 155 void __iomem *base, int muxdiv_offset, u8 div_flags,
108 int gate_offset, u8 gate_shift, u8 gate_flags, 156 int gate_offset, u8 gate_shift, u8 gate_flags,
109 unsigned long flags, spinlock_t *lock) 157 unsigned long flags, struct rockchip_clk_branch *child,
158 spinlock_t *lock)
110{ 159{
160 struct rockchip_clk_frac *frac;
111 struct clk *clk; 161 struct clk *clk;
112 struct clk_gate *gate = NULL; 162 struct clk_gate *gate = NULL;
113 struct clk_fractional_divider *div = NULL; 163 struct clk_fractional_divider *div = NULL;
114 const struct clk_ops *div_ops = NULL, *gate_ops = NULL; 164 const struct clk_ops *div_ops = NULL, *gate_ops = NULL;
115 165
116 if (gate_offset >= 0) { 166 if (muxdiv_offset < 0)
117 gate = kzalloc(sizeof(*gate), GFP_KERNEL); 167 return ERR_PTR(-EINVAL);
118 if (!gate)
119 return ERR_PTR(-ENOMEM);
120 168
169 if (child && child->branch_type != branch_mux) {
170 pr_err("%s: fractional child clock for %s can only be a mux\n",
171 __func__, name);
172 return ERR_PTR(-EINVAL);
173 }
174
175 frac = kzalloc(sizeof(*frac), GFP_KERNEL);
176 if (!frac)
177 return ERR_PTR(-ENOMEM);
178
179 if (gate_offset >= 0) {
180 gate = &frac->gate;
121 gate->flags = gate_flags; 181 gate->flags = gate_flags;
122 gate->reg = base + gate_offset; 182 gate->reg = base + gate_offset;
123 gate->bit_idx = gate_shift; 183 gate->bit_idx = gate_shift;
@@ -125,13 +185,7 @@ static struct clk *rockchip_clk_register_frac_branch(const char *name,
125 gate_ops = &clk_gate_ops; 185 gate_ops = &clk_gate_ops;
126 } 186 }
127 187
128 if (muxdiv_offset < 0) 188 div = &frac->div;
129 return ERR_PTR(-EINVAL);
130
131 div = kzalloc(sizeof(*div), GFP_KERNEL);
132 if (!div)
133 return ERR_PTR(-ENOMEM);
134
135 div->flags = div_flags; 189 div->flags = div_flags;
136 div->reg = base + muxdiv_offset; 190 div->reg = base + muxdiv_offset;
137 div->mshift = 16; 191 div->mshift = 16;
@@ -147,7 +201,61 @@ static struct clk *rockchip_clk_register_frac_branch(const char *name,
147 NULL, NULL, 201 NULL, NULL,
148 &div->hw, div_ops, 202 &div->hw, div_ops,
149 gate ? &gate->hw : NULL, gate_ops, 203 gate ? &gate->hw : NULL, gate_ops,
150 flags); 204 flags | CLK_SET_RATE_UNGATE);
205 if (IS_ERR(clk)) {
206 kfree(frac);
207 return clk;
208 }
209
210 if (child) {
211 struct clk_mux *frac_mux = &frac->mux;
212 struct clk_init_data init;
213 struct clk *mux_clk;
214 int i, ret;
215
216 frac->mux_frac_idx = -1;
217 for (i = 0; i < child->num_parents; i++) {
218 if (!strcmp(name, child->parent_names[i])) {
219 pr_debug("%s: found fractional parent in mux at pos %d\n",
220 __func__, i);
221 frac->mux_frac_idx = i;
222 break;
223 }
224 }
225
226 frac->mux_ops = &clk_mux_ops;
227 frac->clk_nb.notifier_call = rockchip_clk_frac_notifier_cb;
228
229 frac_mux->reg = base + child->muxdiv_offset;
230 frac_mux->shift = child->mux_shift;
231 frac_mux->mask = BIT(child->mux_width) - 1;
232 frac_mux->flags = child->mux_flags;
233 frac_mux->lock = lock;
234 frac_mux->hw.init = &init;
235
236 init.name = child->name;
237 init.flags = child->flags | CLK_SET_RATE_PARENT;
238 init.ops = frac->mux_ops;
239 init.parent_names = child->parent_names;
240 init.num_parents = child->num_parents;
241
242 mux_clk = clk_register(NULL, &frac_mux->hw);
243 if (IS_ERR(mux_clk))
244 return clk;
245
246 rockchip_clk_add_lookup(mux_clk, child->id);
247
248 /* notifier on the fraction divider to catch rate changes */
249 if (frac->mux_frac_idx >= 0) {
250 ret = clk_notifier_register(clk, &frac->clk_nb);
251 if (ret)
252 pr_err("%s: failed to register clock notifier for %s\n",
253 __func__, name);
254 } else {
255 pr_warn("%s: could not find %s as parent of %s, rate changes may not work\n",
256 __func__, name, child->name);
257 }
258 }
151 259
152 return clk; 260 return clk;
153} 261}
@@ -251,7 +359,8 @@ void __init rockchip_clk_register_branches(
251 list->parent_names, list->num_parents, 359 list->parent_names, list->num_parents,
252 reg_base, list->muxdiv_offset, list->div_flags, 360 reg_base, list->muxdiv_offset, list->div_flags,
253 list->gate_offset, list->gate_shift, 361 list->gate_offset, list->gate_shift,
254 list->gate_flags, flags, &clk_lock); 362 list->gate_flags, flags, list->child,
363 &clk_lock);
255 break; 364 break;
256 case branch_gate: 365 case branch_gate:
257 flags |= CLK_SET_RATE_PARENT; 366 flags |= CLK_SET_RATE_PARENT;