From 15b87feb2b7923ef679e9da3f956e7a836fbaa40 Mon Sep 17 00:00:00 2001 From: Ryan Chen Date: Mon, 31 Aug 2020 14:03:03 +0800 Subject: cosmetic: aspeed: ast2500: Rename clock header MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Rename the ast2500-scu.h to aspeed-clock.h. Signed-off-by: Ryan Chen Reviewed-by: Chia-Wei, Wang Reviewed-by: Cédric Le Goater --- drivers/clk/aspeed/clk_ast2500.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/clk') diff --git a/drivers/clk/aspeed/clk_ast2500.c b/drivers/clk/aspeed/clk_ast2500.c index d1940f1884..392fe76b27 100644 --- a/drivers/clk/aspeed/clk_ast2500.c +++ b/drivers/clk/aspeed/clk_ast2500.c @@ -10,7 +10,7 @@ #include #include #include -#include +#include #include #include -- cgit v1.2.3 From c39c9a94cba01d9fbbe359a8922d2ae85061a4e1 Mon Sep 17 00:00:00 2001 From: Ryan Chen Date: Mon, 31 Aug 2020 14:03:04 +0800 Subject: clock:aspeed: Sync with Linux kernel clock header define v2: modify title description aspeed:clock -> clock:aspeed Use kernel include/dt-bindings/clock/aspeed-clock.h define for clock driver. Signed-off-by: Ryan Chen Reviewed-by: Chia-Wei, Wang --- arch/arm/dts/ast2500-u-boot.dtsi | 20 +++++----- drivers/clk/aspeed/clk_ast2500.c | 38 +++++++++--------- include/dt-bindings/clock/aspeed-clock.h | 68 +++++++++++++++++++------------- 3 files changed, 68 insertions(+), 58 deletions(-) (limited to 'drivers/clk') diff --git a/arch/arm/dts/ast2500-u-boot.dtsi b/arch/arm/dts/ast2500-u-boot.dtsi index 3b119e4ace..29b08f16ac 100644 --- a/arch/arm/dts/ast2500-u-boot.dtsi +++ b/arch/arm/dts/ast2500-u-boot.dtsi @@ -25,7 +25,7 @@ reg = <0x1e6e0000 0x174 0x1e6e0200 0x1d4 >; #reset-cells = <1>; - clocks = <&scu PLL_MPLL>; + clocks = <&scu ASPEED_CLK_MPLL>; resets = <&rst AST_RESET_SDRAM>; }; @@ -39,7 +39,7 @@ compatible = "aspeed,ast2500-sdhci"; reg = <0x1e740100>; #reset-cells = <1>; - clocks = <&scu BCLK_SDCLK>; + clocks = <&scu ASPEED_CLK_SDIO>; resets = <&rst AST_RESET_SDIO>; }; @@ -47,7 +47,7 @@ compatible = "aspeed,ast2500-sdhci"; reg = <0x1e740200>; #reset-cells = <1>; - clocks = <&scu BCLK_SDCLK>; + clocks = <&scu ASPEED_CLK_SDIO>; resets = <&rst AST_RESET_SDIO>; }; }; @@ -56,23 +56,23 @@ }; &uart1 { - clocks = <&scu PCLK_UART1>; + clocks = <&scu ASPEED_CLK_GATE_UART1CLK>; }; &uart2 { - clocks = <&scu PCLK_UART2>; + clocks = <&scu ASPEED_CLK_GATE_UART2CLK>; }; &uart3 { - clocks = <&scu PCLK_UART3>; + clocks = <&scu ASPEED_CLK_GATE_UART3CLK>; }; &uart4 { - clocks = <&scu PCLK_UART4>; + clocks = <&scu ASPEED_CLK_GATE_UART4CLK>; }; &uart5 { - clocks = <&scu PCLK_UART5>; + clocks = <&scu ASPEED_CLK_GATE_UART5CLK>; }; &timer { @@ -80,9 +80,9 @@ }; &mac0 { - clocks = <&scu PCLK_MAC1>, <&scu PLL_D2PLL>; + clocks = <&scu ASPEED_CLK_GATE_MAC1CLK>, <&scu ASPEED_CLK_D2PLL>; }; &mac1 { - clocks = <&scu PCLK_MAC2>, <&scu PLL_D2PLL>; + clocks = <&scu ASPEED_CLK_GATE_MAC1CLK>, <&scu ASPEED_CLK_D2PLL>; }; diff --git a/drivers/clk/aspeed/clk_ast2500.c b/drivers/clk/aspeed/clk_ast2500.c index 392fe76b27..aab7d14deb 100644 --- a/drivers/clk/aspeed/clk_ast2500.c +++ b/drivers/clk/aspeed/clk_ast2500.c @@ -122,8 +122,7 @@ static ulong ast2500_clk_get_rate(struct clk *clk) ulong rate; switch (clk->id) { - case PLL_HPLL: - case ARMCLK: + case ASPEED_CLK_HPLL: /* * This ignores dynamic/static slowdown of ARMCLK and may * be inaccurate. @@ -131,11 +130,11 @@ static ulong ast2500_clk_get_rate(struct clk *clk) rate = ast2500_get_hpll_rate(clkin, readl(&priv->scu->h_pll_param)); break; - case MCLK_DDR: + case ASPEED_CLK_MPLL: rate = ast2500_get_mpll_rate(clkin, readl(&priv->scu->m_pll_param)); break; - case BCLK_PCLK: + case ASPEED_CLK_APB: { ulong apb_div = 4 + 4 * ((readl(&priv->scu->clk_sel1) & SCU_PCLK_DIV_MASK) @@ -146,7 +145,7 @@ static ulong ast2500_clk_get_rate(struct clk *clk) rate = rate / apb_div; } break; - case BCLK_SDCLK: + case ASPEED_CLK_SDIO: { ulong apb_div = 4 + 4 * ((readl(&priv->scu->clk_sel1) & SCU_SDCLK_DIV_MASK) @@ -157,19 +156,19 @@ static ulong ast2500_clk_get_rate(struct clk *clk) rate = rate / apb_div; } break; - case PCLK_UART1: + case ASPEED_CLK_GATE_UART1CLK: rate = ast2500_get_uart_clk_rate(priv->scu, 1); break; - case PCLK_UART2: + case ASPEED_CLK_GATE_UART2CLK: rate = ast2500_get_uart_clk_rate(priv->scu, 2); break; - case PCLK_UART3: + case ASPEED_CLK_GATE_UART3CLK: rate = ast2500_get_uart_clk_rate(priv->scu, 3); break; - case PCLK_UART4: + case ASPEED_CLK_GATE_UART4CLK: rate = ast2500_get_uart_clk_rate(priv->scu, 4); break; - case PCLK_UART5: + case ASPEED_CLK_GATE_UART5CLK: rate = ast2500_get_uart_clk_rate(priv->scu, 5); break; default: @@ -431,11 +430,10 @@ static ulong ast2500_clk_set_rate(struct clk *clk, ulong rate) ulong new_rate; switch (clk->id) { - case PLL_MPLL: - case MCLK_DDR: + case ASPEED_CLK_MPLL: new_rate = ast2500_configure_ddr(priv->scu, rate); break; - case PLL_D2PLL: + case ASPEED_CLK_D2PLL: new_rate = ast2500_configure_d2pll(priv->scu, rate); break; default: @@ -450,7 +448,7 @@ static int ast2500_clk_enable(struct clk *clk) struct ast2500_clk_priv *priv = dev_get_priv(clk->dev); switch (clk->id) { - case BCLK_SDCLK: + case ASPEED_CLK_SDIO: if (readl(&priv->scu->clk_stop_ctrl1) & SCU_CLKSTOP_SDCLK) { ast_scu_unlock(priv->scu); @@ -471,13 +469,13 @@ static int ast2500_clk_enable(struct clk *clk) * configured based on whether RGMII or RMII mode has been selected * through hardware strapping. */ - case PCLK_MAC1: + case ASPEED_CLK_GATE_MAC1CLK: ast2500_configure_mac(priv->scu, 1); break; - case PCLK_MAC2: + case ASPEED_CLK_GATE_MAC2CLK: ast2500_configure_mac(priv->scu, 2); break; - case PLL_D2PLL: + case ASPEED_CLK_D2PLL: ast2500_configure_d2pll(priv->scu, D2PLL_DEFAULT_RATE); break; default: @@ -497,9 +495,9 @@ static int ast2500_clk_ofdata_to_platdata(struct udevice *dev) { struct ast2500_clk_priv *priv = dev_get_priv(dev); - priv->scu = dev_read_addr_ptr(dev); - if (!priv->scu) - return -EINVAL; + priv->scu = devfdt_get_addr_ptr(dev); + if (IS_ERR(priv->scu)) + return PTR_ERR(priv->scu); return 0; } diff --git a/include/dt-bindings/clock/aspeed-clock.h b/include/dt-bindings/clock/aspeed-clock.h index 4803abe9f6..e6599deeb9 100644 --- a/include/dt-bindings/clock/aspeed-clock.h +++ b/include/dt-bindings/clock/aspeed-clock.h @@ -1,30 +1,42 @@ /* SPDX-License-Identifier: GPL-2.0+ */ -/* - * Copyright 2016 Google Inc. - */ -/* Core Clocks */ -#define PLL_HPLL 1 -#define PLL_DPLL 2 -#define PLL_D2PLL 3 -#define PLL_MPLL 4 -#define ARMCLK 5 - - -/* Bus Clocks, derived from core clocks */ -#define BCLK_PCLK 101 -#define BCLK_LHCLK 102 -#define BCLK_MACCLK 103 -#define BCLK_SDCLK 104 -#define BCLK_ARMCLK 105 - -#define MCLK_DDR 201 - -/* Special clocks */ -#define PCLK_UART1 501 -#define PCLK_UART2 502 -#define PCLK_UART3 503 -#define PCLK_UART4 504 -#define PCLK_UART5 505 -#define PCLK_MAC1 506 -#define PCLK_MAC2 507 +#define ASPEED_CLK_GATE_ECLK 0 +#define ASPEED_CLK_GATE_GCLK 1 +#define ASPEED_CLK_GATE_MCLK 2 +#define ASPEED_CLK_GATE_VCLK 3 +#define ASPEED_CLK_GATE_BCLK 4 +#define ASPEED_CLK_GATE_DCLK 5 +#define ASPEED_CLK_GATE_REFCLK 6 +#define ASPEED_CLK_GATE_USBPORT2CLK 7 +#define ASPEED_CLK_GATE_LCLK 8 +#define ASPEED_CLK_GATE_USBUHCICLK 9 +#define ASPEED_CLK_GATE_D1CLK 10 +#define ASPEED_CLK_GATE_YCLK 11 +#define ASPEED_CLK_GATE_USBPORT1CLK 12 +#define ASPEED_CLK_GATE_UART1CLK 13 +#define ASPEED_CLK_GATE_UART2CLK 14 +#define ASPEED_CLK_GATE_UART5CLK 15 +#define ASPEED_CLK_GATE_ESPICLK 16 +#define ASPEED_CLK_GATE_MAC1CLK 17 +#define ASPEED_CLK_GATE_MAC2CLK 18 +#define ASPEED_CLK_GATE_RSACLK 19 +#define ASPEED_CLK_GATE_UART3CLK 20 +#define ASPEED_CLK_GATE_UART4CLK 21 +#define ASPEED_CLK_GATE_SDCLK 22 +#define ASPEED_CLK_GATE_LHCCLK 23 +#define ASPEED_CLK_HPLL 24 +#define ASPEED_CLK_AHB 25 +#define ASPEED_CLK_APB 26 +#define ASPEED_CLK_UART 27 +#define ASPEED_CLK_SDIO 28 +#define ASPEED_CLK_ECLK 29 +#define ASPEED_CLK_ECLK_MUX 30 +#define ASPEED_CLK_LHCLK 31 +#define ASPEED_CLK_MAC 32 +#define ASPEED_CLK_BCLK 33 +#define ASPEED_CLK_MPLL 34 +#define ASPEED_CLK_24M 35 +#define ASPEED_CLK_MAC1RCLK 36 +#define ASPEED_CLK_MAC2RCLK 37 +#define ASPEED_CLK_DPLL 38 +#define ASPEED_CLK_D2PLL 39 -- cgit v1.2.3 From b04da9fcf78e233e70ea5ace757c0b8b95bb2459 Mon Sep 17 00:00:00 2001 From: Claudiu Beznea Date: Mon, 7 Sep 2020 17:46:32 +0300 Subject: clk: check hw and hw->dev before dereference it Check hw and hw->dev before dereference it. Signed-off-by: Claudiu Beznea Reviewed-by: Simon Glass --- drivers/clk/clk.c | 3 +++ 1 file changed, 3 insertions(+) (limited to 'drivers/clk') diff --git a/drivers/clk/clk.c b/drivers/clk/clk.c index 786f4e887e..319808d433 100644 --- a/drivers/clk/clk.c +++ b/drivers/clk/clk.c @@ -57,6 +57,9 @@ ulong clk_generic_get_rate(struct clk *clk) const char *clk_hw_get_name(const struct clk *hw) { + assert(hw); + assert(hw->dev); + return hw->dev->name; } -- cgit v1.2.3 From 4d139f3838b4ee48d7df9ca9c37ccd1a57202a52 Mon Sep 17 00:00:00 2001 From: Claudiu Beznea Date: Mon, 7 Sep 2020 17:46:34 +0300 Subject: clk: bind clk to new parent device Clock re-parenting is not binding the clock's device to its new parent device, it only calls the clock's ops->set_parent() API. The changes in this commit re-parent the clock device to its new parent so that subsequent operations like clk_get_parent() to point to the proper parent. Signed-off-by: Claudiu Beznea Reviewed-by: Simon Glass --- drivers/clk/clk-uclass.c | 11 ++++++++++- test/dm/clk_ccf.c | 27 +++++++++++++++++++++++++++ 2 files changed, 37 insertions(+), 1 deletion(-) (limited to 'drivers/clk') diff --git a/drivers/clk/clk-uclass.c b/drivers/clk/clk-uclass.c index 934cd5787a..5e0c8419d6 100644 --- a/drivers/clk/clk-uclass.c +++ b/drivers/clk/clk-uclass.c @@ -14,6 +14,7 @@ #include #include #include +#include #include #include #include @@ -512,6 +513,7 @@ ulong clk_set_rate(struct clk *clk, ulong rate) int clk_set_parent(struct clk *clk, struct clk *parent) { const struct clk_ops *ops; + int ret; debug("%s(clk=%p, parent=%p)\n", __func__, clk, parent); if (!clk_valid(clk)) @@ -521,7 +523,14 @@ int clk_set_parent(struct clk *clk, struct clk *parent) if (!ops->set_parent) return -ENOSYS; - return ops->set_parent(clk, parent); + ret = ops->set_parent(clk, parent); + if (ret) + return ret; + + if (CONFIG_IS_ENABLED(CLK_CCF)) + ret = device_reparent(clk->dev, parent->dev); + + return ret; } int clk_enable(struct clk *clk) diff --git a/test/dm/clk_ccf.c b/test/dm/clk_ccf.c index 32bc4d2b8a..1ce28d747d 100644 --- a/test/dm/clk_ccf.c +++ b/test/dm/clk_ccf.c @@ -22,6 +22,10 @@ static int dm_test_clk_ccf(struct unit_test_state *uts) struct udevice *dev; long long rate; int ret; +#if CONFIG_IS_ENABLED(CLK_CCF) + const char *clkname; + int clkid; +#endif /* Get the device using the clk device */ ut_assertok(uclass_get_device_by_name(UCLASS_CLK, "clk-ccf", &dev)); @@ -130,6 +134,29 @@ static int dm_test_clk_ccf(struct unit_test_state *uts) ret = sandbox_clk_enable_count(pclk); ut_asserteq(ret, 0); + + /* Test clock re-parenting. */ + ret = clk_get_by_id(SANDBOX_CLK_USDHC1_SEL, &clk); + ut_assertok(ret); + ut_asserteq_str("usdhc1_sel", clk->dev->name); + + pclk = clk_get_parent(clk); + ut_assertok_ptr(pclk); + if (!strcmp(pclk->dev->name, "pll3_60m")) { + clkname = "pll3_80m"; + clkid = SANDBOX_CLK_PLL3_80M; + } else { + clkname = "pll3_60m"; + clkid = SANDBOX_CLK_PLL3_60M; + } + + ret = clk_get_by_id(clkid, &pclk); + ut_assertok(ret); + ret = clk_set_parent(clk, pclk); + ut_assertok(ret); + pclk = clk_get_parent(clk); + ut_assertok_ptr(pclk); + ut_asserteq_str(clkname, pclk->dev->name); #endif return 1; -- cgit v1.2.3 From 9a5d59dfc6475c4770bf702e4f30e338499310fd Mon Sep 17 00:00:00 2001 From: Claudiu Beznea Date: Mon, 7 Sep 2020 17:46:35 +0300 Subject: clk: do not disable clock if it is critical Do not disable clock if it is a critical one. Signed-off-by: Claudiu Beznea Reviewed-by: Simon Glass --- drivers/clk/clk-uclass.c | 3 +++ test/dm/clk_ccf.c | 32 +++++++++++++++++++++++++++++++- 2 files changed, 34 insertions(+), 1 deletion(-) (limited to 'drivers/clk') diff --git a/drivers/clk/clk-uclass.c b/drivers/clk/clk-uclass.c index 5e0c8419d6..b8538f342a 100644 --- a/drivers/clk/clk-uclass.c +++ b/drivers/clk/clk-uclass.c @@ -606,6 +606,9 @@ int clk_disable(struct clk *clk) if (CONFIG_IS_ENABLED(CLK_CCF)) { if (clk->id && !clk_get_by_id(clk->id, &clkp)) { + if (clkp->flags & CLK_IS_CRITICAL) + return 0; + if (clkp->enable_count == 0) { printf("clk %s already disabled\n", clkp->dev->name); diff --git a/test/dm/clk_ccf.c b/test/dm/clk_ccf.c index 1ce28d747d..e4ebb93cda 100644 --- a/test/dm/clk_ccf.c +++ b/test/dm/clk_ccf.c @@ -24,7 +24,7 @@ static int dm_test_clk_ccf(struct unit_test_state *uts) int ret; #if CONFIG_IS_ENABLED(CLK_CCF) const char *clkname; - int clkid; + int clkid, i; #endif /* Get the device using the clk device */ @@ -157,6 +157,36 @@ static int dm_test_clk_ccf(struct unit_test_state *uts) pclk = clk_get_parent(clk); ut_assertok_ptr(pclk); ut_asserteq_str(clkname, pclk->dev->name); + + /* Test disabling critical clock. */ + ret = clk_get_by_id(SANDBOX_CLK_I2C_ROOT, &clk); + ut_assertok(ret); + ut_asserteq_str("i2c_root", clk->dev->name); + + /* Disable it, if any. */ + ret = sandbox_clk_enable_count(clk); + for (i = 0; i < ret; i++) { + ret = clk_disable(clk); + ut_assertok(ret); + } + + ret = sandbox_clk_enable_count(clk); + ut_asserteq(ret, 0); + + clk->flags = CLK_IS_CRITICAL; + ret = clk_enable(clk); + ut_assertok(ret); + + ret = clk_disable(clk); + ut_assertok(ret); + ret = sandbox_clk_enable_count(clk); + ut_asserteq(ret, 1); + clk->flags &= ~CLK_IS_CRITICAL; + + ret = clk_disable(clk); + ut_assertok(ret); + ret = sandbox_clk_enable_count(clk); + ut_asserteq(ret, 0); #endif return 1; -- cgit v1.2.3 From b364134f87a2b3d021746b86b1f81278b15fa296 Mon Sep 17 00:00:00 2001 From: Claudiu Beznea Date: Mon, 7 Sep 2020 17:46:36 +0300 Subject: clk: get clock pointer before proceeding clk_get_by_indexed_prop() retrieves a clock with dev member being set with the pointer to the udevice for the clock controller driver. But in case of CCF each clock driver has set in dev member the reference to its parent (the root of the clock tree is a fixed clock, every node in clock tree is a clock registered with clk_register()). In this case the subsequent operations like dev_get_clk_ptr() on clocks retrieved by clk_get_by_indexed_prop() will fail. For this, get the pointer to the proper clock registered (with clk_register()) using clk_get_by_id() before proceeding. Fixes: 1d7993d1d0ef ("clk: Port Linux common clock framework [CCF] for imx6q to U-boot (tag: v5.1.12)") Signed-off-by: Claudiu Beznea Reviewed-by: Simon Glass --- drivers/clk/clk-uclass.c | 37 +++++++++++++++++++++++++++++++++---- 1 file changed, 33 insertions(+), 4 deletions(-) (limited to 'drivers/clk') diff --git a/drivers/clk/clk-uclass.c b/drivers/clk/clk-uclass.c index b8538f342a..4076535271 100644 --- a/drivers/clk/clk-uclass.c +++ b/drivers/clk/clk-uclass.c @@ -188,9 +188,26 @@ bulk_get_err: return ret; } +static struct clk *clk_set_default_get_by_id(struct clk *clk) +{ + struct clk *c = clk; + + if (CONFIG_IS_ENABLED(CLK_CCF)) { + int ret = clk_get_by_id(clk->id, &c); + + if (ret) { + debug("%s(): could not get parent clock pointer, id %lu\n", + __func__, clk->id); + ERR_PTR(ret); + } + } + + return c; +} + static int clk_set_default_parents(struct udevice *dev, int stage) { - struct clk clk, parent_clk; + struct clk clk, parent_clk, *c, *p; int index; int num_parents; int ret; @@ -216,6 +233,10 @@ static int clk_set_default_parents(struct udevice *dev, int stage) return ret; } + p = clk_set_default_get_by_id(&parent_clk); + if (IS_ERR(p)) + return PTR_ERR(p); + ret = clk_get_by_indexed_prop(dev, "assigned-clocks", index, &clk); if (ret) { @@ -235,7 +256,11 @@ static int clk_set_default_parents(struct udevice *dev, int stage) /* do not setup twice the parent clocks */ continue; - ret = clk_set_parent(&clk, &parent_clk); + c = clk_set_default_get_by_id(&clk); + if (IS_ERR(c)) + return PTR_ERR(c); + + ret = clk_set_parent(c, p); /* * Not all drivers may support clock-reparenting (as of now). * Ignore errors due to this. @@ -255,7 +280,7 @@ static int clk_set_default_parents(struct udevice *dev, int stage) static int clk_set_default_rates(struct udevice *dev, int stage) { - struct clk clk; + struct clk clk, *c; int index; int num_rates; int size; @@ -299,7 +324,11 @@ static int clk_set_default_rates(struct udevice *dev, int stage) /* do not setup twice the parent clocks */ continue; - ret = clk_set_rate(&clk, rates[index]); + c = clk_set_default_get_by_id(&clk); + if (IS_ERR(c)) + return PTR_ERR(c); + + ret = clk_set_rate(c, rates[index]); if (ret < 0) { debug("%s: failed to set rate on clock index %d (%ld) for %s\n", -- cgit v1.2.3 From 5d729f96299321af2c132fae4b56306e19a3f52b Mon Sep 17 00:00:00 2001 From: Claudiu Beznea Date: Mon, 7 Sep 2020 17:46:38 +0300 Subject: clk: at91: pmc: add helpers for clock drivers Add helper for clock drivers. These will be used by following commits in the process of switching AT91 clock drivers to CCF. Signed-off-by: Claudiu Beznea --- drivers/clk/at91/pmc.c | 91 ++++++++++++++++++++++++++++++++++++++++++++++++++ drivers/clk/at91/pmc.h | 13 ++++++++ 2 files changed, 104 insertions(+) (limited to 'drivers/clk') diff --git a/drivers/clk/at91/pmc.c b/drivers/clk/at91/pmc.c index ca90abef2d..e403baea0e 100644 --- a/drivers/clk/at91/pmc.c +++ b/drivers/clk/at91/pmc.c @@ -120,3 +120,94 @@ int at91_clk_probe(struct udevice *dev) return 0; } + +/** + * pmc_read() - read content at address base + off into val + * + * @base: base address + * @off: offset to read from + * @val: where the content of base + off is stored + * + * @return: void + */ +void pmc_read(void __iomem *base, unsigned int off, unsigned int *val) +{ + *val = readl(base + off); +} + +/** + * pmc_write() - write content of val at address base + off + * + * @base: base address + * @off: offset to write to + * @val: content to be written at base + off + * + * @return: void + */ +void pmc_write(void __iomem *base, unsigned int off, unsigned int val) +{ + writel(val, base + off); +} + +/** + * pmc_update_bits() - update a set of bits at address base + off + * + * @base: base address + * @off: offset to be updated + * @mask: mask of bits to be updated + * @bits: the new value to be updated + * + * @return: void + */ +void pmc_update_bits(void __iomem *base, unsigned int off, + unsigned int mask, unsigned int bits) +{ + unsigned int tmp; + + tmp = readl(base + off); + tmp &= ~mask; + writel(tmp | (bits & mask), base + off); +} + +/** + * at91_clk_mux_val_to_index() - get parent index in mux table + * + * @table: clock mux table + * @num_parents: clock number of parents + * @val: clock id who's mux index should be retrieved + * + * @return: clock index in mux table or a negative error number in case of + * failure + */ +int at91_clk_mux_val_to_index(const u32 *table, u32 num_parents, u32 val) +{ + int i; + + if (!table || !num_parents) + return -EINVAL; + + for (i = 0; i < num_parents; i++) { + if (table[i] == val) + return i; + } + + return -EINVAL; +} + +/** + * at91_clk_mux_index_to_val() - get parent ID corresponding to an entry in + * clock's mux table + * + * @table: clock's mux table + * @num_parents: clock's number of parents + * @index: index in mux table which clock's ID should be retrieved + * + * @return: clock ID or a negative error number in case of failure + */ +int at91_clk_mux_index_to_val(const u32 *table, u32 num_parents, u32 index) +{ + if (!table || !num_parents || index < 0 || index > num_parents) + return -EINVAL; + + return table[index]; +} diff --git a/drivers/clk/at91/pmc.h b/drivers/clk/at91/pmc.h index 517ba1d6b4..b1ab0a95c8 100644 --- a/drivers/clk/at91/pmc.h +++ b/drivers/clk/at91/pmc.h @@ -8,6 +8,12 @@ #define __AT91_PMC_H__ #include +#include +#include + +/* Keep a range of 256 available clocks for every clock type. */ +#define AT91_TO_CLK_ID(_t, _i) (((_t) << 8) | ((_i) & 0xff)) +#define AT91_CLK_ID_TO_DID(_i) ((_i) & 0xff) struct pmc_platdata { struct at91_pmc *reg_base; @@ -20,4 +26,11 @@ int at91_clk_sub_device_bind(struct udevice *dev, const char *drv_name); int at91_clk_of_xlate(struct clk *clk, struct ofnode_phandle_args *args); int at91_clk_probe(struct udevice *dev); +int at91_clk_mux_val_to_index(const u32 *table, u32 num_parents, u32 val); +int at91_clk_mux_index_to_val(const u32 *table, u32 num_parents, u32 index); + +void pmc_read(void __iomem *base, unsigned int off, unsigned int *val); +void pmc_write(void __iomem *base, unsigned int off, unsigned int val); +void pmc_update_bits(void __iomem *base, unsigned int off, unsigned int mask, + unsigned int bits); #endif -- cgit v1.2.3 From 653bcce4085a5816a9fee560412fbfaa171db7bb Mon Sep 17 00:00:00 2001 From: Claudiu Beznea Date: Mon, 7 Sep 2020 17:46:39 +0300 Subject: clk: at91: move clock code to compat.c Move clock code to compat.c to allow switching to CCF without mixing CCF code with non CCF code. This prepares the field for next commits. Signed-off-by: Claudiu Beznea --- drivers/clk/at91/Makefile | 13 +- drivers/clk/at91/clk-generated.c | 178 ------- drivers/clk/at91/clk-h32mx.c | 56 -- drivers/clk/at91/clk-main.c | 54 -- drivers/clk/at91/clk-master.c | 33 -- drivers/clk/at91/clk-peripheral.c | 113 ---- drivers/clk/at91/clk-plla.c | 54 -- drivers/clk/at91/clk-plladiv.c | 85 --- drivers/clk/at91/clk-slow.c | 36 -- drivers/clk/at91/clk-system.c | 111 ---- drivers/clk/at91/clk-usb.c | 147 ------ drivers/clk/at91/clk-utmi.c | 142 ----- drivers/clk/at91/compat.c | 1023 +++++++++++++++++++++++++++++++++++++ drivers/clk/at91/pmc.c | 116 +---- drivers/clk/at91/pmc.h | 13 +- drivers/clk/at91/sckc.c | 19 - 16 files changed, 1030 insertions(+), 1163 deletions(-) delete mode 100644 drivers/clk/at91/clk-generated.c delete mode 100644 drivers/clk/at91/clk-h32mx.c delete mode 100644 drivers/clk/at91/clk-main.c delete mode 100644 drivers/clk/at91/clk-master.c delete mode 100644 drivers/clk/at91/clk-peripheral.c delete mode 100644 drivers/clk/at91/clk-plla.c delete mode 100644 drivers/clk/at91/clk-plladiv.c delete mode 100644 drivers/clk/at91/clk-slow.c delete mode 100644 drivers/clk/at91/clk-system.c delete mode 100644 drivers/clk/at91/clk-usb.c delete mode 100644 drivers/clk/at91/clk-utmi.c create mode 100644 drivers/clk/at91/compat.c delete mode 100644 drivers/clk/at91/sckc.c (limited to 'drivers/clk') diff --git a/drivers/clk/at91/Makefile b/drivers/clk/at91/Makefile index 8c197ff949..e2413af403 100644 --- a/drivers/clk/at91/Makefile +++ b/drivers/clk/at91/Makefile @@ -2,11 +2,8 @@ # Makefile for at91 specific clk # -obj-y += pmc.o sckc.o -obj-y += clk-slow.o clk-main.o clk-plla.o clk-plladiv.o clk-master.o -obj-y += clk-system.o clk-peripheral.o - -obj-$(CONFIG_AT91_UTMI) += clk-utmi.o -obj-$(CONFIG_AT91_USB_CLK) += clk-usb.o -obj-$(CONFIG_AT91_H32MX) += clk-h32mx.o -obj-$(CONFIG_AT91_GENERIC_CLK) += clk-generated.o +ifdef CONFIG_CLK_CCF +obj-y += pmc.o +else +obj-y += compat.o +endif diff --git a/drivers/clk/at91/clk-generated.c b/drivers/clk/at91/clk-generated.c deleted file mode 100644 index c0610940c3..0000000000 --- a/drivers/clk/at91/clk-generated.c +++ /dev/null @@ -1,178 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0+ -/* - * Copyright (C) 2016 Atmel Corporation - * Wenyou.Yang - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include "pmc.h" - -DECLARE_GLOBAL_DATA_PTR; - -#define GENERATED_SOURCE_MAX 6 -#define GENERATED_MAX_DIV 255 - -/** - * generated_clk_bind() - for the generated clock driver - * Recursively bind its children as clk devices. - * - * @return: 0 on success, or negative error code on failure - */ -static int generated_clk_bind(struct udevice *dev) -{ - return at91_clk_sub_device_bind(dev, "generic-clk"); -} - -static const struct udevice_id generated_clk_match[] = { - { .compatible = "atmel,sama5d2-clk-generated" }, - {} -}; - -U_BOOT_DRIVER(generated_clk) = { - .name = "generated-clk", - .id = UCLASS_MISC, - .of_match = generated_clk_match, - .bind = generated_clk_bind, -}; - -/*-------------------------------------------------------------*/ - -struct generic_clk_priv { - u32 num_parents; -}; - -static ulong generic_clk_get_rate(struct clk *clk) -{ - struct pmc_platdata *plat = dev_get_platdata(clk->dev); - struct at91_pmc *pmc = plat->reg_base; - struct clk parent; - ulong clk_rate; - u32 tmp, gckdiv; - u8 clock_source, parent_index; - int ret; - - writel(clk->id & AT91_PMC_PCR_PID_MASK, &pmc->pcr); - tmp = readl(&pmc->pcr); - clock_source = (tmp >> AT91_PMC_PCR_GCKCSS_OFFSET) & - AT91_PMC_PCR_GCKCSS_MASK; - gckdiv = (tmp >> AT91_PMC_PCR_GCKDIV_OFFSET) & AT91_PMC_PCR_GCKDIV_MASK; - - parent_index = clock_source - 1; - ret = clk_get_by_index(dev_get_parent(clk->dev), parent_index, &parent); - if (ret) - return 0; - - clk_rate = clk_get_rate(&parent) / (gckdiv + 1); - - clk_free(&parent); - - return clk_rate; -} - -static ulong generic_clk_set_rate(struct clk *clk, ulong rate) -{ - struct pmc_platdata *plat = dev_get_platdata(clk->dev); - struct at91_pmc *pmc = plat->reg_base; - struct generic_clk_priv *priv = dev_get_priv(clk->dev); - struct clk parent, best_parent; - ulong tmp_rate, best_rate = rate, parent_rate; - int tmp_diff, best_diff = -1; - u32 div, best_div = 0; - u8 best_parent_index, best_clock_source = 0; - u8 i; - u32 tmp; - int ret; - - for (i = 0; i < priv->num_parents; i++) { - ret = clk_get_by_index(dev_get_parent(clk->dev), i, &parent); - if (ret) - return ret; - - parent_rate = clk_get_rate(&parent); - if (IS_ERR_VALUE(parent_rate)) - return parent_rate; - - for (div = 1; div < GENERATED_MAX_DIV + 2; div++) { - tmp_rate = DIV_ROUND_CLOSEST(parent_rate, div); - tmp_diff = abs(rate - tmp_rate); - - if (best_diff < 0 || best_diff > tmp_diff) { - best_rate = tmp_rate; - best_diff = tmp_diff; - - best_div = div - 1; - best_parent = parent; - best_parent_index = i; - best_clock_source = best_parent_index + 1; - } - - if (!best_diff || tmp_rate < rate) - break; - } - - if (!best_diff) - break; - } - - debug("GCK: best parent: %s, best_rate = %ld, best_div = %d\n", - best_parent.dev->name, best_rate, best_div); - - ret = clk_enable(&best_parent); - if (ret) - return ret; - - writel(clk->id & AT91_PMC_PCR_PID_MASK, &pmc->pcr); - tmp = readl(&pmc->pcr); - tmp &= ~(AT91_PMC_PCR_GCKDIV | AT91_PMC_PCR_GCKCSS); - tmp |= AT91_PMC_PCR_GCKCSS_(best_clock_source) | - AT91_PMC_PCR_CMD_WRITE | - AT91_PMC_PCR_GCKDIV_(best_div) | - AT91_PMC_PCR_GCKEN; - writel(tmp, &pmc->pcr); - - while (!(readl(&pmc->sr) & AT91_PMC_GCKRDY)) - ; - - return 0; -} - -static struct clk_ops generic_clk_ops = { - .of_xlate = at91_clk_of_xlate, - .get_rate = generic_clk_get_rate, - .set_rate = generic_clk_set_rate, -}; - -static int generic_clk_ofdata_to_platdata(struct udevice *dev) -{ - struct generic_clk_priv *priv = dev_get_priv(dev); - u32 cells[GENERATED_SOURCE_MAX]; - u32 num_parents; - - num_parents = fdtdec_get_int_array_count(gd->fdt_blob, - dev_of_offset(dev_get_parent(dev)), "clocks", cells, - GENERATED_SOURCE_MAX); - - if (!num_parents) - return -1; - - priv->num_parents = num_parents; - - return 0; -} - -U_BOOT_DRIVER(generic_clk) = { - .name = "generic-clk", - .id = UCLASS_CLK, - .probe = at91_clk_probe, - .ofdata_to_platdata = generic_clk_ofdata_to_platdata, - .priv_auto_alloc_size = sizeof(struct generic_clk_priv), - .platdata_auto_alloc_size = sizeof(struct pmc_platdata), - .ops = &generic_clk_ops, -}; diff --git a/drivers/clk/at91/clk-h32mx.c b/drivers/clk/at91/clk-h32mx.c deleted file mode 100644 index 86bb71f612..0000000000 --- a/drivers/clk/at91/clk-h32mx.c +++ /dev/null @@ -1,56 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0+ -/* - * Copyright (C) 2016 Atmel Corporation - * Wenyou.Yang - */ - -#include -#include -#include -#include -#include -#include -#include -#include "pmc.h" - -DECLARE_GLOBAL_DATA_PTR; - -#define H32MX_MAX_FREQ 90000000 - -static ulong sama5d4_h32mx_clk_get_rate(struct clk *clk) -{ - struct pmc_platdata *plat = dev_get_platdata(clk->dev); - struct at91_pmc *pmc = plat->reg_base; - ulong rate = gd->arch.mck_rate_hz; - - if (readl(&pmc->mckr) & AT91_PMC_MCKR_H32MXDIV) - rate /= 2; - - if (rate > H32MX_MAX_FREQ) - dev_dbg(clk->dev, "H32MX clock is too fast\n"); - - return rate; -} - -static struct clk_ops sama5d4_h32mx_clk_ops = { - .get_rate = sama5d4_h32mx_clk_get_rate, -}; - -static int sama5d4_h32mx_clk_probe(struct udevice *dev) -{ - return at91_pmc_core_probe(dev); -} - -static const struct udevice_id sama5d4_h32mx_clk_match[] = { - { .compatible = "atmel,sama5d4-clk-h32mx" }, - {} -}; - -U_BOOT_DRIVER(sama5d4_h32mx_clk) = { - .name = "sama5d4-h32mx-clk", - .id = UCLASS_CLK, - .of_match = sama5d4_h32mx_clk_match, - .probe = sama5d4_h32mx_clk_probe, - .platdata_auto_alloc_size = sizeof(struct pmc_platdata), - .ops = &sama5d4_h32mx_clk_ops, -}; diff --git a/drivers/clk/at91/clk-main.c b/drivers/clk/at91/clk-main.c deleted file mode 100644 index b31a1cb682..0000000000 --- a/drivers/clk/at91/clk-main.c +++ /dev/null @@ -1,54 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0+ -/* - * Copyright (C) 2016 Atmel Corporation - * Wenyou.Yang - */ - -#include -#include -#include -#include -#include -#include "pmc.h" - -DECLARE_GLOBAL_DATA_PTR; - -static int main_osc_clk_enable(struct clk *clk) -{ - struct pmc_platdata *plat = dev_get_platdata(clk->dev); - struct at91_pmc *pmc = plat->reg_base; - - if (readl(&pmc->sr) & AT91_PMC_MOSCSELS) - return 0; - - return -EINVAL; -} - -static ulong main_osc_clk_get_rate(struct clk *clk) -{ - return gd->arch.main_clk_rate_hz; -} - -static struct clk_ops main_osc_clk_ops = { - .enable = main_osc_clk_enable, - .get_rate = main_osc_clk_get_rate, -}; - -static int main_osc_clk_probe(struct udevice *dev) -{ - return at91_pmc_core_probe(dev); -} - -static const struct udevice_id main_osc_clk_match[] = { - { .compatible = "atmel,at91sam9x5-clk-main" }, - {} -}; - -U_BOOT_DRIVER(at91sam9x5_main_osc_clk) = { - .name = "at91sam9x5-main-osc-clk", - .id = UCLASS_CLK, - .of_match = main_osc_clk_match, - .probe = main_osc_clk_probe, - .platdata_auto_alloc_size = sizeof(struct pmc_platdata), - .ops = &main_osc_clk_ops, -}; diff --git a/drivers/clk/at91/clk-master.c b/drivers/clk/at91/clk-master.c deleted file mode 100644 index e078fab7b4..0000000000 --- a/drivers/clk/at91/clk-master.c +++ /dev/null @@ -1,33 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0+ -/* - * Copyright (C) 2016 Atmel Corporation - * Wenyou.Yang - */ - -#include -#include -#include - -DECLARE_GLOBAL_DATA_PTR; - -static ulong at91_master_clk_get_rate(struct clk *clk) -{ - return gd->arch.mck_rate_hz; -} - -static struct clk_ops at91_master_clk_ops = { - .get_rate = at91_master_clk_get_rate, -}; - -static const struct udevice_id at91_master_clk_match[] = { - { .compatible = "atmel,at91rm9200-clk-master" }, - { .compatible = "atmel,at91sam9x5-clk-master" }, - {} -}; - -U_BOOT_DRIVER(atmel_at91rm9200_clk_master) = { - .name = "atmel_at91rm9200_clk_master", - .id = UCLASS_CLK, - .of_match = at91_master_clk_match, - .ops = &at91_master_clk_ops, -}; diff --git a/drivers/clk/at91/clk-peripheral.c b/drivers/clk/at91/clk-peripheral.c deleted file mode 100644 index cd9d5e77c0..0000000000 --- a/drivers/clk/at91/clk-peripheral.c +++ /dev/null @@ -1,113 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0+ -/* - * Copyright (C) 2016 Atmel Corporation - * Wenyou.Yang - */ - -#include -#include -#include -#include -#include -#include -#include "pmc.h" - -#define PERIPHERAL_ID_MIN 2 -#define PERIPHERAL_ID_MAX 31 -#define PERIPHERAL_MASK(id) (1 << ((id) & PERIPHERAL_ID_MAX)) - -enum periph_clk_type { - CLK_PERIPH_AT91RM9200 = 0, - CLK_PERIPH_AT91SAM9X5, -}; -/** - * sam9x5_periph_clk_bind() - for the periph clock driver - * Recursively bind its children as clk devices. - * - * @return: 0 on success, or negative error code on failure - */ -static int sam9x5_periph_clk_bind(struct udevice *dev) -{ - return at91_clk_sub_device_bind(dev, "periph-clk"); -} - -static const struct udevice_id sam9x5_periph_clk_match[] = { - { - .compatible = "atmel,at91rm9200-clk-peripheral", - .data = CLK_PERIPH_AT91RM9200, - }, - { - .compatible = "atmel,at91sam9x5-clk-peripheral", - .data = CLK_PERIPH_AT91SAM9X5, - }, - {} -}; - -U_BOOT_DRIVER(atmel_at91rm9200_clk_peripheral) = { - .name = "atmel_at91rm9200_clk_peripheral", - .id = UCLASS_MISC, - .of_match = sam9x5_periph_clk_match, - .bind = sam9x5_periph_clk_bind, -}; - -/*---------------------------------------------------------*/ - -static int periph_clk_enable(struct clk *clk) -{ - struct pmc_platdata *plat = dev_get_platdata(clk->dev); - struct at91_pmc *pmc = plat->reg_base; - enum periph_clk_type clk_type; - void *addr; - - if (clk->id < PERIPHERAL_ID_MIN) - return -1; - - clk_type = dev_get_driver_data(dev_get_parent(clk->dev)); - if (clk_type == CLK_PERIPH_AT91RM9200) { - addr = &pmc->pcer; - if (clk->id > PERIPHERAL_ID_MAX) - addr = &pmc->pcer1; - - setbits_le32(addr, PERIPHERAL_MASK(clk->id)); - } else { - writel(clk->id & AT91_PMC_PCR_PID_MASK, &pmc->pcr); - setbits_le32(&pmc->pcr, - AT91_PMC_PCR_CMD_WRITE | AT91_PMC_PCR_EN); - } - - return 0; -} - -static ulong periph_get_rate(struct clk *clk) -{ - struct udevice *dev; - struct clk clk_dev; - ulong clk_rate; - int ret; - - dev = dev_get_parent(clk->dev); - - ret = clk_get_by_index(dev, 0, &clk_dev); - if (ret) - return ret; - - clk_rate = clk_get_rate(&clk_dev); - - clk_free(&clk_dev); - - return clk_rate; -} - -static struct clk_ops periph_clk_ops = { - .of_xlate = at91_clk_of_xlate, - .enable = periph_clk_enable, - .get_rate = periph_get_rate, -}; - -U_BOOT_DRIVER(clk_periph) = { - .name = "periph-clk", - .id = UCLASS_CLK, - .platdata_auto_alloc_size = sizeof(struct pmc_platdata), - .probe = at91_clk_probe, - .ops = &periph_clk_ops, -}; diff --git a/drivers/clk/at91/clk-plla.c b/drivers/clk/at91/clk-plla.c deleted file mode 100644 index 79d725819f..0000000000 --- a/drivers/clk/at91/clk-plla.c +++ /dev/null @@ -1,54 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0+ -/* - * Copyright (C) 2016 Atmel Corporation - * Wenyou.Yang - */ - -#include -#include -#include -#include -#include -#include "pmc.h" - -DECLARE_GLOBAL_DATA_PTR; - -static int plla_clk_enable(struct clk *clk) -{ - struct pmc_platdata *plat = dev_get_platdata(clk->dev); - struct at91_pmc *pmc = plat->reg_base; - - if (readl(&pmc->sr) & AT91_PMC_LOCKA) - return 0; - - return -EINVAL; -} - -static ulong plla_clk_get_rate(struct clk *clk) -{ - return gd->arch.plla_rate_hz; -} - -static struct clk_ops plla_clk_ops = { - .enable = plla_clk_enable, - .get_rate = plla_clk_get_rate, -}; - -static int plla_clk_probe(struct udevice *dev) -{ - return at91_pmc_core_probe(dev); -} - -static const struct udevice_id plla_clk_match[] = { - { .compatible = "atmel,sama5d3-clk-pll" }, - {} -}; - -U_BOOT_DRIVER(at91_plla_clk) = { - .name = "at91-plla-clk", - .id = UCLASS_CLK, - .of_match = plla_clk_match, - .probe = plla_clk_probe, - .platdata_auto_alloc_size = sizeof(struct pmc_platdata), - .ops = &plla_clk_ops, -}; diff --git a/drivers/clk/at91/clk-plladiv.c b/drivers/clk/at91/clk-plladiv.c deleted file mode 100644 index ca6158ef6a..0000000000 --- a/drivers/clk/at91/clk-plladiv.c +++ /dev/null @@ -1,85 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0+ -/* - * Copyright (C) 2018 Microhip / Atmel Corporation - * Wenyou.Yang - */ - -#include -#include -#include -#include -#include -#include "pmc.h" - -static int at91_plladiv_clk_enable(struct clk *clk) -{ - return 0; -} - -static ulong at91_plladiv_clk_get_rate(struct clk *clk) -{ - struct pmc_platdata *plat = dev_get_platdata(clk->dev); - struct at91_pmc *pmc = plat->reg_base; - struct clk source; - ulong clk_rate; - int ret; - - ret = clk_get_by_index(clk->dev, 0, &source); - if (ret) - return -EINVAL; - - clk_rate = clk_get_rate(&source); - if (readl(&pmc->mckr) & AT91_PMC_MCKR_PLLADIV_2) - clk_rate /= 2; - - return clk_rate; -} - -static ulong at91_plladiv_clk_set_rate(struct clk *clk, ulong rate) -{ - struct pmc_platdata *plat = dev_get_platdata(clk->dev); - struct at91_pmc *pmc = plat->reg_base; - struct clk source; - ulong parent_rate; - int ret; - - ret = clk_get_by_index(clk->dev, 0, &source); - if (ret) - return -EINVAL; - - parent_rate = clk_get_rate(&source); - if ((parent_rate != rate) && ((parent_rate) / 2 != rate)) - return -EINVAL; - - if (parent_rate != rate) { - writel((readl(&pmc->mckr) | AT91_PMC_MCKR_PLLADIV_2), - &pmc->mckr); - } - - return 0; -} - -static struct clk_ops at91_plladiv_clk_ops = { - .enable = at91_plladiv_clk_enable, - .get_rate = at91_plladiv_clk_get_rate, - .set_rate = at91_plladiv_clk_set_rate, -}; - -static int at91_plladiv_clk_probe(struct udevice *dev) -{ - return at91_pmc_core_probe(dev); -} - -static const struct udevice_id at91_plladiv_clk_match[] = { - { .compatible = "atmel,at91sam9x5-clk-plldiv" }, - {} -}; - -U_BOOT_DRIVER(at91_plladiv_clk) = { - .name = "at91-plladiv-clk", - .id = UCLASS_CLK, - .of_match = at91_plladiv_clk_match, - .probe = at91_plladiv_clk_probe, - .platdata_auto_alloc_size = sizeof(struct pmc_platdata), - .ops = &at91_plladiv_clk_ops, -}; diff --git a/drivers/clk/at91/clk-slow.c b/drivers/clk/at91/clk-slow.c deleted file mode 100644 index 1f8665768b..0000000000 --- a/drivers/clk/at91/clk-slow.c +++ /dev/null @@ -1,36 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0+ -/* - * Copyright (C) 2016 Atmel Corporation - * Wenyou.Yang - */ - -#include -#include -#include - -static int at91_slow_clk_enable(struct clk *clk) -{ - return 0; -} - -static ulong at91_slow_clk_get_rate(struct clk *clk) -{ - return CONFIG_SYS_AT91_SLOW_CLOCK; -} - -static struct clk_ops at91_slow_clk_ops = { - .enable = at91_slow_clk_enable, - .get_rate = at91_slow_clk_get_rate, -}; - -static const struct udevice_id at91_slow_clk_match[] = { - { .compatible = "atmel,at91sam9x5-clk-slow" }, - {} -}; - -U_BOOT_DRIVER(at91_slow_clk) = { - .name = "at91-slow-clk", - .id = UCLASS_CLK, - .of_match = at91_slow_clk_match, - .ops = &at91_slow_clk_ops, -}; diff --git a/drivers/clk/at91/clk-system.c b/drivers/clk/at91/clk-system.c deleted file mode 100644 index 76b1958670..0000000000 --- a/drivers/clk/at91/clk-system.c +++ /dev/null @@ -1,111 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0+ -/* - * Copyright (C) 2016 Atmel Corporation - * Wenyou.Yang - */ - -#include -#include -#include -#include -#include -#include -#include "pmc.h" - -#define SYSTEM_MAX_ID 31 - -/** - * at91_system_clk_bind() - for the system clock driver - * Recursively bind its children as clk devices. - * - * @return: 0 on success, or negative error code on failure - */ -static int at91_system_clk_bind(struct udevice *dev) -{ - return at91_clk_sub_device_bind(dev, "system-clk"); -} - -static const struct udevice_id at91_system_clk_match[] = { - { .compatible = "atmel,at91rm9200-clk-system" }, - {} -}; - -U_BOOT_DRIVER(at91_system_clk) = { - .name = "at91-system-clk", - .id = UCLASS_MISC, - .of_match = at91_system_clk_match, - .bind = at91_system_clk_bind, -}; - -/*----------------------------------------------------------*/ - -static inline int is_pck(int id) -{ - return (id >= 8) && (id <= 15); -} - -static ulong system_clk_get_rate(struct clk *clk) -{ - struct clk clk_dev; - int ret; - - ret = clk_get_by_index(clk->dev, 0, &clk_dev); - if (ret) - return -EINVAL; - - return clk_get_rate(&clk_dev); -} - -static ulong system_clk_set_rate(struct clk *clk, ulong rate) -{ - struct clk clk_dev; - int ret; - - ret = clk_get_by_index(clk->dev, 0, &clk_dev); - if (ret) - return -EINVAL; - - return clk_set_rate(&clk_dev, rate); -} - -static int system_clk_enable(struct clk *clk) -{ - struct pmc_platdata *plat = dev_get_platdata(clk->dev); - struct at91_pmc *pmc = plat->reg_base; - u32 mask; - - if (clk->id > SYSTEM_MAX_ID) - return -EINVAL; - - mask = BIT(clk->id); - - writel(mask, &pmc->scer); - - /** - * For the programmable clocks the Ready status in the PMC - * status register should be checked after enabling. - * For other clocks this is unnecessary. - */ - if (!is_pck(clk->id)) - return 0; - - while (!(readl(&pmc->sr) & mask)) - ; - - return 0; -} - -static struct clk_ops system_clk_ops = { - .of_xlate = at91_clk_of_xlate, - .get_rate = system_clk_get_rate, - .set_rate = system_clk_set_rate, - .enable = system_clk_enable, -}; - -U_BOOT_DRIVER(system_clk) = { - .name = "system-clk", - .id = UCLASS_CLK, - .probe = at91_clk_probe, - .platdata_auto_alloc_size = sizeof(struct pmc_platdata), - .ops = &system_clk_ops, -}; diff --git a/drivers/clk/at91/clk-usb.c b/drivers/clk/at91/clk-usb.c deleted file mode 100644 index af9d724369..0000000000 --- a/drivers/clk/at91/clk-usb.c +++ /dev/null @@ -1,147 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0+ -/* - * Copyright (C) 2018 Microhip / Atmel Corporation - * Wenyou.Yang - */ - -#include -#include -#include -#include -#include -#include -#include -#include "pmc.h" - -DECLARE_GLOBAL_DATA_PTR; - -#define AT91_USB_CLK_SOURCE_MAX 2 -#define AT91_USB_CLK_MAX_DIV 15 - -struct at91_usb_clk_priv { - u32 num_clksource; -}; - -static ulong at91_usb_clk_get_rate(struct clk *clk) -{ - struct pmc_platdata *plat = dev_get_platdata(clk->dev); - struct at91_pmc *pmc = plat->reg_base; - struct clk source; - u32 tmp, usbdiv; - u8 source_index; - int ret; - - tmp = readl(&pmc->pcr); - source_index = (tmp >> AT91_PMC_USB_USBS_OFFSET) & - AT91_PMC_USB_USBS_MASK; - usbdiv = (tmp >> AT91_PMC_USB_DIV_OFFSET) & AT91_PMC_USB_DIV_MASK; - - ret = clk_get_by_index(clk->dev, source_index, &source); - if (ret) - return 0; - - return clk_get_rate(&source) / (usbdiv + 1); -} - -static ulong at91_usb_clk_set_rate(struct clk *clk, ulong rate) -{ - struct pmc_platdata *plat = dev_get_platdata(clk->dev); - struct at91_pmc *pmc = plat->reg_base; - struct at91_usb_clk_priv *priv = dev_get_priv(clk->dev); - struct clk source, best_source; - ulong tmp_rate, best_rate = rate, source_rate; - int tmp_diff, best_diff = -1; - u32 div, best_div = 0; - u8 best_source_index = 0; - u8 i; - u32 tmp; - int ret; - - for (i = 0; i < priv->num_clksource; i++) { - ret = clk_get_by_index(clk->dev, i, &source); - if (ret) - return ret; - - source_rate = clk_get_rate(&source); - if (IS_ERR_VALUE(source_rate)) - return source_rate; - - for (div = 1; div < AT91_USB_CLK_MAX_DIV + 2; div++) { - tmp_rate = DIV_ROUND_CLOSEST(source_rate, div); - tmp_diff = abs(rate - tmp_rate); - - if (best_diff < 0 || best_diff > tmp_diff) { - best_rate = tmp_rate; - best_diff = tmp_diff; - - best_div = div - 1; - best_source = source; - best_source_index = i; - } - - if (!best_diff || tmp_rate < rate) - break; - } - - if (!best_diff) - break; - } - - debug("AT91 USB: best sourc: %s, best_rate = %ld, best_div = %d\n", - best_source.dev->name, best_rate, best_div); - - ret = clk_enable(&best_source); - if (ret) - return ret; - - tmp = AT91_PMC_USB_USBS_(best_source_index) | - AT91_PMC_USB_DIV_(best_div); - writel(tmp, &pmc->usb); - - return 0; -} - -static struct clk_ops at91_usb_clk_ops = { - .get_rate = at91_usb_clk_get_rate, - .set_rate = at91_usb_clk_set_rate, -}; - -static int at91_usb_clk_ofdata_to_platdata(struct udevice *dev) -{ - struct at91_usb_clk_priv *priv = dev_get_priv(dev); - u32 cells[AT91_USB_CLK_SOURCE_MAX]; - u32 num_clksource; - - num_clksource = fdtdec_get_int_array_count(gd->fdt_blob, - dev_of_offset(dev), - "clocks", cells, - AT91_USB_CLK_SOURCE_MAX); - - if (!num_clksource) - return -1; - - priv->num_clksource = num_clksource; - - return 0; -} - -static int at91_usb_clk_probe(struct udevice *dev) -{ - return at91_pmc_core_probe(dev); -} - -static const struct udevice_id at91_usb_clk_match[] = { - { .compatible = "atmel,at91sam9x5-clk-usb" }, - {} -}; - -U_BOOT_DRIVER(at91_usb_clk) = { - .name = "at91-usb-clk", - .id = UCLASS_CLK, - .of_match = at91_usb_clk_match, - .probe = at91_usb_clk_probe, - .ofdata_to_platdata = at91_usb_clk_ofdata_to_platdata, - .priv_auto_alloc_size = sizeof(struct at91_usb_clk_priv), - .platdata_auto_alloc_size = sizeof(struct pmc_platdata), - .ops = &at91_usb_clk_ops, -}; diff --git a/drivers/clk/at91/clk-utmi.c b/drivers/clk/at91/clk-utmi.c deleted file mode 100644 index 18af0bfeaa..0000000000 --- a/drivers/clk/at91/clk-utmi.c +++ /dev/null @@ -1,142 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0+ -/* - * Copyright (C) 2016 Atmel Corporation - * Wenyou.Yang - */ - -#include -#include -#include -#include -#include -#include -#include -#include "pmc.h" - -/* - * The purpose of this clock is to generate a 480 MHz signal. A different - * rate can't be configured. - */ -#define UTMI_RATE 480000000 - -static int utmi_clk_enable(struct clk *clk) -{ - struct pmc_platdata *plat = dev_get_platdata(clk->dev); - struct at91_pmc *pmc = plat->reg_base; - struct clk clk_dev; - ulong clk_rate; - u32 utmi_ref_clk_freq; - u32 tmp; - int err; - int timeout = 2000000; - - if (readl(&pmc->sr) & AT91_PMC_LOCKU) - return 0; - - /* - * If mainck rate is different from 12 MHz, we have to configure the - * FREQ field of the SFR_UTMICKTRIM register to generate properly - * the utmi clock. - */ - err = clk_get_by_index(clk->dev, 0, &clk_dev); - if (err) - return -EINVAL; - - clk_rate = clk_get_rate(&clk_dev); - switch (clk_rate) { - case 12000000: - utmi_ref_clk_freq = 0; - break; - case 16000000: - utmi_ref_clk_freq = 1; - break; - case 24000000: - utmi_ref_clk_freq = 2; - break; - /* - * Not supported on SAMA5D2 but it's not an issue since MAINCK - * maximum value is 24 MHz. - */ - case 48000000: - utmi_ref_clk_freq = 3; - break; - default: - printf("UTMICK: unsupported mainck rate\n"); - return -EINVAL; - } - - if (plat->regmap_sfr) { - err = regmap_read(plat->regmap_sfr, AT91_SFR_UTMICKTRIM, &tmp); - if (err) - return -EINVAL; - - tmp &= ~AT91_UTMICKTRIM_FREQ; - tmp |= utmi_ref_clk_freq; - err = regmap_write(plat->regmap_sfr, AT91_SFR_UTMICKTRIM, tmp); - if (err) - return -EINVAL; - } else if (utmi_ref_clk_freq) { - printf("UTMICK: sfr node required\n"); - return -EINVAL; - } - - tmp = readl(&pmc->uckr); - tmp |= AT91_PMC_UPLLEN | - AT91_PMC_UPLLCOUNT | - AT91_PMC_BIASEN; - writel(tmp, &pmc->uckr); - - while ((--timeout) && !(readl(&pmc->sr) & AT91_PMC_LOCKU)) - ; - if (!timeout) { - printf("UTMICK: timeout waiting for UPLL lock\n"); - return -ETIMEDOUT; - } - - return 0; -} - -static ulong utmi_clk_get_rate(struct clk *clk) -{ - /* UTMI clk rate is fixed. */ - return UTMI_RATE; -} - -static struct clk_ops utmi_clk_ops = { - .enable = utmi_clk_enable, - .get_rate = utmi_clk_get_rate, -}; - -static int utmi_clk_ofdata_to_platdata(struct udevice *dev) -{ - struct pmc_platdata *plat = dev_get_platdata(dev); - struct udevice *syscon; - - uclass_get_device_by_phandle(UCLASS_SYSCON, dev, - "regmap-sfr", &syscon); - - if (syscon) - plat->regmap_sfr = syscon_get_regmap(syscon); - - return 0; -} - -static int utmi_clk_probe(struct udevice *dev) -{ - return at91_pmc_core_probe(dev); -} - -static const struct udevice_id utmi_clk_match[] = { - { .compatible = "atmel,at91sam9x5-clk-utmi" }, - {} -}; - -U_BOOT_DRIVER(at91sam9x5_utmi_clk) = { - .name = "at91sam9x5-utmi-clk", - .id = UCLASS_CLK, - .of_match = utmi_clk_match, - .probe = utmi_clk_probe, - .ofdata_to_platdata = utmi_clk_ofdata_to_platdata, - .platdata_auto_alloc_size = sizeof(struct pmc_platdata), - .ops = &utmi_clk_ops, -}; diff --git a/drivers/clk/at91/compat.c b/drivers/clk/at91/compat.c new file mode 100644 index 0000000000..8cf6254046 --- /dev/null +++ b/drivers/clk/at91/compat.c @@ -0,0 +1,1023 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Compatible code for non CCF AT91 platforms. + * + * Copyright (C) 2020 Microchip Technology Inc. and its subsidiaries + * + * Author: Claudiu Beznea + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "pmc.h" + +DECLARE_GLOBAL_DATA_PTR; + +struct pmc_platdata { + struct at91_pmc *reg_base; + struct regmap *regmap_sfr; +}; + +static const struct udevice_id at91_pmc_match[] = { + { .compatible = "atmel,at91rm9200-pmc" }, + { .compatible = "atmel,at91sam9260-pmc" }, + { .compatible = "atmel,at91sam9g45-pmc" }, + { .compatible = "atmel,at91sam9n12-pmc" }, + { .compatible = "atmel,at91sam9x5-pmc" }, + { .compatible = "atmel,sama5d3-pmc" }, + { .compatible = "atmel,sama5d2-pmc" }, + {} +}; + +U_BOOT_DRIVER(at91_pmc) = { + .name = "at91-pmc", + .id = UCLASS_SIMPLE_BUS, + .of_match = at91_pmc_match, +}; + +static int at91_pmc_core_probe(struct udevice *dev) +{ + struct pmc_platdata *plat = dev_get_platdata(dev); + + dev = dev_get_parent(dev); + + plat->reg_base = dev_read_addr_ptr(dev); + + return 0; +} + +/** + * at91_clk_sub_device_bind() - for the at91 clock driver + * Recursively bind its children as clk devices. + * + * @return: 0 on success, or negative error code on failure + */ +int at91_clk_sub_device_bind(struct udevice *dev, const char *drv_name) +{ + const void *fdt = gd->fdt_blob; + int offset = dev_of_offset(dev); + bool pre_reloc_only = !(gd->flags & GD_FLG_RELOC); + const char *name; + int ret; + + for (offset = fdt_first_subnode(fdt, offset); + offset > 0; + offset = fdt_next_subnode(fdt, offset)) { + if (pre_reloc_only && + !ofnode_pre_reloc(offset_to_ofnode(offset))) + continue; + /* + * If this node has "compatible" property, this is not + * a clock sub-node, but a normal device. skip. + */ + fdt_get_property(fdt, offset, "compatible", &ret); + if (ret >= 0) + continue; + + if (ret != -FDT_ERR_NOTFOUND) + return ret; + + name = fdt_get_name(fdt, offset, NULL); + if (!name) + return -EINVAL; + ret = device_bind_driver_to_node(dev, drv_name, name, + offset_to_ofnode(offset), NULL); + if (ret) + return ret; + } + + return 0; +} + +int at91_clk_of_xlate(struct clk *clk, struct ofnode_phandle_args *args) +{ + int periph; + + if (args->args_count) { + debug("Invalid args_count: %d\n", args->args_count); + return -EINVAL; + } + + periph = fdtdec_get_uint(gd->fdt_blob, dev_of_offset(clk->dev), "reg", + -1); + if (periph < 0) + return -EINVAL; + + clk->id = periph; + + return 0; +} + +int at91_clk_probe(struct udevice *dev) +{ + struct udevice *dev_periph_container, *dev_pmc; + struct pmc_platdata *plat = dev_get_platdata(dev); + + dev_periph_container = dev_get_parent(dev); + dev_pmc = dev_get_parent(dev_periph_container); + + plat->reg_base = dev_read_addr_ptr(dev_pmc); + + return 0; +} + +/* SCKC specific code. */ +static const struct udevice_id at91_sckc_match[] = { + { .compatible = "atmel,at91sam9x5-sckc" }, + {} +}; + +U_BOOT_DRIVER(at91_sckc) = { + .name = "at91-sckc", + .id = UCLASS_SIMPLE_BUS, + .of_match = at91_sckc_match, +}; + +/* Slow clock specific code. */ +static int at91_slow_clk_enable(struct clk *clk) +{ + return 0; +} + +static ulong at91_slow_clk_get_rate(struct clk *clk) +{ + return CONFIG_SYS_AT91_SLOW_CLOCK; +} + +static struct clk_ops at91_slow_clk_ops = { + .enable = at91_slow_clk_enable, + .get_rate = at91_slow_clk_get_rate, +}; + +static const struct udevice_id at91_slow_clk_match[] = { + { .compatible = "atmel,at91sam9x5-clk-slow" }, + {} +}; + +U_BOOT_DRIVER(at91_slow_clk) = { + .name = "at91-slow-clk", + .id = UCLASS_CLK, + .of_match = at91_slow_clk_match, + .ops = &at91_slow_clk_ops, +}; + +/* Master clock specific code. */ +static ulong at91_master_clk_get_rate(struct clk *clk) +{ + return gd->arch.mck_rate_hz; +} + +static struct clk_ops at91_master_clk_ops = { + .get_rate = at91_master_clk_get_rate, +}; + +static const struct udevice_id at91_master_clk_match[] = { + { .compatible = "atmel,at91rm9200-clk-master" }, + { .compatible = "atmel,at91sam9x5-clk-master" }, + {} +}; + +U_BOOT_DRIVER(at91_master_clk) = { + .name = "at91-master-clk", + .id = UCLASS_CLK, + .of_match = at91_master_clk_match, + .ops = &at91_master_clk_ops, +}; + +/* Main osc clock specific code. */ +static int main_osc_clk_enable(struct clk *clk) +{ + struct pmc_platdata *plat = dev_get_platdata(clk->dev); + struct at91_pmc *pmc = plat->reg_base; + + if (readl(&pmc->sr) & AT91_PMC_MOSCSELS) + return 0; + + return -EINVAL; +} + +static ulong main_osc_clk_get_rate(struct clk *clk) +{ + return gd->arch.main_clk_rate_hz; +} + +static struct clk_ops main_osc_clk_ops = { + .enable = main_osc_clk_enable, + .get_rate = main_osc_clk_get_rate, +}; + +static int main_osc_clk_probe(struct udevice *dev) +{ + return at91_pmc_core_probe(dev); +} + +static const struct udevice_id main_osc_clk_match[] = { + { .compatible = "atmel,at91sam9x5-clk-main" }, + {} +}; + +U_BOOT_DRIVER(at91sam9x5_main_osc_clk) = { + .name = "at91sam9x5-main-osc-clk", + .id = UCLASS_CLK, + .of_match = main_osc_clk_match, + .probe = main_osc_clk_probe, + .platdata_auto_alloc_size = sizeof(struct pmc_platdata), + .ops = &main_osc_clk_ops, +}; + +/* PLLA clock specific code. */ +static int plla_clk_enable(struct clk *clk) +{ + struct pmc_platdata *plat = dev_get_platdata(clk->dev); + struct at91_pmc *pmc = plat->reg_base; + + if (readl(&pmc->sr) & AT91_PMC_LOCKA) + return 0; + + return -EINVAL; +} + +static ulong plla_clk_get_rate(struct clk *clk) +{ + return gd->arch.plla_rate_hz; +} + +static struct clk_ops plla_clk_ops = { + .enable = plla_clk_enable, + .get_rate = plla_clk_get_rate, +}; + +static int plla_clk_probe(struct udevice *dev) +{ + return at91_pmc_core_probe(dev); +} + +static const struct udevice_id plla_clk_match[] = { + { .compatible = "atmel,sama5d3-clk-pll" }, + {} +}; + +U_BOOT_DRIVER(at91_plla_clk) = { + .name = "at91-plla-clk", + .id = UCLASS_CLK, + .of_match = plla_clk_match, + .probe = plla_clk_probe, + .platdata_auto_alloc_size = sizeof(struct pmc_platdata), + .ops = &plla_clk_ops, +}; + +/* PLLA DIV clock specific code. */ +static int at91_plladiv_clk_enable(struct clk *clk) +{ + return 0; +} + +static ulong at91_plladiv_clk_get_rate(struct clk *clk) +{ + struct pmc_platdata *plat = dev_get_platdata(clk->dev); + struct at91_pmc *pmc = plat->reg_base; + struct clk source; + ulong clk_rate; + int ret; + + ret = clk_get_by_index(clk->dev, 0, &source); + if (ret) + return -EINVAL; + + clk_rate = clk_get_rate(&source); + if (readl(&pmc->mckr) & AT91_PMC_MCKR_PLLADIV_2) + clk_rate /= 2; + + return clk_rate; +} + +static ulong at91_plladiv_clk_set_rate(struct clk *clk, ulong rate) +{ + struct pmc_platdata *plat = dev_get_platdata(clk->dev); + struct at91_pmc *pmc = plat->reg_base; + struct clk source; + ulong parent_rate; + int ret; + + ret = clk_get_by_index(clk->dev, 0, &source); + if (ret) + return -EINVAL; + + parent_rate = clk_get_rate(&source); + if ((parent_rate != rate) && ((parent_rate) / 2 != rate)) + return -EINVAL; + + if (parent_rate != rate) { + writel((readl(&pmc->mckr) | AT91_PMC_MCKR_PLLADIV_2), + &pmc->mckr); + } + + return 0; +} + +static struct clk_ops at91_plladiv_clk_ops = { + .enable = at91_plladiv_clk_enable, + .get_rate = at91_plladiv_clk_get_rate, + .set_rate = at91_plladiv_clk_set_rate, +}; + +static int at91_plladiv_clk_probe(struct udevice *dev) +{ + return at91_pmc_core_probe(dev); +} + +static const struct udevice_id at91_plladiv_clk_match[] = { + { .compatible = "atmel,at91sam9x5-clk-plldiv" }, + {} +}; + +U_BOOT_DRIVER(at91_plladiv_clk) = { + .name = "at91-plladiv-clk", + .id = UCLASS_CLK, + .of_match = at91_plladiv_clk_match, + .probe = at91_plladiv_clk_probe, + .platdata_auto_alloc_size = sizeof(struct pmc_platdata), + .ops = &at91_plladiv_clk_ops, +}; + +/* System clock specific code. */ +#define SYSTEM_MAX_ID 31 + +/** + * at91_system_clk_bind() - for the system clock driver + * Recursively bind its children as clk devices. + * + * @return: 0 on success, or negative error code on failure + */ +static int at91_system_clk_bind(struct udevice *dev) +{ + return at91_clk_sub_device_bind(dev, "system-clk"); +} + +static const struct udevice_id at91_system_clk_match[] = { + { .compatible = "atmel,at91rm9200-clk-system" }, + {} +}; + +U_BOOT_DRIVER(at91_system_clk) = { + .name = "at91-system-clk", + .id = UCLASS_MISC, + .of_match = at91_system_clk_match, + .bind = at91_system_clk_bind, +}; + +static inline int is_pck(int id) +{ + return (id >= 8) && (id <= 15); +} + +static ulong system_clk_get_rate(struct clk *clk) +{ + struct clk clk_dev; + int ret; + + ret = clk_get_by_index(clk->dev, 0, &clk_dev); + if (ret) + return -EINVAL; + + return clk_get_rate(&clk_dev); +} + +static ulong system_clk_set_rate(struct clk *clk, ulong rate) +{ + struct clk clk_dev; + int ret; + + ret = clk_get_by_index(clk->dev, 0, &clk_dev); + if (ret) + return -EINVAL; + + return clk_set_rate(&clk_dev, rate); +} + +static int system_clk_enable(struct clk *clk) +{ + struct pmc_platdata *plat = dev_get_platdata(clk->dev); + struct at91_pmc *pmc = plat->reg_base; + u32 mask; + + if (clk->id > SYSTEM_MAX_ID) + return -EINVAL; + + mask = BIT(clk->id); + + writel(mask, &pmc->scer); + + /** + * For the programmable clocks the Ready status in the PMC + * status register should be checked after enabling. + * For other clocks this is unnecessary. + */ + if (!is_pck(clk->id)) + return 0; + + while (!(readl(&pmc->sr) & mask)) + ; + + return 0; +} + +static struct clk_ops system_clk_ops = { + .of_xlate = at91_clk_of_xlate, + .get_rate = system_clk_get_rate, + .set_rate = system_clk_set_rate, + .enable = system_clk_enable, +}; + +U_BOOT_DRIVER(system_clk) = { + .name = "system-clk", + .id = UCLASS_CLK, + .probe = at91_clk_probe, + .platdata_auto_alloc_size = sizeof(struct pmc_platdata), + .ops = &system_clk_ops, +}; + +/* Peripheral clock specific code. */ +#define PERIPHERAL_ID_MIN 2 +#define PERIPHERAL_ID_MAX 31 +#define PERIPHERAL_MASK(id) (1 << ((id) & PERIPHERAL_ID_MAX)) + +enum periph_clk_type { + CLK_PERIPH_AT91RM9200 = 0, + CLK_PERIPH_AT91SAM9X5, +}; + +/** + * sam9x5_periph_clk_bind() - for the periph clock driver + * Recursively bind its children as clk devices. + * + * @return: 0 on success, or negative error code on failure + */ +static int sam9x5_periph_clk_bind(struct udevice *dev) +{ + return at91_clk_sub_device_bind(dev, "periph-clk"); +} + +static const struct udevice_id sam9x5_periph_clk_match[] = { + { + .compatible = "atmel,at91rm9200-clk-peripheral", + .data = CLK_PERIPH_AT91RM9200, + }, + { + .compatible = "atmel,at91sam9x5-clk-peripheral", + .data = CLK_PERIPH_AT91SAM9X5, + }, + {} +}; + +U_BOOT_DRIVER(sam9x5_periph_clk) = { + .name = "sam9x5-periph-clk", + .id = UCLASS_MISC, + .of_match = sam9x5_periph_clk_match, + .bind = sam9x5_periph_clk_bind, +}; + +static int periph_clk_enable(struct clk *clk) +{ + struct pmc_platdata *plat = dev_get_platdata(clk->dev); + struct at91_pmc *pmc = plat->reg_base; + enum periph_clk_type clk_type; + void *addr; + + if (clk->id < PERIPHERAL_ID_MIN) + return -1; + + clk_type = dev_get_driver_data(dev_get_parent(clk->dev)); + if (clk_type == CLK_PERIPH_AT91RM9200) { + addr = &pmc->pcer; + if (clk->id > PERIPHERAL_ID_MAX) + addr = &pmc->pcer1; + + setbits_le32(addr, PERIPHERAL_MASK(clk->id)); + } else { + writel(clk->id & AT91_PMC_PCR_PID_MASK, &pmc->pcr); + setbits_le32(&pmc->pcr, + AT91_PMC_PCR_CMD_WRITE | AT91_PMC_PCR_EN); + } + + return 0; +} + +static ulong periph_get_rate(struct clk *clk) +{ + struct udevice *dev; + struct clk clk_dev; + ulong clk_rate; + int ret; + + dev = dev_get_parent(clk->dev); + + ret = clk_get_by_index(dev, 0, &clk_dev); + if (ret) + return ret; + + clk_rate = clk_get_rate(&clk_dev); + + clk_free(&clk_dev); + + return clk_rate; +} + +static struct clk_ops periph_clk_ops = { + .of_xlate = at91_clk_of_xlate, + .enable = periph_clk_enable, + .get_rate = periph_get_rate, +}; + +U_BOOT_DRIVER(clk_periph) = { + .name = "periph-clk", + .id = UCLASS_CLK, + .platdata_auto_alloc_size = sizeof(struct pmc_platdata), + .probe = at91_clk_probe, + .ops = &periph_clk_ops, +}; + +/* UTMI clock specific code. */ +#ifdef CONFIG_AT91_UTMI + +/* + * The purpose of this clock is to generate a 480 MHz signal. A different + * rate can't be configured. + */ +#define UTMI_RATE 480000000 + +static int utmi_clk_enable(struct clk *clk) +{ + struct pmc_platdata *plat = dev_get_platdata(clk->dev); + struct at91_pmc *pmc = plat->reg_base; + struct clk clk_dev; + ulong clk_rate; + u32 utmi_ref_clk_freq; + u32 tmp; + int err; + int timeout = 2000000; + + if (readl(&pmc->sr) & AT91_PMC_LOCKU) + return 0; + + /* + * If mainck rate is different from 12 MHz, we have to configure the + * FREQ field of the SFR_UTMICKTRIM register to generate properly + * the utmi clock. + */ + err = clk_get_by_index(clk->dev, 0, &clk_dev); + if (err) + return -EINVAL; + + clk_rate = clk_get_rate(&clk_dev); + switch (clk_rate) { + case 12000000: + utmi_ref_clk_freq = 0; + break; + case 16000000: + utmi_ref_clk_freq = 1; + break; + case 24000000: + utmi_ref_clk_freq = 2; + break; + /* + * Not supported on SAMA5D2 but it's not an issue since MAINCK + * maximum value is 24 MHz. + */ + case 48000000: + utmi_ref_clk_freq = 3; + break; + default: + printf("UTMICK: unsupported mainck rate\n"); + return -EINVAL; + } + + if (plat->regmap_sfr) { + err = regmap_read(plat->regmap_sfr, AT91_SFR_UTMICKTRIM, &tmp); + if (err) + return -EINVAL; + + tmp &= ~AT91_UTMICKTRIM_FREQ; + tmp |= utmi_ref_clk_freq; + err = regmap_write(plat->regmap_sfr, AT91_SFR_UTMICKTRIM, tmp); + if (err) + return -EINVAL; + } else if (utmi_ref_clk_freq) { + printf("UTMICK: sfr node required\n"); + return -EINVAL; + } + + tmp = readl(&pmc->uckr); + tmp |= AT91_PMC_UPLLEN | + AT91_PMC_UPLLCOUNT | + AT91_PMC_BIASEN; + writel(tmp, &pmc->uckr); + + while ((--timeout) && !(readl(&pmc->sr) & AT91_PMC_LOCKU)) + ; + if (!timeout) { + printf("UTMICK: timeout waiting for UPLL lock\n"); + return -ETIMEDOUT; + } + + return 0; +} + +static ulong utmi_clk_get_rate(struct clk *clk) +{ + /* UTMI clk rate is fixed. */ + return UTMI_RATE; +} + +static struct clk_ops utmi_clk_ops = { + .enable = utmi_clk_enable, + .get_rate = utmi_clk_get_rate, +}; + +static int utmi_clk_ofdata_to_platdata(struct udevice *dev) +{ + struct pmc_platdata *plat = dev_get_platdata(dev); + struct udevice *syscon; + + uclass_get_device_by_phandle(UCLASS_SYSCON, dev, + "regmap-sfr", &syscon); + + if (syscon) + plat->regmap_sfr = syscon_get_regmap(syscon); + + return 0; +} + +static int utmi_clk_probe(struct udevice *dev) +{ + return at91_pmc_core_probe(dev); +} + +static const struct udevice_id utmi_clk_match[] = { + { .compatible = "atmel,at91sam9x5-clk-utmi" }, + {} +}; + +U_BOOT_DRIVER(at91sam9x5_utmi_clk) = { + .name = "at91sam9x5-utmi-clk", + .id = UCLASS_CLK, + .of_match = utmi_clk_match, + .probe = utmi_clk_probe, + .ofdata_to_platdata = utmi_clk_ofdata_to_platdata, + .platdata_auto_alloc_size = sizeof(struct pmc_platdata), + .ops = &utmi_clk_ops, +}; + +#endif /* CONFIG_AT91_UTMI */ + +/* H32MX clock specific code. */ +#ifdef CONFIG_AT91_H32MX + +#define H32MX_MAX_FREQ 90000000 + +static ulong sama5d4_h32mx_clk_get_rate(struct clk *clk) +{ + struct pmc_platdata *plat = dev_get_platdata(clk->dev); + struct at91_pmc *pmc = plat->reg_base; + ulong rate = gd->arch.mck_rate_hz; + + if (readl(&pmc->mckr) & AT91_PMC_MCKR_H32MXDIV) + rate /= 2; + + if (rate > H32MX_MAX_FREQ) + dev_dbg(clk->dev, "H32MX clock is too fast\n"); + + return rate; +} + +static struct clk_ops sama5d4_h32mx_clk_ops = { + .get_rate = sama5d4_h32mx_clk_get_rate, +}; + +static int sama5d4_h32mx_clk_probe(struct udevice *dev) +{ + return at91_pmc_core_probe(dev); +} + +static const struct udevice_id sama5d4_h32mx_clk_match[] = { + { .compatible = "atmel,sama5d4-clk-h32mx" }, + {} +}; + +U_BOOT_DRIVER(sama5d4_h32mx_clk) = { + .name = "sama5d4-h32mx-clk", + .id = UCLASS_CLK, + .of_match = sama5d4_h32mx_clk_match, + .probe = sama5d4_h32mx_clk_probe, + .platdata_auto_alloc_size = sizeof(struct pmc_platdata), + .ops = &sama5d4_h32mx_clk_ops, +}; + +#endif /* CONFIG_AT91_H32MX */ + +/* Generic clock specific code. */ +#ifdef CONFIG_AT91_GENERIC_CLK + +#define GENERATED_SOURCE_MAX 6 +#define GENERATED_MAX_DIV 255 + +/** + * generated_clk_bind() - for the generated clock driver + * Recursively bind its children as clk devices. + * + * @return: 0 on success, or negative error code on failure + */ +static int generated_clk_bind(struct udevice *dev) +{ + return at91_clk_sub_device_bind(dev, "generic-clk"); +} + +static const struct udevice_id generated_clk_match[] = { + { .compatible = "atmel,sama5d2-clk-generated" }, + {} +}; + +U_BOOT_DRIVER(generated_clk) = { + .name = "generated-clk", + .id = UCLASS_MISC, + .of_match = generated_clk_match, + .bind = generated_clk_bind, +}; + +struct generic_clk_priv { + u32 num_parents; +}; + +static ulong generic_clk_get_rate(struct clk *clk) +{ + struct pmc_platdata *plat = dev_get_platdata(clk->dev); + struct at91_pmc *pmc = plat->reg_base; + struct clk parent; + ulong clk_rate; + u32 tmp, gckdiv; + u8 clock_source, parent_index; + int ret; + + writel(clk->id & AT91_PMC_PCR_PID_MASK, &pmc->pcr); + tmp = readl(&pmc->pcr); + clock_source = (tmp >> AT91_PMC_PCR_GCKCSS_OFFSET) & + AT91_PMC_PCR_GCKCSS_MASK; + gckdiv = (tmp >> AT91_PMC_PCR_GCKDIV_OFFSET) & AT91_PMC_PCR_GCKDIV_MASK; + + parent_index = clock_source - 1; + ret = clk_get_by_index(dev_get_parent(clk->dev), parent_index, &parent); + if (ret) + return 0; + + clk_rate = clk_get_rate(&parent) / (gckdiv + 1); + + clk_free(&parent); + + return clk_rate; +} + +static ulong generic_clk_set_rate(struct clk *clk, ulong rate) +{ + struct pmc_platdata *plat = dev_get_platdata(clk->dev); + struct at91_pmc *pmc = plat->reg_base; + struct generic_clk_priv *priv = dev_get_priv(clk->dev); + struct clk parent, best_parent; + ulong tmp_rate, best_rate = rate, parent_rate; + int tmp_diff, best_diff = -1; + u32 div, best_div = 0; + u8 best_parent_index, best_clock_source = 0; + u8 i; + u32 tmp; + int ret; + + for (i = 0; i < priv->num_parents; i++) { + ret = clk_get_by_index(dev_get_parent(clk->dev), i, &parent); + if (ret) + return ret; + + parent_rate = clk_get_rate(&parent); + if (IS_ERR_VALUE(parent_rate)) + return parent_rate; + + for (div = 1; div < GENERATED_MAX_DIV + 2; div++) { + tmp_rate = DIV_ROUND_CLOSEST(parent_rate, div); + tmp_diff = abs(rate - tmp_rate); + + if (best_diff < 0 || best_diff > tmp_diff) { + best_rate = tmp_rate; + best_diff = tmp_diff; + + best_div = div - 1; + best_parent = parent; + best_parent_index = i; + best_clock_source = best_parent_index + 1; + } + + if (!best_diff || tmp_rate < rate) + break; + } + + if (!best_diff) + break; + } + + debug("GCK: best parent: %s, best_rate = %ld, best_div = %d\n", + best_parent.dev->name, best_rate, best_div); + + ret = clk_enable(&best_parent); + if (ret) + return ret; + + writel(clk->id & AT91_PMC_PCR_PID_MASK, &pmc->pcr); + tmp = readl(&pmc->pcr); + tmp &= ~(AT91_PMC_PCR_GCKDIV | AT91_PMC_PCR_GCKCSS); + tmp |= AT91_PMC_PCR_GCKCSS_(best_clock_source) | + AT91_PMC_PCR_CMD_WRITE | + AT91_PMC_PCR_GCKDIV_(best_div) | + AT91_PMC_PCR_GCKEN; + writel(tmp, &pmc->pcr); + + while (!(readl(&pmc->sr) & AT91_PMC_GCKRDY)) + ; + + return 0; +} + +static struct clk_ops generic_clk_ops = { + .of_xlate = at91_clk_of_xlate, + .get_rate = generic_clk_get_rate, + .set_rate = generic_clk_set_rate, +}; + +static int generic_clk_ofdata_to_platdata(struct udevice *dev) +{ + struct generic_clk_priv *priv = dev_get_priv(dev); + u32 cells[GENERATED_SOURCE_MAX]; + u32 num_parents; + + num_parents = fdtdec_get_int_array_count(gd->fdt_blob, + dev_of_offset(dev_get_parent(dev)), "clocks", cells, + GENERATED_SOURCE_MAX); + + if (!num_parents) + return -1; + + priv->num_parents = num_parents; + + return 0; +} + +U_BOOT_DRIVER(generic_clk) = { + .name = "generic-clk", + .id = UCLASS_CLK, + .probe = at91_clk_probe, + .ofdata_to_platdata = generic_clk_ofdata_to_platdata, + .priv_auto_alloc_size = sizeof(struct generic_clk_priv), + .platdata_auto_alloc_size = sizeof(struct pmc_platdata), + .ops = &generic_clk_ops, +}; + +#endif /* CONFIG_AT91_GENERIC_CLK */ + +/* USB clock specific code. */ +#ifdef CONFIG_AT91_USB_CLK + +#define AT91_USB_CLK_SOURCE_MAX 2 +#define AT91_USB_CLK_MAX_DIV 15 + +struct at91_usb_clk_priv { + u32 num_clksource; +}; + +static ulong at91_usb_clk_get_rate(struct clk *clk) +{ + struct pmc_platdata *plat = dev_get_platdata(clk->dev); + struct at91_pmc *pmc = plat->reg_base; + struct clk source; + u32 tmp, usbdiv; + u8 source_index; + int ret; + + tmp = readl(&pmc->pcr); + source_index = (tmp >> AT91_PMC_USB_USBS_OFFSET) & + AT91_PMC_USB_USBS_MASK; + usbdiv = (tmp >> AT91_PMC_USB_DIV_OFFSET) & AT91_PMC_USB_DIV_MASK; + + ret = clk_get_by_index(clk->dev, source_index, &source); + if (ret) + return 0; + + return clk_get_rate(&source) / (usbdiv + 1); +} + +static ulong at91_usb_clk_set_rate(struct clk *clk, ulong rate) +{ + struct pmc_platdata *plat = dev_get_platdata(clk->dev); + struct at91_pmc *pmc = plat->reg_base; + struct at91_usb_clk_priv *priv = dev_get_priv(clk->dev); + struct clk source, best_source; + ulong tmp_rate, best_rate = rate, source_rate; + int tmp_diff, best_diff = -1; + u32 div, best_div = 0; + u8 best_source_index = 0; + u8 i; + u32 tmp; + int ret; + + for (i = 0; i < priv->num_clksource; i++) { + ret = clk_get_by_index(clk->dev, i, &source); + if (ret) + return ret; + + source_rate = clk_get_rate(&source); + if (IS_ERR_VALUE(source_rate)) + return source_rate; + + for (div = 1; div < AT91_USB_CLK_MAX_DIV + 2; div++) { + tmp_rate = DIV_ROUND_CLOSEST(source_rate, div); + tmp_diff = abs(rate - tmp_rate); + + if (best_diff < 0 || best_diff > tmp_diff) { + best_rate = tmp_rate; + best_diff = tmp_diff; + + best_div = div - 1; + best_source = source; + best_source_index = i; + } + + if (!best_diff || tmp_rate < rate) + break; + } + + if (!best_diff) + break; + } + + debug("AT91 USB: best sourc: %s, best_rate = %ld, best_div = %d\n", + best_source.dev->name, best_rate, best_div); + + ret = clk_enable(&best_source); + if (ret) + return ret; + + tmp = AT91_PMC_USB_USBS_(best_source_index) | + AT91_PMC_USB_DIV_(best_div); + writel(tmp, &pmc->usb); + + return 0; +} + +static struct clk_ops at91_usb_clk_ops = { + .get_rate = at91_usb_clk_get_rate, + .set_rate = at91_usb_clk_set_rate, +}; + +static int at91_usb_clk_ofdata_to_platdata(struct udevice *dev) +{ + struct at91_usb_clk_priv *priv = dev_get_priv(dev); + u32 cells[AT91_USB_CLK_SOURCE_MAX]; + u32 num_clksource; + + num_clksource = fdtdec_get_int_array_count(gd->fdt_blob, + dev_of_offset(dev), + "clocks", cells, + AT91_USB_CLK_SOURCE_MAX); + + if (!num_clksource) + return -1; + + priv->num_clksource = num_clksource; + + return 0; +} + +static int at91_usb_clk_probe(struct udevice *dev) +{ + return at91_pmc_core_probe(dev); +} + +static const struct udevice_id at91_usb_clk_match[] = { + { .compatible = "atmel,at91sam9x5-clk-usb" }, + {} +}; + +U_BOOT_DRIVER(at91_usb_clk) = { + .name = "at91-usb-clk", + .id = UCLASS_CLK, + .of_match = at91_usb_clk_match, + .probe = at91_usb_clk_probe, + .ofdata_to_platdata = at91_usb_clk_ofdata_to_platdata, + .priv_auto_alloc_size = sizeof(struct at91_usb_clk_priv), + .platdata_auto_alloc_size = sizeof(struct pmc_platdata), + .ops = &at91_usb_clk_ops, +}; + +#endif /* CONFIG_AT91_USB_CLK */ diff --git a/drivers/clk/at91/pmc.c b/drivers/clk/at91/pmc.c index e403baea0e..29c6452497 100644 --- a/drivers/clk/at91/pmc.c +++ b/drivers/clk/at91/pmc.c @@ -4,122 +4,8 @@ * Wenyou.Yang */ +#include #include -#include -#include -#include -#include -#include -#include "pmc.h" - -DECLARE_GLOBAL_DATA_PTR; - -static const struct udevice_id at91_pmc_match[] = { - { .compatible = "atmel,at91rm9200-pmc" }, - { .compatible = "atmel,at91sam9260-pmc" }, - { .compatible = "atmel,at91sam9g45-pmc" }, - { .compatible = "atmel,at91sam9n12-pmc" }, - { .compatible = "atmel,at91sam9x5-pmc" }, - { .compatible = "atmel,sama5d3-pmc" }, - { .compatible = "atmel,sama5d2-pmc" }, - {} -}; - -U_BOOT_DRIVER(atmel_at91rm9200_pmc) = { - .name = "atmel_at91rm9200_pmc", - .id = UCLASS_SIMPLE_BUS, - .of_match = at91_pmc_match, -}; - -U_BOOT_DRIVER_ALIAS(atmel_at91rm9200_pmc, atmel_at91sam9260_pmc) - -/*---------------------------------------------------------*/ - -int at91_pmc_core_probe(struct udevice *dev) -{ - struct pmc_platdata *plat = dev_get_platdata(dev); - - dev = dev_get_parent(dev); - - plat->reg_base = dev_read_addr_ptr(dev); - - return 0; -} - -/** - * at91_clk_sub_device_bind() - for the at91 clock driver - * Recursively bind its children as clk devices. - * - * @return: 0 on success, or negative error code on failure - */ -int at91_clk_sub_device_bind(struct udevice *dev, const char *drv_name) -{ - const void *fdt = gd->fdt_blob; - int offset = dev_of_offset(dev); - bool pre_reloc_only = !(gd->flags & GD_FLG_RELOC); - const char *name; - int ret; - - for (offset = fdt_first_subnode(fdt, offset); - offset > 0; - offset = fdt_next_subnode(fdt, offset)) { - if (pre_reloc_only && - !ofnode_pre_reloc(offset_to_ofnode(offset))) - continue; - /* - * If this node has "compatible" property, this is not - * a clock sub-node, but a normal device. skip. - */ - fdt_get_property(fdt, offset, "compatible", &ret); - if (ret >= 0) - continue; - - if (ret != -FDT_ERR_NOTFOUND) - return ret; - - name = fdt_get_name(fdt, offset, NULL); - if (!name) - return -EINVAL; - ret = device_bind_driver_to_node(dev, drv_name, name, - offset_to_ofnode(offset), NULL); - if (ret) - return ret; - } - - return 0; -} - -int at91_clk_of_xlate(struct clk *clk, struct ofnode_phandle_args *args) -{ - int periph; - - if (args->args_count) { - debug("Invalid args_count: %d\n", args->args_count); - return -EINVAL; - } - - periph = fdtdec_get_uint(gd->fdt_blob, dev_of_offset(clk->dev), "reg", - -1); - if (periph < 0) - return -EINVAL; - - clk->id = periph; - - return 0; -} - -int at91_clk_probe(struct udevice *dev) -{ - struct udevice *dev_periph_container, *dev_pmc; - struct pmc_platdata *plat = dev_get_platdata(dev); - - dev_periph_container = dev_get_parent(dev); - dev_pmc = dev_get_parent(dev_periph_container); - - plat->reg_base = dev_read_addr_ptr(dev_pmc); - - return 0; -} /** * pmc_read() - read content at address base + off into val diff --git a/drivers/clk/at91/pmc.h b/drivers/clk/at91/pmc.h index b1ab0a95c8..33ded02156 100644 --- a/drivers/clk/at91/pmc.h +++ b/drivers/clk/at91/pmc.h @@ -7,7 +7,6 @@ #ifndef __AT91_PMC_H__ #define __AT91_PMC_H__ -#include #include #include @@ -15,17 +14,6 @@ #define AT91_TO_CLK_ID(_t, _i) (((_t) << 8) | ((_i) & 0xff)) #define AT91_CLK_ID_TO_DID(_i) ((_i) & 0xff) -struct pmc_platdata { - struct at91_pmc *reg_base; - struct regmap *regmap_sfr; -}; - -int at91_pmc_core_probe(struct udevice *dev); -int at91_clk_sub_device_bind(struct udevice *dev, const char *drv_name); - -int at91_clk_of_xlate(struct clk *clk, struct ofnode_phandle_args *args); -int at91_clk_probe(struct udevice *dev); - int at91_clk_mux_val_to_index(const u32 *table, u32 num_parents, u32 val); int at91_clk_mux_index_to_val(const u32 *table, u32 num_parents, u32 index); @@ -33,4 +21,5 @@ void pmc_read(void __iomem *base, unsigned int off, unsigned int *val); void pmc_write(void __iomem *base, unsigned int off, unsigned int val); void pmc_update_bits(void __iomem *base, unsigned int off, unsigned int mask, unsigned int bits); + #endif diff --git a/drivers/clk/at91/sckc.c b/drivers/clk/at91/sckc.c deleted file mode 100644 index a879b008ff..0000000000 --- a/drivers/clk/at91/sckc.c +++ /dev/null @@ -1,19 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0+ -/* - * Copyright (C) 2016 Atmel Corporation - * Wenyou.Yang - */ - -#include -#include - -static const struct udevice_id at91_sckc_match[] = { - { .compatible = "atmel,at91sam9x5-sckc" }, - {} -}; - -U_BOOT_DRIVER(at91_sckc) = { - .name = "at91-sckc", - .id = UCLASS_SIMPLE_BUS, - .of_match = at91_sckc_match, -}; -- cgit v1.2.3 From e9885aa7cc23c21f1fa254c7c8cc459eca67db84 Mon Sep 17 00:00:00 2001 From: Claudiu Beznea Date: Mon, 7 Sep 2020 17:46:40 +0300 Subject: clk: at91: sckc: add driver compatible with ccf Add sckc driver compatible with common clock framework. Driver implements slow clock support for SAM9X60 compatible IPs (in this list it is also present SAMA7G5's slow clock IP). Signed-off-by: Claudiu Beznea --- drivers/clk/at91/Makefile | 2 +- drivers/clk/at91/sckc.c | 172 ++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 173 insertions(+), 1 deletion(-) create mode 100644 drivers/clk/at91/sckc.c (limited to 'drivers/clk') diff --git a/drivers/clk/at91/Makefile b/drivers/clk/at91/Makefile index e2413af403..7083ce2890 100644 --- a/drivers/clk/at91/Makefile +++ b/drivers/clk/at91/Makefile @@ -3,7 +3,7 @@ # ifdef CONFIG_CLK_CCF -obj-y += pmc.o +obj-y += pmc.o sckc.o else obj-y += compat.o endif diff --git a/drivers/clk/at91/sckc.c b/drivers/clk/at91/sckc.c new file mode 100644 index 0000000000..dd62dc5510 --- /dev/null +++ b/drivers/clk/at91/sckc.c @@ -0,0 +1,172 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Slow clock support for AT91 architectures. + * + * Copyright (C) 2020 Microchip Technology Inc. and its subsidiaries + * + * Author: Claudiu Beznea + */ + +#include +#include +#include +#include +#include + +#include "pmc.h" + +#define UBOOT_DM_CLK_AT91_SAM9X60_TD_SLCK "at91-sam9x60-td-slck" +#define UBOOT_DM_CLK_AT91_SCKC "at91-sckc" + +#define AT91_OSC_SEL BIT(24) +#define AT91_OSC_SEL_SHIFT (24) + +struct sam9x60_sckc { + void __iomem *reg; + const char **parent_names; + unsigned int num_parents; + struct clk clk; +}; + +#define to_sam9x60_sckc(c) container_of(c, struct sam9x60_sckc, clk) + +static int sam9x60_sckc_of_xlate(struct clk *clk, + struct ofnode_phandle_args *args) +{ + if (args->args_count != 1) { + debug("AT91: SCKC: Invalid args_count: %d\n", args->args_count); + return -EINVAL; + } + + clk->id = AT91_TO_CLK_ID(PMC_TYPE_SLOW, args->args[0]); + + return 0; +} + +static const struct clk_ops sam9x60_sckc_ops = { + .of_xlate = sam9x60_sckc_of_xlate, + .get_rate = clk_generic_get_rate, +}; + +static int sam9x60_td_slck_set_parent(struct clk *clk, struct clk *parent) +{ + struct sam9x60_sckc *sckc = to_sam9x60_sckc(clk); + u32 i; + + for (i = 0; i < sckc->num_parents; i++) { + if (!strcmp(parent->dev->name, sckc->parent_names[i])) + break; + } + if (i == sckc->num_parents) + return -EINVAL; + + pmc_update_bits(sckc->reg, 0, AT91_OSC_SEL, (i << AT91_OSC_SEL_SHIFT)); + + return 0; +} + +static const struct clk_ops sam9x60_td_slck_ops = { + .get_rate = clk_generic_get_rate, + .set_parent = sam9x60_td_slck_set_parent, +}; + +static struct clk *at91_sam9x60_clk_register_td_slck(struct sam9x60_sckc *sckc, + const char *name, const char * const *parent_names, + int num_parents) +{ + struct clk *clk; + int ret = -ENOMEM; + u32 val, i; + + if (!sckc || !name || !parent_names || num_parents != 2) + return ERR_PTR(-EINVAL); + + sckc->parent_names = kzalloc(sizeof(*sckc->parent_names) * num_parents, + GFP_KERNEL); + if (!sckc->parent_names) + return ERR_PTR(ret); + + for (i = 0; i < num_parents; i++) { + sckc->parent_names[i] = kmemdup(parent_names[i], + strlen(parent_names[i]) + 1, GFP_KERNEL); + if (!sckc->parent_names[i]) + goto free; + } + sckc->num_parents = num_parents; + + pmc_read(sckc->reg, 0, &val); + val = (val & AT91_OSC_SEL) >> AT91_OSC_SEL_SHIFT; + + clk = &sckc->clk; + ret = clk_register(clk, UBOOT_DM_CLK_AT91_SAM9X60_TD_SLCK, name, + parent_names[val]); + if (ret) + goto free; + + return clk; + +free: + for (; i >= 0; i--) + kfree(sckc->parent_names[i]); + kfree(sckc->parent_names); + + return ERR_PTR(ret); +} + +U_BOOT_DRIVER(at91_sam9x60_td_slck) = { + .name = UBOOT_DM_CLK_AT91_SAM9X60_TD_SLCK, + .id = UCLASS_CLK, + .ops = &sam9x60_td_slck_ops, + .flags = DM_FLAG_PRE_RELOC, +}; + +static int at91_sam9x60_sckc_probe(struct udevice *dev) +{ + struct sam9x60_sckc *sckc = dev_get_priv(dev); + void __iomem *base = (void *)devfdt_get_addr(dev); + const char *slow_rc_osc, *slow_osc; + const char *parents[2]; + struct clk *clk, c; + int ret; + + ret = clk_get_by_index(dev, 0, &c); + if (ret) + return ret; + slow_rc_osc = clk_hw_get_name(&c); + + ret = clk_get_by_index(dev, 1, &c); + if (ret) + return ret; + slow_osc = clk_hw_get_name(&c); + + clk = clk_register_fixed_factor(NULL, "md_slck", slow_rc_osc, 0, 1, 1); + if (IS_ERR(clk)) + return PTR_ERR(clk); + clk_dm(AT91_TO_CLK_ID(PMC_TYPE_SLOW, 0), clk); + + parents[0] = slow_rc_osc; + parents[1] = slow_osc; + sckc[1].reg = base; + clk = at91_sam9x60_clk_register_td_slck(&sckc[1], "td_slck", + parents, 2); + if (IS_ERR(clk)) + return PTR_ERR(clk); + clk_dm(AT91_TO_CLK_ID(PMC_TYPE_SLOW, 1), clk); + + return 0; +} + +static const struct udevice_id sam9x60_sckc_ids[] = { + { .compatible = "microchip,sam9x60-sckc" }, + { /* Sentinel. */ }, +}; + +U_BOOT_DRIVER(at91_sckc) = { + .name = UBOOT_DM_CLK_AT91_SCKC, + .id = UCLASS_CLK, + .of_match = sam9x60_sckc_ids, + .priv_auto_alloc_size = sizeof(struct sam9x60_sckc) * 2, + .ops = &sam9x60_sckc_ops, + .probe = at91_sam9x60_sckc_probe, + .flags = DM_FLAG_PRE_RELOC, +}; -- cgit v1.2.3 From f1218f0b4fe95379d54312348c97865cab5ba1cd Mon Sep 17 00:00:00 2001 From: Claudiu Beznea Date: Mon, 7 Sep 2020 17:46:41 +0300 Subject: clk: at91: clk-main: add driver compatible with ccf Add clk-main driver compatible with common clock framework. Signed-off-by: Claudiu Beznea --- drivers/clk/at91/Makefile | 2 +- drivers/clk/at91/clk-main.c | 387 ++++++++++++++++++++++++++++++++++++++++++++ drivers/clk/at91/pmc.h | 10 ++ 3 files changed, 398 insertions(+), 1 deletion(-) create mode 100644 drivers/clk/at91/clk-main.c (limited to 'drivers/clk') diff --git a/drivers/clk/at91/Makefile b/drivers/clk/at91/Makefile index 7083ce2890..805f67677a 100644 --- a/drivers/clk/at91/Makefile +++ b/drivers/clk/at91/Makefile @@ -3,7 +3,7 @@ # ifdef CONFIG_CLK_CCF -obj-y += pmc.o sckc.o +obj-y += pmc.o sckc.o clk-main.o else obj-y += compat.o endif diff --git a/drivers/clk/at91/clk-main.c b/drivers/clk/at91/clk-main.c new file mode 100644 index 0000000000..b52d926f33 --- /dev/null +++ b/drivers/clk/at91/clk-main.c @@ -0,0 +1,387 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Main clock support for AT91 architectures. + * + * Copyright (C) 2020 Microchip Technology Inc. and its subsidiaries + * + * Author: Claudiu Beznea + * + * Based on drivers/clk/at91/clk-main.c from Linux. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include "pmc.h" + +#define UBOOT_DM_CLK_AT91_MAIN_RC "at91-main-rc-clk" +#define UBOOT_DM_CLK_AT91_MAIN_OSC "at91-main-osc-clk" +#define UBOOT_DM_CLK_AT91_RM9200_MAIN "at91-rm9200-main-clk" +#define UBOOT_DM_CLK_AT91_SAM9X5_MAIN "at91-sam9x5-main-clk" + +#define MOR_KEY_MASK GENMASK(23, 16) +#define USEC_PER_SEC 1000000UL +#define SLOW_CLOCK_FREQ 32768 + +#define clk_main_parent_select(s) (((s) & \ + (AT91_PMC_MOSCEN | \ + AT91_PMC_OSCBYPASS)) ? 1 : 0) + +struct clk_main_rc { + void __iomem *reg; + struct clk clk; +}; + +#define to_clk_main_rc(_clk) container_of(_clk, struct clk_main_rc, clk) + +struct clk_main_osc { + void __iomem *reg; + struct clk clk; +}; + +#define to_clk_main_osc(_clk) container_of(_clk, struct clk_main_osc, clk) + +struct clk_main { + void __iomem *reg; + const unsigned int *clk_mux_table; + const char * const *parent_names; + unsigned int num_parents; + int type; + struct clk clk; +}; + +#define to_clk_main(_clk) container_of(_clk, struct clk_main, clk) + +static int main_rc_enable(struct clk *clk) +{ + struct clk_main_rc *main_rc = to_clk_main_rc(clk); + void __iomem *reg = main_rc->reg; + unsigned int val; + + pmc_read(reg, AT91_CKGR_MOR, &val); + + if (!(val & AT91_PMC_MOSCRCEN)) { + pmc_update_bits(reg, AT91_CKGR_MOR, + MOR_KEY_MASK | AT91_PMC_MOSCRCEN, + AT91_PMC_KEY | AT91_PMC_MOSCRCEN); + } + + pmc_read(reg, AT91_PMC_SR, &val); + while (!(val & AT91_PMC_MOSCRCS)) { + pmc_read(reg, AT91_PMC_SR, &val); + debug("waiting for main rc...\n"); + cpu_relax(); + } + + return 0; +} + +static int main_rc_disable(struct clk *clk) +{ + struct clk_main_rc *main_rc = to_clk_main_rc(clk); + struct reg *reg = main_rc->reg; + unsigned int val; + + pmc_read(reg, AT91_CKGR_MOR, &val); + + if (!(val & AT91_PMC_MOSCRCEN)) + return 0; + + pmc_update_bits(reg, AT91_CKGR_MOR, MOR_KEY_MASK | AT91_PMC_MOSCRCEN, + AT91_PMC_KEY); + + return 0; +} + +static const struct clk_ops main_rc_clk_ops = { + .enable = main_rc_enable, + .disable = main_rc_disable, + .get_rate = clk_generic_get_rate, +}; + +struct clk *at91_clk_main_rc(void __iomem *reg, const char *name, + const char *parent_name) +{ + struct clk_main_rc *main_rc; + struct clk *clk; + int ret; + + if (!reg || !name || !parent_name) + return ERR_PTR(-EINVAL); + + main_rc = kzalloc(sizeof(*main_rc), GFP_KERNEL); + if (!main_rc) + return ERR_PTR(-ENOMEM); + + main_rc->reg = reg; + clk = &main_rc->clk; + + ret = clk_register(clk, UBOOT_DM_CLK_AT91_MAIN_RC, name, + parent_name); + if (ret) { + kfree(main_rc); + clk = ERR_PTR(ret); + } + + return clk; +} + +U_BOOT_DRIVER(at91_main_rc_clk) = { + .name = UBOOT_DM_CLK_AT91_MAIN_RC, + .id = UCLASS_CLK, + .ops = &main_rc_clk_ops, + .flags = DM_FLAG_PRE_RELOC, +}; + +static int clk_main_osc_enable(struct clk *clk) +{ + struct clk_main_osc *main = to_clk_main_osc(clk); + void __iomem *reg = main->reg; + unsigned int val; + + pmc_read(reg, AT91_CKGR_MOR, &val); + val &= ~MOR_KEY_MASK; + + if (val & AT91_PMC_OSCBYPASS) + return 0; + + if (!(val & AT91_PMC_MOSCEN)) { + val |= AT91_PMC_MOSCEN | AT91_PMC_KEY; + pmc_write(reg, AT91_CKGR_MOR, val); + } + + pmc_read(reg, AT91_PMC_SR, &val); + while (!(val & AT91_PMC_MOSCS)) { + pmc_read(reg, AT91_PMC_SR, &val); + debug("waiting for main osc..\n"); + cpu_relax(); + } + + return 0; +} + +static int clk_main_osc_disable(struct clk *clk) +{ + struct clk_main_osc *main = to_clk_main_osc(clk); + void __iomem *reg = main->reg; + unsigned int val; + + pmc_read(reg, AT91_CKGR_MOR, &val); + if (val & AT91_PMC_OSCBYPASS) + return 0; + + if (!(val & AT91_PMC_MOSCEN)) + return 0; + + val &= ~(AT91_PMC_KEY | AT91_PMC_MOSCEN); + pmc_write(reg, AT91_CKGR_MOR, val | AT91_PMC_KEY); + + return 0; +} + +static const struct clk_ops main_osc_clk_ops = { + .enable = clk_main_osc_enable, + .disable = clk_main_osc_disable, + .get_rate = clk_generic_get_rate, +}; + +struct clk *at91_clk_main_osc(void __iomem *reg, const char *name, + const char *parent_name, bool bypass) +{ + struct clk_main_osc *main; + struct clk *clk; + int ret; + + if (!reg || !name || !parent_name) + return ERR_PTR(-EINVAL); + + main = kzalloc(sizeof(*main), GFP_KERNEL); + if (!main) + return ERR_PTR(-ENOMEM); + + main->reg = reg; + clk = &main->clk; + + if (bypass) { + pmc_update_bits(reg, AT91_CKGR_MOR, + MOR_KEY_MASK | AT91_PMC_OSCBYPASS, + AT91_PMC_KEY | AT91_PMC_OSCBYPASS); + } + + ret = clk_register(clk, UBOOT_DM_CLK_AT91_MAIN_OSC, name, parent_name); + if (ret) { + kfree(main); + clk = ERR_PTR(ret); + } + + return clk; +} + +U_BOOT_DRIVER(at91_main_osc_clk) = { + .name = UBOOT_DM_CLK_AT91_MAIN_OSC, + .id = UCLASS_CLK, + .ops = &main_osc_clk_ops, + .flags = DM_FLAG_PRE_RELOC, +}; + +static int clk_main_probe_frequency(void __iomem *reg) +{ + unsigned int cycles = 16; + unsigned int cycle = DIV_ROUND_UP(USEC_PER_SEC, SLOW_CLOCK_FREQ); + unsigned int mcfr; + + while (cycles--) { + pmc_read(reg, AT91_CKGR_MCFR, &mcfr); + if (mcfr & AT91_PMC_MAINRDY) + return 0; + udelay(cycle); + } + + return -ETIMEDOUT; +} + +static int clk_rm9200_main_enable(struct clk *clk) +{ + struct clk_main *main = to_clk_main(clk); + + return clk_main_probe_frequency(main->reg); +} + +static const struct clk_ops rm9200_main_clk_ops = { + .enable = clk_rm9200_main_enable, +}; + +struct clk *at91_clk_rm9200_main(void __iomem *reg, const char *name, + const char *parent_name) +{ + struct clk_main *main; + struct clk *clk; + int ret; + + if (!reg || !name || !parent_name) + return ERR_PTR(-EINVAL); + + main = kzalloc(sizeof(*main), GFP_KERNEL); + if (!main) + return ERR_PTR(-ENOMEM); + + main->reg = reg; + clk = &main->clk; + + ret = clk_register(clk, UBOOT_DM_CLK_AT91_RM9200_MAIN, name, + parent_name); + if (ret) { + kfree(main); + clk = ERR_PTR(ret); + } + + return clk; +} + +U_BOOT_DRIVER(at91_rm9200_main_clk) = { + .name = UBOOT_DM_CLK_AT91_RM9200_MAIN, + .id = UCLASS_CLK, + .ops = &rm9200_main_clk_ops, + .flags = DM_FLAG_PRE_RELOC, +}; + +static inline bool clk_sam9x5_main_ready(void __iomem *reg) +{ + unsigned int val; + + pmc_read(reg, AT91_PMC_SR, &val); + + return !!(val & AT91_PMC_MOSCSELS); +} + +static int clk_sam9x5_main_enable(struct clk *clk) +{ + struct clk_main *main = to_clk_main(clk); + void __iomem *reg = main->reg; + + while (!clk_sam9x5_main_ready(reg)) { + debug("waiting for main..."); + cpu_relax(); + } + + return clk_main_probe_frequency(reg); +} + +static int clk_sam9x5_main_set_parent(struct clk *clk, struct clk *parent) +{ + struct clk_main *main = to_clk_main(clk); + void __iomem *reg = main->reg; + unsigned int tmp, index; + + index = at91_clk_mux_val_to_index(main->clk_mux_table, + main->num_parents, AT91_CLK_ID_TO_DID(parent->id)); + if (index < 0) + return index; + + pmc_read(reg, AT91_CKGR_MOR, &tmp); + tmp &= ~MOR_KEY_MASK; + tmp |= AT91_PMC_KEY; + + if (index && !(tmp & AT91_PMC_MOSCSEL)) + pmc_write(reg, AT91_CKGR_MOR, tmp | AT91_PMC_MOSCSEL); + else if (!index && (tmp & AT91_PMC_MOSCSEL)) + pmc_write(reg, AT91_CKGR_MOR, tmp & ~AT91_PMC_MOSCSEL); + + while (!clk_sam9x5_main_ready(reg)) + cpu_relax(); + + return 0; +} + +static const struct clk_ops sam9x5_main_clk_ops = { + .enable = clk_sam9x5_main_enable, + .set_parent = clk_sam9x5_main_set_parent, + .get_rate = clk_generic_get_rate, +}; + +struct clk *at91_clk_sam9x5_main(void __iomem *reg, const char *name, + const char * const *parent_names, + int num_parents, const u32 *clk_mux_table, + int type) +{ + struct clk *clk = ERR_PTR(-ENOMEM); + struct clk_main *main = NULL; + unsigned int val; + int ret; + + if (!reg || !name || !parent_names || !num_parents || !clk_mux_table) + return ERR_PTR(-EINVAL); + + main = kzalloc(sizeof(*main), GFP_KERNEL); + if (!main) + return ERR_PTR(-ENOMEM); + + main->reg = reg; + main->parent_names = parent_names; + main->num_parents = num_parents; + main->clk_mux_table = clk_mux_table; + main->type = type; + clk = &main->clk; + clk->flags = CLK_GET_RATE_NOCACHE; + pmc_read(reg, AT91_CKGR_MOR, &val); + ret = clk_register(clk, UBOOT_DM_CLK_AT91_SAM9X5_MAIN, name, + main->parent_names[clk_main_parent_select(val)]); + if (ret) { + kfree(main); + clk = ERR_PTR(ret); + } + + return clk; +} + +U_BOOT_DRIVER(at91_sam9x5_main_clk) = { + .name = UBOOT_DM_CLK_AT91_SAM9X5_MAIN, + .id = UCLASS_CLK, + .ops = &sam9x5_main_clk_ops, + .flags = DM_FLAG_PRE_RELOC, +}; diff --git a/drivers/clk/at91/pmc.h b/drivers/clk/at91/pmc.h index 33ded02156..62bfc4b305 100644 --- a/drivers/clk/at91/pmc.h +++ b/drivers/clk/at91/pmc.h @@ -14,6 +14,16 @@ #define AT91_TO_CLK_ID(_t, _i) (((_t) << 8) | ((_i) & 0xff)) #define AT91_CLK_ID_TO_DID(_i) ((_i) & 0xff) +struct clk *at91_clk_main_rc(void __iomem *reg, const char *name, + const char *parent_name); +struct clk *at91_clk_main_osc(void __iomem *reg, const char *name, + const char *parent_name, bool bypass); +struct clk *at91_clk_rm9200_main(void __iomem *reg, const char *name, + const char *parent_name); +struct clk *at91_clk_sam9x5_main(void __iomem *reg, const char *name, + const char * const *parent_names, int num_parents, + const u32 *mux_table, int type); + int at91_clk_mux_val_to_index(const u32 *table, u32 num_parents, u32 val); int at91_clk_mux_index_to_val(const u32 *table, u32 num_parents, u32 index); -- cgit v1.2.3 From e6547a6d0c24129543ec58ed9cc5fcd6ebffc7ad Mon Sep 17 00:00:00 2001 From: Claudiu Beznea Date: Mon, 7 Sep 2020 17:46:42 +0300 Subject: clk: at91: sam9x60-pll: add driver compatible with ccf Add sam9x60-pll driver compatible with common clock framework. Signed-off-by: Claudiu Beznea --- drivers/clk/at91/Kconfig | 7 + drivers/clk/at91/Makefile | 1 + drivers/clk/at91/clk-sam9x60-pll.c | 442 +++++++++++++++++++++++++++++++++++++ drivers/clk/at91/pmc.h | 36 +++ 4 files changed, 486 insertions(+) create mode 100644 drivers/clk/at91/clk-sam9x60-pll.c (limited to 'drivers/clk') diff --git a/drivers/clk/at91/Kconfig b/drivers/clk/at91/Kconfig index 8d482a2752..4abc8026b4 100644 --- a/drivers/clk/at91/Kconfig +++ b/drivers/clk/at91/Kconfig @@ -54,3 +54,10 @@ config AT91_GENERIC_CLK that may be different from the system clock. This second clock is the generic clock (GCLK) and is managed by the PMC via PMC_PCR register. + +config AT91_SAM9X60_PLL + bool "PLL support for SAM9X60 SoCs" + depends on CLK_AT91 + help + This option is used to enable the AT91 SAM9X60's PLL clock + driver. diff --git a/drivers/clk/at91/Makefile b/drivers/clk/at91/Makefile index 805f67677a..338582b88a 100644 --- a/drivers/clk/at91/Makefile +++ b/drivers/clk/at91/Makefile @@ -4,6 +4,7 @@ ifdef CONFIG_CLK_CCF obj-y += pmc.o sckc.o clk-main.o +obj-$(CONFIG_AT91_SAM9X60_PLL) += clk-sam9x60-pll.o else obj-y += compat.o endif diff --git a/drivers/clk/at91/clk-sam9x60-pll.c b/drivers/clk/at91/clk-sam9x60-pll.c new file mode 100644 index 0000000000..1bfae5fd01 --- /dev/null +++ b/drivers/clk/at91/clk-sam9x60-pll.c @@ -0,0 +1,442 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * SAM9X60's PLL clock support. + * + * Copyright (C) 2020 Microchip Technology Inc. and its subsidiaries + * + * Author: Claudiu Beznea + * + * Based on drivers/clk/at91/clk-sam9x60-pll.c from Linux. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "pmc.h" + +#define UBOOT_DM_CLK_AT91_SAM9X60_DIV_PLL "at91-sam9x60-div-pll-clk" +#define UBOOT_DM_CLK_AT91_SAM9X60_FRAC_PLL "at91-sam9x60-frac-pll-clk" + +#define PMC_PLL_CTRL0_DIV_MSK GENMASK(7, 0) +#define PMC_PLL_CTRL1_MUL_MSK GENMASK(31, 24) +#define PMC_PLL_CTRL1_FRACR_MSK GENMASK(21, 0) + +#define PLL_DIV_MAX (FIELD_GET(PMC_PLL_CTRL0_DIV_MSK, UINT_MAX) + 1) +#define UPLL_DIV 2 +#define PLL_MUL_MAX (FIELD_GET(PMC_PLL_CTRL1_MUL_MSK, UINT_MAX) + 1) + +#define FCORE_MIN (600000000) +#define FCORE_MAX (1200000000) + +#define PLL_MAX_ID 7 + +struct sam9x60_pll { + void __iomem *base; + const struct clk_pll_characteristics *characteristics; + const struct clk_pll_layout *layout; + struct clk clk; + u8 id; +}; + +#define to_sam9x60_pll(_clk) container_of(_clk, struct sam9x60_pll, clk) + +static inline bool sam9x60_pll_ready(void __iomem *base, int id) +{ + unsigned int status; + + pmc_read(base, AT91_PMC_PLL_ISR0, &status); + + return !!(status & BIT(id)); +} + +static long sam9x60_frac_pll_compute_mul_frac(u32 *mul, u32 *frac, ulong rate, + ulong parent_rate) +{ + unsigned long tmprate, remainder; + unsigned long nmul = 0; + unsigned long nfrac = 0; + + if (rate < FCORE_MIN || rate > FCORE_MAX) + return -ERANGE; + + /* + * Calculate the multiplier associated with the current + * divider that provide the closest rate to the requested one. + */ + nmul = mult_frac(rate, 1, parent_rate); + tmprate = mult_frac(parent_rate, nmul, 1); + remainder = rate - tmprate; + + if (remainder) { + nfrac = DIV_ROUND_CLOSEST_ULL((u64)remainder * (1 << 22), + parent_rate); + + tmprate += DIV_ROUND_CLOSEST_ULL((u64)nfrac * parent_rate, + (1 << 22)); + } + + /* Check if resulted rate is valid. */ + if (tmprate < FCORE_MIN || tmprate > FCORE_MAX) + return -ERANGE; + + *mul = nmul - 1; + *frac = nfrac; + + return tmprate; +} + +static ulong sam9x60_frac_pll_set_rate(struct clk *clk, ulong rate) +{ + struct sam9x60_pll *pll = to_sam9x60_pll(clk); + void __iomem *base = pll->base; + ulong parent_rate = clk_get_parent_rate(clk); + u32 nmul, cmul, nfrac, cfrac, val; + bool ready = sam9x60_pll_ready(base, pll->id); + long ret; + + if (!parent_rate) + return 0; + + ret = sam9x60_frac_pll_compute_mul_frac(&nmul, &nfrac, rate, + parent_rate); + if (ret < 0) + return 0; + + pmc_update_bits(base, AT91_PMC_PLL_UPDT, AT91_PMC_PLL_UPDT_ID_MSK, + pll->id); + pmc_read(base, AT91_PMC_PLL_CTRL1, &val); + cmul = (val & pll->layout->mul_mask) >> pll->layout->mul_shift; + cfrac = (val & pll->layout->frac_mask) >> pll->layout->frac_shift; + + /* Check against current values. */ + if (sam9x60_pll_ready(base, pll->id) && + nmul == cmul && nfrac == cfrac) + return 0; + + /* Update it to hardware. */ + pmc_write(base, AT91_PMC_PLL_CTRL1, + (nmul << pll->layout->mul_shift) | + (nfrac << pll->layout->frac_shift)); + + pmc_update_bits(base, AT91_PMC_PLL_UPDT, + AT91_PMC_PLL_UPDT_UPDATE | AT91_PMC_PLL_UPDT_ID_MSK, + AT91_PMC_PLL_UPDT_UPDATE | pll->id); + + while (ready && !sam9x60_pll_ready(base, pll->id)) { + debug("waiting for pll %u...\n", pll->id); + cpu_relax(); + } + + return parent_rate * (nmul + 1) + ((u64)parent_rate * nfrac >> 22); +} + +static ulong sam9x60_frac_pll_get_rate(struct clk *clk) +{ + struct sam9x60_pll *pll = to_sam9x60_pll(clk); + void __iomem *base = pll->base; + ulong parent_rate = clk_get_parent_rate(clk); + u32 mul, frac, val; + + if (!parent_rate) + return 0; + + pmc_update_bits(base, AT91_PMC_PLL_UPDT, AT91_PMC_PLL_UPDT_ID_MSK, + pll->id); + pmc_read(base, AT91_PMC_PLL_CTRL1, &val); + mul = (val & pll->layout->mul_mask) >> pll->layout->mul_shift; + frac = (val & pll->layout->frac_mask) >> pll->layout->frac_shift; + + return (parent_rate * (mul + 1) + ((u64)parent_rate * frac >> 22)); +} + +static int sam9x60_frac_pll_enable(struct clk *clk) +{ + struct sam9x60_pll *pll = to_sam9x60_pll(clk); + void __iomem *base = pll->base; + unsigned int val; + ulong crate; + + crate = sam9x60_frac_pll_get_rate(clk); + if (crate < FCORE_MIN || crate > FCORE_MAX) + return -ERANGE; + + pmc_update_bits(base, AT91_PMC_PLL_UPDT, AT91_PMC_PLL_UPDT_ID_MSK, + pll->id); + pmc_read(base, AT91_PMC_PLL_CTRL1, &val); + + if (sam9x60_pll_ready(base, pll->id)) + return 0; + + pmc_update_bits(base, AT91_PMC_PLL_UPDT, + AT91_PMC_PMM_UPDT_STUPTIM_MSK | + AT91_PMC_PLL_UPDT_ID_MSK, + AT91_PMC_PLL_UPDT_STUPTIM(0x3f) | pll->id); + + /* Recommended value for AT91_PMC_PLL_ACR */ + if (pll->characteristics->upll) + val = AT91_PMC_PLL_ACR_DEFAULT_UPLL; + else + val = AT91_PMC_PLL_ACR_DEFAULT_PLLA; + pmc_write(base, AT91_PMC_PLL_ACR, val); + + if (pll->characteristics->upll) { + /* Enable the UTMI internal bandgap */ + val |= AT91_PMC_PLL_ACR_UTMIBG; + pmc_write(base, AT91_PMC_PLL_ACR, val); + + udelay(10); + + /* Enable the UTMI internal regulator */ + val |= AT91_PMC_PLL_ACR_UTMIVR; + pmc_write(base, AT91_PMC_PLL_ACR, val); + + udelay(10); + + pmc_update_bits(base, AT91_PMC_PLL_UPDT, + AT91_PMC_PLL_UPDT_UPDATE | + AT91_PMC_PLL_UPDT_ID_MSK, + AT91_PMC_PLL_UPDT_UPDATE | pll->id); + } + + pmc_update_bits(base, AT91_PMC_PLL_CTRL0, + AT91_PMC_PLL_CTRL0_ENLOCK | AT91_PMC_PLL_CTRL0_ENPLL, + AT91_PMC_PLL_CTRL0_ENLOCK | AT91_PMC_PLL_CTRL0_ENPLL); + + pmc_update_bits(base, AT91_PMC_PLL_UPDT, + AT91_PMC_PLL_UPDT_UPDATE | AT91_PMC_PLL_UPDT_ID_MSK, + AT91_PMC_PLL_UPDT_UPDATE | pll->id); + + while (!sam9x60_pll_ready(base, pll->id)) { + debug("waiting for pll %u...\n", pll->id); + cpu_relax(); + } + + return 0; +} + +static int sam9x60_frac_pll_disable(struct clk *clk) +{ + struct sam9x60_pll *pll = to_sam9x60_pll(clk); + void __iomem *base = pll->base; + + pmc_update_bits(base, AT91_PMC_PLL_UPDT, AT91_PMC_PLL_UPDT_ID_MSK, + pll->id); + + pmc_update_bits(base, AT91_PMC_PLL_CTRL0, + AT91_PMC_PLL_CTRL0_ENPLL, 0); + + if (pll->characteristics->upll) + pmc_update_bits(base, AT91_PMC_PLL_ACR, + AT91_PMC_PLL_ACR_UTMIBG | + AT91_PMC_PLL_ACR_UTMIVR, 0); + + pmc_update_bits(base, AT91_PMC_PLL_UPDT, + AT91_PMC_PLL_UPDT_UPDATE | AT91_PMC_PLL_UPDT_ID_MSK, + AT91_PMC_PLL_UPDT_UPDATE | pll->id); + + return 0; +} + +static const struct clk_ops sam9x60_frac_pll_ops = { + .enable = sam9x60_frac_pll_enable, + .disable = sam9x60_frac_pll_disable, + .set_rate = sam9x60_frac_pll_set_rate, + .get_rate = sam9x60_frac_pll_get_rate, +}; + +static int sam9x60_div_pll_enable(struct clk *clk) +{ + struct sam9x60_pll *pll = to_sam9x60_pll(clk); + void __iomem *base = pll->base; + unsigned int val; + + pmc_update_bits(base, AT91_PMC_PLL_UPDT, AT91_PMC_PLL_UPDT_ID_MSK, + pll->id); + pmc_read(base, AT91_PMC_PLL_CTRL0, &val); + + /* Stop if enabled. */ + if (val & pll->layout->endiv_mask) + return 0; + + pmc_update_bits(base, AT91_PMC_PLL_CTRL0, + pll->layout->endiv_mask, + (1 << pll->layout->endiv_shift)); + + pmc_update_bits(base, AT91_PMC_PLL_UPDT, + AT91_PMC_PLL_UPDT_UPDATE | AT91_PMC_PLL_UPDT_ID_MSK, + AT91_PMC_PLL_UPDT_UPDATE | pll->id); + + while (!sam9x60_pll_ready(base, pll->id)) { + debug("waiting for pll %u...\n", pll->id); + cpu_relax(); + } + + return 0; +} + +static int sam9x60_div_pll_disable(struct clk *clk) +{ + struct sam9x60_pll *pll = to_sam9x60_pll(clk); + void __iomem *base = pll->base; + + pmc_update_bits(base, AT91_PMC_PLL_UPDT, AT91_PMC_PLL_UPDT_ID_MSK, + pll->id); + + pmc_update_bits(base, AT91_PMC_PLL_CTRL0, + pll->layout->endiv_mask, 0); + + pmc_update_bits(base, AT91_PMC_PLL_UPDT, + AT91_PMC_PLL_UPDT_UPDATE | AT91_PMC_PLL_UPDT_ID_MSK, + AT91_PMC_PLL_UPDT_UPDATE | pll->id); + + return 0; +} + +static ulong sam9x60_div_pll_set_rate(struct clk *clk, ulong rate) +{ + struct sam9x60_pll *pll = to_sam9x60_pll(clk); + void __iomem *base = pll->base; + const struct clk_pll_characteristics *characteristics = + pll->characteristics; + ulong parent_rate = clk_get_parent_rate(clk); + u8 div = DIV_ROUND_CLOSEST_ULL(parent_rate, rate) - 1; + ulong req_rate = parent_rate / (div + 1); + bool ready = sam9x60_pll_ready(base, pll->id); + u32 val; + + if (!parent_rate || div > pll->layout->div_mask || + req_rate < characteristics->output[0].min || + req_rate > characteristics->output[0].max) + return 0; + + pmc_update_bits(base, AT91_PMC_PLL_UPDT, AT91_PMC_PLL_UPDT_ID_MSK, + pll->id); + pmc_read(base, AT91_PMC_PLL_CTRL0, &val); + /* Compare against current value. */ + if (div == ((val & pll->layout->div_mask) >> pll->layout->div_shift)) + return 0; + + /* Update it to hardware. */ + pmc_update_bits(base, AT91_PMC_PLL_CTRL0, + pll->layout->div_mask, + div << pll->layout->div_shift); + + pmc_update_bits(base, AT91_PMC_PLL_UPDT, + AT91_PMC_PLL_UPDT_UPDATE | AT91_PMC_PLL_UPDT_ID_MSK, + AT91_PMC_PLL_UPDT_UPDATE | pll->id); + + while (ready && !sam9x60_pll_ready(base, pll->id)) { + debug("waiting for pll %u...\n", pll->id); + cpu_relax(); + } + + return req_rate; +} + +static ulong sam9x60_div_pll_get_rate(struct clk *clk) +{ + struct sam9x60_pll *pll = to_sam9x60_pll(clk); + void __iomem *base = pll->base; + ulong parent_rate = clk_get_parent_rate(clk); + u32 val; + u8 div; + + if (!parent_rate) + return 0; + + pmc_update_bits(base, AT91_PMC_PLL_UPDT, AT91_PMC_PLL_UPDT_ID_MSK, + pll->id); + + pmc_read(base, AT91_PMC_PLL_CTRL0, &val); + + div = (val & pll->layout->div_mask) >> pll->layout->div_shift; + + return parent_rate / (div + 1); +} + +static const struct clk_ops sam9x60_div_pll_ops = { + .enable = sam9x60_div_pll_enable, + .disable = sam9x60_div_pll_disable, + .set_rate = sam9x60_div_pll_set_rate, + .get_rate = sam9x60_div_pll_get_rate, +}; + +static struct clk * +sam9x60_clk_register_pll(void __iomem *base, const char *type, + const char *name, const char *parent_name, u8 id, + const struct clk_pll_characteristics *characteristics, + const struct clk_pll_layout *layout, u32 flags) +{ + struct sam9x60_pll *pll; + struct clk *clk; + int ret; + + if (!base || !type || !name || !parent_name || !characteristics || + !layout || id > PLL_MAX_ID) + return ERR_PTR(-EINVAL); + + pll = kzalloc(sizeof(*pll), GFP_KERNEL); + if (!pll) + return ERR_PTR(-ENOMEM); + + pll->id = id; + pll->characteristics = characteristics; + pll->layout = layout; + pll->base = base; + clk = &pll->clk; + clk->flags = flags; + + ret = clk_register(clk, type, name, parent_name); + if (ret) { + kfree(pll); + clk = ERR_PTR(ret); + } + + return clk; +} + +struct clk * +sam9x60_clk_register_div_pll(void __iomem *base, const char *name, + const char *parent_name, u8 id, + const struct clk_pll_characteristics *characteristics, + const struct clk_pll_layout *layout, bool critical) +{ + return sam9x60_clk_register_pll(base, + UBOOT_DM_CLK_AT91_SAM9X60_DIV_PLL, name, parent_name, id, + characteristics, layout, + CLK_GET_RATE_NOCACHE | (critical ? CLK_IS_CRITICAL : 0)); +} + +struct clk * +sam9x60_clk_register_frac_pll(void __iomem *base, const char *name, + const char *parent_name, u8 id, + const struct clk_pll_characteristics *characteristics, + const struct clk_pll_layout *layout, bool critical) +{ + return sam9x60_clk_register_pll(base, + UBOOT_DM_CLK_AT91_SAM9X60_FRAC_PLL, name, parent_name, id, + characteristics, layout, + CLK_GET_RATE_NOCACHE | (critical ? CLK_IS_CRITICAL : 0)); +} + +U_BOOT_DRIVER(at91_sam9x60_div_pll_clk) = { + .name = UBOOT_DM_CLK_AT91_SAM9X60_DIV_PLL, + .id = UCLASS_CLK, + .ops = &sam9x60_div_pll_ops, + .flags = DM_FLAG_PRE_RELOC, +}; + +U_BOOT_DRIVER(at91_sam9x60_frac_pll_clk) = { + .name = UBOOT_DM_CLK_AT91_SAM9X60_FRAC_PLL, + .id = UCLASS_CLK, + .ops = &sam9x60_frac_pll_ops, + .flags = DM_FLAG_PRE_RELOC, +}; + diff --git a/drivers/clk/at91/pmc.h b/drivers/clk/at91/pmc.h index 62bfc4b305..b24d2d8990 100644 --- a/drivers/clk/at91/pmc.h +++ b/drivers/clk/at91/pmc.h @@ -14,6 +14,32 @@ #define AT91_TO_CLK_ID(_t, _i) (((_t) << 8) | ((_i) & 0xff)) #define AT91_CLK_ID_TO_DID(_i) ((_i) & 0xff) +struct clk_range { + unsigned long min; + unsigned long max; +}; + +struct clk_pll_characteristics { + struct clk_range input; + int num_output; + const struct clk_range *output; + u16 *icpll; + u8 *out; + u8 upll : 1; +}; + +struct clk_pll_layout { + u32 pllr_mask; + u32 mul_mask; + u32 frac_mask; + u32 div_mask; + u32 endiv_mask; + u8 mul_shift; + u8 frac_shift; + u8 div_shift; + u8 endiv_shift; +}; + struct clk *at91_clk_main_rc(void __iomem *reg, const char *name, const char *parent_name); struct clk *at91_clk_main_osc(void __iomem *reg, const char *name, @@ -23,6 +49,16 @@ struct clk *at91_clk_rm9200_main(void __iomem *reg, const char *name, struct clk *at91_clk_sam9x5_main(void __iomem *reg, const char *name, const char * const *parent_names, int num_parents, const u32 *mux_table, int type); +struct clk * +sam9x60_clk_register_div_pll(void __iomem *base, const char *name, + const char *parent_name, u8 id, + const struct clk_pll_characteristics *characteristics, + const struct clk_pll_layout *layout, bool critical); +struct clk * +sam9x60_clk_register_frac_pll(void __iomem *base, const char *name, + const char *parent_name, u8 id, + const struct clk_pll_characteristics *characteristics, + const struct clk_pll_layout *layout, bool critical); int at91_clk_mux_val_to_index(const u32 *table, u32 num_parents, u32 val); int at91_clk_mux_index_to_val(const u32 *table, u32 num_parents, u32 index); -- cgit v1.2.3 From b4c4e18dbda178a86bd88e96f20874c6fd51ca56 Mon Sep 17 00:00:00 2001 From: Claudiu Beznea Date: Mon, 7 Sep 2020 17:46:43 +0300 Subject: clk: at91: clk-master: add driver compatible with ccf Add clk-master driver compatible with common clock framework. Signed-off-by: Claudiu Beznea --- drivers/clk/at91/Makefile | 2 +- drivers/clk/at91/clk-master.c | 156 ++++++++++++++++++++++++++++++++++++++++++ drivers/clk/at91/pmc.h | 21 ++++++ 3 files changed, 178 insertions(+), 1 deletion(-) create mode 100644 drivers/clk/at91/clk-master.c (limited to 'drivers/clk') diff --git a/drivers/clk/at91/Makefile b/drivers/clk/at91/Makefile index 338582b88a..a4e397066e 100644 --- a/drivers/clk/at91/Makefile +++ b/drivers/clk/at91/Makefile @@ -3,7 +3,7 @@ # ifdef CONFIG_CLK_CCF -obj-y += pmc.o sckc.o clk-main.o +obj-y += pmc.o sckc.o clk-main.o clk-master.o obj-$(CONFIG_AT91_SAM9X60_PLL) += clk-sam9x60-pll.o else obj-y += compat.o diff --git a/drivers/clk/at91/clk-master.c b/drivers/clk/at91/clk-master.c new file mode 100644 index 0000000000..1d388b6b25 --- /dev/null +++ b/drivers/clk/at91/clk-master.c @@ -0,0 +1,156 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Master clock support for AT91 architectures. + * + * Copyright (C) 2020 Microchip Technology Inc. and its subsidiaries + * + * Author: Claudiu Beznea + * + * Based on drivers/clk/at91/clk-master.c from Linux. + */ + +#include +#include +#include +#include +#include +#include + +#include "pmc.h" + +#define UBOOT_DM_CLK_AT91_MASTER "at91-master-clk" + +#define MASTER_PRES_MASK 0x7 +#define MASTER_PRES_MAX MASTER_PRES_MASK +#define MASTER_DIV_SHIFT 8 +#define MASTER_DIV_MASK 0x3 + +struct clk_master { + void __iomem *base; + const struct clk_master_layout *layout; + const struct clk_master_characteristics *characteristics; + const u32 *mux_table; + const u32 *clk_mux_table; + u32 num_parents; + struct clk clk; + u8 id; +}; + +#define to_clk_master(_clk) container_of(_clk, struct clk_master, clk) + +static inline bool clk_master_ready(struct clk_master *master) +{ + unsigned int status; + + pmc_read(master->base, AT91_PMC_SR, &status); + + return !!(status & AT91_PMC_MCKRDY); +} + +static int clk_master_enable(struct clk *clk) +{ + struct clk_master *master = to_clk_master(clk); + + while (!clk_master_ready(master)) { + debug("waiting for mck %d\n", master->id); + cpu_relax(); + } + + return 0; +} + +static ulong clk_master_get_rate(struct clk *clk) +{ + struct clk_master *master = to_clk_master(clk); + const struct clk_master_layout *layout = master->layout; + const struct clk_master_characteristics *characteristics = + master->characteristics; + ulong rate = clk_get_parent_rate(clk); + unsigned int mckr; + u8 pres, div; + + if (!rate) + return 0; + + pmc_read(master->base, master->layout->offset, &mckr); + mckr &= layout->mask; + + pres = (mckr >> layout->pres_shift) & MASTER_PRES_MASK; + div = (mckr >> MASTER_DIV_SHIFT) & MASTER_DIV_MASK; + + if (characteristics->have_div3_pres && pres == MASTER_PRES_MAX) + rate /= 3; + else + rate >>= pres; + + rate /= characteristics->divisors[div]; + + if (rate < characteristics->output.min) + pr_warn("master clk is underclocked"); + else if (rate > characteristics->output.max) + pr_warn("master clk is overclocked"); + + return rate; +} + +static const struct clk_ops master_ops = { + .enable = clk_master_enable, + .get_rate = clk_master_get_rate, +}; + +struct clk *at91_clk_register_master(void __iomem *base, + const char *name, const char * const *parent_names, + int num_parents, const struct clk_master_layout *layout, + const struct clk_master_characteristics *characteristics, + const u32 *mux_table) +{ + struct clk_master *master; + struct clk *clk; + unsigned int val; + int ret; + + if (!base || !name || !num_parents || !parent_names || + !layout || !characteristics || !mux_table) + return ERR_PTR(-EINVAL); + + master = kzalloc(sizeof(*master), GFP_KERNEL); + if (!master) + return ERR_PTR(-ENOMEM); + + master->layout = layout; + master->characteristics = characteristics; + master->base = base; + master->num_parents = num_parents; + master->mux_table = mux_table; + + pmc_read(master->base, master->layout->offset, &val); + clk = &master->clk; + clk->flags = CLK_GET_RATE_NOCACHE | CLK_IS_CRITICAL; + ret = clk_register(clk, UBOOT_DM_CLK_AT91_MASTER, name, + parent_names[val & AT91_PMC_CSS]); + if (ret) { + kfree(master); + clk = ERR_PTR(ret); + } + + return clk; +} + +U_BOOT_DRIVER(at91_master_clk) = { + .name = UBOOT_DM_CLK_AT91_MASTER, + .id = UCLASS_CLK, + .ops = &master_ops, + .flags = DM_FLAG_PRE_RELOC, +}; + +const struct clk_master_layout at91rm9200_master_layout = { + .mask = 0x31F, + .pres_shift = 2, + .offset = AT91_PMC_MCKR, +}; + +const struct clk_master_layout at91sam9x5_master_layout = { + .mask = 0x373, + .pres_shift = 4, + .offset = AT91_PMC_MCKR, +}; diff --git a/drivers/clk/at91/pmc.h b/drivers/clk/at91/pmc.h index b24d2d8990..33c7a66e84 100644 --- a/drivers/clk/at91/pmc.h +++ b/drivers/clk/at91/pmc.h @@ -19,6 +19,21 @@ struct clk_range { unsigned long max; }; +struct clk_master_layout { + u32 offset; + u32 mask; + u8 pres_shift; +}; + +extern const struct clk_master_layout at91rm9200_master_layout; +extern const struct clk_master_layout at91sam9x5_master_layout; + +struct clk_master_characteristics { + struct clk_range output; + u32 divisors[4]; + u8 have_div3_pres; +}; + struct clk_pll_characteristics { struct clk_range input; int num_output; @@ -59,6 +74,12 @@ sam9x60_clk_register_frac_pll(void __iomem *base, const char *name, const char *parent_name, u8 id, const struct clk_pll_characteristics *characteristics, const struct clk_pll_layout *layout, bool critical); +struct clk * +at91_clk_register_master(void __iomem *base, const char *name, + const char * const *parent_names, int num_parents, + const struct clk_master_layout *layout, + const struct clk_master_characteristics *characteristics, + const u32 *mux_table); int at91_clk_mux_val_to_index(const u32 *table, u32 num_parents, u32 val); int at91_clk_mux_index_to_val(const u32 *table, u32 num_parents, u32 index); -- cgit v1.2.3 From dd4d19ddfb6590c4504a7d80f2e4dffe56c28337 Mon Sep 17 00:00:00 2001 From: Claudiu Beznea Date: Mon, 7 Sep 2020 17:46:44 +0300 Subject: clk: at91: clk-master: add support for sama7g5 Add master clock (MCK1..MCK4) support for SAMA7G5. SAMA7G5's PMC has multiple master clocks feeding different subsystems. One of them feeds image subsystem and is changeable based on image subsystem needs. Signed-off-by: Claudiu Beznea --- drivers/clk/at91/clk-master.c | 178 +++++++++++++++++++++++++++++++++++++++++- drivers/clk/at91/pmc.h | 5 ++ 2 files changed, 182 insertions(+), 1 deletion(-) (limited to 'drivers/clk') diff --git a/drivers/clk/at91/clk-master.c b/drivers/clk/at91/clk-master.c index 1d388b6b25..759df93697 100644 --- a/drivers/clk/at91/clk-master.c +++ b/drivers/clk/at91/clk-master.c @@ -19,12 +19,25 @@ #include "pmc.h" #define UBOOT_DM_CLK_AT91_MASTER "at91-master-clk" +#define UBOOT_DM_CLK_AT91_SAMA7G5_MASTER "at91-sama7g5-master-clk" #define MASTER_PRES_MASK 0x7 #define MASTER_PRES_MAX MASTER_PRES_MASK #define MASTER_DIV_SHIFT 8 #define MASTER_DIV_MASK 0x3 +#define PMC_MCR 0x30 +#define PMC_MCR_ID_MSK GENMASK(3, 0) +#define PMC_MCR_CMD BIT(7) +#define PMC_MCR_DIV GENMASK(10, 8) +#define PMC_MCR_CSS GENMASK(20, 16) +#define PMC_MCR_CSS_SHIFT (16) +#define PMC_MCR_EN BIT(28) + +#define PMC_MCR_ID(x) ((x) & PMC_MCR_ID_MSK) + +#define MASTER_MAX_ID 4 + struct clk_master { void __iomem *base; const struct clk_master_layout *layout; @@ -40,11 +53,12 @@ struct clk_master { static inline bool clk_master_ready(struct clk_master *master) { + unsigned int bit = master->id ? AT91_PMC_MCKXRDY : AT91_PMC_MCKRDY; unsigned int status; pmc_read(master->base, AT91_PMC_SR, &status); - return !!(status & AT91_PMC_MCKRDY); + return !!(status & bit); } static int clk_master_enable(struct clk *clk) @@ -143,6 +157,168 @@ U_BOOT_DRIVER(at91_master_clk) = { .flags = DM_FLAG_PRE_RELOC, }; +static int clk_sama7g5_master_set_parent(struct clk *clk, struct clk *parent) +{ + struct clk_master *master = to_clk_master(clk); + int index; + + index = at91_clk_mux_val_to_index(master->clk_mux_table, + master->num_parents, parent->id); + if (index < 0) + return index; + + index = at91_clk_mux_index_to_val(master->mux_table, + master->num_parents, index); + if (index < 0) + return index; + + pmc_write(master->base, PMC_MCR, PMC_MCR_ID(master->id)); + pmc_update_bits(master->base, PMC_MCR, + PMC_MCR_CSS | PMC_MCR_CMD | PMC_MCR_ID_MSK, + (index << PMC_MCR_CSS_SHIFT) | PMC_MCR_CMD | + PMC_MCR_ID(master->id)); + return 0; +} + +static int clk_sama7g5_master_enable(struct clk *clk) +{ + struct clk_master *master = to_clk_master(clk); + + pmc_write(master->base, PMC_MCR, PMC_MCR_ID(master->id)); + pmc_update_bits(master->base, PMC_MCR, + PMC_MCR_EN | PMC_MCR_CMD | PMC_MCR_ID_MSK, + PMC_MCR_EN | PMC_MCR_CMD | PMC_MCR_ID(master->id)); + + return 0; +} + +static int clk_sama7g5_master_disable(struct clk *clk) +{ + struct clk_master *master = to_clk_master(clk); + + pmc_write(master->base, PMC_MCR, master->id); + pmc_update_bits(master->base, PMC_MCR, + PMC_MCR_EN | PMC_MCR_CMD | PMC_MCR_ID_MSK, + PMC_MCR_CMD | PMC_MCR_ID(master->id)); + + return 0; +} + +static ulong clk_sama7g5_master_set_rate(struct clk *clk, ulong rate) +{ + struct clk_master *master = to_clk_master(clk); + ulong parent_rate = clk_get_parent_rate(clk); + ulong div, rrate; + + if (!parent_rate) + return 0; + + div = DIV_ROUND_CLOSEST(parent_rate, rate); + if ((div > (1 << (MASTER_PRES_MAX - 1))) || (div & (div - 1))) { + return 0; + } else if (div == 3) { + rrate = DIV_ROUND_CLOSEST(parent_rate, MASTER_PRES_MAX); + div = MASTER_PRES_MAX; + } else { + rrate = DIV_ROUND_CLOSEST(parent_rate, div); + div = ffs(div) - 1; + } + + pmc_write(master->base, PMC_MCR, master->id); + pmc_update_bits(master->base, PMC_MCR, + PMC_MCR_DIV | PMC_MCR_CMD | PMC_MCR_ID_MSK, + (div << MASTER_DIV_SHIFT) | PMC_MCR_CMD | + PMC_MCR_ID(master->id)); + + return rrate; +} + +static ulong clk_sama7g5_master_get_rate(struct clk *clk) +{ + struct clk_master *master = to_clk_master(clk); + ulong parent_rate = clk_get_parent_rate(clk); + unsigned int val; + ulong div; + + if (!parent_rate) + return 0; + + pmc_write(master->base, PMC_MCR, master->id); + pmc_read(master->base, PMC_MCR, &val); + + div = (val >> MASTER_DIV_SHIFT) & MASTER_DIV_MASK; + + if (div == MASTER_PRES_MAX) + div = 3; + else + div = 1 << div; + + return DIV_ROUND_CLOSEST(parent_rate, div); +} + +static const struct clk_ops sama7g5_master_ops = { + .enable = clk_sama7g5_master_enable, + .disable = clk_sama7g5_master_disable, + .set_rate = clk_sama7g5_master_set_rate, + .get_rate = clk_sama7g5_master_get_rate, + .set_parent = clk_sama7g5_master_set_parent, +}; + +struct clk *at91_clk_sama7g5_register_master(void __iomem *base, + const char *name, const char * const *parent_names, + int num_parents, const u32 *mux_table, const u32 *clk_mux_table, + bool critical, u8 id) +{ + struct clk_master *master; + struct clk *clk; + u32 val, index; + int ret; + + if (!base || !name || !num_parents || !parent_names || + !mux_table || !clk_mux_table || id > MASTER_MAX_ID) + return ERR_PTR(-EINVAL); + + master = kzalloc(sizeof(*master), GFP_KERNEL); + if (!master) + return ERR_PTR(-ENOMEM); + + master->base = base; + master->id = id; + master->mux_table = mux_table; + master->clk_mux_table = clk_mux_table; + master->num_parents = num_parents; + + pmc_write(master->base, PMC_MCR, master->id); + pmc_read(master->base, PMC_MCR, &val); + + index = at91_clk_mux_val_to_index(master->mux_table, + master->num_parents, + (val & PMC_MCR_CSS) >> PMC_MCR_CSS_SHIFT); + if (index < 0) { + kfree(master); + return ERR_PTR(index); + } + + clk = &master->clk; + clk->flags = CLK_GET_RATE_NOCACHE | (critical ? CLK_IS_CRITICAL : 0); + + ret = clk_register(clk, UBOOT_DM_CLK_AT91_SAMA7G5_MASTER, name, + parent_names[index]); + if (ret) { + kfree(master); + clk = ERR_PTR(ret); + } + + return clk; +} + +U_BOOT_DRIVER(at91_sama7g5_master_clk) = { + .name = UBOOT_DM_CLK_AT91_SAMA7G5_MASTER, + .id = UCLASS_CLK, + .ops = &sama7g5_master_ops, + .flags = DM_FLAG_PRE_RELOC, +}; + const struct clk_master_layout at91rm9200_master_layout = { .mask = 0x31F, .pres_shift = 2, diff --git a/drivers/clk/at91/pmc.h b/drivers/clk/at91/pmc.h index 33c7a66e84..49e1e372b8 100644 --- a/drivers/clk/at91/pmc.h +++ b/drivers/clk/at91/pmc.h @@ -80,6 +80,11 @@ at91_clk_register_master(void __iomem *base, const char *name, const struct clk_master_layout *layout, const struct clk_master_characteristics *characteristics, const u32 *mux_table); +struct clk * +at91_clk_sama7g5_register_master(void __iomem *base, const char *name, + const char * const *parent_names, int num_parents, + const u32 *mux_table, const u32 *clk_mux_table, + bool critical, u8 id); int at91_clk_mux_val_to_index(const u32 *table, u32 num_parents, u32 val); int at91_clk_mux_index_to_val(const u32 *table, u32 num_parents, u32 index); -- cgit v1.2.3 From ad4d39a964e1d3abb4f2c3d933234f86a7960d54 Mon Sep 17 00:00:00 2001 From: Claudiu Beznea Date: Mon, 7 Sep 2020 17:46:45 +0300 Subject: clk: at91: clk-utmi: add driver compatible with ccf Add clk-utmi driver compatible with common clock framework. Signed-off-by: Claudiu Beznea --- drivers/clk/at91/Makefile | 1 + drivers/clk/at91/clk-utmi.c | 165 ++++++++++++++++++++++++++++++++++++++++++++ drivers/clk/at91/pmc.h | 3 + 3 files changed, 169 insertions(+) create mode 100644 drivers/clk/at91/clk-utmi.c (limited to 'drivers/clk') diff --git a/drivers/clk/at91/Makefile b/drivers/clk/at91/Makefile index a4e397066e..9c38bda5e6 100644 --- a/drivers/clk/at91/Makefile +++ b/drivers/clk/at91/Makefile @@ -4,6 +4,7 @@ ifdef CONFIG_CLK_CCF obj-y += pmc.o sckc.o clk-main.o clk-master.o +obj-$(CONFIG_AT91_UTMI) += clk-utmi.o obj-$(CONFIG_AT91_SAM9X60_PLL) += clk-sam9x60-pll.o else obj-y += compat.o diff --git a/drivers/clk/at91/clk-utmi.c b/drivers/clk/at91/clk-utmi.c new file mode 100644 index 0000000000..b60fd35b6b --- /dev/null +++ b/drivers/clk/at91/clk-utmi.c @@ -0,0 +1,165 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * UTMI clock support for AT91 architectures. + * + * Copyright (C) 2020 Microchip Technology Inc. and its subsidiaries + * + * Author: Claudiu Beznea + * + * Based on drivers/clk/at91/clk-utmi.c from Linux. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "pmc.h" + +#define UBOOT_DM_CLK_AT91_UTMI "at91-utmi-clk" + +/* + * The purpose of this clock is to generate a 480 MHz signal. A different + * rate can't be configured. + */ +#define UTMI_RATE 480000000 + +struct clk_utmi { + void __iomem *base; + struct regmap *regmap_sfr; + struct clk clk; +}; + +#define to_clk_utmi(_clk) container_of(_clk, struct clk_utmi, clk) + +static inline bool clk_utmi_ready(struct regmap *regmap) +{ + unsigned int status; + + pmc_read(regmap, AT91_PMC_SR, &status); + + return !!(status & AT91_PMC_LOCKU); +} + +static int clk_utmi_enable(struct clk *clk) +{ + struct clk_utmi *utmi = to_clk_utmi(clk); + unsigned int uckr = AT91_PMC_UPLLEN | AT91_PMC_UPLLCOUNT | + AT91_PMC_BIASEN; + unsigned int utmi_ref_clk_freq; + ulong parent_rate = clk_get_parent_rate(clk); + + /* + * If mainck rate is different from 12 MHz, we have to configure the + * FREQ field of the SFR_UTMICKTRIM register to generate properly + * the utmi clock. + */ + switch (parent_rate) { + case 12000000: + utmi_ref_clk_freq = 0; + break; + case 16000000: + utmi_ref_clk_freq = 1; + break; + case 24000000: + utmi_ref_clk_freq = 2; + break; + /* + * Not supported on SAMA5D2 but it's not an issue since MAINCK + * maximum value is 24 MHz. + */ + case 48000000: + utmi_ref_clk_freq = 3; + break; + default: + debug("UTMICK: unsupported mainck rate\n"); + return -EINVAL; + } + + if (utmi->regmap_sfr) { + regmap_update_bits(utmi->regmap_sfr, AT91_SFR_UTMICKTRIM, + AT91_UTMICKTRIM_FREQ, utmi_ref_clk_freq); + } else if (utmi_ref_clk_freq) { + debug("UTMICK: sfr node required\n"); + return -EINVAL; + } + + pmc_update_bits(utmi->base, AT91_CKGR_UCKR, uckr, uckr); + + while (!clk_utmi_ready(utmi->base)) { + debug("waiting for utmi...\n"); + cpu_relax(); + } + + return 0; +} + +static int clk_utmi_disable(struct clk *clk) +{ + struct clk_utmi *utmi = to_clk_utmi(clk); + + pmc_update_bits(utmi->base, AT91_CKGR_UCKR, AT91_PMC_UPLLEN, 0); + + return 0; +} + +static ulong clk_utmi_get_rate(struct clk *clk) +{ + /* UTMI clk rate is fixed. */ + return UTMI_RATE; +} + +static const struct clk_ops utmi_ops = { + .enable = clk_utmi_enable, + .disable = clk_utmi_disable, + .get_rate = clk_utmi_get_rate, +}; + +struct clk *at91_clk_register_utmi(void __iomem *base, struct udevice *dev, + const char *name, const char *parent_name) +{ + struct udevice *syscon; + struct clk_utmi *utmi; + struct clk *clk; + int ret; + + if (!base || !dev || !name || !parent_name) + return ERR_PTR(-EINVAL); + + ret = uclass_get_device_by_phandle(UCLASS_SYSCON, dev, + "regmap-sfr", &syscon); + if (ret) + return ERR_PTR(ret); + + utmi = kzalloc(sizeof(*utmi), GFP_KERNEL); + if (!utmi) + return ERR_PTR(-ENOMEM); + + utmi->base = base; + utmi->regmap_sfr = syscon_get_regmap(syscon); + if (!utmi->regmap_sfr) { + kfree(utmi); + return ERR_PTR(-ENODEV); + } + + clk = &utmi->clk; + clk->flags = CLK_GET_RATE_NOCACHE; + ret = clk_register(clk, UBOOT_DM_CLK_AT91_UTMI, name, parent_name); + if (ret) { + kfree(utmi); + clk = ERR_PTR(ret); + } + + return clk; +} + +U_BOOT_DRIVER(at91_utmi_clk) = { + .name = UBOOT_DM_CLK_AT91_UTMI, + .id = UCLASS_CLK, + .ops = &utmi_ops, + .flags = DM_FLAG_PRE_RELOC, +}; diff --git a/drivers/clk/at91/pmc.h b/drivers/clk/at91/pmc.h index 49e1e372b8..264b36c7b4 100644 --- a/drivers/clk/at91/pmc.h +++ b/drivers/clk/at91/pmc.h @@ -85,6 +85,9 @@ at91_clk_sama7g5_register_master(void __iomem *base, const char *name, const char * const *parent_names, int num_parents, const u32 *mux_table, const u32 *clk_mux_table, bool critical, u8 id); +struct clk * +at91_clk_register_utmi(void __iomem *base, struct udevice *dev, + const char *name, const char *parent_name); int at91_clk_mux_val_to_index(const u32 *table, u32 num_parents, u32 val); int at91_clk_mux_index_to_val(const u32 *table, u32 num_parents, u32 index); -- cgit v1.2.3 From 03417335702b50d70b57d8594efd61239e88c362 Mon Sep 17 00:00:00 2001 From: Claudiu Beznea Date: Mon, 7 Sep 2020 17:46:46 +0300 Subject: clk: at91: clk-utmi: add support for sama7g5 Add UTMI support for SAMA7G5. SAMA7G5's UTMI control is done via XTALF register. Values written at bits 2..0 in this register correspond to the on board crystal oscillator frequency. Signed-off-by: Claudiu Beznea --- drivers/clk/at91/clk-utmi.c | 71 ++++++++++++++++++++++++++++++++++++++++++++- drivers/clk/at91/pmc.h | 3 ++ 2 files changed, 73 insertions(+), 1 deletion(-) (limited to 'drivers/clk') diff --git a/drivers/clk/at91/clk-utmi.c b/drivers/clk/at91/clk-utmi.c index b60fd35b6b..7c8bcfb51d 100644 --- a/drivers/clk/at91/clk-utmi.c +++ b/drivers/clk/at91/clk-utmi.c @@ -20,7 +20,8 @@ #include "pmc.h" -#define UBOOT_DM_CLK_AT91_UTMI "at91-utmi-clk" +#define UBOOT_DM_CLK_AT91_UTMI "at91-utmi-clk" +#define UBOOT_DM_CLK_AT91_SAMA7G5_UTMI "at91-sama7g5-utmi-clk" /* * The purpose of this clock is to generate a 480 MHz signal. A different @@ -163,3 +164,71 @@ U_BOOT_DRIVER(at91_utmi_clk) = { .ops = &utmi_ops, .flags = DM_FLAG_PRE_RELOC, }; + +static int clk_utmi_sama7g5_enable(struct clk *clk) +{ + struct clk_utmi *utmi = to_clk_utmi(clk); + ulong parent_rate = clk_get_parent_rate(clk); + unsigned int val; + + switch (parent_rate) { + case 16000000: + val = 0; + break; + case 20000000: + val = 2; + break; + case 24000000: + val = 3; + break; + case 32000000: + val = 5; + break; + default: + debug("UTMICK: unsupported main_xtal rate\n"); + return -EINVAL; + } + + pmc_write(utmi->base, AT91_PMC_XTALF, val); + + return 0; +} + +static const struct clk_ops sama7g5_utmi_ops = { + .enable = clk_utmi_sama7g5_enable, + .get_rate = clk_utmi_get_rate, +}; + +struct clk *at91_clk_sama7g5_register_utmi(void __iomem *base, + const char *name, const char *parent_name) +{ + struct clk_utmi *utmi; + struct clk *clk; + int ret; + + if (!base || !name || !parent_name) + return ERR_PTR(-EINVAL); + + utmi = kzalloc(sizeof(*utmi), GFP_KERNEL); + if (!utmi) + return ERR_PTR(-ENOMEM); + + utmi->base = base; + + clk = &utmi->clk; + ret = clk_register(clk, UBOOT_DM_CLK_AT91_SAMA7G5_UTMI, name, + parent_name); + if (ret) { + kfree(utmi); + clk = ERR_PTR(ret); + } + + return clk; +} + +U_BOOT_DRIVER(at91_sama7g5_utmi_clk) = { + .name = UBOOT_DM_CLK_AT91_SAMA7G5_UTMI, + .id = UCLASS_CLK, + .ops = &sama7g5_utmi_ops, + .flags = DM_FLAG_PRE_RELOC, +}; diff --git a/drivers/clk/at91/pmc.h b/drivers/clk/at91/pmc.h index 264b36c7b4..d34005f698 100644 --- a/drivers/clk/at91/pmc.h +++ b/drivers/clk/at91/pmc.h @@ -88,6 +88,9 @@ at91_clk_sama7g5_register_master(void __iomem *base, const char *name, struct clk * at91_clk_register_utmi(void __iomem *base, struct udevice *dev, const char *name, const char *parent_name); +struct clk * +at91_clk_sama7g5_register_utmi(void __iomem *base, const char *name, + const char *parent_name); int at91_clk_mux_val_to_index(const u32 *table, u32 num_parents, u32 val); int at91_clk_mux_index_to_val(const u32 *table, u32 num_parents, u32 index); -- cgit v1.2.3 From 2a1a579bde83f0ab6a6eb61a4189820972bfe691 Mon Sep 17 00:00:00 2001 From: Claudiu Beznea Date: Mon, 7 Sep 2020 17:46:47 +0300 Subject: clk: at91: clk-programmable: add driver compatible with ccf Add clk-programmable driver compatible with common clock framework. Signed-off-by: Claudiu Beznea --- drivers/clk/at91/Makefile | 2 +- drivers/clk/at91/clk-programmable.c | 208 ++++++++++++++++++++++++++++++++++++ drivers/clk/at91/pmc.h | 17 +++ 3 files changed, 226 insertions(+), 1 deletion(-) create mode 100644 drivers/clk/at91/clk-programmable.c (limited to 'drivers/clk') diff --git a/drivers/clk/at91/Makefile b/drivers/clk/at91/Makefile index 9c38bda5e6..8951052eb0 100644 --- a/drivers/clk/at91/Makefile +++ b/drivers/clk/at91/Makefile @@ -3,7 +3,7 @@ # ifdef CONFIG_CLK_CCF -obj-y += pmc.o sckc.o clk-main.o clk-master.o +obj-y += pmc.o sckc.o clk-main.o clk-master.o clk-programmable.o obj-$(CONFIG_AT91_UTMI) += clk-utmi.o obj-$(CONFIG_AT91_SAM9X60_PLL) += clk-sam9x60-pll.o else diff --git a/drivers/clk/at91/clk-programmable.c b/drivers/clk/at91/clk-programmable.c new file mode 100644 index 0000000000..868de4b177 --- /dev/null +++ b/drivers/clk/at91/clk-programmable.c @@ -0,0 +1,208 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Programmable clock support for AT91 architectures. + * + * Copyright (C) 2020 Microchip Technology Inc. and its subsidiaries + * + * Author: Claudiu Beznea + * + * Based on drivers/clk/at91/clk-programmable.c from Linux. + */ +#include +#include +#include +#include +#include + +#include "pmc.h" + +#define UBOOT_DM_CLK_AT91_PROG "at91-prog-clk" + +#define PROG_ID_MAX 7 + +#define PROG_STATUS_MASK(id) (1 << ((id) + 8)) +#define PROG_PRES(_l, _p) (((_p) >> (_l)->pres_shift) & (_l)->pres_mask) +#define PROG_MAX_RM9200_CSS 3 + +struct clk_programmable { + void __iomem *base; + const u32 *clk_mux_table; + const u32 *mux_table; + const struct clk_programmable_layout *layout; + u32 num_parents; + struct clk clk; + u8 id; +}; + +#define to_clk_programmable(_c) container_of(_c, struct clk_programmable, clk) + +static ulong clk_programmable_get_rate(struct clk *clk) +{ + struct clk_programmable *prog = to_clk_programmable(clk); + const struct clk_programmable_layout *layout = prog->layout; + ulong rate, parent_rate = clk_get_parent_rate(clk); + unsigned int pckr; + + pmc_read(prog->base, AT91_PMC_PCKR(prog->id), &pckr); + + if (layout->is_pres_direct) + rate = parent_rate / (PROG_PRES(layout, pckr) + 1); + else + rate = parent_rate >> PROG_PRES(layout, pckr); + + return rate; +} + +static int clk_programmable_set_parent(struct clk *clk, struct clk *parent) +{ + struct clk_programmable *prog = to_clk_programmable(clk); + const struct clk_programmable_layout *layout = prog->layout; + unsigned int mask = layout->css_mask; + int index; + + index = at91_clk_mux_val_to_index(prog->clk_mux_table, + prog->num_parents, parent->id); + if (index < 0) + return index; + + index = at91_clk_mux_index_to_val(prog->mux_table, prog->num_parents, + index); + if (index < 0) + return index; + + if (layout->have_slck_mck) + mask |= AT91_PMC_CSSMCK_MCK; + + if (index > layout->css_mask) { + if (index > PROG_MAX_RM9200_CSS && !layout->have_slck_mck) + return -EINVAL; + + index |= AT91_PMC_CSSMCK_MCK; + } + + pmc_update_bits(prog->base, AT91_PMC_PCKR(prog->id), mask, index); + + return 0; +} + +static ulong clk_programmable_set_rate(struct clk *clk, ulong rate) +{ + struct clk_programmable *prog = to_clk_programmable(clk); + const struct clk_programmable_layout *layout = prog->layout; + ulong parent_rate = clk_get_parent_rate(clk); + ulong div = parent_rate / rate; + int shift = 0; + + if (!parent_rate || !div) + return -EINVAL; + + if (layout->is_pres_direct) { + shift = div - 1; + + if (shift > layout->pres_mask) + return -EINVAL; + } else { + shift = fls(div) - 1; + + if (div != (1 << shift)) + return -EINVAL; + + if (shift >= layout->pres_mask) + return -EINVAL; + } + + pmc_update_bits(prog->base, AT91_PMC_PCKR(prog->id), + layout->pres_mask << layout->pres_shift, + shift << layout->pres_shift); + + if (layout->is_pres_direct) + return (parent_rate / shift + 1); + + return parent_rate >> shift; +} + +static const struct clk_ops programmable_ops = { + .get_rate = clk_programmable_get_rate, + .set_parent = clk_programmable_set_parent, + .set_rate = clk_programmable_set_rate, +}; + +struct clk *at91_clk_register_programmable(void __iomem *base, const char *name, + const char *const *parent_names, u8 num_parents, u8 id, + const struct clk_programmable_layout *layout, + const u32 *clk_mux_table, const u32 *mux_table) +{ + struct clk_programmable *prog; + struct clk *clk; + u32 val, tmp; + int ret; + + if (!base || !name || !parent_names || !num_parents || + !layout || !clk_mux_table || !mux_table || id > PROG_ID_MAX) + return ERR_PTR(-EINVAL); + + prog = kzalloc(sizeof(*prog), GFP_KERNEL); + if (!prog) + return ERR_PTR(-ENOMEM); + + prog->id = id; + prog->layout = layout; + prog->base = base; + prog->clk_mux_table = clk_mux_table; + prog->mux_table = mux_table; + prog->num_parents = num_parents; + + pmc_read(prog->base, AT91_PMC_PCKR(prog->id), &tmp); + val = tmp & prog->layout->css_mask; + if (layout->have_slck_mck && (tmp & AT91_PMC_CSSMCK_MCK) && !val) + ret = PROG_MAX_RM9200_CSS + 1; + else + ret = at91_clk_mux_val_to_index(prog->mux_table, + prog->num_parents, val); + if (ret < 0) { + kfree(prog); + return ERR_PTR(ret); + } + + clk = &prog->clk; + clk->flags = CLK_GET_RATE_NOCACHE; + ret = clk_register(clk, UBOOT_DM_CLK_AT91_PROG, name, + parent_names[ret]); + if (ret) { + kfree(prog); + clk = ERR_PTR(ret); + } + + return clk; +} + +U_BOOT_DRIVER(at91_prog_clk) = { + .name = UBOOT_DM_CLK_AT91_PROG, + .id = UCLASS_CLK, + .ops = &programmable_ops, + .flags = DM_FLAG_PRE_RELOC, +}; + +const struct clk_programmable_layout at91rm9200_programmable_layout = { + .pres_mask = 0x7, + .pres_shift = 2, + .css_mask = 0x3, + .have_slck_mck = 0, + .is_pres_direct = 0, +}; + +const struct clk_programmable_layout at91sam9g45_programmable_layout = { + .pres_mask = 0x7, + .pres_shift = 2, + .css_mask = 0x3, + .have_slck_mck = 1, + .is_pres_direct = 0, +}; + +const struct clk_programmable_layout at91sam9x5_programmable_layout = { + .pres_mask = 0x7, + .pres_shift = 4, + .css_mask = 0x7, + .have_slck_mck = 0, + .is_pres_direct = 0, +}; diff --git a/drivers/clk/at91/pmc.h b/drivers/clk/at91/pmc.h index d34005f698..a443b65257 100644 --- a/drivers/clk/at91/pmc.h +++ b/drivers/clk/at91/pmc.h @@ -55,6 +55,18 @@ struct clk_pll_layout { u8 endiv_shift; }; +struct clk_programmable_layout { + u8 pres_mask; + u8 pres_shift; + u8 css_mask; + u8 have_slck_mck; + u8 is_pres_direct; +}; + +extern const struct clk_programmable_layout at91rm9200_programmable_layout; +extern const struct clk_programmable_layout at91sam9g45_programmable_layout; +extern const struct clk_programmable_layout at91sam9x5_programmable_layout; + struct clk *at91_clk_main_rc(void __iomem *reg, const char *name, const char *parent_name); struct clk *at91_clk_main_osc(void __iomem *reg, const char *name, @@ -91,6 +103,11 @@ at91_clk_register_utmi(void __iomem *base, struct udevice *dev, struct clk * at91_clk_sama7g5_register_utmi(void __iomem *base, const char *name, const char *parent_name); +struct clk * +at91_clk_register_programmable(void __iomem *base, const char *name, + const char * const *parent_names, u8 num_parents, u8 id, + const struct clk_programmable_layout *layout, + const u32 *clk_mux_table, const u32 *mux_table); int at91_clk_mux_val_to_index(const u32 *table, u32 num_parents, u32 val); int at91_clk_mux_index_to_val(const u32 *table, u32 num_parents, u32 index); -- cgit v1.2.3 From 16502bfa7c969cff366f92de67c048d2b0c626b8 Mon Sep 17 00:00:00 2001 From: Claudiu Beznea Date: Mon, 7 Sep 2020 17:46:48 +0300 Subject: clk: at91: clk-system: add driver compatible with ccf Add clk-system driver compatible with common clock framework. Signed-off-by: Claudiu Beznea --- drivers/clk/at91/Makefile | 2 +- drivers/clk/at91/clk-system.c | 112 ++++++++++++++++++++++++++++++++++++++++++ drivers/clk/at91/pmc.h | 3 ++ 3 files changed, 116 insertions(+), 1 deletion(-) create mode 100644 drivers/clk/at91/clk-system.c (limited to 'drivers/clk') diff --git a/drivers/clk/at91/Makefile b/drivers/clk/at91/Makefile index 8951052eb0..1d59531e0c 100644 --- a/drivers/clk/at91/Makefile +++ b/drivers/clk/at91/Makefile @@ -3,7 +3,7 @@ # ifdef CONFIG_CLK_CCF -obj-y += pmc.o sckc.o clk-main.o clk-master.o clk-programmable.o +obj-y += pmc.o sckc.o clk-main.o clk-master.o clk-programmable.o clk-system.o obj-$(CONFIG_AT91_UTMI) += clk-utmi.o obj-$(CONFIG_AT91_SAM9X60_PLL) += clk-sam9x60-pll.o else diff --git a/drivers/clk/at91/clk-system.c b/drivers/clk/at91/clk-system.c new file mode 100644 index 0000000000..82f79e74a1 --- /dev/null +++ b/drivers/clk/at91/clk-system.c @@ -0,0 +1,112 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * System clock support for AT91 architectures. + * + * Copyright (C) Microchip Technology Inc. and its subsidiaries + * + * Author: Claudiu Beznea + * + * Based on drivers/clk/at91/clk-system.c from Linux. + */ +#include +#include +#include +#include +#include +#include +#include + +#include "pmc.h" + +#define UBOOT_DM_CLK_AT91_SYSTEM "at91-system-clk" + +#define SYSTEM_MAX_ID 31 + +struct clk_system { + void __iomem *base; + struct clk clk; + u8 id; +}; + +#define to_clk_system(_c) container_of(_c, struct clk_system, clk) + +static inline int is_pck(int id) +{ + return (id >= 8) && (id <= 15); +} + +static inline bool clk_system_ready(void __iomem *base, int id) +{ + unsigned int status; + + pmc_read(base, AT91_PMC_SR, &status); + + return !!(status & (1 << id)); +} + +static int clk_system_enable(struct clk *clk) +{ + struct clk_system *sys = to_clk_system(clk); + + pmc_write(sys->base, AT91_PMC_SCER, 1 << sys->id); + + if (!is_pck(sys->id)) + return 0; + + while (!clk_system_ready(sys->base, sys->id)) { + debug("waiting for pck%u\n", sys->id); + cpu_relax(); + } + + return 0; +} + +static int clk_system_disable(struct clk *clk) +{ + struct clk_system *sys = to_clk_system(clk); + + pmc_write(sys->base, AT91_PMC_SCDR, 1 << sys->id); + + return 0; +} + +static const struct clk_ops system_ops = { + .enable = clk_system_enable, + .disable = clk_system_disable, + .get_rate = clk_generic_get_rate, +}; + +struct clk *at91_clk_register_system(void __iomem *base, const char *name, + const char *parent_name, u8 id) +{ + struct clk_system *sys; + struct clk *clk; + int ret; + + if (!base || !name || !parent_name || id > SYSTEM_MAX_ID) + return ERR_PTR(-EINVAL); + + sys = kzalloc(sizeof(*sys), GFP_KERNEL); + if (!sys) + return ERR_PTR(-ENOMEM); + + sys->id = id; + sys->base = base; + + clk = &sys->clk; + clk->flags = CLK_GET_RATE_NOCACHE; + ret = clk_register(clk, UBOOT_DM_CLK_AT91_SYSTEM, name, parent_name); + if (ret) { + kfree(sys); + clk = ERR_PTR(ret); + } + + return clk; +} + +U_BOOT_DRIVER(at91_system_clk) = { + .name = UBOOT_DM_CLK_AT91_SYSTEM, + .id = UCLASS_CLK, + .ops = &system_ops, + .flags = DM_FLAG_PRE_RELOC, +}; diff --git a/drivers/clk/at91/pmc.h b/drivers/clk/at91/pmc.h index a443b65257..c372f39fc9 100644 --- a/drivers/clk/at91/pmc.h +++ b/drivers/clk/at91/pmc.h @@ -108,6 +108,9 @@ at91_clk_register_programmable(void __iomem *base, const char *name, const char * const *parent_names, u8 num_parents, u8 id, const struct clk_programmable_layout *layout, const u32 *clk_mux_table, const u32 *mux_table); +struct clk * +at91_clk_register_system(void __iomem *base, const char *name, + const char *parent_name, u8 id); int at91_clk_mux_val_to_index(const u32 *table, u32 num_parents, u32 val); int at91_clk_mux_index_to_val(const u32 *table, u32 num_parents, u32 index); -- cgit v1.2.3 From f89268e46814a56323ca78b85eb0e32e6ef3cbd0 Mon Sep 17 00:00:00 2001 From: Claudiu Beznea Date: Mon, 7 Sep 2020 17:46:49 +0300 Subject: clk: at91: clk-peripheral: add driver compatible with ccf Add clk-peripheral compatible with common clock framework. Signed-off-by: Claudiu Beznea --- drivers/clk/at91/Makefile | 2 + drivers/clk/at91/clk-peripheral.c | 254 ++++++++++++++++++++++++++++++++++++++ drivers/clk/at91/pmc.h | 16 +++ 3 files changed, 272 insertions(+) create mode 100644 drivers/clk/at91/clk-peripheral.c (limited to 'drivers/clk') diff --git a/drivers/clk/at91/Makefile b/drivers/clk/at91/Makefile index 1d59531e0c..b5529065b5 100644 --- a/drivers/clk/at91/Makefile +++ b/drivers/clk/at91/Makefile @@ -4,6 +4,8 @@ ifdef CONFIG_CLK_CCF obj-y += pmc.o sckc.o clk-main.o clk-master.o clk-programmable.o clk-system.o +obj-y += clk-peripheral.o + obj-$(CONFIG_AT91_UTMI) += clk-utmi.o obj-$(CONFIG_AT91_SAM9X60_PLL) += clk-sam9x60-pll.o else diff --git a/drivers/clk/at91/clk-peripheral.c b/drivers/clk/at91/clk-peripheral.c new file mode 100644 index 0000000000..52cbc520ce --- /dev/null +++ b/drivers/clk/at91/clk-peripheral.c @@ -0,0 +1,254 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Peripheral clock support for AT91 architectures. + * + * Copyright (C) 2020 Microchip Technology Inc. and its subsidiaries + * + * Author: Claudiu Beznea + * + * Based on drivers/clk/at91/clk-peripheral.c from Linux. + */ +#include +#include +#include +#include +#include +#include + +#include "pmc.h" + +#define UBOOT_DM_CLK_AT91_PERIPH "at91-periph-clk" +#define UBOOT_DM_CLK_AT91_SAM9X5_PERIPH "at91-sam9x5-periph-clk" + +#define PERIPHERAL_ID_MIN 2 +#define PERIPHERAL_ID_MAX 31 +#define PERIPHERAL_MASK(id) (1 << ((id) & PERIPHERAL_ID_MAX)) + +#define PERIPHERAL_MAX_SHIFT 3 + +struct clk_peripheral { + void __iomem *base; + struct clk clk; + u32 id; +}; + +#define to_clk_peripheral(_c) container_of(_c, struct clk_peripheral, clk) + +struct clk_sam9x5_peripheral { + const struct clk_pcr_layout *layout; + void __iomem *base; + struct clk clk; + struct clk_range range; + u32 id; + u32 div; + bool auto_div; +}; + +#define to_clk_sam9x5_peripheral(_c) \ + container_of(_c, struct clk_sam9x5_peripheral, clk) + +static int clk_peripheral_enable(struct clk *clk) +{ + struct clk_peripheral *periph = to_clk_peripheral(clk); + int offset = AT91_PMC_PCER; + u32 id = periph->id; + + if (id < PERIPHERAL_ID_MIN) + return 0; + if (id > PERIPHERAL_ID_MAX) + offset = AT91_PMC_PCER1; + pmc_write(periph->base, offset, PERIPHERAL_MASK(id)); + + return 0; +} + +static int clk_peripheral_disable(struct clk *clk) +{ + struct clk_peripheral *periph = to_clk_peripheral(clk); + int offset = AT91_PMC_PCDR; + u32 id = periph->id; + + if (id < PERIPHERAL_ID_MIN) + return -EINVAL; + + if (id > PERIPHERAL_ID_MAX) + offset = AT91_PMC_PCDR1; + pmc_write(periph->base, offset, PERIPHERAL_MASK(id)); + + return 0; +} + +static const struct clk_ops peripheral_ops = { + .enable = clk_peripheral_enable, + .disable = clk_peripheral_disable, + .get_rate = clk_generic_get_rate, +}; + +struct clk * +at91_clk_register_peripheral(void __iomem *base, const char *name, + const char *parent_name, u32 id) +{ + struct clk_peripheral *periph; + struct clk *clk; + int ret; + + if (!base || !name || !parent_name || id > PERIPHERAL_ID_MAX) + return ERR_PTR(-EINVAL); + + periph = kzalloc(sizeof(*periph), GFP_KERNEL); + if (!periph) + return ERR_PTR(-ENOMEM); + + periph->id = id; + periph->base = base; + + clk = &periph->clk; + clk->flags = CLK_GET_RATE_NOCACHE; + ret = clk_register(clk, UBOOT_DM_CLK_AT91_PERIPH, name, parent_name); + if (ret) { + kfree(periph); + clk = ERR_PTR(ret); + } + + return clk; +} + +U_BOOT_DRIVER(at91_periph_clk) = { + .name = UBOOT_DM_CLK_AT91_PERIPH, + .id = UCLASS_CLK, + .ops = &peripheral_ops, + .flags = DM_FLAG_PRE_RELOC, +}; + +static int clk_sam9x5_peripheral_enable(struct clk *clk) +{ + struct clk_sam9x5_peripheral *periph = to_clk_sam9x5_peripheral(clk); + + if (periph->id < PERIPHERAL_ID_MIN) + return 0; + + pmc_write(periph->base, periph->layout->offset, + (periph->id & periph->layout->pid_mask)); + pmc_update_bits(periph->base, periph->layout->offset, + periph->layout->cmd | AT91_PMC_PCR_EN, + periph->layout->cmd | AT91_PMC_PCR_EN); + + return 0; +} + +static int clk_sam9x5_peripheral_disable(struct clk *clk) +{ + struct clk_sam9x5_peripheral *periph = to_clk_sam9x5_peripheral(clk); + + if (periph->id < PERIPHERAL_ID_MIN) + return -EINVAL; + + pmc_write(periph->base, periph->layout->offset, + (periph->id & periph->layout->pid_mask)); + pmc_update_bits(periph->base, periph->layout->offset, + AT91_PMC_PCR_EN | periph->layout->cmd, + periph->layout->cmd); + + return 0; +} + +static ulong clk_sam9x5_peripheral_get_rate(struct clk *clk) +{ + struct clk_sam9x5_peripheral *periph = to_clk_sam9x5_peripheral(clk); + ulong parent_rate = clk_get_parent_rate(clk); + u32 val, shift = ffs(periph->layout->div_mask) - 1; + + if (!parent_rate) + return 0; + + pmc_write(periph->base, periph->layout->offset, + (periph->id & periph->layout->pid_mask)); + pmc_read(periph->base, periph->layout->offset, &val); + shift = (val & periph->layout->div_mask) >> shift; + + return parent_rate >> shift; +} + +static ulong clk_sam9x5_peripheral_set_rate(struct clk *clk, ulong rate) +{ + struct clk_sam9x5_peripheral *periph = to_clk_sam9x5_peripheral(clk); + ulong parent_rate = clk_get_parent_rate(clk); + int shift; + + if (!parent_rate) + return 0; + + if (periph->id < PERIPHERAL_ID_MIN || !periph->range.max) { + if (parent_rate == rate) + return rate; + else + return 0; + } + + if (periph->range.max && rate > periph->range.max) + return 0; + + for (shift = 0; shift <= PERIPHERAL_MAX_SHIFT; shift++) { + if (parent_rate >> shift <= rate) + break; + } + if (shift == PERIPHERAL_MAX_SHIFT + 1) + return 0; + + pmc_write(periph->base, periph->layout->offset, + (periph->id & periph->layout->pid_mask)); + pmc_update_bits(periph->base, periph->layout->offset, + periph->layout->div_mask | periph->layout->cmd, + (shift << (ffs(periph->layout->div_mask) - 1)) | + periph->layout->cmd); + + return parent_rate >> shift; +} + +static const struct clk_ops sam9x5_peripheral_ops = { + .enable = clk_sam9x5_peripheral_enable, + .disable = clk_sam9x5_peripheral_disable, + .get_rate = clk_sam9x5_peripheral_get_rate, + .set_rate = clk_sam9x5_peripheral_set_rate, +}; + +struct clk * +at91_clk_register_sam9x5_peripheral(void __iomem *base, + const struct clk_pcr_layout *layout, + const char *name, const char *parent_name, + u32 id, const struct clk_range *range) +{ + struct clk_sam9x5_peripheral *periph; + struct clk *clk; + int ret; + + if (!base || !layout || !name || !parent_name || !range) + return ERR_PTR(-EINVAL); + + periph = kzalloc(sizeof(*periph), GFP_KERNEL); + if (!periph) + return ERR_PTR(-ENOMEM); + + periph->id = id; + periph->base = base; + periph->layout = layout; + periph->range = *range; + + clk = &periph->clk; + clk->flags = CLK_GET_RATE_NOCACHE; + ret = clk_register(clk, UBOOT_DM_CLK_AT91_SAM9X5_PERIPH, name, + parent_name); + if (ret) { + kfree(periph); + clk = ERR_PTR(ret); + } + + return clk; +} + +U_BOOT_DRIVER(at91_sam9x5_periph_clk) = { + .name = UBOOT_DM_CLK_AT91_SAM9X5_PERIPH, + .id = UCLASS_CLK, + .ops = &sam9x5_peripheral_ops, + .flags = DM_FLAG_PRE_RELOC, +}; diff --git a/drivers/clk/at91/pmc.h b/drivers/clk/at91/pmc.h index c372f39fc9..5e80f353e6 100644 --- a/drivers/clk/at91/pmc.h +++ b/drivers/clk/at91/pmc.h @@ -63,6 +63,14 @@ struct clk_programmable_layout { u8 is_pres_direct; }; +struct clk_pcr_layout { + u32 offset; + u32 cmd; + u32 div_mask; + u32 gckcss_mask; + u32 pid_mask; +}; + extern const struct clk_programmable_layout at91rm9200_programmable_layout; extern const struct clk_programmable_layout at91sam9g45_programmable_layout; extern const struct clk_programmable_layout at91sam9x5_programmable_layout; @@ -111,6 +119,14 @@ at91_clk_register_programmable(void __iomem *base, const char *name, struct clk * at91_clk_register_system(void __iomem *base, const char *name, const char *parent_name, u8 id); +struct clk * +at91_clk_register_peripheral(void __iomem *base, const char *name, + const char *parent_name, u32 id); +struct clk * +at91_clk_register_sam9x5_peripheral(void __iomem *base, + const struct clk_pcr_layout *layout, + const char *name, const char *parent_name, + u32 id, const struct clk_range *range); int at91_clk_mux_val_to_index(const u32 *table, u32 num_parents, u32 val); int at91_clk_mux_index_to_val(const u32 *table, u32 num_parents, u32 index); -- cgit v1.2.3 From 36a9630fcbb0bfb2d3fc6c04ae9de666846ced32 Mon Sep 17 00:00:00 2001 From: Claudiu Beznea Date: Mon, 7 Sep 2020 17:46:50 +0300 Subject: clk: at91: clk-generic: add driver compatible with ccf Add clk-generic driver compatible with common clock framework. Signed-off-by: Claudiu Beznea --- drivers/clk/at91/Makefile | 1 + drivers/clk/at91/clk-generic.c | 202 +++++++++++++++++++++++++++++++++++++++++ drivers/clk/at91/pmc.h | 6 ++ 3 files changed, 209 insertions(+) create mode 100644 drivers/clk/at91/clk-generic.c (limited to 'drivers/clk') diff --git a/drivers/clk/at91/Makefile b/drivers/clk/at91/Makefile index b5529065b5..2cd840af38 100644 --- a/drivers/clk/at91/Makefile +++ b/drivers/clk/at91/Makefile @@ -6,6 +6,7 @@ ifdef CONFIG_CLK_CCF obj-y += pmc.o sckc.o clk-main.o clk-master.o clk-programmable.o clk-system.o obj-y += clk-peripheral.o +obj-$(CONFIG_AT91_GENERIC_CLK) += clk-generic.o obj-$(CONFIG_AT91_UTMI) += clk-utmi.o obj-$(CONFIG_AT91_SAM9X60_PLL) += clk-sam9x60-pll.o else diff --git a/drivers/clk/at91/clk-generic.c b/drivers/clk/at91/clk-generic.c new file mode 100644 index 0000000000..87738b7b5b --- /dev/null +++ b/drivers/clk/at91/clk-generic.c @@ -0,0 +1,202 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Generic clock support for AT91 architectures. + * + * Copyright (C) 2020 Microchip Technology Inc. and its subsidiaries + * + * Author: Claudiu Beznea + * + * Based on drivers/clk/at91/clk-generated.c from Linux. + */ +#include +#include +#include +#include +#include +#include + +#include "pmc.h" + +#define UBOOT_DM_CLK_AT91_GCK "at91-gck-clk" + +#define GENERATED_MAX_DIV 255 + +struct clk_gck { + void __iomem *base; + const u32 *clk_mux_table; + const u32 *mux_table; + const struct clk_pcr_layout *layout; + struct clk_range range; + struct clk clk; + u32 num_parents; + u32 id; +}; + +#define to_clk_gck(_c) container_of(_c, struct clk_gck, clk) + +static int clk_gck_enable(struct clk *clk) +{ + struct clk_gck *gck = to_clk_gck(clk); + + pmc_write(gck->base, gck->layout->offset, + (gck->id & gck->layout->pid_mask)); + pmc_update_bits(gck->base, gck->layout->offset, + gck->layout->cmd | AT91_PMC_PCR_GCKEN, + gck->layout->cmd | AT91_PMC_PCR_GCKEN); + + return 0; +} + +static int clk_gck_disable(struct clk *clk) +{ + struct clk_gck *gck = to_clk_gck(clk); + + pmc_write(gck->base, gck->layout->offset, + (gck->id & gck->layout->pid_mask)); + pmc_update_bits(gck->base, gck->layout->offset, + gck->layout->cmd | AT91_PMC_PCR_GCKEN, + gck->layout->cmd); + + return 0; +} + +static int clk_gck_set_parent(struct clk *clk, struct clk *parent) +{ + struct clk_gck *gck = to_clk_gck(clk); + int index; + + index = at91_clk_mux_val_to_index(gck->clk_mux_table, gck->num_parents, + parent->id); + if (index < 0) + return index; + + index = at91_clk_mux_index_to_val(gck->mux_table, gck->num_parents, + index); + if (index < 0) + return index; + + pmc_write(gck->base, gck->layout->offset, + (gck->id & gck->layout->pid_mask)); + pmc_update_bits(gck->base, gck->layout->offset, + gck->layout->gckcss_mask | gck->layout->cmd, + (index << (ffs(gck->layout->gckcss_mask) - 1)) | + gck->layout->cmd); + + return 0; +} + +static ulong clk_gck_set_rate(struct clk *clk, ulong rate) +{ + struct clk_gck *gck = to_clk_gck(clk); + ulong parent_rate = clk_get_parent_rate(clk); + u32 div; + + if (!rate || !parent_rate) + return 0; + + if (gck->range.max && rate > gck->range.max) + return 0; + + div = DIV_ROUND_CLOSEST(parent_rate, rate); + if (div > GENERATED_MAX_DIV + 1 || !div) + return 0; + + pmc_write(gck->base, gck->layout->offset, + (gck->id & gck->layout->pid_mask)); + pmc_update_bits(gck->base, gck->layout->offset, + AT91_PMC_PCR_GCKDIV_MASK | gck->layout->cmd, + ((div - 1) << (ffs(AT91_PMC_PCR_GCKDIV_MASK) - 1)) | + gck->layout->cmd); + + return parent_rate / div; +} + +static ulong clk_gck_get_rate(struct clk *clk) +{ + struct clk_gck *gck = to_clk_gck(clk); + ulong parent_rate = clk_get_parent_rate(clk); + u32 val, div; + + if (!parent_rate) + return 0; + + pmc_write(gck->base, gck->layout->offset, + (gck->id & gck->layout->pid_mask)); + pmc_read(gck->base, gck->layout->offset, &val); + + div = (val & AT91_PMC_PCR_GCKDIV_MASK) >> + (ffs(AT91_PMC_PCR_GCKDIV_MASK) - 1); + + return parent_rate / (div + 1); +} + +static const struct clk_ops gck_ops = { + .enable = clk_gck_enable, + .disable = clk_gck_disable, + .set_parent = clk_gck_set_parent, + .set_rate = clk_gck_set_rate, + .get_rate = clk_gck_get_rate, +}; + +struct clk * +at91_clk_register_generic(void __iomem *base, + const struct clk_pcr_layout *layout, + const char *name, const char * const *parent_names, + const u32 *clk_mux_table, const u32 *mux_table, + u8 num_parents, u8 id, + const struct clk_range *range) +{ + struct clk_gck *gck; + struct clk *clk; + int ret, index; + u32 val; + + if (!base || !layout || !name || !parent_names || !num_parents || + !clk_mux_table || !mux_table || !range) + return ERR_PTR(-EINVAL); + + gck = kzalloc(sizeof(*gck), GFP_KERNEL); + if (!gck) + return ERR_PTR(-ENOMEM); + + gck->id = id; + gck->base = base; + gck->range = *range; + gck->layout = layout; + gck->clk_mux_table = clk_mux_table; + gck->mux_table = mux_table; + gck->num_parents = num_parents; + + clk = &gck->clk; + clk->flags = CLK_GET_RATE_NOCACHE; + + pmc_write(gck->base, gck->layout->offset, + (gck->id & gck->layout->pid_mask)); + pmc_read(gck->base, gck->layout->offset, &val); + + val = (val & gck->layout->gckcss_mask) >> + (ffs(gck->layout->gckcss_mask) - 1); + + index = at91_clk_mux_val_to_index(gck->mux_table, gck->num_parents, + val); + if (index < 0) { + kfree(gck); + return ERR_PTR(index); + } + + ret = clk_register(clk, UBOOT_DM_CLK_AT91_GCK, name, + parent_names[index]); + if (ret) { + kfree(gck); + clk = ERR_PTR(ret); + } + + return clk; +} + +U_BOOT_DRIVER(at91_gck_clk) = { + .name = UBOOT_DM_CLK_AT91_GCK, + .id = UCLASS_CLK, + .ops = &gck_ops, + .flags = DM_FLAG_PRE_RELOC, +}; diff --git a/drivers/clk/at91/pmc.h b/drivers/clk/at91/pmc.h index 5e80f353e6..176855e7a0 100644 --- a/drivers/clk/at91/pmc.h +++ b/drivers/clk/at91/pmc.h @@ -127,6 +127,12 @@ at91_clk_register_sam9x5_peripheral(void __iomem *base, const struct clk_pcr_layout *layout, const char *name, const char *parent_name, u32 id, const struct clk_range *range); +struct clk * +at91_clk_register_generic(void __iomem *base, + const struct clk_pcr_layout *layout, const char *name, + const char * const *parent_names, + const u32 *clk_mux_table, const u32 *mux_table, + u8 num_parents, u8 id, const struct clk_range *range); int at91_clk_mux_val_to_index(const u32 *table, u32 num_parents, u32 val); int at91_clk_mux_index_to_val(const u32 *table, u32 num_parents, u32 index); -- cgit v1.2.3 From 7b7e226739104347a69d88dc3414c16e5fb1fe9d Mon Sep 17 00:00:00 2001 From: Claudiu Beznea Date: Mon, 7 Sep 2020 17:46:51 +0300 Subject: clk: at91: pmc: add generic clock ops Add generic clock ops to be used by every AT91 PMC driver built on top of CCF. Signed-off-by: Claudiu Beznea --- drivers/clk/at91/pmc.c | 71 ++++++++++++++++++++++++++++++++++++++++++++++++++ drivers/clk/at91/pmc.h | 2 ++ 2 files changed, 73 insertions(+) (limited to 'drivers/clk') diff --git a/drivers/clk/at91/pmc.c b/drivers/clk/at91/pmc.c index 29c6452497..660e231921 100644 --- a/drivers/clk/at91/pmc.c +++ b/drivers/clk/at91/pmc.c @@ -5,8 +5,79 @@ */ #include +#include #include +#include "pmc.h" + +static int at91_clk_of_xlate(struct clk *clk, struct ofnode_phandle_args *args) +{ + if (args->args_count != 2) { + debug("AT91: clk: Invalid args_count: %d\n", args->args_count); + return -EINVAL; + } + + clk->id = AT91_TO_CLK_ID(args->args[0], args->args[1]); + + return 0; +} + +static ulong at91_clk_get_rate(struct clk *clk) +{ + struct clk *c; + int ret; + + ret = clk_get_by_id(clk->id, &c); + if (ret) + return ret; + + return clk_get_rate(c); +} + +static ulong at91_clk_set_rate(struct clk *clk, ulong rate) +{ + struct clk *c; + int ret; + + ret = clk_get_by_id(clk->id, &c); + if (ret) + return ret; + + return clk_set_rate(c, rate); +} + +static int at91_clk_enable(struct clk *clk) +{ + struct clk *c; + int ret; + + ret = clk_get_by_id(clk->id, &c); + if (ret) + return ret; + + return clk_enable(c); +} + +static int at91_clk_disable(struct clk *clk) +{ + struct clk *c; + int ret; + + ret = clk_get_by_id(clk->id, &c); + if (ret) + return ret; + + return clk_disable(c); +} + +const struct clk_ops at91_clk_ops = { + .of_xlate = at91_clk_of_xlate, + .set_rate = at91_clk_set_rate, + .get_rate = at91_clk_get_rate, + .enable = at91_clk_enable, + .disable = at91_clk_disable, +}; + /** * pmc_read() - read content at address base + off into val * diff --git a/drivers/clk/at91/pmc.h b/drivers/clk/at91/pmc.h index 176855e7a0..a6a714fd22 100644 --- a/drivers/clk/at91/pmc.h +++ b/drivers/clk/at91/pmc.h @@ -75,6 +75,8 @@ extern const struct clk_programmable_layout at91rm9200_programmable_layout; extern const struct clk_programmable_layout at91sam9g45_programmable_layout; extern const struct clk_programmable_layout at91sam9x5_programmable_layout; +extern const struct clk_ops at91_clk_ops; + struct clk *at91_clk_main_rc(void __iomem *reg, const char *name, const char *parent_name); struct clk *at91_clk_main_osc(void __iomem *reg, const char *name, -- cgit v1.2.3 From 6a6fe3ed4d80afba7efc65a87aca5344672768c5 Mon Sep 17 00:00:00 2001 From: Claudiu Beznea Date: Mon, 7 Sep 2020 17:46:52 +0300 Subject: clk: at91: sama7g5: add clock support Add clock support for SAMA7G5. Signed-off-by: Claudiu Beznea --- drivers/clk/at91/Makefile | 1 + drivers/clk/at91/sama7g5.c | 1401 ++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 1402 insertions(+) create mode 100644 drivers/clk/at91/sama7g5.c (limited to 'drivers/clk') diff --git a/drivers/clk/at91/Makefile b/drivers/clk/at91/Makefile index 2cd840af38..2453c38af1 100644 --- a/drivers/clk/at91/Makefile +++ b/drivers/clk/at91/Makefile @@ -9,6 +9,7 @@ obj-y += clk-peripheral.o obj-$(CONFIG_AT91_GENERIC_CLK) += clk-generic.o obj-$(CONFIG_AT91_UTMI) += clk-utmi.o obj-$(CONFIG_AT91_SAM9X60_PLL) += clk-sam9x60-pll.o +obj-$(CONFIG_SAMA7G5) += sama7g5.o else obj-y += compat.o endif diff --git a/drivers/clk/at91/sama7g5.c b/drivers/clk/at91/sama7g5.c new file mode 100644 index 0000000000..b96937673b --- /dev/null +++ b/drivers/clk/at91/sama7g5.c @@ -0,0 +1,1401 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * SAMA7G5 PMC clock support. + * + * Copyright (C) 2020 Microchip Technology Inc. and its subsidiaries + * + * Author: Claudiu Beznea + * + * Based on drivers/clk/at91/sama7g5.c from Linux. + */ + +#include +#include +#include +#include +#include + +#include "pmc.h" + +/** + * Clock identifiers to be used in conjunction with macros like + * AT91_TO_CLK_ID() + * + * @ID_MD_SLCK: TD slow clock identifier + * @ID_TD_SLCK: MD slow clock identifier + * @ID_MAIN_XTAL: Main Xtal clock identifier + * @ID_MAIN_RC: Main RC clock identifier + * @ID_MAIN_RC_OSC: Main RC Oscillator clock identifier + * @ID_MAIN_OSC: Main Oscillator clock identifier + * @ID_MAINCK: MAINCK clock identifier + * @ID_PLL_CPU_FRAC: CPU PLL fractional clock identifier + * @ID_PLL_CPU_DIV: CPU PLL divider clock identifier + * @ID_PLL_SYS_FRAC: SYS PLL fractional clock identifier + * @ID_PLL_SYS_DIV: SYS PLL divider clock identifier + * @ID_PLL_DDR_FRAC: DDR PLL fractional clock identifier + * @ID_PLL_DDR_DIV: DDR PLL divider clock identifier + * @ID_PLL_IMG_FRAC: IMC PLL fractional clock identifier + * @ID_PLL_IMG_DIV: IMG PLL divider clock identifier + * @ID_PLL_BAUD_FRAC: Baud PLL fractional clock identifier + * @ID_PLL_BAUD_DIV: Baud PLL divider clock identifier + * @ID_PLL_AUDIO_FRAC: Audio PLL fractional clock identifier + * @ID_PLL_AUDIO_DIVPMC: Audio PLL PMC divider clock identifier + * @ID_PLL_AUDIO_DIVIO: Audio PLL IO divider clock identifier + * @ID_PLL_ETH_FRAC: Ethernet PLL fractional clock identifier + * @ID_PLL_ETH_DIV: Ethernet PLL divider clock identifier + + * @ID_MCK0: MCK0 clock identifier + * @ID_MCK1: MCK1 clock identifier + * @ID_MCK2: MCK2 clock identifier + * @ID_MCK3: MCK3 clock identifier + * @ID_MCK4: MCK4 clock identifier + + * @ID_UTMI: UTMI clock identifier + + * @ID_PROG0: Programmable 0 clock identifier + * @ID_PROG1: Programmable 1 clock identifier + * @ID_PROG2: Programmable 2 clock identifier + * @ID_PROG3: Programmable 3 clock identifier + * @ID_PROG4: Programmable 4 clock identifier + * @ID_PROG5: Programmable 5 clock identifier + * @ID_PROG6: Programmable 6 clock identifier + * @ID_PROG7: Programmable 7 clock identifier + + * @ID_PCK0: System clock 0 clock identifier + * @ID_PCK1: System clock 1 clock identifier + * @ID_PCK2: System clock 2 clock identifier + * @ID_PCK3: System clock 3 clock identifier + * @ID_PCK4: System clock 4 clock identifier + * @ID_PCK5: System clock 5 clock identifier + * @ID_PCK6: System clock 6 clock identifier + * @ID_PCK7: System clock 7 clock identifier + */ +enum pmc_clk_ids { + ID_MD_SLCK = 0, + ID_TD_SLCK = 1, + ID_MAIN_XTAL = 2, + ID_MAIN_RC = 3, + ID_MAIN_RC_OSC = 4, + ID_MAIN_OSC = 5, + ID_MAINCK = 6, + + ID_PLL_CPU_FRAC = 7, + ID_PLL_CPU_DIV = 8, + ID_PLL_SYS_FRAC = 9, + ID_PLL_SYS_DIV = 10, + ID_PLL_DDR_FRAC = 11, + ID_PLL_DDR_DIV = 12, + ID_PLL_IMG_FRAC = 13, + ID_PLL_IMG_DIV = 14, + ID_PLL_BAUD_FRAC = 15, + ID_PLL_BAUD_DIV = 16, + ID_PLL_AUDIO_FRAC = 17, + ID_PLL_AUDIO_DIVPMC = 18, + ID_PLL_AUDIO_DIVIO = 19, + ID_PLL_ETH_FRAC = 20, + ID_PLL_ETH_DIV = 21, + + ID_MCK0 = 22, + ID_MCK1 = 23, + ID_MCK2 = 24, + ID_MCK3 = 25, + ID_MCK4 = 26, + + ID_UTMI = 27, + + ID_PROG0 = 28, + ID_PROG1 = 29, + ID_PROG2 = 30, + ID_PROG3 = 31, + ID_PROG4 = 32, + ID_PROG5 = 33, + ID_PROG6 = 34, + ID_PROG7 = 35, + + ID_PCK0 = 36, + ID_PCK1 = 37, + ID_PCK2 = 38, + ID_PCK3 = 39, + ID_PCK4 = 40, + ID_PCK5 = 41, + ID_PCK6 = 42, + ID_PCK7 = 43, + + ID_MAX, +}; + +/** + * PLL type identifiers + * @PLL_TYPE_FRAC: fractional PLL identifier + * @PLL_TYPE_DIV: divider PLL identifier + */ +enum pll_type { + PLL_TYPE_FRAC, + PLL_TYPE_DIV, +}; + +/* Clock names used as parents for multiple clocks. */ +static const char *clk_names[] = { + [ID_MAIN_RC_OSC] = "main_rc_osc", + [ID_MAIN_OSC] = "main_osc", + [ID_MAINCK] = "mainck", + [ID_PLL_CPU_DIV] = "cpupll_divpmcck", + [ID_PLL_SYS_DIV] = "syspll_divpmcck", + [ID_PLL_DDR_DIV] = "ddrpll_divpmcck", + [ID_PLL_IMG_DIV] = "imgpll_divpmcck", + [ID_PLL_BAUD_DIV] = "baudpll_divpmcck", + [ID_PLL_AUDIO_DIVPMC] = "audiopll_divpmcck", + [ID_PLL_AUDIO_DIVIO] = "audiopll_diviock", + [ID_PLL_ETH_DIV] = "ethpll_divpmcck", + [ID_MCK0] = "mck0", +}; + +/* Fractional PLL output range. */ +static const struct clk_range pll_outputs[] = { + { .min = 2343750, .max = 1200000000 }, +}; + +/* PLL characteristics. */ +static const struct clk_pll_characteristics pll_characteristics = { + .input = { .min = 12000000, .max = 50000000 }, + .num_output = ARRAY_SIZE(pll_outputs), + .output = pll_outputs, +}; + +/* Layout for fractional PLLs. */ +static const struct clk_pll_layout pll_layout_frac = { + .mul_mask = GENMASK(31, 24), + .frac_mask = GENMASK(21, 0), + .mul_shift = 24, + .frac_shift = 0, +}; + +/* Layout for DIVPMC dividers. */ +static const struct clk_pll_layout pll_layout_divpmc = { + .div_mask = GENMASK(7, 0), + .endiv_mask = BIT(29), + .div_shift = 0, + .endiv_shift = 29, +}; + +/* Layout for DIVIO dividers. */ +static const struct clk_pll_layout pll_layout_divio = { + .div_mask = GENMASK(19, 12), + .endiv_mask = BIT(30), + .div_shift = 12, + .endiv_shift = 30, +}; + +/* MCK0 characteristics. */ +static const struct clk_master_characteristics mck0_characteristics = { + .output = { .min = 140000000, .max = 200000000 }, + .divisors = { 1, 2, 4, 3 }, + .have_div3_pres = 1, +}; + +/* MCK0 layout. */ +static const struct clk_master_layout mck0_layout = { + .mask = 0x373, + .pres_shift = 4, + .offset = 0x28, +}; + +/* Programmable clock layout. */ +static const struct clk_programmable_layout programmable_layout = { + .pres_mask = 0xff, + .pres_shift = 8, + .css_mask = 0x1f, + .have_slck_mck = 0, + .is_pres_direct = 1, +}; + +/* Peripheral clock layout. */ +static const struct clk_pcr_layout sama7g5_pcr_layout = { + .offset = 0x88, + .cmd = BIT(31), + .gckcss_mask = GENMASK(12, 8), + .pid_mask = GENMASK(6, 0), + .div_mask = GENMASK(15, 14), +}; + +/** + * PLL clocks description + * @n: clock name + * @p: clock parent + * @l: clock layout + * @t: clock type + * @c: true if clock is critical and cannot be disabled + * @id: clock id corresponding to PLL driver + * @cid: clock id corresponding to clock subsystem + */ +static const struct { + const char *n; + const char *p; + const struct clk_pll_layout *l; + u8 t; + u8 c; + u8 id; + u8 cid; +} sama7g5_plls[] = { + { + .n = "cpupll_fracck", + .p = "mainck", + .l = &pll_layout_frac, + .t = PLL_TYPE_FRAC, + .c = 1, + .id = 0, + .cid = ID_PLL_CPU_FRAC, + }, + + { + .n = "cpupll_divpmcck", + .p = "cpupll_fracck", + .l = &pll_layout_divpmc, + .t = PLL_TYPE_DIV, + .c = 1, + .id = 0, + .cid = ID_PLL_CPU_DIV, + }, + + { + .n = "syspll_fracck", + .p = "mainck", + .l = &pll_layout_frac, + .t = PLL_TYPE_FRAC, + .c = 1, + .id = 1, + .cid = ID_PLL_SYS_FRAC, + }, + + { + .n = "syspll_divpmcck", + .p = "syspll_fracck", + .l = &pll_layout_divpmc, + .t = PLL_TYPE_DIV, + .c = 1, + .id = 1, + .cid = ID_PLL_SYS_DIV, + }, + + { + .n = "ddrpll_fracck", + .p = "mainck", + .l = &pll_layout_frac, + .t = PLL_TYPE_FRAC, + .c = 1, + .id = 2, + .cid = ID_PLL_DDR_FRAC, + }, + + { + .n = "ddrpll_divpmcck", + .p = "ddrpll_fracck", + .l = &pll_layout_divpmc, + .t = PLL_TYPE_DIV, + .c = 1, + .id = 2, + .cid = ID_PLL_DDR_DIV, + }, + + { + .n = "imgpll_fracck", + .p = "mainck", + .l = &pll_layout_frac, + .t = PLL_TYPE_FRAC, + .id = 3, + .cid = ID_PLL_IMG_FRAC, + }, + + { + .n = "imgpll_divpmcck", + .p = "imgpll_fracck", + .l = &pll_layout_divpmc, + .t = PLL_TYPE_DIV, + .id = 3, + .cid = ID_PLL_IMG_DIV + }, + + { + .n = "baudpll_fracck", + .p = "mainck", + .l = &pll_layout_frac, + .t = PLL_TYPE_FRAC, + .id = 4, + .cid = ID_PLL_BAUD_FRAC, + }, + + { + .n = "baudpll_divpmcck", + .p = "baudpll_fracck", + .l = &pll_layout_divpmc, + .t = PLL_TYPE_DIV, + .id = 4, + .cid = ID_PLL_BAUD_DIV, + }, + + { + .n = "audiopll_fracck", + .p = "main_osc", + .l = &pll_layout_frac, + .t = PLL_TYPE_FRAC, + .id = 5, + .cid = ID_PLL_AUDIO_FRAC, + }, + + { + .n = "audiopll_divpmcck", + .p = "audiopll_fracck", + .l = &pll_layout_divpmc, + .t = PLL_TYPE_DIV, + .id = 5, + .cid = ID_PLL_AUDIO_DIVPMC, + }, + + { + .n = "audiopll_diviock", + .p = "audiopll_fracck", + .l = &pll_layout_divio, + .t = PLL_TYPE_DIV, + .id = 5, + .cid = ID_PLL_AUDIO_DIVIO, + }, + + { + .n = "ethpll_fracck", + .p = "main_osc", + .l = &pll_layout_frac, + .t = PLL_TYPE_FRAC, + .id = 6, + .cid = ID_PLL_ETH_FRAC, + }, + + { + .n = "ethpll_divpmcck", + .p = "ethpll_fracck", + .l = &pll_layout_divpmc, + .t = PLL_TYPE_DIV, + .id = 6, + .cid = ID_PLL_ETH_DIV, + }, +}; + +/** + * Master clock (MCK[1..4]) description + * @n: clock name + * @ep: extra parents names array + * @ep_chg_chg_id: index in parents array that specifies the changeable + * parent + * @ep_count: extra parents count + * @ep_mux_table: mux table for extra parents + * @ep_clk_mux_table: mux table to deal with subsystem clock ids + * @id: clock id corresponding to MCK driver + * @cid: clock id corresponding to clock subsystem + * @c: true if clock is critical and cannot be disabled + */ +static const struct { + const char *n; + const char *ep[4]; + u8 ep_count; + u8 ep_mux_table[4]; + u8 ep_clk_mux_table[4]; + u8 id; + u8 cid; + u8 c; +} sama7g5_mckx[] = { + { + .n = "mck1", + .id = 1, + .cid = ID_MCK1, + .ep = { "syspll_divpmcck", }, + .ep_mux_table = { 5, }, + .ep_clk_mux_table = { ID_PLL_SYS_DIV, }, + .ep_count = 1, + .c = 1, + }, + + { + .n = "mck2", + .id = 2, + .cid = ID_MCK2, + .ep = { "ddrpll_divpmcck", }, + .ep_mux_table = { 6, }, + .ep_clk_mux_table = { ID_PLL_DDR_DIV, }, + .ep_count = 1, + .c = 1, + }, + + { + .n = "mck3", + .id = 3, + .cid = ID_MCK3, + .ep = { "syspll_divpmcck", "ddrpll_divpmcck", "imgpll_divpmcck", }, + .ep_mux_table = { 5, 6, 7, }, + .ep_clk_mux_table = { ID_PLL_SYS_DIV, ID_PLL_DDR_DIV, ID_PLL_IMG_DIV, }, + .ep_count = 3, + }, + + { + .n = "mck4", + .id = 4, + .cid = ID_MCK4, + .ep = { "syspll_divpmcck", }, + .ep_mux_table = { 5, }, + .ep_clk_mux_table = { ID_PLL_SYS_DIV, }, + .ep_count = 1, + .c = 1, + }, +}; + +/** + * Programmable clock description + * @n: clock name + * @cid: clock id corresponding to clock subsystem + */ +static const struct { + const char *n; + u8 cid; +} sama7g5_prog[] = { + { .n = "prog0", .cid = ID_PROG0, }, + { .n = "prog1", .cid = ID_PROG1, }, + { .n = "prog2", .cid = ID_PROG2, }, + { .n = "prog3", .cid = ID_PROG3, }, + { .n = "prog4", .cid = ID_PROG4, }, + { .n = "prog5", .cid = ID_PROG5, }, + { .n = "prog6", .cid = ID_PROG6, }, + { .n = "prog7", .cid = ID_PROG7, }, +}; + +/* Mux table for programmable clocks. */ +static u32 sama7g5_prog_mux_table[] = { 0, 1, 2, 3, 5, 6, 7, 8, 9, 10, }; + +/** + * System clock description + * @n: clock name + * @p: parent clock name + * @id: clock id corresponding to system clock driver + * @cid: clock id corresponding to clock subsystem + */ +static const struct { + const char *n; + const char *p; + u8 id; + u8 cid; +} sama7g5_systemck[] = { + { .n = "pck0", .p = "prog0", .id = 8, .cid = ID_PCK0, }, + { .n = "pck1", .p = "prog1", .id = 9, .cid = ID_PCK1, }, + { .n = "pck2", .p = "prog2", .id = 10, .cid = ID_PCK2, }, + { .n = "pck3", .p = "prog3", .id = 11, .cid = ID_PCK3, }, + { .n = "pck4", .p = "prog4", .id = 12, .cid = ID_PCK4, }, + { .n = "pck5", .p = "prog5", .id = 13, .cid = ID_PCK5, }, + { .n = "pck6", .p = "prog6", .id = 14, .cid = ID_PCK6, }, + { .n = "pck7", .p = "prog7", .id = 15, .cid = ID_PCK7, }, +}; + +/** + * Peripheral clock description + * @n: clock name + * @p: clock parent name + * @r: clock range values + * @id: clock id + */ +static const struct { + const char *n; + const char *p; + struct clk_range r; + u8 id; +} sama7g5_periphck[] = { + { .n = "pioA_clk", .p = "mck0", .id = 11, }, + { .n = "sfr_clk", .p = "mck1", .id = 19, }, + { .n = "hsmc_clk", .p = "mck1", .id = 21, }, + { .n = "xdmac0_clk", .p = "mck1", .id = 22, }, + { .n = "xdmac1_clk", .p = "mck1", .id = 23, }, + { .n = "xdmac2_clk", .p = "mck1", .id = 24, }, + { .n = "acc_clk", .p = "mck1", .id = 25, }, + { .n = "aes_clk", .p = "mck1", .id = 27, }, + { .n = "tzaesbasc_clk", .p = "mck1", .id = 28, }, + { .n = "asrc_clk", .p = "mck1", .id = 30, .r = { .max = 200000000, }, }, + { .n = "cpkcc_clk", .p = "mck0", .id = 32, }, + { .n = "csi_clk", .p = "mck3", .id = 33, .r = { .max = 266000000, }, }, + { .n = "csi2dc_clk", .p = "mck3", .id = 34, .r = { .max = 266000000, }, }, + { .n = "eic_clk", .p = "mck1", .id = 37, }, + { .n = "flex0_clk", .p = "mck1", .id = 38, }, + { .n = "flex1_clk", .p = "mck1", .id = 39, }, + { .n = "flex2_clk", .p = "mck1", .id = 40, }, + { .n = "flex3_clk", .p = "mck1", .id = 41, }, + { .n = "flex4_clk", .p = "mck1", .id = 42, }, + { .n = "flex5_clk", .p = "mck1", .id = 43, }, + { .n = "flex6_clk", .p = "mck1", .id = 44, }, + { .n = "flex7_clk", .p = "mck1", .id = 45, }, + { .n = "flex8_clk", .p = "mck1", .id = 46, }, + { .n = "flex9_clk", .p = "mck1", .id = 47, }, + { .n = "flex10_clk", .p = "mck1", .id = 48, }, + { .n = "flex11_clk", .p = "mck1", .id = 49, }, + { .n = "gmac0_clk", .p = "mck1", .id = 51, }, + { .n = "gmac1_clk", .p = "mck1", .id = 52, }, + { .n = "gmac0_tsu_clk", .p = "mck1", .id = 53, }, + { .n = "gmac1_tsu_clk", .p = "mck1", .id = 54, }, + { .n = "icm_clk", .p = "mck1", .id = 55, }, + { .n = "isc_clk", .p = "mck3", .id = 56, .r = { .max = 266000000, }, }, + { .n = "i2smcc0_clk", .p = "mck1", .id = 57, .r = { .max = 200000000, }, }, + { .n = "i2smcc1_clk", .p = "mck1", .id = 58, .r = { .max = 200000000, }, }, + { .n = "matrix_clk", .p = "mck1", .id = 60, }, + { .n = "mcan0_clk", .p = "mck1", .id = 61, .r = { .max = 200000000, }, }, + { .n = "mcan1_clk", .p = "mck1", .id = 62, .r = { .max = 200000000, }, }, + { .n = "mcan2_clk", .p = "mck1", .id = 63, .r = { .max = 200000000, }, }, + { .n = "mcan3_clk", .p = "mck1", .id = 64, .r = { .max = 200000000, }, }, + { .n = "mcan4_clk", .p = "mck1", .id = 65, .r = { .max = 200000000, }, }, + { .n = "mcan5_clk", .p = "mck1", .id = 66, .r = { .max = 200000000, }, }, + { .n = "pdmc0_clk", .p = "mck1", .id = 68, .r = { .max = 200000000, }, }, + { .n = "pdmc1_clk", .p = "mck1", .id = 69, .r = { .max = 200000000, }, }, + { .n = "pit64b0_clk", .p = "mck1", .id = 70, }, + { .n = "pit64b1_clk", .p = "mck1", .id = 71, }, + { .n = "pit64b2_clk", .p = "mck1", .id = 72, }, + { .n = "pit64b3_clk", .p = "mck1", .id = 73, }, + { .n = "pit64b4_clk", .p = "mck1", .id = 74, }, + { .n = "pit64b5_clk", .p = "mck1", .id = 75, }, + { .n = "pwm_clk", .p = "mck1", .id = 77, }, + { .n = "qspi0_clk", .p = "mck1", .id = 78, }, + { .n = "qspi1_clk", .p = "mck1", .id = 79, }, + { .n = "sdmmc0_clk", .p = "mck1", .id = 80, }, + { .n = "sdmmc1_clk", .p = "mck1", .id = 81, }, + { .n = "sdmmc2_clk", .p = "mck1", .id = 82, }, + { .n = "sha_clk", .p = "mck1", .id = 83, }, + { .n = "spdifrx_clk", .p = "mck1", .id = 84, .r = { .max = 200000000, }, }, + { .n = "spdiftx_clk", .p = "mck1", .id = 85, .r = { .max = 200000000, }, }, + { .n = "ssc0_clk", .p = "mck1", .id = 86, .r = { .max = 200000000, }, }, + { .n = "ssc1_clk", .p = "mck1", .id = 87, .r = { .max = 200000000, }, }, + { .n = "tcb0_ch0_clk", .p = "mck1", .id = 88, .r = { .max = 200000000, }, }, + { .n = "tcb0_ch1_clk", .p = "mck1", .id = 89, .r = { .max = 200000000, }, }, + { .n = "tcb0_ch2_clk", .p = "mck1", .id = 90, .r = { .max = 200000000, }, }, + { .n = "tcb1_ch0_clk", .p = "mck1", .id = 91, .r = { .max = 200000000, }, }, + { .n = "tcb1_ch1_clk", .p = "mck1", .id = 92, .r = { .max = 200000000, }, }, + { .n = "tcb1_ch2_clk", .p = "mck1", .id = 93, .r = { .max = 200000000, }, }, + { .n = "tcpca_clk", .p = "mck1", .id = 94, }, + { .n = "tcpcb_clk", .p = "mck1", .id = 95, }, + { .n = "tdes_clk", .p = "mck1", .id = 96, }, + { .n = "trng_clk", .p = "mck1", .id = 97, }, + { .n = "udphsa_clk", .p = "mck1", .id = 104, }, + { .n = "udphsb_clk", .p = "mck1", .id = 105, }, + { .n = "uhphs_clk", .p = "mck1", .id = 106, }, +}; + +/** + * Generic clock description + * @n: clock name + * @ep: extra parents names + * @ep_mux_table: extra parents mux table + * @ep_clk_mux_table: extra parents clock mux table (for CCF) + * @r: clock output range + * @ep_count: extra parents count + * @id: clock id + */ +static const struct { + const char *n; + const char *ep[8]; + const char ep_mux_table[8]; + const char ep_clk_mux_table[8]; + struct clk_range r; + u8 ep_count; + u8 id; +} sama7g5_gck[] = { + { + .n = "adc_gclk", + .id = 26, + .r = { .max = 100000000, }, + .ep = { "syspll_divpmcck", "imgpll_divpmcck", "audiopll_divpmcck", }, + .ep_mux_table = { 5, 7, 9, }, + .ep_clk_mux_table = { ID_PLL_SYS_DIV, ID_PLL_IMG_DIV, + ID_PLL_AUDIO_DIVPMC, }, + .ep_count = 3, + }, + + { + .n = "asrc_gclk", + .id = 30, + .r = { .max = 200000000 }, + .ep = { "audiopll_divpmcck", }, + .ep_mux_table = { 9, }, + .ep_clk_mux_table = { ID_PLL_AUDIO_DIVPMC, }, + .ep_count = 1, + }, + + { + .n = "csi_gclk", + .id = 33, + .r = { .max = 27000000 }, + .ep = { "ddrpll_divpmcck", "imgpll_divpmcck", }, + .ep_clk_mux_table = { ID_PLL_DDR_DIV, ID_PLL_IMG_DIV, }, + .ep_mux_table = { 6, 7, }, + .ep_count = 2, + }, + + { + .n = "flex0_gclk", + .id = 38, + .r = { .max = 200000000 }, + .ep = { "syspll_divpmcck", "baudpll_divpmcck", }, + .ep_mux_table = { 5, 8, }, + .ep_clk_mux_table = { ID_PLL_SYS_DIV, ID_PLL_BAUD_DIV, }, + .ep_count = 2, + }, + + { + .n = "flex1_gclk", + .id = 39, + .r = { .max = 200000000 }, + .ep = { "syspll_divpmcck", "baudpll_divpmcck", }, + .ep_mux_table = { 5, 8, }, + .ep_clk_mux_table = { ID_PLL_SYS_DIV, ID_PLL_BAUD_DIV, }, + .ep_count = 2, + }, + + { + .n = "flex2_gclk", + .id = 40, + .r = { .max = 200000000 }, + .ep = { "syspll_divpmcck", "baudpll_divpmcck", }, + .ep_mux_table = { 5, 8, }, + .ep_clk_mux_table = { ID_PLL_SYS_DIV, ID_PLL_BAUD_DIV, }, + .ep_count = 2, + }, + + { + .n = "flex3_gclk", + .id = 41, + .r = { .max = 200000000 }, + .ep = { "syspll_divpmcck", "baudpll_divpmcck", }, + .ep_mux_table = { 5, 8, }, + .ep_clk_mux_table = { ID_PLL_SYS_DIV, ID_PLL_BAUD_DIV, }, + .ep_count = 2, + }, + + { + .n = "flex4_gclk", + .id = 42, + .r = { .max = 200000000 }, + .ep = { "syspll_divpmcck", "baudpll_divpmcck", }, + .ep_mux_table = { 5, 8, }, + .ep_clk_mux_table = { ID_PLL_SYS_DIV, ID_PLL_BAUD_DIV, }, + .ep_count = 2, + }, + + { + .n = "flex5_gclk", + .id = 43, + .r = { .max = 200000000 }, + .ep = { "syspll_divpmcck", "baudpll_divpmcck", }, + .ep_mux_table = { 5, 8, }, + .ep_clk_mux_table = { ID_PLL_SYS_DIV, ID_PLL_BAUD_DIV, }, + .ep_count = 2, + }, + + { + .n = "flex6_gclk", + .id = 44, + .r = { .max = 200000000 }, + .ep = { "syspll_divpmcck", "baudpll_divpmcck", }, + .ep_mux_table = { 5, 8, }, + .ep_clk_mux_table = { ID_PLL_SYS_DIV, ID_PLL_BAUD_DIV, }, + .ep_count = 2, + }, + + { + .n = "flex7_gclk", + .id = 45, + .r = { .max = 200000000 }, + .ep = { "syspll_divpmcck", "baudpll_divpmcck", }, + .ep_mux_table = { 5, 8, }, + .ep_clk_mux_table = { ID_PLL_SYS_DIV, ID_PLL_BAUD_DIV, }, + .ep_count = 2, + }, + + { + .n = "flex8_gclk", + .id = 46, + .r = { .max = 200000000 }, + .ep = { "syspll_divpmcck", "baudpll_divpmcck", }, + .ep_mux_table = { 5, 8, }, + .ep_clk_mux_table = { ID_PLL_SYS_DIV, ID_PLL_BAUD_DIV, }, + .ep_count = 2, + }, + + { + .n = "flex9_gclk", + .id = 47, + .r = { .max = 200000000 }, + .ep = { "syspll_divpmcck", "baudpll_divpmcck", }, + .ep_mux_table = { 5, 8, }, + .ep_clk_mux_table = { ID_PLL_SYS_DIV, ID_PLL_BAUD_DIV, }, + .ep_count = 2, + }, + + { + .n = "flex10_gclk", + .id = 48, + .r = { .max = 200000000 }, + .ep = { "syspll_divpmcck", "baudpll_divpmcck", }, + .ep_mux_table = { 5, 8, }, + .ep_clk_mux_table = { ID_PLL_SYS_DIV, ID_PLL_BAUD_DIV, }, + .ep_count = 2, + }, + + { + .n = "flex11_gclk", + .id = 49, + .r = { .max = 200000000 }, + .ep = { "syspll_divpmcck", "baudpll_divpmcck", }, + .ep_mux_table = { 5, 8, }, + .ep_clk_mux_table = { ID_PLL_SYS_DIV, ID_PLL_BAUD_DIV, }, + .ep_count = 2, + }, + + { + .n = "gmac0_gclk", + .id = 51, + .r = { .max = 125000000 }, + .ep = { "ethpll_divpmcck", }, + .ep_clk_mux_table = { ID_PLL_ETH_DIV, }, + .ep_mux_table = { 10, }, + .ep_count = 1, + }, + + { + .n = "gmac1_gclk", + .id = 52, + .r = { .max = 50000000 }, + .ep = { "ethpll_divpmcck", }, + .ep_mux_table = { 10, }, + .ep_clk_mux_table = { ID_PLL_ETH_DIV, }, + .ep_count = 1, + }, + + { + .n = "gmac0_tsu_gclk", + .id = 53, + .r = { .max = 300000000 }, + .ep = { "audiopll_divpmcck", "ethpll_divpmcck", }, + .ep_mux_table = { 9, 10, }, + .ep_clk_mux_table = { ID_PLL_AUDIO_DIVPMC, ID_PLL_ETH_DIV, }, + .ep_count = 2, + }, + + { + .n = "gmac1_tsu_gclk", + .id = 54, + .r = { .max = 300000000 }, + .ep = { "audiopll_divpmcck", "ethpll_divpmcck", }, + .ep_mux_table = { 9, 10, }, + .ep_clk_mux_table = { ID_PLL_AUDIO_DIVPMC, ID_PLL_ETH_DIV, }, + .ep_count = 2, + }, + + { + .n = "i2smcc0_gclk", + .id = 57, + .r = { .max = 100000000 }, + .ep = { "syspll_divpmcck", "audiopll_divpmcck", }, + .ep_mux_table = { 5, 9, }, + .ep_clk_mux_table = { ID_PLL_SYS_DIV, ID_PLL_AUDIO_DIVPMC, }, + .ep_count = 2, + }, + + { + .n = "i2smcc1_gclk", + .id = 58, + .r = { .max = 100000000 }, + .ep = { "syspll_divpmcck", "audiopll_divpmcck", }, + .ep_mux_table = { 5, 9, }, + .ep_clk_mux_table = { ID_PLL_SYS_DIV, ID_PLL_AUDIO_DIVPMC, }, + .ep_count = 2, + }, + + { + .n = "mcan0_gclk", + .id = 61, + .r = { .max = 200000000 }, + .ep = { "syspll_divpmcck", "baudpll_divpmcck", }, + .ep_mux_table = { 5, 8, }, + .ep_clk_mux_table = { ID_PLL_SYS_DIV, ID_PLL_BAUD_DIV, }, + .ep_count = 2, + }, + + { + .n = "mcan1_gclk", + .id = 62, + .r = { .max = 200000000 }, + .ep = { "syspll_divpmcck", "baudpll_divpmcck", }, + .ep_mux_table = { 5, 8, }, + .ep_clk_mux_table = { ID_PLL_SYS_DIV, ID_PLL_BAUD_DIV, }, + .ep_count = 2, + }, + + { + .n = "mcan2_gclk", + .id = 63, + .r = { .max = 200000000 }, + .ep = { "syspll_divpmcck", "baudpll_divpmcck", }, + .ep_mux_table = { 5, 8, }, + .ep_clk_mux_table = { ID_PLL_SYS_DIV, ID_PLL_BAUD_DIV, }, + .ep_count = 2, + }, + + { + .n = "mcan3_gclk", + .id = 64, + .r = { .max = 200000000 }, + .ep = { "syspll_divpmcck", "baudpll_divpmcck", }, + .ep_mux_table = { 5, 8, }, + .ep_clk_mux_table = { ID_PLL_SYS_DIV, ID_PLL_BAUD_DIV, }, + .ep_count = 2, + }, + + { + .n = "mcan4_gclk", + .id = 65, + .r = { .max = 200000000 }, + .ep = { "syspll_divpmcck", "baudpll_divpmcck", }, + .ep_mux_table = { 5, 8, }, + .ep_clk_mux_table = { ID_PLL_SYS_DIV, ID_PLL_BAUD_DIV, }, + .ep_count = 2, + }, + + { + .n = "mcan5_gclk", + .id = 66, + .r = { .max = 200000000 }, + .ep = { "syspll_divpmcck", "baudpll_divpmcck", }, + .ep_mux_table = { 5, 8, }, + .ep_clk_mux_table = { ID_PLL_SYS_DIV, ID_PLL_BAUD_DIV, }, + .ep_count = 2, + }, + + { + .n = "pdmc0_gclk", + .id = 68, + .r = { .max = 50000000 }, + .ep = { "syspll_divpmcck", "baudpll_divpmcck", }, + .ep_mux_table = { 5, 8, }, + .ep_clk_mux_table = { ID_PLL_SYS_DIV, ID_PLL_BAUD_DIV, }, + .ep_count = 2, + }, + + { + .n = "pdmc1_gclk", + .id = 69, + .r = { .max = 50000000, }, + .ep = { "syspll_divpmcck", "baudpll_divpmcck", }, + .ep_mux_table = { 5, 8, }, + .ep_clk_mux_table = { ID_PLL_SYS_DIV, ID_PLL_BAUD_DIV, }, + .ep_count = 2, + }, + + { + .n = "pit64b0_gclk", + .id = 70, + .r = { .max = 200000000 }, + .ep = { "syspll_divpmcck", "imgpll_divpmcck", "baudpll_divpmcck", + "audiopll_divpmcck", "ethpll_divpmcck", }, + .ep_mux_table = { 5, 7, 8, 9, 10, }, + .ep_clk_mux_table = { ID_PLL_SYS_DIV, ID_PLL_IMG_DIV, + ID_PLL_BAUD_DIV, ID_PLL_AUDIO_DIVPMC, + ID_PLL_ETH_DIV, }, + .ep_count = 5, + }, + + { + .n = "pit64b1_gclk", + .id = 71, + .r = { .max = 200000000 }, + .ep = { "syspll_divpmcck", "imgpll_divpmcck", "baudpll_divpmcck", + "audiopll_divpmcck", "ethpll_divpmcck", }, + .ep_mux_table = { 5, 7, 8, 9, 10, }, + .ep_clk_mux_table = { ID_PLL_SYS_DIV, ID_PLL_IMG_DIV, + ID_PLL_BAUD_DIV, ID_PLL_AUDIO_DIVPMC, + ID_PLL_ETH_DIV, }, + .ep_count = 5, + }, + + { + .n = "pit64b2_gclk", + .id = 72, + .r = { .max = 200000000 }, + .ep = { "syspll_divpmcck", "imgpll_divpmcck", "baudpll_divpmcck", + "audiopll_divpmcck", "ethpll_divpmcck", }, + .ep_mux_table = { 5, 7, 8, 9, 10, }, + .ep_clk_mux_table = { ID_PLL_SYS_DIV, ID_PLL_IMG_DIV, + ID_PLL_BAUD_DIV, ID_PLL_AUDIO_DIVPMC, + ID_PLL_ETH_DIV, }, + .ep_count = 5, + }, + + { + .n = "pit64b3_gclk", + .id = 73, + .r = { .max = 200000000 }, + .ep = { "syspll_divpmcck", "imgpll_divpmcck", "baudpll_divpmcck", + "audiopll_divpmcck", "ethpll_divpmcck", }, + .ep_mux_table = { 5, 7, 8, 9, 10, }, + .ep_clk_mux_table = { ID_PLL_SYS_DIV, ID_PLL_IMG_DIV, + ID_PLL_BAUD_DIV, ID_PLL_AUDIO_DIVPMC, + ID_PLL_ETH_DIV, }, + .ep_count = 5, + }, + + { + .n = "pit64b4_gclk", + .id = 74, + .r = { .max = 200000000 }, + .ep = { "syspll_divpmcck", "imgpll_divpmcck", "baudpll_divpmcck", + "audiopll_divpmcck", "ethpll_divpmcck", }, + .ep_mux_table = { 5, 7, 8, 9, 10, }, + .ep_clk_mux_table = { ID_PLL_SYS_DIV, ID_PLL_IMG_DIV, + ID_PLL_BAUD_DIV, ID_PLL_AUDIO_DIVPMC, + ID_PLL_ETH_DIV, }, + .ep_count = 5, + }, + + { + .n = "pit64b5_gclk", + .id = 75, + .r = { .max = 200000000 }, + .ep = { "syspll_divpmcck", "imgpll_divpmcck", "baudpll_divpmcck", + "audiopll_divpmcck", "ethpll_divpmcck", }, + .ep_mux_table = { 5, 7, 8, 9, 10, }, + .ep_clk_mux_table = { ID_PLL_SYS_DIV, ID_PLL_IMG_DIV, + ID_PLL_BAUD_DIV, ID_PLL_AUDIO_DIVPMC, + ID_PLL_ETH_DIV, }, + .ep_count = 5, + }, + + { + .n = "qspi0_gclk", + .id = 78, + .r = { .max = 200000000 }, + .ep = { "syspll_divpmcck", "baudpll_divpmcck", }, + .ep_mux_table = { 5, 8, }, + .ep_clk_mux_table = { ID_PLL_SYS_DIV, ID_PLL_IMG_DIV, + ID_PLL_BAUD_DIV, ID_PLL_AUDIO_DIVPMC, + ID_PLL_ETH_DIV, }, + .ep_count = 2, + }, + + { + .n = "qspi1_gclk", + .id = 79, + .r = { .max = 200000000 }, + .ep = { "syspll_divpmcck", "baudpll_divpmcck", }, + .ep_mux_table = { 5, 8, }, + .ep_clk_mux_table = { ID_PLL_SYS_DIV, ID_PLL_BAUD_DIV, }, + .ep_count = 2, + }, + + { + .n = "sdmmc0_gclk", + .id = 80, + .r = { .max = 208000000 }, + .ep = { "syspll_divpmcck", "baudpll_divpmcck", }, + .ep_mux_table = { 5, 8, }, + .ep_clk_mux_table = { ID_PLL_SYS_DIV, ID_PLL_BAUD_DIV, }, + .ep_count = 2, + }, + + { + .n = "sdmmc1_gclk", + .id = 81, + .r = { .max = 208000000 }, + .ep = { "syspll_divpmcck", "baudpll_divpmcck", }, + .ep_mux_table = { 5, 8, }, + .ep_clk_mux_table = { ID_PLL_SYS_DIV, ID_PLL_BAUD_DIV, }, + .ep_count = 2, + }, + + { + .n = "sdmmc2_gclk", + .id = 82, + .r = { .max = 208000000 }, + .ep = { "syspll_divpmcck", "baudpll_divpmcck", }, + .ep_mux_table = { 5, 8, }, + .ep_clk_mux_table = { ID_PLL_SYS_DIV, ID_PLL_BAUD_DIV, }, + .ep_count = 2, + }, + + { + .n = "spdifrx_gclk", + .id = 84, + .r = { .max = 150000000 }, + .ep = { "syspll_divpmcck", "audiopll_divpmcck", }, + .ep_mux_table = { 5, 9, }, + .ep_clk_mux_table = { ID_PLL_SYS_DIV, ID_PLL_AUDIO_DIVPMC, }, + .ep_count = 2, + }, + + { + .n = "spdiftx_gclk", + .id = 85, + .r = { .max = 25000000 }, + .ep = { "syspll_divpmcck", "audiopll_divpmcck", }, + .ep_mux_table = { 5, 9, }, + .ep_clk_mux_table = { ID_PLL_SYS_DIV, ID_PLL_AUDIO_DIVPMC, }, + .ep_count = 2, + }, + + { + .n = "tcb0_ch0_gclk", + .id = 88, + .r = { .max = 200000000 }, + .ep = { "syspll_divpmcck", "imgpll_divpmcck", "baudpll_divpmcck", + "audiopll_divpmcck", "ethpll_divpmcck", }, + .ep_mux_table = { 5, 7, 8, 9, 10, }, + .ep_clk_mux_table = { ID_PLL_SYS_DIV, ID_PLL_IMG_DIV, + ID_PLL_BAUD_DIV, ID_PLL_AUDIO_DIVPMC, + ID_PLL_ETH_DIV, }, + .ep_count = 5, + }, + + { + .n = "tcb1_ch0_gclk", + .id = 91, + .r = { .max = 200000000 }, + .ep = { "syspll_divpmcck", "imgpll_divpmcck", "baudpll_divpmcck", + "audiopll_divpmcck", "ethpll_divpmcck", }, + .ep_mux_table = { 5, 7, 8, 9, 10, }, + .ep_clk_mux_table = { ID_PLL_SYS_DIV, ID_PLL_IMG_DIV, + ID_PLL_BAUD_DIV, ID_PLL_AUDIO_DIVPMC, + ID_PLL_ETH_DIV, }, + .ep_count = 5, + }, +}; + +/** + * Clock setup description + * @cid: clock id corresponding to clock subsystem + * @pid: parent clock id corresponding to clock subsystem + * @rate: clock rate + * @prate: parent rate + */ +static const struct pmc_clk_setup { + unsigned int cid; + unsigned int pid; + unsigned long rate; + unsigned long prate; +} sama7g5_clk_setup[] = { + { + .cid = AT91_TO_CLK_ID(PMC_TYPE_CORE, ID_PLL_ETH_FRAC), + .rate = 625000000, + }, + + { + .cid = AT91_TO_CLK_ID(PMC_TYPE_CORE, ID_PLL_ETH_DIV), + .rate = 625000000, + }, +}; + +#define SAMA7G5_MAX_MUX_ALLOCS (64) + +#define prepare_mux_table(_allocs, _index, _dst, _src, _num, _label) \ + do { \ + int _i; \ + if ((_index) >= SAMA7G5_MAX_MUX_ALLOCS) { \ + debug("%s(): AT91: MUX: insufficient space\n", \ + __func__); \ + goto _label; \ + } \ + (_dst) = kzalloc(sizeof(*(_dst)) * (_num), GFP_KERNEL); \ + if (!(_dst)) \ + goto _label; \ + (_allocs)[(_index)++] = (_dst); \ + for (_i = 0; _i < (_num); _i++) \ + (_dst)[_i] = (_src)[_i]; \ + } while (0) + +static int sama7g5_clk_probe(struct udevice *dev) +{ + void __iomem *base = (void *)devfdt_get_addr(dev); + unsigned int *clkmuxallocs[SAMA7G5_MAX_MUX_ALLOCS]; + unsigned int *muxallocs[SAMA7G5_MAX_MUX_ALLOCS]; + const char *p[10]; + unsigned int cm[10], m[10], *tmpclkmux, *tmpmux; + struct clk clk, *c, *parent; + bool main_osc_bypass; + int ret, muxallocindex = 0, clkmuxallocindex = 0, i, j; + + if (IS_ERR(base)) + return PTR_ERR(base); + + memset(muxallocs, 0, ARRAY_SIZE(muxallocs)); + memset(clkmuxallocs, 0, ARRAY_SIZE(clkmuxallocs)); + + ret = clk_get_by_index(dev, 0, &clk); + if (ret) + return ret; + ret = clk_get_by_id(clk.id, &c); + if (ret) + return ret; + clk_names[ID_TD_SLCK] = kmemdup(clk_hw_get_name(c), + strlen(clk_hw_get_name(c)) + 1, GFP_KERNEL); + if (!clk_names[ID_TD_SLCK]) + return -ENOMEM; + + ret = clk_get_by_index(dev, 1, &clk); + if (ret) + return ret; + ret = clk_get_by_id(clk.id, &c); + if (ret) + return ret; + clk_names[ID_MD_SLCK] = kmemdup(clk_hw_get_name(c), + strlen(clk_hw_get_name(c)) + 1, GFP_KERNEL); + if (!clk_names[ID_MD_SLCK]) + return -ENOMEM; + + ret = clk_get_by_index(dev, 2, &clk); + if (ret) + return ret; + clk_names[ID_MAIN_XTAL] = kmemdup(clk_hw_get_name(&clk), + strlen(clk_hw_get_name(&clk)) + 1, GFP_KERNEL); + if (!clk_names[ID_MAIN_XTAL]) + return -ENOMEM; + + ret = clk_get_by_index(dev, 3, &clk); + if (ret) + goto fail; + clk_names[ID_MAIN_RC] = kmemdup(clk_hw_get_name(&clk), + strlen(clk_hw_get_name(&clk)) + 1, GFP_KERNEL); + if (ret) + goto fail; + + main_osc_bypass = dev_read_bool(dev, "atmel,main-osc-bypass"); + + /* Register main rc oscillator. */ + clk_dm(AT91_TO_CLK_ID(PMC_TYPE_CORE, ID_MAIN_RC_OSC), + at91_clk_main_rc(base, clk_names[ID_MAIN_RC_OSC], + clk_names[ID_MAIN_RC])); + + /* Register main oscillator. */ + clk_dm(AT91_TO_CLK_ID(PMC_TYPE_CORE, ID_MAIN_OSC), + at91_clk_main_osc(base, clk_names[ID_MAIN_OSC], + clk_names[ID_MAIN_XTAL], main_osc_bypass)); + + /* Register mainck. */ + p[0] = clk_names[ID_MAIN_RC_OSC]; + p[1] = clk_names[ID_MAIN_OSC]; + cm[0] = AT91_TO_CLK_ID(PMC_TYPE_CORE, ID_MAIN_RC_OSC); + cm[1] = AT91_TO_CLK_ID(PMC_TYPE_CORE, ID_MAIN_OSC); + prepare_mux_table(clkmuxallocs, clkmuxallocindex, tmpclkmux, cm, 2, + fail); + clk_dm(AT91_TO_CLK_ID(PMC_TYPE_CORE, ID_MAINCK), + at91_clk_sam9x5_main(base, clk_names[ID_MAINCK], p, + 2, tmpclkmux, PMC_TYPE_CORE)); + + /* Register PLL fracs clocks. */ + for (i = 0; i < ARRAY_SIZE(sama7g5_plls); i++) { + if (sama7g5_plls[i].t != PLL_TYPE_FRAC) + continue; + + clk_dm(AT91_TO_CLK_ID(PMC_TYPE_CORE, sama7g5_plls[i].cid), + sam9x60_clk_register_frac_pll(base, sama7g5_plls[i].n, + sama7g5_plls[i].p, sama7g5_plls[i].id, + &pll_characteristics, sama7g5_plls[i].l, + sama7g5_plls[i].c)); + } + + /* Register PLL div clocks. */ + for (i = 0; i < ARRAY_SIZE(sama7g5_plls); i++) { + if (sama7g5_plls[i].t != PLL_TYPE_DIV) + continue; + + clk_dm(AT91_TO_CLK_ID(PMC_TYPE_CORE, sama7g5_plls[i].cid), + sam9x60_clk_register_div_pll(base, sama7g5_plls[i].n, + sama7g5_plls[i].p, sama7g5_plls[i].id, + &pll_characteristics, sama7g5_plls[i].l, + sama7g5_plls[i].c)); + } + + /* Register MCK0 clock. */ + p[0] = clk_names[ID_MD_SLCK]; + p[1] = clk_names[ID_MAINCK]; + p[2] = clk_names[ID_PLL_CPU_DIV]; + p[3] = clk_names[ID_PLL_SYS_DIV]; + cm[0] = AT91_TO_CLK_ID(PMC_TYPE_CORE, ID_MD_SLCK); + cm[1] = AT91_TO_CLK_ID(PMC_TYPE_CORE, ID_MAINCK); + cm[2] = AT91_TO_CLK_ID(PMC_TYPE_CORE, ID_PLL_CPU_DIV); + cm[3] = AT91_TO_CLK_ID(PMC_TYPE_CORE, ID_PLL_SYS_DIV); + prepare_mux_table(clkmuxallocs, clkmuxallocindex, tmpclkmux, cm, 2, + fail); + clk_dm(AT91_TO_CLK_ID(PMC_TYPE_CORE, ID_MCK0), + at91_clk_register_master(base, clk_names[ID_MCK0], p, + 4, &mck0_layout, &mck0_characteristics, tmpclkmux)); + + /* Register MCK1-4 clocks. */ + p[0] = clk_names[ID_MD_SLCK]; + p[1] = clk_names[ID_TD_SLCK]; + p[2] = clk_names[ID_MAINCK]; + p[3] = clk_names[ID_MCK0]; + m[0] = 0; + m[1] = 1; + m[2] = 2; + m[3] = 3; + cm[0] = AT91_TO_CLK_ID(PMC_TYPE_CORE, ID_MD_SLCK); + cm[1] = AT91_TO_CLK_ID(PMC_TYPE_CORE, ID_TD_SLCK); + cm[2] = AT91_TO_CLK_ID(PMC_TYPE_CORE, ID_MAINCK); + cm[3] = AT91_TO_CLK_ID(PMC_TYPE_CORE, ID_MCK0); + for (i = 0; i < ARRAY_SIZE(sama7g5_mckx); i++) { + for (j = 0; j < sama7g5_mckx[i].ep_count; j++) { + p[4 + j] = sama7g5_mckx[i].ep[j]; + m[4 + j] = sama7g5_mckx[i].ep_mux_table[j]; + cm[4 + j] = AT91_TO_CLK_ID(PMC_TYPE_CORE, + sama7g5_mckx[i].ep_clk_mux_table[j]); + } + + prepare_mux_table(clkmuxallocs, clkmuxallocindex, tmpclkmux, cm, + 4 + sama7g5_mckx[i].ep_count, fail); + prepare_mux_table(muxallocs, muxallocindex, tmpmux, m, + 4 + sama7g5_mckx[i].ep_count, fail); + + clk_dm(AT91_TO_CLK_ID(PMC_TYPE_CORE, sama7g5_mckx[i].cid), + at91_clk_sama7g5_register_master(base, + sama7g5_mckx[i].n, p, 4 + sama7g5_mckx[i].ep_count, + tmpmux, tmpclkmux, sama7g5_mckx[i].c, + sama7g5_mckx[i].id)); + } + + /* Register UTMI clock. */ + clk_dm(AT91_TO_CLK_ID(PMC_TYPE_CORE, ID_UTMI), + at91_clk_sama7g5_register_utmi(base, "utmick", + clk_names[ID_MAIN_XTAL])); + + /* Register programmable clocks. */ + p[0] = clk_names[ID_MD_SLCK]; + p[1] = clk_names[ID_TD_SLCK]; + p[2] = clk_names[ID_MAINCK]; + p[3] = clk_names[ID_MCK0]; + p[4] = clk_names[ID_PLL_SYS_DIV]; + p[5] = clk_names[ID_PLL_DDR_DIV]; + p[6] = clk_names[ID_PLL_IMG_DIV]; + p[7] = clk_names[ID_PLL_BAUD_DIV]; + p[8] = clk_names[ID_PLL_AUDIO_DIVPMC]; + p[9] = clk_names[ID_PLL_ETH_DIV]; + cm[0] = AT91_TO_CLK_ID(PMC_TYPE_CORE, ID_MD_SLCK); + cm[1] = AT91_TO_CLK_ID(PMC_TYPE_CORE, ID_TD_SLCK); + cm[2] = AT91_TO_CLK_ID(PMC_TYPE_CORE, ID_MAINCK); + cm[3] = AT91_TO_CLK_ID(PMC_TYPE_CORE, ID_MCK0); + cm[4] = AT91_TO_CLK_ID(PMC_TYPE_CORE, ID_PLL_SYS_DIV); + cm[5] = AT91_TO_CLK_ID(PMC_TYPE_CORE, ID_PLL_DDR_DIV); + cm[6] = AT91_TO_CLK_ID(PMC_TYPE_CORE, ID_PLL_IMG_DIV); + cm[7] = AT91_TO_CLK_ID(PMC_TYPE_CORE, ID_PLL_BAUD_DIV); + cm[8] = AT91_TO_CLK_ID(PMC_TYPE_CORE, ID_PLL_AUDIO_DIVPMC); + cm[9] = AT91_TO_CLK_ID(PMC_TYPE_CORE, ID_PLL_ETH_DIV); + for (i = 0; i < ARRAY_SIZE(sama7g5_prog); i++) { + prepare_mux_table(clkmuxallocs, clkmuxallocindex, tmpclkmux, cm, + 10, fail); + + clk_dm(AT91_TO_CLK_ID(PMC_TYPE_CORE, sama7g5_prog[i].cid), + at91_clk_register_programmable(base, sama7g5_prog[i].n, + p, 10, i, &programmable_layout, tmpclkmux, + sama7g5_prog_mux_table)); + } + + /* System clocks. */ + for (i = 0; i < ARRAY_SIZE(sama7g5_systemck); i++) { + clk_dm(AT91_TO_CLK_ID(PMC_TYPE_SYSTEM, sama7g5_systemck[i].cid), + at91_clk_register_system(base, sama7g5_systemck[i].n, + sama7g5_systemck[i].p, sama7g5_systemck[i].id)); + } + + /* Peripheral clocks. */ + for (i = 0; i < ARRAY_SIZE(sama7g5_periphck); i++) { + clk_dm(AT91_TO_CLK_ID(PMC_TYPE_PERIPHERAL, + sama7g5_periphck[i].id), + at91_clk_register_sam9x5_peripheral(base, + &sama7g5_pcr_layout, sama7g5_periphck[i].n, + sama7g5_periphck[i].p, sama7g5_periphck[i].id, + &sama7g5_periphck[i].r)); + } + + /* Generic clocks. */ + p[0] = clk_names[ID_MD_SLCK]; + p[1] = clk_names[ID_TD_SLCK]; + p[2] = clk_names[ID_MAINCK]; + p[3] = clk_names[ID_MCK0]; + m[0] = 0; + m[1] = 1; + m[2] = 2; + m[3] = 3; + cm[0] = AT91_TO_CLK_ID(PMC_TYPE_CORE, ID_MD_SLCK); + cm[1] = AT91_TO_CLK_ID(PMC_TYPE_CORE, ID_TD_SLCK); + cm[2] = AT91_TO_CLK_ID(PMC_TYPE_CORE, ID_MAINCK); + cm[3] = AT91_TO_CLK_ID(PMC_TYPE_CORE, ID_MCK0); + for (i = 0; i < ARRAY_SIZE(sama7g5_gck); i++) { + for (j = 0; j < sama7g5_gck[i].ep_count; j++) { + p[4 + j] = sama7g5_gck[i].ep[j]; + m[4 + j] = sama7g5_gck[i].ep_mux_table[j]; + cm[4 + j] = AT91_TO_CLK_ID(PMC_TYPE_CORE, + sama7g5_gck[i].ep_clk_mux_table[j]); + } + + prepare_mux_table(clkmuxallocs, clkmuxallocindex, tmpclkmux, cm, + 4 + sama7g5_gck[i].ep_count, fail); + prepare_mux_table(muxallocs, muxallocindex, tmpmux, m, + 4 + sama7g5_gck[i].ep_count, fail); + + clk_dm(AT91_TO_CLK_ID(PMC_TYPE_GCK, sama7g5_gck[i].id), + at91_clk_register_generic(base, &sama7g5_pcr_layout, + sama7g5_gck[i].n, p, tmpclkmux, tmpmux, + 4 + sama7g5_gck[i].ep_count, sama7g5_gck[i].id, + &sama7g5_gck[i].r)); + } + + /* Setup clocks. */ + for (i = 0; i < ARRAY_SIZE(sama7g5_clk_setup); i++) { + ret = clk_get_by_id(sama7g5_clk_setup[i].cid, &c); + if (ret) + goto fail; + + if (sama7g5_clk_setup[i].pid) { + ret = clk_get_by_id(sama7g5_clk_setup[i].pid, &parent); + if (ret) + goto fail; + + ret = clk_set_parent(c, parent); + if (ret) + goto fail; + + if (sama7g5_clk_setup[i].prate) { + ret = clk_set_rate(parent, + sama7g5_clk_setup[i].prate); + if (ret < 0) + goto fail; + } + } + + if (sama7g5_clk_setup[i].rate) { + ret = clk_set_rate(c, sama7g5_clk_setup[i].rate); + if (ret < 0) + goto fail; + } + } + + return 0; + +fail: + for (i = 0; i < ARRAY_SIZE(muxallocs); i++) + kfree(muxallocs[i]); + + for (i = 0; i < ARRAY_SIZE(clkmuxallocs); i++) + kfree(clkmuxallocs[i]); + + return -ENOMEM; +} + +static const struct udevice_id sama7g5_clk_ids[] = { + { .compatible = "microchip,sama7g5-pmc" }, + { /* Sentinel. */ }, +}; + +U_BOOT_DRIVER(at91_sama7g5_pmc) = { + .name = "at91-sama7g5-pmc", + .id = UCLASS_CLK, + .of_match = sama7g5_clk_ids, + .ops = &at91_clk_ops, + .probe = sama7g5_clk_probe, + .flags = DM_FLAG_PRE_RELOC, +}; -- cgit v1.2.3 From 6c0e59fcd9bfc258c2ec4cad4971352a17a58411 Mon Sep 17 00:00:00 2001 From: Michal Simek Date: Tue, 7 Jan 2020 08:50:34 +0100 Subject: xilinx: drivers: Use '_' instead of '-' in driver name The most of drivers are using '_' instead of '-' in driver name. That's why sync up these names to be aligned. It looks quite bad to see both in use. It is visible via dm tree command. Signed-off-by: Michal Simek Reviewed-by: Simon Glass --- drivers/clk/clk_zynqmp.c | 2 +- drivers/firmware/firmware-zynqmp.c | 2 +- drivers/i2c/i2c-cdns.c | 2 +- drivers/mailbox/zynqmp-ipi.c | 2 +- drivers/mtd/nand/raw/arasan_nfc.c | 2 +- drivers/mtd/nand/raw/zynq_nand.c | 2 +- 6 files changed, 6 insertions(+), 6 deletions(-) (limited to 'drivers/clk') diff --git a/drivers/clk/clk_zynqmp.c b/drivers/clk/clk_zynqmp.c index 2313ac0bc0..7795119756 100644 --- a/drivers/clk/clk_zynqmp.c +++ b/drivers/clk/clk_zynqmp.c @@ -710,7 +710,7 @@ static const struct udevice_id zynqmp_clk_ids[] = { }; U_BOOT_DRIVER(zynqmp_clk) = { - .name = "zynqmp-clk", + .name = "zynqmp_clk", .id = UCLASS_CLK, .of_match = zynqmp_clk_ids, .probe = zynqmp_clk_probe, diff --git a/drivers/firmware/firmware-zynqmp.c b/drivers/firmware/firmware-zynqmp.c index 903a8f5878..7583f24a20 100644 --- a/drivers/firmware/firmware-zynqmp.c +++ b/drivers/firmware/firmware-zynqmp.c @@ -202,6 +202,6 @@ static const struct udevice_id zynqmp_firmware_ids[] = { U_BOOT_DRIVER(zynqmp_firmware) = { .id = UCLASS_FIRMWARE, - .name = "zynqmp-firmware", + .name = "zynqmp_firmware", .of_match = zynqmp_firmware_ids, }; diff --git a/drivers/i2c/i2c-cdns.c b/drivers/i2c/i2c-cdns.c index 024c63c4ea..7144d39984 100644 --- a/drivers/i2c/i2c-cdns.c +++ b/drivers/i2c/i2c-cdns.c @@ -500,7 +500,7 @@ static const struct udevice_id cdns_i2c_of_match[] = { }; U_BOOT_DRIVER(cdns_i2c) = { - .name = "i2c-cdns", + .name = "i2c_cdns", .id = UCLASS_I2C, .of_match = cdns_i2c_of_match, .ofdata_to_platdata = cdns_i2c_ofdata_to_platdata, diff --git a/drivers/mailbox/zynqmp-ipi.c b/drivers/mailbox/zynqmp-ipi.c index 746377e557..9483ed9cef 100644 --- a/drivers/mailbox/zynqmp-ipi.c +++ b/drivers/mailbox/zynqmp-ipi.c @@ -133,7 +133,7 @@ struct mbox_ops zynqmp_ipi_mbox_ops = { }; U_BOOT_DRIVER(zynqmp_ipi) = { - .name = "zynqmp-ipi", + .name = "zynqmp_ipi", .id = UCLASS_MAILBOX, .of_match = zynqmp_ipi_ids, .probe = zynqmp_ipi_probe, diff --git a/drivers/mtd/nand/raw/arasan_nfc.c b/drivers/mtd/nand/raw/arasan_nfc.c index 6c1d64054c..0615e50378 100644 --- a/drivers/mtd/nand/raw/arasan_nfc.c +++ b/drivers/mtd/nand/raw/arasan_nfc.c @@ -1306,7 +1306,7 @@ static const struct udevice_id arasan_nand_dt_ids[] = { }; U_BOOT_DRIVER(arasan_nand) = { - .name = "arasan-nand", + .name = "arasan_nand", .id = UCLASS_MTD, .of_match = arasan_nand_dt_ids, .probe = arasan_probe, diff --git a/drivers/mtd/nand/raw/zynq_nand.c b/drivers/mtd/nand/raw/zynq_nand.c index fa59455210..92db2aa19c 100644 --- a/drivers/mtd/nand/raw/zynq_nand.c +++ b/drivers/mtd/nand/raw/zynq_nand.c @@ -1282,7 +1282,7 @@ static const struct udevice_id zynq_nand_dt_ids[] = { }; U_BOOT_DRIVER(zynq_nand) = { - .name = "zynq-nand", + .name = "zynq_nand", .id = UCLASS_MTD, .of_match = zynq_nand_dt_ids, .probe = zynq_nand_probe, -- cgit v1.2.3 From 4ab3817ff16a154981f9394a2c4a0f8f6a72713b Mon Sep 17 00:00:00 2001 From: Michal Simek Date: Wed, 16 Sep 2020 13:20:55 +0200 Subject: clk: fixed-rate: Enable DM_FLAG_PRE_RELOC flag fixed-rate driver is not different from clk_fixed_factor and it is required very early in boot that's why setup flag for it. Signed-off-by: Michal Simek --- drivers/clk/clk_fixed_rate.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers/clk') diff --git a/drivers/clk/clk_fixed_rate.c b/drivers/clk/clk_fixed_rate.c index 2c20eddb0b..55e1f8caa5 100644 --- a/drivers/clk/clk_fixed_rate.c +++ b/drivers/clk/clk_fixed_rate.c @@ -53,4 +53,5 @@ U_BOOT_DRIVER(clk_fixed_rate) = { .ofdata_to_platdata = clk_fixed_rate_ofdata_to_platdata, .platdata_auto_alloc_size = sizeof(struct clk_fixed_rate), .ops = &clk_fixed_rate_ops, + .flags = DM_FLAG_PRE_RELOC, }; -- cgit v1.2.3 From a952c3a4546ba1d6c5a487cae2e73760ecfd0c60 Mon Sep 17 00:00:00 2001 From: Sean Anderson Date: Mon, 28 Sep 2020 10:52:27 -0400 Subject: riscv: clk: Add CLINT clock to kendryte clock driver Another "virtual" clock (in the sense that it isn't configurable). This could possibly be done as a clock in the device tree, but I think this is a bit cleaner. Signed-off-by: Sean Anderson --- drivers/clk/kendryte/clk.c | 4 ++++ include/dt-bindings/clock/k210-sysctl.h | 1 + 2 files changed, 5 insertions(+) (limited to 'drivers/clk') diff --git a/drivers/clk/kendryte/clk.c b/drivers/clk/kendryte/clk.c index 981b3b7699..bb196961af 100644 --- a/drivers/clk/kendryte/clk.c +++ b/drivers/clk/kendryte/clk.c @@ -646,6 +646,10 @@ static int k210_clk_probe(struct udevice *dev) REGISTER_GATE(K210_CLK_RTC, "rtc", in0); #undef REGISTER_GATE + /* The MTIME register in CLINT runs at one 50th the CPU clock speed */ + clk_dm(K210_CLK_CLINT, + clk_register_fixed_factor(NULL, "clint", "cpu", 0, 1, 50)); + return 0; } diff --git a/include/dt-bindings/clock/k210-sysctl.h b/include/dt-bindings/clock/k210-sysctl.h index 0e3ed3fb9f..fe852bbd92 100644 --- a/include/dt-bindings/clock/k210-sysctl.h +++ b/include/dt-bindings/clock/k210-sysctl.h @@ -55,5 +55,6 @@ #define K210_CLK_OTP 43 #define K210_CLK_RTC 44 #define K210_CLK_ACLK 45 +#define K210_CLK_CLINT 46 #endif /* CLOCK_K210_SYSCTL_H */ -- cgit v1.2.3 From 60388844836f5639e6c9a4331335ff22298128da Mon Sep 17 00:00:00 2001 From: Etienne Carriere Date: Wed, 9 Sep 2020 18:44:04 +0200 Subject: clk: add clock driver for SCMI agents This change introduces a clock driver for SCMI agent devices. When SCMI agent and SCMI clock drivers are enabled, SCMI agent binds a clock device for each SCMI clock protocol devices enabled in the FDT. SCMI clock driver is embedded upon CONFIG_CLK_SCMI=y. If enabled, CONFIG_SCMI_AGENT is also enabled. SCMI Clock protocol is defined in the SCMI specification [1]. Links: [1] https://developer.arm.com/architectures/system-architectures/software-standards/scmi Signed-off-by: Etienne Carriere Cc: Lukasz Majewski Cc: Simon Glass Cc: Peng Fan Cc: Sudeep Holla Reviewed-by: Simon Glass --- drivers/clk/Kconfig | 8 +++ drivers/clk/Makefile | 1 + drivers/clk/clk_scmi.c | 99 +++++++++++++++++++++++++++++++ drivers/firmware/scmi/scmi_agent-uclass.c | 14 ++++- include/scmi_protocols.h | 78 ++++++++++++++++++++++++ 5 files changed, 197 insertions(+), 3 deletions(-) create mode 100644 drivers/clk/clk_scmi.c (limited to 'drivers/clk') diff --git a/drivers/clk/Kconfig b/drivers/clk/Kconfig index 6003e140b5..4dfbad7986 100644 --- a/drivers/clk/Kconfig +++ b/drivers/clk/Kconfig @@ -159,6 +159,14 @@ config CLK_CDCE9XX Enable the clock synthesizer driver for CDCE913/925/937/949 series of chips. +config CLK_SCMI + bool "Enable SCMI clock driver" + depends on SCMI_FIRMWARE + help + Enable this option if you want to support clock devices exposed + by a SCMI agent based on SCMI clock protocol communication + with a SCMI server. + source "drivers/clk/analogbits/Kconfig" source "drivers/clk/at91/Kconfig" source "drivers/clk/exynos/Kconfig" diff --git a/drivers/clk/Makefile b/drivers/clk/Makefile index cda4b4b605..d1e295ac7c 100644 --- a/drivers/clk/Makefile +++ b/drivers/clk/Makefile @@ -32,6 +32,7 @@ obj-$(CONFIG_CLK_MPC83XX) += mpc83xx_clk.o obj-$(CONFIG_CLK_OCTEON) += clk_octeon.o obj-$(CONFIG_CLK_OWL) += owl/ obj-$(CONFIG_CLK_RENESAS) += renesas/ +obj-$(CONFIG_CLK_SCMI) += clk_scmi.o obj-$(CONFIG_CLK_SIFIVE) += sifive/ obj-$(CONFIG_ARCH_SUNXI) += sunxi/ obj-$(CONFIG_CLK_STM32F) += clk_stm32f.o diff --git a/drivers/clk/clk_scmi.c b/drivers/clk/clk_scmi.c new file mode 100644 index 0000000000..93a4819501 --- /dev/null +++ b/drivers/clk/clk_scmi.c @@ -0,0 +1,99 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (C) 2019-2020 Linaro Limited + */ +#include +#include +#include +#include +#include +#include + +static int scmi_clk_gate(struct clk *clk, int enable) +{ + struct scmi_clk_state_in in = { + .clock_id = clk->id, + .attributes = enable, + }; + struct scmi_clk_state_out out; + struct scmi_msg msg = SCMI_MSG_IN(SCMI_PROTOCOL_ID_CLOCK, + SCMI_CLOCK_CONFIG_SET, + in, out); + int ret; + + ret = devm_scmi_process_msg(clk->dev->parent, &msg); + if (ret) + return ret; + + return scmi_to_linux_errno(out.status); +} + +static int scmi_clk_enable(struct clk *clk) +{ + return scmi_clk_gate(clk, 1); +} + +static int scmi_clk_disable(struct clk *clk) +{ + return scmi_clk_gate(clk, 0); +} + +static ulong scmi_clk_get_rate(struct clk *clk) +{ + struct scmi_clk_rate_get_in in = { + .clock_id = clk->id, + }; + struct scmi_clk_rate_get_out out; + struct scmi_msg msg = SCMI_MSG_IN(SCMI_PROTOCOL_ID_CLOCK, + SCMI_CLOCK_RATE_GET, + in, out); + int ret; + + ret = devm_scmi_process_msg(clk->dev->parent, &msg); + if (ret < 0) + return ret; + + ret = scmi_to_linux_errno(out.status); + if (ret < 0) + return ret; + + return (ulong)(((u64)out.rate_msb << 32) | out.rate_lsb); +} + +static ulong scmi_clk_set_rate(struct clk *clk, ulong rate) +{ + struct scmi_clk_rate_set_in in = { + .clock_id = clk->id, + .flags = SCMI_CLK_RATE_ROUND_CLOSEST, + .rate_lsb = (u32)rate, + .rate_msb = (u32)((u64)rate >> 32), + }; + struct scmi_clk_rate_set_out out; + struct scmi_msg msg = SCMI_MSG_IN(SCMI_PROTOCOL_ID_CLOCK, + SCMI_CLOCK_RATE_SET, + in, out); + int ret; + + ret = devm_scmi_process_msg(clk->dev->parent, &msg); + if (ret < 0) + return ret; + + ret = scmi_to_linux_errno(out.status); + if (ret < 0) + return ret; + + return scmi_clk_get_rate(clk); +} + +static const struct clk_ops scmi_clk_ops = { + .enable = scmi_clk_enable, + .disable = scmi_clk_disable, + .get_rate = scmi_clk_get_rate, + .set_rate = scmi_clk_set_rate, +}; + +U_BOOT_DRIVER(scmi_clock) = { + .name = "scmi_clk", + .id = UCLASS_CLK, + .ops = &scmi_clk_ops, +}; diff --git a/drivers/firmware/scmi/scmi_agent-uclass.c b/drivers/firmware/scmi/scmi_agent-uclass.c index 67a6f907c9..1f36f23b6d 100644 --- a/drivers/firmware/scmi/scmi_agent-uclass.c +++ b/drivers/firmware/scmi/scmi_agent-uclass.c @@ -58,9 +58,9 @@ static int scmi_bind_protocols(struct udevice *dev) { int ret = 0; ofnode node; - struct driver *drv; dev_for_each_subnode(node, dev) { + struct driver *drv = NULL; u32 protocol_id; if (!ofnode_is_available(node)) @@ -70,9 +70,17 @@ static int scmi_bind_protocols(struct udevice *dev) continue; switch (protocol_id) { + case SCMI_PROTOCOL_ID_CLOCK: + if (IS_ENABLED(CONFIG_CLK_SCMI)) + drv = DM_GET_DRIVER(scmi_clock); + break; default: - dev_info(dev, "Ignore unsupported SCMI protocol %#x\n", - protocol_id); + break; + } + + if (!drv) { + dev_dbg(dev, "Ignore unsupported SCMI protocol %#x\n", + protocol_id); continue; } diff --git a/include/scmi_protocols.h b/include/scmi_protocols.h index 86a2d109c8..4778bcfc47 100644 --- a/include/scmi_protocols.h +++ b/include/scmi_protocols.h @@ -7,6 +7,7 @@ #define _SCMI_PROTOCOLS_H #include +#include /* * Subset the SCMI protocols definition @@ -38,4 +39,81 @@ enum scmi_status_code { SCMI_PROTOCOL_ERROR = -10, }; +/* + * SCMI Clock Protocol + */ + +enum scmi_clock_message_id { + SCMI_CLOCK_RATE_SET = 0x5, + SCMI_CLOCK_RATE_GET = 0x6, + SCMI_CLOCK_CONFIG_SET = 0x7, +}; + +#define SCMI_CLK_RATE_ASYNC_NOTIFY BIT(0) +#define SCMI_CLK_RATE_ASYNC_NORESP (BIT(0) | BIT(1)) +#define SCMI_CLK_RATE_ROUND_DOWN 0 +#define SCMI_CLK_RATE_ROUND_UP BIT(2) +#define SCMI_CLK_RATE_ROUND_CLOSEST BIT(3) + +/** + * struct scmi_clk_state_in - Message payload for CLOCK_CONFIG_SET command + * @clock_id: SCMI clock ID + * @attributes: Attributes of the targets clock state + */ +struct scmi_clk_state_in { + u32 clock_id; + u32 attributes; +}; + +/** + * struct scmi_clk_state_out - Response payload for CLOCK_CONFIG_SET command + * @status: SCMI command status + */ +struct scmi_clk_state_out { + s32 status; +}; + +/** + * struct scmi_clk_state_in - Message payload for CLOCK_RATE_GET command + * @clock_id: SCMI clock ID + * @attributes: Attributes of the targets clock state + */ +struct scmi_clk_rate_get_in { + u32 clock_id; +}; + +/** + * struct scmi_clk_rate_get_out - Response payload for CLOCK_RATE_GET command + * @status: SCMI command status + * @rate_lsb: 32bit LSB of the clock rate in Hertz + * @rate_msb: 32bit MSB of the clock rate in Hertz + */ +struct scmi_clk_rate_get_out { + s32 status; + u32 rate_lsb; + u32 rate_msb; +}; + +/** + * struct scmi_clk_state_in - Message payload for CLOCK_RATE_SET command + * @clock_id: SCMI clock ID + * @flags: Flags for the clock rate set request + * @rate_lsb: 32bit LSB of the clock rate in Hertz + * @rate_msb: 32bit MSB of the clock rate in Hertz + */ +struct scmi_clk_rate_set_in { + u32 clock_id; + u32 flags; + u32 rate_lsb; + u32 rate_msb; +}; + +/** + * struct scmi_clk_rate_set_out - Response payload for CLOCK_RATE_SET command + * @status: SCMI command status + */ +struct scmi_clk_rate_set_out { + s32 status; +}; + #endif /* _SCMI_PROTOCOLS_H */ -- cgit v1.2.3