aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPeng Fan2019-08-21 13:35:09 +0000
committerLukasz Majewski2019-08-22 00:10:09 +0200
commit0520be0f67e358f57f5a1f805b24f39b143e4334 (patch)
treea9293447c924a316005fcffb8a07bf2544a6fcea
parente6849e2fd88f16d9592059422173f7f6ab790e07 (diff)
clk: prograte clk enable/disable to parent
On i.MX8MM, thinking such as clk path OSC->PLL->PLL GATE->CCM ROOT->CCGR GATE->Device Only enabling CCGR GATE is not enough, we also need to enable PLL GATE to make sure the clk path work. So when enabling CCGR GATE, we could prograte to enabling PLL GATE to make life easier. Signed-off-by: Peng Fan <peng.fan@nxp.com>
-rw-r--r--drivers/clk/clk-uclass.c77
1 files changed, 71 insertions, 6 deletions
diff --git a/drivers/clk/clk-uclass.c b/drivers/clk/clk-uclass.c
index c66b6f3c4eb..64c181f4ad3 100644
--- a/drivers/clk/clk-uclass.c
+++ b/drivers/clk/clk-uclass.c
@@ -449,13 +449,45 @@ int clk_set_parent(struct clk *clk, struct clk *parent)
int clk_enable(struct clk *clk)
{
const struct clk_ops *ops = clk_dev_ops(clk->dev);
+ struct clk *clkp = NULL;
+ int ret;
debug("%s(clk=%p)\n", __func__, clk);
- if (!ops->enable)
- return -ENOSYS;
+ if (CONFIG_IS_ENABLED(CLK_CCF)) {
+ /* Take id 0 as a non-valid clk, such as dummy */
+ if (clk->id && !clk_get_by_id(clk->id, &clkp)) {
+ if (clkp->enable_count) {
+ clkp->enable_count++;
+ return 0;
+ }
+ if (clkp->dev->parent &&
+ device_get_uclass_id(clkp->dev) == UCLASS_CLK) {
+ ret = clk_enable(dev_get_clk_ptr(clkp->dev->parent));
+ if (ret) {
+ printf("Enable %s failed\n",
+ clkp->dev->parent->name);
+ return ret;
+ }
+ }
+ }
- return ops->enable(clk);
+ if (ops->enable) {
+ ret = ops->enable(clk);
+ if (ret) {
+ printf("Enable %s failed\n", clk->dev->name);
+ return ret;
+ }
+ }
+ if (clkp)
+ clkp->enable_count++;
+ } else {
+ if (!ops->enable)
+ return -ENOSYS;
+ return ops->enable(clk);
+ }
+
+ return 0;
}
int clk_enable_bulk(struct clk_bulk *bulk)
@@ -474,13 +506,46 @@ int clk_enable_bulk(struct clk_bulk *bulk)
int clk_disable(struct clk *clk)
{
const struct clk_ops *ops = clk_dev_ops(clk->dev);
+ struct clk *clkp = NULL;
+ int ret;
debug("%s(clk=%p)\n", __func__, clk);
- if (!ops->disable)
- return -ENOSYS;
+ if (CONFIG_IS_ENABLED(CLK_CCF)) {
+ if (clk->id && !clk_get_by_id(clk->id, &clkp)) {
+ if (clkp->enable_count == 0) {
+ printf("clk %s already disabled\n",
+ clkp->dev->name);
+ return 0;
+ }
- return ops->disable(clk);
+ if (--clkp->enable_count > 0)
+ return 0;
+ }
+
+ if (ops->disable) {
+ ret = ops->disable(clk);
+ if (ret)
+ return ret;
+ }
+
+ if (clkp && clkp->dev->parent &&
+ device_get_uclass_id(clkp->dev) == UCLASS_CLK) {
+ ret = clk_disable(dev_get_clk_ptr(clkp->dev->parent));
+ if (ret) {
+ printf("Disable %s failed\n",
+ clkp->dev->parent->name);
+ return ret;
+ }
+ }
+ } else {
+ if (!ops->disable)
+ return -ENOSYS;
+
+ return ops->disable(clk);
+ }
+
+ return 0;
}
int clk_disable_bulk(struct clk_bulk *bulk)