[U-Boot] [PATCH v5 00/18] clk: Port Linux common clock framework [CCF] to U-boot (tag: v5.1.12)

This patch series brings the files from Linux kernel (SHA1: 5752b50477da Linux 5.1.12 to provide clocks support as it is used on the Linux kernel with Common Clock Framework [CCF] setup.
This series also fixes several problems with current clocks and provides sandbox tests for functions added to clk-uclass.c file.
CCF impact to U-Boot size: -------------------------- SPL U-Boot.img Without CCF: 51 KiB 358 KiB With CCF: 55 KiB 363 KiB Size increase: 1.3% 7.2%
The SPL implementation is not yet optimized (no OF_PLATDATA, etc).
Repository: https://github.com/lmajewski/u-boot-dfu/commits/CCF-v5
Applicable on top of u-boot/master: SHA1: 77f6e2dd0551d8a825bab391a1bd6b838874bcd4
Travis-CI: https://travis-ci.org/lmajewski/u-boot-dfu/builds/549603356
Changes in v6: - Use dev->uclass_priv pointer to store pointer to clk
Changes in v5: - s/U-boot/U-Boot/g - Update Linux version to 5.1.12 - Add paragraph regarding sandbox CCF testing (common uclass code) - Use long long to store rate value (to avoid int overflow and also return errors correctly) - Use u32 to avoid checkpatch warning - s/U-boot/U-Boot/g - Replace dev->driver_data with dev_get_clk_ptr() wrapper on uclass_priv - Replace ulong with long long (to accommodate large freqs and return errors) - s/U-boot/U-Boot/g - Check if the relevant code has changed between Linux tag v5.0-rc3 and v5.1.12 (no changes - the version can be safely updated). - Use imply CLK_IMX6Q in Kconfig for (SPL_)CCF - Fix clk-fixed-factor implementation (kzalloc needed for correct Sandbox operation) - Fix gate2 implementation - Use dev->uclass_priv instead of dev->driver_data to store back pointer to the struct clk. - Split and introduce earlier the clk-provider.h header file
Changes in v4: - New patch - Port some more Linux code to facilitate imx8 code porting (most notably flags) - Explicitly use container_of() based macro to provide struct clk in various places (e.g. gate2, mux, etc) Following patches has been squashed: http://patchwork.ozlabs.org/patch/1093141/ http://patchwork.ozlabs.org/patch/1093142/ http://patchwork.ozlabs.org/patch/1093146/
Changes in v3: - New patch - The rate information is now cached into struct clk field - The clk_get_parent() is used to get pointer to the parent struct clk - Replace -ENODEV with -ENOENT - Use **clkp instead of **c - Replace dev->driver_data with dev_get_clk_ptr() wrapper on uclas_priv
Lukasz Majewski (18): clk: doc: Add documentation entry for Common Clock Framework [CCF] (i.MX) dm: Fix documentation entry as there is no UCLASS_CLOCK uclass clk: Remove clock ID check in .get_rate() of clk_fixed_* clk: Extend struct clk to provide information regarding clock rate clk: Extend struct clk to provide clock type agnostic flags clk: Provide struct clk for fixed rate clock (clk_fixed_rate.c) clk: Introduce clk-provider.h to store Common Clock Framework's internals dm: clk: Define clk_get_parent() for clk operations dm: clk: Define clk_get_parent_rate() for clk operations dm: clk: Define clk_get_by_id() for clk operations clk: Port Linux common clock framework [CCF] for imx6q to U-boot (tag: v5.1.12) dm: clk: Extend clk_get_parent_rate() to support CLK_GET_RATE_NOCACHE flag dts: sandbox: Add 'osc' clock for Common Clock Framework [CCF] testing clk: sandbox: Adjust clk-divider to emulate reading its value from HW clk: sandbox: Adjust clk-mux.c to emulate reading divider value from HW clk: sandbox: Add sandbox test code for Common Clock Framework [CCF] defconfig: sandbox: Enable SANDBOX_CLK_CCF to reuse generic CCF code clk: Add MAINTAINERS entry for clocks (./drivers/clk/)
MAINTAINERS | 7 ++ arch/sandbox/dts/test.dts | 10 ++ configs/sandbox_defconfig | 1 + configs/sandbox_flattree_defconfig | 1 + doc/imx/clk/ccf.txt | 101 ++++++++++++++++++++ drivers/clk/Kconfig | 22 +++++ drivers/clk/Makefile | 3 + drivers/clk/clk-divider.c | 155 +++++++++++++++++++++++++++++++ drivers/clk/clk-fixed-factor.c | 80 ++++++++++++++++ drivers/clk/clk-mux.c | 172 ++++++++++++++++++++++++++++++++++ drivers/clk/clk-uclass.c | 60 ++++++++++++ drivers/clk/clk.c | 57 ++++++++++++ drivers/clk/clk_fixed_factor.c | 3 - drivers/clk/clk_fixed_rate.c | 8 +- drivers/clk/clk_sandbox_ccf.c | 185 +++++++++++++++++++++++++++++++++++++ drivers/clk/imx/Kconfig | 16 ++++ drivers/clk/imx/Makefile | 2 + drivers/clk/imx/clk-gate2.c | 103 +++++++++++++++++++++ drivers/clk/imx/clk-imx6q.c | 179 +++++++++++++++++++++++++++++++++++ drivers/clk/imx/clk-pfd.c | 90 ++++++++++++++++++ drivers/clk/imx/clk-pllv3.c | 82 ++++++++++++++++ drivers/clk/imx/clk.h | 69 ++++++++++++++ include/clk.h | 37 +++++++- include/linux/clk-provider.h | 132 ++++++++++++++++++++++++++ include/sandbox-clk.h | 76 +++++++++++++++ test/dm/Makefile | 2 +- test/dm/clk_ccf.c | 62 +++++++++++++ 27 files changed, 1707 insertions(+), 8 deletions(-) create mode 100644 doc/imx/clk/ccf.txt create mode 100644 drivers/clk/clk-divider.c create mode 100644 drivers/clk/clk-fixed-factor.c create mode 100644 drivers/clk/clk-mux.c create mode 100644 drivers/clk/clk.c create mode 100644 drivers/clk/clk_sandbox_ccf.c create mode 100644 drivers/clk/imx/clk-gate2.c create mode 100644 drivers/clk/imx/clk-imx6q.c create mode 100644 drivers/clk/imx/clk-pfd.c create mode 100644 drivers/clk/imx/clk-pllv3.c create mode 100644 drivers/clk/imx/clk.h create mode 100644 include/linux/clk-provider.h create mode 100644 include/sandbox-clk.h create mode 100644 test/dm/clk_ccf.c

This patch describes the design decisions considerations and taken approach for porting in a separate documentation entry.
Signed-off-by: Lukasz Majewski lukma@denx.de
---
Changes in v6: None Changes in v5: - s/U-boot/U-Boot/g - Update Linux version to 5.1.12 - Add paragraph regarding sandbox CCF testing (common uclass code)
Changes in v4: - New patch
Changes in v3: None
doc/imx/clk/ccf.txt | 101 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 101 insertions(+) create mode 100644 doc/imx/clk/ccf.txt
diff --git a/doc/imx/clk/ccf.txt b/doc/imx/clk/ccf.txt new file mode 100644 index 0000000000..36b60dc438 --- /dev/null +++ b/doc/imx/clk/ccf.txt @@ -0,0 +1,101 @@ +Introduction: +============= + +This documentation entry describes the Common Clock Framework [CCF] +port from Linux kernel (v5.1.12) to U-Boot. + +This code is supposed to bring CCF to IMX based devices (imx6q, imx7 +imx8). Moreover, it also provides some common clock code, which would +allow easy porting of CCF Linux code to other platforms. + +Design decisions: +================= + +* U-Boot's driver model [DM] for clk differs from Linux CCF. The most + notably difference is the lack of support for hierarchical clocks and + "clock as a manager driver" (single clock DTS node acts as a starting + point for all other clocks). + +* The clk_get_rate() caches the previously read data if CLK_GET_RATE_NOCACHE + is not set (no need for recursive access). + +* On purpose the "manager" clk driver (clk-imx6q.c) is not using large + table to store pointers to clocks - e.g. clk[IMX6QDL_CLK_USDHC2_SEL] = .... + Instead we use udevice's linked list for the same class (UCLASS_CLK). + + Rationale: + ---------- + When porting the code as is from Linux, one would need ~1KiB of RAM to + store it. This is way too much if we do plan to use this driver in SPL. + +* The "central" structure of this patch series is struct udevice and its + uclass_priv field contains the struct clk pointer (to the originally created + one). + +* Up till now U-Boot's driver model (DM) CLK operates on udevice (main + access to clock is by udevice ops) + In the CCF the access to struct clk (embodying pointer to *dev) is + possible via dev_get_clk_ptr() (it is a wrapper on dev_get_uclass_priv()). + +* To keep things simple the struct udevice's uclass_priv pointer is used to + store back pointer to corresponding struct clk. However, it is possible to + modify clk-uclass.c file and add there struct uc_clk_priv, which would have + clock related members (like pointer to clk). As of this writing there is no + such need, so to avoid extra allocations (as it can be auto allocated by + setting .per_device_auto_alloc_size = sizeof(struct uc_clk_priv)) the + uclass_priv stores the pointer to struct clk. + +* It is advised to add common clock code (like already added rate and flags) to + the struct clk, which is a top level description of the clock. + +* U-Boot's driver model already provides the facility to automatically allocate + (via private_alloc_size) device private data (accessible via dev->priv). + It may look appealing to use this feature to allocate private structures for + CCF clk devices e.g. divider (struct clk_divider *divider) for IMX6Q clock. + + The above feature had not been used for following reasons: + - The original CCF Linux kernel driver is the "manager" for clocks - it + decides when clock is instantiated (and when memory for it is allocated). + + - Using it would change the original structure of the CCF code. + + - To bind (via clk_register()) the clock device with U-Boot driver model we + first need udevice for it (the "chicken and egg problem"). + +* I've added the clk_get_parent(), which reads parent's dev->uclass_priv to + provide parent's struct clk pointer. This seems the easiest way to get + child/parent relationship for struct clk in U-Boot's udevice based clocks. + +* Linux's CCF 'struct clk_core' corresponds to U-Boot's udevice in 'struct clk'. + Clock IP block agnostic flags from 'struct clk_core' (e.g. NOCACHE) have been + moved from this struct one level up to 'struct clk'. + +* For tests the new ./test/dm/clk_ccf.c and ./drivers/clk/clk_sandbox_ccf.c + files have been introduced. The latter setups the CCF clock structure for + sandbox by reusing, if possible, generic clock primitives - like divier + and mux. The former file provides code to tests this setup. + + For sandbox new CONFIG_SANDBOX_CLK_CCF Kconfig define has been introduced. + All new primitives added for new architectures must have corresponding test + in the two aforementioned files. + + +Testing (sandbox): +================== + +make mrproper; make sandbox_defconfig; make -j4 +./u-boot -i -d arch/sandbox/dts/test.dtb +=> ut dm clk + +or in a more "scriptable" way (with -v to print debug output): +./u-boot --fdt arch/sandbox/dts/test.dtb --command "ut dm clk_ccf" -v + +To do: +------ + +* Use of OF_PLATDATA in the SPL setup for CCF - as it is now - the SPL grows + considerably and using CCF in boards with tiny resources (OCRAM) is + problematic. + +* On demand port other parts of CCF to U-Boot - as now only features _really_ + needed by DM/DTS converted drivers are used.

This patch describes the design decisions considerations and taken approach for porting in a separate documentation entry. Signed-off-by: Lukasz Majewski lukma@denx.de
Applied to u-boot-imx, master, thanks !
Best regards, Stefano Babic

There is no UCLASS_CLOCK uclass defined. Instead we do use the UCLASS_CLK.
Signed-off-by: Lukasz Majewski lukma@denx.de Reviewed-by: Simon Glass sjg@chromium.org Reviewed-by: Peng Fan peng.fan@nxp.com ---
Changes in v6: None Changes in v5: None Changes in v4: None Changes in v3: None
include/clk.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/include/clk.h b/include/clk.h index d24e99713a..a909b71f1a 100644 --- a/include/clk.h +++ b/include/clk.h @@ -20,7 +20,7 @@ * clock provider. This API provides a standard means for drivers to enable and * disable clocks, and to set the rate at which they oscillate. * - * A driver that implements UCLASS_CLOCK is a clock provider. A provider will + * A driver that implements UCLASS_CLK is a clock provider. A provider will * often implement multiple separate clocks, since the hardware it manages * often has this capability. clk-uclass.h describes the interface which * clock providers must implement.

There is no UCLASS_CLOCK uclass defined. Instead we do use the UCLASS_CLK. Signed-off-by: Lukasz Majewski lukma@denx.de Reviewed-by: Simon Glass sjg@chromium.org Reviewed-by: Peng Fan peng.fan@nxp.com
Applied to u-boot-imx, master, thanks !
Best regards, Stefano Babic

This check requires the struct clk passed to .get_rate() to be always cleared out as any clock with valid ID causes -EINVAL return value.
The return code of fixed clocks shall always be returned.
Signed-off-by: Lukasz Majewski lukma@denx.de Reviewed-by: Peng Fan peng.fan@nxp.com ---
Changes in v6: None Changes in v5: None Changes in v4: None Changes in v3: None
drivers/clk/clk_fixed_factor.c | 3 --- drivers/clk/clk_fixed_rate.c | 3 --- 2 files changed, 6 deletions(-)
diff --git a/drivers/clk/clk_fixed_factor.c b/drivers/clk/clk_fixed_factor.c index 5fa20a84db..dcdb6ddf5c 100644 --- a/drivers/clk/clk_fixed_factor.c +++ b/drivers/clk/clk_fixed_factor.c @@ -24,9 +24,6 @@ static ulong clk_fixed_factor_get_rate(struct clk *clk) uint64_t rate; struct clk_fixed_factor *ff = to_clk_fixed_factor(clk->dev);
- if (clk->id != 0) - return -EINVAL; - rate = clk_get_rate(&ff->parent); if (IS_ERR_VALUE(rate)) return rate; diff --git a/drivers/clk/clk_fixed_rate.c b/drivers/clk/clk_fixed_rate.c index d8d9f86c86..50dbb13655 100644 --- a/drivers/clk/clk_fixed_rate.c +++ b/drivers/clk/clk_fixed_rate.c @@ -15,9 +15,6 @@ struct clk_fixed_rate {
static ulong clk_fixed_rate_get_rate(struct clk *clk) { - if (clk->id != 0) - return -EINVAL; - return to_clk_fixed_rate(clk->dev)->fixed_rate; }

This check requires the struct clk passed to .get_rate() to be always cleared out as any clock with valid ID causes -EINVAL return value. The return code of fixed clocks shall always be returned. Signed-off-by: Lukasz Majewski lukma@denx.de Reviewed-by: Peng Fan peng.fan@nxp.com
Applied to u-boot-imx, master, thanks !
Best regards, Stefano Babic

This commit extends the struct clk to provide information regarding the clock rate. As a result the clock tree traversal is performed at most once, and further reads are using the cached value.
Signed-off-by: Lukasz Majewski lukma@denx.de
---
Changes in v6: None Changes in v5: - Use long long to store rate value (to avoid int overflow and also return errors correctly)
Changes in v4: None Changes in v3: None
include/clk.h | 2 ++ 1 file changed, 2 insertions(+)
diff --git a/include/clk.h b/include/clk.h index a909b71f1a..d7b937ca7b 100644 --- a/include/clk.h +++ b/include/clk.h @@ -40,6 +40,7 @@ struct udevice; * other clock APIs to identify which clock signal to operate upon. * * @dev: The device which implements the clock signal. + * @rate: The clock rate (in HZ). * @id: The clock signal ID within the provider. * @data: An optional data field for scenarios where a single integer ID is not * sufficient. If used, it can be populated through an .of_xlate op and @@ -55,6 +56,7 @@ struct udevice; */ struct clk { struct udevice *dev; + long long rate; /* in HZ */ /* * Written by of_xlate. In the future, we might add more fields here. */

This commit extends the struct clk to provide information regarding the clock rate. As a result the clock tree traversal is performed at most once, and further reads are using the cached value. Signed-off-by: Lukasz Majewski lukma@denx.de
Applied to u-boot-imx, master, thanks !
Best regards, Stefano Babic

This commit extends the struct clk to provide information regarding the flags related to this devices.
Those flags are clk device agnostic and indicate generic features (like e.g. CLK_GET_RATE_NOCACHE - the need to always recalculate the rate).
Signed-off-by: Lukasz Majewski lukma@denx.de Reviewed-by: Peng Fan peng.fan@nxp.com
---
Changes in v6: None Changes in v5: - Use u32 to avoid checkpatch warning
Changes in v4: None Changes in v3: None
include/clk.h | 4 ++++ 1 file changed, 4 insertions(+)
diff --git a/include/clk.h b/include/clk.h index d7b937ca7b..b10c0013b1 100644 --- a/include/clk.h +++ b/include/clk.h @@ -41,6 +41,9 @@ struct udevice; * * @dev: The device which implements the clock signal. * @rate: The clock rate (in HZ). + * @flags: Flags used across common clock structure (e.g. CLK_) + * Clock IP blocks specific flags (i.e. mux, div, gate, etc) are defined + * in struct's for those devices (e.g. struct clk_mux). * @id: The clock signal ID within the provider. * @data: An optional data field for scenarios where a single integer ID is not * sufficient. If used, it can be populated through an .of_xlate op and @@ -57,6 +60,7 @@ struct udevice; struct clk { struct udevice *dev; long long rate; /* in HZ */ + u32 flags; /* * Written by of_xlate. In the future, we might add more fields here. */

This commit extends the struct clk to provide information regarding the flags related to this devices. Those flags are clk device agnostic and indicate generic features (like e.g. CLK_GET_RATE_NOCACHE - the need to always recalculate the rate). Signed-off-by: Lukasz Majewski lukma@denx.de Reviewed-by: Peng Fan peng.fan@nxp.com
Applied to u-boot-imx, master, thanks !
Best regards, Stefano Babic

Up till now the fixed rate clock ('osc') has been added to UCLASS_CLK without declaring struct clk. As a result it was only accessible by iterating the udevice's uclass list.
This is a problem for clock code, which operates on pointers to struct clk (like clk_get_rate()), not udevices.
After this change struct clk is accessible from udevice and udevice from struct clk.
Signed-off-by: Lukasz Majewski lukma@denx.de Reviewed-by: Peng Fan peng.fan@nxp.com
---
Changes in v6: - Use dev->uclass_priv pointer to store pointer to clk
Changes in v5: None Changes in v4: None Changes in v3: None
drivers/clk/clk_fixed_rate.c | 5 +++++ 1 file changed, 5 insertions(+)
diff --git a/drivers/clk/clk_fixed_rate.c b/drivers/clk/clk_fixed_rate.c index 50dbb13655..1fdf8c4e54 100644 --- a/drivers/clk/clk_fixed_rate.c +++ b/drivers/clk/clk_fixed_rate.c @@ -8,6 +8,7 @@ #include <dm.h>
struct clk_fixed_rate { + struct clk clk; unsigned long fixed_rate; };
@@ -24,10 +25,14 @@ const struct clk_ops clk_fixed_rate_ops = {
static int clk_fixed_rate_ofdata_to_platdata(struct udevice *dev) { + struct clk *clk = &to_clk_fixed_rate(dev)->clk; #if !CONFIG_IS_ENABLED(OF_PLATDATA) to_clk_fixed_rate(dev)->fixed_rate = dev_read_u32_default(dev, "clock-frequency", 0); #endif + /* Make fixed rate clock accessible from higher level struct clk */ + dev->uclass_priv = clk; + clk->dev = dev;
return 0; }

Up till now the fixed rate clock ('osc') has been added to UCLASS_CLK without declaring struct clk. As a result it was only accessible by iterating the udevice's uclass list. This is a problem for clock code, which operates on pointers to struct clk (like clk_get_rate()), not udevices. After this change struct clk is accessible from udevice and udevice from struct clk. Signed-off-by: Lukasz Majewski lukma@denx.de Reviewed-by: Peng Fan peng.fan@nxp.com
Applied to u-boot-imx, master, thanks !
Best regards, Stefano Babic

This file now stores the dev_get_clk_ptr() wrapper on the dev_get_uclass_priv() function.
Signed-off-by: Lukasz Majewski lukma@denx.de ---
Changes in v6: None Changes in v5: None Changes in v4: None Changes in v3: None
include/linux/clk-provider.h | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) create mode 100644 include/linux/clk-provider.h
diff --git a/include/linux/clk-provider.h b/include/linux/clk-provider.h new file mode 100644 index 0000000000..6adf0a5791 --- /dev/null +++ b/include/linux/clk-provider.h @@ -0,0 +1,16 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * Copyright (C) 2019 DENX Software Engineering + * Lukasz Majewski, DENX Software Engineering, lukma@denx.de + * + * Copyright (c) 2010-2011 Jeremy Kerr jeremy.kerr@canonical.com + * Copyright (C) 2011-2012 Linaro Ltd mturquette@linaro.org + */ +#ifndef __LINUX_CLK_PROVIDER_H +#define __LINUX_CLK_PROVIDER_H + +static inline struct clk *dev_get_clk_ptr(struct udevice *dev) +{ + return (struct clk *)dev_get_uclass_priv(dev); +} +#endif /* __LINUX_CLK_PROVIDER_H */

This file now stores the dev_get_clk_ptr() wrapper on the dev_get_uclass_priv() function. Signed-off-by: Lukasz Majewski lukma@denx.de
Applied to u-boot-imx, master, thanks !
Best regards, Stefano Babic

This commit adds the clk_get_parent() function, which is responsible for getting the parent's struct clock pointer.
U-Boot's DM support for getting parent is different (the parent relationship is in udevice) than the one in Common Clock Framework [CCF] in Linux. To obtain the pointer to struct clk of parent the pdev->uclass_priv field is read via dev_get_clk_ptr() wrapper.
Signed-off-by: Lukasz Majewski lukma@denx.de Reviewed-by: Peng Fan peng.fan@nxp.com
---
Changes in v6: None Changes in v5: - s/U-boot/U-Boot/g - Replace dev->driver_data with dev_get_clk_ptr() wrapper on uclass_priv
Changes in v4: None Changes in v3: - New patch
drivers/clk/clk-uclass.c | 16 ++++++++++++++++ include/clk.h | 9 +++++++++ 2 files changed, 25 insertions(+)
diff --git a/drivers/clk/clk-uclass.c b/drivers/clk/clk-uclass.c index 79b3b0494c..4346c61eea 100644 --- a/drivers/clk/clk-uclass.c +++ b/drivers/clk/clk-uclass.c @@ -13,6 +13,7 @@ #include <dm/read.h> #include <dt-structs.h> #include <errno.h> +#include <linux/clk-provider.h>
static inline const struct clk_ops *clk_dev_ops(struct udevice *dev) { @@ -379,6 +380,21 @@ ulong clk_get_rate(struct clk *clk) return ops->get_rate(clk); }
+struct clk *clk_get_parent(struct clk *clk) +{ + struct udevice *pdev; + struct clk *pclk; + + debug("%s(clk=%p)\n", __func__, clk); + + pdev = dev_get_parent(clk->dev); + pclk = dev_get_clk_ptr(pdev); + if (!pclk) + return ERR_PTR(-ENODEV); + + return pclk; +} + ulong clk_set_rate(struct clk *clk, ulong rate) { const struct clk_ops *ops = clk_dev_ops(clk->dev); diff --git a/include/clk.h b/include/clk.h index b10c0013b1..e20641ee98 100644 --- a/include/clk.h +++ b/include/clk.h @@ -259,6 +259,15 @@ int clk_free(struct clk *clk); ulong clk_get_rate(struct clk *clk);
/** + * clk_get_parent() - Get current clock's parent. + * + * @clk: A clock struct that was previously successfully requested by + * clk_request/get_by_*(). + * @return pointer to parent's struct clk, or error code passed as pointer + */ +struct clk *clk_get_parent(struct clk *clk); + +/** * clk_set_rate() - Set current clock rate. * * @clk: A clock struct that was previously successfully requested by

This commit adds the clk_get_parent() function, which is responsible for getting the parent's struct clock pointer. U-Boot's DM support for getting parent is different (the parent relationship is in udevice) than the one in Common Clock Framework [CCF] in Linux. To obtain the pointer to struct clk of parent the pdev->uclass_priv field is read via dev_get_clk_ptr() wrapper. Signed-off-by: Lukasz Majewski lukma@denx.de Reviewed-by: Peng Fan peng.fan@nxp.com
Applied to u-boot-imx, master, thanks !
Best regards, Stefano Babic

This commit adds the clk_get_parent_rate() function, which is responsible for getting the rate of parent clock. Unfortunately, u-boot's DM support for getting parent is different (the parent relationship is in udevice) than the one in Common Clock Framework [CCF] in Linux.
To alleviate this problem - the clk_get_parent_rate() function has been introduced to clk-uclass.c.
Signed-off-by: Lukasz Majewski lukma@denx.de Reviewed-by: Peng Fan peng.fan@nxp.com
---
Changes in v6: None Changes in v5: - Replace ulong with long long (to accommodate large freqs and return errors)
Changes in v4: None Changes in v3: - The rate information is now cached into struct clk field - The clk_get_parent() is used to get pointer to the parent struct clk
drivers/clk/clk-uclass.c | 22 ++++++++++++++++++++++ include/clk.h | 9 +++++++++ 2 files changed, 31 insertions(+)
diff --git a/drivers/clk/clk-uclass.c b/drivers/clk/clk-uclass.c index 4346c61eea..899b2dda6f 100644 --- a/drivers/clk/clk-uclass.c +++ b/drivers/clk/clk-uclass.c @@ -395,6 +395,28 @@ struct clk *clk_get_parent(struct clk *clk) return pclk; }
+long long clk_get_parent_rate(struct clk *clk) +{ + const struct clk_ops *ops; + struct clk *pclk; + + debug("%s(clk=%p)\n", __func__, clk); + + pclk = clk_get_parent(clk); + if (IS_ERR(pclk)) + return -ENODEV; + + ops = clk_dev_ops(pclk->dev); + if (!ops->get_rate) + return -ENOSYS; + + /* Read the 'rate' if not already set */ + if (!pclk->rate) + pclk->rate = clk_get_rate(pclk); + + return pclk->rate; +} + ulong clk_set_rate(struct clk *clk, ulong rate) { const struct clk_ops *ops = clk_dev_ops(clk->dev); diff --git a/include/clk.h b/include/clk.h index e20641ee98..7b2ff8ebe6 100644 --- a/include/clk.h +++ b/include/clk.h @@ -268,6 +268,15 @@ ulong clk_get_rate(struct clk *clk); struct clk *clk_get_parent(struct clk *clk);
/** + * clk_get_parent_rate() - Get parent of current clock rate. + * + * @clk: A clock struct that was previously successfully requested by + * clk_request/get_by_*(). + * @return clock rate in Hz, or -ve error code. + */ +long long clk_get_parent_rate(struct clk *clk); + +/** * clk_set_rate() - Set current clock rate. * * @clk: A clock struct that was previously successfully requested by

This commit adds the clk_get_parent_rate() function, which is responsible for getting the rate of parent clock. Unfortunately, u-boot's DM support for getting parent is different (the parent relationship is in udevice) than the one in Common Clock Framework [CCF] in Linux. To alleviate this problem - the clk_get_parent_rate() function has been introduced to clk-uclass.c. Signed-off-by: Lukasz Majewski lukma@denx.de Reviewed-by: Peng Fan peng.fan@nxp.com
Applied to u-boot-imx, master, thanks !
Best regards, Stefano Babic

This commit adds the clk_get_by_id() function, which is responsible for getting the udevice with matching clk->id. Such approach allows re-usage of inherit DM list relationship for the same class (UCLASS_CLK). As a result - we don't need any other external list - it is just enough to look for UCLASS_CLK related udevices.
Signed-off-by: Lukasz Majewski lukma@denx.de Reviewed-by: Peng Fan peng.fan@nxp.com
---
Changes in v6: None Changes in v5: None Changes in v4: None Changes in v3: - Replace -ENODEV with -ENOENT - Use **clkp instead of **c - Replace dev->driver_data with dev_get_clk_ptr() wrapper on uclas_priv
drivers/clk/clk-uclass.c | 22 ++++++++++++++++++++++ include/clk.h | 11 +++++++++++ 2 files changed, 33 insertions(+)
diff --git a/drivers/clk/clk-uclass.c b/drivers/clk/clk-uclass.c index 899b2dda6f..506ba6014c 100644 --- a/drivers/clk/clk-uclass.c +++ b/drivers/clk/clk-uclass.c @@ -491,6 +491,28 @@ int clk_disable_bulk(struct clk_bulk *bulk) return 0; }
+int clk_get_by_id(ulong id, struct clk **clkp) +{ + struct udevice *dev; + struct uclass *uc; + int ret; + + ret = uclass_get(UCLASS_CLK, &uc); + if (ret) + return ret; + + uclass_foreach_dev(dev, uc) { + struct clk *clk = dev_get_clk_ptr(dev); + + if (clk && clk->id == id) { + *clkp = clk; + return 0; + } + } + + return -ENOENT; +} + UCLASS_DRIVER(clk) = { .id = UCLASS_CLK, .name = "clk", diff --git a/include/clk.h b/include/clk.h index 7b2ff8ebe6..f8f56d9cf0 100644 --- a/include/clk.h +++ b/include/clk.h @@ -345,4 +345,15 @@ static inline bool clk_valid(struct clk *clk) { return !!clk->dev; } + +/** + * clk_get_by_id() - Get the clock by its ID + * + * @id: The clock ID to search for + * + * @clkp: A pointer to clock struct that has been found among added clocks + * to UCLASS_CLK + * @return zero on success, or -ENOENT on error + */ +int clk_get_by_id(ulong id, struct clk **clkp); #endif

This commit adds the clk_get_by_id() function, which is responsible for getting the udevice with matching clk->id. Such approach allows re-usage of inherit DM list relationship for the same class (UCLASS_CLK). As a result - we don't need any other external list - it is just enough to look for UCLASS_CLK related udevices. Signed-off-by: Lukasz Majewski lukma@denx.de Reviewed-by: Peng Fan peng.fan@nxp.com
Applied to u-boot-imx, master, thanks !
Best regards, Stefano Babic

This patch brings the files from Linux kernel (linux-stable/linux-5.1.y SHA1: 5752b50477da)to provide clocks support as it is used on the Linux kernel with Common Clock Framework [CCF] setup.
The directory structure has been preserved. The ported code only supports reading information from PLL, MUX, Divider, etc and enabling/disabling the clocks USDHCx/ECSPIx depending on used bus. Moreover, it is agnostic to the alias numbering as the information about the clock is read from the device tree.
One needs to pay attention to the comments indicating necessary for U-Boot's driver model changes.
If needed, the code can be extended to support the "set" part of the clock management.
Signed-off-by: Lukasz Majewski lukma@denx.de ---
Changes in v6: None Changes in v5: - s/U-boot/U-Boot/g - Check if the relevant code has changed between Linux tag v5.0-rc3 and v5.1.12 (no changes - the version can be safely updated). - Use imply CLK_IMX6Q in Kconfig for (SPL_)CCF - Fix clk-fixed-factor implementation (kzalloc needed for correct Sandbox operation) - Fix gate2 implementation - Use dev->uclass_priv instead of dev->driver_data to store back pointer to the struct clk. - Split and introduce earlier the clk-provider.h header file
Changes in v4: - Port some more Linux code to facilitate imx8 code porting (most notably flags) - Explicitly use container_of() based macro to provide struct clk in various places (e.g. gate2, mux, etc) Following patches has been squashed: http://patchwork.ozlabs.org/patch/1093141/ http://patchwork.ozlabs.org/patch/1093142/ http://patchwork.ozlabs.org/patch/1093146/
Changes in v3: None
drivers/clk/Kconfig | 14 ++++ drivers/clk/Makefile | 2 + drivers/clk/clk-divider.c | 147 +++++++++++++++++++++++++++++++++ drivers/clk/clk-fixed-factor.c | 80 ++++++++++++++++++ drivers/clk/clk-mux.c | 164 +++++++++++++++++++++++++++++++++++++ drivers/clk/clk.c | 57 +++++++++++++ drivers/clk/imx/Kconfig | 16 ++++ drivers/clk/imx/Makefile | 2 + drivers/clk/imx/clk-gate2.c | 103 ++++++++++++++++++++++++ drivers/clk/imx/clk-imx6q.c | 179 +++++++++++++++++++++++++++++++++++++++++ drivers/clk/imx/clk-pfd.c | 90 +++++++++++++++++++++ drivers/clk/imx/clk-pllv3.c | 82 +++++++++++++++++++ drivers/clk/imx/clk.h | 69 ++++++++++++++++ include/linux/clk-provider.h | 109 +++++++++++++++++++++++++ 14 files changed, 1114 insertions(+) create mode 100644 drivers/clk/clk-divider.c create mode 100644 drivers/clk/clk-fixed-factor.c create mode 100644 drivers/clk/clk-mux.c create mode 100644 drivers/clk/clk.c create mode 100644 drivers/clk/imx/clk-gate2.c create mode 100644 drivers/clk/imx/clk-imx6q.c create mode 100644 drivers/clk/imx/clk-pfd.c create mode 100644 drivers/clk/imx/clk-pllv3.c create mode 100644 drivers/clk/imx/clk.h
diff --git a/drivers/clk/Kconfig b/drivers/clk/Kconfig index 96969b9e30..9c2b284d02 100644 --- a/drivers/clk/Kconfig +++ b/drivers/clk/Kconfig @@ -46,6 +46,20 @@ config CLK_BOSTON help Enable this to support the clocks
+config SPL_CLK_CCF + bool "SPL Common Clock Framework [CCF] support " + depends on SPL_CLK_IMX6Q + help + Enable this option if you want to (re-)use the Linux kernel's Common + Clock Framework [CCF] code in U-Boot's SPL. + +config CLK_CCF + bool "Common Clock Framework [CCF] support " + depends on CLK_IMX6Q + help + Enable this option if you want to (re-)use the Linux kernel's Common + Clock Framework [CCF] code in U-Boot's clock driver. + config CLK_STM32F bool "Enable clock driver support for STM32F family" depends on CLK && (STM32F7 || STM32F4) diff --git a/drivers/clk/Makefile b/drivers/clk/Makefile index 719b9b8e02..1a45bf3505 100644 --- a/drivers/clk/Makefile +++ b/drivers/clk/Makefile @@ -7,6 +7,8 @@ obj-$(CONFIG_$(SPL_TPL_)CLK) += clk-uclass.o obj-$(CONFIG_$(SPL_TPL_)CLK) += clk_fixed_rate.o obj-$(CONFIG_$(SPL_TPL_)CLK) += clk_fixed_factor.o +obj-$(CONFIG_$(SPL_TPL_)CLK_CCF) += clk.o clk-divider.o clk-mux.o +obj-$(CONFIG_$(SPL_TPL_)CLK_CCF) += clk-fixed-factor.o
obj-y += imx/ obj-y += tegra/ diff --git a/drivers/clk/clk-divider.c b/drivers/clk/clk-divider.c new file mode 100644 index 0000000000..3348d97829 --- /dev/null +++ b/drivers/clk/clk-divider.c @@ -0,0 +1,147 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2019 DENX Software Engineering + * Lukasz Majewski, DENX Software Engineering, lukma@denx.de + * + * Copyright (C) 2011 Sascha Hauer, Pengutronix s.hauer@pengutronix.de + * Copyright (C) 2011 Richard Zhao, Linaro richard.zhao@linaro.org + * Copyright (C) 2011-2012 Mike Turquette, Linaro Ltd mturquette@linaro.org + * + */ + +#include <common.h> +#include <asm/io.h> +#include <malloc.h> +#include <clk-uclass.h> +#include <dm/device.h> +#include <dm/uclass.h> +#include <dm/lists.h> +#include <dm/device-internal.h> +#include <linux/clk-provider.h> +#include <div64.h> +#include <clk.h> +#include "clk.h" + +#define UBOOT_DM_CLK_CCF_DIVIDER "ccf_clk_divider" + +static unsigned int _get_table_div(const struct clk_div_table *table, + unsigned int val) +{ + const struct clk_div_table *clkt; + + for (clkt = table; clkt->div; clkt++) + if (clkt->val == val) + return clkt->div; + return 0; +} + +static unsigned int _get_div(const struct clk_div_table *table, + unsigned int val, unsigned long flags, u8 width) +{ + if (flags & CLK_DIVIDER_ONE_BASED) + return val; + if (flags & CLK_DIVIDER_POWER_OF_TWO) + return 1 << val; + if (flags & CLK_DIVIDER_MAX_AT_ZERO) + return val ? val : clk_div_mask(width) + 1; + if (table) + return _get_table_div(table, val); + return val + 1; +} + +unsigned long divider_recalc_rate(struct clk *hw, unsigned long parent_rate, + unsigned int val, + const struct clk_div_table *table, + unsigned long flags, unsigned long width) +{ + unsigned int div; + + div = _get_div(table, val, flags, width); + if (!div) { + WARN(!(flags & CLK_DIVIDER_ALLOW_ZERO), + "%s: Zero divisor and CLK_DIVIDER_ALLOW_ZERO not set\n", + clk_hw_get_name(hw)); + return parent_rate; + } + + return DIV_ROUND_UP_ULL((u64)parent_rate, div); +} + +static ulong clk_divider_recalc_rate(struct clk *clk) +{ + struct clk_divider *divider = + to_clk_divider(dev_get_clk_ptr(clk->dev)); + unsigned long parent_rate = clk_get_parent_rate(clk); + unsigned int val; + + val = readl(divider->reg) >> divider->shift; + val &= clk_div_mask(divider->width); + + return divider_recalc_rate(clk, parent_rate, val, divider->table, + divider->flags, divider->width); +} + +const struct clk_ops clk_divider_ops = { + .get_rate = clk_divider_recalc_rate, +}; + +static struct clk *_register_divider(struct device *dev, const char *name, + const char *parent_name, unsigned long flags, + void __iomem *reg, u8 shift, u8 width, + u8 clk_divider_flags, const struct clk_div_table *table) +{ + struct clk_divider *div; + struct clk *clk; + int ret; + + if (clk_divider_flags & CLK_DIVIDER_HIWORD_MASK) { + if (width + shift > 16) { + pr_warn("divider value exceeds LOWORD field\n"); + return ERR_PTR(-EINVAL); + } + } + + /* allocate the divider */ + div = kzalloc(sizeof(*div), GFP_KERNEL); + if (!div) + return ERR_PTR(-ENOMEM); + + /* struct clk_divider assignments */ + div->reg = reg; + div->shift = shift; + div->width = width; + div->flags = clk_divider_flags; + div->table = table; + + /* register the clock */ + clk = &div->clk; + + ret = clk_register(clk, UBOOT_DM_CLK_CCF_DIVIDER, name, parent_name); + if (ret) { + kfree(div); + return ERR_PTR(ret); + } + + return clk; +} + +struct clk *clk_register_divider(struct device *dev, const char *name, + const char *parent_name, unsigned long flags, + void __iomem *reg, u8 shift, u8 width, + u8 clk_divider_flags) +{ + struct clk *clk; + + clk = _register_divider(dev, name, parent_name, flags, reg, shift, + width, clk_divider_flags, NULL); + if (IS_ERR(clk)) + return ERR_CAST(clk); + return clk; +} + +U_BOOT_DRIVER(ccf_clk_divider) = { + .name = UBOOT_DM_CLK_CCF_DIVIDER, + .id = UCLASS_CLK, + .ops = &clk_divider_ops, + .flags = DM_FLAG_PRE_RELOC, +}; diff --git a/drivers/clk/clk-fixed-factor.c b/drivers/clk/clk-fixed-factor.c new file mode 100644 index 0000000000..711b0588bc --- /dev/null +++ b/drivers/clk/clk-fixed-factor.c @@ -0,0 +1,80 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2019 DENX Software Engineering + * Lukasz Majewski, DENX Software Engineering, lukma@denx.de + * + * Copyright (C) 2011 Sascha Hauer, Pengutronix s.hauer@pengutronix.de + */ +#include <common.h> +#include <malloc.h> +#include <clk-uclass.h> +#include <dm/device.h> +#include <linux/clk-provider.h> +#include <div64.h> +#include <clk.h> +#include "clk.h" + +#define UBOOT_DM_CLK_IMX_FIXED_FACTOR "ccf_clk_fixed_factor" + +static ulong clk_factor_recalc_rate(struct clk *clk) +{ + struct clk_fixed_factor *fix = + to_clk_fixed_factor(dev_get_clk_ptr(clk->dev)); + unsigned long parent_rate = clk_get_parent_rate(clk); + unsigned long long int rate; + + rate = (unsigned long long int)parent_rate * fix->mult; + do_div(rate, fix->div); + return (ulong)rate; +} + +const struct clk_ops ccf_clk_fixed_factor_ops = { + .get_rate = clk_factor_recalc_rate, +}; + +struct clk *clk_hw_register_fixed_factor(struct device *dev, + const char *name, const char *parent_name, unsigned long flags, + unsigned int mult, unsigned int div) +{ + struct clk_fixed_factor *fix; + struct clk *clk; + int ret; + + fix = kzalloc(sizeof(*fix), GFP_KERNEL); + if (!fix) + return ERR_PTR(-ENOMEM); + + /* struct clk_fixed_factor assignments */ + fix->mult = mult; + fix->div = div; + clk = &fix->clk; + + ret = clk_register(clk, UBOOT_DM_CLK_IMX_FIXED_FACTOR, name, + parent_name); + if (ret) { + kfree(fix); + return ERR_PTR(ret); + } + + return clk; +} + +struct clk *clk_register_fixed_factor(struct device *dev, const char *name, + const char *parent_name, unsigned long flags, + unsigned int mult, unsigned int div) +{ + struct clk *clk; + + clk = clk_hw_register_fixed_factor(dev, name, parent_name, flags, mult, + div); + if (IS_ERR(clk)) + return ERR_CAST(clk); + return clk; +} + +U_BOOT_DRIVER(imx_clk_fixed_factor) = { + .name = UBOOT_DM_CLK_IMX_FIXED_FACTOR, + .id = UCLASS_CLK, + .ops = &ccf_clk_fixed_factor_ops, + .flags = DM_FLAG_PRE_RELOC, +}; diff --git a/drivers/clk/clk-mux.c b/drivers/clk/clk-mux.c new file mode 100644 index 0000000000..14b9f2b1d9 --- /dev/null +++ b/drivers/clk/clk-mux.c @@ -0,0 +1,164 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2019 DENX Software Engineering + * Lukasz Majewski, DENX Software Engineering, lukma@denx.de + * + * Copyright (C) 2011 Sascha Hauer, Pengutronix s.hauer@pengutronix.de + * Copyright (C) 2011 Richard Zhao, Linaro richard.zhao@linaro.org + * Copyright (C) 2011-2012 Mike Turquette, Linaro Ltd mturquette@linaro.org + * + * Simple multiplexer clock implementation + */ + +/* + * U-Boot CCF porting node: + * + * The Linux kernel - as of tag: 5.0-rc3 is using also the imx_clk_fixup_mux() + * version of CCF mux. It is used on e.g. imx6q to provide fixes (like + * imx_cscmr1_fixup) for broken HW. + * + * At least for IMX6Q (but NOT IMX6QP) it is important when we set the parent + * clock. + */ + +#include <common.h> +#include <asm/io.h> +#include <malloc.h> +#include <clk-uclass.h> +#include <dm/device.h> +#include <linux/clk-provider.h> +#include <clk.h> +#include "clk.h" + +#define UBOOT_DM_CLK_CCF_MUX "ccf_clk_mux" + +int clk_mux_val_to_index(struct clk *clk, u32 *table, unsigned int flags, + unsigned int val) +{ + struct clk_mux *mux = to_clk_mux(clk); + int num_parents = mux->num_parents; + + if (table) { + int i; + + for (i = 0; i < num_parents; i++) + if (table[i] == val) + return i; + return -EINVAL; + } + + if (val && (flags & CLK_MUX_INDEX_BIT)) + val = ffs(val) - 1; + + if (val && (flags & CLK_MUX_INDEX_ONE)) + val--; + + if (val >= num_parents) + return -EINVAL; + + return val; +} + +static u8 clk_mux_get_parent(struct clk *clk) +{ + struct clk_mux *mux = to_clk_mux(clk); + u32 val; + + val = readl(mux->reg) >> mux->shift; + val &= mux->mask; + + return clk_mux_val_to_index(clk, mux->table, mux->flags, val); +} + +const struct clk_ops clk_mux_ops = { + .get_rate = clk_generic_get_rate, +}; + +struct clk *clk_hw_register_mux_table(struct device *dev, const char *name, + const char * const *parent_names, u8 num_parents, + unsigned long flags, + void __iomem *reg, u8 shift, u32 mask, + u8 clk_mux_flags, u32 *table) +{ + struct clk_mux *mux; + struct clk *clk; + u8 width = 0; + int ret; + + if (clk_mux_flags & CLK_MUX_HIWORD_MASK) { + width = fls(mask) - ffs(mask) + 1; + if (width + shift > 16) { + pr_err("mux value exceeds LOWORD field\n"); + return ERR_PTR(-EINVAL); + } + } + + /* allocate the mux */ + mux = kzalloc(sizeof(*mux), GFP_KERNEL); + if (!mux) + return ERR_PTR(-ENOMEM); + + /* U-boot specific assignments */ + mux->parent_names = parent_names; + mux->num_parents = num_parents; + + /* struct clk_mux assignments */ + mux->reg = reg; + mux->shift = shift; + mux->mask = mask; + mux->flags = clk_mux_flags; + mux->table = table; + + clk = &mux->clk; + + /* + * Read the current mux setup - so we assign correct parent. + * + * Changing parent would require changing internals of udevice struct + * for the corresponding clock (to do that define .set_parent() method. + */ + ret = clk_register(clk, UBOOT_DM_CLK_CCF_MUX, name, + parent_names[clk_mux_get_parent(clk)]); + if (ret) { + kfree(mux); + return ERR_PTR(ret); + } + + return clk; +} + +struct clk *clk_register_mux_table(struct device *dev, const char *name, + const char * const *parent_names, u8 num_parents, + unsigned long flags, + void __iomem *reg, u8 shift, u32 mask, + u8 clk_mux_flags, u32 *table) +{ + struct clk *clk; + + clk = clk_hw_register_mux_table(dev, name, parent_names, num_parents, + flags, reg, shift, mask, clk_mux_flags, + table); + if (IS_ERR(clk)) + return ERR_CAST(clk); + return clk; +} + +struct clk *clk_register_mux(struct device *dev, const char *name, + const char * const *parent_names, u8 num_parents, + unsigned long flags, + void __iomem *reg, u8 shift, u8 width, + u8 clk_mux_flags) +{ + u32 mask = BIT(width) - 1; + + return clk_register_mux_table(dev, name, parent_names, num_parents, + flags, reg, shift, mask, clk_mux_flags, + NULL); +} + +U_BOOT_DRIVER(ccf_clk_mux) = { + .name = UBOOT_DM_CLK_CCF_MUX, + .id = UCLASS_CLK, + .ops = &clk_mux_ops, + .flags = DM_FLAG_PRE_RELOC, +}; diff --git a/drivers/clk/clk.c b/drivers/clk/clk.c new file mode 100644 index 0000000000..7d748c9fc7 --- /dev/null +++ b/drivers/clk/clk.c @@ -0,0 +1,57 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (C) 2019 DENX Software Engineering + * Lukasz Majewski, DENX Software Engineering, lukma@denx.de + */ + +#include <common.h> +#include <clk-uclass.h> +#include <dm/device.h> +#include <dm/uclass.h> +#include <dm/lists.h> +#include <dm/device-internal.h> +#include <clk.h> + +int clk_register(struct clk *clk, const char *drv_name, + const char *name, const char *parent_name) +{ + struct udevice *parent; + struct driver *drv; + int ret; + + ret = uclass_get_device_by_name(UCLASS_CLK, parent_name, &parent); + if (ret) + printf("%s: UCLASS parent: 0x%p\n", __func__, parent); + + debug("%s: name: %s parent: %s [0x%p]\n", __func__, name, parent->name, + parent); + + drv = lists_driver_lookup_name(drv_name); + if (!drv) { + printf("%s: %s is not a valid driver name\n", + __func__, drv_name); + return -ENOENT; + } + + ret = device_bind(parent, drv, name, NULL, -1, &clk->dev); + if (ret) { + printf("%s: CLK: %s driver bind error [%d]!\n", __func__, name, + ret); + return ret; + } + + /* Store back pointer to clk from udevice */ + clk->dev->uclass_priv = clk; + + return 0; +} + +ulong clk_generic_get_rate(struct clk *clk) +{ + return clk_get_parent_rate(clk); +} + +const char *clk_hw_get_name(const struct clk *hw) +{ + return hw->dev->name; +} diff --git a/drivers/clk/imx/Kconfig b/drivers/clk/imx/Kconfig index a6fb58d6cf..3e6a980c8c 100644 --- a/drivers/clk/imx/Kconfig +++ b/drivers/clk/imx/Kconfig @@ -1,3 +1,19 @@ +config SPL_CLK_IMX6Q + bool "SPL clock support for i.MX6Q" + depends on ARCH_MX6 && SPL + select SPL_CLK + select SPL_CLK_CCF + help + This enables SPL DM/DTS support for clock driver in i.MX6Q platforms. + +config CLK_IMX6Q + bool "Clock support for i.MX6Q" + depends on ARCH_MX6 + select CLK + select CLK_CCF + help + This enables DM/DTS support for clock driver in i.MX6Q platforms. + config CLK_IMX8 bool "Clock support for i.MX8" depends on ARCH_IMX8 diff --git a/drivers/clk/imx/Makefile b/drivers/clk/imx/Makefile index eb379c188a..105a58ca90 100644 --- a/drivers/clk/imx/Makefile +++ b/drivers/clk/imx/Makefile @@ -2,6 +2,8 @@ # # SPDX-License-Identifier: GPL-2.0
+obj-$(CONFIG_$(SPL_TPL_)CLK_CCF) += clk-gate2.o clk-pllv3.o clk-pfd.o +obj-$(CONFIG_$(SPL_TPL_)CLK_IMX6Q) += clk-imx6q.o obj-$(CONFIG_CLK_IMX8) += clk-imx8.o
ifdef CONFIG_CLK_IMX8 diff --git a/drivers/clk/imx/clk-gate2.c b/drivers/clk/imx/clk-gate2.c new file mode 100644 index 0000000000..571be32088 --- /dev/null +++ b/drivers/clk/imx/clk-gate2.c @@ -0,0 +1,103 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (C) 2019 DENX Software Engineering + * Lukasz Majewski, DENX Software Engineering, lukma@denx.de + * + * Copyright (C) 2010-2011 Canonical Ltd jeremy.kerr@canonical.com + * Copyright (C) 2011-2012 Mike Turquette, Linaro Ltd mturquette@linaro.org + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Gated clock implementation + * + */ + +#include <common.h> +#include <asm/io.h> +#include <malloc.h> +#include <clk-uclass.h> +#include <dm/device.h> +#include <linux/clk-provider.h> +#include <clk.h> +#include "clk.h" + +#define UBOOT_DM_CLK_IMX_GATE2 "imx_clk_gate2" + +struct clk_gate2 { + struct clk clk; + void __iomem *reg; + u8 bit_idx; + u8 cgr_val; + u8 flags; +}; + +#define to_clk_gate2(_clk) container_of(_clk, struct clk_gate2, clk) + +static int clk_gate2_enable(struct clk *clk) +{ + struct clk_gate2 *gate = to_clk_gate2(dev_get_clk_ptr(clk->dev)); + u32 reg; + + reg = readl(gate->reg); + reg &= ~(3 << gate->bit_idx); + reg |= gate->cgr_val << gate->bit_idx; + writel(reg, gate->reg); + + return 0; +} + +static int clk_gate2_disable(struct clk *clk) +{ + struct clk_gate2 *gate = to_clk_gate2(dev_get_clk_ptr(clk->dev)); + u32 reg; + + reg = readl(gate->reg); + reg &= ~(3 << gate->bit_idx); + writel(reg, gate->reg); + + return 0; +} + +static const struct clk_ops clk_gate2_ops = { + .enable = clk_gate2_enable, + .disable = clk_gate2_disable, + .get_rate = clk_generic_get_rate, +}; + +struct clk *clk_register_gate2(struct device *dev, const char *name, + const char *parent_name, unsigned long flags, + void __iomem *reg, u8 bit_idx, u8 cgr_val, + u8 clk_gate2_flags) +{ + struct clk_gate2 *gate; + struct clk *clk; + int ret; + + gate = kzalloc(sizeof(*gate), GFP_KERNEL); + if (!gate) + return ERR_PTR(-ENOMEM); + + gate->reg = reg; + gate->bit_idx = bit_idx; + gate->cgr_val = cgr_val; + gate->flags = clk_gate2_flags; + + clk = &gate->clk; + + ret = clk_register(clk, UBOOT_DM_CLK_IMX_GATE2, name, parent_name); + if (ret) { + kfree(gate); + return ERR_PTR(ret); + } + + return clk; +} + +U_BOOT_DRIVER(clk_gate2) = { + .name = UBOOT_DM_CLK_IMX_GATE2, + .id = UCLASS_CLK, + .ops = &clk_gate2_ops, + .flags = DM_FLAG_PRE_RELOC, +}; diff --git a/drivers/clk/imx/clk-imx6q.c b/drivers/clk/imx/clk-imx6q.c new file mode 100644 index 0000000000..92e9337d44 --- /dev/null +++ b/drivers/clk/imx/clk-imx6q.c @@ -0,0 +1,179 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2019 DENX Software Engineering + * Lukasz Majewski, DENX Software Engineering, lukma@denx.de + */ + +#include <common.h> +#include <clk-uclass.h> +#include <dm.h> +#include <asm/arch/clock.h> +#include <asm/arch/imx-regs.h> +#include <dt-bindings/clock/imx6qdl-clock.h> + +#include "clk.h" + +static int imx6q_check_id(ulong id) +{ + if (id < IMX6QDL_CLK_DUMMY || id >= IMX6QDL_CLK_END) { + printf("%s: Invalid clk ID #%lu\n", __func__, id); + return -EINVAL; + } + + return 0; +} + +static ulong imx6q_clk_get_rate(struct clk *clk) +{ + struct clk *c; + int ret; + + debug("%s(#%lu)\n", __func__, clk->id); + + ret = imx6q_check_id(clk->id); + if (ret) + return ret; + + ret = clk_get_by_id(clk->id, &c); + if (ret) + return ret; + + return clk_get_rate(c); +} + +static ulong imx6q_clk_set_rate(struct clk *clk, unsigned long rate) +{ + debug("%s(#%lu), rate: %lu\n", __func__, clk->id, rate); + + return rate; +} + +static int __imx6q_clk_enable(struct clk *clk, bool enable) +{ + struct clk *c; + int ret = 0; + + debug("%s(#%lu) en: %d\n", __func__, clk->id, enable); + + ret = imx6q_check_id(clk->id); + if (ret) + return ret; + + ret = clk_get_by_id(clk->id, &c); + if (ret) + return ret; + + if (enable) + ret = clk_enable(c); + else + ret = clk_disable(c); + + return ret; +} + +static int imx6q_clk_disable(struct clk *clk) +{ + return __imx6q_clk_enable(clk, 0); +} + +static int imx6q_clk_enable(struct clk *clk) +{ + return __imx6q_clk_enable(clk, 1); +} + +static struct clk_ops imx6q_clk_ops = { + .set_rate = imx6q_clk_set_rate, + .get_rate = imx6q_clk_get_rate, + .enable = imx6q_clk_enable, + .disable = imx6q_clk_disable, +}; + +static const char *const usdhc_sels[] = { "pll2_pfd2_396m", "pll2_pfd0_352m", }; + +static int imx6q_clk_probe(struct udevice *dev) +{ + void *base; + + /* Anatop clocks */ + base = (void *)ANATOP_BASE_ADDR; + + clk_dm(IMX6QDL_CLK_PLL2, + imx_clk_pllv3(IMX_PLLV3_GENERIC, "pll2_bus", "osc", + base + 0x30, 0x1)); + clk_dm(IMX6QDL_CLK_PLL3_USB_OTG, + imx_clk_pllv3(IMX_PLLV3_USB, "pll3_usb_otg", "osc", + base + 0x10, 0x3)); + clk_dm(IMX6QDL_CLK_PLL3_60M, + imx_clk_fixed_factor("pll3_60m", "pll3_usb_otg", 1, 8)); + clk_dm(IMX6QDL_CLK_PLL2_PFD0_352M, + imx_clk_pfd("pll2_pfd0_352m", "pll2_bus", base + 0x100, 0)); + clk_dm(IMX6QDL_CLK_PLL2_PFD2_396M, + imx_clk_pfd("pll2_pfd2_396m", "pll2_bus", base + 0x100, 2)); + + /* CCM clocks */ + base = dev_read_addr_ptr(dev); + if (base == (void *)FDT_ADDR_T_NONE) + return -EINVAL; + + clk_dm(IMX6QDL_CLK_USDHC1_SEL, + imx_clk_mux("usdhc1_sel", base + 0x1c, 16, 1, + usdhc_sels, ARRAY_SIZE(usdhc_sels))); + clk_dm(IMX6QDL_CLK_USDHC2_SEL, + imx_clk_mux("usdhc2_sel", base + 0x1c, 17, 1, + usdhc_sels, ARRAY_SIZE(usdhc_sels))); + clk_dm(IMX6QDL_CLK_USDHC3_SEL, + imx_clk_mux("usdhc3_sel", base + 0x1c, 18, 1, + usdhc_sels, ARRAY_SIZE(usdhc_sels))); + clk_dm(IMX6QDL_CLK_USDHC4_SEL, + imx_clk_mux("usdhc4_sel", base + 0x1c, 19, 1, + usdhc_sels, ARRAY_SIZE(usdhc_sels))); + + clk_dm(IMX6QDL_CLK_USDHC1_PODF, + imx_clk_divider("usdhc1_podf", "usdhc1_sel", + base + 0x24, 11, 3)); + clk_dm(IMX6QDL_CLK_USDHC2_PODF, + imx_clk_divider("usdhc2_podf", "usdhc2_sel", + base + 0x24, 16, 3)); + clk_dm(IMX6QDL_CLK_USDHC3_PODF, + imx_clk_divider("usdhc3_podf", "usdhc3_sel", + base + 0x24, 19, 3)); + clk_dm(IMX6QDL_CLK_USDHC4_PODF, + imx_clk_divider("usdhc4_podf", "usdhc4_sel", + base + 0x24, 22, 3)); + + clk_dm(IMX6QDL_CLK_ECSPI_ROOT, + imx_clk_divider("ecspi_root", "pll3_60m", base + 0x38, 19, 6)); + + clk_dm(IMX6QDL_CLK_ECSPI1, + imx_clk_gate2("ecspi1", "ecspi_root", base + 0x6c, 0)); + clk_dm(IMX6QDL_CLK_ECSPI2, + imx_clk_gate2("ecspi2", "ecspi_root", base + 0x6c, 2)); + clk_dm(IMX6QDL_CLK_ECSPI3, + imx_clk_gate2("ecspi3", "ecspi_root", base + 0x6c, 4)); + clk_dm(IMX6QDL_CLK_ECSPI4, + imx_clk_gate2("ecspi4", "ecspi_root", base + 0x6c, 6)); + clk_dm(IMX6QDL_CLK_USDHC1, + imx_clk_gate2("usdhc1", "usdhc1_podf", base + 0x80, 2)); + clk_dm(IMX6QDL_CLK_USDHC2, + imx_clk_gate2("usdhc2", "usdhc2_podf", base + 0x80, 4)); + clk_dm(IMX6QDL_CLK_USDHC3, + imx_clk_gate2("usdhc3", "usdhc3_podf", base + 0x80, 6)); + clk_dm(IMX6QDL_CLK_USDHC4, + imx_clk_gate2("usdhc4", "usdhc4_podf", base + 0x80, 8)); + + return 0; +} + +static const struct udevice_id imx6q_clk_ids[] = { + { .compatible = "fsl,imx6q-ccm" }, + { }, +}; + +U_BOOT_DRIVER(imx6q_clk) = { + .name = "clk_imx6q", + .id = UCLASS_CLK, + .of_match = imx6q_clk_ids, + .ops = &imx6q_clk_ops, + .probe = imx6q_clk_probe, + .flags = DM_FLAG_PRE_RELOC, +}; diff --git a/drivers/clk/imx/clk-pfd.c b/drivers/clk/imx/clk-pfd.c new file mode 100644 index 0000000000..188b2b3b90 --- /dev/null +++ b/drivers/clk/imx/clk-pfd.c @@ -0,0 +1,90 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (C) 2019 DENX Software Engineering + * Lukasz Majewski, DENX Software Engineering, lukma@denx.de + * + * Copyright 2012 Freescale Semiconductor, Inc. + * Copyright 2012 Linaro Ltd. + * + * The code contained herein is licensed under the GNU General Public + * License. You may obtain a copy of the GNU General Public License + * Version 2 or later at the following locations: + * + * http://www.opensource.org/licenses/gpl-license.html + * http://www.gnu.org/copyleft/gpl.html + */ + +#include <common.h> +#include <asm/io.h> +#include <malloc.h> +#include <clk-uclass.h> +#include <dm/device.h> +#include <linux/clk-provider.h> +#include <div64.h> +#include <clk.h> +#include "clk.h" + +#define UBOOT_DM_CLK_IMX_PFD "imx_clk_pfd" + +struct clk_pfd { + struct clk clk; + void __iomem *reg; + u8 idx; +}; + +#define to_clk_pfd(_clk) container_of(_clk, struct clk_pfd, clk) + +#define SET 0x4 +#define CLR 0x8 +#define OTG 0xc + +static unsigned long clk_pfd_recalc_rate(struct clk *clk) +{ + struct clk_pfd *pfd = + to_clk_pfd(dev_get_clk_ptr(clk->dev)); + unsigned long parent_rate = clk_get_parent_rate(clk); + u64 tmp = parent_rate; + u8 frac = (readl(pfd->reg) >> (pfd->idx * 8)) & 0x3f; + + tmp *= 18; + do_div(tmp, frac); + + return tmp; +} + +static const struct clk_ops clk_pfd_ops = { + .get_rate = clk_pfd_recalc_rate, +}; + +struct clk *imx_clk_pfd(const char *name, const char *parent_name, + void __iomem *reg, u8 idx) +{ + struct clk_pfd *pfd; + struct clk *clk; + int ret; + + pfd = kzalloc(sizeof(*pfd), GFP_KERNEL); + if (!pfd) + return ERR_PTR(-ENOMEM); + + pfd->reg = reg; + pfd->idx = idx; + + /* register the clock */ + clk = &pfd->clk; + + ret = clk_register(clk, UBOOT_DM_CLK_IMX_PFD, name, parent_name); + if (ret) { + kfree(pfd); + return ERR_PTR(ret); + } + + return clk; +} + +U_BOOT_DRIVER(clk_pfd) = { + .name = UBOOT_DM_CLK_IMX_PFD, + .id = UCLASS_CLK, + .ops = &clk_pfd_ops, + .flags = DM_FLAG_PRE_RELOC, +}; diff --git a/drivers/clk/imx/clk-pllv3.c b/drivers/clk/imx/clk-pllv3.c new file mode 100644 index 0000000000..fbb7b24d5e --- /dev/null +++ b/drivers/clk/imx/clk-pllv3.c @@ -0,0 +1,82 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (C) 2019 DENX Software Engineering + * Lukasz Majewski, DENX Software Engineering, lukma@denx.de + */ + +#include <common.h> +#include <asm/io.h> +#include <malloc.h> +#include <clk-uclass.h> +#include <dm/device.h> +#include <dm/uclass.h> +#include <clk.h> +#include "clk.h" + +#define UBOOT_DM_CLK_IMX_PLLV3 "imx_clk_pllv3" + +struct clk_pllv3 { + struct clk clk; + void __iomem *base; + u32 div_mask; + u32 div_shift; +}; + +#define to_clk_pllv3(_clk) container_of(_clk, struct clk_pllv3, clk) + +static ulong clk_pllv3_get_rate(struct clk *clk) +{ + struct clk_pllv3 *pll = to_clk_pllv3(dev_get_clk_ptr(clk->dev)); + unsigned long parent_rate = clk_get_parent_rate(clk); + + u32 div = (readl(pll->base) >> pll->div_shift) & pll->div_mask; + + return (div == 1) ? parent_rate * 22 : parent_rate * 20; +} + +static const struct clk_ops clk_pllv3_generic_ops = { + .get_rate = clk_pllv3_get_rate, +}; + +struct clk *imx_clk_pllv3(enum imx_pllv3_type type, const char *name, + const char *parent_name, void __iomem *base, + u32 div_mask) +{ + struct clk_pllv3 *pll; + struct clk *clk; + char *drv_name; + int ret; + + pll = kzalloc(sizeof(*pll), GFP_KERNEL); + if (!pll) + return ERR_PTR(-ENOMEM); + + switch (type) { + case IMX_PLLV3_GENERIC: + case IMX_PLLV3_USB: + drv_name = UBOOT_DM_CLK_IMX_PLLV3; + break; + default: + kfree(pll); + return ERR_PTR(-ENOTSUPP); + } + + pll->base = base; + pll->div_mask = div_mask; + clk = &pll->clk; + + ret = clk_register(clk, drv_name, name, parent_name); + if (ret) { + kfree(pll); + return ERR_PTR(ret); + } + + return clk; +} + +U_BOOT_DRIVER(clk_pllv3_generic) = { + .name = UBOOT_DM_CLK_IMX_PLLV3, + .id = UCLASS_CLK, + .ops = &clk_pllv3_generic_ops, + .flags = DM_FLAG_PRE_RELOC, +}; diff --git a/drivers/clk/imx/clk.h b/drivers/clk/imx/clk.h new file mode 100644 index 0000000000..e6d51830e8 --- /dev/null +++ b/drivers/clk/imx/clk.h @@ -0,0 +1,69 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (C) 2019 DENX Software Engineering + * Lukasz Majewski, DENX Software Engineering, lukma@denx.de + */ +#ifndef __MACH_IMX_CLK_H +#define __MACH_IMX_CLK_H + +#include <linux/clk-provider.h> + +enum imx_pllv3_type { + IMX_PLLV3_GENERIC, + IMX_PLLV3_SYS, + IMX_PLLV3_USB, + IMX_PLLV3_USB_VF610, + IMX_PLLV3_AV, + IMX_PLLV3_ENET, + IMX_PLLV3_ENET_IMX7, + IMX_PLLV3_SYS_VF610, + IMX_PLLV3_DDR_IMX7, +}; + +struct clk *clk_register_gate2(struct device *dev, const char *name, + const char *parent_name, unsigned long flags, + void __iomem *reg, u8 bit_idx, u8 cgr_val, + u8 clk_gate_flags); + +struct clk *imx_clk_pllv3(enum imx_pllv3_type type, const char *name, + const char *parent_name, void __iomem *base, + u32 div_mask); + +static inline struct clk *imx_clk_gate2(const char *name, const char *parent, + void __iomem *reg, u8 shift) +{ + return clk_register_gate2(NULL, name, parent, CLK_SET_RATE_PARENT, reg, + shift, 0x3, 0); +} + +static inline struct clk *imx_clk_fixed_factor(const char *name, + const char *parent, unsigned int mult, unsigned int div) +{ + return clk_register_fixed_factor(NULL, name, parent, + CLK_SET_RATE_PARENT, mult, div); +} + +static inline struct clk *imx_clk_divider(const char *name, const char *parent, + void __iomem *reg, u8 shift, u8 width) +{ + return clk_register_divider(NULL, name, parent, CLK_SET_RATE_PARENT, + reg, shift, width, 0); +} + +struct clk *imx_clk_pfd(const char *name, const char *parent_name, + void __iomem *reg, u8 idx); + +struct clk *imx_clk_fixup_mux(const char *name, void __iomem *reg, + u8 shift, u8 width, const char * const *parents, + int num_parents, void (*fixup)(u32 *val)); + +static inline struct clk *imx_clk_mux(const char *name, void __iomem *reg, + u8 shift, u8 width, const char * const *parents, + int num_parents) +{ + return clk_register_mux(NULL, name, parents, num_parents, + CLK_SET_RATE_NO_REPARENT, reg, shift, + width, 0); +} + +#endif /* __MACH_IMX_CLK_H */ diff --git a/include/linux/clk-provider.h b/include/linux/clk-provider.h index 6adf0a5791..e06487f07b 100644 --- a/include/linux/clk-provider.h +++ b/include/linux/clk-provider.h @@ -9,6 +9,115 @@ #ifndef __LINUX_CLK_PROVIDER_H #define __LINUX_CLK_PROVIDER_H
+static inline void clk_dm(ulong id, struct clk *clk) +{ + if (!IS_ERR(clk)) + clk->id = id; +} + +/* + * flags used across common struct clk. these flags should only affect the + * top-level framework. custom flags for dealing with hardware specifics + * belong in struct clk_foo + * + * Please update clk_flags[] in drivers/clk/clk.c when making changes here! + */ +#define CLK_SET_RATE_GATE BIT(0) /* must be gated across rate change */ +#define CLK_SET_PARENT_GATE BIT(1) /* must be gated across re-parent */ +#define CLK_SET_RATE_PARENT BIT(2) /* propagate rate change up one level */ +#define CLK_IGNORE_UNUSED BIT(3) /* do not gate even if unused */ + /* unused */ +#define CLK_IS_BASIC BIT(5) /* Basic clk, can't do a to_clk_foo() */ +#define CLK_GET_RATE_NOCACHE BIT(6) /* do not use the cached clk rate */ +#define CLK_SET_RATE_NO_REPARENT BIT(7) /* don't re-parent on rate change */ +#define CLK_GET_ACCURACY_NOCACHE BIT(8) /* do not use the cached clk accuracy */ +#define CLK_RECALC_NEW_RATES BIT(9) /* recalc rates after notifications */ +#define CLK_SET_RATE_UNGATE BIT(10) /* clock needs to run to set rate */ +#define CLK_IS_CRITICAL BIT(11) /* do not gate, ever */ +/* parents need enable during gate/ungate, set rate and re-parent */ +#define CLK_OPS_PARENT_ENABLE BIT(12) +/* duty cycle call may be forwarded to the parent clock */ +#define CLK_DUTY_CYCLE_PARENT BIT(13) + +#define CLK_MUX_INDEX_ONE BIT(0) +#define CLK_MUX_INDEX_BIT BIT(1) +#define CLK_MUX_HIWORD_MASK BIT(2) +#define CLK_MUX_READ_ONLY BIT(3) /* mux can't be changed */ +#define CLK_MUX_ROUND_CLOSEST BIT(4) + +struct clk_mux { + struct clk clk; + void __iomem *reg; + u32 *table; + u32 mask; + u8 shift; + u8 flags; + + /* + * Fields from struct clk_init_data - this struct has been + * omitted to avoid too deep level of CCF for bootloader + */ + const char * const *parent_names; + u8 num_parents; +}; + +#define to_clk_mux(_clk) container_of(_clk, struct clk_mux, clk) + +struct clk_div_table { + unsigned int val; + unsigned int div; +}; + +struct clk_divider { + struct clk clk; + void __iomem *reg; + u8 shift; + u8 width; + u8 flags; + const struct clk_div_table *table; +}; + +#define clk_div_mask(width) ((1 << (width)) - 1) +#define to_clk_divider(_clk) container_of(_clk, struct clk_divider, clk) + +#define CLK_DIVIDER_ONE_BASED BIT(0) +#define CLK_DIVIDER_POWER_OF_TWO BIT(1) +#define CLK_DIVIDER_ALLOW_ZERO BIT(2) +#define CLK_DIVIDER_HIWORD_MASK BIT(3) +#define CLK_DIVIDER_ROUND_CLOSEST BIT(4) +#define CLK_DIVIDER_READ_ONLY BIT(5) +#define CLK_DIVIDER_MAX_AT_ZERO BIT(6) + +struct clk_fixed_factor { + struct clk clk; + unsigned int mult; + unsigned int div; +}; + +#define to_clk_fixed_factor(_clk) container_of(_clk, struct clk_fixed_factor,\ + clk) + +int clk_register(struct clk *clk, const char *drv_name, const char *name, + const char *parent_name); + +struct clk *clk_register_fixed_factor(struct device *dev, const char *name, + const char *parent_name, unsigned long flags, + unsigned int mult, unsigned int div); + +struct clk *clk_register_divider(struct device *dev, const char *name, + const char *parent_name, unsigned long flags, + void __iomem *reg, u8 shift, u8 width, + u8 clk_divider_flags); + +struct clk *clk_register_mux(struct device *dev, const char *name, + const char * const *parent_names, u8 num_parents, + unsigned long flags, + void __iomem *reg, u8 shift, u8 width, + u8 clk_mux_flags); + +const char *clk_hw_get_name(const struct clk *hw); +ulong clk_generic_get_rate(struct clk *clk); + static inline struct clk *dev_get_clk_ptr(struct udevice *dev) { return (struct clk *)dev_get_uclass_priv(dev);

This patch brings the files from Linux kernel (linux-stable/linux-5.1.y SHA1: 5752b50477da)to provide clocks support as it is used on the Linux kernel with Common Clock Framework [CCF] setup. The directory structure has been preserved. The ported code only supports reading information from PLL, MUX, Divider, etc and enabling/disabling the clocks USDHCx/ECSPIx depending on used bus. Moreover, it is agnostic to the alias numbering as the information about the clock is read from the device tree. One needs to pay attention to the comments indicating necessary for U-Boot's driver model changes. If needed, the code can be extended to support the "set" part of the clock management. Signed-off-by: Lukasz Majewski lukma@denx.de
Applied to u-boot-imx, master, thanks !
Best regards, Stefano Babic

If the CLK_GET_RATE_NOCACHE flag is set - the clk_get_parent_rate() provides recalculated clock value without considering the cache setting.
This may be necessary for some clocks tightly coupled with power domains (i.e. imx8), and prevents from reading invalid cached values.
Signed-off-by: Lukasz Majewski lukma@denx.de Reviewed-by: Peng Fan peng.fan@nxp.com ---
Changes in v6: None Changes in v5: None Changes in v4: None Changes in v3: None
drivers/clk/clk-uclass.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/drivers/clk/clk-uclass.c b/drivers/clk/clk-uclass.c index 506ba6014c..5acf186b01 100644 --- a/drivers/clk/clk-uclass.c +++ b/drivers/clk/clk-uclass.c @@ -410,8 +410,8 @@ long long clk_get_parent_rate(struct clk *clk) if (!ops->get_rate) return -ENOSYS;
- /* Read the 'rate' if not already set */ - if (!pclk->rate) + /* Read the 'rate' if not already set or if proper flag set*/ + if (!pclk->rate || pclk->flags & CLK_GET_RATE_NOCACHE) pclk->rate = clk_get_rate(pclk);
return pclk->rate;

If the CLK_GET_RATE_NOCACHE flag is set - the clk_get_parent_rate() provides recalculated clock value without considering the cache setting. This may be necessary for some clocks tightly coupled with power domains (i.e. imx8), and prevents from reading invalid cached values. Signed-off-by: Lukasz Majewski lukma@denx.de Reviewed-by: Peng Fan peng.fan@nxp.com
Applied to u-boot-imx, master, thanks !
Best regards, Stefano Babic

This patch adds the 'osc' fixed clock to facilitate the CCF testing in the sandbox U-Boot. It is a starting point for building CCF hierarchy of clocks.
Signed-off-by: Lukasz Majewski lukma@denx.de ---
Changes in v6: None Changes in v5: None Changes in v4: None Changes in v3: None
arch/sandbox/dts/test.dts | 6 ++++++ 1 file changed, 6 insertions(+)
diff --git a/arch/sandbox/dts/test.dts b/arch/sandbox/dts/test.dts index 8b2d6451c6..b56c980c9d 100644 --- a/arch/sandbox/dts/test.dts +++ b/arch/sandbox/dts/test.dts @@ -211,6 +211,12 @@ clock-mult = <2>; clocks = <&clk_fixed>; }; + + osc { + compatible = "fixed-clock"; + #clock-cells = <0>; + clock-frequency = <20000000>; + }; };
clk_sandbox: clk-sbox {

This patch adds the 'osc' fixed clock to facilitate the CCF testing in the sandbox U-Boot. It is a starting point for building CCF hierarchy of clocks. Signed-off-by: Lukasz Majewski lukma@denx.de
Applied to u-boot-imx, master, thanks !
Best regards, Stefano Babic

The generic divider clock code for CCF requires reading the divider value from HW registers. As sandbox by design has readl() as no-op it was necessary to provide this value in the other way.
The new field in the divider structure (accessible only when sandbox is run) has been introduced for this purpose.
Signed-off-by: Lukasz Majewski lukma@denx.de ---
Changes in v6: None Changes in v5: None Changes in v4: None Changes in v3: None
drivers/clk/clk-divider.c | 10 +++++++++- include/linux/clk-provider.h | 3 +++ 2 files changed, 12 insertions(+), 1 deletion(-)
diff --git a/drivers/clk/clk-divider.c b/drivers/clk/clk-divider.c index 3348d97829..6921c76a48 100644 --- a/drivers/clk/clk-divider.c +++ b/drivers/clk/clk-divider.c @@ -74,7 +74,12 @@ static ulong clk_divider_recalc_rate(struct clk *clk) unsigned long parent_rate = clk_get_parent_rate(clk); unsigned int val;
- val = readl(divider->reg) >> divider->shift; +#if CONFIG_IS_ENABLED(SANDBOX_CLK_CCF) + val = divider->io_divider_val; +#else + val = readl(divider->reg); +#endif + val >>= divider->shift; val &= clk_div_mask(divider->width);
return divider_recalc_rate(clk, parent_rate, val, divider->table, @@ -112,6 +117,9 @@ static struct clk *_register_divider(struct device *dev, const char *name, div->width = width; div->flags = clk_divider_flags; div->table = table; +#if CONFIG_IS_ENABLED(SANDBOX_CLK_CCF) + div->io_divider_val = *(u32 *)reg; +#endif
/* register the clock */ clk = &div->clk; diff --git a/include/linux/clk-provider.h b/include/linux/clk-provider.h index e06487f07b..53c9c41b90 100644 --- a/include/linux/clk-provider.h +++ b/include/linux/clk-provider.h @@ -75,6 +75,9 @@ struct clk_divider { u8 width; u8 flags; const struct clk_div_table *table; +#if CONFIG_IS_ENABLED(SANDBOX_CLK_CCF) + u32 io_divider_val; +#endif };
#define clk_div_mask(width) ((1 << (width)) - 1)

The generic divider clock code for CCF requires reading the divider value from HW registers. As sandbox by design has readl() as no-op it was necessary to provide this value in the other way. The new field in the divider structure (accessible only when sandbox is run) has been introduced for this purpose. Signed-off-by: Lukasz Majewski lukma@denx.de
Applied to u-boot-imx, master, thanks !
Best regards, Stefano Babic

The generic mux clock code for CCF requires reading the clock multiplexer value from HW registers. As sandbox by design has readl() as no-op it was necessary to provide this value in the other way.
The new field in the mux structure (accessible only when sandbox is run) has been introduced for this purpose.
Signed-off-by: Lukasz Majewski lukma@denx.de ---
Changes in v6: None Changes in v5: None Changes in v4: None Changes in v3: None
drivers/clk/clk-mux.c | 10 +++++++++- include/linux/clk-provider.h | 4 ++++ 2 files changed, 13 insertions(+), 1 deletion(-)
diff --git a/drivers/clk/clk-mux.c b/drivers/clk/clk-mux.c index 14b9f2b1d9..3c075aa09e 100644 --- a/drivers/clk/clk-mux.c +++ b/drivers/clk/clk-mux.c @@ -64,7 +64,12 @@ static u8 clk_mux_get_parent(struct clk *clk) struct clk_mux *mux = to_clk_mux(clk); u32 val;
- val = readl(mux->reg) >> mux->shift; +#if CONFIG_IS_ENABLED(SANDBOX_CLK_CCF) + val = mux->io_mux_val; +#else + val = readl(mux->reg); +#endif + val >>= mux->shift; val &= mux->mask;
return clk_mux_val_to_index(clk, mux->table, mux->flags, val); @@ -108,6 +113,9 @@ struct clk *clk_hw_register_mux_table(struct device *dev, const char *name, mux->mask = mask; mux->flags = clk_mux_flags; mux->table = table; +#if CONFIG_IS_ENABLED(SANDBOX_CLK_CCF) + mux->io_mux_val = *(u32 *)reg; +#endif
clk = &mux->clk;
diff --git a/include/linux/clk-provider.h b/include/linux/clk-provider.h index 53c9c41b90..43a25e9c6a 100644 --- a/include/linux/clk-provider.h +++ b/include/linux/clk-provider.h @@ -59,6 +59,10 @@ struct clk_mux { */ const char * const *parent_names; u8 num_parents; +#if CONFIG_IS_ENABLED(SANDBOX_CLK_CCF) + u32 io_mux_val; +#endif + };
#define to_clk_mux(_clk) container_of(_clk, struct clk_mux, clk)

The generic mux clock code for CCF requires reading the clock multiplexer value from HW registers. As sandbox by design has readl() as no-op it was necessary to provide this value in the other way. The new field in the mux structure (accessible only when sandbox is run) has been introduced for this purpose. Signed-off-by: Lukasz Majewski lukma@denx.de
Applied to u-boot-imx, master, thanks !
Best regards, Stefano Babic

This patch provides code to implement the CCF clock tree in sandbox. It uses all the introduced primitives; some generic ones are reused, some sandbox specific were developed.
In that way (after introducing the real CCF tree in sandbox) the recently added to clk-uclass.c: clk_get_by_id() and clk_get_parent_rate() are tested in their natural work environment.
Usage (sandbox_defconfig and sandbox_flattree_defconfig): ./u-boot --fdt arch/sandbox/dts/test.dtb --command "ut dm clk_ccf"
Signed-off-by: Lukasz Majewski lukma@denx.de ---
Changes in v6: None Changes in v5: None Changes in v4: None Changes in v3: None
arch/sandbox/dts/test.dts | 4 + drivers/clk/Kconfig | 10 ++- drivers/clk/Makefile | 1 + drivers/clk/clk_sandbox_ccf.c | 185 ++++++++++++++++++++++++++++++++++++++++++ include/sandbox-clk.h | 76 +++++++++++++++++ test/dm/Makefile | 2 +- test/dm/clk_ccf.c | 62 ++++++++++++++ 7 files changed, 338 insertions(+), 2 deletions(-) create mode 100644 drivers/clk/clk_sandbox_ccf.c create mode 100644 include/sandbox-clk.h create mode 100644 test/dm/clk_ccf.c
diff --git a/arch/sandbox/dts/test.dts b/arch/sandbox/dts/test.dts index b56c980c9d..da12adb8ce 100644 --- a/arch/sandbox/dts/test.dts +++ b/arch/sandbox/dts/test.dts @@ -232,6 +232,10 @@ clock-names = "fixed", "i2c", "spi"; };
+ ccf: clk-ccf { + compatible = "sandbox,clk-ccf"; + }; + eth@10002000 { compatible = "sandbox,eth"; reg = <0x10002000 0x1000>; diff --git a/drivers/clk/Kconfig b/drivers/clk/Kconfig index 9c2b284d02..9399bb79e9 100644 --- a/drivers/clk/Kconfig +++ b/drivers/clk/Kconfig @@ -55,7 +55,7 @@ config SPL_CLK_CCF
config CLK_CCF bool "Common Clock Framework [CCF] support " - depends on CLK_IMX6Q + depends on CLK_IMX6Q || SANDBOX_CLK_CCF help Enable this option if you want to (re-)use the Linux kernel's Common Clock Framework [CCF] code in U-Boot's clock driver. @@ -138,4 +138,12 @@ config CLK_MPC83XX help Support for the clock driver of the MPC83xx series of SoCs.
+config SANDBOX_CLK_CCF + bool "Sandbox Common Clock Framework [CCF] support " + depends on SANDBOX + select CLK_CCF + help + Enable this option if you want to test the Linux kernel's Common + Clock Framework [CCF] code in U-Boot's Sandbox clock driver. + endmenu diff --git a/drivers/clk/Makefile b/drivers/clk/Makefile index 1a45bf3505..37dfd281e9 100644 --- a/drivers/clk/Makefile +++ b/drivers/clk/Makefile @@ -38,5 +38,6 @@ obj-$(CONFIG_ICS8N3QV01) += ics8n3qv01.o obj-$(CONFIG_MACH_PIC32) += clk_pic32.o obj-$(CONFIG_SANDBOX) += clk_sandbox.o obj-$(CONFIG_SANDBOX) += clk_sandbox_test.o +obj-$(CONFIG_SANDBOX_CLK_CCF) += clk_sandbox_ccf.o obj-$(CONFIG_STM32H7) += clk_stm32h7.o obj-$(CONFIG_CLK_TI_SCI) += clk-ti-sci.o diff --git a/drivers/clk/clk_sandbox_ccf.c b/drivers/clk/clk_sandbox_ccf.c new file mode 100644 index 0000000000..edeb0f2cf3 --- /dev/null +++ b/drivers/clk/clk_sandbox_ccf.c @@ -0,0 +1,185 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (C) 2019 + * Lukasz Majewski, DENX Software Engineering, lukma@denx.de + * + * Common Clock Framework [CCF] driver for Sandbox + */ + +#include <common.h> +#include <dm.h> +#include <clk.h> +#include <asm/clk.h> +#include <clk-uclass.h> +#include <linux/clk-provider.h> +#include <sandbox-clk.h> + +/* + * Sandbox implementation of CCF primitives necessary for clk-uclass testing + * + * --- Sandbox PLLv3 --- + */ +struct clk_pllv3 { + struct clk clk; + u32 div_mask; + u32 div_shift; +}; + +static ulong clk_pllv3_get_rate(struct clk *clk) +{ + unsigned long parent_rate = clk_get_parent_rate(clk); + + return parent_rate * 24; +} + +static const struct clk_ops clk_pllv3_generic_ops = { + .get_rate = clk_pllv3_get_rate, +}; + +struct clk *sandbox_clk_pllv3(enum sandbox_pllv3_type type, const char *name, + const char *parent_name, void __iomem *base, + u32 div_mask) +{ + struct clk_pllv3 *pll; + struct clk *clk; + char *drv_name = "sandbox_clk_pllv3"; + int ret; + + pll = kzalloc(sizeof(*pll), GFP_KERNEL); + if (!pll) + return ERR_PTR(-ENOMEM); + + pll->div_mask = div_mask; + clk = &pll->clk; + + ret = clk_register(clk, drv_name, name, parent_name); + if (ret) { + kfree(pll); + return ERR_PTR(ret); + } + + return clk; +} + +U_BOOT_DRIVER(sandbox_clk_pll_generic) = { + .name = "sandbox_clk_pllv3", + .id = UCLASS_CLK, + .ops = &clk_pllv3_generic_ops, +}; + +/* --- Sandbox PLLv3 --- */ +/* --- Sandbox Gate --- */ +struct clk_gate2 { + struct clk clk; + bool state; +}; + +#define to_clk_gate2(_clk) container_of(_clk, struct clk_gate2, clk) + +static int clk_gate2_enable(struct clk *clk) +{ + struct clk_gate2 *gate = to_clk_gate2(dev_get_clk_ptr(clk->dev)); + + gate->state = 1; + return 0; +} + +static int clk_gate2_disable(struct clk *clk) +{ + struct clk_gate2 *gate = to_clk_gate2(dev_get_clk_ptr(clk->dev)); + + gate->state = 0; + return 0; +} + +static const struct clk_ops clk_gate2_ops = { + .enable = clk_gate2_enable, + .disable = clk_gate2_disable, + .get_rate = clk_generic_get_rate, +}; + +struct clk *sandbox_clk_register_gate2(struct device *dev, const char *name, + const char *parent_name, + unsigned long flags, void __iomem *reg, + u8 bit_idx, u8 cgr_val, + u8 clk_gate2_flags) +{ + struct clk_gate2 *gate; + struct clk *clk; + int ret; + + gate = kzalloc(sizeof(*gate), GFP_KERNEL); + if (!gate) + return ERR_PTR(-ENOMEM); + + gate->state = 0; + clk = &gate->clk; + + ret = clk_register(clk, "sandbox_clk_gate2", name, parent_name); + if (ret) { + kfree(gate); + return ERR_PTR(ret); + } + + return clk; +} + +U_BOOT_DRIVER(sandbox_clk_gate2) = { + .name = "sandbox_clk_gate2", + .id = UCLASS_CLK, + .ops = &clk_gate2_ops, +}; + +/* --- Sandbox Gate --- */ +/* The CCF core driver itself */ +static const struct udevice_id sandbox_clk_ccf_test_ids[] = { + { .compatible = "sandbox,clk-ccf" }, + { } +}; + +static const char *const usdhc_sels[] = { "pll3_60m", "pll3_80m", }; + +static int sandbox_clk_ccf_probe(struct udevice *dev) +{ + void *base = NULL; + u32 reg; + + clk_dm(SANDBOX_CLK_PLL3, + sandbox_clk_pllv3(SANDBOX_PLLV3_USB, "pll3_usb_otg", "osc", + base + 0x10, 0x3)); + + clk_dm(SANDBOX_CLK_PLL3_60M, + sandbox_clk_fixed_factor("pll3_60m", "pll3_usb_otg", 1, 8)); + + clk_dm(SANDBOX_CLK_PLL3_80M, + sandbox_clk_fixed_factor("pll3_80m", "pll3_usb_otg", 1, 6)); + + /* The HW adds +1 to the divider value (2+1) is the divider */ + reg = (2 << 19); + clk_dm(SANDBOX_CLK_ECSPI_ROOT, + sandbox_clk_divider("ecspi_root", "pll3_60m", ®, 19, 6)); + + clk_dm(SANDBOX_CLK_ECSPI1, + sandbox_clk_gate2("ecspi1", "ecspi_root", base + 0x6c, 0)); + + /* Select 'pll3_60m' */ + reg = 0; + clk_dm(SANDBOX_CLK_USDHC1_SEL, + sandbox_clk_mux("usdhc1_sel", ®, 16, 1, usdhc_sels, + ARRAY_SIZE(usdhc_sels))); + + /* Select 'pll3_80m' */ + reg = BIT(17); + clk_dm(SANDBOX_CLK_USDHC2_SEL, + sandbox_clk_mux("usdhc2_sel", ®, 17, 1, usdhc_sels, + ARRAY_SIZE(usdhc_sels))); + + return 0; +} + +U_BOOT_DRIVER(sandbox_clk_ccf) = { + .name = "sandbox_clk_ccf", + .id = UCLASS_CLK, + .probe = sandbox_clk_ccf_probe, + .of_match = sandbox_clk_ccf_test_ids, +}; diff --git a/include/sandbox-clk.h b/include/sandbox-clk.h new file mode 100644 index 0000000000..37c9838f76 --- /dev/null +++ b/include/sandbox-clk.h @@ -0,0 +1,76 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * Copyright (C) 2019 + * Lukasz Majewski, DENX Software Engineering, lukma@denx.de + */ + +#ifndef __SANDBOX_CLK_H__ +#define __SANDBOX_CLK_H__ + +#include <linux/clk-provider.h> + +enum { + SANDBOX_CLK_PLL2 = 1, + SANDBOX_CLK_PLL3, + SANDBOX_CLK_PLL3_60M, + SANDBOX_CLK_PLL3_80M, + SANDBOX_CLK_ECSPI_ROOT, + SANDBOX_CLK_ECSPI0, + SANDBOX_CLK_ECSPI1, + SANDBOX_CLK_USDHC1_SEL, + SANDBOX_CLK_USDHC2_SEL, +}; + +enum sandbox_pllv3_type { + SANDBOX_PLLV3_GENERIC, + SANDBOX_PLLV3_USB, +}; + +struct clk *sandbox_clk_pllv3(enum sandbox_pllv3_type type, const char *name, + const char *parent_name, void __iomem *base, + u32 div_mask); + +static inline struct clk *sandbox_clk_fixed_factor(const char *name, + const char *parent, + unsigned int mult, + unsigned int div) +{ + return clk_register_fixed_factor(NULL, name, parent, + CLK_SET_RATE_PARENT, mult, div); +} + +static inline struct clk *sandbox_clk_divider(const char *name, + const char *parent, + void __iomem *reg, u8 shift, + u8 width) +{ + return clk_register_divider(NULL, name, parent, CLK_SET_RATE_PARENT, + reg, shift, width, 0); +} + +struct clk *sandbox_clk_register_gate2(struct device *dev, const char *name, + const char *parent_name, + unsigned long flags, + void __iomem *reg, u8 bit_idx, + u8 cgr_val, u8 clk_gate_flags); + +static inline struct clk *sandbox_clk_gate2(const char *name, + const char *parent, + void __iomem *reg, u8 shift) +{ + return sandbox_clk_register_gate2(NULL, name, parent, + CLK_SET_RATE_PARENT, reg, shift, + 0x3, 0); +} + +static inline struct clk *sandbox_clk_mux(const char *name, void __iomem *reg, + u8 shift, u8 width, + const char * const *parents, + int num_parents) +{ + return clk_register_mux(NULL, name, parents, num_parents, + CLK_SET_RATE_NO_REPARENT, reg, shift, + width, 0); +} + +#endif /* __SANDBOX_CLK_H__ */ diff --git a/test/dm/Makefile b/test/dm/Makefile index 49857c5092..01a0a3674c 100644 --- a/test/dm/Makefile +++ b/test/dm/Makefile @@ -16,7 +16,7 @@ obj-$(CONFIG_SOUND) += audio.o obj-$(CONFIG_BLK) += blk.o obj-$(CONFIG_BOARD) += board.o obj-$(CONFIG_DM_BOOTCOUNT) += bootcount.o -obj-$(CONFIG_CLK) += clk.o +obj-$(CONFIG_CLK) += clk.o clk_ccf.o obj-$(CONFIG_DM_ETH) += eth.o obj-$(CONFIG_FIRMWARE) += firmware.o obj-$(CONFIG_DM_GPIO) += gpio.o diff --git a/test/dm/clk_ccf.c b/test/dm/clk_ccf.c new file mode 100644 index 0000000000..8d397593a3 --- /dev/null +++ b/test/dm/clk_ccf.c @@ -0,0 +1,62 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (C) 2019 + * Lukasz Majewski, DENX Software Engineering, lukma@denx.de + */ + +#include <common.h> +#include <clk.h> +#include <dm.h> +#include <asm/clk.h> +#include <dm/test.h> +#include <dm/uclass.h> +#include <linux/err.h> +#include <test/ut.h> +#include <sandbox-clk.h> + +/* Tests for Common Clock Framework driver */ +static int dm_test_clk_ccf(struct unit_test_state *uts) +{ + struct clk *clk, *pclk; + struct udevice *dev; + long long rate; + int ret; + + /* Get the device using the clk device */ + ut_assertok(uclass_get_device_by_name(UCLASS_CLK, "clk-ccf", &dev)); + + /* Test for clk_get_by_id() */ + ret = clk_get_by_id(SANDBOX_CLK_ECSPI_ROOT, &clk); + ut_assertok(ret); + ut_asserteq_str("ecspi_root", clk->dev->name); + + /* Test for clk_get_parent_rate() */ + ret = clk_get_by_id(SANDBOX_CLK_ECSPI1, &clk); + ut_assertok(ret); + ut_asserteq_str("ecspi1", clk->dev->name); + + rate = clk_get_parent_rate(clk); + ut_asserteq(rate, 20000000); + + /* Test the mux of CCF */ + ret = clk_get_by_id(SANDBOX_CLK_USDHC1_SEL, &clk); + ut_assertok(ret); + ut_asserteq_str("usdhc1_sel", clk->dev->name); + + rate = clk_get_parent_rate(clk); + ut_asserteq(rate, 60000000); + + ret = clk_get_by_id(SANDBOX_CLK_USDHC2_SEL, &clk); + ut_assertok(ret); + ut_asserteq_str("usdhc2_sel", clk->dev->name); + + rate = clk_get_parent_rate(clk); + ut_asserteq(rate, 80000000); + + pclk = clk_get_parent(clk); + ut_asserteq_str("pll3_80m", pclk->dev->name); + + return 1; +} + +DM_TEST(dm_test_clk_ccf, DM_TESTF_SCAN_FDT);

This patch provides code to implement the CCF clock tree in sandbox. It uses all the introduced primitives; some generic ones are reused, some sandbox specific were developed. In that way (after introducing the real CCF tree in sandbox) the recently added to clk-uclass.c: clk_get_by_id() and clk_get_parent_rate() are tested in their natural work environment. Usage (sandbox_defconfig and sandbox_flattree_defconfig): ./u-boot --fdt arch/sandbox/dts/test.dtb --command "ut dm clk_ccf" Signed-off-by: Lukasz Majewski lukma@denx.de
Applied to u-boot-imx, master, thanks !
Best regards, Stefano Babic

Enable by default the Common Clock Framework [CCF] clock code for sandbox.
Signed-off-by: Lukasz Majewski lukma@denx.de ---
Changes in v6: None Changes in v5: None Changes in v4: None Changes in v3: None
configs/sandbox_defconfig | 1 + configs/sandbox_flattree_defconfig | 1 + 2 files changed, 2 insertions(+)
diff --git a/configs/sandbox_defconfig b/configs/sandbox_defconfig index 4cffa2c604..a6056584c2 100644 --- a/configs/sandbox_defconfig +++ b/configs/sandbox_defconfig @@ -92,6 +92,7 @@ CONFIG_BOOTCOUNT_LIMIT=y CONFIG_DM_BOOTCOUNT=y CONFIG_DM_BOOTCOUNT_RTC=y CONFIG_CLK=y +CONFIG_SANDBOX_CLK_CCF=y CONFIG_CPU=y CONFIG_DM_DEMO=y CONFIG_DM_DEMO_SIMPLE=y diff --git a/configs/sandbox_flattree_defconfig b/configs/sandbox_flattree_defconfig index dda6832f83..5c69206143 100644 --- a/configs/sandbox_flattree_defconfig +++ b/configs/sandbox_flattree_defconfig @@ -67,6 +67,7 @@ CONFIG_DEBUG_DEVRES=y CONFIG_ADC=y CONFIG_ADC_SANDBOX=y CONFIG_CLK=y +CONFIG_SANDBOX_CLK_CCF=y CONFIG_CPU=y CONFIG_DM_DEMO=y CONFIG_DM_DEMO_SIMPLE=y

Enable by default the Common Clock Framework [CCF] clock code for sandbox. Signed-off-by: Lukasz Majewski lukma@denx.de
Applied to u-boot-imx, master, thanks !
Best regards, Stefano Babic

The clock subsystem needs active maintenance as it steadily grows. I do offer my help for this task.
Signed-off-by: Lukasz Majewski lukma@denx.de ---
Changes in v6: None Changes in v5: None Changes in v4: None Changes in v3: None
MAINTAINERS | 7 +++++++ 1 file changed, 7 insertions(+)
diff --git a/MAINTAINERS b/MAINTAINERS index 8e26eda2c8..e659491d3c 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -443,6 +443,13 @@ T: git git://git.denx.de/u-boot-cfi-flash.git F: drivers/mtd/cfi_flash.c F: drivers/mtd/jedec_flash.c
+CLOCK +M: Lukasz Majewski lukma@denx.de +S: Maintained +T: git git://git.denx.de/u-boot-dfu.git +F: drivers/clk/ +F: drivers/clk/imx/ + COLDFIRE M: Huan Wang alison.wang@nxp.com M: Angelo Dureghello angelo@sysam.it

The clock subsystem needs active maintenance as it steadily grows. I do offer my help for this task. Signed-off-by: Lukasz Majewski lukma@denx.de
Applied to u-boot-imx, master, thanks !
Best regards, Stefano Babic

Hi Lukasz,
On 24.06.19 15:50, Lukasz Majewski wrote:
This patch series brings the files from Linux kernel (SHA1: 5752b50477da Linux 5.1.12 to provide clocks support as it is used on the Linux kernel with Common Clock Framework [CCF] setup.
This series also fixes several problems with current clocks and provides sandbox tests for functions added to clk-uclass.c file.
CCF impact to U-Boot size:
SPL U-Boot.img
Without CCF: 51 KiB 358 KiB With CCF: 55 KiB 363 KiB Size increase: 1.3% 7.2%
The size increase numbers seem to be a bit off. It's 7.8% for SPL (which is quite a lot) and 1.4% for U-Boot proper from your size values.
By the way: Thanks for the new version. I'm currently still experimenting with your v3 and Peng's patches for i.MX8MM on top of 2019.04. I hope I have time to rebase on master and v5 soon.
Thanks, Frieder
The SPL implementation is not yet optimized (no OF_PLATDATA, etc).
Repository: https://github.com/lmajewski/u-boot-dfu/commits/CCF-v5
Applicable on top of u-boot/master: SHA1: 77f6e2dd0551d8a825bab391a1bd6b838874bcd4
Travis-CI: https://travis-ci.org/lmajewski/u-boot-dfu/builds/549603356
Changes in v6:
- Use dev->uclass_priv pointer to store pointer to clk
Changes in v5:
- s/U-boot/U-Boot/g
- Update Linux version to 5.1.12
- Add paragraph regarding sandbox CCF testing (common uclass code)
- Use long long to store rate value (to avoid int overflow and also return errors correctly)
- Use u32 to avoid checkpatch warning
- s/U-boot/U-Boot/g
- Replace dev->driver_data with dev_get_clk_ptr() wrapper on uclass_priv
- Replace ulong with long long (to accommodate large freqs and return errors)
- s/U-boot/U-Boot/g
- Check if the relevant code has changed between Linux tag v5.0-rc3 and v5.1.12 (no changes - the version can be safely updated).
- Use imply CLK_IMX6Q in Kconfig for (SPL_)CCF
- Fix clk-fixed-factor implementation (kzalloc needed for correct Sandbox operation)
- Fix gate2 implementation
- Use dev->uclass_priv instead of dev->driver_data to store back pointer to the struct clk.
- Split and introduce earlier the clk-provider.h header file
Changes in v4:
- New patch
- Port some more Linux code to facilitate imx8 code porting (most notably flags)
- Explicitly use container_of() based macro to provide struct clk in various places (e.g. gate2, mux, etc) Following patches has been squashed: http://patchwork.ozlabs.org/patch/1093141/ http://patchwork.ozlabs.org/patch/1093142/ http://patchwork.ozlabs.org/patch/1093146/
Changes in v3:
- New patch
- The rate information is now cached into struct clk field
- The clk_get_parent() is used to get pointer to the parent struct clk
- Replace -ENODEV with -ENOENT
- Use **clkp instead of **c
- Replace dev->driver_data with dev_get_clk_ptr() wrapper on uclas_priv
Lukasz Majewski (18): clk: doc: Add documentation entry for Common Clock Framework [CCF] (i.MX) dm: Fix documentation entry as there is no UCLASS_CLOCK uclass clk: Remove clock ID check in .get_rate() of clk_fixed_* clk: Extend struct clk to provide information regarding clock rate clk: Extend struct clk to provide clock type agnostic flags clk: Provide struct clk for fixed rate clock (clk_fixed_rate.c) clk: Introduce clk-provider.h to store Common Clock Framework's internals dm: clk: Define clk_get_parent() for clk operations dm: clk: Define clk_get_parent_rate() for clk operations dm: clk: Define clk_get_by_id() for clk operations clk: Port Linux common clock framework [CCF] for imx6q to U-boot (tag: v5.1.12) dm: clk: Extend clk_get_parent_rate() to support CLK_GET_RATE_NOCACHE flag dts: sandbox: Add 'osc' clock for Common Clock Framework [CCF] testing clk: sandbox: Adjust clk-divider to emulate reading its value from HW clk: sandbox: Adjust clk-mux.c to emulate reading divider value from HW clk: sandbox: Add sandbox test code for Common Clock Framework [CCF] defconfig: sandbox: Enable SANDBOX_CLK_CCF to reuse generic CCF code clk: Add MAINTAINERS entry for clocks (./drivers/clk/)
MAINTAINERS | 7 ++ arch/sandbox/dts/test.dts | 10 ++ configs/sandbox_defconfig | 1 + configs/sandbox_flattree_defconfig | 1 + doc/imx/clk/ccf.txt | 101 ++++++++++++++++++++ drivers/clk/Kconfig | 22 +++++ drivers/clk/Makefile | 3 + drivers/clk/clk-divider.c | 155 +++++++++++++++++++++++++++++++ drivers/clk/clk-fixed-factor.c | 80 ++++++++++++++++ drivers/clk/clk-mux.c | 172 ++++++++++++++++++++++++++++++++++ drivers/clk/clk-uclass.c | 60 ++++++++++++ drivers/clk/clk.c | 57 ++++++++++++ drivers/clk/clk_fixed_factor.c | 3 - drivers/clk/clk_fixed_rate.c | 8 +- drivers/clk/clk_sandbox_ccf.c | 185 +++++++++++++++++++++++++++++++++++++ drivers/clk/imx/Kconfig | 16 ++++ drivers/clk/imx/Makefile | 2 + drivers/clk/imx/clk-gate2.c | 103 +++++++++++++++++++++ drivers/clk/imx/clk-imx6q.c | 179 +++++++++++++++++++++++++++++++++++ drivers/clk/imx/clk-pfd.c | 90 ++++++++++++++++++ drivers/clk/imx/clk-pllv3.c | 82 ++++++++++++++++ drivers/clk/imx/clk.h | 69 ++++++++++++++ include/clk.h | 37 +++++++- include/linux/clk-provider.h | 132 ++++++++++++++++++++++++++ include/sandbox-clk.h | 76 +++++++++++++++ test/dm/Makefile | 2 +- test/dm/clk_ccf.c | 62 +++++++++++++ 27 files changed, 1707 insertions(+), 8 deletions(-) create mode 100644 doc/imx/clk/ccf.txt create mode 100644 drivers/clk/clk-divider.c create mode 100644 drivers/clk/clk-fixed-factor.c create mode 100644 drivers/clk/clk-mux.c create mode 100644 drivers/clk/clk.c create mode 100644 drivers/clk/clk_sandbox_ccf.c create mode 100644 drivers/clk/imx/clk-gate2.c create mode 100644 drivers/clk/imx/clk-imx6q.c create mode 100644 drivers/clk/imx/clk-pfd.c create mode 100644 drivers/clk/imx/clk-pllv3.c create mode 100644 drivers/clk/imx/clk.h create mode 100644 include/linux/clk-provider.h create mode 100644 include/sandbox-clk.h create mode 100644 test/dm/clk_ccf.c

Hi Frieder,
Hi Lukasz,
On 24.06.19 15:50, Lukasz Majewski wrote:
This patch series brings the files from Linux kernel (SHA1: 5752b50477da Linux 5.1.12 to provide clocks support as it is used on the Linux kernel with Common Clock Framework [CCF] setup.
This series also fixes several problems with current clocks and provides sandbox tests for functions added to clk-uclass.c file.
CCF impact to U-Boot size:
SPL U-Boot.img
Without CCF: 51 KiB 358 KiB With CCF: 55 KiB 363 KiB Size increase: 1.3% 7.2%
The size increase numbers seem to be a bit off. It's 7.8% for SPL (which is quite a lot) and 1.4% for U-Boot proper from your size values.
The size increase for U-Boot proper is IMHO acceptable. The problem is with SPL. I do think that the next step for SPL (when the code gets accepted) would be to convert it to OF_PLATDATA (no need for DTS parsing code as well as the description iself) and try to keep number of necessary clocks low.
One issue with clocks is that we currently in u-boot by default strip the clock properties. For CCF we need them, and as a result the DTS is bigger.
By the way: Thanks for the new version. I'm currently still experimenting with your v3 and Peng's patches for i.MX8MM on top of 2019.04. I hope I have time to rebase on master and v5 soon.
v5 has several improvements/changes.
Thanks, Frieder
The SPL implementation is not yet optimized (no OF_PLATDATA, etc).
Repository: https://github.com/lmajewski/u-boot-dfu/commits/CCF-v5
Applicable on top of u-boot/master: SHA1: 77f6e2dd0551d8a825bab391a1bd6b838874bcd4
Travis-CI: https://travis-ci.org/lmajewski/u-boot-dfu/builds/549603356
Changes in v6:
- Use dev->uclass_priv pointer to store pointer to clk
Changes in v5:
- s/U-boot/U-Boot/g
- Update Linux version to 5.1.12
- Add paragraph regarding sandbox CCF testing (common uclass code)
- Use long long to store rate value (to avoid int overflow and also
return errors correctly)
- Use u32 to avoid checkpatch warning
- s/U-boot/U-Boot/g
- Replace dev->driver_data with dev_get_clk_ptr() wrapper on
uclass_priv
- Replace ulong with long long (to accommodate large freqs and
return errors)
- s/U-boot/U-Boot/g
- Check if the relevant code has changed between Linux tag v5.0-rc3
and v5.1.12 (no changes - the version can be safely updated).
- Use imply CLK_IMX6Q in Kconfig for (SPL_)CCF
- Fix clk-fixed-factor implementation (kzalloc needed for correct
Sandbox operation)
- Fix gate2 implementation
- Use dev->uclass_priv instead of dev->driver_data to store back
pointer to the struct clk.
- Split and introduce earlier the clk-provider.h header file
Changes in v4:
- New patch
- Port some more Linux code to facilitate imx8 code porting (most
notably flags)
- Explicitly use container_of() based macro to provide struct clk
in various places (e.g. gate2, mux, etc) Following patches has been squashed: http://patchwork.ozlabs.org/patch/1093141/ http://patchwork.ozlabs.org/patch/1093142/ http://patchwork.ozlabs.org/patch/1093146/
Changes in v3:
- New patch
- The rate information is now cached into struct clk field
- The clk_get_parent() is used to get pointer to the parent struct
clk
- Replace -ENODEV with -ENOENT
- Use **clkp instead of **c
- Replace dev->driver_data with dev_get_clk_ptr() wrapper on
uclas_priv
Lukasz Majewski (18): clk: doc: Add documentation entry for Common Clock Framework [CCF] (i.MX) dm: Fix documentation entry as there is no UCLASS_CLOCK uclass clk: Remove clock ID check in .get_rate() of clk_fixed_* clk: Extend struct clk to provide information regarding clock rate clk: Extend struct clk to provide clock type agnostic flags clk: Provide struct clk for fixed rate clock (clk_fixed_rate.c) clk: Introduce clk-provider.h to store Common Clock Framework's internals dm: clk: Define clk_get_parent() for clk operations dm: clk: Define clk_get_parent_rate() for clk operations dm: clk: Define clk_get_by_id() for clk operations clk: Port Linux common clock framework [CCF] for imx6q to U-boot (tag: v5.1.12) dm: clk: Extend clk_get_parent_rate() to support CLK_GET_RATE_NOCACHE flag dts: sandbox: Add 'osc' clock for Common Clock Framework [CCF] testing clk: sandbox: Adjust clk-divider to emulate reading its value from HW clk: sandbox: Adjust clk-mux.c to emulate reading divider value from HW clk: sandbox: Add sandbox test code for Common Clock Framework [CCF] defconfig: sandbox: Enable SANDBOX_CLK_CCF to reuse generic CCF code clk: Add MAINTAINERS entry for clocks (./drivers/clk/)
MAINTAINERS | 7 ++ arch/sandbox/dts/test.dts | 10 ++ configs/sandbox_defconfig | 1 + configs/sandbox_flattree_defconfig | 1 + doc/imx/clk/ccf.txt | 101 ++++++++++++++++++++ drivers/clk/Kconfig | 22 +++++ drivers/clk/Makefile | 3 + drivers/clk/clk-divider.c | 155 +++++++++++++++++++++++++++++++ drivers/clk/clk-fixed-factor.c | 80 ++++++++++++++++ drivers/clk/clk-mux.c | 172 ++++++++++++++++++++++++++++++++++ drivers/clk/clk-uclass.c | 60 ++++++++++++ drivers/clk/clk.c | 57 ++++++++++++ drivers/clk/clk_fixed_factor.c | 3 - drivers/clk/clk_fixed_rate.c | 8 +- drivers/clk/clk_sandbox_ccf.c | 185 +++++++++++++++++++++++++++++++++++++ drivers/clk/imx/Kconfig | 16 ++++ drivers/clk/imx/Makefile | 2 + drivers/clk/imx/clk-gate2.c | 103 +++++++++++++++++++++ drivers/clk/imx/clk-imx6q.c | 179 +++++++++++++++++++++++++++++++++++ drivers/clk/imx/clk-pfd.c | 90 ++++++++++++++++++ drivers/clk/imx/clk-pllv3.c | 82 ++++++++++++++++ drivers/clk/imx/clk.h | 69 ++++++++++++++ include/clk.h | 37 +++++++- include/linux/clk-provider.h | 132 ++++++++++++++++++++++++++ include/sandbox-clk.h | 76 +++++++++++++++ test/dm/Makefile | 2 +- test/dm/clk_ccf.c | 62 +++++++++++++ 27 files changed, 1707 insertions(+), 8 deletions(-) create mode 100644 doc/imx/clk/ccf.txt create mode 100644 drivers/clk/clk-divider.c create mode 100644 drivers/clk/clk-fixed-factor.c create mode 100644 drivers/clk/clk-mux.c create mode 100644 drivers/clk/clk.c create mode 100644 drivers/clk/clk_sandbox_ccf.c create mode 100644 drivers/clk/imx/clk-gate2.c create mode 100644 drivers/clk/imx/clk-imx6q.c create mode 100644 drivers/clk/imx/clk-pfd.c create mode 100644 drivers/clk/imx/clk-pllv3.c create mode 100644 drivers/clk/imx/clk.h create mode 100644 include/linux/clk-provider.h create mode 100644 include/sandbox-clk.h create mode 100644 test/dm/clk_ccf.c
Best regards,
Lukasz Majewski
--
DENX Software Engineering GmbH, Managing Director: Wolfgang Denk HRB 165235 Munich, Office: Kirchenstr.5, D-82194 Groebenzell, Germany Phone: (+49)-8142-66989-59 Fax: (+49)-8142-66989-80 Email: lukma@denx.de

Hi Simon,
Would you find time to review the v5 of CCF?
https://patchwork.ozlabs.org/cover/1121348/
Thanks in advance,
Best regards,
Lukasz Majewski
--
DENX Software Engineering GmbH, Managing Director: Wolfgang Denk HRB 165235 Munich, Office: Kirchenstr.5, D-82194 Groebenzell, Germany Phone: (+49)-8142-66989-59 Fax: (+49)-8142-66989-80 Email: lukma@denx.de
participants (3)
-
Lukasz Majewski
-
sbabic@denx.de
-
Schrempf Frieder