Commit 60cf09e4 authored by Shawn Lin's avatar Shawn Lin Committed by Heiko Stuebner

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: default avatarShawn Lin <shawn.lin@rock-chips.com>
Signed-off-by: default avatarHeiko Stuebner <heiko@sntech.de>
parent 4bf59902
...@@ -25,6 +25,8 @@ struct rockchip_mmc_clock { ...@@ -25,6 +25,8 @@ struct rockchip_mmc_clock {
void __iomem *reg; void __iomem *reg;
int id; int id;
int shift; int shift;
int cached_phase;
struct notifier_block clk_rate_change_nb;
}; };
#define to_mmc_clock(_hw) container_of(_hw, struct rockchip_mmc_clock, hw) #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,6 +164,29 @@ static const struct clk_ops rockchip_mmc_clk_ops = {
.set_phase = rockchip_mmc_set_phase, .set_phase = rockchip_mmc_set_phase,
}; };
#define to_rockchip_mmc_clock(x) \
container_of(x, struct rockchip_mmc_clock, clk_rate_change_nb)
static int rockchip_mmc_clk_rate_notify(struct notifier_block *nb,
unsigned long event, void *data)
{
struct rockchip_mmc_clock *mmc_clock = to_rockchip_mmc_clock(nb);
/*
* rockchip_mmc_clk is mostly used by mmc controllers to sample
* the intput data, which expects the fixed phase after the tuning
* process. However if the clock rate is changed, the phase is stale
* and may break the data sampling. So here we try to restore the phase
* for that case.
*/
if (event == PRE_RATE_CHANGE)
mmc_clock->cached_phase =
rockchip_mmc_get_phase(&mmc_clock->hw);
else if (event == POST_RATE_CHANGE)
rockchip_mmc_set_phase(&mmc_clock->hw, mmc_clock->cached_phase);
return NOTIFY_DONE;
}
struct clk *rockchip_clk_register_mmc(const char *name, struct clk *rockchip_clk_register_mmc(const char *name,
const char *const *parent_names, u8 num_parents, const char *const *parent_names, u8 num_parents,
void __iomem *reg, int shift) void __iomem *reg, int shift)
...@@ -169,6 +194,7 @@ struct clk *rockchip_clk_register_mmc(const char *name, ...@@ -169,6 +194,7 @@ struct clk *rockchip_clk_register_mmc(const char *name,
struct clk_init_data init; struct clk_init_data init;
struct rockchip_mmc_clock *mmc_clock; struct rockchip_mmc_clock *mmc_clock;
struct clk *clk; struct clk *clk;
int ret;
mmc_clock = kmalloc(sizeof(*mmc_clock), GFP_KERNEL); mmc_clock = kmalloc(sizeof(*mmc_clock), GFP_KERNEL);
if (!mmc_clock) if (!mmc_clock)
...@@ -186,7 +212,18 @@ struct clk *rockchip_clk_register_mmc(const char *name, ...@@ -186,7 +212,18 @@ struct clk *rockchip_clk_register_mmc(const char *name,
clk = clk_register(NULL, &mmc_clock->hw); clk = clk_register(NULL, &mmc_clock->hw);
if (IS_ERR(clk)) if (IS_ERR(clk))
kfree(mmc_clock); goto err_register;
mmc_clock->clk_rate_change_nb.notifier_call =
&rockchip_mmc_clk_rate_notify;
ret = clk_notifier_register(clk, &mmc_clock->clk_rate_change_nb);
if (ret)
goto err_notifier;
return clk;
err_notifier:
clk_unregister(clk);
err_register:
kfree(mmc_clock);
return clk; return clk;
} }
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment