diff options
Diffstat (limited to 'drivers/clk')
-rw-r--r-- | drivers/clk/clk-uclass.c | 141 | ||||
-rw-r--r-- | drivers/clk/clk_sandbox.c | 56 | ||||
-rw-r--r-- | drivers/clk/clk_sandbox_test.c | 66 | ||||
-rw-r--r-- | drivers/clk/imx/clk-imx6q.c | 21 | ||||
-rw-r--r-- | drivers/clk/imx/clk.h | 18 |
5 files changed, 279 insertions, 23 deletions
diff --git a/drivers/clk/clk-uclass.c b/drivers/clk/clk-uclass.c index 64c181f4ad..9aa8537004 100644 --- a/drivers/clk/clk-uclass.c +++ b/drivers/clk/clk-uclass.c @@ -178,7 +178,7 @@ bulk_get_err: return ret; } -static int clk_set_default_parents(struct udevice *dev) +static int clk_set_default_parents(struct udevice *dev, int stage) { struct clk clk, parent_clk; int index; @@ -214,8 +214,18 @@ static int clk_set_default_parents(struct udevice *dev) return ret; } - ret = clk_set_parent(&clk, &parent_clk); + /* This is clk provider device trying to reparent itself + * It cannot be done right now but need to wait after the + * device is probed + */ + if (stage == 0 && clk.dev == dev) + continue; + + if (stage > 0 && clk.dev != dev) + /* do not setup twice the parent clocks */ + continue; + ret = clk_set_parent(&clk, &parent_clk); /* * Not all drivers may support clock-reparenting (as of now). * Ignore errors due to this. @@ -223,7 +233,7 @@ static int clk_set_default_parents(struct udevice *dev) if (ret == -ENOSYS) continue; - if (ret) { + if (ret < 0) { debug("%s: failed to reparent clock %d for %s\n", __func__, index, dev_read_name(dev)); return ret; @@ -233,7 +243,7 @@ static int clk_set_default_parents(struct udevice *dev) return 0; } -static int clk_set_default_rates(struct udevice *dev) +static int clk_set_default_rates(struct udevice *dev, int stage) { struct clk clk; int index; @@ -268,7 +278,19 @@ static int clk_set_default_rates(struct udevice *dev) continue; } + /* This is clk provider device trying to program itself + * It cannot be done right now but need to wait after the + * device is probed + */ + if (stage == 0 && clk.dev == dev) + continue; + + if (stage > 0 && clk.dev != dev) + /* do not setup twice the parent clocks */ + continue; + ret = clk_set_rate(&clk, rates[index]); + if (ret < 0) { debug("%s: failed to set rate on clock index %d (%ld) for %s\n", __func__, index, clk.id, dev_read_name(dev)); @@ -281,7 +303,7 @@ fail: return ret; } -int clk_set_defaults(struct udevice *dev) +int clk_set_defaults(struct udevice *dev, int stage) { int ret; @@ -294,11 +316,11 @@ int clk_set_defaults(struct udevice *dev) debug("%s(%s)\n", __func__, dev_read_name(dev)); - ret = clk_set_default_parents(dev); + ret = clk_set_default_parents(dev, stage); if (ret) return ret; - ret = clk_set_default_rates(dev); + ret = clk_set_default_rates(dev, stage); if (ret < 0) return ret; @@ -349,9 +371,12 @@ int clk_release_all(struct clk *clk, int count) int clk_request(struct udevice *dev, struct clk *clk) { - const struct clk_ops *ops = clk_dev_ops(dev); + const struct clk_ops *ops; debug("%s(dev=%p, clk=%p)\n", __func__, dev, clk); + if (!clk) + return 0; + ops = clk_dev_ops(dev); clk->dev = dev; @@ -363,9 +388,12 @@ int clk_request(struct udevice *dev, struct clk *clk) int clk_free(struct clk *clk) { - const struct clk_ops *ops = clk_dev_ops(clk->dev); + const struct clk_ops *ops; debug("%s(clk=%p)\n", __func__, clk); + if (!clk) + return 0; + ops = clk_dev_ops(clk->dev); if (!ops->free) return 0; @@ -375,9 +403,12 @@ int clk_free(struct clk *clk) ulong clk_get_rate(struct clk *clk) { - const struct clk_ops *ops = clk_dev_ops(clk->dev); + const struct clk_ops *ops; debug("%s(clk=%p)\n", __func__, clk); + if (!clk) + return 0; + ops = clk_dev_ops(clk->dev); if (!ops->get_rate) return -ENOSYS; @@ -391,6 +422,8 @@ struct clk *clk_get_parent(struct clk *clk) struct clk *pclk; debug("%s(clk=%p)\n", __func__, clk); + if (!clk) + return NULL; pdev = dev_get_parent(clk->dev); pclk = dev_get_clk_ptr(pdev); @@ -406,6 +439,8 @@ long long clk_get_parent_rate(struct clk *clk) struct clk *pclk; debug("%s(clk=%p)\n", __func__, clk); + if (!clk) + return 0; pclk = clk_get_parent(clk); if (IS_ERR(pclk)) @@ -424,9 +459,12 @@ long long clk_get_parent_rate(struct clk *clk) ulong clk_set_rate(struct clk *clk, ulong rate) { - const struct clk_ops *ops = clk_dev_ops(clk->dev); + const struct clk_ops *ops; debug("%s(clk=%p, rate=%lu)\n", __func__, clk, rate); + if (!clk) + return 0; + ops = clk_dev_ops(clk->dev); if (!ops->set_rate) return -ENOSYS; @@ -436,9 +474,12 @@ ulong clk_set_rate(struct clk *clk, ulong rate) int clk_set_parent(struct clk *clk, struct clk *parent) { - const struct clk_ops *ops = clk_dev_ops(clk->dev); + const struct clk_ops *ops; debug("%s(clk=%p, parent=%p)\n", __func__, clk, parent); + if (!clk) + return 0; + ops = clk_dev_ops(clk->dev); if (!ops->set_parent) return -ENOSYS; @@ -448,11 +489,14 @@ 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); + const struct clk_ops *ops; struct clk *clkp = NULL; int ret; debug("%s(clk=%p)\n", __func__, clk); + if (!clk) + return 0; + ops = clk_dev_ops(clk->dev); if (CONFIG_IS_ENABLED(CLK_CCF)) { /* Take id 0 as a non-valid clk, such as dummy */ @@ -505,11 +549,14 @@ int clk_enable_bulk(struct clk_bulk *bulk) int clk_disable(struct clk *clk) { - const struct clk_ops *ops = clk_dev_ops(clk->dev); + const struct clk_ops *ops; struct clk *clkp = NULL; int ret; debug("%s(clk=%p)\n", __func__, clk); + if (!clk) + return 0; + ops = clk_dev_ops(clk->dev); if (CONFIG_IS_ENABLED(CLK_CCF)) { if (clk->id && !clk_get_by_id(clk->id, &clkp)) { @@ -589,6 +636,10 @@ bool clk_is_match(const struct clk *p, const struct clk *q) if (p == q) return true; + /* trivial case #2: on the clk pointer is NULL */ + if (!p || !q) + return false; + /* same device, id and data */ if (p->dev == q->dev && p->id == q->id && p->data == q->data) return true; @@ -596,7 +647,69 @@ bool clk_is_match(const struct clk *p, const struct clk *q) return false; } +static void devm_clk_release(struct udevice *dev, void *res) +{ + clk_free(res); +} + +static int devm_clk_match(struct udevice *dev, void *res, void *data) +{ + return res == data; +} + +struct clk *devm_clk_get(struct udevice *dev, const char *id) +{ + int rc; + struct clk *clk; + + clk = devres_alloc(devm_clk_release, sizeof(struct clk), __GFP_ZERO); + if (unlikely(!clk)) + return ERR_PTR(-ENOMEM); + + rc = clk_get_by_name(dev, id, clk); + if (rc) + return ERR_PTR(rc); + + devres_add(dev, clk); + return clk; +} + +struct clk *devm_clk_get_optional(struct udevice *dev, const char *id) +{ + struct clk *clk = devm_clk_get(dev, id); + + if (IS_ERR(clk)) + return NULL; + + return clk; +} + +void devm_clk_put(struct udevice *dev, struct clk *clk) +{ + int rc; + + if (!clk) + return; + + rc = devres_release(dev, devm_clk_release, devm_clk_match, clk); + WARN_ON(rc); +} + +int clk_uclass_post_probe(struct udevice *dev) +{ + /* + * when a clock provider is probed. Call clk_set_defaults() + * also after the device is probed. This takes care of cases + * where the DT is used to setup default parents and rates + * using assigned-clocks + */ + clk_set_defaults(dev, 1); + + return 0; +} + UCLASS_DRIVER(clk) = { .id = UCLASS_CLK, .name = "clk", + .post_probe = clk_uclass_post_probe, }; diff --git a/drivers/clk/clk_sandbox.c b/drivers/clk/clk_sandbox.c index 1d5cbb589a..de6b2f7c82 100644 --- a/drivers/clk/clk_sandbox.c +++ b/drivers/clk/clk_sandbox.c @@ -10,14 +10,19 @@ #include <asm/clk.h> struct sandbox_clk_priv { + bool probed; ulong rate[SANDBOX_CLK_ID_COUNT]; bool enabled[SANDBOX_CLK_ID_COUNT]; + bool requested[SANDBOX_CLK_ID_COUNT]; }; static ulong sandbox_clk_get_rate(struct clk *clk) { struct sandbox_clk_priv *priv = dev_get_priv(clk->dev); + if (!priv->probed) + return -ENODEV; + if (clk->id >= SANDBOX_CLK_ID_COUNT) return -EINVAL; @@ -29,6 +34,9 @@ static ulong sandbox_clk_set_rate(struct clk *clk, ulong rate) struct sandbox_clk_priv *priv = dev_get_priv(clk->dev); ulong old_rate; + if (!priv->probed) + return -ENODEV; + if (clk->id >= SANDBOX_CLK_ID_COUNT) return -EINVAL; @@ -45,6 +53,9 @@ static int sandbox_clk_enable(struct clk *clk) { struct sandbox_clk_priv *priv = dev_get_priv(clk->dev); + if (!priv->probed) + return -ENODEV; + if (clk->id >= SANDBOX_CLK_ID_COUNT) return -EINVAL; @@ -57,6 +68,9 @@ static int sandbox_clk_disable(struct clk *clk) { struct sandbox_clk_priv *priv = dev_get_priv(clk->dev); + if (!priv->probed) + return -ENODEV; + if (clk->id >= SANDBOX_CLK_ID_COUNT) return -EINVAL; @@ -65,13 +79,45 @@ static int sandbox_clk_disable(struct clk *clk) return 0; } +static int sandbox_clk_request(struct clk *clk) +{ + struct sandbox_clk_priv *priv = dev_get_priv(clk->dev); + + if (clk->id >= SANDBOX_CLK_ID_COUNT) + return -EINVAL; + + priv->requested[clk->id] = true; + return 0; +} + +static int sandbox_clk_free(struct clk *clk) +{ + struct sandbox_clk_priv *priv = dev_get_priv(clk->dev); + + if (clk->id >= SANDBOX_CLK_ID_COUNT) + return -EINVAL; + + priv->requested[clk->id] = false; + return 0; +} + static struct clk_ops sandbox_clk_ops = { .get_rate = sandbox_clk_get_rate, .set_rate = sandbox_clk_set_rate, .enable = sandbox_clk_enable, .disable = sandbox_clk_disable, + .request = sandbox_clk_request, + .free = sandbox_clk_free, }; +static int sandbox_clk_probe(struct udevice *dev) +{ + struct sandbox_clk_priv *priv = dev_get_priv(dev); + + priv->probed = true; + return 0; +} + static const struct udevice_id sandbox_clk_ids[] = { { .compatible = "sandbox,clk" }, { } @@ -82,6 +128,7 @@ U_BOOT_DRIVER(clk_sandbox) = { .id = UCLASS_CLK, .of_match = sandbox_clk_ids, .ops = &sandbox_clk_ops, + .probe = sandbox_clk_probe, .priv_auto_alloc_size = sizeof(struct sandbox_clk_priv), }; @@ -104,3 +151,12 @@ int sandbox_clk_query_enable(struct udevice *dev, int id) return priv->enabled[id]; } + +int sandbox_clk_query_requested(struct udevice *dev, int id) +{ + struct sandbox_clk_priv *priv = dev_get_priv(dev); + + if (id < 0 || id >= SANDBOX_CLK_ID_COUNT) + return -EINVAL; + return priv->requested[id]; +} diff --git a/drivers/clk/clk_sandbox_test.c b/drivers/clk/clk_sandbox_test.c index e8465dbfad..41954660ea 100644 --- a/drivers/clk/clk_sandbox_test.c +++ b/drivers/clk/clk_sandbox_test.c @@ -9,7 +9,8 @@ #include <asm/clk.h> struct sandbox_clk_test { - struct clk clks[SANDBOX_CLK_TEST_ID_COUNT]; + struct clk clks[SANDBOX_CLK_TEST_NON_DEVM_COUNT]; + struct clk *clkps[SANDBOX_CLK_TEST_ID_COUNT]; struct clk_bulk bulk; }; @@ -24,7 +25,7 @@ int sandbox_clk_test_get(struct udevice *dev) struct sandbox_clk_test *sbct = dev_get_priv(dev); int i, ret; - for (i = 0; i < SANDBOX_CLK_TEST_ID_COUNT; i++) { + for (i = 0; i < SANDBOX_CLK_TEST_NON_DEVM_COUNT; i++) { ret = clk_get_by_name(dev, sandbox_clk_test_names[i], &sbct->clks[i]); if (ret) @@ -34,6 +35,37 @@ int sandbox_clk_test_get(struct udevice *dev) return 0; } +int sandbox_clk_test_devm_get(struct udevice *dev) +{ + struct sandbox_clk_test *sbct = dev_get_priv(dev); + struct clk *clk; + + clk = devm_clk_get(dev, "no-an-existing-clock"); + if (!IS_ERR(clk)) { + dev_err(dev, "devm_clk_get() should have failed\n"); + return -EINVAL; + } + + clk = devm_clk_get(dev, "uart2"); + if (IS_ERR(clk)) + return PTR_ERR(clk); + sbct->clkps[SANDBOX_CLK_TEST_ID_DEVM1] = clk; + + clk = devm_clk_get_optional(dev, "uart1"); + if (IS_ERR(clk)) + return PTR_ERR(clk); + sbct->clkps[SANDBOX_CLK_TEST_ID_DEVM2] = clk; + + sbct->clkps[SANDBOX_CLK_TEST_ID_DEVM_NULL] = NULL; + clk = devm_clk_get_optional(dev, "not_an_existing_clock"); + if (IS_ERR(clk)) + return PTR_ERR(clk); + if (clk) + return -EINVAL; + + return 0; +} + int sandbox_clk_test_get_bulk(struct udevice *dev) { struct sandbox_clk_test *sbct = dev_get_priv(dev); @@ -48,7 +80,7 @@ ulong sandbox_clk_test_get_rate(struct udevice *dev, int id) if (id < 0 || id >= SANDBOX_CLK_TEST_ID_COUNT) return -EINVAL; - return clk_get_rate(&sbct->clks[id]); + return clk_get_rate(sbct->clkps[id]); } ulong sandbox_clk_test_set_rate(struct udevice *dev, int id, ulong rate) @@ -58,7 +90,7 @@ ulong sandbox_clk_test_set_rate(struct udevice *dev, int id, ulong rate) if (id < 0 || id >= SANDBOX_CLK_TEST_ID_COUNT) return -EINVAL; - return clk_set_rate(&sbct->clks[id], rate); + return clk_set_rate(sbct->clkps[id], rate); } int sandbox_clk_test_enable(struct udevice *dev, int id) @@ -68,7 +100,7 @@ int sandbox_clk_test_enable(struct udevice *dev, int id) if (id < 0 || id >= SANDBOX_CLK_TEST_ID_COUNT) return -EINVAL; - return clk_enable(&sbct->clks[id]); + return clk_enable(sbct->clkps[id]); } int sandbox_clk_test_enable_bulk(struct udevice *dev) @@ -85,7 +117,7 @@ int sandbox_clk_test_disable(struct udevice *dev, int id) if (id < 0 || id >= SANDBOX_CLK_TEST_ID_COUNT) return -EINVAL; - return clk_disable(&sbct->clks[id]); + return clk_disable(sbct->clkps[id]); } int sandbox_clk_test_disable_bulk(struct udevice *dev) @@ -100,7 +132,8 @@ int sandbox_clk_test_free(struct udevice *dev) struct sandbox_clk_test *sbct = dev_get_priv(dev); int i, ret; - for (i = 0; i < SANDBOX_CLK_TEST_ID_COUNT; i++) { + devm_clk_put(dev, sbct->clkps[SANDBOX_CLK_TEST_ID_DEVM1]); + for (i = 0; i < SANDBOX_CLK_TEST_NON_DEVM_COUNT; i++) { ret = clk_free(&sbct->clks[i]); if (ret) return ret; @@ -122,13 +155,27 @@ int sandbox_clk_test_valid(struct udevice *dev) int i; for (i = 0; i < SANDBOX_CLK_TEST_ID_COUNT; i++) { - if (!clk_valid(&sbct->clks[i])) - return -EINVAL; + if (!clk_valid(sbct->clkps[i])) + if (i != SANDBOX_CLK_TEST_ID_DEVM_NULL) + return -EINVAL; } return 0; } +static int sandbox_clk_test_probe(struct udevice *dev) +{ + struct sandbox_clk_test *sbct = dev_get_priv(dev); + int i; + + for (i = 0; i < SANDBOX_CLK_TEST_ID_DEVM1; i++) + sbct->clkps[i] = &sbct->clks[i]; + for (i = SANDBOX_CLK_TEST_ID_DEVM1; i < SANDBOX_CLK_TEST_ID_COUNT; i++) + sbct->clkps[i] = NULL; + + return 0; +} + static const struct udevice_id sandbox_clk_test_ids[] = { { .compatible = "sandbox,clk-test" }, { } @@ -138,5 +185,6 @@ U_BOOT_DRIVER(sandbox_clk_test) = { .name = "sandbox_clk_test", .id = UCLASS_MISC, .of_match = sandbox_clk_test_ids, + .probe = sandbox_clk_test_probe, .priv_auto_alloc_size = sizeof(struct sandbox_clk_test), }; diff --git a/drivers/clk/imx/clk-imx6q.c b/drivers/clk/imx/clk-imx6q.c index 92e9337d44..5ae4781d11 100644 --- a/drivers/clk/imx/clk-imx6q.c +++ b/drivers/clk/imx/clk-imx6q.c @@ -89,6 +89,9 @@ static struct clk_ops imx6q_clk_ops = { }; static const char *const usdhc_sels[] = { "pll2_pfd2_396m", "pll2_pfd0_352m", }; +static const char *const periph_sels[] = { "periph_pre", "periph_clk2", }; +static const char *const periph_pre_sels[] = { "pll2_bus", "pll2_pfd2_396m", + "pll2_pfd0_352m", "pll2_198m", }; static int imx6q_clk_probe(struct udevice *dev) { @@ -161,6 +164,24 @@ static int imx6q_clk_probe(struct udevice *dev) clk_dm(IMX6QDL_CLK_USDHC4, imx_clk_gate2("usdhc4", "usdhc4_podf", base + 0x80, 8)); + clk_dm(IMX6QDL_CLK_PERIPH_PRE, + imx_clk_mux("periph_pre", base + 0x18, 18, 2, periph_pre_sels, + ARRAY_SIZE(periph_pre_sels))); + clk_dm(IMX6QDL_CLK_PERIPH, + imx_clk_busy_mux("periph", base + 0x14, 25, 1, base + 0x48, + 5, periph_sels, ARRAY_SIZE(periph_sels))); + clk_dm(IMX6QDL_CLK_AHB, + imx_clk_busy_divider("ahb", "periph", base + 0x14, 10, 3, + base + 0x48, 1)); + clk_dm(IMX6QDL_CLK_IPG, + imx_clk_divider("ipg", "ahb", base + 0x14, 8, 2)); + clk_dm(IMX6QDL_CLK_IPG_PER, + imx_clk_divider("ipg_per", "ipg", base + 0x1c, 0, 6)); + clk_dm(IMX6QDL_CLK_I2C1, + imx_clk_gate2("i2c1", "ipg_per", base + 0x70, 6)); + clk_dm(IMX6QDL_CLK_I2C2, + imx_clk_gate2("i2c2", "ipg_per", base + 0x70, 8)); + return 0; } diff --git a/drivers/clk/imx/clk.h b/drivers/clk/imx/clk.h index 4956e04a92..07dcf94ea5 100644 --- a/drivers/clk/imx/clk.h +++ b/drivers/clk/imx/clk.h @@ -92,6 +92,14 @@ static inline struct clk *imx_clk_divider(const char *name, const char *parent, reg, shift, width, 0); } +static inline struct clk * +imx_clk_busy_divider(const char *name, const char *parent, void __iomem *reg, + u8 shift, u8 width, void __iomem *busy_reg, u8 busy_shift) +{ + return clk_register_divider(NULL, name, parent, CLK_SET_RATE_PARENT, + reg, shift, width, 0); +} + static inline struct clk *imx_clk_divider2(const char *name, const char *parent, void __iomem *reg, u8 shift, u8 width) { @@ -126,6 +134,16 @@ static inline struct clk *imx_clk_mux(const char *name, void __iomem *reg, width, 0); } +static inline struct clk * +imx_clk_busy_mux(const char *name, void __iomem *reg, u8 shift, u8 width, + void __iomem *busy_reg, u8 busy_shift, + const char * const *parents, int num_parents) +{ + return clk_register_mux(NULL, name, parents, num_parents, + CLK_SET_RATE_NO_REPARENT, reg, shift, + width, 0); +} + static inline struct clk *imx_clk_mux2(const char *name, void __iomem *reg, u8 shift, u8 width, const char * const *parents, int num_parents) |