diff options
-rw-r--r-- | drivers/clk/rockchip/clk-mmc-phase.c | 39 |
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) | ||
169 | static 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 | |||
165 | struct clk *rockchip_clk_register_mmc(const char *name, | 190 | struct 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; | ||
224 | err_notifier: | ||
225 | clk_unregister(clk); | ||
226 | err_register: | ||
227 | kfree(mmc_clock); | ||
191 | return clk; | 228 | return clk; |
192 | } | 229 | } |