[PATCH v2 00/30] Add DM support for omap PWM backlight

The series was born from the need to manage the PWM backlight of the display connected to my beaglebone board. To hit the target, I had to develop drivers for PWM management which in turn relied on drivers for managing timers and clocks, all developed according to the driver model. My intention was to use the SoC-specific API only at strictly necessary points in the code. My previous patches for migrating the AM335x display driver to the driver model had required the implementation of additional functions outside the concerns of the driver, (settings for dividing the pixel clock rate, configuring the display DPLL rate, ....) not being able to use the API of the related clock drivers. This series shouldn't have repeated the same kind of mistake. Furthermore, I also wanted to fix that kind of forced choice. Almost everything should have been accessible via the driver model API. In the series there are also some patches that could be submitted separately, but which I have however inserted to avoid applying future patches to incorporate them. With this last consideration, I hope I have convincingly justified the large number of patches in the series.
The patch enabling address translation into a CPU physical address from device-tree even in case of crossing levels with #size-cells = <0>, is crucial for the series. The previous implementation was unable to perform the address translation required by the am33xx device tree. I tried to apply in a conservative way as few changes as possible and to verify the execution of all the tests already developed, as well as the new ones I added for the new feature.
Changes in v2: - Add the clk_ prefix to the divider functions. - Add kernel-doc comments to the exported functions. - Merged to patch [09/31] clk: ti: refactor mux and divider clock drivers. - Remove the 'ti_am3_prcm_clocks' driver. Handle 'prcm_clocks' node in the 'ti_am3_prcm' driver. - Update the commit message. - Fix a missing line in the commit message. - Add dm_flags to global_data structure and GD_DM_FLG_SIZE_CELLS_0 macro to test without recompiling. - Update the OF_CHECK_COUNTS macro in order to have just one #define by bringing the GD_DM_FLG_SIZE_CELLS_0 into the expression. - Lower-case the 0xC019 hex number. - Remove the 'ti_am3_scm_clocks' driver. Handle 'scm_clocks' node in the 'ti_am3_scm' driver. - Update the commit message.
Dario Binacchi (30): clk: remove a redundant header clk: export generic routines arch: sandbox: fix typo in clk.h clk: add clk_round_rate() clk: ti: add mux clock driver arm: ti: am33xx: add DPLL_EN_FAST_RELOCK_BYPASS macro clk: ti: am33xx: add DPLL clock drivers clk: ti: add divider clock driver clk: ti: add gate clock driver ti: am33xx: fix do_enable_clocks() to accept NULL parameters clk: ti: add support for clkctrl clocks clk: ti: move drivers to 'ti' directory clk: ti: omap4: add clock manager driver clk: ti: am335x: add clock manager driver fdt: translate address if #size-cells = <0> omap: timer: fix the rate setting misc: am33xx: add control module driver pwm: ti: am33xx: add enhanced pwm driver pwm: ti: am33xx: add subsystem driver video: backlight: fix pwm's duty cycle calculation video: backlight: fix pwm data structure description dm: core: improve uclass_get_device_by_phandle_id() description gpio: fix gpio_request_by_name() description dm: core: add a function to decode display timings video: omap: add panel driver video: omap: enable LCD clock domain through DM API video: omap: set LCD clock rate through DM API video: omap: split the legacy code from the DM code video: omap: move drivers to 'ti' directory board: ti: am335x-ice: get CDCE913 clock device
arch/arm/dts/am335x-brppt1-mmc.dts | 3 +- arch/arm/dts/am335x-brppt1-nand.dts | 3 +- arch/arm/dts/am335x-brppt1-spi.dts | 3 +- arch/arm/dts/am335x-brsmarc1.dts | 2 +- arch/arm/dts/am335x-brxre1.dts | 3 +- arch/arm/dts/am335x-evm-u-boot.dtsi | 7 +- arch/arm/dts/am335x-evmsk-u-boot.dtsi | 6 +- arch/arm/dts/am335x-guardian-u-boot.dtsi | 8 +- arch/arm/dts/am335x-pdu001-u-boot.dtsi | 8 +- arch/arm/dts/am335x-pxm50-u-boot.dtsi | 6 +- arch/arm/dts/am335x-rut-u-boot.dtsi | 6 +- arch/arm/dts/am33xx.dtsi | 12 + arch/arm/dts/da850-evm-u-boot.dtsi | 8 +- arch/arm/include/asm/arch-am33xx/clock.h | 1 + arch/arm/mach-omap2/am33xx/clock.c | 10 +- arch/arm/mach-omap2/am33xx/clock_am33xx.c | 2 +- arch/sandbox/dts/test.dts | 67 +++ arch/sandbox/include/asm/clk.h | 35 +- board/ti/am335x/board.c | 2 +- board/ti/am43xx/board.c | 2 +- common/fdt_support.c | 6 +- doc/device-tree-bindings/arm/omap,ctrl.txt | 82 +++ doc/device-tree-bindings/arm/omap,prcm.txt | 63 +++ .../clock/clock-bindings.txt | 186 +++++++ .../clock/gpio-gate-clock.txt | 21 + .../clock/ti,autoidle.txt | 39 ++ doc/device-tree-bindings/clock/ti,clkctrl.txt | 61 +++ .../clock/ti,clockdomain.txt | 24 + doc/device-tree-bindings/clock/ti,divider.txt | 117 +++++ doc/device-tree-bindings/clock/ti,dpll.txt | 85 ++++ doc/device-tree-bindings/clock/ti,gate.txt | 106 ++++ doc/device-tree-bindings/clock/ti,mux.txt | 79 +++ .../pinctrl/pinctrl-single.txt | 255 ++++++++++ doc/device-tree-bindings/pwm/ti,ehrpwm.txt | 49 ++ doc/device-tree-bindings/pwm/ti,pwmss.txt | 58 +++ drivers/clk/Kconfig | 9 +- drivers/clk/Makefile | 2 +- drivers/clk/clk-divider.c | 24 +- drivers/clk/clk-uclass.c | 15 + drivers/clk/clk_sandbox.c | 17 + drivers/clk/clk_sandbox_test.c | 10 + drivers/clk/ti/Kconfig | 42 ++ drivers/clk/ti/Makefile | 18 + drivers/clk/ti/am3-prcm.c | 65 +++ drivers/clk/ti/clk-am3-dpll-x2.c | 78 +++ drivers/clk/ti/clk-am3-dpll.c | 267 ++++++++++ drivers/clk/ti/clk-ctrl.c | 102 ++++ drivers/clk/ti/clk-divider.c | 380 ++++++++++++++ drivers/clk/ti/clk-gate.c | 92 ++++ drivers/clk/ti/clk-mux.c | 252 ++++++++++ drivers/clk/{clk-ti-sci.c => ti/clk-sci.c} | 0 drivers/clk/ti/clk.c | 34 ++ drivers/clk/ti/clk.h | 13 + drivers/clk/ti/omap4-cm.c | 22 + drivers/core/Kconfig | 12 + drivers/core/fdtaddr.c | 2 +- drivers/core/of_addr.c | 14 +- drivers/core/ofnode.c | 7 +- drivers/core/read.c | 6 + drivers/core/root.c | 3 + drivers/misc/Kconfig | 7 + drivers/misc/Makefile | 1 + drivers/misc/ti-am3-scm.c | 81 +++ drivers/pwm/Kconfig | 13 + drivers/pwm/Makefile | 2 + drivers/pwm/pwm-ti-ehrpwm.c | 465 ++++++++++++++++++ drivers/pwm/pwm-ti-pwmss.c | 95 ++++ drivers/timer/omap-timer.c | 6 +- drivers/video/Kconfig | 5 +- drivers/video/Makefile | 2 +- drivers/video/pwm_backlight.c | 4 +- drivers/video/ti/Kconfig | 8 + drivers/video/ti/Makefile | 10 + drivers/video/{ => ti}/am335x-fb.c | 342 +------------ drivers/video/{ => ti}/am335x-fb.h | 4 - drivers/video/ti/tilcdc-panel.c | 171 +++++++ drivers/video/ti/tilcdc-panel.h | 14 + drivers/video/ti/tilcdc.c | 438 +++++++++++++++++ drivers/video/ti/tilcdc.h | 38 ++ include/asm-generic/global_data.h | 6 + include/asm-generic/gpio.h | 2 +- include/clk-uclass.h | 8 + include/clk.h | 29 ++ include/dm/read.h | 24 + include/dm/uclass.h | 3 +- include/linux/clk-provider.h | 58 ++- test/dm/clk.c | 22 + test/dm/panel.c | 12 +- test/dm/test-fdt.c | 148 +++++- 89 files changed, 4488 insertions(+), 441 deletions(-) create mode 100644 doc/device-tree-bindings/arm/omap,ctrl.txt create mode 100644 doc/device-tree-bindings/arm/omap,prcm.txt create mode 100644 doc/device-tree-bindings/clock/clock-bindings.txt create mode 100644 doc/device-tree-bindings/clock/gpio-gate-clock.txt create mode 100644 doc/device-tree-bindings/clock/ti,autoidle.txt create mode 100644 doc/device-tree-bindings/clock/ti,clkctrl.txt create mode 100644 doc/device-tree-bindings/clock/ti,clockdomain.txt create mode 100644 doc/device-tree-bindings/clock/ti,divider.txt create mode 100644 doc/device-tree-bindings/clock/ti,dpll.txt create mode 100644 doc/device-tree-bindings/clock/ti,gate.txt create mode 100644 doc/device-tree-bindings/clock/ti,mux.txt create mode 100644 doc/device-tree-bindings/pinctrl/pinctrl-single.txt create mode 100644 doc/device-tree-bindings/pwm/ti,ehrpwm.txt create mode 100644 doc/device-tree-bindings/pwm/ti,pwmss.txt create mode 100644 drivers/clk/ti/Kconfig create mode 100644 drivers/clk/ti/Makefile create mode 100644 drivers/clk/ti/am3-prcm.c create mode 100644 drivers/clk/ti/clk-am3-dpll-x2.c create mode 100644 drivers/clk/ti/clk-am3-dpll.c create mode 100644 drivers/clk/ti/clk-ctrl.c create mode 100644 drivers/clk/ti/clk-divider.c create mode 100644 drivers/clk/ti/clk-gate.c create mode 100644 drivers/clk/ti/clk-mux.c rename drivers/clk/{clk-ti-sci.c => ti/clk-sci.c} (100%) create mode 100644 drivers/clk/ti/clk.c create mode 100644 drivers/clk/ti/clk.h create mode 100644 drivers/clk/ti/omap4-cm.c create mode 100644 drivers/misc/ti-am3-scm.c create mode 100644 drivers/pwm/pwm-ti-ehrpwm.c create mode 100644 drivers/pwm/pwm-ti-pwmss.c create mode 100644 drivers/video/ti/Kconfig create mode 100644 drivers/video/ti/Makefile rename drivers/video/{ => ti}/am335x-fb.c (54%) rename drivers/video/{ => ti}/am335x-fb.h (97%) create mode 100644 drivers/video/ti/tilcdc-panel.c create mode 100644 drivers/video/ti/tilcdc-panel.h create mode 100644 drivers/video/ti/tilcdc.c create mode 100644 drivers/video/ti/tilcdc.h

The linux/err.h header file was included twice.
Signed-off-by: Dario Binacchi dariobin@libero.it Reviewed-by: Simon Glass sjg@chromium.org ---
(no changes since v1)
include/linux/clk-provider.h | 1 - 1 file changed, 1 deletion(-)
diff --git a/include/linux/clk-provider.h b/include/linux/clk-provider.h index 79dce8f0ad..a2630056de 100644 --- a/include/linux/clk-provider.h +++ b/include/linux/clk-provider.h @@ -12,7 +12,6 @@ #include <linux/bitops.h> #include <linux/err.h> #include <clk-uclass.h> -#include <linux/err.h>
struct udevice;

Export routines that can be used by other drivers avoiding duplicating code.
Signed-off-by: Dario Binacchi dariobin@libero.it
---
Changes in v2: - Add the clk_ prefix to the divider functions. - Add kernel-doc comments to the exported functions.
drivers/clk/clk-divider.c | 24 +++++++-------- include/linux/clk-provider.h | 57 ++++++++++++++++++++++++++++++++++++ 2 files changed, 69 insertions(+), 12 deletions(-)
diff --git a/drivers/clk/clk-divider.c b/drivers/clk/clk-divider.c index 8f59d7fb72..9df50a5e72 100644 --- a/drivers/clk/clk-divider.c +++ b/drivers/clk/clk-divider.c @@ -28,8 +28,8 @@
#define UBOOT_DM_CLK_CCF_DIVIDER "ccf_clk_divider"
-static unsigned int _get_table_div(const struct clk_div_table *table, - unsigned int val) +unsigned int clk_divider_get_table_div(const struct clk_div_table *table, + unsigned int val) { const struct clk_div_table *clkt;
@@ -49,7 +49,7 @@ static unsigned int _get_div(const struct clk_div_table *table, if (flags & CLK_DIVIDER_MAX_AT_ZERO) return val ? val : clk_div_mask(width) + 1; if (table) - return _get_table_div(table, val); + return clk_divider_get_table_div(table, val); return val + 1; }
@@ -89,8 +89,8 @@ static ulong clk_divider_recalc_rate(struct clk *clk) divider->flags, divider->width); }
-static bool _is_valid_table_div(const struct clk_div_table *table, - unsigned int div) +bool clk_divider_is_valid_table_div(const struct clk_div_table *table, + unsigned int div) { const struct clk_div_table *clkt;
@@ -100,18 +100,18 @@ static bool _is_valid_table_div(const struct clk_div_table *table, return false; }
-static bool _is_valid_div(const struct clk_div_table *table, unsigned int div, - unsigned long flags) +bool clk_divider_is_valid_div(const struct clk_div_table *table, + unsigned int div, unsigned long flags) { if (flags & CLK_DIVIDER_POWER_OF_TWO) return is_power_of_2(div); if (table) - return _is_valid_table_div(table, div); + return clk_divider_is_valid_table_div(table, div); return true; }
-static unsigned int _get_table_val(const struct clk_div_table *table, - unsigned int div) +unsigned int clk_divider_get_table_val(const struct clk_div_table *table, + unsigned int div) { const struct clk_div_table *clkt;
@@ -131,7 +131,7 @@ static unsigned int _get_val(const struct clk_div_table *table, if (flags & CLK_DIVIDER_MAX_AT_ZERO) return (div == clk_div_mask(width) + 1) ? 0 : div; if (table) - return _get_table_val(table, div); + return clk_divider_get_table_val(table, div); return div - 1; } int divider_get_val(unsigned long rate, unsigned long parent_rate, @@ -142,7 +142,7 @@ int divider_get_val(unsigned long rate, unsigned long parent_rate,
div = DIV_ROUND_UP_ULL((u64)parent_rate, rate);
- if (!_is_valid_div(table, div, flags)) + if (!clk_divider_is_valid_div(table, div, flags)) return -EINVAL;
value = _get_val(table, div, flags, width); diff --git a/include/linux/clk-provider.h b/include/linux/clk-provider.h index a2630056de..4d313b0462 100644 --- a/include/linux/clk-provider.h +++ b/include/linux/clk-provider.h @@ -75,6 +75,19 @@ struct clk_mux { extern const struct clk_ops clk_mux_ops; u8 clk_mux_get_parent(struct clk *clk);
+/** + * clk_mux_index_to_val() - Convert the parent index to the register value + * + * It returns the value to write in the hardware register to output the selected + * input clock parent. + * + * @table: array of register values corresponding to the parent index (optional) + * @flags: hardware-specific flags + * @index: parent clock index + * @return the register value + */ +unsigned int clk_mux_index_to_val(u32 *table, unsigned int flags, u8 index); + struct clk_gate { struct clk clk; void __iomem *reg; @@ -124,6 +137,50 @@ struct clk_divider { #define CLK_DIVIDER_READ_ONLY BIT(5) #define CLK_DIVIDER_MAX_AT_ZERO BIT(6) extern const struct clk_ops clk_divider_ops; + +/** + * clk_divider_get_table_div() - convert the register value to the divider + * + * @table: array of register values corresponding to valid dividers + * @val: value to convert + * @return the divider + */ +unsigned int clk_divider_get_table_div(const struct clk_div_table *table, + unsigned int val); + +/** + * clk_divider_get_table_val() - convert the divider to the register value + * + * It returns the value to write in the hardware register to divide the input + * clock rate by @div. + * + * @table: array of register values corresponding to valid dividers + * @div: requested divider + * @return the register value + */ +unsigned int clk_divider_get_table_val(const struct clk_div_table *table, + unsigned int div); + +/** + * clk_divider_is_valid_div() - check if the divider is valid + * + * @table: array of valid dividers (optional) + * @div: divider to check + * @flags: hardware-specific flags + * @return true if the divider is valid, false otherwise + */ +bool clk_divider_is_valid_div(const struct clk_div_table *table, + unsigned int div, unsigned long flags); + +/** + * clk_divider_is_valid_table_div - check if the divider is in the @table array + * + * @table: array of valid dividers + * @div: divider to check + * @return true if the divider is found in the @table array, false otherwise + */ +bool clk_divider_is_valid_table_div(const struct clk_div_table *table, + unsigned int div); unsigned long divider_recalc_rate(struct clk *hw, unsigned long parent_rate, unsigned int val, const struct clk_div_table *table,

On Sun, 6 Sep 2020 at 06:09, Dario Binacchi dariobin@libero.it wrote:
Export routines that can be used by other drivers avoiding duplicating code.
Signed-off-by: Dario Binacchi dariobin@libero.it
Changes in v2:
- Add the clk_ prefix to the divider functions.
- Add kernel-doc comments to the exported functions.
drivers/clk/clk-divider.c | 24 +++++++-------- include/linux/clk-provider.h | 57 ++++++++++++++++++++++++++++++++++++ 2 files changed, 69 insertions(+), 12 deletions(-)
Reviewed-by: Simon Glass sjg@chromium.org

Fix the 'devivce' typo in arch/sandbox/include/asm/clk.h.
Signed-off-by: Dario Binacchi dariobin@libero.it Reviewed-by: Simon Glass sjg@chromium.org ---
(no changes since v1)
arch/sandbox/include/asm/clk.h | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-)
diff --git a/arch/sandbox/include/asm/clk.h b/arch/sandbox/include/asm/clk.h index c184c4bffc..1163e61026 100644 --- a/arch/sandbox/include/asm/clk.h +++ b/arch/sandbox/include/asm/clk.h @@ -74,7 +74,7 @@ int sandbox_clk_query_requested(struct udevice *dev, int id); * sandbox_clk_test_get - Ask the sandbox clock test device to request its * clocks. * - * @dev: The sandbox clock test (client) devivce. + * @dev: The sandbox clock test (client) device. * @return: 0 if OK, or a negative error code. */ int sandbox_clk_test_get(struct udevice *dev); @@ -83,7 +83,7 @@ int sandbox_clk_test_get(struct udevice *dev); * sandbox_clk_test_devm_get - Ask the sandbox clock test device to request its * clocks using the managed API. * - * @dev: The sandbox clock test (client) devivce. + * @dev: The sandbox clock test (client) device. * @return: 0 if OK, or a negative error code. */ int sandbox_clk_test_devm_get(struct udevice *dev); @@ -92,7 +92,7 @@ int sandbox_clk_test_devm_get(struct udevice *dev); * sandbox_clk_test_get_bulk - Ask the sandbox clock test device to request its * clocks with the bulk clk API. * - * @dev: The sandbox clock test (client) devivce. + * @dev: The sandbox clock test (client) device. * @return: 0 if OK, or a negative error code. */ int sandbox_clk_test_get_bulk(struct udevice *dev); @@ -100,7 +100,7 @@ int sandbox_clk_test_get_bulk(struct udevice *dev); * sandbox_clk_test_get_rate - Ask the sandbox clock test device to query a * clock's rate. * - * @dev: The sandbox clock test (client) devivce. + * @dev: The sandbox clock test (client) device. * @id: The test device's clock ID to query. * @return: The rate of the clock. */ @@ -109,7 +109,7 @@ ulong sandbox_clk_test_get_rate(struct udevice *dev, int id); * sandbox_clk_test_set_rate - Ask the sandbox clock test device to set a * clock's rate. * - * @dev: The sandbox clock test (client) devivce. + * @dev: The sandbox clock test (client) device. * @id: The test device's clock ID to configure. * @return: The new rate of the clock. */ @@ -118,7 +118,7 @@ ulong sandbox_clk_test_set_rate(struct udevice *dev, int id, ulong rate); * sandbox_clk_test_enable - Ask the sandbox clock test device to enable a * clock. * - * @dev: The sandbox clock test (client) devivce. + * @dev: The sandbox clock test (client) device. * @id: The test device's clock ID to configure. * @return: 0 if OK, or a negative error code. */ @@ -127,7 +127,7 @@ int sandbox_clk_test_enable(struct udevice *dev, int id); * sandbox_clk_test_enable_bulk - Ask the sandbox clock test device to enable * all clocks in it's clock bulk struct. * - * @dev: The sandbox clock test (client) devivce. + * @dev: The sandbox clock test (client) device. * @return: 0 if OK, or a negative error code. */ int sandbox_clk_test_enable_bulk(struct udevice *dev); @@ -135,7 +135,7 @@ int sandbox_clk_test_enable_bulk(struct udevice *dev); * sandbox_clk_test_disable - Ask the sandbox clock test device to disable a * clock. * - * @dev: The sandbox clock test (client) devivce. + * @dev: The sandbox clock test (client) device. * @id: The test device's clock ID to configure. * @return: 0 if OK, or a negative error code. */ @@ -144,7 +144,7 @@ int sandbox_clk_test_disable(struct udevice *dev, int id); * sandbox_clk_test_disable_bulk - Ask the sandbox clock test device to disable * all clocks in it's clock bulk struct. * - * @dev: The sandbox clock test (client) devivce. + * @dev: The sandbox clock test (client) device. * @return: 0 if OK, or a negative error code. */ int sandbox_clk_test_disable_bulk(struct udevice *dev); @@ -152,7 +152,7 @@ int sandbox_clk_test_disable_bulk(struct udevice *dev); * sandbox_clk_test_free - Ask the sandbox clock test device to free its * clocks. * - * @dev: The sandbox clock test (client) devivce. + * @dev: The sandbox clock test (client) device. * @return: 0 if OK, or a negative error code. */ int sandbox_clk_test_free(struct udevice *dev); @@ -160,7 +160,7 @@ int sandbox_clk_test_free(struct udevice *dev); * sandbox_clk_test_release_bulk - Ask the sandbox clock test device to release * all clocks in it's clock bulk struct. * - * @dev: The sandbox clock test (client) devivce. + * @dev: The sandbox clock test (client) device. * @return: 0 if OK, or a negative error code. */ int sandbox_clk_test_release_bulk(struct udevice *dev); @@ -168,7 +168,7 @@ int sandbox_clk_test_release_bulk(struct udevice *dev); * sandbox_clk_test_valid - Ask the sandbox clock test device to check its * clocks are valid. * - * @dev: The sandbox clock test (client) devivce. + * @dev: The sandbox clock test (client) device. * @return: 0 if OK, or a negative error code. */ int sandbox_clk_test_valid(struct udevice *dev); @@ -176,7 +176,7 @@ int sandbox_clk_test_valid(struct udevice *dev); * sandbox_clk_test_valid - Ask the sandbox clock test device to check its * clocks are valid. * - * @dev: The sandbox clock test (client) devivce. + * @dev: The sandbox clock test (client) device. * @return: 0 if OK, or a negative error code. */ struct clk *sandbox_clk_test_get_devm_clk(struct udevice *dev, int id);

It returns the rate which will be set if you ask clk_set_rate() to set that rate. It provides a way to query exactly what rate you'll get if you call clk_set_rate() with that same argument. So essentially, clk_round_rate() and clk_set_rate() are equivalent except the former does not modify the clock hardware in any way.
Signed-off-by: Dario Binacchi dariobin@libero.it Reviewed-by: Simon Glass sjg@chromium.org
---
(no changes since v1)
arch/sandbox/include/asm/clk.h | 9 +++++++++ drivers/clk/clk-uclass.c | 15 +++++++++++++++ drivers/clk/clk_sandbox.c | 17 +++++++++++++++++ drivers/clk/clk_sandbox_test.c | 10 ++++++++++ include/clk-uclass.h | 8 ++++++++ include/clk.h | 29 +++++++++++++++++++++++++++++ test/dm/clk.c | 22 ++++++++++++++++++++++ 7 files changed, 110 insertions(+)
diff --git a/arch/sandbox/include/asm/clk.h b/arch/sandbox/include/asm/clk.h index 1163e61026..68a8687f57 100644 --- a/arch/sandbox/include/asm/clk.h +++ b/arch/sandbox/include/asm/clk.h @@ -105,6 +105,15 @@ int sandbox_clk_test_get_bulk(struct udevice *dev); * @return: The rate of the clock. */ ulong sandbox_clk_test_get_rate(struct udevice *dev, int id); +/** + * sandbox_clk_test_round_rate - Ask the sandbox clock test device to round a + * clock's rate. + * + * @dev: The sandbox clock test (client) device. + * @id: The test device's clock ID to configure. + * @return: The rounded rate of the clock. + */ +ulong sandbox_clk_test_round_rate(struct udevice *dev, int id, ulong rate); /** * sandbox_clk_test_set_rate - Ask the sandbox clock test device to set a * clock's rate. diff --git a/drivers/clk/clk-uclass.c b/drivers/clk/clk-uclass.c index 934cd5787a..1b5541c3af 100644 --- a/drivers/clk/clk-uclass.c +++ b/drivers/clk/clk-uclass.c @@ -494,6 +494,21 @@ long long clk_get_parent_rate(struct clk *clk) return pclk->rate; }
+ulong clk_round_rate(struct clk *clk, ulong rate) +{ + const struct clk_ops *ops; + + debug("%s(clk=%p, rate=%lu)\n", __func__, clk, rate); + if (!clk_valid(clk)) + return 0; + + ops = clk_dev_ops(clk->dev); + if (!ops->round_rate) + return -ENOSYS; + + return ops->round_rate(clk, rate); +} + ulong clk_set_rate(struct clk *clk, ulong rate) { const struct clk_ops *ops; diff --git a/drivers/clk/clk_sandbox.c b/drivers/clk/clk_sandbox.c index 768fbb7c52..8361b930df 100644 --- a/drivers/clk/clk_sandbox.c +++ b/drivers/clk/clk_sandbox.c @@ -30,6 +30,22 @@ static ulong sandbox_clk_get_rate(struct clk *clk) return priv->rate[clk->id]; }
+static ulong sandbox_clk_round_rate(struct clk *clk, ulong rate) +{ + struct sandbox_clk_priv *priv = dev_get_priv(clk->dev); + + if (!priv->probed) + return -ENODEV; + + if (clk->id >= SANDBOX_CLK_ID_COUNT) + return -EINVAL; + + if (!rate) + return -EINVAL; + + return rate; +} + static ulong sandbox_clk_set_rate(struct clk *clk, ulong rate) { struct sandbox_clk_priv *priv = dev_get_priv(clk->dev); @@ -103,6 +119,7 @@ static int sandbox_clk_free(struct clk *clk) }
static struct clk_ops sandbox_clk_ops = { + .round_rate = sandbox_clk_round_rate, .get_rate = sandbox_clk_get_rate, .set_rate = sandbox_clk_set_rate, .enable = sandbox_clk_enable, diff --git a/drivers/clk/clk_sandbox_test.c b/drivers/clk/clk_sandbox_test.c index 873383856f..f7b77aa674 100644 --- a/drivers/clk/clk_sandbox_test.c +++ b/drivers/clk/clk_sandbox_test.c @@ -86,6 +86,16 @@ ulong sandbox_clk_test_get_rate(struct udevice *dev, int id) return clk_get_rate(sbct->clkps[id]); }
+ulong sandbox_clk_test_round_rate(struct udevice *dev, int id, ulong rate) +{ + struct sandbox_clk_test *sbct = dev_get_priv(dev); + + if (id < 0 || id >= SANDBOX_CLK_TEST_ID_COUNT) + return -EINVAL; + + return clk_round_rate(sbct->clkps[id], rate); +} + ulong sandbox_clk_test_set_rate(struct udevice *dev, int id, ulong rate) { struct sandbox_clk_test *sbct = dev_get_priv(dev); diff --git a/include/clk-uclass.h b/include/clk-uclass.h index dac42dab36..50e8681b55 100644 --- a/include/clk-uclass.h +++ b/include/clk-uclass.h @@ -61,6 +61,14 @@ struct clk_ops { * @return 0 if OK, or a negative error code. */ int (*rfree)(struct clk *clock); + /** + * round_rate() - Adjust a rate to the exact rate a clock can provide. + * + * @clk: The clock to manipulate. + * @rate: Desidered clock rate in Hz. + * @return rounded rate in Hz, or -ve error code. + */ + ulong (*round_rate)(struct clk *clk, ulong rate); /** * get_rate() - Get current clock rate. * diff --git a/include/clk.h b/include/clk.h index a62e2efa2c..cce763b05b 100644 --- a/include/clk.h +++ b/include/clk.h @@ -366,6 +366,30 @@ struct clk *clk_get_parent(struct clk *clk); */ long long clk_get_parent_rate(struct clk *clk);
+/** + * clk_round_rate() - Adjust a rate to the exact rate a clock can provide + * + * This answers the question "if I were to pass @rate to clk_set_rate(), + * what clock rate would I end up with?" without changing the hardware + * in any way. In other words: + * + * rate = clk_round_rate(clk, r); + * + * and: + * + * clk_set_rate(clk, r); + * rate = clk_get_rate(clk); + * + * are equivalent except the former does not modify the clock hardware + * in any way. + * + * @clk: A clock struct that was previously successfully requested by + * clk_request/get_by_*(). + * @rate: desired clock rate in Hz. + * @return rounded rate in Hz, or -ve error code. + */ +ulong clk_round_rate(struct clk *clk, ulong rate); + /** * clk_set_rate() - Set current clock rate. * @@ -482,6 +506,11 @@ static inline long long clk_get_parent_rate(struct clk *clk) return -ENOSYS; }
+static inline ulong clk_round_rate(struct clk *clk, ulong rate) +{ + return -ENOSYS; +} + static inline ulong clk_set_rate(struct clk *clk, ulong rate) { return -ENOSYS; diff --git a/test/dm/clk.c b/test/dm/clk.c index edca3b49f6..21997ed892 100644 --- a/test/dm/clk.c +++ b/test/dm/clk.c @@ -112,6 +112,28 @@ static int dm_test_clk(struct unit_test_state *uts) rate = sandbox_clk_test_set_rate(dev_test, SANDBOX_CLK_TEST_ID_I2C, 0); ut_assert(IS_ERR_VALUE(rate));
+ ut_asserteq(10000, sandbox_clk_test_get_rate(dev_test, + SANDBOX_CLK_TEST_ID_SPI)); + ut_asserteq(20000, sandbox_clk_test_get_rate(dev_test, + SANDBOX_CLK_TEST_ID_I2C)); + + ut_asserteq(5000, sandbox_clk_test_round_rate(dev_test, + SANDBOX_CLK_TEST_ID_SPI, + 5000)); + ut_asserteq(7000, sandbox_clk_test_round_rate(dev_test, + SANDBOX_CLK_TEST_ID_I2C, + 7000)); + + ut_asserteq(10000, sandbox_clk_test_get_rate(dev_test, + SANDBOX_CLK_TEST_ID_SPI)); + ut_asserteq(20000, sandbox_clk_test_get_rate(dev_test, + SANDBOX_CLK_TEST_ID_I2C)); + + rate = sandbox_clk_test_round_rate(dev_test, SANDBOX_CLK_TEST_ID_SPI, 0); + ut_assert(IS_ERR_VALUE(rate)); + rate = sandbox_clk_test_round_rate(dev_test, SANDBOX_CLK_TEST_ID_I2C, 0); + ut_assert(IS_ERR_VALUE(rate)); + ut_asserteq(10000, sandbox_clk_test_get_rate(dev_test, SANDBOX_CLK_TEST_ID_SPI)); ut_asserteq(20000, sandbox_clk_test_get_rate(dev_test,

The driver manages a register-mapped multiplexer with multiple input clock signals or parents, one of which can be selected as output. It uses routines provided by the common clock framework (ccf).
The code is based on the drivers/clk/ti/mux.c driver of the Linux kernel.
Signed-off-by: Dario Binacchi dariobin@libero.it ---
(no changes since v1)
.../clock/clock-bindings.txt | 186 ++++++++++++ doc/device-tree-bindings/clock/ti,mux.txt | 79 +++++ drivers/clk/Kconfig | 6 + drivers/clk/Makefile | 1 + drivers/clk/clk-ti-mux.c | 275 ++++++++++++++++++ 5 files changed, 547 insertions(+) create mode 100644 doc/device-tree-bindings/clock/clock-bindings.txt create mode 100644 doc/device-tree-bindings/clock/ti,mux.txt create mode 100644 drivers/clk/clk-ti-mux.c
diff --git a/doc/device-tree-bindings/clock/clock-bindings.txt b/doc/device-tree-bindings/clock/clock-bindings.txt new file mode 100644 index 0000000000..8a55fdcf96 --- /dev/null +++ b/doc/device-tree-bindings/clock/clock-bindings.txt @@ -0,0 +1,186 @@ +This binding is a work-in-progress, and are based on some experimental +work by benh[1]. + +Sources of clock signal can be represented by any node in the device +tree. Those nodes are designated as clock providers. Clock consumer +nodes use a phandle and clock specifier pair to connect clock provider +outputs to clock inputs. Similar to the gpio specifiers, a clock +specifier is an array of zero, one or more cells identifying the clock +output on a device. The length of a clock specifier is defined by the +value of a #clock-cells property in the clock provider node. + +[1] http://patchwork.ozlabs.org/patch/31551/ + +==Clock providers== + +Required properties: +#clock-cells: Number of cells in a clock specifier; Typically 0 for nodes + with a single clock output and 1 for nodes with multiple + clock outputs. + +Optional properties: +clock-output-names: Recommended to be a list of strings of clock output signal + names indexed by the first cell in the clock specifier. + However, the meaning of clock-output-names is domain + specific to the clock provider, and is only provided to + encourage using the same meaning for the majority of clock + providers. This format may not work for clock providers + using a complex clock specifier format. In those cases it + is recommended to omit this property and create a binding + specific names property. + + Clock consumer nodes must never directly reference + the provider's clock-output-names property. + +For example: + + oscillator { + #clock-cells = <1>; + clock-output-names = "ckil", "ckih"; + }; + +- this node defines a device with two clock outputs, the first named + "ckil" and the second named "ckih". Consumer nodes always reference + clocks by index. The names should reflect the clock output signal + names for the device. + +clock-indices: If the identifying number for the clocks in the node + is not linear from zero, then this allows the mapping of + identifiers into the clock-output-names array. + +For example, if we have two clocks <&oscillator 1> and <&oscillator 3>: + + oscillator { + compatible = "myclocktype"; + #clock-cells = <1>; + clock-indices = <1>, <3>; + clock-output-names = "clka", "clkb"; + } + + This ensures we do not have any empty strings in clock-output-names + + +==Clock consumers== + +Required properties: +clocks: List of phandle and clock specifier pairs, one pair + for each clock input to the device. Note: if the + clock provider specifies '0' for #clock-cells, then + only the phandle portion of the pair will appear. + +Optional properties: +clock-names: List of clock input name strings sorted in the same + order as the clocks property. Consumers drivers + will use clock-names to match clock input names + with clocks specifiers. +clock-ranges: Empty property indicating that child nodes can inherit named + clocks from this node. Useful for bus nodes to provide a + clock to their children. + +For example: + + device { + clocks = <&osc 1>, <&ref 0>; + clock-names = "baud", "register"; + }; + + +This represents a device with two clock inputs, named "baud" and "register". +The baud clock is connected to output 1 of the &osc device, and the register +clock is connected to output 0 of the &ref. + +==Example== + + /* external oscillator */ + osc: oscillator { + compatible = "fixed-clock"; + #clock-cells = <0>; + clock-frequency = <32678>; + clock-output-names = "osc"; + }; + + /* phase-locked-loop device, generates a higher frequency clock + * from the external oscillator reference */ + pll: pll@4c000 { + compatible = "vendor,some-pll-interface" + #clock-cells = <1>; + clocks = <&osc 0>; + clock-names = "ref"; + reg = <0x4c000 0x1000>; + clock-output-names = "pll", "pll-switched"; + }; + + /* UART, using the low frequency oscillator for the baud clock, + * and the high frequency switched PLL output for register + * clocking */ + uart@a000 { + compatible = "fsl,imx-uart"; + reg = <0xa000 0x1000>; + interrupts = <33>; + clocks = <&osc 0>, <&pll 1>; + clock-names = "baud", "register"; + }; + +This DT fragment defines three devices: an external oscillator to provide a +low-frequency reference clock, a PLL device to generate a higher frequency +clock signal, and a UART. + +* The oscillator is fixed-frequency, and provides one clock output, named "osc". +* The PLL is both a clock provider and a clock consumer. It uses the clock + signal generated by the external oscillator, and provides two output signals + ("pll" and "pll-switched"). +* The UART has its baud clock connected the external oscillator and its + register clock connected to the PLL clock (the "pll-switched" signal) + +==Assigned clock parents and rates== + +Some platforms may require initial configuration of default parent clocks +and clock frequencies. Such a configuration can be specified in a device tree +node through assigned-clocks, assigned-clock-parents and assigned-clock-rates +properties. The assigned-clock-parents property should contain a list of parent +clocks in the form of a phandle and clock specifier pair and the +assigned-clock-rates property should contain a list of frequencies in Hz. Both +these properties should correspond to the clocks listed in the assigned-clocks +property. + +To skip setting parent or rate of a clock its corresponding entry should be +set to 0, or can be omitted if it is not followed by any non-zero entry. + + uart@a000 { + compatible = "fsl,imx-uart"; + reg = <0xa000 0x1000>; + ... + clocks = <&osc 0>, <&pll 1>; + clock-names = "baud", "register"; + + assigned-clocks = <&clkcon 0>, <&pll 2>; + assigned-clock-parents = <&pll 2>; + assigned-clock-rates = <0>, <460800>; + }; + +In this example the <&pll 2> clock is set as parent of clock <&clkcon 0> and +the <&pll 2> clock is assigned a frequency value of 460800 Hz. + +Configuring a clock's parent and rate through the device node that consumes +the clock can be done only for clocks that have a single user. Specifying +conflicting parent or rate configuration in multiple consumer nodes for +a shared clock is forbidden. + +Configuration of common clocks, which affect multiple consumer devices can +be similarly specified in the clock provider node. + +==Protected clocks== + +Some platforms or firmwares may not fully expose all the clocks to the OS, such +as in situations where those clks are used by drivers running in ARM secure +execution levels. Such a configuration can be specified in device tree with the +protected-clocks property in the form of a clock specifier list. This property should +only be specified in the node that is providing the clocks being protected: + + clock-controller@a000f000 { + compatible = "vendor,clk95; + reg = <0xa000f000 0x1000> + #clocks-cells = <1>; + ... + protected-clocks = <UART3_CLK>, <SPI5_CLK>; + }; diff --git a/doc/device-tree-bindings/clock/ti,mux.txt b/doc/device-tree-bindings/clock/ti,mux.txt new file mode 100644 index 0000000000..4c279288cb --- /dev/null +++ b/doc/device-tree-bindings/clock/ti,mux.txt @@ -0,0 +1,79 @@ +Binding for TI mux clock. + +Binding status: Unstable - ABI compatibility may be broken in the future + +This binding uses the common clock binding[1]. It assumes a +register-mapped multiplexer with multiple input clock signals or +parents, one of which can be selected as output. This clock does not +gate or adjust the parent rate via a divider or multiplier. + +By default the "clocks" property lists the parents in the same order +as they are programmed into the register. E.g: + + clocks = <&foo_clock>, <&bar_clock>, <&baz_clock>; + +results in programming the register as follows: + +register value selected parent clock +0 foo_clock +1 bar_clock +2 baz_clock + +Some clock controller IPs do not allow a value of zero to be programmed +into the register, instead indexing begins at 1. The optional property +"index-starts-at-one" modified the scheme as follows: + +register value selected clock parent +1 foo_clock +2 bar_clock +3 baz_clock + +The binding must provide the register to control the mux. Optionally +the number of bits to shift the control field in the register can be +supplied. If the shift value is missing it is the same as supplying +a zero shift. + +[1] doc/device-tree-bindings/clock/clock-bindings.txt + +Required properties: +- compatible : shall be "ti,mux-clock" or "ti,composite-mux-clock". +- #clock-cells : from common clock binding; shall be set to 0. +- clocks : link phandles of parent clocks +- reg : register offset for register controlling adjustable mux + +Optional properties: +- ti,bit-shift : number of bits to shift the bit-mask, defaults to + 0 if not present +- ti,index-starts-at-one : valid input select programming starts at 1, not + zero +- ti,set-rate-parent : clk_set_rate is propagated to parent clock, + not supported by the composite-mux-clock subtype +- ti,latch-bit : latch the mux value to HW, only needed if the register + access requires this. As an example, dra7x DPLL_GMAC H14 muxing + implements such behavior. + +Examples: + +sys_clkin_ck: sys_clkin_ck@4a306110 { + #clock-cells = <0>; + compatible = "ti,mux-clock"; + clocks = <&virt_12000000_ck>, <&virt_13000000_ck>, <&virt_16800000_ck>, <&virt_19200000_ck>, <&virt_26000000_ck>, <&virt_27000000_ck>, <&virt_38400000_ck>; + reg = <0x0110>; + ti,index-starts-at-one; +}; + +abe_dpll_bypass_clk_mux_ck: abe_dpll_bypass_clk_mux_ck@4a306108 { + #clock-cells = <0>; + compatible = "ti,mux-clock"; + clocks = <&sys_clkin_ck>, <&sys_32k_ck>; + ti,bit-shift = <24>; + reg = <0x0108>; +}; + +mcbsp5_mux_fck: mcbsp5_mux_fck { + #clock-cells = <0>; + compatible = "ti,composite-mux-clock"; + clocks = <&core_96m_fck>, <&mcbsp_clks>; + ti,bit-shift = <4>; + reg = <0x02d8>; +}; diff --git a/drivers/clk/Kconfig b/drivers/clk/Kconfig index 6003e140b5..f070d2637f 100644 --- a/drivers/clk/Kconfig +++ b/drivers/clk/Kconfig @@ -98,6 +98,12 @@ config CLK_STM32F This clock driver adds support for RCC clock management for STM32F4 and STM32F7 SoCs.
+config CLK_TI_MUX + bool "TI mux clock driver" + depends on CLK && OF_CONTROL && CLK_CCF + help + This enables the mux clock driver support on TI's SoCs. + config CLK_TI_SCI bool "TI System Control Interface (TI SCI) clock driver" depends on CLK && TI_SCI_PROTOCOL && OF_CONTROL diff --git a/drivers/clk/Makefile b/drivers/clk/Makefile index cda4b4b605..c0dfe4b599 100644 --- a/drivers/clk/Makefile +++ b/drivers/clk/Makefile @@ -46,6 +46,7 @@ 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_MUX) += clk-ti-mux.o obj-$(CONFIG_CLK_TI_SCI) += clk-ti-sci.o obj-$(CONFIG_CLK_VERSAL) += clk_versal.o obj-$(CONFIG_CLK_CDCE9XX) += clk-cdce9xx.o diff --git a/drivers/clk/clk-ti-mux.c b/drivers/clk/clk-ti-mux.c new file mode 100644 index 0000000000..7e39dd3477 --- /dev/null +++ b/drivers/clk/clk-ti-mux.c @@ -0,0 +1,275 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * TI multiplexer clock support + * + * Copyright (C) 2020 Dario Binacchi dariobin@libero.it + * + * Based on Linux kernel drivers/clk/ti/mux.c + */ + +#include <common.h> +#include <dm.h> +#include <clk-uclass.h> +#include <asm/io.h> +#include <linux/clk-provider.h> + +struct clk_ti_mux_priv { + struct clk_bulk parents; + fdt_addr_t reg; + u32 flags; + u32 mux_flags; + u32 mask; + u32 shift; + s32 latch; +}; + +static void clk_ti_mux_rmw(u32 val, u32 mask, fdt_addr_t reg) +{ + u32 v; + + v = readl(reg); + v &= ~mask; + v |= val; + writel(v, reg); +} + +static void clk_ti_mux_latch(fdt_addr_t reg, s8 shift) +{ + u32 latch; + + if (shift < 0) + return; + + latch = 1 << shift; + + clk_ti_mux_rmw(latch, latch, reg); + clk_ti_mux_rmw(0, latch, reg); + readl(reg); /* OCP barrier */ +} + +static struct clk *clk_ti_mux_get_parent_by_index(struct clk_bulk *parents, + int index) +{ + if (index < 0 || !parents) + return ERR_PTR(-EINVAL); + + if (index >= parents->count) + return ERR_PTR(-ENODEV); + + return &parents->clks[index]; +} + +static int clk_ti_mux_get_parent_index(struct clk_bulk *parents, + struct clk *parent) +{ + int i; + + if (!parents || !parent) + return -EINVAL; + + for (i = 0; i < parents->count; i++) { + if (parents->clks[i].dev == parent->dev) + return i; + } + + return -ENODEV; +} + +static int clk_ti_mux_get_index(struct clk *clk) +{ + struct clk_ti_mux_priv *priv = dev_get_priv(clk->dev); + u32 val; + + val = readl(priv->reg); + val >>= priv->shift; + val &= priv->mask; + + if (val && (priv->flags & CLK_MUX_INDEX_BIT)) + val = ffs(val) - 1; + + if (val && (priv->flags & CLK_MUX_INDEX_ONE)) + val--; + + if (val >= priv->parents.count) + return -EINVAL; + + return val; +} + +static int clk_ti_mux_set_parent(struct clk *clk, struct clk *parent) +{ + struct clk_ti_mux_priv *priv = dev_get_priv(clk->dev); + int index; + u32 val; + + index = clk_ti_mux_get_parent_index(&priv->parents, parent); + if (index < 0) { + dev_err(clk->dev, "failed to get parent clock\n"); + return index; + } + + index = clk_mux_index_to_val(NULL, priv->flags, index); + + if (priv->flags & CLK_MUX_HIWORD_MASK) { + val = priv->mask << (priv->shift + 16); + } else { + val = readl(priv->reg); + val &= ~(priv->mask << priv->shift); + } + + val |= index << priv->shift; + writel(val, priv->reg); + clk_ti_mux_latch(priv->reg, priv->latch); + return 0; +} + +static ulong clk_ti_mux_set_rate(struct clk *clk, ulong rate) +{ + struct clk_ti_mux_priv *priv = dev_get_priv(clk->dev); + struct clk *parent; + int index; + + if ((clk->flags & CLK_SET_RATE_PARENT) == 0) + return -ENOSYS; + + index = clk_ti_mux_get_index(clk); + parent = clk_ti_mux_get_parent_by_index(&priv->parents, index); + if (IS_ERR(parent)) + return PTR_ERR(parent); + + rate = clk_set_rate(parent, rate); + dev_dbg(clk->dev, "rate=%ld\n", rate); + return rate; +} + +static ulong clk_ti_mux_get_rate(struct clk *clk) +{ + struct clk_ti_mux_priv *priv = dev_get_priv(clk->dev); + int index; + struct clk *parent; + ulong rate; + + index = clk_ti_mux_get_index(clk); + parent = clk_ti_mux_get_parent_by_index(&priv->parents, index); + if (IS_ERR(parent)) + return PTR_ERR(parent); + + rate = clk_get_rate(parent); + dev_dbg(clk->dev, "rate=%ld\n", rate); + return rate; +} + +static ulong clk_ti_mux_round_rate(struct clk *clk, ulong rate) +{ + struct clk_ti_mux_priv *priv = dev_get_priv(clk->dev); + struct clk *parent; + int index; + + if ((clk->flags & CLK_SET_RATE_PARENT) == 0) + return -ENOSYS; + + index = clk_ti_mux_get_index(clk); + parent = clk_ti_mux_get_parent_by_index(&priv->parents, index); + if (IS_ERR(parent)) + return PTR_ERR(parent); + + rate = clk_round_rate(parent, rate); + dev_dbg(clk->dev, "rate=%ld\n", rate); + return rate; +} + +static int clk_ti_mux_request(struct clk *clk) +{ + struct clk_ti_mux_priv *priv = dev_get_priv(clk->dev); + struct clk *parent; + int index; + + clk->flags = priv->flags; + + index = clk_ti_mux_get_index(clk); + parent = clk_ti_mux_get_parent_by_index(&priv->parents, index); + if (IS_ERR(parent)) + return PTR_ERR(parent); + + return clk_ti_mux_set_parent(clk, parent); +} + +static struct clk_ops clk_ti_mux_ops = { + .request = clk_ti_mux_request, + .round_rate = clk_ti_mux_round_rate, + .get_rate = clk_ti_mux_get_rate, + .set_rate = clk_ti_mux_set_rate, + .set_parent = clk_ti_mux_set_parent, +}; + +static int clk_ti_mux_remove(struct udevice *dev) +{ + struct clk_ti_mux_priv *priv = dev_get_priv(dev); + int err; + + err = clk_release_all(priv->parents.clks, priv->parents.count); + if (err) + dev_dbg(dev, "could not release all parents' clocks\n"); + + return err; +} + +static int clk_ti_mux_probe(struct udevice *dev) +{ + struct clk_ti_mux_priv *priv = dev_get_priv(dev); + int err; + + err = clk_get_bulk(dev, &priv->parents); + if (err || priv->parents.count < 2) { + dev_err(dev, "mux-clock must have parents\n"); + return err ? err : -EFAULT; + } + + /* Generate bit-mask based on parents info */ + priv->mask = priv->parents.count; + if (!(priv->mux_flags & CLK_MUX_INDEX_ONE)) + priv->mask--; + + priv->mask = (1 << fls(priv->mask)) - 1; + return 0; +} + +static int clk_ti_mux_ofdata_to_platdata(struct udevice *dev) +{ + struct clk_ti_mux_priv *priv = dev_get_priv(dev); + + priv->reg = dev_read_addr(dev); + if (priv->reg == FDT_ADDR_T_NONE) { + dev_err(dev, "failed to get register\n"); + return -EINVAL; + } + + dev_dbg(dev, "reg=0x%08lx\n", priv->reg); + priv->shift = dev_read_u32_default(dev, "ti,bit-shift", 0); + priv->latch = dev_read_s32_default(dev, "ti,latch-bit", -EINVAL); + + priv->flags = CLK_SET_RATE_NO_REPARENT; + if (dev_read_bool(dev, "ti,set-rate-parent")) + priv->flags |= CLK_SET_RATE_PARENT; + + if (dev_read_bool(dev, "ti,index-starts-at-one")) + priv->mux_flags |= CLK_MUX_INDEX_ONE; + + return 0; +} + +static const struct udevice_id clk_ti_mux_of_match[] = { + {.compatible = "ti,mux-clock"}, + {}, +}; + +U_BOOT_DRIVER(clk_ti_mux) = { + .name = "ti_mux_clock", + .id = UCLASS_CLK, + .of_match = clk_ti_mux_of_match, + .ofdata_to_platdata = clk_ti_mux_ofdata_to_platdata, + .probe = clk_ti_mux_probe, + .remove = clk_ti_mux_remove, + .priv_auto_alloc_size = sizeof(struct clk_ti_mux_priv), + .ops = &clk_ti_mux_ops, +};

Add missing DPLL_EN_FAST_RELOCK_BYPASS macro. Used to put the DPLL in idle bypass fast relock mode.
Signed-off-by: Dario Binacchi dariobin@libero.it ---
(no changes since v1)
arch/arm/include/asm/arch-am33xx/clock.h | 1 + 1 file changed, 1 insertion(+)
diff --git a/arch/arm/include/asm/arch-am33xx/clock.h b/arch/arm/include/asm/arch-am33xx/clock.h index dc7a9b188d..5d775902bb 100644 --- a/arch/arm/include/asm/arch-am33xx/clock.h +++ b/arch/arm/include/asm/arch-am33xx/clock.h @@ -66,6 +66,7 @@ #define DPLL_EN_STOP 1 #define DPLL_EN_MN_BYPASS 4 #define DPLL_EN_LOW_POWER_BYPASS 5 +#define DPLL_EN_FAST_RELOCK_BYPASS 6 #define DPLL_EN_LOCK 7
/* CM_IDLEST_DPLL fields */

The digital phase-locked loop (DPLL) provides all interface clocks and functional clocks to the processor of the AM33xx device. The AM33xx device integrates five different DPLLs: * Core DPLL * Per DPLL * LCD DPLL * DDR DPLL * MPU DPLL
The patch adds support for the compatible strings: * "ti,am3-dpll-core-clock" * "ti,am3-dpll-no-gate-clock" * "ti,am3-dpll-no-gate-j-type-clock" * "ti,am3-dpll-x2-clock"
The code is loosely based on the drivers/clk/ti/dpll.c drivers of the Linux kernel.
Signed-off-by: Dario Binacchi dariobin@libero.it ---
(no changes since v1)
doc/device-tree-bindings/clock/ti,dpll.txt | 85 +++++++ drivers/clk/Kconfig | 7 + drivers/clk/Makefile | 1 + drivers/clk/clk-ti-am3-dpll-x2.c | 78 ++++++ drivers/clk/clk-ti-am3-dpll.c | 267 +++++++++++++++++++++ 5 files changed, 438 insertions(+) create mode 100644 doc/device-tree-bindings/clock/ti,dpll.txt create mode 100644 drivers/clk/clk-ti-am3-dpll-x2.c create mode 100644 drivers/clk/clk-ti-am3-dpll.c
diff --git a/doc/device-tree-bindings/clock/ti,dpll.txt b/doc/device-tree-bindings/clock/ti,dpll.txt new file mode 100644 index 0000000000..a807b09241 --- /dev/null +++ b/doc/device-tree-bindings/clock/ti,dpll.txt @@ -0,0 +1,85 @@ +Binding for Texas Instruments DPLL clock. + +Binding status: Unstable - ABI compatibility may be broken in the future + +This binding uses the common clock binding[1]. It assumes a +register-mapped DPLL with usually two selectable input clocks +(reference clock and bypass clock), with digital phase locked +loop logic for multiplying the input clock to a desired output +clock. This clock also typically supports different operation +modes (locked, low power stop etc.) This binding has several +sub-types, which effectively result in slightly different setup +for the actual DPLL clock. + +[1] doc/device-tree-bindings/clock/clock-bindings.txt + +Required properties: +- compatible : shall be one of: + "ti,omap3-dpll-clock", + "ti,omap3-dpll-core-clock", + "ti,omap3-dpll-per-clock", + "ti,omap3-dpll-per-j-type-clock", + "ti,omap4-dpll-clock", + "ti,omap4-dpll-x2-clock", + "ti,omap4-dpll-core-clock", + "ti,omap4-dpll-m4xen-clock", + "ti,omap4-dpll-j-type-clock", + "ti,omap5-mpu-dpll-clock", + "ti,am3-dpll-no-gate-clock", + "ti,am3-dpll-j-type-clock", + "ti,am3-dpll-no-gate-j-type-clock", + "ti,am3-dpll-clock", + "ti,am3-dpll-core-clock", + "ti,am3-dpll-x2-clock", + "ti,omap2-dpll-core-clock", + +- #clock-cells : from common clock binding; shall be set to 0. +- clocks : link phandles of parent clocks, first entry lists reference clock + and second entry bypass clock +- reg : offsets for the register set for controlling the DPLL. + Registers are listed in following order: + "control" - contains the control register base address + "idlest" - contains the idle status register base address + "mult-div1" - contains the multiplier / divider register base address + "autoidle" - contains the autoidle register base address (optional) + ti,am3-* dpll types do not have autoidle register + ti,omap2-* dpll type does not support idlest / autoidle registers + +Optional properties: +- DPLL mode setting - defining any one or more of the following overrides + default setting. + - ti,low-power-stop : DPLL supports low power stop mode, gating output + - ti,low-power-bypass : DPLL output matches rate of parent bypass clock + - ti,lock : DPLL locks in programmed rate + +Examples: + dpll_core_ck: dpll_core_ck@44e00490 { + #clock-cells = <0>; + compatible = "ti,omap4-dpll-core-clock"; + clocks = <&sys_clkin_ck>, <&sys_clkin_ck>; + reg = <0x490>, <0x45c>, <0x488>, <0x468>; + }; + + dpll2_ck: dpll2_ck@48004004 { + #clock-cells = <0>; + compatible = "ti,omap3-dpll-clock"; + clocks = <&sys_ck>, <&dpll2_fck>; + ti,low-power-stop; + ti,low-power-bypass; + ti,lock; + reg = <0x4>, <0x24>, <0x34>, <0x40>; + }; + + dpll_core_ck: dpll_core_ck@44e00490 { + #clock-cells = <0>; + compatible = "ti,am3-dpll-core-clock"; + clocks = <&sys_clkin_ck>, <&sys_clkin_ck>; + reg = <0x90>, <0x5c>, <0x68>; + }; + + dpll_ck: dpll_ck { + #clock-cells = <0>; + compatible = "ti,omap2-dpll-core-clock"; + clocks = <&sys_ck>, <&sys_ck>; + reg = <0x0500>, <0x0540>; + }; diff --git a/drivers/clk/Kconfig b/drivers/clk/Kconfig index f070d2637f..bddb454b7c 100644 --- a/drivers/clk/Kconfig +++ b/drivers/clk/Kconfig @@ -98,6 +98,13 @@ config CLK_STM32F This clock driver adds support for RCC clock management for STM32F4 and STM32F7 SoCs.
+config CLK_TI_AM3_DPLL + bool "TI AM33XX Digital Phase-Locked Loop (DPLL) clock drivers" + depends on CLK && OF_CONTROL + help + This enables the DPLL clock drivers support on AM33XX SoCs. The DPLL + provides all interface clocks and functional clocks to the processor. + config CLK_TI_MUX bool "TI mux clock driver" depends on CLK && OF_CONTROL && CLK_CCF diff --git a/drivers/clk/Makefile b/drivers/clk/Makefile index c0dfe4b599..cc20279a20 100644 --- a/drivers/clk/Makefile +++ b/drivers/clk/Makefile @@ -46,6 +46,7 @@ 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_AM3_DPLL) += clk-ti-am3-dpll.o clk-ti-am3-dpll-x2.o obj-$(CONFIG_CLK_TI_MUX) += clk-ti-mux.o obj-$(CONFIG_CLK_TI_SCI) += clk-ti-sci.o obj-$(CONFIG_CLK_VERSAL) += clk_versal.o diff --git a/drivers/clk/clk-ti-am3-dpll-x2.c b/drivers/clk/clk-ti-am3-dpll-x2.c new file mode 100644 index 0000000000..c8dedbe1f8 --- /dev/null +++ b/drivers/clk/clk-ti-am3-dpll-x2.c @@ -0,0 +1,78 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * TI DPLL x2 clock support + * + * Copyright (C) 2020 Dario Binacchi dariobin@libero.it + * + * Loosely based on Linux kernel drivers/clk/ti/dpll.c + */ + +#include <common.h> +#include <clk-uclass.h> +#include <dm.h> +#include <linux/clk-provider.h> + +struct clk_ti_am3_dpll_x2_priv { + struct clk parent; +}; + +static ulong clk_ti_am3_dpll_x2_get_rate(struct clk *clk) +{ + struct clk_ti_am3_dpll_x2_priv *priv = dev_get_priv(clk->dev); + unsigned long rate; + + rate = clk_get_rate(&priv->parent); + if (IS_ERR_VALUE(rate)) + return rate; + + rate *= 2; + dev_dbg(clk->dev, "rate=%ld\n", rate); + return rate; +} + +const struct clk_ops clk_ti_am3_dpll_x2_ops = { + .get_rate = clk_ti_am3_dpll_x2_get_rate, +}; + +static int clk_ti_am3_dpll_x2_remove(struct udevice *dev) +{ + struct clk_ti_am3_dpll_x2_priv *priv = dev_get_priv(dev); + int err; + + err = clk_release_all(&priv->parent, 1); + if (err) { + dev_err(dev, "failed to release parent clock\n"); + return err; + } + + return 0; +} + +static int clk_ti_am3_dpll_x2_probe(struct udevice *dev) +{ + struct clk_ti_am3_dpll_x2_priv *priv = dev_get_priv(dev); + int err; + + err = clk_get_by_index(dev, 0, &priv->parent); + if (err) { + dev_err(dev, "%s: failed to get parent clock\n", __func__); + return err; + } + + return 0; +} + +static const struct udevice_id clk_ti_am3_dpll_x2_of_match[] = { + {.compatible = "ti,am3-dpll-x2-clock"}, + {} +}; + +U_BOOT_DRIVER(clk_ti_am3_dpll_x2) = { + .name = "ti_am3_dpll_x2_clock", + .id = UCLASS_CLK, + .of_match = clk_ti_am3_dpll_x2_of_match, + .probe = clk_ti_am3_dpll_x2_probe, + .remove = clk_ti_am3_dpll_x2_remove, + .priv_auto_alloc_size = sizeof(struct clk_ti_am3_dpll_x2_priv), + .ops = &clk_ti_am3_dpll_x2_ops, +}; diff --git a/drivers/clk/clk-ti-am3-dpll.c b/drivers/clk/clk-ti-am3-dpll.c new file mode 100644 index 0000000000..75739f991f --- /dev/null +++ b/drivers/clk/clk-ti-am3-dpll.c @@ -0,0 +1,267 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * TI DPLL clock support + * + * Copyright (C) 2020 Dario Binacchi dariobin@libero.it + * + * Loosely based on Linux kernel drivers/clk/ti/dpll.c + */ + +#include <common.h> +#include <clk.h> +#include <clk-uclass.h> +#include <div64.h> +#include <dm.h> +#include <hang.h> +#include <asm/arch/clock.h> +#include <asm/arch/sys_proto.h> +#include <asm/io.h> + +struct clk_ti_am3_dpll_drv_data { + ulong max_rate; +}; + +struct clk_ti_am3_dpll_priv { + fdt_addr_t clkmode_reg; + fdt_addr_t idlest_reg; + fdt_addr_t clksel_reg; + struct clk clk_bypass; + struct clk clk_ref; + u16 last_rounded_mult; + u8 last_rounded_div; + ulong max_rate; +}; + +static ulong clk_ti_am3_dpll_round_rate(struct clk *clk, ulong rate) +{ + struct clk_ti_am3_dpll_priv *priv = dev_get_priv(clk->dev); + ulong ret, ref_rate, r; + int m, d, err_min, err; + int mult = INT_MAX, div = INT_MAX; + + if (priv->max_rate && rate > priv->max_rate) { + dev_warn(dev, "%ld is to high a rate, lowered to %ld\n", rate, + priv->max_rate); + rate = priv->max_rate; + } + + ret = -EFAULT; + err = rate; + err_min = rate; + ref_rate = clk_get_rate(&priv->clk_ref); + for (d = 1; err_min && d <= 128; d++) { + for (m = 2; m <= 2047; m++) { + r = (ref_rate * m) / d; + err = abs(r - rate); + if (err < err_min) { + err_min = err; + ret = r; + mult = m; + div = d; + + if (err == 0) + break; + } else if (r > rate) { + break; + } + } + } + + priv->last_rounded_mult = mult; + priv->last_rounded_div = div; + dev_dbg(dev, "rate=%ld, best_rate=%ld, mult=%d, div=%d\n", rate, ret, + mult, div); + return ret; +} + +static ulong clk_ti_am3_dpll_set_rate(struct clk *clk, ulong rate) +{ + struct clk_ti_am3_dpll_priv *priv = dev_get_priv(clk->dev); + u32 v; + ulong round_rate; + + round_rate = clk_ti_am3_dpll_round_rate(clk, rate); + if (IS_ERR_VALUE(round_rate)) + return round_rate; + + v = readl(priv->clksel_reg); + + /* enter bypass mode */ + clrsetbits_le32(priv->clkmode_reg, CM_CLKMODE_DPLL_DPLL_EN_MASK, + DPLL_EN_MN_BYPASS << CM_CLKMODE_DPLL_EN_SHIFT); + + /* wait for bypass mode */ + if (!wait_on_value(ST_DPLL_CLK_MASK, 0, + (void *)priv->idlest_reg, LDELAY)) + dev_err(clk->dev, "failed bypassing dpll\n"); + + /* set M & N */ + v &= ~CM_CLKSEL_DPLL_M_MASK; + v |= (priv->last_rounded_mult << CM_CLKSEL_DPLL_M_SHIFT) & + CM_CLKSEL_DPLL_M_MASK; + + v &= ~CM_CLKSEL_DPLL_N_MASK; + v |= ((priv->last_rounded_div - 1) << CM_CLKSEL_DPLL_N_SHIFT) & + CM_CLKSEL_DPLL_N_MASK; + + writel(v, priv->clksel_reg); + + /* lock dpll */ + clrsetbits_le32(priv->clkmode_reg, CM_CLKMODE_DPLL_DPLL_EN_MASK, + DPLL_EN_LOCK << CM_CLKMODE_DPLL_EN_SHIFT); + + /* wait till the dpll locks */ + if (!wait_on_value(ST_DPLL_CLK_MASK, ST_DPLL_CLK_MASK, + (void *)priv->idlest_reg, LDELAY)) { + dev_err(clk->dev, "failed locking dpll\n"); + hang(); + } + + return round_rate; +} + +static ulong clk_ti_am3_dpll_get_rate(struct clk *clk) +{ + struct clk_ti_am3_dpll_priv *priv = dev_get_priv(clk->dev); + u64 rate; + u32 m, n, v; + + /* Return bypass rate if DPLL is bypassed */ + v = readl(priv->clkmode_reg); + v &= CM_CLKMODE_DPLL_EN_MASK; + v >>= CM_CLKMODE_DPLL_EN_SHIFT; + + switch (v) { + case DPLL_EN_MN_BYPASS: + case DPLL_EN_LOW_POWER_BYPASS: + case DPLL_EN_FAST_RELOCK_BYPASS: + rate = clk_get_rate(&priv->clk_bypass); + dev_dbg(clk->dev, "rate=%lld\n", rate); + return rate; + } + + v = readl(priv->clksel_reg); + m = v & CM_CLKSEL_DPLL_M_MASK; + m >>= CM_CLKSEL_DPLL_M_SHIFT; + n = v & CM_CLKSEL_DPLL_N_MASK; + n >>= CM_CLKSEL_DPLL_N_SHIFT; + + rate = clk_get_rate(&priv->clk_ref) * m; + do_div(rate, n + 1); + dev_dbg(clk->dev, "rate=%lld\n", rate); + return rate; +} + +const struct clk_ops clk_ti_am3_dpll_ops = { + .round_rate = clk_ti_am3_dpll_round_rate, + .get_rate = clk_ti_am3_dpll_get_rate, + .set_rate = clk_ti_am3_dpll_set_rate, +}; + +static int clk_ti_am3_dpll_remove(struct udevice *dev) +{ + struct clk_ti_am3_dpll_priv *priv = dev_get_priv(dev); + int err; + + err = clk_release_all(&priv->clk_bypass, 1); + if (err) { + dev_err(dev, "failed to release bypass clock\n"); + return err; + } + + err = clk_release_all(&priv->clk_ref, 1); + if (err) { + dev_err(dev, "failed to release reference clock\n"); + return err; + } + + return 0; +} + +static int clk_ti_am3_dpll_probe(struct udevice *dev) +{ + struct clk_ti_am3_dpll_priv *priv = dev_get_priv(dev); + int err; + + err = clk_get_by_index(dev, 0, &priv->clk_ref); + if (err) { + dev_err(dev, "failed to get reference clock\n"); + return err; + } + + err = clk_get_by_index(dev, 1, &priv->clk_bypass); + if (err) { + dev_err(dev, "failed to get bypass clock\n"); + return err; + } + + return 0; +} + +static int clk_ti_am3_dpll_ofdata_to_platdata(struct udevice *dev) +{ + struct clk_ti_am3_dpll_priv *priv = dev_get_priv(dev); + struct clk_ti_am3_dpll_drv_data *data = + (struct clk_ti_am3_dpll_drv_data *)dev_get_driver_data(dev); + + priv->max_rate = data->max_rate; + + priv->clkmode_reg = dev_read_addr_index(dev, 0); + if (priv->clkmode_reg == FDT_ADDR_T_NONE) { + dev_err(dev, "failed to get clkmode register\n"); + return -EINVAL; + } + + dev_dbg(dev, "clkmode_reg=0x%08lx\n", priv->clkmode_reg); + + priv->idlest_reg = dev_read_addr_index(dev, 1); + if (priv->idlest_reg == FDT_ADDR_T_NONE) { + dev_err(dev, "failed to get idlest register\n"); + return -EINVAL; + } + + dev_dbg(dev, "idlest_reg=0x%08lx\n", priv->idlest_reg); + + priv->clksel_reg = dev_read_addr_index(dev, 2); + if (priv->clksel_reg == FDT_ADDR_T_NONE) { + dev_err(dev, "failed to get clksel register\n"); + return -EINVAL; + } + + dev_dbg(dev, "clksel_reg=0x%08lx\n", priv->clksel_reg); + + return 0; +} + +static const struct clk_ti_am3_dpll_drv_data dpll_no_gate_data = { + .max_rate = 1000000000 +}; + +static const struct clk_ti_am3_dpll_drv_data dpll_no_gate_j_type_data = { + .max_rate = 2000000000 +}; + +static const struct clk_ti_am3_dpll_drv_data dpll_core_data = { + .max_rate = 1000000000 +}; + +static const struct udevice_id clk_ti_am3_dpll_of_match[] = { + {.compatible = "ti,am3-dpll-core-clock", + .data = (ulong)&dpll_core_data}, + {.compatible = "ti,am3-dpll-no-gate-clock", + .data = (ulong)&dpll_no_gate_data}, + {.compatible = "ti,am3-dpll-no-gate-j-type-clock", + .data = (ulong)&dpll_no_gate_j_type_data}, + {} +}; + +U_BOOT_DRIVER(clk_ti_am3_dpll) = { + .name = "ti_am3_dpll_clock", + .id = UCLASS_CLK, + .of_match = clk_ti_am3_dpll_of_match, + .ofdata_to_platdata = clk_ti_am3_dpll_ofdata_to_platdata, + .probe = clk_ti_am3_dpll_probe, + .remove = clk_ti_am3_dpll_remove, + .priv_auto_alloc_size = sizeof(struct clk_ti_am3_dpll_priv), + .ops = &clk_ti_am3_dpll_ops, +};

Hi Dario,
On 06/09/2020 15:08, Dario Binacchi wrote:
The series was born from the need to manage the PWM backlight of the display connected to my beaglebone board. To hit the target, I had to develop drivers for PWM management which in turn relied on drivers for managing timers and clocks, all developed according to the driver model. My intention was to use the SoC-specific API only at strictly necessary points in the code. My previous patches for migrating the AM335x display driver to the driver model had required the implementation of additional functions outside the concerns of the driver, (settings for dividing the pixel clock rate, configuring the display DPLL rate, ....) not being able to use the API of the related clock drivers. This series shouldn't have repeated the same kind of mistake. Furthermore, I also wanted to fix that kind of forced choice. Almost everything should have been accessible via the driver model API. In the series there are also some patches that could be submitted separately, but which I have however inserted to avoid applying future patches to incorporate them. With this last consideration, I hope I have convincingly justified the large number of patches in the series.
The patch enabling address translation into a CPU physical address from device-tree even in case of crossing levels with #size-cells = <0>, is crucial for the series. The previous implementation was unable to perform the address translation required by the am33xx device tree. I tried to apply in a conservative way as few changes as possible and to verify the execution of all the tests already developed, as well as the new ones I added for the new feature.
Thank you for you patches.
In my opinion it's better if you split this series as it is - too big - modifies different subsystems - contains as fixes as new features
I'd recommend to separate - fixes first, like clk: remove a redundant header arch: sandbox: fix typo in clk.h fdt: translate address if #size-cells = <0> omap: timer: fix the rate setting dm: core: add a function to decode display timings .. - clk patches - pwm/backlight patches - video: omap: panel patches
And I'd recommend not to port device tree bindings in u-boot as it's just duplication of kernel binding which u-boot shell follow. Just providing links to Kernel binding in commit messages should be enough.
Changes in v2:
- Add the clk_ prefix to the divider functions.
- Add kernel-doc comments to the exported functions.
- Merged to patch [09/31] clk: ti: refactor mux and divider clock drivers.
- Remove the 'ti_am3_prcm_clocks' driver. Handle 'prcm_clocks' node in the 'ti_am3_prcm' driver.
- Update the commit message.
- Fix a missing line in the commit message.
- Add dm_flags to global_data structure and GD_DM_FLG_SIZE_CELLS_0 macro to test without recompiling.
- Update the OF_CHECK_COUNTS macro in order to have just one #define by bringing the GD_DM_FLG_SIZE_CELLS_0 into the expression.
- Lower-case the 0xC019 hex number.
- Remove the 'ti_am3_scm_clocks' driver. Handle 'scm_clocks' node in the 'ti_am3_scm' driver.
- Update the commit message.
Dario Binacchi (30): clk: remove a redundant header clk: export generic routines arch: sandbox: fix typo in clk.h clk: add clk_round_rate() clk: ti: add mux clock driver arm: ti: am33xx: add DPLL_EN_FAST_RELOCK_BYPASS macro clk: ti: am33xx: add DPLL clock drivers clk: ti: add divider clock driver clk: ti: add gate clock driver ti: am33xx: fix do_enable_clocks() to accept NULL parameters clk: ti: add support for clkctrl clocks clk: ti: move drivers to 'ti' directory clk: ti: omap4: add clock manager driver clk: ti: am335x: add clock manager driver fdt: translate address if #size-cells = <0> omap: timer: fix the rate setting misc: am33xx: add control module driver pwm: ti: am33xx: add enhanced pwm driver pwm: ti: am33xx: add subsystem driver video: backlight: fix pwm's duty cycle calculation video: backlight: fix pwm data structure description dm: core: improve uclass_get_device_by_phandle_id() description gpio: fix gpio_request_by_name() description dm: core: add a function to decode display timings video: omap: add panel driver video: omap: enable LCD clock domain through DM API video: omap: set LCD clock rate through DM API video: omap: split the legacy code from the DM code video: omap: move drivers to 'ti' directory board: ti: am335x-ice: get CDCE913 clock device

Hi Grygorii,
Il 17/09/2020 08:57 Grygorii Strashko grygorii.strashko@ti.com ha scritto:
Hi Dario,
On 06/09/2020 15:08, Dario Binacchi wrote:
The series was born from the need to manage the PWM backlight of the display connected to my beaglebone board. To hit the target, I had to develop drivers for PWM management which in turn relied on drivers for managing timers and clocks, all developed according to the driver model. My intention was to use the SoC-specific API only at strictly necessary points in the code. My previous patches for migrating the AM335x display driver to the driver model had required the implementation of additional functions outside the concerns of the driver, (settings for dividing the pixel clock rate, configuring the display DPLL rate, ....) not being able to use the API of the related clock drivers. This series shouldn't have repeated the same kind of mistake. Furthermore, I also wanted to fix that kind of forced choice. Almost everything should have been accessible via the driver model API. In the series there are also some patches that could be submitted separately, but which I have however inserted to avoid applying future patches to incorporate them. With this last consideration, I hope I have convincingly justified the large number of patches in the series.
The patch enabling address translation into a CPU physical address from device-tree even in case of crossing levels with #size-cells = <0>, is crucial for the series. The previous implementation was unable to perform the address translation required by the am33xx device tree. I tried to apply in a conservative way as few changes as possible and to verify the execution of all the tests already developed, as well as the new ones I added for the new feature.
Thank you for you patches.
In my opinion it's better if you split this series as it is
- too big
- modifies different subsystems
- contains as fixes as new features
I agree with you. I've been thinking about it for some time too. Hope in the weekend. Anyway, next patches upload will split this series.
I'd recommend to separate
- fixes first, like clk: remove a redundant header arch: sandbox: fix typo in clk.h fdt: translate address if #size-cells = <0> omap: timer: fix the rate setting dm: core: add a function to decode display timings ..
- clk patches
- pwm/backlight patches
- video: omap: panel patches
And I'd recommend not to port device tree bindings in u-boot as it's just duplication of kernel binding which u-boot shell follow. Just providing links to Kernel binding in commit messages should be enough.
I have already added device-tree bindings in patches that have been accepted. No one has ever pointed out what you recommend to me. Also, the doc/device-tree-bindings directory seems very crowded. I have read that device-tree bindings are often evolving and I think that not copying them in uboot does not favor their consultation. Also I wonder if it is enough to report in the commit message the kernel file path or to refer to a particular file version by specifying the commit sha1. Can you help me figure out what to do?
Best regards, Dario
Changes in v2:
- Add the clk_ prefix to the divider functions.
- Add kernel-doc comments to the exported functions.
- Merged to patch [09/31] clk: ti: refactor mux and divider clock drivers.
- Remove the 'ti_am3_prcm_clocks' driver. Handle 'prcm_clocks' node in the 'ti_am3_prcm' driver.
- Update the commit message.
- Fix a missing line in the commit message.
- Add dm_flags to global_data structure and GD_DM_FLG_SIZE_CELLS_0 macro to test without recompiling.
- Update the OF_CHECK_COUNTS macro in order to have just one #define by bringing the GD_DM_FLG_SIZE_CELLS_0 into the expression.
- Lower-case the 0xC019 hex number.
- Remove the 'ti_am3_scm_clocks' driver. Handle 'scm_clocks' node in the 'ti_am3_scm' driver.
- Update the commit message.
Dario Binacchi (30): clk: remove a redundant header clk: export generic routines arch: sandbox: fix typo in clk.h clk: add clk_round_rate() clk: ti: add mux clock driver arm: ti: am33xx: add DPLL_EN_FAST_RELOCK_BYPASS macro clk: ti: am33xx: add DPLL clock drivers clk: ti: add divider clock driver clk: ti: add gate clock driver ti: am33xx: fix do_enable_clocks() to accept NULL parameters clk: ti: add support for clkctrl clocks clk: ti: move drivers to 'ti' directory clk: ti: omap4: add clock manager driver clk: ti: am335x: add clock manager driver fdt: translate address if #size-cells = <0> omap: timer: fix the rate setting misc: am33xx: add control module driver pwm: ti: am33xx: add enhanced pwm driver pwm: ti: am33xx: add subsystem driver video: backlight: fix pwm's duty cycle calculation video: backlight: fix pwm data structure description dm: core: improve uclass_get_device_by_phandle_id() description gpio: fix gpio_request_by_name() description dm: core: add a function to decode display timings video: omap: add panel driver video: omap: enable LCD clock domain through DM API video: omap: set LCD clock rate through DM API video: omap: split the legacy code from the DM code video: omap: move drivers to 'ti' directory board: ti: am335x-ice: get CDCE913 clock device
-- Best regards, grygorii

On 17/09/2020 22:23, Dario Binacchi wrote:
Hi Grygorii,
Il 17/09/2020 08:57 Grygorii Strashko grygorii.strashko@ti.com ha scritto:
Hi Dario,
On 06/09/2020 15:08, Dario Binacchi wrote:
The series was born from the need to manage the PWM backlight of the display connected to my beaglebone board. To hit the target, I had to develop drivers for PWM management which in turn relied on drivers for managing timers and clocks, all developed according to the driver model. My intention was to use the SoC-specific API only at strictly necessary points in the code. My previous patches for migrating the AM335x display driver to the driver model had required the implementation of additional functions outside the concerns of the driver, (settings for dividing the pixel clock rate, configuring the display DPLL rate, ....) not being able to use the API of the related clock drivers. This series shouldn't have repeated the same kind of mistake. Furthermore, I also wanted to fix that kind of forced choice. Almost everything should have been accessible via the driver model API. In the series there are also some patches that could be submitted separately, but which I have however inserted to avoid applying future patches to incorporate them. With this last consideration, I hope I have convincingly justified the large number of patches in the series.
The patch enabling address translation into a CPU physical address from device-tree even in case of crossing levels with #size-cells = <0>, is crucial for the series. The previous implementation was unable to perform the address translation required by the am33xx device tree. I tried to apply in a conservative way as few changes as possible and to verify the execution of all the tests already developed, as well as the new ones I added for the new feature.
Thank you for you patches.
In my opinion it's better if you split this series as it is
- too big
- modifies different subsystems
- contains as fixes as new features
I agree with you. I've been thinking about it for some time too. Hope in the weekend. Anyway, next patches upload will split this series.
I'd recommend to separate
- fixes first, like clk: remove a redundant header arch: sandbox: fix typo in clk.h fdt: translate address if #size-cells = <0> omap: timer: fix the rate setting dm: core: add a function to decode display timings ..
- clk patches
- pwm/backlight patches
- video: omap: panel patches
And I'd recommend not to port device tree bindings in u-boot as it's just duplication of kernel binding which u-boot shell follow. Just providing links to Kernel binding in commit messages should be enough.
I have already added device-tree bindings in patches that have been accepted. No one has ever pointed out what you recommend to me. Also, the doc/device-tree-bindings directory seems very crowded. I have read that device-tree bindings are often evolving and I think that not copying them in uboot does not favor their consultation. Also I wonder if it is enough to report in the commit message the kernel file path or to refer to a particular file version by specifying the commit sha1. Can you help me figure out what to do?
It depends, if you porting code to u-boot it's most probably that kernel bindings evolved already (>1 commit). In such case link on file may be preferable, i think. if it's new driver which just has been accepted to the Kernel with bindings - it could be sha1. Note. Common practice for u-boot is to accept new drivers only after their binding accepted in Linux Kernel.
participants (3)
-
Dario Binacchi
-
Grygorii Strashko
-
Simon Glass