aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorShawn Lin <shawn.lin@rock-chips.com>2018-03-08 20:51:03 -0500
committerHeiko Stuebner <heiko@sntech.de>2018-03-13 08:06:15 -0400
commit60cf09e45fbcbbbb3162f02e0923a25ae7f5627e (patch)
treefa29a28ee11a6ea776d8fe33dab7333adadf7077
parent4bf59902b50012b1dddeeaa23b217d9c4956cdda (diff)
clk: rockchip: Restore the clock phase after the rate was changed
There are many factors affecting the clock phase, including clock rate, temperature, logic voltage and silicon process, etc. But clock rate is the most significant one here, and the driver should be aware of the change of the clock rate. As mmc controller need a fixed phase after tuning was completed, at least before explicitly doing re-tune, so this patch try to restore the clock phase by monitoring the event of rate change. Signed-off-by: Shawn Lin <shawn.lin@rock-chips.com> Signed-off-by: Heiko Stuebner <heiko@sntech.de>
-rw-r--r--drivers/clk/rockchip/clk-mmc-phase.c39
1 files changed, 38 insertions, 1 deletions
diff --git a/drivers/clk/rockchip/clk-mmc-phase.c b/drivers/clk/rockchip/clk-mmc-phase.c
index fe7d9ed1d436..dc4c227732bd 100644
--- a/drivers/clk/rockchip/clk-mmc-phase.c
+++ b/drivers/clk/rockchip/clk-mmc-phase.c
@@ -25,6 +25,8 @@ struct rockchip_mmc_clock {
25 void __iomem *reg; 25 void __iomem *reg;
26 int id; 26 int id;
27 int shift; 27 int shift;
28 int cached_phase;
29 struct notifier_block clk_rate_change_nb;
28}; 30};
29 31
30#define to_mmc_clock(_hw) container_of(_hw, struct rockchip_mmc_clock, hw) 32#define to_mmc_clock(_hw) container_of(_hw, struct rockchip_mmc_clock, hw)
@@ -162,6 +164,29 @@ static const struct clk_ops rockchip_mmc_clk_ops = {
162 .set_phase = rockchip_mmc_set_phase, 164 .set_phase = rockchip_mmc_set_phase,
163}; 165};
164 166
167#define to_rockchip_mmc_clock(x) \
168 container_of(x, struct rockchip_mmc_clock, clk_rate_change_nb)
169static int rockchip_mmc_clk_rate_notify(struct notifier_block *nb,
170 unsigned long event, void *data)
171{
172 struct rockchip_mmc_clock *mmc_clock = to_rockchip_mmc_clock(nb);
173
174 /*
175 * rockchip_mmc_clk is mostly used by mmc controllers to sample
176 * the intput data, which expects the fixed phase after the tuning
177 * process. However if the clock rate is changed, the phase is stale
178 * and may break the data sampling. So here we try to restore the phase
179 * for that case.
180 */
181 if (event == PRE_RATE_CHANGE)
182 mmc_clock->cached_phase =
183 rockchip_mmc_get_phase(&mmc_clock->hw);
184 else if (event == POST_RATE_CHANGE)
185 rockchip_mmc_set_phase(&mmc_clock->hw, mmc_clock->cached_phase);
186
187 return NOTIFY_DONE;
188}
189
165struct clk *rockchip_clk_register_mmc(const char *name, 190struct clk *rockchip_clk_register_mmc(const char *name,
166 const char *const *parent_names, u8 num_parents, 191 const char *const *parent_names, u8 num_parents,
167 void __iomem *reg, int shift) 192 void __iomem *reg, int shift)
@@ -169,6 +194,7 @@ struct clk *rockchip_clk_register_mmc(const char *name,
169 struct clk_init_data init; 194 struct clk_init_data init;
170 struct rockchip_mmc_clock *mmc_clock; 195 struct rockchip_mmc_clock *mmc_clock;
171 struct clk *clk; 196 struct clk *clk;
197 int ret;
172 198
173 mmc_clock = kmalloc(sizeof(*mmc_clock), GFP_KERNEL); 199 mmc_clock = kmalloc(sizeof(*mmc_clock), GFP_KERNEL);
174 if (!mmc_clock) 200 if (!mmc_clock)
@@ -186,7 +212,18 @@ struct clk *rockchip_clk_register_mmc(const char *name,
186 212
187 clk = clk_register(NULL, &mmc_clock->hw); 213 clk = clk_register(NULL, &mmc_clock->hw);
188 if (IS_ERR(clk)) 214 if (IS_ERR(clk))
189 kfree(mmc_clock); 215 goto err_register;
190 216
217 mmc_clock->clk_rate_change_nb.notifier_call =
218 &rockchip_mmc_clk_rate_notify;
219 ret = clk_notifier_register(clk, &mmc_clock->clk_rate_change_nb);
220 if (ret)
221 goto err_notifier;
222
223 return clk;
224err_notifier:
225 clk_unregister(clk);
226err_register:
227 kfree(mmc_clock);
191 return clk; 228 return clk;
192} 229}