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 | } |
