2023-04-26 10:00:30

by Dmitry Rokosov

[permalink] [raw]
Subject: [PATCH v14 0/6] add Amlogic A1 clock controller drivers

A1 SoC has four clock controllers on the board: PLL, Peripherals, CPU,
and Audio. The audio clock controller is different from others, but the
rest are very similar from a functional and regmap point of view.
This patch series add support for Amlogic A1 PLL and Peripherals clock
drivers.
It blocks all A1 peripherals mainline support and a couple of patch series,
which were already reviewed and acked, but weren't merged due to pending
clock controller drivers series, e.g.
https://lore.kernel.org/all/[email protected]/

TODO: CPU and Audio clock controllers are not included in this patch
series, it will be sent later. The following clks from these controllers
are not supported for now:
* Audio clks - vad, mclk_vad, mclk_d, resample_a, locker_in, mclk_b,
pdmdclk, pdmsysclk, eqdrc, spdifin, mclk_a, audio2_toaudiotop,
audio2_tovad, audio2_toddr_vad, audio2_tdmin_vad, audio2_pdm,
audio2_ddr_arb, audio_audiolocker, audio_eqdrc, audio_resamplea,
audio_spdifin, audio_toddrb, audio_toddra, audio_frddrb, audio_frddra,
audio_tdmoutb, audio_tdmouta, audio_loopbacka, audio_tdminlb,
audio_tdminb, audio_tdmina, audio_ddr_arb, mclk_c

* CPU clks: cpu_fixed_source_sel0, cpu_fixed_source_div0,
cpu_fixed_source_sel1, cpu_fixed_source_div1, cpu_clk

Validation:
* to double check all clk flags run below helper script:
pushd /sys/kernel/debug/clk
for f in *; do
if [[ -f "$f/clk_flags" ]]; then
flags="$(cat $f/clk_flags | awk '{$1=$1};1' | sed ':a;N;$!ba;s/\n/ | /g')"
echo -e "$f: $flags"
fi
done
popd

* to trace current clks state use '/sys/kernel/debug/clk/clk_dump' node
with jq post-processing:
$ cat /sys/kernel/debug/clk/clk_dump | jq '.' > clk_dump.json

* to debug clk rate propagation, compile kernel with the following
definition:
$ sed -i "s/undef CLOCK_ALLOW_WRITE_DEBUGFS/define CLOCK_ALLOW_WRITE_DEBUGFS/g" drivers/clk/clk.c
after that, clk_rate debug node for each clock will be available for
write operation

Changes v14 since v13 at [14]:
- remove the term "Meson" from all patchsets or replace it with
"Amlogic"
- purge extra commas as per Martin's suggestion
- drop the unneeded use of of_match_ptr(), since all Meson clock
drivers depend on the ARM64 config which selects CONFIG_OF by default

Changes v13 since v12 at [13]:
- make the clock object registration order from the roots to the leaves
- rearrange the clkids following above rule

Changes v12 since v11 at [12]:
- split the DT bindings patchset into two patches: one for the PLL clock
controller driver, and one for the peripherals clock controller driver
- to satisfy the DT binding checker, use fake references to the
peripherals clock controller in the PLL DT bindings schema, and then
replace them with real references when the peripherals bindings
become available.
- remove the public/private clocks concept from both controllers,
and instead use a linear clkid list with both exposed and internal
objects, all of which are registered in the clock provider
- combine all comments about RTC children with the flag
'CLK_SET_RATE_NO_REPARENT' into a single item with multiple
references to it

Changes v11 since v10 at [11]:
- change include/dt-bindings license to proper value required for
bindings files: 'GPL-2.0-only OR BSD-2-Clause'
- pll and peripherals clocks are split into public and private parts;
public clocks are available for external consumers through the DT layer,
private clocks include internal muxes and dividers of composite clocks,
they are placed inside clock controller driver without external access
- make public clks CLKID bindings continuous
- mark the following clock muxes as NO_REPARENT and add them to
public clocks list: GEN_SEL, DSPA_A_SEL, DSPA_B_SEL, DSPB_A_SEL,
DSP_B_B_SEL, PWM_A_SEL, PWM_B_SEL, PWM_C_SEL, PWM_D_SEL,
PWM_E_SEL, PWM_F_SEL, CECA_32K_SEL, CECB_32K_SEL; each of them can
be inherited from more accurate RTC clock and sometimes it's
required to forbid reparenting in such situation; also GEN_SEL can
be connected to external PAD and should not change parent
automatically due to rate propagation. For such clocks user must
setup parents on the DT side

Changes v10 since v9 at [10]:
- split general clk-pll changes into two different patchsets:
optional rst usage and new power sequence
- squash dt bindings patchsets to avoid chicken-or-the-egg problem
during run dt binding check routines
- add vendor prefix to PLL and Peripherals clkcs bindings filenames
- clear managed hifi_pll fields from initial poke table
- move DSPA_SEL, DSPB_SEL and SARADC_SEL to private clkid table,
because it should not be opened for direct usage
- pwm_a clk used for voltage regulation is not critical anymore, it
must be included to the proper cpu voltage regulation setup (will
be available in the next patch series)
- as discussed with Jerome, dspX clks are simple clocks and it
should be enabled/disabled/ignored/anything else from appropriate
DSP driver, so remove CLK_IGNORE_UNUSED tags
- provide more understandable comments and remove irrelevant (I hope so)
- remove CONFIG_OF usage, because it's redundant
- fix license issue, it's GPL-2.0+ only in the current version
- some commit msgs rewording

Changes v9 since v8 at [9]:
- remove common a1-clkc driver for the first version of a1 clock
controllers as Jerome suggested (it will be discussed after s4 and
a1 clks landed, hope so)
- replace inherited a1-pll clk_pll_ops with common ops and
introduce custom A1 PLL logic under MESON_PARM_APPLICABLE()
conditions
- rename xtal depended clocks in PLL and Peripherals domains
- remove 'a1_' prefix for all clocks, because they are already
inside A1 driver, it's redundant
- change udelay() to usleep_range() as preferred for small msec
amount
- purge all double quotes from the yaml schemas
- use proper dt node names following kernel guidelines
https://devicetree-specification.readthedocs.io/en/latest/chapter2-devicetree-basics.html#generic-names-recommendation
- use devm_platform_ioremap_resource() instead of simple
devm_ioremap_resource()
- mark all dspX clocks as CLK_IGNORE_UNUSED, because we do not want
to touch these clocks during CCF initialization due to possible
workload execution on it started from bootloader; in this case
bootloader already made all initialization stuff for dspX
- also mark all dspX with NO_REPARENT tag, because from dspX clocks
we want to select proper clk source from device tree

Changes v8 since v7 at [8]:
- introduced a1-clkc common driver for all A1 clock controllers
- exported meson_clk_pll_wait_lock symbol
- supported meson-a1-clkc common driver in the a1-pll and a1 clkc
- inherited a1-pll from the base clk-pll driver, implemented own
version of init/enable/disable/enabled routines; rate calculating
logic is fully the same
- aligned CLKID-related definitions with CLKID list from order
perspective to remove holes and permutations
- corrected Kconfig dependencies and types
- provided correct MODULE_AUTHORs()
- optimized and fixed up some clock relationships
- removed unused register offset definitions
- fixed up A1 PLL and Peripherals clkc dtb_check errors
- fixed clk_summary kernel panic due to missing a1_pad_ctrl
clk_regmap definition
- included PLL and Peripherals clk controllers to the base a1 dts
- The previous v7 version [8] had several logic and style problems,
all of them are resolved in this version. Original Jian Hu v7 patches
are not touched, and all additional fixes are implemented in separate
patches. Patch "clk: meson: add support for A1 PLL clock ops" is
removed, because a1-pll clk driver inherits all stuff from clk-pll
base driver, just implements custom init/enable/disable/is_enabled
callbacks.

Changes v7 since v6 at [7]:
- fix 'dt_binding_check' compiling error
- add acked-by

Changes v6 since v5 at [6]:
- fix yaml file
- add rst/current_en/l_detect parm detection
- remove 'meson_eeclkc_data' in a1.c and a1-pll.c

Changes v5 since v4 at [5]:
- change yaml GPL
- drop meson-eeclk.c patch, add probe function in each driver
- add CLK_IS_CRITICAL for sys_clk clock, drop the flag for sys_a
and sys_b
- add new parm for pll, add protection for rst parm
- drop flag for a1_fixed_pll
- remove the same comment for fclk_div, add "refer to"
- add critical flag for a1_sys_clk
- remove rtc table
- rename a1_dspa_en_dspa and a1_dspb_en_dspb
- remove useless comment

Changes v4 since v3 at [3]:
- fix reparenting orphan failed, it depends on jerome's patch [4]
- fix changelist in v3 about reparenting orphan
- remove the dts patch

Changes v3 since v2 at [2]:
- add probe function for A1
- separate the clock driver into two patch
- change some clock flags and ops
- add support for a1 PLL ops
- add A1 clock node
- fix reparenting orphan clock failed, registering xtal_fixpll
and xtal_hifipll after the provider registration, it is not
a best way.

Changes v2 since v1 at [1]:
- place A1 config alphabetically
- add actual reason for RO ops, CLK_IS_CRITICAL, CLK_IGNORE_UNUSED
- separate the driver into two driver: peripheral and pll driver
- delete CLK_IGNORE_UNUSED flag for pwm b/c/d/e/f clock, dsp clock
- delete the change in Kconfig.platforms, address to Kevin alone
- remove the useless comments
- modify the meson pll driver to support A1 PLLs

Links:
[1] https://lkml.kernel.org/r/[email protected]
[2] https://lkml.kernel.org/r/[email protected]
[3] https://lkml.kernel.org/r/[email protected]
[4] https://lkml.kernel.org/r/[email protected]
[5] https://lkml.kernel.org/r/[email protected]
[6] https://lkml.kernel.org/r/[email protected]
[7] https://lkml.kernel.org/r/[email protected]
[8] https://lore.kernel.org/linux-amlogic/[email protected]/
[9] https://lore.kernel.org/linux-amlogic/[email protected]/
[10] https://lore.kernel.org/all/[email protected]/
[11] https://lore.kernel.org/all/[email protected]/
[12] https://lore.kernel.org/all/[email protected]/
[13] https://lore.kernel.org/all/[email protected]/
[14] https://lore.kernel.org/linux-amlogic/[email protected]/

Dmitry Rokosov (6):
clk: meson: make pll rst bit as optional
clk: meson: introduce new pll power-on sequence for A1 SoC family
dt-bindings: clock: meson: add A1 PLL clock controller bindings
clk: meson: a1: add Amlogic A1 PLL clock controller driver
dt-bindings: clock: meson: add A1 Peripherals clock controller
bindings
clk: meson: a1: add Amlogic A1 Peripherals clock controller driver

.../bindings/clock/amlogic,a1-clkc.yaml | 73 +
.../bindings/clock/amlogic,a1-pll-clkc.yaml | 59 +
MAINTAINERS | 1 +
drivers/clk/meson/Kconfig | 20 +
drivers/clk/meson/Makefile | 2 +
drivers/clk/meson/a1-pll.c | 356 +++
drivers/clk/meson/a1-pll.h | 47 +
drivers/clk/meson/a1.c | 2273 +++++++++++++++++
drivers/clk/meson/a1.h | 114 +
drivers/clk/meson/clk-pll.c | 47 +-
drivers/clk/meson/clk-pll.h | 2 +
include/dt-bindings/clock/amlogic,a1-clkc.h | 114 +
.../dt-bindings/clock/amlogic,a1-pll-clkc.h | 20 +
13 files changed, 3121 insertions(+), 7 deletions(-)
create mode 100644 Documentation/devicetree/bindings/clock/amlogic,a1-clkc.yaml
create mode 100644 Documentation/devicetree/bindings/clock/amlogic,a1-pll-clkc.yaml
create mode 100644 drivers/clk/meson/a1-pll.c
create mode 100644 drivers/clk/meson/a1-pll.h
create mode 100644 drivers/clk/meson/a1.c
create mode 100644 drivers/clk/meson/a1.h
create mode 100644 include/dt-bindings/clock/amlogic,a1-clkc.h
create mode 100644 include/dt-bindings/clock/amlogic,a1-pll-clkc.h

--
2.36.0


2023-04-26 10:00:34

by Dmitry Rokosov

[permalink] [raw]
Subject: [PATCH v14 5/6] dt-bindings: clock: meson: add A1 Peripherals clock controller bindings

Add the documentation for Amlogic A1 Peripherals clock driver,
and A1 Peripherals clock controller bindings.
A1 PLL clock controller has references to A1 Peripherals clock
controller objects, so reflect them in the schema.

Signed-off-by: Jian Hu <[email protected]>
Signed-off-by: Dmitry Rokosov <[email protected]>
Reviewed-by: Rob Herring <[email protected]>
---
.../bindings/clock/amlogic,a1-clkc.yaml | 73 +++++++++++
.../bindings/clock/amlogic,a1-pll-clkc.yaml | 5 +-
include/dt-bindings/clock/amlogic,a1-clkc.h | 114 ++++++++++++++++++
3 files changed, 190 insertions(+), 2 deletions(-)
create mode 100644 Documentation/devicetree/bindings/clock/amlogic,a1-clkc.yaml
create mode 100644 include/dt-bindings/clock/amlogic,a1-clkc.h

diff --git a/Documentation/devicetree/bindings/clock/amlogic,a1-clkc.yaml b/Documentation/devicetree/bindings/clock/amlogic,a1-clkc.yaml
new file mode 100644
index 000000000000..37dd89b43d9d
--- /dev/null
+++ b/Documentation/devicetree/bindings/clock/amlogic,a1-clkc.yaml
@@ -0,0 +1,73 @@
+# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/clock/amlogic,a1-clkc.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Amlogic A1 Peripheral Clock Control Unit
+
+maintainers:
+ - Neil Armstrong <[email protected]>
+ - Jerome Brunet <[email protected]>
+ - Jian Hu <[email protected]>
+ - Dmitry Rokosov <[email protected]>
+
+properties:
+ compatible:
+ const: amlogic,a1-clkc
+
+ '#clock-cells':
+ const: 1
+
+ reg:
+ maxItems: 1
+
+ clocks:
+ items:
+ - description: input fixed pll div2
+ - description: input fixed pll div3
+ - description: input fixed pll div5
+ - description: input fixed pll div7
+ - description: input hifi pll
+ - description: input oscillator (usually at 24MHz)
+
+ clock-names:
+ items:
+ - const: fclk_div2
+ - const: fclk_div3
+ - const: fclk_div5
+ - const: fclk_div7
+ - const: hifi_pll
+ - const: xtal
+
+required:
+ - compatible
+ - '#clock-cells'
+ - reg
+ - clocks
+ - clock-names
+
+additionalProperties: false
+
+examples:
+ - |
+ #include <dt-bindings/clock/amlogic,a1-pll-clkc.h>
+ apb {
+ #address-cells = <2>;
+ #size-cells = <2>;
+
+ clock-controller@800 {
+ compatible = "amlogic,a1-clkc";
+ reg = <0 0x800 0 0x104>;
+ #clock-cells = <1>;
+ clocks = <&clkc_pll CLKID_FCLK_DIV2>,
+ <&clkc_pll CLKID_FCLK_DIV3>,
+ <&clkc_pll CLKID_FCLK_DIV5>,
+ <&clkc_pll CLKID_FCLK_DIV7>,
+ <&clkc_pll CLKID_HIFI_PLL>,
+ <&xtal>;
+ clock-names = "fclk_div2", "fclk_div3",
+ "fclk_div5", "fclk_div7",
+ "hifi_pll", "xtal";
+ };
+ };
diff --git a/Documentation/devicetree/bindings/clock/amlogic,a1-pll-clkc.yaml b/Documentation/devicetree/bindings/clock/amlogic,a1-pll-clkc.yaml
index 5c6fa620a63c..3d8490ebcaa6 100644
--- a/Documentation/devicetree/bindings/clock/amlogic,a1-pll-clkc.yaml
+++ b/Documentation/devicetree/bindings/clock/amlogic,a1-pll-clkc.yaml
@@ -43,6 +43,7 @@ additionalProperties: false

examples:
- |
+ #include <dt-bindings/clock/amlogic,a1-clkc.h>
apb {
#address-cells = <2>;
#size-cells = <2>;
@@ -51,8 +52,8 @@ examples:
compatible = "amlogic,a1-pll-clkc";
reg = <0 0x7c80 0 0x18c>;
#clock-cells = <1>;
- clocks = <&clkc_periphs_fixpll_in>,
- <&clkc_periphs_hifipll_in>;
+ clocks = <&clkc_periphs CLKID_FIXPLL_IN>,
+ <&clkc_periphs CLKID_HIFIPLL_IN>;
clock-names = "fixpll_in", "hifipll_in";
};
};
diff --git a/include/dt-bindings/clock/amlogic,a1-clkc.h b/include/dt-bindings/clock/amlogic,a1-clkc.h
new file mode 100644
index 000000000000..2c6221f2dc6b
--- /dev/null
+++ b/include/dt-bindings/clock/amlogic,a1-clkc.h
@@ -0,0 +1,114 @@
+/* SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause */
+/*
+ * Copyright (c) 2019 Amlogic, Inc. All rights reserved.
+ * Author: Jian Hu <[email protected]>
+ *
+ * Copyright (c) 2023, SberDevices. All Rights Reserved.
+ * Author: Dmitry Rokosov <[email protected]>
+ */
+
+#ifndef __A1_CLKC_H
+#define __A1_CLKC_H
+
+#define CLKID_FIXPLL_IN 1
+#define CLKID_USB_PHY_IN 2
+#define CLKID_USB_CTRL_IN 3
+#define CLKID_HIFIPLL_IN 4
+#define CLKID_SYSPLL_IN 5
+#define CLKID_DDS_IN 6
+#define CLKID_SYS 7
+#define CLKID_CLKTREE 8
+#define CLKID_RESET_CTRL 9
+#define CLKID_ANALOG_CTRL 10
+#define CLKID_PWR_CTRL 11
+#define CLKID_PAD_CTRL 12
+#define CLKID_SYS_CTRL 13
+#define CLKID_TEMP_SENSOR 14
+#define CLKID_AM2AXI_DIV 15
+#define CLKID_SPICC_B 16
+#define CLKID_SPICC_A 17
+#define CLKID_MSR 18
+#define CLKID_AUDIO 19
+#define CLKID_JTAG_CTRL 20
+#define CLKID_SARADC_EN 21
+#define CLKID_PWM_EF 22
+#define CLKID_PWM_CD 23
+#define CLKID_PWM_AB 24
+#define CLKID_CEC 25
+#define CLKID_I2C_S 26
+#define CLKID_IR_CTRL 27
+#define CLKID_I2C_M_D 28
+#define CLKID_I2C_M_C 29
+#define CLKID_I2C_M_B 30
+#define CLKID_I2C_M_A 31
+#define CLKID_ACODEC 32
+#define CLKID_OTP 33
+#define CLKID_SD_EMMC_A 34
+#define CLKID_USB_PHY 35
+#define CLKID_USB_CTRL 36
+#define CLKID_SYS_DSPB 37
+#define CLKID_SYS_DSPA 38
+#define CLKID_DMA 39
+#define CLKID_IRQ_CTRL 40
+#define CLKID_NIC 41
+#define CLKID_GIC 42
+#define CLKID_UART_C 43
+#define CLKID_UART_B 44
+#define CLKID_UART_A 45
+#define CLKID_SYS_PSRAM 46
+#define CLKID_RSA 47
+#define CLKID_CORESIGHT 48
+#define CLKID_AM2AXI_VAD 49
+#define CLKID_AUDIO_VAD 50
+#define CLKID_AXI_DMC 51
+#define CLKID_AXI_PSRAM 52
+#define CLKID_RAMB 53
+#define CLKID_RAMA 54
+#define CLKID_AXI_SPIFC 55
+#define CLKID_AXI_NIC 56
+#define CLKID_AXI_DMA 57
+#define CLKID_CPU_CTRL 58
+#define CLKID_ROM 59
+#define CLKID_PROC_I2C 60
+#define CLKID_DSPA_EN 63
+#define CLKID_DSPA_EN_NIC 64
+#define CLKID_DSPB_EN 65
+#define CLKID_DSPB_EN_NIC 66
+#define CLKID_RTC 67
+#define CLKID_CECA_32K 68
+#define CLKID_CECB_32K 69
+#define CLKID_24M 70
+#define CLKID_12M 71
+#define CLKID_FCLK_DIV2_DIVN 72
+#define CLKID_GEN 73
+#define CLKID_SARADC 75
+#define CLKID_PWM_A 76
+#define CLKID_PWM_B 77
+#define CLKID_PWM_C 78
+#define CLKID_PWM_D 79
+#define CLKID_PWM_E 80
+#define CLKID_PWM_F 81
+#define CLKID_SPICC 82
+#define CLKID_TS 83
+#define CLKID_SPIFC 84
+#define CLKID_USB_BUS 85
+#define CLKID_SD_EMMC 86
+#define CLKID_PSRAM 87
+#define CLKID_DMC 88
+#define CLKID_DSPA_A_SEL 95
+#define CLKID_DSPA_B_SEL 98
+#define CLKID_DSPB_A_SEL 101
+#define CLKID_DSPB_B_SEL 104
+#define CLKID_CECB_32K_SEL_PRE 113
+#define CLKID_CECB_32K_SEL 114
+#define CLKID_CECA_32K_SEL_PRE 117
+#define CLKID_CECA_32K_SEL 118
+#define CLKID_GEN_SEL 121
+#define CLKID_PWM_A_SEL 124
+#define CLKID_PWM_B_SEL 126
+#define CLKID_PWM_C_SEL 128
+#define CLKID_PWM_D_SEL 130
+#define CLKID_PWM_E_SEL 132
+#define CLKID_PWM_F_SEL 134
+
+#endif /* __A1_CLKC_H */
--
2.36.0

2023-04-26 10:00:39

by Dmitry Rokosov

[permalink] [raw]
Subject: [PATCH v14 6/6] clk: meson: a1: add Amlogic A1 Peripherals clock controller driver

Introduce Peripherals clock controller for Amlogic A1 SoC family.

A1 SoC has four clock controllers on the board: PLL, Peripherals, CPU,
and Audio.
This patchset adds support for Amlogic A1 Peripherals clock driver and
allows to generate clocks for all A1 SoC peripheral IPs.

Signed-off-by: Jian Hu <[email protected]>
Signed-off-by: Dmitry Rokosov <[email protected]>
---
drivers/clk/meson/Kconfig | 10 +
drivers/clk/meson/Makefile | 1 +
drivers/clk/meson/a1.c | 2273 ++++++++++++++++++++++++++++++++++++
drivers/clk/meson/a1.h | 114 ++
4 files changed, 2398 insertions(+)
create mode 100644 drivers/clk/meson/a1.c
create mode 100644 drivers/clk/meson/a1.h

diff --git a/drivers/clk/meson/Kconfig b/drivers/clk/meson/Kconfig
index 693c4cf60f27..dff576363b1f 100644
--- a/drivers/clk/meson/Kconfig
+++ b/drivers/clk/meson/Kconfig
@@ -109,6 +109,16 @@ config COMMON_CLK_A1_PLL
device, A1 SoC Family. Say Y if you want A1 PLL clock controller
to work.

+config COMMON_CLK_A1
+ tristate "Amlogic A1 SoC clock controller support"
+ depends on ARM64
+ select COMMON_CLK_MESON_DUALDIV
+ select COMMON_CLK_MESON_REGMAP
+ help
+ Support for the Peripherals clock controller on Amlogic A113L based
+ device, A1 SoC Family. Say Y if you want A1 Peripherals clock
+ controller to work.
+
config COMMON_CLK_G12A
tristate "G12 and SM1 SoC clock controllers support"
depends on ARM64
diff --git a/drivers/clk/meson/Makefile b/drivers/clk/meson/Makefile
index 2f17f475a48f..0e6f293c05d4 100644
--- a/drivers/clk/meson/Makefile
+++ b/drivers/clk/meson/Makefile
@@ -17,6 +17,7 @@ obj-$(CONFIG_COMMON_CLK_MESON_VID_PLL_DIV) += vid-pll-div.o
obj-$(CONFIG_COMMON_CLK_AXG) += axg.o axg-aoclk.o
obj-$(CONFIG_COMMON_CLK_AXG_AUDIO) += axg-audio.o
obj-$(CONFIG_COMMON_CLK_A1_PLL) += a1-pll.o
+obj-$(CONFIG_COMMON_CLK_A1) += a1.o
obj-$(CONFIG_COMMON_CLK_GXBB) += gxbb.o gxbb-aoclk.o
obj-$(CONFIG_COMMON_CLK_G12A) += g12a.o g12a-aoclk.o
obj-$(CONFIG_COMMON_CLK_MESON8B) += meson8b.o meson8-ddr.o
diff --git a/drivers/clk/meson/a1.c b/drivers/clk/meson/a1.c
new file mode 100644
index 000000000000..51b50b3ff100
--- /dev/null
+++ b/drivers/clk/meson/a1.c
@@ -0,0 +1,2273 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (c) 2019 Amlogic, Inc. All rights reserved.
+ * Author: Jian Hu <[email protected]>
+ *
+ * Copyright (c) 2023, SberDevices. All Rights Reserved.
+ * Author: Dmitry Rokosov <[email protected]>
+ */
+
+#include <linux/clk-provider.h>
+#include <linux/of_device.h>
+#include <linux/platform_device.h>
+#include "a1.h"
+#include "clk-dualdiv.h"
+#include "clk-regmap.h"
+
+static struct clk_regmap xtal_in = {
+ .data = &(struct clk_regmap_gate_data){
+ .offset = SYS_OSCIN_CTRL,
+ .bit_idx = 0,
+ },
+ .hw.init = &(struct clk_init_data) {
+ .name = "xtal_in",
+ .ops = &clk_regmap_gate_ro_ops,
+ .parent_data = &(const struct clk_parent_data) {
+ .fw_name = "xtal",
+ },
+ .num_parents = 1,
+ },
+};
+
+static struct clk_regmap fixpll_in = {
+ .data = &(struct clk_regmap_gate_data){
+ .offset = SYS_OSCIN_CTRL,
+ .bit_idx = 1,
+ },
+ .hw.init = &(struct clk_init_data) {
+ .name = "fixpll_in",
+ .ops = &clk_regmap_gate_ro_ops,
+ .parent_data = &(const struct clk_parent_data) {
+ .fw_name = "xtal",
+ },
+ .num_parents = 1,
+ },
+};
+
+static struct clk_regmap usb_phy_in = {
+ .data = &(struct clk_regmap_gate_data){
+ .offset = SYS_OSCIN_CTRL,
+ .bit_idx = 2,
+ },
+ .hw.init = &(struct clk_init_data) {
+ .name = "usb_phy_in",
+ .ops = &clk_regmap_gate_ops,
+ .parent_data = &(const struct clk_parent_data) {
+ .fw_name = "xtal",
+ },
+ .num_parents = 1,
+ },
+};
+
+static struct clk_regmap usb_ctrl_in = {
+ .data = &(struct clk_regmap_gate_data){
+ .offset = SYS_OSCIN_CTRL,
+ .bit_idx = 3,
+ },
+ .hw.init = &(struct clk_init_data) {
+ .name = "usb_ctrl_in",
+ .ops = &clk_regmap_gate_ops,
+ .parent_data = &(const struct clk_parent_data) {
+ .fw_name = "xtal",
+ },
+ .num_parents = 1,
+ },
+};
+
+static struct clk_regmap hifipll_in = {
+ .data = &(struct clk_regmap_gate_data){
+ .offset = SYS_OSCIN_CTRL,
+ .bit_idx = 4,
+ },
+ .hw.init = &(struct clk_init_data) {
+ .name = "hifipll_in",
+ .ops = &clk_regmap_gate_ops,
+ .parent_data = &(const struct clk_parent_data) {
+ .fw_name = "xtal",
+ },
+ .num_parents = 1,
+ },
+};
+
+static struct clk_regmap syspll_in = {
+ .data = &(struct clk_regmap_gate_data){
+ .offset = SYS_OSCIN_CTRL,
+ .bit_idx = 5,
+ },
+ .hw.init = &(struct clk_init_data) {
+ .name = "syspll_in",
+ .ops = &clk_regmap_gate_ops,
+ .parent_data = &(const struct clk_parent_data) {
+ .fw_name = "xtal",
+ },
+ .num_parents = 1,
+ },
+};
+
+static struct clk_regmap dds_in = {
+ .data = &(struct clk_regmap_gate_data){
+ .offset = SYS_OSCIN_CTRL,
+ .bit_idx = 6,
+ },
+ .hw.init = &(struct clk_init_data) {
+ .name = "dds_in",
+ .ops = &clk_regmap_gate_ops,
+ .parent_data = &(const struct clk_parent_data) {
+ .fw_name = "xtal",
+ },
+ .num_parents = 1,
+ },
+};
+
+static struct clk_regmap rtc_32k_in = {
+ .data = &(struct clk_regmap_gate_data){
+ .offset = RTC_BY_OSCIN_CTRL0,
+ .bit_idx = 31,
+ },
+ .hw.init = &(struct clk_init_data) {
+ .name = "rtc_32k_in",
+ .ops = &clk_regmap_gate_ops,
+ .parent_data = &(const struct clk_parent_data) {
+ .fw_name = "xtal",
+ },
+ .num_parents = 1,
+ },
+};
+
+static const struct meson_clk_dualdiv_param clk_32k_div_table[] = {
+ {
+ .dual = 1,
+ .n1 = 733,
+ .m1 = 8,
+ .n2 = 732,
+ .m2 = 11,
+ },
+ {}
+};
+
+static struct clk_regmap rtc_32k_div = {
+ .data = &(struct meson_clk_dualdiv_data){
+ .n1 = {
+ .reg_off = RTC_BY_OSCIN_CTRL0,
+ .shift = 0,
+ .width = 12,
+ },
+ .n2 = {
+ .reg_off = RTC_BY_OSCIN_CTRL0,
+ .shift = 12,
+ .width = 12,
+ },
+ .m1 = {
+ .reg_off = RTC_BY_OSCIN_CTRL1,
+ .shift = 0,
+ .width = 12,
+ },
+ .m2 = {
+ .reg_off = RTC_BY_OSCIN_CTRL1,
+ .shift = 12,
+ .width = 12,
+ },
+ .dual = {
+ .reg_off = RTC_BY_OSCIN_CTRL0,
+ .shift = 28,
+ .width = 1,
+ },
+ .table = clk_32k_div_table,
+ },
+ .hw.init = &(struct clk_init_data){
+ .name = "rtc_32k_div",
+ .ops = &meson_clk_dualdiv_ops,
+ .parent_hws = (const struct clk_hw *[]) {
+ &rtc_32k_in.hw
+ },
+ .num_parents = 1,
+ },
+};
+
+static struct clk_regmap rtc_32k_xtal = {
+ .data = &(struct clk_regmap_gate_data){
+ .offset = RTC_BY_OSCIN_CTRL1,
+ .bit_idx = 24,
+ },
+ .hw.init = &(struct clk_init_data) {
+ .name = "rtc_32k_xtal",
+ .ops = &clk_regmap_gate_ops,
+ .parent_hws = (const struct clk_hw *[]) {
+ &rtc_32k_in.hw
+ },
+ .num_parents = 1,
+ },
+};
+
+static struct clk_regmap rtc_32k_sel = {
+ .data = &(struct clk_regmap_mux_data) {
+ .offset = RTC_CTRL,
+ .mask = 0x3,
+ .shift = 0,
+ .flags = CLK_MUX_ROUND_CLOSEST,
+ },
+ .hw.init = &(struct clk_init_data){
+ .name = "rtc_32k_sel",
+ .ops = &clk_regmap_mux_ops,
+ .parent_hws = (const struct clk_hw *[]) {
+ &rtc_32k_xtal.hw,
+ &rtc_32k_div.hw,
+ },
+ .num_parents = 2,
+ .flags = CLK_SET_RATE_PARENT,
+ },
+};
+
+/*
+ * All clocks that can be inherited from a more accurate RTC clock are marked
+ * with the CLK_SET_RATE_NO_REPARENT flag. This is because in certain
+ * situations, we may need to freeze their parent. The parent setup of these
+ * clocks should be located on the device tree side.
+ */
+struct clk_regmap rtc = {
+ .data = &(struct clk_regmap_gate_data){
+ .offset = RTC_BY_OSCIN_CTRL0,
+ .bit_idx = 30,
+ },
+ .hw.init = &(struct clk_init_data){
+ .name = "rtc",
+ .ops = &clk_regmap_gate_ops,
+ .parent_hws = (const struct clk_hw *[]) {
+ &rtc_32k_sel.hw
+ },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
+ },
+};
+
+static u32 mux_table_sys[] = { 0, 1, 2, 3, 7 };
+static const struct clk_parent_data sys_parents[] = {
+ { .fw_name = "xtal" },
+ { .fw_name = "fclk_div2" },
+ { .fw_name = "fclk_div3" },
+ { .fw_name = "fclk_div5" },
+ { .hw = &rtc.hw },
+};
+
+static struct clk_regmap sys_b_sel = {
+ .data = &(struct clk_regmap_mux_data){
+ .offset = SYS_CLK_CTRL0,
+ .mask = 0x7,
+ .shift = 26,
+ .table = mux_table_sys,
+ },
+ .hw.init = &(struct clk_init_data){
+ .name = "sys_b_sel",
+ .ops = &clk_regmap_mux_ro_ops,
+ .parent_data = sys_parents,
+ .num_parents = ARRAY_SIZE(sys_parents),
+ },
+};
+
+static struct clk_regmap sys_b_div = {
+ .data = &(struct clk_regmap_div_data){
+ .offset = SYS_CLK_CTRL0,
+ .shift = 16,
+ .width = 10,
+ },
+ .hw.init = &(struct clk_init_data){
+ .name = "sys_b_div",
+ .ops = &clk_regmap_divider_ops,
+ .parent_hws = (const struct clk_hw *[]) {
+ &sys_b_sel.hw
+ },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
+ },
+};
+
+static struct clk_regmap sys_b = {
+ .data = &(struct clk_regmap_gate_data){
+ .offset = SYS_CLK_CTRL0,
+ .bit_idx = 29,
+ },
+ .hw.init = &(struct clk_init_data) {
+ .name = "sys_b",
+ .ops = &clk_regmap_gate_ops,
+ .parent_hws = (const struct clk_hw *[]) {
+ &sys_b_div.hw
+ },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
+ },
+};
+
+static struct clk_regmap sys_a_sel = {
+ .data = &(struct clk_regmap_mux_data){
+ .offset = SYS_CLK_CTRL0,
+ .mask = 0x7,
+ .shift = 10,
+ .table = mux_table_sys,
+ },
+ .hw.init = &(struct clk_init_data){
+ .name = "sys_a_sel",
+ .ops = &clk_regmap_mux_ro_ops,
+ .parent_data = sys_parents,
+ .num_parents = ARRAY_SIZE(sys_parents),
+ },
+};
+
+static struct clk_regmap sys_a_div = {
+ .data = &(struct clk_regmap_div_data){
+ .offset = SYS_CLK_CTRL0,
+ .shift = 0,
+ .width = 10,
+ },
+ .hw.init = &(struct clk_init_data){
+ .name = "sys_a_div",
+ .ops = &clk_regmap_divider_ops,
+ .parent_hws = (const struct clk_hw *[]) {
+ &sys_a_sel.hw
+ },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
+ },
+};
+
+static struct clk_regmap sys_a = {
+ .data = &(struct clk_regmap_gate_data){
+ .offset = SYS_CLK_CTRL0,
+ .bit_idx = 13,
+ },
+ .hw.init = &(struct clk_init_data) {
+ .name = "sys_a",
+ .ops = &clk_regmap_gate_ops,
+ .parent_hws = (const struct clk_hw *[]) {
+ &sys_a_div.hw
+ },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
+ },
+};
+
+static struct clk_regmap sys = {
+ .data = &(struct clk_regmap_mux_data){
+ .offset = SYS_CLK_CTRL0,
+ .mask = 0x1,
+ .shift = 31,
+ },
+ .hw.init = &(struct clk_init_data){
+ .name = "sys",
+ .ops = &clk_regmap_mux_ro_ops,
+ .parent_hws = (const struct clk_hw *[]) {
+ &sys_a.hw,
+ &sys_b.hw,
+ },
+ .num_parents = 2,
+ /*
+ * This clock is used by APB bus which is set in boot ROM code
+ * and is required by the platform to operate correctly.
+ * Until the following condition are met, we need this clock to
+ * be marked as critical:
+ * a) Mark the clock used by a firmware resource, if possible
+ * b) CCF has a clock hand-off mechanism to make the sure the
+ * clock stays on until the proper driver comes along
+ */
+ .flags = CLK_SET_RATE_PARENT | CLK_IS_CRITICAL,
+ },
+};
+
+static u32 mux_table_dsp_ab[] = { 0, 1, 2, 3, 4, 7 };
+static const struct clk_parent_data dsp_ab_parent_data[] = {
+ { .fw_name = "xtal", },
+ { .fw_name = "fclk_div2", },
+ { .fw_name = "fclk_div3", },
+ { .fw_name = "fclk_div5", },
+ { .fw_name = "hifi_pll", },
+ { .hw = &rtc.hw },
+};
+
+static struct clk_regmap dspa_a_sel = {
+ .data = &(struct clk_regmap_mux_data){
+ .offset = DSPA_CLK_CTRL0,
+ .mask = 0x7,
+ .shift = 10,
+ .table = mux_table_dsp_ab,
+ },
+ .hw.init = &(struct clk_init_data){
+ .name = "dspa_a_sel",
+ .ops = &clk_regmap_mux_ops,
+ .parent_data = dsp_ab_parent_data,
+ .num_parents = ARRAY_SIZE(dsp_ab_parent_data),
+ /* For more information, please refer to rtc clock */
+ .flags = CLK_SET_RATE_NO_REPARENT,
+ },
+};
+
+static struct clk_regmap dspa_a_div = {
+ .data = &(struct clk_regmap_div_data){
+ .offset = DSPA_CLK_CTRL0,
+ .shift = 0,
+ .width = 10,
+ },
+ .hw.init = &(struct clk_init_data){
+ .name = "dspa_a_div",
+ .ops = &clk_regmap_divider_ops,
+ .parent_hws = (const struct clk_hw *[]) {
+ &dspa_a_sel.hw
+ },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
+ },
+};
+
+static struct clk_regmap dspa_a = {
+ .data = &(struct clk_regmap_gate_data){
+ .offset = DSPA_CLK_CTRL0,
+ .bit_idx = 13,
+ },
+ .hw.init = &(struct clk_init_data) {
+ .name = "dspa_a",
+ .ops = &clk_regmap_gate_ops,
+ .parent_hws = (const struct clk_hw *[]) {
+ &dspa_a_div.hw
+ },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
+ },
+};
+
+static struct clk_regmap dspa_b_sel = {
+ .data = &(struct clk_regmap_mux_data){
+ .offset = DSPA_CLK_CTRL0,
+ .mask = 0x7,
+ .shift = 26,
+ .table = mux_table_dsp_ab,
+ },
+ .hw.init = &(struct clk_init_data){
+ .name = "dspa_b_sel",
+ .ops = &clk_regmap_mux_ops,
+ .parent_data = dsp_ab_parent_data,
+ .num_parents = ARRAY_SIZE(dsp_ab_parent_data),
+ /* For more information, please refer to rtc clock */
+ .flags = CLK_SET_RATE_NO_REPARENT,
+ },
+};
+
+static struct clk_regmap dspa_b_div = {
+ .data = &(struct clk_regmap_div_data){
+ .offset = DSPA_CLK_CTRL0,
+ .shift = 16,
+ .width = 10,
+ },
+ .hw.init = &(struct clk_init_data){
+ .name = "dspa_b_div",
+ .ops = &clk_regmap_divider_ops,
+ .parent_hws = (const struct clk_hw *[]) {
+ &dspa_b_sel.hw
+ },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
+ },
+};
+
+static struct clk_regmap dspa_b = {
+ .data = &(struct clk_regmap_gate_data){
+ .offset = DSPA_CLK_CTRL0,
+ .bit_idx = 29,
+ },
+ .hw.init = &(struct clk_init_data) {
+ .name = "dspa_b",
+ .ops = &clk_regmap_gate_ops,
+ .parent_hws = (const struct clk_hw *[]) {
+ &dspa_b_div.hw
+ },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
+ },
+};
+
+static struct clk_regmap dspa_sel = {
+ .data = &(struct clk_regmap_mux_data){
+ .offset = DSPA_CLK_CTRL0,
+ .mask = 0x1,
+ .shift = 15,
+ },
+ .hw.init = &(struct clk_init_data){
+ .name = "dspa_sel",
+ .ops = &clk_regmap_mux_ops,
+ .parent_hws = (const struct clk_hw *[]) {
+ &dspa_a.hw,
+ &dspa_b.hw,
+ },
+ .num_parents = 2,
+ .flags = CLK_SET_RATE_PARENT,
+ },
+};
+
+static struct clk_regmap dspa_en = {
+ .data = &(struct clk_regmap_gate_data){
+ .offset = DSPA_CLK_EN,
+ .bit_idx = 1,
+ },
+ .hw.init = &(struct clk_init_data) {
+ .name = "dspa_en",
+ .ops = &clk_regmap_gate_ops,
+ .parent_hws = (const struct clk_hw *[]) {
+ &dspa_sel.hw
+ },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
+ },
+};
+
+static struct clk_regmap dspa_en_nic = {
+ .data = &(struct clk_regmap_gate_data){
+ .offset = DSPA_CLK_EN,
+ .bit_idx = 0,
+ },
+ .hw.init = &(struct clk_init_data) {
+ .name = "dspa_en_nic",
+ .ops = &clk_regmap_gate_ops,
+ .parent_hws = (const struct clk_hw *[]) {
+ &dspa_sel.hw
+ },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
+ },
+};
+
+static struct clk_regmap dspb_a_sel = {
+ .data = &(struct clk_regmap_mux_data){
+ .offset = DSPB_CLK_CTRL0,
+ .mask = 0x7,
+ .shift = 10,
+ .table = mux_table_dsp_ab,
+ },
+ .hw.init = &(struct clk_init_data){
+ .name = "dspb_a_sel",
+ .ops = &clk_regmap_mux_ops,
+ .parent_data = dsp_ab_parent_data,
+ .num_parents = ARRAY_SIZE(dsp_ab_parent_data),
+ /* For more information, please refer to rtc clock */
+ .flags = CLK_SET_RATE_NO_REPARENT,
+ },
+};
+
+static struct clk_regmap dspb_a_div = {
+ .data = &(struct clk_regmap_div_data){
+ .offset = DSPB_CLK_CTRL0,
+ .shift = 0,
+ .width = 10,
+ },
+ .hw.init = &(struct clk_init_data){
+ .name = "dspb_a_div",
+ .ops = &clk_regmap_divider_ops,
+ .parent_hws = (const struct clk_hw *[]) {
+ &dspb_a_sel.hw
+ },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
+ },
+};
+
+static struct clk_regmap dspb_a = {
+ .data = &(struct clk_regmap_gate_data){
+ .offset = DSPB_CLK_CTRL0,
+ .bit_idx = 13,
+ },
+ .hw.init = &(struct clk_init_data) {
+ .name = "dspb_a",
+ .ops = &clk_regmap_gate_ops,
+ .parent_hws = (const struct clk_hw *[]) {
+ &dspb_a_div.hw
+ },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
+ },
+};
+
+static struct clk_regmap dspb_b_sel = {
+ .data = &(struct clk_regmap_mux_data){
+ .offset = DSPB_CLK_CTRL0,
+ .mask = 0x7,
+ .shift = 26,
+ .table = mux_table_dsp_ab,
+ },
+ .hw.init = &(struct clk_init_data){
+ .name = "dspb_b_sel",
+ .ops = &clk_regmap_mux_ops,
+ .parent_data = dsp_ab_parent_data,
+ .num_parents = ARRAY_SIZE(dsp_ab_parent_data),
+ /* For more information, please refer to rtc clock */
+ .flags = CLK_SET_RATE_NO_REPARENT,
+ },
+};
+
+static struct clk_regmap dspb_b_div = {
+ .data = &(struct clk_regmap_div_data){
+ .offset = DSPB_CLK_CTRL0,
+ .shift = 16,
+ .width = 10,
+ },
+ .hw.init = &(struct clk_init_data){
+ .name = "dspb_b_div",
+ .ops = &clk_regmap_divider_ops,
+ .parent_hws = (const struct clk_hw *[]) {
+ &dspb_b_sel.hw
+ },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
+ },
+};
+
+static struct clk_regmap dspb_b = {
+ .data = &(struct clk_regmap_gate_data){
+ .offset = DSPB_CLK_CTRL0,
+ .bit_idx = 29,
+ },
+ .hw.init = &(struct clk_init_data) {
+ .name = "dspb_b",
+ .ops = &clk_regmap_gate_ops,
+ .parent_hws = (const struct clk_hw *[]) {
+ &dspb_b_div.hw
+ },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
+ },
+};
+
+static struct clk_regmap dspb_sel = {
+ .data = &(struct clk_regmap_mux_data){
+ .offset = DSPB_CLK_CTRL0,
+ .mask = 0x1,
+ .shift = 15,
+ },
+ .hw.init = &(struct clk_init_data){
+ .name = "dspb_sel",
+ .ops = &clk_regmap_mux_ops,
+ .parent_hws = (const struct clk_hw *[]) {
+ &dspb_a.hw,
+ &dspb_b.hw,
+ },
+ .num_parents = 2,
+ .flags = CLK_SET_RATE_PARENT,
+ },
+};
+
+static struct clk_regmap dspb_en = {
+ .data = &(struct clk_regmap_gate_data){
+ .offset = DSPB_CLK_EN,
+ .bit_idx = 1,
+ },
+ .hw.init = &(struct clk_init_data) {
+ .name = "dspb_en",
+ .ops = &clk_regmap_gate_ops,
+ .parent_hws = (const struct clk_hw *[]) {
+ &dspb_sel.hw
+ },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
+ },
+};
+
+static struct clk_regmap dspb_en_nic = {
+ .data = &(struct clk_regmap_gate_data){
+ .offset = DSPB_CLK_EN,
+ .bit_idx = 0,
+ },
+ .hw.init = &(struct clk_init_data) {
+ .name = "dspb_en_nic",
+ .ops = &clk_regmap_gate_ops,
+ .parent_hws = (const struct clk_hw *[]) {
+ &dspb_sel.hw
+ },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
+ },
+};
+
+static struct clk_regmap clk_24m = {
+ .data = &(struct clk_regmap_gate_data){
+ .offset = CLK12_24_CTRL,
+ .bit_idx = 11,
+ },
+ .hw.init = &(struct clk_init_data) {
+ .name = "24m",
+ .ops = &clk_regmap_gate_ops,
+ .parent_data = &(const struct clk_parent_data) {
+ .fw_name = "xtal",
+ },
+ .num_parents = 1,
+ },
+};
+
+static struct clk_fixed_factor clk_24m_div2 = {
+ .mult = 1,
+ .div = 2,
+ .hw.init = &(struct clk_init_data){
+ .name = "24m_div2",
+ .ops = &clk_fixed_factor_ops,
+ .parent_hws = (const struct clk_hw *[]) {
+ &clk_24m.hw
+ },
+ .num_parents = 1,
+ },
+};
+
+static struct clk_regmap clk_12m = {
+ .data = &(struct clk_regmap_gate_data){
+ .offset = CLK12_24_CTRL,
+ .bit_idx = 10,
+ },
+ .hw.init = &(struct clk_init_data) {
+ .name = "12m",
+ .ops = &clk_regmap_gate_ops,
+ .parent_hws = (const struct clk_hw *[]) {
+ &clk_24m_div2.hw
+ },
+ .num_parents = 1,
+ },
+};
+
+static struct clk_regmap fclk_div2_divn_pre = {
+ .data = &(struct clk_regmap_div_data){
+ .offset = CLK12_24_CTRL,
+ .shift = 0,
+ .width = 8,
+ },
+ .hw.init = &(struct clk_init_data){
+ .name = "fclk_div2_divn_pre",
+ .ops = &clk_regmap_divider_ops,
+ .parent_data = &(const struct clk_parent_data) {
+ .fw_name = "fclk_div2",
+ },
+ .num_parents = 1,
+ },
+};
+
+static struct clk_regmap fclk_div2_divn = {
+ .data = &(struct clk_regmap_gate_data){
+ .offset = CLK12_24_CTRL,
+ .bit_idx = 12,
+ },
+ .hw.init = &(struct clk_init_data){
+ .name = "fclk_div2_divn",
+ .ops = &clk_regmap_gate_ops,
+ .parent_hws = (const struct clk_hw *[]) {
+ &fclk_div2_divn_pre.hw
+ },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
+ },
+};
+
+/*
+ * the index 2 is sys_pll_div16, it will be implemented in the CPU clock driver,
+ * the index 4 is the clock measurement source, it's not supported yet
+ */
+static u32 gen_table[] = { 0, 1, 3, 5, 6, 7, 8 };
+static const struct clk_parent_data gen_parent_data[] = {
+ { .fw_name = "xtal", },
+ { .hw = &rtc.hw },
+ { .fw_name = "hifi_pll", },
+ { .fw_name = "fclk_div2", },
+ { .fw_name = "fclk_div3", },
+ { .fw_name = "fclk_div5", },
+ { .fw_name = "fclk_div7", },
+};
+
+static struct clk_regmap gen_sel = {
+ .data = &(struct clk_regmap_mux_data){
+ .offset = GEN_CLK_CTRL,
+ .mask = 0xf,
+ .shift = 12,
+ .table = gen_table,
+ },
+ .hw.init = &(struct clk_init_data){
+ .name = "gen_sel",
+ .ops = &clk_regmap_mux_ops,
+ .parent_data = gen_parent_data,
+ .num_parents = ARRAY_SIZE(gen_parent_data),
+ /*
+ * The GEN clock can be connected to an external pad, so it
+ * may be set up directly from the device tree. Additionally,
+ * the GEN clock can be inherited from a more accurate RTC
+ * clock, so in certain situations, it may be necessary
+ * to freeze its parent.
+ */
+ .flags = CLK_SET_RATE_NO_REPARENT,
+ },
+};
+
+static struct clk_regmap gen_div = {
+ .data = &(struct clk_regmap_div_data){
+ .offset = GEN_CLK_CTRL,
+ .shift = 0,
+ .width = 11,
+ },
+ .hw.init = &(struct clk_init_data){
+ .name = "gen_div",
+ .ops = &clk_regmap_divider_ops,
+ .parent_hws = (const struct clk_hw *[]) {
+ &gen_sel.hw
+ },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
+ },
+};
+
+static struct clk_regmap gen = {
+ .data = &(struct clk_regmap_gate_data){
+ .offset = GEN_CLK_CTRL,
+ .bit_idx = 11,
+ },
+ .hw.init = &(struct clk_init_data) {
+ .name = "gen",
+ .ops = &clk_regmap_gate_ops,
+ .parent_hws = (const struct clk_hw *[]) {
+ &gen_div.hw
+ },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
+ },
+};
+
+static struct clk_regmap saradc_sel = {
+ .data = &(struct clk_regmap_mux_data){
+ .offset = SAR_ADC_CLK_CTRL,
+ .mask = 0x1,
+ .shift = 9,
+ },
+ .hw.init = &(struct clk_init_data){
+ .name = "saradc_sel",
+ .ops = &clk_regmap_mux_ops,
+ .parent_data = (const struct clk_parent_data []) {
+ { .fw_name = "xtal", },
+ { .hw = &sys.hw, },
+ },
+ .num_parents = 2,
+ },
+};
+
+static struct clk_regmap saradc_div = {
+ .data = &(struct clk_regmap_div_data){
+ .offset = SAR_ADC_CLK_CTRL,
+ .shift = 0,
+ .width = 8,
+ },
+ .hw.init = &(struct clk_init_data){
+ .name = "saradc_div",
+ .ops = &clk_regmap_divider_ops,
+ .parent_hws = (const struct clk_hw *[]) {
+ &saradc_sel.hw
+ },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
+ },
+};
+
+static struct clk_regmap saradc = {
+ .data = &(struct clk_regmap_gate_data){
+ .offset = SAR_ADC_CLK_CTRL,
+ .bit_idx = 8,
+ },
+ .hw.init = &(struct clk_init_data) {
+ .name = "saradc",
+ .ops = &clk_regmap_gate_ops,
+ .parent_hws = (const struct clk_hw *[]) {
+ &saradc_div.hw
+ },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
+ },
+};
+
+static const struct clk_parent_data pwm_abcd_parents[] = {
+ { .fw_name = "xtal", },
+ { .hw = &sys.hw },
+ { .hw = &rtc.hw },
+};
+
+static struct clk_regmap pwm_a_sel = {
+ .data = &(struct clk_regmap_mux_data){
+ .offset = PWM_CLK_AB_CTRL,
+ .mask = 0x1,
+ .shift = 9,
+ },
+ .hw.init = &(struct clk_init_data){
+ .name = "pwm_a_sel",
+ .ops = &clk_regmap_mux_ops,
+ .parent_data = pwm_abcd_parents,
+ .num_parents = ARRAY_SIZE(pwm_abcd_parents),
+ /* For more information, please refer to rtc clock */
+ .flags = CLK_SET_RATE_NO_REPARENT,
+ },
+};
+
+static struct clk_regmap pwm_a_div = {
+ .data = &(struct clk_regmap_div_data){
+ .offset = PWM_CLK_AB_CTRL,
+ .shift = 0,
+ .width = 8,
+ },
+ .hw.init = &(struct clk_init_data){
+ .name = "pwm_a_div",
+ .ops = &clk_regmap_divider_ops,
+ .parent_hws = (const struct clk_hw *[]) {
+ &pwm_a_sel.hw
+ },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
+ },
+};
+
+static struct clk_regmap pwm_a = {
+ .data = &(struct clk_regmap_gate_data){
+ .offset = PWM_CLK_AB_CTRL,
+ .bit_idx = 8,
+ },
+ .hw.init = &(struct clk_init_data) {
+ .name = "pwm_a",
+ .ops = &clk_regmap_gate_ops,
+ .parent_hws = (const struct clk_hw *[]) {
+ &pwm_a_div.hw
+ },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
+ },
+};
+
+static struct clk_regmap pwm_b_sel = {
+ .data = &(struct clk_regmap_mux_data){
+ .offset = PWM_CLK_AB_CTRL,
+ .mask = 0x1,
+ .shift = 25,
+ },
+ .hw.init = &(struct clk_init_data){
+ .name = "pwm_b_sel",
+ .ops = &clk_regmap_mux_ops,
+ .parent_data = pwm_abcd_parents,
+ .num_parents = ARRAY_SIZE(pwm_abcd_parents),
+ /* For more information, please refer to rtc clock */
+ .flags = CLK_SET_RATE_NO_REPARENT,
+ },
+};
+
+static struct clk_regmap pwm_b_div = {
+ .data = &(struct clk_regmap_div_data){
+ .offset = PWM_CLK_AB_CTRL,
+ .shift = 16,
+ .width = 8,
+ },
+ .hw.init = &(struct clk_init_data){
+ .name = "pwm_b_div",
+ .ops = &clk_regmap_divider_ops,
+ .parent_hws = (const struct clk_hw *[]) {
+ &pwm_b_sel.hw
+ },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
+ },
+};
+
+static struct clk_regmap pwm_b = {
+ .data = &(struct clk_regmap_gate_data){
+ .offset = PWM_CLK_AB_CTRL,
+ .bit_idx = 24,
+ },
+ .hw.init = &(struct clk_init_data) {
+ .name = "pwm_b",
+ .ops = &clk_regmap_gate_ops,
+ .parent_hws = (const struct clk_hw *[]) {
+ &pwm_b_div.hw
+ },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
+ },
+};
+
+static struct clk_regmap pwm_c_sel = {
+ .data = &(struct clk_regmap_mux_data){
+ .offset = PWM_CLK_CD_CTRL,
+ .mask = 0x1,
+ .shift = 9,
+ },
+ .hw.init = &(struct clk_init_data){
+ .name = "pwm_c_sel",
+ .ops = &clk_regmap_mux_ops,
+ .parent_data = pwm_abcd_parents,
+ .num_parents = ARRAY_SIZE(pwm_abcd_parents),
+ /* For more information, please refer to rtc clock */
+ .flags = CLK_SET_RATE_NO_REPARENT,
+ },
+};
+
+static struct clk_regmap pwm_c_div = {
+ .data = &(struct clk_regmap_div_data){
+ .offset = PWM_CLK_CD_CTRL,
+ .shift = 0,
+ .width = 8,
+ },
+ .hw.init = &(struct clk_init_data){
+ .name = "pwm_c_div",
+ .ops = &clk_regmap_divider_ops,
+ .parent_hws = (const struct clk_hw *[]) {
+ &pwm_c_sel.hw
+ },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
+ },
+};
+
+static struct clk_regmap pwm_c = {
+ .data = &(struct clk_regmap_gate_data){
+ .offset = PWM_CLK_CD_CTRL,
+ .bit_idx = 8,
+ },
+ .hw.init = &(struct clk_init_data) {
+ .name = "pwm_c",
+ .ops = &clk_regmap_gate_ops,
+ .parent_hws = (const struct clk_hw *[]) {
+ &pwm_c_div.hw
+ },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
+ },
+};
+
+static struct clk_regmap pwm_d_sel = {
+ .data = &(struct clk_regmap_mux_data){
+ .offset = PWM_CLK_CD_CTRL,
+ .mask = 0x1,
+ .shift = 25,
+ },
+ .hw.init = &(struct clk_init_data){
+ .name = "pwm_d_sel",
+ .ops = &clk_regmap_mux_ops,
+ .parent_data = pwm_abcd_parents,
+ .num_parents = ARRAY_SIZE(pwm_abcd_parents),
+ /* For more information, please refer to rtc clock */
+ .flags = CLK_SET_RATE_NO_REPARENT,
+ },
+};
+
+static struct clk_regmap pwm_d_div = {
+ .data = &(struct clk_regmap_div_data){
+ .offset = PWM_CLK_CD_CTRL,
+ .shift = 16,
+ .width = 8,
+ },
+ .hw.init = &(struct clk_init_data){
+ .name = "pwm_d_div",
+ .ops = &clk_regmap_divider_ops,
+ .parent_hws = (const struct clk_hw *[]) {
+ &pwm_d_sel.hw
+ },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
+ },
+};
+
+static struct clk_regmap pwm_d = {
+ .data = &(struct clk_regmap_gate_data){
+ .offset = PWM_CLK_CD_CTRL,
+ .bit_idx = 24,
+ },
+ .hw.init = &(struct clk_init_data) {
+ .name = "pwm_d",
+ .ops = &clk_regmap_gate_ops,
+ .parent_hws = (const struct clk_hw *[]) {
+ &pwm_d_div.hw
+ },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
+ },
+};
+
+static const struct clk_parent_data pwm_ef_parents[] = {
+ { .fw_name = "xtal", },
+ { .hw = &sys.hw },
+ { .fw_name = "fclk_div5", },
+ { .hw = &rtc.hw },
+};
+
+static struct clk_regmap pwm_e_sel = {
+ .data = &(struct clk_regmap_mux_data){
+ .offset = PWM_CLK_EF_CTRL,
+ .mask = 0x3,
+ .shift = 9,
+ },
+ .hw.init = &(struct clk_init_data){
+ .name = "pwm_e_sel",
+ .ops = &clk_regmap_mux_ops,
+ .parent_data = pwm_ef_parents,
+ .num_parents = ARRAY_SIZE(pwm_ef_parents),
+ /* For more information, please refer to rtc clock */
+ .flags = CLK_SET_RATE_NO_REPARENT,
+ },
+};
+
+static struct clk_regmap pwm_e_div = {
+ .data = &(struct clk_regmap_div_data){
+ .offset = PWM_CLK_EF_CTRL,
+ .shift = 0,
+ .width = 8,
+ },
+ .hw.init = &(struct clk_init_data){
+ .name = "pwm_e_div",
+ .ops = &clk_regmap_divider_ops,
+ .parent_hws = (const struct clk_hw *[]) {
+ &pwm_e_sel.hw
+ },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
+ },
+};
+
+static struct clk_regmap pwm_e = {
+ .data = &(struct clk_regmap_gate_data){
+ .offset = PWM_CLK_EF_CTRL,
+ .bit_idx = 8,
+ },
+ .hw.init = &(struct clk_init_data) {
+ .name = "pwm_e",
+ .ops = &clk_regmap_gate_ops,
+ .parent_hws = (const struct clk_hw *[]) {
+ &pwm_e_div.hw
+ },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
+ },
+};
+
+static struct clk_regmap pwm_f_sel = {
+ .data = &(struct clk_regmap_mux_data){
+ .offset = PWM_CLK_EF_CTRL,
+ .mask = 0x3,
+ .shift = 25,
+ },
+ .hw.init = &(struct clk_init_data){
+ .name = "pwm_f_sel",
+ .ops = &clk_regmap_mux_ops,
+ .parent_data = pwm_ef_parents,
+ .num_parents = ARRAY_SIZE(pwm_ef_parents),
+ /* For more information, please refer to rtc clock */
+ .flags = CLK_SET_RATE_NO_REPARENT,
+ },
+};
+
+static struct clk_regmap pwm_f_div = {
+ .data = &(struct clk_regmap_div_data){
+ .offset = PWM_CLK_EF_CTRL,
+ .shift = 16,
+ .width = 8,
+ },
+ .hw.init = &(struct clk_init_data){
+ .name = "pwm_f_div",
+ .ops = &clk_regmap_divider_ops,
+ .parent_hws = (const struct clk_hw *[]) {
+ &pwm_f_sel.hw
+ },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
+ },
+};
+
+static struct clk_regmap pwm_f = {
+ .data = &(struct clk_regmap_gate_data){
+ .offset = PWM_CLK_EF_CTRL,
+ .bit_idx = 24,
+ },
+ .hw.init = &(struct clk_init_data) {
+ .name = "pwm_f",
+ .ops = &clk_regmap_gate_ops,
+ .parent_hws = (const struct clk_hw *[]) {
+ &pwm_f_div.hw
+ },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
+ },
+};
+
+/*
+ * spicc clk
+ * fdiv2 |\ |\ _____
+ * ---------| |---DIV--| | | | spicc out
+ * ---------| | | |-----|GATE |---------
+ * ..... |/ | / |_____|
+ * --------------------|/
+ * 24M
+ */
+static const struct clk_parent_data spicc_spifc_parents[] = {
+ { .fw_name = "fclk_div2"},
+ { .fw_name = "fclk_div3"},
+ { .fw_name = "fclk_div5"},
+ { .fw_name = "hifi_pll" },
+};
+
+static struct clk_regmap spicc_sel = {
+ .data = &(struct clk_regmap_mux_data){
+ .offset = SPICC_CLK_CTRL,
+ .mask = 0x3,
+ .shift = 9,
+ },
+ .hw.init = &(struct clk_init_data){
+ .name = "spicc_sel",
+ .ops = &clk_regmap_mux_ops,
+ .parent_data = spicc_spifc_parents,
+ .num_parents = ARRAY_SIZE(spicc_spifc_parents),
+ },
+};
+
+static struct clk_regmap spicc_div = {
+ .data = &(struct clk_regmap_div_data){
+ .offset = SPICC_CLK_CTRL,
+ .shift = 0,
+ .width = 8,
+ },
+ .hw.init = &(struct clk_init_data){
+ .name = "spicc_div",
+ .ops = &clk_regmap_divider_ops,
+ .parent_hws = (const struct clk_hw *[]) {
+ &spicc_sel.hw
+ },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
+ },
+};
+
+static struct clk_regmap spicc_sel2 = {
+ .data = &(struct clk_regmap_mux_data){
+ .offset = SPICC_CLK_CTRL,
+ .mask = 0x1,
+ .shift = 15,
+ },
+ .hw.init = &(struct clk_init_data){
+ .name = "spicc_sel2",
+ .ops = &clk_regmap_mux_ops,
+ .parent_data = (const struct clk_parent_data []) {
+ { .hw = &spicc_div.hw },
+ { .fw_name = "xtal", },
+ },
+ .num_parents = 2,
+ .flags = CLK_SET_RATE_PARENT,
+ },
+};
+
+static struct clk_regmap spicc = {
+ .data = &(struct clk_regmap_gate_data){
+ .offset = SPICC_CLK_CTRL,
+ .bit_idx = 8,
+ },
+ .hw.init = &(struct clk_init_data) {
+ .name = "spicc",
+ .ops = &clk_regmap_gate_ops,
+ .parent_hws = (const struct clk_hw *[]) {
+ &spicc_sel2.hw
+ },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
+ },
+};
+
+static struct clk_regmap ts_div = {
+ .data = &(struct clk_regmap_div_data){
+ .offset = TS_CLK_CTRL,
+ .shift = 0,
+ .width = 8,
+ },
+ .hw.init = &(struct clk_init_data){
+ .name = "ts_div",
+ .ops = &clk_regmap_divider_ops,
+ .parent_data = &(const struct clk_parent_data) {
+ .fw_name = "xtal",
+ },
+ .num_parents = 1,
+ },
+};
+
+static struct clk_regmap ts = {
+ .data = &(struct clk_regmap_gate_data){
+ .offset = TS_CLK_CTRL,
+ .bit_idx = 8,
+ },
+ .hw.init = &(struct clk_init_data) {
+ .name = "ts",
+ .ops = &clk_regmap_gate_ops,
+ .parent_hws = (const struct clk_hw *[]) {
+ &ts_div.hw
+ },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
+ },
+};
+
+static struct clk_regmap spifc_sel = {
+ .data = &(struct clk_regmap_mux_data){
+ .offset = SPIFC_CLK_CTRL,
+ .mask = 0x3,
+ .shift = 9,
+ },
+ .hw.init = &(struct clk_init_data){
+ .name = "spifc_sel",
+ .ops = &clk_regmap_mux_ops,
+ .parent_data = spicc_spifc_parents,
+ .num_parents = ARRAY_SIZE(spicc_spifc_parents),
+ },
+};
+
+static struct clk_regmap spifc_div = {
+ .data = &(struct clk_regmap_div_data){
+ .offset = SPIFC_CLK_CTRL,
+ .shift = 0,
+ .width = 8,
+ },
+ .hw.init = &(struct clk_init_data){
+ .name = "spifc_div",
+ .ops = &clk_regmap_divider_ops,
+ .parent_hws = (const struct clk_hw *[]) {
+ &spifc_sel.hw
+ },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
+ },
+};
+
+static struct clk_regmap spifc_sel2 = {
+ .data = &(struct clk_regmap_mux_data){
+ .offset = SPIFC_CLK_CTRL,
+ .mask = 0x1,
+ .shift = 15,
+ },
+ .hw.init = &(struct clk_init_data){
+ .name = "spifc_sel2",
+ .ops = &clk_regmap_mux_ops,
+ .parent_data = (const struct clk_parent_data []) {
+ { .hw = &spifc_div.hw },
+ { .fw_name = "xtal", },
+ },
+ .num_parents = 2,
+ .flags = CLK_SET_RATE_PARENT,
+ },
+};
+
+static struct clk_regmap spifc = {
+ .data = &(struct clk_regmap_gate_data){
+ .offset = SPIFC_CLK_CTRL,
+ .bit_idx = 8,
+ },
+ .hw.init = &(struct clk_init_data) {
+ .name = "spifc",
+ .ops = &clk_regmap_gate_ops,
+ .parent_hws = (const struct clk_hw *[]) {
+ &spifc_sel2.hw
+ },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
+ },
+};
+
+static const struct clk_parent_data usb_bus_parents[] = {
+ { .fw_name = "xtal", },
+ { .hw = &sys.hw },
+ { .fw_name = "fclk_div3", },
+ { .fw_name = "fclk_div5", },
+};
+
+static struct clk_regmap usb_bus_sel = {
+ .data = &(struct clk_regmap_mux_data){
+ .offset = USB_BUSCLK_CTRL,
+ .mask = 0x3,
+ .shift = 9,
+ },
+ .hw.init = &(struct clk_init_data){
+ .name = "usb_bus_sel",
+ .ops = &clk_regmap_mux_ops,
+ .parent_data = usb_bus_parents,
+ .num_parents = ARRAY_SIZE(usb_bus_parents),
+ .flags = CLK_SET_RATE_PARENT,
+ },
+};
+
+static struct clk_regmap usb_bus_div = {
+ .data = &(struct clk_regmap_div_data){
+ .offset = USB_BUSCLK_CTRL,
+ .shift = 0,
+ .width = 8,
+ },
+ .hw.init = &(struct clk_init_data){
+ .name = "usb_bus_div",
+ .ops = &clk_regmap_divider_ops,
+ .parent_hws = (const struct clk_hw *[]) {
+ &usb_bus_sel.hw
+ },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
+ },
+};
+
+static struct clk_regmap usb_bus = {
+ .data = &(struct clk_regmap_gate_data){
+ .offset = USB_BUSCLK_CTRL,
+ .bit_idx = 8,
+ },
+ .hw.init = &(struct clk_init_data) {
+ .name = "usb_bus",
+ .ops = &clk_regmap_gate_ops,
+ .parent_hws = (const struct clk_hw *[]) {
+ &usb_bus_div.hw
+ },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
+ },
+};
+
+static const struct clk_parent_data sd_emmc_psram_dmc_parents[] = {
+ { .fw_name = "fclk_div2", },
+ { .fw_name = "fclk_div3", },
+ { .fw_name = "fclk_div5", },
+ { .fw_name = "hifi_pll", },
+};
+
+static struct clk_regmap sd_emmc_sel = {
+ .data = &(struct clk_regmap_mux_data){
+ .offset = SD_EMMC_CLK_CTRL,
+ .mask = 0x3,
+ .shift = 9,
+ },
+ .hw.init = &(struct clk_init_data){
+ .name = "sd_emmc_sel",
+ .ops = &clk_regmap_mux_ops,
+ .parent_data = sd_emmc_psram_dmc_parents,
+ .num_parents = ARRAY_SIZE(sd_emmc_psram_dmc_parents),
+ },
+};
+
+static struct clk_regmap sd_emmc_div = {
+ .data = &(struct clk_regmap_div_data){
+ .offset = SD_EMMC_CLK_CTRL,
+ .shift = 0,
+ .width = 8,
+ },
+ .hw.init = &(struct clk_init_data){
+ .name = "sd_emmc_div",
+ .ops = &clk_regmap_divider_ops,
+ .parent_hws = (const struct clk_hw *[]) {
+ &sd_emmc_sel.hw
+ },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
+ },
+};
+
+static struct clk_regmap sd_emmc_sel2 = {
+ .data = &(struct clk_regmap_mux_data){
+ .offset = SD_EMMC_CLK_CTRL,
+ .mask = 0x1,
+ .shift = 15,
+ },
+ .hw.init = &(struct clk_init_data){
+ .name = "sd_emmc_sel2",
+ .ops = &clk_regmap_mux_ops,
+ .parent_data = (const struct clk_parent_data []) {
+ { .hw = &sd_emmc_div.hw },
+ { .fw_name = "xtal", },
+ },
+ .num_parents = 2,
+ .flags = CLK_SET_RATE_PARENT,
+ },
+};
+
+static struct clk_regmap sd_emmc = {
+ .data = &(struct clk_regmap_gate_data){
+ .offset = SD_EMMC_CLK_CTRL,
+ .bit_idx = 8,
+ },
+ .hw.init = &(struct clk_init_data) {
+ .name = "sd_emmc",
+ .ops = &clk_regmap_gate_ops,
+ .parent_hws = (const struct clk_hw *[]) {
+ &sd_emmc_sel2.hw
+ },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
+ },
+};
+
+static struct clk_regmap psram_sel = {
+ .data = &(struct clk_regmap_mux_data){
+ .offset = PSRAM_CLK_CTRL,
+ .mask = 0x3,
+ .shift = 9,
+ },
+ .hw.init = &(struct clk_init_data){
+ .name = "psram_sel",
+ .ops = &clk_regmap_mux_ops,
+ .parent_data = sd_emmc_psram_dmc_parents,
+ .num_parents = ARRAY_SIZE(sd_emmc_psram_dmc_parents),
+ },
+};
+
+static struct clk_regmap psram_div = {
+ .data = &(struct clk_regmap_div_data){
+ .offset = PSRAM_CLK_CTRL,
+ .shift = 0,
+ .width = 8,
+ },
+ .hw.init = &(struct clk_init_data){
+ .name = "psram_div",
+ .ops = &clk_regmap_divider_ops,
+ .parent_hws = (const struct clk_hw *[]) {
+ &psram_sel.hw
+ },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
+ },
+};
+
+static struct clk_regmap psram_sel2 = {
+ .data = &(struct clk_regmap_mux_data){
+ .offset = PSRAM_CLK_CTRL,
+ .mask = 0x1,
+ .shift = 15,
+ },
+ .hw.init = &(struct clk_init_data){
+ .name = "psram_sel2",
+ .ops = &clk_regmap_mux_ops,
+ .parent_data = (const struct clk_parent_data []) {
+ { .hw = &psram_div.hw },
+ { .fw_name = "xtal", },
+ },
+ .num_parents = 2,
+ .flags = CLK_SET_RATE_PARENT,
+ },
+};
+
+static struct clk_regmap psram = {
+ .data = &(struct clk_regmap_gate_data){
+ .offset = PSRAM_CLK_CTRL,
+ .bit_idx = 8,
+ },
+ .hw.init = &(struct clk_init_data) {
+ .name = "psram",
+ .ops = &clk_regmap_gate_ops,
+ .parent_hws = (const struct clk_hw *[]) {
+ &psram_sel2.hw
+ },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
+ },
+};
+
+static struct clk_regmap dmc_sel = {
+ .data = &(struct clk_regmap_mux_data){
+ .offset = DMC_CLK_CTRL,
+ .mask = 0x3,
+ .shift = 9,
+ },
+ .hw.init = &(struct clk_init_data){
+ .name = "dmc_sel",
+ .ops = &clk_regmap_mux_ops,
+ .parent_data = sd_emmc_psram_dmc_parents,
+ .num_parents = ARRAY_SIZE(sd_emmc_psram_dmc_parents),
+ },
+};
+
+static struct clk_regmap dmc_div = {
+ .data = &(struct clk_regmap_div_data){
+ .offset = DMC_CLK_CTRL,
+ .shift = 0,
+ .width = 8,
+ },
+ .hw.init = &(struct clk_init_data){
+ .name = "dmc_div",
+ .ops = &clk_regmap_divider_ops,
+ .parent_hws = (const struct clk_hw *[]) {
+ &dmc_sel.hw
+ },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
+ },
+};
+
+static struct clk_regmap dmc_sel2 = {
+ .data = &(struct clk_regmap_mux_data){
+ .offset = DMC_CLK_CTRL,
+ .mask = 0x1,
+ .shift = 15,
+ },
+ .hw.init = &(struct clk_init_data){
+ .name = "dmc_sel2",
+ .ops = &clk_regmap_mux_ops,
+ .parent_data = (const struct clk_parent_data []) {
+ { .hw = &dmc_div.hw },
+ { .fw_name = "xtal", },
+ },
+ .num_parents = 2,
+ .flags = CLK_SET_RATE_PARENT,
+ },
+};
+
+static struct clk_regmap dmc = {
+ .data = &(struct clk_regmap_gate_data){
+ .offset = DMC_CLK_CTRL,
+ .bit_idx = 8,
+ },
+ .hw.init = &(struct clk_init_data) {
+ .name = "dmc",
+ .ops = &clk_regmap_gate_ro_ops,
+ .parent_hws = (const struct clk_hw *[]) {
+ &dmc_sel2.hw
+ },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
+ },
+};
+
+static struct clk_regmap ceca_32k_in = {
+ .data = &(struct clk_regmap_gate_data){
+ .offset = CECA_CLK_CTRL0,
+ .bit_idx = 31,
+ },
+ .hw.init = &(struct clk_init_data) {
+ .name = "ceca_32k_in",
+ .ops = &clk_regmap_gate_ops,
+ .parent_data = &(const struct clk_parent_data) {
+ .fw_name = "xtal",
+ },
+ .num_parents = 1,
+ },
+};
+
+static struct clk_regmap ceca_32k_div = {
+ .data = &(struct meson_clk_dualdiv_data){
+ .n1 = {
+ .reg_off = CECA_CLK_CTRL0,
+ .shift = 0,
+ .width = 12,
+ },
+ .n2 = {
+ .reg_off = CECA_CLK_CTRL0,
+ .shift = 12,
+ .width = 12,
+ },
+ .m1 = {
+ .reg_off = CECA_CLK_CTRL1,
+ .shift = 0,
+ .width = 12,
+ },
+ .m2 = {
+ .reg_off = CECA_CLK_CTRL1,
+ .shift = 12,
+ .width = 12,
+ },
+ .dual = {
+ .reg_off = CECA_CLK_CTRL0,
+ .shift = 28,
+ .width = 1,
+ },
+ .table = clk_32k_div_table,
+ },
+ .hw.init = &(struct clk_init_data){
+ .name = "ceca_32k_div",
+ .ops = &meson_clk_dualdiv_ops,
+ .parent_hws = (const struct clk_hw *[]) {
+ &ceca_32k_in.hw
+ },
+ .num_parents = 1,
+ },
+};
+
+static struct clk_regmap ceca_32k_sel_pre = {
+ .data = &(struct clk_regmap_mux_data) {
+ .offset = CECA_CLK_CTRL1,
+ .mask = 0x1,
+ .shift = 24,
+ .flags = CLK_MUX_ROUND_CLOSEST,
+ },
+ .hw.init = &(struct clk_init_data){
+ .name = "ceca_32k_sel_pre",
+ .ops = &clk_regmap_mux_ops,
+ .parent_hws = (const struct clk_hw *[]) {
+ &ceca_32k_div.hw,
+ &ceca_32k_in.hw,
+ },
+ .num_parents = 2,
+ .flags = CLK_SET_RATE_PARENT,
+ },
+};
+
+static struct clk_regmap ceca_32k_sel = {
+ .data = &(struct clk_regmap_mux_data) {
+ .offset = CECA_CLK_CTRL1,
+ .mask = 0x1,
+ .shift = 31,
+ .flags = CLK_MUX_ROUND_CLOSEST,
+ },
+ .hw.init = &(struct clk_init_data){
+ .name = "ceca_32k_sel",
+ .ops = &clk_regmap_mux_ops,
+ .parent_hws = (const struct clk_hw *[]) {
+ &ceca_32k_sel_pre.hw,
+ &rtc.hw,
+ },
+ .num_parents = 2,
+ /* For more information, please refer to rtc clock */
+ .flags = CLK_SET_RATE_NO_REPARENT,
+ },
+};
+
+static struct clk_regmap ceca_32k_out = {
+ .data = &(struct clk_regmap_gate_data){
+ .offset = CECA_CLK_CTRL0,
+ .bit_idx = 30,
+ },
+ .hw.init = &(struct clk_init_data){
+ .name = "ceca_32k_out",
+ .ops = &clk_regmap_gate_ops,
+ .parent_hws = (const struct clk_hw *[]) {
+ &ceca_32k_sel.hw
+ },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
+ },
+};
+
+static struct clk_regmap cecb_32k_in = {
+ .data = &(struct clk_regmap_gate_data){
+ .offset = CECB_CLK_CTRL0,
+ .bit_idx = 31,
+ },
+ .hw.init = &(struct clk_init_data) {
+ .name = "cecb_32k_in",
+ .ops = &clk_regmap_gate_ops,
+ .parent_data = &(const struct clk_parent_data) {
+ .fw_name = "xtal",
+ },
+ .num_parents = 1,
+ },
+};
+
+static struct clk_regmap cecb_32k_div = {
+ .data = &(struct meson_clk_dualdiv_data){
+ .n1 = {
+ .reg_off = CECB_CLK_CTRL0,
+ .shift = 0,
+ .width = 12,
+ },
+ .n2 = {
+ .reg_off = CECB_CLK_CTRL0,
+ .shift = 12,
+ .width = 12,
+ },
+ .m1 = {
+ .reg_off = CECB_CLK_CTRL1,
+ .shift = 0,
+ .width = 12,
+ },
+ .m2 = {
+ .reg_off = CECB_CLK_CTRL1,
+ .shift = 12,
+ .width = 12,
+ },
+ .dual = {
+ .reg_off = CECB_CLK_CTRL0,
+ .shift = 28,
+ .width = 1,
+ },
+ .table = clk_32k_div_table,
+ },
+ .hw.init = &(struct clk_init_data){
+ .name = "cecb_32k_div",
+ .ops = &meson_clk_dualdiv_ops,
+ .parent_hws = (const struct clk_hw *[]) {
+ &cecb_32k_in.hw
+ },
+ .num_parents = 1,
+ },
+};
+
+static struct clk_regmap cecb_32k_sel_pre = {
+ .data = &(struct clk_regmap_mux_data) {
+ .offset = CECB_CLK_CTRL1,
+ .mask = 0x1,
+ .shift = 24,
+ .flags = CLK_MUX_ROUND_CLOSEST,
+ },
+ .hw.init = &(struct clk_init_data){
+ .name = "cecb_32k_sel_pre",
+ .ops = &clk_regmap_mux_ops,
+ .parent_hws = (const struct clk_hw *[]) {
+ &cecb_32k_div.hw,
+ &cecb_32k_in.hw,
+ },
+ .num_parents = 2,
+ .flags = CLK_SET_RATE_PARENT,
+ },
+};
+
+static struct clk_regmap cecb_32k_sel = {
+ .data = &(struct clk_regmap_mux_data) {
+ .offset = CECB_CLK_CTRL1,
+ .mask = 0x1,
+ .shift = 31,
+ .flags = CLK_MUX_ROUND_CLOSEST,
+ },
+ .hw.init = &(struct clk_init_data){
+ .name = "cecb_32k_sel",
+ .ops = &clk_regmap_mux_ops,
+ .parent_hws = (const struct clk_hw *[]) {
+ &cecb_32k_sel_pre.hw,
+ &rtc.hw,
+ },
+ .num_parents = 2,
+ /* For more information, please refer to rtc clock */
+ .flags = CLK_SET_RATE_NO_REPARENT,
+ },
+};
+
+static struct clk_regmap cecb_32k_out = {
+ .data = &(struct clk_regmap_gate_data){
+ .offset = CECB_CLK_CTRL0,
+ .bit_idx = 30,
+ },
+ .hw.init = &(struct clk_init_data){
+ .name = "cecb_32k_out",
+ .ops = &clk_regmap_gate_ops,
+ .parent_hws = (const struct clk_hw *[]) {
+ &cecb_32k_sel.hw
+ },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
+ },
+};
+
+#define MESON_GATE(_name, _reg, _bit) \
+ MESON_PCLK(_name, _reg, _bit, &sys.hw)
+
+static MESON_GATE(clktree, SYS_CLK_EN0, 0);
+static MESON_GATE(reset_ctrl, SYS_CLK_EN0, 1);
+static MESON_GATE(analog_ctrl, SYS_CLK_EN0, 2);
+static MESON_GATE(pwr_ctrl, SYS_CLK_EN0, 3);
+static MESON_GATE(pad_ctrl, SYS_CLK_EN0, 4);
+static MESON_GATE(sys_ctrl, SYS_CLK_EN0, 5);
+static MESON_GATE(temp_sensor, SYS_CLK_EN0, 6);
+static MESON_GATE(am2axi_dev, SYS_CLK_EN0, 7);
+static MESON_GATE(spicc_b, SYS_CLK_EN0, 8);
+static MESON_GATE(spicc_a, SYS_CLK_EN0, 9);
+static MESON_GATE(msr, SYS_CLK_EN0, 10);
+static MESON_GATE(audio, SYS_CLK_EN0, 11);
+static MESON_GATE(jtag_ctrl, SYS_CLK_EN0, 12);
+static MESON_GATE(saradc_en, SYS_CLK_EN0, 13);
+static MESON_GATE(pwm_ef, SYS_CLK_EN0, 14);
+static MESON_GATE(pwm_cd, SYS_CLK_EN0, 15);
+static MESON_GATE(pwm_ab, SYS_CLK_EN0, 16);
+static MESON_GATE(cec, SYS_CLK_EN0, 17);
+static MESON_GATE(i2c_s, SYS_CLK_EN0, 18);
+static MESON_GATE(ir_ctrl, SYS_CLK_EN0, 19);
+static MESON_GATE(i2c_m_d, SYS_CLK_EN0, 20);
+static MESON_GATE(i2c_m_c, SYS_CLK_EN0, 21);
+static MESON_GATE(i2c_m_b, SYS_CLK_EN0, 22);
+static MESON_GATE(i2c_m_a, SYS_CLK_EN0, 23);
+static MESON_GATE(acodec, SYS_CLK_EN0, 24);
+static MESON_GATE(otp, SYS_CLK_EN0, 25);
+static MESON_GATE(sd_emmc_a, SYS_CLK_EN0, 26);
+static MESON_GATE(usb_phy, SYS_CLK_EN0, 27);
+static MESON_GATE(usb_ctrl, SYS_CLK_EN0, 28);
+static MESON_GATE(sys_dspb, SYS_CLK_EN0, 29);
+static MESON_GATE(sys_dspa, SYS_CLK_EN0, 30);
+static MESON_GATE(dma, SYS_CLK_EN0, 31);
+static MESON_GATE(irq_ctrl, SYS_CLK_EN1, 0);
+static MESON_GATE(nic, SYS_CLK_EN1, 1);
+static MESON_GATE(gic, SYS_CLK_EN1, 2);
+static MESON_GATE(uart_c, SYS_CLK_EN1, 3);
+static MESON_GATE(uart_b, SYS_CLK_EN1, 4);
+static MESON_GATE(uart_a, SYS_CLK_EN1, 5);
+static MESON_GATE(sys_psram, SYS_CLK_EN1, 6);
+static MESON_GATE(rsa, SYS_CLK_EN1, 8);
+static MESON_GATE(coresight, SYS_CLK_EN1, 9);
+static MESON_GATE(am2axi_vad, AXI_CLK_EN, 0);
+static MESON_GATE(audio_vad, AXI_CLK_EN, 1);
+static MESON_GATE(axi_dmc, AXI_CLK_EN, 3);
+static MESON_GATE(axi_psram, AXI_CLK_EN, 4);
+static MESON_GATE(ramb, AXI_CLK_EN, 5);
+static MESON_GATE(rama, AXI_CLK_EN, 6);
+static MESON_GATE(axi_spifc, AXI_CLK_EN, 7);
+static MESON_GATE(axi_nic, AXI_CLK_EN, 8);
+static MESON_GATE(axi_dma, AXI_CLK_EN, 9);
+static MESON_GATE(cpu_ctrl, AXI_CLK_EN, 10);
+static MESON_GATE(rom, AXI_CLK_EN, 11);
+static MESON_GATE(prod_i2c, AXI_CLK_EN, 12);
+
+/* Array of all clocks registered by this provider */
+static struct clk_hw_onecell_data a1_periphs_clks = {
+ .hws = {
+ [CLKID_XTAL_IN] = &xtal_in.hw,
+ [CLKID_FIXPLL_IN] = &fixpll_in.hw,
+ [CLKID_USB_PHY_IN] = &usb_phy_in.hw,
+ [CLKID_USB_CTRL_IN] = &usb_ctrl_in.hw,
+ [CLKID_HIFIPLL_IN] = &hifipll_in.hw,
+ [CLKID_SYSPLL_IN] = &syspll_in.hw,
+ [CLKID_DDS_IN] = &dds_in.hw,
+ [CLKID_SYS] = &sys.hw,
+ [CLKID_CLKTREE] = &clktree.hw,
+ [CLKID_RESET_CTRL] = &reset_ctrl.hw,
+ [CLKID_ANALOG_CTRL] = &analog_ctrl.hw,
+ [CLKID_PWR_CTRL] = &pwr_ctrl.hw,
+ [CLKID_PAD_CTRL] = &pad_ctrl.hw,
+ [CLKID_SYS_CTRL] = &sys_ctrl.hw,
+ [CLKID_TEMP_SENSOR] = &temp_sensor.hw,
+ [CLKID_AM2AXI_DIV] = &am2axi_dev.hw,
+ [CLKID_SPICC_B] = &spicc_b.hw,
+ [CLKID_SPICC_A] = &spicc_a.hw,
+ [CLKID_MSR] = &msr.hw,
+ [CLKID_AUDIO] = &audio.hw,
+ [CLKID_JTAG_CTRL] = &jtag_ctrl.hw,
+ [CLKID_SARADC_EN] = &saradc_en.hw,
+ [CLKID_PWM_EF] = &pwm_ef.hw,
+ [CLKID_PWM_CD] = &pwm_cd.hw,
+ [CLKID_PWM_AB] = &pwm_ab.hw,
+ [CLKID_CEC] = &cec.hw,
+ [CLKID_I2C_S] = &i2c_s.hw,
+ [CLKID_IR_CTRL] = &ir_ctrl.hw,
+ [CLKID_I2C_M_D] = &i2c_m_d.hw,
+ [CLKID_I2C_M_C] = &i2c_m_c.hw,
+ [CLKID_I2C_M_B] = &i2c_m_b.hw,
+ [CLKID_I2C_M_A] = &i2c_m_a.hw,
+ [CLKID_ACODEC] = &acodec.hw,
+ [CLKID_OTP] = &otp.hw,
+ [CLKID_SD_EMMC_A] = &sd_emmc_a.hw,
+ [CLKID_USB_PHY] = &usb_phy.hw,
+ [CLKID_USB_CTRL] = &usb_ctrl.hw,
+ [CLKID_SYS_DSPB] = &sys_dspb.hw,
+ [CLKID_SYS_DSPA] = &sys_dspa.hw,
+ [CLKID_DMA] = &dma.hw,
+ [CLKID_IRQ_CTRL] = &irq_ctrl.hw,
+ [CLKID_NIC] = &nic.hw,
+ [CLKID_GIC] = &gic.hw,
+ [CLKID_UART_C] = &uart_c.hw,
+ [CLKID_UART_B] = &uart_b.hw,
+ [CLKID_UART_A] = &uart_a.hw,
+ [CLKID_SYS_PSRAM] = &sys_psram.hw,
+ [CLKID_RSA] = &rsa.hw,
+ [CLKID_CORESIGHT] = &coresight.hw,
+ [CLKID_AM2AXI_VAD] = &am2axi_vad.hw,
+ [CLKID_AUDIO_VAD] = &audio_vad.hw,
+ [CLKID_AXI_DMC] = &axi_dmc.hw,
+ [CLKID_AXI_PSRAM] = &axi_psram.hw,
+ [CLKID_RAMB] = &ramb.hw,
+ [CLKID_RAMA] = &rama.hw,
+ [CLKID_AXI_SPIFC] = &axi_spifc.hw,
+ [CLKID_AXI_NIC] = &axi_nic.hw,
+ [CLKID_AXI_DMA] = &axi_dma.hw,
+ [CLKID_CPU_CTRL] = &cpu_ctrl.hw,
+ [CLKID_ROM] = &rom.hw,
+ [CLKID_PROC_I2C] = &prod_i2c.hw,
+ [CLKID_DSPA_SEL] = &dspa_sel.hw,
+ [CLKID_DSPB_SEL] = &dspb_sel.hw,
+ [CLKID_DSPA_EN] = &dspa_en.hw,
+ [CLKID_DSPA_EN_NIC] = &dspa_en_nic.hw,
+ [CLKID_DSPB_EN] = &dspb_en.hw,
+ [CLKID_DSPB_EN_NIC] = &dspb_en_nic.hw,
+ [CLKID_RTC] = &rtc.hw,
+ [CLKID_CECA_32K] = &ceca_32k_out.hw,
+ [CLKID_CECB_32K] = &cecb_32k_out.hw,
+ [CLKID_24M] = &clk_24m.hw,
+ [CLKID_12M] = &clk_12m.hw,
+ [CLKID_FCLK_DIV2_DIVN] = &fclk_div2_divn.hw,
+ [CLKID_GEN] = &gen.hw,
+ [CLKID_SARADC_SEL] = &saradc_sel.hw,
+ [CLKID_SARADC] = &saradc.hw,
+ [CLKID_PWM_A] = &pwm_a.hw,
+ [CLKID_PWM_B] = &pwm_b.hw,
+ [CLKID_PWM_C] = &pwm_c.hw,
+ [CLKID_PWM_D] = &pwm_d.hw,
+ [CLKID_PWM_E] = &pwm_e.hw,
+ [CLKID_PWM_F] = &pwm_f.hw,
+ [CLKID_SPICC] = &spicc.hw,
+ [CLKID_TS] = &ts.hw,
+ [CLKID_SPIFC] = &spifc.hw,
+ [CLKID_USB_BUS] = &usb_bus.hw,
+ [CLKID_SD_EMMC] = &sd_emmc.hw,
+ [CLKID_PSRAM] = &psram.hw,
+ [CLKID_DMC] = &dmc.hw,
+ [CLKID_SYS_A_SEL] = &sys_a_sel.hw,
+ [CLKID_SYS_A_DIV] = &sys_a_div.hw,
+ [CLKID_SYS_A] = &sys_a.hw,
+ [CLKID_SYS_B_SEL] = &sys_b_sel.hw,
+ [CLKID_SYS_B_DIV] = &sys_b_div.hw,
+ [CLKID_SYS_B] = &sys_b.hw,
+ [CLKID_DSPA_A_SEL] = &dspa_a_sel.hw,
+ [CLKID_DSPA_A_DIV] = &dspa_a_div.hw,
+ [CLKID_DSPA_A] = &dspa_a.hw,
+ [CLKID_DSPA_B_SEL] = &dspa_b_sel.hw,
+ [CLKID_DSPA_B_DIV] = &dspa_b_div.hw,
+ [CLKID_DSPA_B] = &dspa_b.hw,
+ [CLKID_DSPB_A_SEL] = &dspb_a_sel.hw,
+ [CLKID_DSPB_A_DIV] = &dspb_a_div.hw,
+ [CLKID_DSPB_A] = &dspb_a.hw,
+ [CLKID_DSPB_B_SEL] = &dspb_b_sel.hw,
+ [CLKID_DSPB_B_DIV] = &dspb_b_div.hw,
+ [CLKID_DSPB_B] = &dspb_b.hw,
+ [CLKID_RTC_32K_IN] = &rtc_32k_in.hw,
+ [CLKID_RTC_32K_DIV] = &rtc_32k_div.hw,
+ [CLKID_RTC_32K_XTAL] = &rtc_32k_xtal.hw,
+ [CLKID_RTC_32K_SEL] = &rtc_32k_sel.hw,
+ [CLKID_CECB_32K_IN] = &cecb_32k_in.hw,
+ [CLKID_CECB_32K_DIV] = &cecb_32k_div.hw,
+ [CLKID_CECB_32K_SEL_PRE] = &cecb_32k_sel_pre.hw,
+ [CLKID_CECB_32K_SEL] = &cecb_32k_sel.hw,
+ [CLKID_CECA_32K_IN] = &ceca_32k_in.hw,
+ [CLKID_CECA_32K_DIV] = &ceca_32k_div.hw,
+ [CLKID_CECA_32K_SEL_PRE] = &ceca_32k_sel_pre.hw,
+ [CLKID_CECA_32K_SEL] = &ceca_32k_sel.hw,
+ [CLKID_DIV2_PRE] = &fclk_div2_divn_pre.hw,
+ [CLKID_24M_DIV2] = &clk_24m_div2.hw,
+ [CLKID_GEN_SEL] = &gen_sel.hw,
+ [CLKID_GEN_DIV] = &gen_div.hw,
+ [CLKID_SARADC_DIV] = &saradc_div.hw,
+ [CLKID_PWM_A_SEL] = &pwm_a_sel.hw,
+ [CLKID_PWM_A_DIV] = &pwm_a_div.hw,
+ [CLKID_PWM_B_SEL] = &pwm_b_sel.hw,
+ [CLKID_PWM_B_DIV] = &pwm_b_div.hw,
+ [CLKID_PWM_C_SEL] = &pwm_c_sel.hw,
+ [CLKID_PWM_C_DIV] = &pwm_c_div.hw,
+ [CLKID_PWM_D_SEL] = &pwm_d_sel.hw,
+ [CLKID_PWM_D_DIV] = &pwm_d_div.hw,
+ [CLKID_PWM_E_SEL] = &pwm_e_sel.hw,
+ [CLKID_PWM_E_DIV] = &pwm_e_div.hw,
+ [CLKID_PWM_F_SEL] = &pwm_f_sel.hw,
+ [CLKID_PWM_F_DIV] = &pwm_f_div.hw,
+ [CLKID_SPICC_SEL] = &spicc_sel.hw,
+ [CLKID_SPICC_DIV] = &spicc_div.hw,
+ [CLKID_SPICC_SEL2] = &spicc_sel2.hw,
+ [CLKID_TS_DIV] = &ts_div.hw,
+ [CLKID_SPIFC_SEL] = &spifc_sel.hw,
+ [CLKID_SPIFC_DIV] = &spifc_div.hw,
+ [CLKID_SPIFC_SEL2] = &spifc_sel2.hw,
+ [CLKID_USB_BUS_SEL] = &usb_bus_sel.hw,
+ [CLKID_USB_BUS_DIV] = &usb_bus_div.hw,
+ [CLKID_SD_EMMC_SEL] = &sd_emmc_sel.hw,
+ [CLKID_SD_EMMC_DIV] = &sd_emmc_div.hw,
+ [CLKID_SD_EMMC_SEL2] = &sd_emmc_sel2.hw,
+ [CLKID_PSRAM_SEL] = &psram_sel.hw,
+ [CLKID_PSRAM_DIV] = &psram_div.hw,
+ [CLKID_PSRAM_SEL2] = &psram_sel2.hw,
+ [CLKID_DMC_SEL] = &dmc_sel.hw,
+ [CLKID_DMC_DIV] = &dmc_div.hw,
+ [CLKID_DMC_SEL2] = &dmc_sel2.hw,
+ [NR_CLKS] = NULL,
+ },
+ .num = NR_CLKS,
+};
+
+/* Convenience table to populate regmap in .probe */
+static struct clk_regmap *const a1_periphs_regmaps[] = {
+ &xtal_in,
+ &fixpll_in,
+ &usb_phy_in,
+ &usb_ctrl_in,
+ &hifipll_in,
+ &syspll_in,
+ &dds_in,
+ &sys,
+ &clktree,
+ &reset_ctrl,
+ &analog_ctrl,
+ &pwr_ctrl,
+ &pad_ctrl,
+ &sys_ctrl,
+ &temp_sensor,
+ &am2axi_dev,
+ &spicc_b,
+ &spicc_a,
+ &msr,
+ &audio,
+ &jtag_ctrl,
+ &saradc_en,
+ &pwm_ef,
+ &pwm_cd,
+ &pwm_ab,
+ &cec,
+ &i2c_s,
+ &ir_ctrl,
+ &i2c_m_d,
+ &i2c_m_c,
+ &i2c_m_b,
+ &i2c_m_a,
+ &acodec,
+ &otp,
+ &sd_emmc_a,
+ &usb_phy,
+ &usb_ctrl,
+ &sys_dspb,
+ &sys_dspa,
+ &dma,
+ &irq_ctrl,
+ &nic,
+ &gic,
+ &uart_c,
+ &uart_b,
+ &uart_a,
+ &sys_psram,
+ &rsa,
+ &coresight,
+ &am2axi_vad,
+ &audio_vad,
+ &axi_dmc,
+ &axi_psram,
+ &ramb,
+ &rama,
+ &axi_spifc,
+ &axi_nic,
+ &axi_dma,
+ &cpu_ctrl,
+ &rom,
+ &prod_i2c,
+ &dspa_sel,
+ &dspb_sel,
+ &dspa_en,
+ &dspa_en_nic,
+ &dspb_en,
+ &dspb_en_nic,
+ &rtc,
+ &ceca_32k_out,
+ &cecb_32k_out,
+ &clk_24m,
+ &clk_12m,
+ &fclk_div2_divn,
+ &gen,
+ &saradc_sel,
+ &saradc,
+ &pwm_a,
+ &pwm_b,
+ &pwm_c,
+ &pwm_d,
+ &pwm_e,
+ &pwm_f,
+ &spicc,
+ &ts,
+ &spifc,
+ &usb_bus,
+ &sd_emmc,
+ &psram,
+ &dmc,
+ &sys_a_sel,
+ &sys_a_div,
+ &sys_a,
+ &sys_b_sel,
+ &sys_b_div,
+ &sys_b,
+ &dspa_a_sel,
+ &dspa_a_div,
+ &dspa_a,
+ &dspa_b_sel,
+ &dspa_b_div,
+ &dspa_b,
+ &dspb_a_sel,
+ &dspb_a_div,
+ &dspb_a,
+ &dspb_b_sel,
+ &dspb_b_div,
+ &dspb_b,
+ &rtc_32k_in,
+ &rtc_32k_div,
+ &rtc_32k_xtal,
+ &rtc_32k_sel,
+ &cecb_32k_in,
+ &cecb_32k_div,
+ &cecb_32k_sel_pre,
+ &cecb_32k_sel,
+ &ceca_32k_in,
+ &ceca_32k_div,
+ &ceca_32k_sel_pre,
+ &ceca_32k_sel,
+ &fclk_div2_divn_pre,
+ &gen_sel,
+ &gen_div,
+ &saradc_div,
+ &pwm_a_sel,
+ &pwm_a_div,
+ &pwm_b_sel,
+ &pwm_b_div,
+ &pwm_c_sel,
+ &pwm_c_div,
+ &pwm_d_sel,
+ &pwm_d_div,
+ &pwm_e_sel,
+ &pwm_e_div,
+ &pwm_f_sel,
+ &pwm_f_div,
+ &spicc_sel,
+ &spicc_div,
+ &spicc_sel2,
+ &ts_div,
+ &spifc_sel,
+ &spifc_div,
+ &spifc_sel2,
+ &usb_bus_sel,
+ &usb_bus_div,
+ &sd_emmc_sel,
+ &sd_emmc_div,
+ &sd_emmc_sel2,
+ &psram_sel,
+ &psram_div,
+ &psram_sel2,
+ &dmc_sel,
+ &dmc_div,
+ &dmc_sel2,
+};
+
+static struct regmap_config a1_periphs_regmap_cfg = {
+ .reg_bits = 32,
+ .val_bits = 32,
+ .reg_stride = 4,
+};
+
+static int meson_a1_periphs_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ void __iomem *base;
+ struct regmap *map;
+ int clkid, i, err;
+
+ base = devm_platform_ioremap_resource(pdev, 0);
+ if (IS_ERR(base))
+ return dev_err_probe(dev, PTR_ERR(base),
+ "can't ioremap resource\n");
+
+ map = devm_regmap_init_mmio(dev, base, &a1_periphs_regmap_cfg);
+ if (IS_ERR(map))
+ return dev_err_probe(dev, PTR_ERR(map),
+ "can't init regmap mmio region\n");
+
+ /* Populate regmap for the regmap backed clocks */
+ for (i = 0; i < ARRAY_SIZE(a1_periphs_regmaps); i++)
+ a1_periphs_regmaps[i]->map = map;
+
+ for (clkid = 0; clkid < a1_periphs_clks.num; clkid++) {
+ err = devm_clk_hw_register(dev, a1_periphs_clks.hws[clkid]);
+ if (err)
+ return dev_err_probe(dev, err,
+ "clock[%d] registration failed\n",
+ clkid);
+ }
+
+ return devm_of_clk_add_hw_provider(dev, of_clk_hw_onecell_get,
+ &a1_periphs_clks);
+}
+
+static const struct of_device_id a1_periphs_clkc_match_table[] = {
+ { .compatible = "amlogic,a1-clkc", },
+ {}
+};
+MODULE_DEVICE_TABLE(of, a1_periphs_clkc_match_table);
+
+static struct platform_driver a1_periphs_clkc_driver = {
+ .probe = meson_a1_periphs_probe,
+ .driver = {
+ .name = "a1-clkc",
+ .of_match_table = a1_periphs_clkc_match_table,
+ },
+};
+
+module_platform_driver(a1_periphs_clkc_driver);
+MODULE_AUTHOR("Jian Hu <[email protected]>");
+MODULE_AUTHOR("Dmitry Rokosov <[email protected]>");
+MODULE_LICENSE("GPL");
diff --git a/drivers/clk/meson/a1.h b/drivers/clk/meson/a1.h
new file mode 100644
index 000000000000..359b46b73035
--- /dev/null
+++ b/drivers/clk/meson/a1.h
@@ -0,0 +1,114 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+/*
+ * Amlogic A1 Peripheral Clock Controller internals
+ *
+ * Copyright (c) 2019 Amlogic, Inc. All rights reserved.
+ * Author: Jian Hu <[email protected]>
+ *
+ * Copyright (c) 2023, SberDevices. All Rights Reserved.
+ * Author: Dmitry Rokosov <[email protected]>
+ */
+
+#ifndef __A1_H
+#define __A1_H
+
+/* peripheral clock controller register offset */
+#define SYS_OSCIN_CTRL 0x0
+#define RTC_BY_OSCIN_CTRL0 0x4
+#define RTC_BY_OSCIN_CTRL1 0x8
+#define RTC_CTRL 0xc
+#define SYS_CLK_CTRL0 0x10
+#define SYS_CLK_EN0 0x1c
+#define SYS_CLK_EN1 0x20
+#define AXI_CLK_EN 0x24
+#define DSPA_CLK_EN 0x28
+#define DSPB_CLK_EN 0x2c
+#define DSPA_CLK_CTRL0 0x30
+#define DSPB_CLK_CTRL0 0x34
+#define CLK12_24_CTRL 0x38
+#define GEN_CLK_CTRL 0x3c
+#define SAR_ADC_CLK_CTRL 0xc0
+#define PWM_CLK_AB_CTRL 0xc4
+#define PWM_CLK_CD_CTRL 0xc8
+#define PWM_CLK_EF_CTRL 0xcc
+#define SPICC_CLK_CTRL 0xd0
+#define TS_CLK_CTRL 0xd4
+#define SPIFC_CLK_CTRL 0xd8
+#define USB_BUSCLK_CTRL 0xdc
+#define SD_EMMC_CLK_CTRL 0xe0
+#define CECA_CLK_CTRL0 0xe4
+#define CECA_CLK_CTRL1 0xe8
+#define CECB_CLK_CTRL0 0xec
+#define CECB_CLK_CTRL1 0xf0
+#define PSRAM_CLK_CTRL 0xf4
+#define DMC_CLK_CTRL 0xf8
+
+/* include the CLKIDs that have been made part of the DT binding */
+#include <dt-bindings/clock/amlogic,a1-clkc.h>
+
+/*
+ * CLKID index values for internal clocks
+ *
+ * These indices are entirely contrived and do not map onto the hardware.
+ * It has now been decided to expose everything by default in the DT header:
+ * include/dt-bindings/clock/a1-clkc.h. Only the clocks ids we don't want
+ * to expose, such as the internal muxes and dividers of composite clocks,
+ * will remain defined here.
+ */
+#define CLKID_XTAL_IN 0
+#define CLKID_DSPA_SEL 61
+#define CLKID_DSPB_SEL 62
+#define CLKID_SARADC_SEL 74
+#define CLKID_SYS_A_SEL 89
+#define CLKID_SYS_A_DIV 90
+#define CLKID_SYS_A 91
+#define CLKID_SYS_B_SEL 92
+#define CLKID_SYS_B_DIV 93
+#define CLKID_SYS_B 94
+#define CLKID_DSPA_A_DIV 96
+#define CLKID_DSPA_A 97
+#define CLKID_DSPA_B_DIV 99
+#define CLKID_DSPA_B 100
+#define CLKID_DSPB_A_DIV 102
+#define CLKID_DSPB_A 103
+#define CLKID_DSPB_B_DIV 105
+#define CLKID_DSPB_B 106
+#define CLKID_RTC_32K_IN 107
+#define CLKID_RTC_32K_DIV 108
+#define CLKID_RTC_32K_XTAL 109
+#define CLKID_RTC_32K_SEL 110
+#define CLKID_CECB_32K_IN 111
+#define CLKID_CECB_32K_DIV 112
+#define CLKID_CECA_32K_IN 115
+#define CLKID_CECA_32K_DIV 116
+#define CLKID_DIV2_PRE 119
+#define CLKID_24M_DIV2 120
+#define CLKID_GEN_DIV 122
+#define CLKID_SARADC_DIV 123
+#define CLKID_PWM_A_DIV 125
+#define CLKID_PWM_B_DIV 127
+#define CLKID_PWM_C_DIV 129
+#define CLKID_PWM_D_DIV 131
+#define CLKID_PWM_E_DIV 133
+#define CLKID_PWM_F_DIV 135
+#define CLKID_SPICC_SEL 136
+#define CLKID_SPICC_DIV 137
+#define CLKID_SPICC_SEL2 138
+#define CLKID_TS_DIV 139
+#define CLKID_SPIFC_SEL 140
+#define CLKID_SPIFC_DIV 141
+#define CLKID_SPIFC_SEL2 142
+#define CLKID_USB_BUS_SEL 143
+#define CLKID_USB_BUS_DIV 144
+#define CLKID_SD_EMMC_SEL 145
+#define CLKID_SD_EMMC_DIV 146
+#define CLKID_SD_EMMC_SEL2 147
+#define CLKID_PSRAM_SEL 148
+#define CLKID_PSRAM_DIV 149
+#define CLKID_PSRAM_SEL2 150
+#define CLKID_DMC_SEL 151
+#define CLKID_DMC_DIV 152
+#define CLKID_DMC_SEL2 153
+#define NR_CLKS 154
+
+#endif /* __A1_H */
--
2.36.0

2023-05-01 19:01:22

by Martin Blumenstingl

[permalink] [raw]
Subject: Re: [PATCH v14 5/6] dt-bindings: clock: meson: add A1 Peripherals clock controller bindings

Hi Dmitry,

On Wed, Apr 26, 2023 at 11:58 AM Dmitry Rokosov
<[email protected]> wrote:
>
> Add the documentation for Amlogic A1 Peripherals clock driver,
> and A1 Peripherals clock controller bindings.
Maybe a native English speaker can comment on whether it's
"peripheral" or "peripherals".

[...]
> Signed-off-by: Jian Hu <[email protected]>
> Signed-off-by: Dmitry Rokosov <[email protected]>
> Reviewed-by: Rob Herring <[email protected]>
> ---
> .../bindings/clock/amlogic,a1-clkc.yaml | 73 +++++++++++
> .../bindings/clock/amlogic,a1-pll-clkc.yaml | 5 +-
> include/dt-bindings/clock/amlogic,a1-clkc.h | 114 ++++++++++++++++++
I have seen that Yu Tu named the S4 peripheral clock controller
binding and driver "s4-peripherals-clkc" [0].
Does it make sense to apply the same naming here as well?


Best regards,
Martin


[0] https://lore.kernel.org/linux-amlogic/[email protected]/

2023-05-01 19:18:41

by Martin Blumenstingl

[permalink] [raw]
Subject: Re: [PATCH v14 6/6] clk: meson: a1: add Amlogic A1 Peripherals clock controller driver

Hi Dmitry,

overall this looks pretty good.

+Cc Heiner

On Wed, Apr 26, 2023 at 11:58 AM Dmitry Rokosov
<[email protected]> wrote:
[...]
> +static struct clk_regmap pwm_a_sel = {
> + .data = &(struct clk_regmap_mux_data){
> + .offset = PWM_CLK_AB_CTRL,
> + .mask = 0x1,
> + .shift = 9,
> + },
> + .hw.init = &(struct clk_init_data){
> + .name = "pwm_a_sel",
> + .ops = &clk_regmap_mux_ops,
> + .parent_data = pwm_abcd_parents,
> + .num_parents = ARRAY_SIZE(pwm_abcd_parents),
> + /* For more information, please refer to rtc clock */
> + .flags = CLK_SET_RATE_NO_REPARENT,
Heiner is working on a series that adds common clock support to the
PWM driver [0].
I think his plans for a next step are adding support for SoCs where
the PWM clocks are part of the peripheral clock controller (instead of
being part of the PWM controller registers).

Have you considered removing CLK_SET_RATE_PARENT from the &rtc clock
so downstream clocks won't change the rtc clock rate by accident?
Then we could drop the CLK_SET_RATE_NO_REPARENT flag from the PWM
clocks to allow them to pick the best available parent (whether that's
the rtc clock, xtal or sys_pll).
That said, it would require managing the CLKID_RTC_32K_SEL clock (or
it's parents) using assigned-clocks instead of doing so with the PWM
(and other) clocks. Whether this would cause problems: I'm not sure,
so I'm hoping that you can share some insights.


Best regards,
Martin


[0] https://lore.kernel.org/linux-amlogic/[email protected]/

2023-05-02 02:19:32

by Christian Hewitt

[permalink] [raw]
Subject: Re: [PATCH v14 5/6] dt-bindings: clock: meson: add A1 Peripherals clock controller bindings

> On 1 May 2023, at 7:51 pm, Martin Blumenstingl <[email protected]> wrote:
>
> Hi Dmitry,
>
> On Wed, Apr 26, 2023 at 11:58 AM Dmitry Rokosov
> <[email protected]> wrote:
>>
>> Add the documentation for Amlogic A1 Peripherals clock driver,
>> and A1 Peripherals clock controller bindings.
> Maybe a native English speaker can comment on whether it's
> "peripheral" or "peripherals".

I’m not a grammar specialist, but I would write:

“Add documentation and bindings for the Amlogic A1 SoC peripherals
clock driver”

Peripherals is the correct plural but reads better when you add
context on the type of peripherals.

Christian

> [...]
>> Signed-off-by: Jian Hu <[email protected]>
>> Signed-off-by: Dmitry Rokosov <[email protected]>
>> Reviewed-by: Rob Herring <[email protected]>
>> ---
>> .../bindings/clock/amlogic,a1-clkc.yaml | 73 +++++++++++
>> .../bindings/clock/amlogic,a1-pll-clkc.yaml | 5 +-
>> include/dt-bindings/clock/amlogic,a1-clkc.h | 114 ++++++++++++++++++
> I have seen that Yu Tu named the S4 peripheral clock controller
> binding and driver "s4-peripherals-clkc" [0].
> Does it make sense to apply the same naming here as well?
>
>
> Best regards,
> Martin
>
>
> [0] https://lore.kernel.org/linux-amlogic/[email protected]/
>
> _______________________________________________
> linux-amlogic mailing list
> [email protected]
> http://lists.infradead.org/mailman/listinfo/linux-amlogic

2023-05-02 07:51:01

by Krzysztof Kozlowski

[permalink] [raw]
Subject: Re: [PATCH v14 5/6] dt-bindings: clock: meson: add A1 Peripherals clock controller bindings

On 02/05/2023 03:38, Christian Hewitt wrote:
>> On 1 May 2023, at 7:51 pm, Martin Blumenstingl <[email protected]> wrote:
>>
>> Hi Dmitry,
>>
>> On Wed, Apr 26, 2023 at 11:58 AM Dmitry Rokosov
>> <[email protected]> wrote:
>>>
>>> Add the documentation for Amlogic A1 Peripherals clock driver,
>>> and A1 Peripherals clock controller bindings.
>> Maybe a native English speaker can comment on whether it's
>> "peripheral" or "peripherals".
>
> I’m not a grammar specialist, but I would write:
>
> “Add documentation and bindings for the Amlogic A1 SoC peripherals
> clock driver”
>
> Peripherals is the correct plural but reads better when you add
> context on the type of peripherals.

Drop the "driver" references - from the binding itself and from commit
msg. The bindings are for hardware, not for the driver, so: "for the
Amlogic A1 SoC peripherals clock controller.".

Best regards,
Krzysztof

2023-05-11 13:46:45

by Dmitry Rokosov

[permalink] [raw]
Subject: Re: [PATCH v14 5/6] dt-bindings: clock: meson: add A1 Peripherals clock controller bindings

Hello Martin,

Sorry for the delayed response as I was on vacation without laptop.

On Mon, May 01, 2023 at 08:51:30PM +0200, Martin Blumenstingl wrote:
> Hi Dmitry,
>
> On Wed, Apr 26, 2023 at 11:58 AM Dmitry Rokosov
> <[email protected]> wrote:
> >
> > Add the documentation for Amlogic A1 Peripherals clock driver,
> > and A1 Peripherals clock controller bindings.
> Maybe a native English speaker can comment on whether it's
> "peripheral" or "peripherals".
>

Ok

> [...]
> > Signed-off-by: Jian Hu <[email protected]>
> > Signed-off-by: Dmitry Rokosov <[email protected]>
> > Reviewed-by: Rob Herring <[email protected]>
> > ---
> > .../bindings/clock/amlogic,a1-clkc.yaml | 73 +++++++++++
> > .../bindings/clock/amlogic,a1-pll-clkc.yaml | 5 +-
> > include/dt-bindings/clock/amlogic,a1-clkc.h | 114 ++++++++++++++++++
> I have seen that Yu Tu named the S4 peripheral clock controller
> binding and driver "s4-peripherals-clkc" [0].
> Does it make sense to apply the same naming here as well?

Yes, it makes sense. I will prepare a new version with the necessary
renaming.

>
>
> Best regards,
> Martin
>
>
> [0] https://lore.kernel.org/linux-amlogic/[email protected]/

--
Thank you,
Dmitry

2023-05-11 14:10:13

by Dmitry Rokosov

[permalink] [raw]
Subject: Re: [PATCH v14 5/6] dt-bindings: clock: meson: add A1 Peripherals clock controller bindings

Hello Christian,

On Tue, May 02, 2023 at 02:38:20AM +0100, Christian Hewitt wrote:
> > On 1 May 2023, at 7:51 pm, Martin Blumenstingl <[email protected]> wrote:
> >
> > Hi Dmitry,
> >
> > On Wed, Apr 26, 2023 at 11:58 AM Dmitry Rokosov
> > <[email protected]> wrote:
> >>
> >> Add the documentation for Amlogic A1 Peripherals clock driver,
> >> and A1 Peripherals clock controller bindings.
> > Maybe a native English speaker can comment on whether it's
> > "peripheral" or "peripherals".
>
> I’m not a grammar specialist, but I would write:
>
> “Add documentation and bindings for the Amlogic A1 SoC peripherals
> clock driver”
>
> Peripherals is the correct plural but reads better when you add
> context on the type of peripherals.

Thank you for your suggestion! I will make the change in the v15.

[...]

--
Thank you,
Dmitry

2023-05-11 14:17:15

by Dmitry Rokosov

[permalink] [raw]
Subject: Re: [PATCH v14 5/6] dt-bindings: clock: meson: add A1 Peripherals clock controller bindings

Hello Krzysztof,

Thank you for the review!

On Tue, May 02, 2023 at 09:39:12AM +0200, Krzysztof Kozlowski wrote:
> On 02/05/2023 03:38, Christian Hewitt wrote:
> >> On 1 May 2023, at 7:51 pm, Martin Blumenstingl <[email protected]> wrote:
> >>
> >> Hi Dmitry,
> >>
> >> On Wed, Apr 26, 2023 at 11:58 AM Dmitry Rokosov
> >> <[email protected]> wrote:
> >>>
> >>> Add the documentation for Amlogic A1 Peripherals clock driver,
> >>> and A1 Peripherals clock controller bindings.
> >> Maybe a native English speaker can comment on whether it's
> >> "peripheral" or "peripherals".
> >
> > I’m not a grammar specialist, but I would write:
> >
> > “Add documentation and bindings for the Amlogic A1 SoC peripherals
> > clock driver”
> >
> > Peripherals is the correct plural but reads better when you add
> > context on the type of peripherals.
>
> Drop the "driver" references - from the binding itself and from commit
> msg. The bindings are for hardware, not for the driver, so: "for the
> Amlogic A1 SoC peripherals clock controller.".

Okay, thank you for the suggestion! I will remove it in the next
version.

--
Thank you,
Dmitry

2023-05-12 14:30:14

by Dmitry Rokosov

[permalink] [raw]
Subject: Re: [PATCH v14 6/6] clk: meson: a1: add Amlogic A1 Peripherals clock controller driver

Hello Martin,

On Mon, May 01, 2023 at 09:06:24PM +0200, Martin Blumenstingl wrote:
> Hi Dmitry,
>
> overall this looks pretty good.

Thank you! Please find my thoughts about RTC clock below.

>
> +Cc Heiner
>
> On Wed, Apr 26, 2023 at 11:58 AM Dmitry Rokosov
> <[email protected]> wrote:
> [...]
> > +static struct clk_regmap pwm_a_sel = {
> > + .data = &(struct clk_regmap_mux_data){
> > + .offset = PWM_CLK_AB_CTRL,
> > + .mask = 0x1,
> > + .shift = 9,
> > + },
> > + .hw.init = &(struct clk_init_data){
> > + .name = "pwm_a_sel",
> > + .ops = &clk_regmap_mux_ops,
> > + .parent_data = pwm_abcd_parents,
> > + .num_parents = ARRAY_SIZE(pwm_abcd_parents),
> > + /* For more information, please refer to rtc clock */
> > + .flags = CLK_SET_RATE_NO_REPARENT,
> Heiner is working on a series that adds common clock support to the
> PWM driver [0].
> I think his plans for a next step are adding support for SoCs where
> the PWM clocks are part of the peripheral clock controller (instead of
> being part of the PWM controller registers).
>

Yes, I'm keeping up with this review and staying informed. It's worth
noting that the peripheral clock driver already includes PWM clocks,
with an important remark about reparenting being switched off. It's
described below.

> Have you considered removing CLK_SET_RATE_PARENT from the &rtc clock
> so downstream clocks won't change the rtc clock rate by accident?
> Then we could drop the CLK_SET_RATE_NO_REPARENT flag from the PWM
> clocks to allow them to pick the best available parent (whether that's
> the rtc clock, xtal or sys_pll).
> That said, it would require managing the CLKID_RTC_32K_SEL clock (or
> it's parents) using assigned-clocks instead of doing so with the PWM
> (and other) clocks. Whether this would cause problems: I'm not sure,
> so I'm hoping that you can share some insights.
>
>

Allow me to share my thoughts on this matter. From my understanding,
Amlogic provides an RTC clock that is both accurate and power-effective
in achieving a 32KHz signal from an internal xtal of 24MHz. However,
this requires a complex RTC divider with four parameters (m1, m2, n1,
n2), as it cannot be accomplished with a single divider. Our team has
measured the RTC clock using an oscilloscope on the GEN CLK pin and
found that it provides a stable 32KHz signal with acceptable jitter. On
the other hand, other approaches, such as the PWM way, yield less stable
and less accurate 32KHz signals with greater jitter.

Additionally, the CCF determines the best ancestor based on how close
its rate is to the given one, based on arithmetic calculations. However,
we have independent knowledge that a certain clock would be better, with
less jitter and fewer intermediaries, which will likely improve energy
efficiency. Sadly, the CCF cannot take this into account.

Given the advantages of the RTC clock, we wish to be able to control the
RTC as a parent for specific leaf clocks. This is achievable with the
'assigned-clocks' feature of CCF OF, but it poses a significant
architectural problem. The 'assigned-clocks' node does not lock/pin the
parent, and a simple clk_set_rate() call can change the parent during
rate propagation. In my opinion, an ideal solution to this problem would
be an additional patch to the CCF core that provides this locking
capability. As a board DTS developer, I know which clock I want to use
as the parent and have a strong reason for doing so, and I do not wish
to open up my parent muxing to other drivers. But until the behavior of
'assigned-clocks' is not available, we will simply label all RTC
children with the CLK_SET_RATE_NO_REPARENT flag.

> Best regards,
> Martin
>
>
> [0] https://lore.kernel.org/linux-amlogic/[email protected]/

--
Thank you,
Dmitry

2023-05-16 21:34:14

by Martin Blumenstingl

[permalink] [raw]
Subject: Re: [PATCH v14 6/6] clk: meson: a1: add Amlogic A1 Peripherals clock controller driver

Hello Dmitry,

On Fri, May 12, 2023 at 4:06 PM Dmitry Rokosov <[email protected]> wrote:
[...]
> > > +static struct clk_regmap pwm_a_sel = {
> > > + .data = &(struct clk_regmap_mux_data){
> > > + .offset = PWM_CLK_AB_CTRL,
> > > + .mask = 0x1,
> > > + .shift = 9,
> > > + },
> > > + .hw.init = &(struct clk_init_data){
> > > + .name = "pwm_a_sel",
> > > + .ops = &clk_regmap_mux_ops,
> > > + .parent_data = pwm_abcd_parents,
> > > + .num_parents = ARRAY_SIZE(pwm_abcd_parents),
> > > + /* For more information, please refer to rtc clock */
> > > + .flags = CLK_SET_RATE_NO_REPARENT,
> > Heiner is working on a series that adds common clock support to the
> > PWM driver [0].
> > I think his plans for a next step are adding support for SoCs where
> > the PWM clocks are part of the peripheral clock controller (instead of
> > being part of the PWM controller registers).
> >
>
> Yes, I'm keeping up with this review and staying informed. It's worth
> noting that the peripheral clock driver already includes PWM clocks,
> with an important remark about reparenting being switched off. It's
> described below.
Indeed, this is why this question came to my mind

> > Have you considered removing CLK_SET_RATE_PARENT from the &rtc clock
> > so downstream clocks won't change the rtc clock rate by accident?
> > Then we could drop the CLK_SET_RATE_NO_REPARENT flag from the PWM
> > clocks to allow them to pick the best available parent (whether that's
> > the rtc clock, xtal or sys_pll).
> > That said, it would require managing the CLKID_RTC_32K_SEL clock (or
> > it's parents) using assigned-clocks instead of doing so with the PWM
> > (and other) clocks. Whether this would cause problems: I'm not sure,
> > so I'm hoping that you can share some insights.
> >
> >
>
> Allow me to share my thoughts on this matter. From my understanding,
> Amlogic provides an RTC clock that is both accurate and power-effective
> in achieving a 32KHz signal from an internal xtal of 24MHz. However,
> this requires a complex RTC divider with four parameters (m1, m2, n1,
> n2), as it cannot be accomplished with a single divider. Our team has
> measured the RTC clock using an oscilloscope on the GEN CLK pin and
> found that it provides a stable 32KHz signal with acceptable jitter. On
> the other hand, other approaches, such as the PWM way, yield less stable
> and less accurate 32KHz signals with greater jitter.
This part is clear to me (we may have even chatted on IRC how to use
the GEN CLK output previously)

> Additionally, the CCF determines the best ancestor based on how close
> its rate is to the given one, based on arithmetic calculations. However,
> we have independent knowledge that a certain clock would be better, with
> less jitter and fewer intermediaries, which will likely improve energy
> efficiency. Sadly, the CCF cannot take this into account.
I agree that the implementation in CCF is fairly simple. There's ways
to trick it though: IIRC if there are multiple equally suitable clocks
it picks the first one. For me all of this has worked so far which is
what makes me curious in this case (not saying that anything is wrong
with your approach).

Do you have a (real world) example where the RTC clock should be
preferred over another clock?

I'm thinking about the following scenario.
PWM parents:
- XTAL: 24MHz
- sys: not sure - let's say 166.67MHz
- RTC: 32kHz

Then after that there's a divider and a gate.

Let's say the PWM controller needs a 1MHz clock: it can take that from
XTAL or sys. Since XTAL is evenly divisible to 1MHz CCF will pick that
and use the divider.
But let's say the PWM controller needs a 32kHz clock: CCF would
automatically pick the RTC clock.
So is your implementation there to cover let's say 1kHz where
mathematically 24MHz can be divided evenly to 1kHz (and thus should
not result in any jitter) but RTC gives better precision in the real
world (even though it's off by 24Hz)?

> Given the advantages of the RTC clock, we wish to be able to control the
> RTC as a parent for specific leaf clocks. This is achievable with the
> 'assigned-clocks' feature of CCF OF, but it poses a significant
> architectural problem. The 'assigned-clocks' node does not lock/pin the
> parent, and a simple clk_set_rate() call can change the parent during
> rate propagation.
Are you aware of clk_set_rate_exclusive() and clk_rate_exclusive_{get,put}()?
It locks a clock and all of its parents to a certain rate. Other
consumers are unable to change the rate unless the lock is released
again.

> In my opinion, an ideal solution to this problem would
> be an additional patch to the CCF core that provides this locking
> capability.As a board DTS developer, I know which clock I want to use
> as the parent and have a strong reason for doing so, and I do not wish
> to open up my parent muxing to other drivers. But until the behavior of
> 'assigned-clocks' is not available, we will simply label all RTC
> children with the CLK_SET_RATE_NO_REPARENT flag.

PS: while writing this reply I found
drivers/clk/sunxi-ng/ccu-sun6i-rtc.c which implements
clk_ops.recalc_accuracy
I'm not sure I understand this correctly but it seems that CCF is not
using that information when making the decision which parent to use.


Best regards,
Martin

2023-05-17 10:42:59

by Dmitry Rokosov

[permalink] [raw]
Subject: Re: [PATCH v14 6/6] clk: meson: a1: add Amlogic A1 Peripherals clock controller driver

Hello Martin,

Thank you for reply and for sharing your thoughts, appreciate it!
Please find my comments below.

On Tue, May 16, 2023 at 11:10:19PM +0200, Martin Blumenstingl wrote:
> Hello Dmitry,
>
> On Fri, May 12, 2023 at 4:06 PM Dmitry Rokosov <[email protected]> wrote:
> [...]
> > > > +static struct clk_regmap pwm_a_sel = {
> > > > + .data = &(struct clk_regmap_mux_data){
> > > > + .offset = PWM_CLK_AB_CTRL,
> > > > + .mask = 0x1,
> > > > + .shift = 9,
> > > > + },
> > > > + .hw.init = &(struct clk_init_data){
> > > > + .name = "pwm_a_sel",
> > > > + .ops = &clk_regmap_mux_ops,
> > > > + .parent_data = pwm_abcd_parents,
> > > > + .num_parents = ARRAY_SIZE(pwm_abcd_parents),
> > > > + /* For more information, please refer to rtc clock */
> > > > + .flags = CLK_SET_RATE_NO_REPARENT,
> > > Heiner is working on a series that adds common clock support to the
> > > PWM driver [0].
> > > I think his plans for a next step are adding support for SoCs where
> > > the PWM clocks are part of the peripheral clock controller (instead of
> > > being part of the PWM controller registers).
> > >
> >
> > Yes, I'm keeping up with this review and staying informed. It's worth
> > noting that the peripheral clock driver already includes PWM clocks,
> > with an important remark about reparenting being switched off. It's
> > described below.
> Indeed, this is why this question came to my mind
>
> > > Have you considered removing CLK_SET_RATE_PARENT from the &rtc clock
> > > so downstream clocks won't change the rtc clock rate by accident?
> > > Then we could drop the CLK_SET_RATE_NO_REPARENT flag from the PWM
> > > clocks to allow them to pick the best available parent (whether that's
> > > the rtc clock, xtal or sys_pll).
> > > That said, it would require managing the CLKID_RTC_32K_SEL clock (or
> > > it's parents) using assigned-clocks instead of doing so with the PWM
> > > (and other) clocks. Whether this would cause problems: I'm not sure,
> > > so I'm hoping that you can share some insights.
> > >
> > >
> >
> > Allow me to share my thoughts on this matter. From my understanding,
> > Amlogic provides an RTC clock that is both accurate and power-effective
> > in achieving a 32KHz signal from an internal xtal of 24MHz. However,
> > this requires a complex RTC divider with four parameters (m1, m2, n1,
> > n2), as it cannot be accomplished with a single divider. Our team has
> > measured the RTC clock using an oscilloscope on the GEN CLK pin and
> > found that it provides a stable 32KHz signal with acceptable jitter. On
> > the other hand, other approaches, such as the PWM way, yield less stable
> > and less accurate 32KHz signals with greater jitter.
> This part is clear to me (we may have even chatted on IRC how to use
> the GEN CLK output previously)
>
> > Additionally, the CCF determines the best ancestor based on how close
> > its rate is to the given one, based on arithmetic calculations. However,
> > we have independent knowledge that a certain clock would be better, with
> > less jitter and fewer intermediaries, which will likely improve energy
> > efficiency. Sadly, the CCF cannot take this into account.
> I agree that the implementation in CCF is fairly simple. There's ways
> to trick it though: IIRC if there are multiple equally suitable clocks
> it picks the first one. For me all of this has worked so far which is
> what makes me curious in this case (not saying that anything is wrong
> with your approach).
>
> Do you have a (real world) example where the RTC clock should be
> preferred over another clock?
>

Yes, a real-life example is the need for a 32Khz clock for an external
wifi chip. There is one option to provide this clock with high
precision, which is RTC + GENCLK.

> I'm thinking about the following scenario.
> PWM parents:
> - XTAL: 24MHz
> - sys: not sure - let's say 166.67MHz
> - RTC: 32kHz
>
> Then after that there's a divider and a gate.
>
> Let's say the PWM controller needs a 1MHz clock: it can take that from
> XTAL or sys. Since XTAL is evenly divisible to 1MHz CCF will pick that
> and use the divider.
> But let's say the PWM controller needs a 32kHz clock: CCF would
> automatically pick the RTC clock.
> So is your implementation there to cover let's say 1kHz where
> mathematically 24MHz can be divided evenly to 1kHz (and thus should
> not result in any jitter) but RTC gives better precision in the real
> world (even though it's off by 24Hz)?
>

I don't think so. The highest precision that RTC can provide is from a
32KHz rate only. However, I believe that a 1kHz frequency can also be
achieved by using xtal 24MHz with a divider, which can provide high
precision as well.

> > Given the advantages of the RTC clock, we wish to be able to control the
> > RTC as a parent for specific leaf clocks. This is achievable with the
> > 'assigned-clocks' feature of CCF OF, but it poses a significant
> > architectural problem. The 'assigned-clocks' node does not lock/pin the
> > parent, and a simple clk_set_rate() call can change the parent during
> > rate propagation.
> Are you aware of clk_set_rate_exclusive() and clk_rate_exclusive_{get,put}()?
> It locks a clock and all of its parents to a certain rate. Other
> consumers are unable to change the rate unless the lock is released
> again.
>

Agreed, this API works well for protecting the rate changes. However, I
do not think it covers the scenario where we want to change the rate but
still retain the parent (if it has already been assigned from the dts).

> > In my opinion, an ideal solution to this problem would
> > be an additional patch to the CCF core that provides this locking
> > capability.As a board DTS developer, I know which clock I want to use
> > as the parent and have a strong reason for doing so, and I do not wish
> > to open up my parent muxing to other drivers. But until the behavior of
> > 'assigned-clocks' is not available, we will simply label all RTC
> > children with the CLK_SET_RATE_NO_REPARENT flag.
>
> PS: while writing this reply I found
> drivers/clk/sunxi-ng/ccu-sun6i-rtc.c which implements
> clk_ops.recalc_accuracy
> I'm not sure I understand this correctly but it seems that CCF is not
> using that information when making the decision which parent to use.

Hmm, I couldn't find how accuracy is used in the logic. It seems that
only the clk_summary contains information on accuracy.

I actually do not like my approach very much. CCF is not very flexible,
yet at the same time it does not allow for strict rules to be set for
the clock. With my approach, each developer would have to set up
assigned clocks directly from the device tree. However, if we back all
clocks inherited from RTC to the reparenting process, we can lose
assigned clocks for the RTC state.

As I mentioned earlier, I believe the best solution is to change CCF to
provide a new 'assigned-clocks-exclusive' mechanism while protecting the
clock from parent changing. I can provide an RFC patch for this if you
do not mind.

However, I think it is necessary to move forward with that patch series
since many other meson-a1.dtsi changes depend on it. We can merge the
current driver implementation as is, and after the CCF has an exclusive
assigned-clocks mechanism, then I will rework this driver.
Alternatively, I can revert all PARENT flags back and change the logic,
if it becomes necessary in the future on upstream boards.

--
Thank you,
Dmitry

2023-05-18 20:17:03

by Martin Blumenstingl

[permalink] [raw]
Subject: Re: [PATCH v14 6/6] clk: meson: a1: add Amlogic A1 Peripherals clock controller driver

Hi Dmitry,

On Wed, May 17, 2023 at 12:34 PM Dmitry Rokosov
<[email protected]> wrote:
[...]
> > > Additionally, the CCF determines the best ancestor based on how close
> > > its rate is to the given one, based on arithmetic calculations. However,
> > > we have independent knowledge that a certain clock would be better, with
> > > less jitter and fewer intermediaries, which will likely improve energy
> > > efficiency. Sadly, the CCF cannot take this into account.
> > I agree that the implementation in CCF is fairly simple. There's ways
> > to trick it though: IIRC if there are multiple equally suitable clocks
> > it picks the first one. For me all of this has worked so far which is
> > what makes me curious in this case (not saying that anything is wrong
> > with your approach).
> >
> > Do you have a (real world) example where the RTC clock should be
> > preferred over another clock?
> >
>
> Yes, a real-life example is the need for a 32Khz clock for an external
> wifi chip. There is one option to provide this clock with high
> precision, which is RTC + GENCLK.
>
> > I'm thinking about the following scenario.
> > PWM parents:
> > - XTAL: 24MHz
> > - sys: not sure - let's say 166.67MHz
> > - RTC: 32kHz
> >
> > Then after that there's a divider and a gate.
> >
> > Let's say the PWM controller needs a 1MHz clock: it can take that from
> > XTAL or sys. Since XTAL is evenly divisible to 1MHz CCF will pick that
> > and use the divider.
> > But let's say the PWM controller needs a 32kHz clock: CCF would
> > automatically pick the RTC clock.
> > So is your implementation there to cover let's say 1kHz where
> > mathematically 24MHz can be divided evenly to 1kHz (and thus should
> > not result in any jitter) but RTC gives better precision in the real
> > world (even though it's off by 24Hz)?
> >
>
> I don't think so. The highest precision that RTC can provide is from a
> 32KHz rate only. However, I believe that a 1kHz frequency can also be
> achieved by using xtal 24MHz with a divider, which can provide high
> precision as well.
Thank you again for the great discussion on IRC today.
Here's my short summary so I don't forget before you'll follow up on this.

In general there's two known cases where the RTC clock needs to be used:
a) When using the GENCLK output of the SoC to output the 32kHz RTC
clock and connect that to an SDIO WiFi chip clock input (this seems
useful in my understanding because the RTC clock provides high
precision)
b) When using the PWM controller to output a 32kHz clock signal. In
this case my understanding is that using the RTC clock as input to the
PWM controller results in the best possible signal

The second case won't be supported with Heiner's patches [0] that use
CCF (common clock framework) in the PWM controller driver.
In this series the parent clock is calculated using:
freq = div64_u64(NSEC_PER_SEC * (u64)0xffff, period);

A 32kHz clock means a PWM period of 30518ns. So with the above
calculation the PWM driver is asking for a clock rate of >=2GHz.
We concluded that letting the common clock framework choose the best
possible parent (meaning: removing CLK_SET_RATE_NO_REPARENT here) can
be a way forward.
But this means that the PWM controller driver must try to find the
best possible parent somehow. The easiest way we came up with
(pseudo-code):
freq = NSEC_PER_SEC / period;
fin_freq = clk_round_rate(channel->clk, freq);
if (fin_freq != freq) {
freq = div64_u64(NSEC_PER_SEC * (u64)0xffff, period);
fin_freq = clk_round_rate(channel->clk, freq);
}

The idea is: for a requested 32kHz signal the PWM period is 30518ns.
The updated logic would find that there's a matching clock input and
use that directly. If not: use the original logic as suggested by
Heiner.


Best regards,
Martin


[0] https://lore.kernel.org/linux-amlogic/[email protected]/

2023-05-19 15:11:30

by Dmitry Rokosov

[permalink] [raw]
Subject: Re: [PATCH v14 6/6] clk: meson: a1: add Amlogic A1 Peripherals clock controller driver

On Thu, May 18, 2023 at 10:04:41PM +0200, Martin Blumenstingl wrote:
> Hi Dmitry,
>
> On Wed, May 17, 2023 at 12:34 PM Dmitry Rokosov
> <[email protected]> wrote:
> [...]
> > > > Additionally, the CCF determines the best ancestor based on how close
> > > > its rate is to the given one, based on arithmetic calculations. However,
> > > > we have independent knowledge that a certain clock would be better, with
> > > > less jitter and fewer intermediaries, which will likely improve energy
> > > > efficiency. Sadly, the CCF cannot take this into account.
> > > I agree that the implementation in CCF is fairly simple. There's ways
> > > to trick it though: IIRC if there are multiple equally suitable clocks
> > > it picks the first one. For me all of this has worked so far which is
> > > what makes me curious in this case (not saying that anything is wrong
> > > with your approach).
> > >
> > > Do you have a (real world) example where the RTC clock should be
> > > preferred over another clock?
> > >
> >
> > Yes, a real-life example is the need for a 32Khz clock for an external
> > wifi chip. There is one option to provide this clock with high
> > precision, which is RTC + GENCLK.
> >
> > > I'm thinking about the following scenario.
> > > PWM parents:
> > > - XTAL: 24MHz
> > > - sys: not sure - let's say 166.67MHz
> > > - RTC: 32kHz
> > >
> > > Then after that there's a divider and a gate.
> > >
> > > Let's say the PWM controller needs a 1MHz clock: it can take that from
> > > XTAL or sys. Since XTAL is evenly divisible to 1MHz CCF will pick that
> > > and use the divider.
> > > But let's say the PWM controller needs a 32kHz clock: CCF would
> > > automatically pick the RTC clock.
> > > So is your implementation there to cover let's say 1kHz where
> > > mathematically 24MHz can be divided evenly to 1kHz (and thus should
> > > not result in any jitter) but RTC gives better precision in the real
> > > world (even though it's off by 24Hz)?
> > >
> >
> > I don't think so. The highest precision that RTC can provide is from a
> > 32KHz rate only. However, I believe that a 1kHz frequency can also be
> > achieved by using xtal 24MHz with a divider, which can provide high
> > precision as well.
> Thank you again for the great discussion on IRC today.
> Here's my short summary so I don't forget before you'll follow up on this.
>
> In general there's two known cases where the RTC clock needs to be used:
> a) When using the GENCLK output of the SoC to output the 32kHz RTC
> clock and connect that to an SDIO WiFi chip clock input (this seems
> useful in my understanding because the RTC clock provides high
> precision)
> b) When using the PWM controller to output a 32kHz clock signal. In
> this case my understanding is that using the RTC clock as input to the
> PWM controller results in the best possible signal
>
> The second case won't be supported with Heiner's patches [0] that use
> CCF (common clock framework) in the PWM controller driver.
> In this series the parent clock is calculated using:
> freq = div64_u64(NSEC_PER_SEC * (u64)0xffff, period);
>
> A 32kHz clock means a PWM period of 30518ns. So with the above
> calculation the PWM driver is asking for a clock rate of >=2GHz.
> We concluded that letting the common clock framework choose the best
> possible parent (meaning: removing CLK_SET_RATE_NO_REPARENT here) can
> be a way forward.
> But this means that the PWM controller driver must try to find the
> best possible parent somehow. The easiest way we came up with
> (pseudo-code):
> freq = NSEC_PER_SEC / period;
> fin_freq = clk_round_rate(channel->clk, freq);
> if (fin_freq != freq) {
> freq = div64_u64(NSEC_PER_SEC * (u64)0xffff, period);
> fin_freq = clk_round_rate(channel->clk, freq);
> }
>
> The idea is: for a requested 32kHz signal the PWM period is 30518ns.
> The updated logic would find that there's a matching clock input and
> use that directly. If not: use the original logic as suggested by
> Heiner.
>
>
> Best regards,
> Martin
>
>
> [0] https://lore.kernel.org/linux-amlogic/[email protected]/

Thank you for the excellent follow-up! I will reply to Heiner's thread
with these comments. Let's continue this discussion further in the
Heiner patch series.

--
Thank you,
Dmitry

2023-05-19 16:21:52

by Heiner Kallweit

[permalink] [raw]
Subject: Re: [PATCH v14 6/6] clk: meson: a1: add Amlogic A1 Peripherals clock controller driver

On 18.05.2023 22:04, Martin Blumenstingl wrote:
> Hi Dmitry,
>
> On Wed, May 17, 2023 at 12:34 PM Dmitry Rokosov
> <[email protected]> wrote:
> [...]
>>>> Additionally, the CCF determines the best ancestor based on how close
>>>> its rate is to the given one, based on arithmetic calculations. However,
>>>> we have independent knowledge that a certain clock would be better, with
>>>> less jitter and fewer intermediaries, which will likely improve energy
>>>> efficiency. Sadly, the CCF cannot take this into account.
>>> I agree that the implementation in CCF is fairly simple. There's ways
>>> to trick it though: IIRC if there are multiple equally suitable clocks
>>> it picks the first one. For me all of this has worked so far which is
>>> what makes me curious in this case (not saying that anything is wrong
>>> with your approach).
>>>
>>> Do you have a (real world) example where the RTC clock should be
>>> preferred over another clock?
>>>
>>
>> Yes, a real-life example is the need for a 32Khz clock for an external
>> wifi chip. There is one option to provide this clock with high
>> precision, which is RTC + GENCLK.
>>
>>> I'm thinking about the following scenario.
>>> PWM parents:
>>> - XTAL: 24MHz
>>> - sys: not sure - let's say 166.67MHz
>>> - RTC: 32kHz
>>>
>>> Then after that there's a divider and a gate.
>>>
>>> Let's say the PWM controller needs a 1MHz clock: it can take that from
>>> XTAL or sys. Since XTAL is evenly divisible to 1MHz CCF will pick that
>>> and use the divider.
>>> But let's say the PWM controller needs a 32kHz clock: CCF would
>>> automatically pick the RTC clock.
>>> So is your implementation there to cover let's say 1kHz where
>>> mathematically 24MHz can be divided evenly to 1kHz (and thus should
>>> not result in any jitter) but RTC gives better precision in the real
>>> world (even though it's off by 24Hz)?
>>>
>>
>> I don't think so. The highest precision that RTC can provide is from a
>> 32KHz rate only. However, I believe that a 1kHz frequency can also be
>> achieved by using xtal 24MHz with a divider, which can provide high
>> precision as well.
> Thank you again for the great discussion on IRC today.
> Here's my short summary so I don't forget before you'll follow up on this.
>
> In general there's two known cases where the RTC clock needs to be used:
> a) When using the GENCLK output of the SoC to output the 32kHz RTC
> clock and connect that to an SDIO WiFi chip clock input (this seems
> useful in my understanding because the RTC clock provides high
> precision)
> b) When using the PWM controller to output a 32kHz clock signal. In
> this case my understanding is that using the RTC clock as input to the
> PWM controller results in the best possible signal
>
> The second case won't be supported with Heiner's patches [0] that use
> CCF (common clock framework) in the PWM controller driver.
> In this series the parent clock is calculated using:
> freq = div64_u64(NSEC_PER_SEC * (u64)0xffff, period);
>
> A 32kHz clock means a PWM period of 30518ns. So with the above

To be precise: 30517,578125ns
What means that the PWM framework can't say "I want 32768Hz",
but just "I want something being very close to 32768Hz".
So what you need is some simple heuristic to interpret the
PWM request -> "PWM requests 30518ns, but supposedly it wants
32768Hz"

NSEC_PER_SEC / 30518 = 32767 (rounded down from 32767,547)
clk_round_rate(channel->clk, 32767) would return 0 (I *think*),
because it tries to find the next lower clock.

The SoC families I'm familiar with have fclkin2 as PWM parent.
That's 1 GHz in my case, what results in a frequency of 32.767,547Hz
for period = 30518n.
What you're saying is that newer generations don't have PWM parents
>24MHz any longer?


> calculation the PWM driver is asking for a clock rate of >=2GHz.
> We concluded that letting the common clock framework choose the best
> possible parent (meaning: removing CLK_SET_RATE_NO_REPARENT here) can
> be a way forward.
> But this means that the PWM controller driver must try to find the
> best possible parent somehow. The easiest way we came up with
> (pseudo-code):
> freq = NSEC_PER_SEC / period;
> fin_freq = clk_round_rate(channel->clk, freq);
> if (fin_freq != freq) {
> freq = div64_u64(NSEC_PER_SEC * (u64)0xffff, period);
> fin_freq = clk_round_rate(channel->clk, freq);
> }
>
> The idea is: for a requested 32kHz signal the PWM period is 30518ns.
> The updated logic would find that there's a matching clock input and
> use that directly. If not: use the original logic as suggested by
> Heiner.
>
>
> Best regards,
> Martin
>
>
> [0] https://lore.kernel.org/linux-amlogic/[email protected]/


2023-05-22 14:15:22

by Dmitry Rokosov

[permalink] [raw]
Subject: Re: [PATCH v14 6/6] clk: meson: a1: add Amlogic A1 Peripherals clock controller driver

Heiner,

On Fri, May 19, 2023 at 06:10:50PM +0200, Heiner Kallweit wrote:
> On 18.05.2023 22:04, Martin Blumenstingl wrote:
> > Hi Dmitry,
> >
> > On Wed, May 17, 2023 at 12:34 PM Dmitry Rokosov
> > <[email protected]> wrote:
> > [...]
> >>>> Additionally, the CCF determines the best ancestor based on how close
> >>>> its rate is to the given one, based on arithmetic calculations. However,
> >>>> we have independent knowledge that a certain clock would be better, with
> >>>> less jitter and fewer intermediaries, which will likely improve energy
> >>>> efficiency. Sadly, the CCF cannot take this into account.
> >>> I agree that the implementation in CCF is fairly simple. There's ways
> >>> to trick it though: IIRC if there are multiple equally suitable clocks
> >>> it picks the first one. For me all of this has worked so far which is
> >>> what makes me curious in this case (not saying that anything is wrong
> >>> with your approach).
> >>>
> >>> Do you have a (real world) example where the RTC clock should be
> >>> preferred over another clock?
> >>>
> >>
> >> Yes, a real-life example is the need for a 32Khz clock for an external
> >> wifi chip. There is one option to provide this clock with high
> >> precision, which is RTC + GENCLK.
> >>
> >>> I'm thinking about the following scenario.
> >>> PWM parents:
> >>> - XTAL: 24MHz
> >>> - sys: not sure - let's say 166.67MHz
> >>> - RTC: 32kHz
> >>>
> >>> Then after that there's a divider and a gate.
> >>>
> >>> Let's say the PWM controller needs a 1MHz clock: it can take that from
> >>> XTAL or sys. Since XTAL is evenly divisible to 1MHz CCF will pick that
> >>> and use the divider.
> >>> But let's say the PWM controller needs a 32kHz clock: CCF would
> >>> automatically pick the RTC clock.
> >>> So is your implementation there to cover let's say 1kHz where
> >>> mathematically 24MHz can be divided evenly to 1kHz (and thus should
> >>> not result in any jitter) but RTC gives better precision in the real
> >>> world (even though it's off by 24Hz)?
> >>>
> >>
> >> I don't think so. The highest precision that RTC can provide is from a
> >> 32KHz rate only. However, I believe that a 1kHz frequency can also be
> >> achieved by using xtal 24MHz with a divider, which can provide high
> >> precision as well.
> > Thank you again for the great discussion on IRC today.
> > Here's my short summary so I don't forget before you'll follow up on this.
> >
> > In general there's two known cases where the RTC clock needs to be used:
> > a) When using the GENCLK output of the SoC to output the 32kHz RTC
> > clock and connect that to an SDIO WiFi chip clock input (this seems
> > useful in my understanding because the RTC clock provides high
> > precision)
> > b) When using the PWM controller to output a 32kHz clock signal. In
> > this case my understanding is that using the RTC clock as input to the
> > PWM controller results in the best possible signal
> >
> > The second case won't be supported with Heiner's patches [0] that use
> > CCF (common clock framework) in the PWM controller driver.
> > In this series the parent clock is calculated using:
> > freq = div64_u64(NSEC_PER_SEC * (u64)0xffff, period);
> >
> > A 32kHz clock means a PWM period of 30518ns. So with the above
>
> To be precise: 30517,578125ns
> What means that the PWM framework can't say "I want 32768Hz",
> but just "I want something being very close to 32768Hz".
> So what you need is some simple heuristic to interpret the
> PWM request -> "PWM requests 30518ns, but supposedly it wants
> 32768Hz"
>
> NSEC_PER_SEC / 30518 = 32767 (rounded down from 32767,547)
> clk_round_rate(channel->clk, 32767) would return 0 (I *think*),
> because it tries to find the next lower clock.
>
> The SoC families I'm familiar with have fclkin2 as PWM parent.
> That's 1 GHz in my case, what results in a frequency of 32.767,547Hz
> for period = 30518n.
> What you're saying is that newer generations don't have PWM parents
> >24MHz any longer?

No, of course not. For example, a fixed PLL (with all fclk_divX
settings) has rates higher than 24MHz. However, we need to consider the
'heavy' background of such PWM.

However, we have a "lightweight" clkin (special rtc32k) with a rate of
32kHz that we could potentially use as an input to produce a 32kHz
output on the PWM lines. I don't see any reason why we should not
support such special cases.

>
>
> > calculation the PWM driver is asking for a clock rate of >=2GHz.
> > We concluded that letting the common clock framework choose the best
> > possible parent (meaning: removing CLK_SET_RATE_NO_REPARENT here) can
> > be a way forward.
> > But this means that the PWM controller driver must try to find the
> > best possible parent somehow. The easiest way we came up with
> > (pseudo-code):
> > freq = NSEC_PER_SEC / period;
> > fin_freq = clk_round_rate(channel->clk, freq);
> > if (fin_freq != freq) {
> > freq = div64_u64(NSEC_PER_SEC * (u64)0xffff, period);
> > fin_freq = clk_round_rate(channel->clk, freq);
> > }
> >
> > The idea is: for a requested 32kHz signal the PWM period is 30518ns.
> > The updated logic would find that there's a matching clock input and
> > use that directly. If not: use the original logic as suggested by
> > Heiner.
> >
> >
> > Best regards,
> > Martin
> >
> >
> > [0] https://lore.kernel.org/linux-amlogic/[email protected]/
>

--
Thank you,
Dmitry

2023-05-22 20:52:14

by Heiner Kallweit

[permalink] [raw]
Subject: Re: [PATCH v14 6/6] clk: meson: a1: add Amlogic A1 Peripherals clock controller driver

On 22.05.2023 15:44, Dmitry Rokosov wrote:
> Heiner,
>
> On Fri, May 19, 2023 at 06:10:50PM +0200, Heiner Kallweit wrote:
>> On 18.05.2023 22:04, Martin Blumenstingl wrote:
>>> Hi Dmitry,
>>>
>>> On Wed, May 17, 2023 at 12:34 PM Dmitry Rokosov
>>> <[email protected]> wrote:
>>> [...]
>>>>>> Additionally, the CCF determines the best ancestor based on how close
>>>>>> its rate is to the given one, based on arithmetic calculations. However,
>>>>>> we have independent knowledge that a certain clock would be better, with
>>>>>> less jitter and fewer intermediaries, which will likely improve energy
>>>>>> efficiency. Sadly, the CCF cannot take this into account.
>>>>> I agree that the implementation in CCF is fairly simple. There's ways
>>>>> to trick it though: IIRC if there are multiple equally suitable clocks
>>>>> it picks the first one. For me all of this has worked so far which is
>>>>> what makes me curious in this case (not saying that anything is wrong
>>>>> with your approach).
>>>>>
>>>>> Do you have a (real world) example where the RTC clock should be
>>>>> preferred over another clock?
>>>>>
>>>>
>>>> Yes, a real-life example is the need for a 32Khz clock for an external
>>>> wifi chip. There is one option to provide this clock with high
>>>> precision, which is RTC + GENCLK.
>>>>
>>>>> I'm thinking about the following scenario.
>>>>> PWM parents:
>>>>> - XTAL: 24MHz
>>>>> - sys: not sure - let's say 166.67MHz
>>>>> - RTC: 32kHz
>>>>>
>>>>> Then after that there's a divider and a gate.
>>>>>
>>>>> Let's say the PWM controller needs a 1MHz clock: it can take that from
>>>>> XTAL or sys. Since XTAL is evenly divisible to 1MHz CCF will pick that
>>>>> and use the divider.
>>>>> But let's say the PWM controller needs a 32kHz clock: CCF would
>>>>> automatically pick the RTC clock.
>>>>> So is your implementation there to cover let's say 1kHz where
>>>>> mathematically 24MHz can be divided evenly to 1kHz (and thus should
>>>>> not result in any jitter) but RTC gives better precision in the real
>>>>> world (even though it's off by 24Hz)?
>>>>>
>>>>
>>>> I don't think so. The highest precision that RTC can provide is from a
>>>> 32KHz rate only. However, I believe that a 1kHz frequency can also be
>>>> achieved by using xtal 24MHz with a divider, which can provide high
>>>> precision as well.
>>> Thank you again for the great discussion on IRC today.
>>> Here's my short summary so I don't forget before you'll follow up on this.
>>>
>>> In general there's two known cases where the RTC clock needs to be used:
>>> a) When using the GENCLK output of the SoC to output the 32kHz RTC
>>> clock and connect that to an SDIO WiFi chip clock input (this seems
>>> useful in my understanding because the RTC clock provides high
>>> precision)
>>> b) When using the PWM controller to output a 32kHz clock signal. In
>>> this case my understanding is that using the RTC clock as input to the
>>> PWM controller results in the best possible signal
>>>
>>> The second case won't be supported with Heiner's patches [0] that use
>>> CCF (common clock framework) in the PWM controller driver.
>>> In this series the parent clock is calculated using:
>>> freq = div64_u64(NSEC_PER_SEC * (u64)0xffff, period);
>>>
>>> A 32kHz clock means a PWM period of 30518ns. So with the above
>>
>> To be precise: 30517,578125ns
>> What means that the PWM framework can't say "I want 32768Hz",
>> but just "I want something being very close to 32768Hz".
>> So what you need is some simple heuristic to interpret the
>> PWM request -> "PWM requests 30518ns, but supposedly it wants
>> 32768Hz"
>>
>> NSEC_PER_SEC / 30518 = 32767 (rounded down from 32767,547)
>> clk_round_rate(channel->clk, 32767) would return 0 (I *think*),
>> because it tries to find the next lower clock.
>>
>> The SoC families I'm familiar with have fclkin2 as PWM parent.
>> That's 1 GHz in my case, what results in a frequency of 32.767,547Hz
>> for period = 30518n.
>> What you're saying is that newer generations don't have PWM parents
>>> 24MHz any longer?
>
> No, of course not. For example, a fixed PLL (with all fclk_divX
> settings) has rates higher than 24MHz. However, we need to consider the
> 'heavy' background of such PWM.
>
> However, we have a "lightweight" clkin (special rtc32k) with a rate of
> 32kHz that we could potentially use as an input to produce a 32kHz
> output on the PWM lines. I don't see any reason why we should not
> support such special cases.
>

Two more things to consider:
1. When wanting a 32kHz (well, 32768Hz) output with a 50% duty cycle,
then we need hi=0 and lo=0 with a 64kHz input clock.
See point 2 for an explanation of why 0 and not 1.
Means we couldn't use the RTC input clock. Did you consider this?
Or do I miss something?
2. Seems the PWM block internally increments hi and lo, except the
constant_en bit is set on newer PWM block versions.
For bigger cnt values the impact is negligible, but for very small
values it's something we have to consider.
This was one additional motivation for me to choose an input
frequency that creates big cnt values.

>>
>>
>>> calculation the PWM driver is asking for a clock rate of >=2GHz.
>>> We concluded that letting the common clock framework choose the best
>>> possible parent (meaning: removing CLK_SET_RATE_NO_REPARENT here) can
>>> be a way forward.
>>> But this means that the PWM controller driver must try to find the
>>> best possible parent somehow. The easiest way we came up with
>>> (pseudo-code):
>>> freq = NSEC_PER_SEC / period;
>>> fin_freq = clk_round_rate(channel->clk, freq);
>>> if (fin_freq != freq) {
>>> freq = div64_u64(NSEC_PER_SEC * (u64)0xffff, period);
>>> fin_freq = clk_round_rate(channel->clk, freq);
>>> }
>>>
>>> The idea is: for a requested 32kHz signal the PWM period is 30518ns.
>>> The updated logic would find that there's a matching clock input and
>>> use that directly. If not: use the original logic as suggested by
>>> Heiner.
>>>
>>>
>>> Best regards,
>>> Martin
>>>
>>>
>>> [0] https://lore.kernel.org/linux-amlogic/[email protected]/
>>
>


2023-05-23 09:52:16

by George Stark

[permalink] [raw]
Subject: Re: [PATCH v14 6/6] clk: meson: a1: add Amlogic A1 Peripherals clock controller driver

On 5/22/23 23:36, Heiner Kallweit wrote:
> On 22.05.2023 15:44, Dmitry Rokosov wrote:
>> Heiner,
>>
>> On Fri, May 19, 2023 at 06:10:50PM +0200, Heiner Kallweit wrote:
>>> On 18.05.2023 22:04, Martin Blumenstingl wrote:
>>>> Hi Dmitry,
>>>>
>>>> On Wed, May 17, 2023 at 12:34 PM Dmitry Rokosov
>>>> <[email protected]> wrote:
>>>> [...]
>>>>>>> Additionally, the CCF determines the best ancestor based on how close
>>>>>>> its rate is to the given one, based on arithmetic calculations. However,
>>>>>>> we have independent knowledge that a certain clock would be better, with
>>>>>>> less jitter and fewer intermediaries, which will likely improve energy
>>>>>>> efficiency. Sadly, the CCF cannot take this into account.
>>>>>> I agree that the implementation in CCF is fairly simple. There's ways
>>>>>> to trick it though: IIRC if there are multiple equally suitable clocks
>>>>>> it picks the first one. For me all of this has worked so far which is
>>>>>> what makes me curious in this case (not saying that anything is wrong
>>>>>> with your approach).
>>>>>>
>>>>>> Do you have a (real world) example where the RTC clock should be
>>>>>> preferred over another clock?
>>>>>>
>>>>> Yes, a real-life example is the need for a 32Khz clock for an external
>>>>> wifi chip. There is one option to provide this clock with high
>>>>> precision, which is RTC + GENCLK.
>>>>>
>>>>>> I'm thinking about the following scenario.
>>>>>> PWM parents:
>>>>>> - XTAL: 24MHz
>>>>>> - sys: not sure - let's say 166.67MHz
>>>>>> - RTC: 32kHz
>>>>>>
>>>>>> Then after that there's a divider and a gate.
>>>>>>
>>>>>> Let's say the PWM controller needs a 1MHz clock: it can take that from
>>>>>> XTAL or sys. Since XTAL is evenly divisible to 1MHz CCF will pick that
>>>>>> and use the divider.
>>>>>> But let's say the PWM controller needs a 32kHz clock: CCF would
>>>>>> automatically pick the RTC clock.
>>>>>> So is your implementation there to cover let's say 1kHz where
>>>>>> mathematically 24MHz can be divided evenly to 1kHz (and thus should
>>>>>> not result in any jitter) but RTC gives better precision in the real
>>>>>> world (even though it's off by 24Hz)?
>>>>>>
>>>>> I don't think so. The highest precision that RTC can provide is from a
>>>>> 32KHz rate only. However, I believe that a 1kHz frequency can also be
>>>>> achieved by using xtal 24MHz with a divider, which can provide high
>>>>> precision as well.
>>>> Thank you again for the great discussion on IRC today.
>>>> Here's my short summary so I don't forget before you'll follow up on this.
>>>>
>>>> In general there's two known cases where the RTC clock needs to be used:
>>>> a) When using the GENCLK output of the SoC to output the 32kHz RTC
>>>> clock and connect that to an SDIO WiFi chip clock input (this seems
>>>> useful in my understanding because the RTC clock provides high
>>>> precision)
>>>> b) When using the PWM controller to output a 32kHz clock signal. In
>>>> this case my understanding is that using the RTC clock as input to the
>>>> PWM controller results in the best possible signal
>>>>
>>>> The second case won't be supported with Heiner's patches [0] that use
>>>> CCF (common clock framework) in the PWM controller driver.
>>>> In this series the parent clock is calculated using:
>>>> freq = div64_u64(NSEC_PER_SEC * (u64)0xffff, period);
>>>>
>>>> A 32kHz clock means a PWM period of 30518ns. So with the above
>>> To be precise: 30517,578125ns
>>> What means that the PWM framework can't say "I want 32768Hz",
>>> but just "I want something being very close to 32768Hz".
>>> So what you need is some simple heuristic to interpret the
>>> PWM request -> "PWM requests 30518ns, but supposedly it wants
>>> 32768Hz"
>>>
>>> NSEC_PER_SEC / 30518 = 32767 (rounded down from 32767,547)
>>> clk_round_rate(channel->clk, 32767) would return 0 (I *think*),
>>> because it tries to find the next lower clock.
>>>
>>> The SoC families I'm familiar with have fclkin2 as PWM parent.
>>> That's 1 GHz in my case, what results in a frequency of 32.767,547Hz
>>> for period = 30518n.
>>> What you're saying is that newer generations don't have PWM parents
>>>> 24MHz any longer?
>> No, of course not. For example, a fixed PLL (with all fclk_divX
>> settings) has rates higher than 24MHz. However, we need to consider the
>> 'heavy' background of such PWM.
>>
>> However, we have a "lightweight" clkin (special rtc32k) with a rate of
>> 32kHz that we could potentially use as an input to produce a 32kHz
>> output on the PWM lines. I don't see any reason why we should not
>> support such special cases.
>>
> Two more things to consider:
> 1. When wanting a 32kHz (well, 32768Hz) output with a 50% duty cycle,
> then we need hi=0 and lo=0 with a 64kHz input clock.
> See point 2 for an explanation of why 0 and not 1.
> Means we couldn't use the RTC input clock. Did you consider this?
> Or do I miss something?
> 2. Seems the PWM block internally increments hi and lo, except the
> constant_en bit is set on newer PWM block versions.
> For bigger cnt values the impact is negligible, but for very small
> values it's something we have to consider.
> This was one additional motivation for me to choose an input
> frequency that creates big cnt values.

Hello Heiner

1. yes, you're right. To get pwm output 32k clock parent clock should be 64k at least.
2. yes, you're right. We have the same vision of pwm IP working. Seems like lo and hi regs are treated like divider regs (internal increment) except special case when constant bit on.

We have patches supporting constant bit andtaking into account hi\lo "shadow" incrementing, will public it soon
Best regards
George

>>>
>>>> calculation the PWM driver is asking for a clock rate of >=2GHz.
>>>> We concluded that letting the common clock framework choose the best
>>>> possible parent (meaning: removing CLK_SET_RATE_NO_REPARENT here) can
>>>> be a way forward.
>>>> But this means that the PWM controller driver must try to find the
>>>> best possible parent somehow. The easiest way we came up with
>>>> (pseudo-code):
>>>> freq = NSEC_PER_SEC / period;
>>>> fin_freq = clk_round_rate(channel->clk, freq);
>>>> if (fin_freq != freq) {
>>>> freq = div64_u64(NSEC_PER_SEC * (u64)0xffff, period);
>>>> fin_freq = clk_round_rate(channel->clk, freq);
>>>> }
>>>>
>>>> The idea is: for a requested 32kHz signal the PWM period is 30518ns.
>>>> The updated logic would find that there's a matching clock input and
>>>> use that directly. If not: use the original logic as suggested by
>>>> Heiner.
>>>>
>>>>
>>>> Best regards,
>>>> Martin
>>>>
>>>>
>>>> [0] https://lore.kernel.org/linux-amlogic/[email protected]/
>

2023-05-23 10:40:39

by Dmitry Rokosov

[permalink] [raw]
Subject: Re: [PATCH v14 6/6] clk: meson: a1: add Amlogic A1 Peripherals clock controller driver

Heiner,

On Mon, May 22, 2023 at 10:35:59PM +0200, Heiner Kallweit wrote:
> On 22.05.2023 15:44, Dmitry Rokosov wrote:
> > Heiner,
> >
> > On Fri, May 19, 2023 at 06:10:50PM +0200, Heiner Kallweit wrote:
> >> On 18.05.2023 22:04, Martin Blumenstingl wrote:
> >>> Hi Dmitry,
> >>>
> >>> On Wed, May 17, 2023 at 12:34 PM Dmitry Rokosov
> >>> <[email protected]> wrote:
> >>> [...]
> >>>>>> Additionally, the CCF determines the best ancestor based on how close
> >>>>>> its rate is to the given one, based on arithmetic calculations. However,
> >>>>>> we have independent knowledge that a certain clock would be better, with
> >>>>>> less jitter and fewer intermediaries, which will likely improve energy
> >>>>>> efficiency. Sadly, the CCF cannot take this into account.
> >>>>> I agree that the implementation in CCF is fairly simple. There's ways
> >>>>> to trick it though: IIRC if there are multiple equally suitable clocks
> >>>>> it picks the first one. For me all of this has worked so far which is
> >>>>> what makes me curious in this case (not saying that anything is wrong
> >>>>> with your approach).
> >>>>>
> >>>>> Do you have a (real world) example where the RTC clock should be
> >>>>> preferred over another clock?
> >>>>>
> >>>>
> >>>> Yes, a real-life example is the need for a 32Khz clock for an external
> >>>> wifi chip. There is one option to provide this clock with high
> >>>> precision, which is RTC + GENCLK.
> >>>>
> >>>>> I'm thinking about the following scenario.
> >>>>> PWM parents:
> >>>>> - XTAL: 24MHz
> >>>>> - sys: not sure - let's say 166.67MHz
> >>>>> - RTC: 32kHz
> >>>>>
> >>>>> Then after that there's a divider and a gate.
> >>>>>
> >>>>> Let's say the PWM controller needs a 1MHz clock: it can take that from
> >>>>> XTAL or sys. Since XTAL is evenly divisible to 1MHz CCF will pick that
> >>>>> and use the divider.
> >>>>> But let's say the PWM controller needs a 32kHz clock: CCF would
> >>>>> automatically pick the RTC clock.
> >>>>> So is your implementation there to cover let's say 1kHz where
> >>>>> mathematically 24MHz can be divided evenly to 1kHz (and thus should
> >>>>> not result in any jitter) but RTC gives better precision in the real
> >>>>> world (even though it's off by 24Hz)?
> >>>>>
> >>>>
> >>>> I don't think so. The highest precision that RTC can provide is from a
> >>>> 32KHz rate only. However, I believe that a 1kHz frequency can also be
> >>>> achieved by using xtal 24MHz with a divider, which can provide high
> >>>> precision as well.
> >>> Thank you again for the great discussion on IRC today.
> >>> Here's my short summary so I don't forget before you'll follow up on this.
> >>>
> >>> In general there's two known cases where the RTC clock needs to be used:
> >>> a) When using the GENCLK output of the SoC to output the 32kHz RTC
> >>> clock and connect that to an SDIO WiFi chip clock input (this seems
> >>> useful in my understanding because the RTC clock provides high
> >>> precision)
> >>> b) When using the PWM controller to output a 32kHz clock signal. In
> >>> this case my understanding is that using the RTC clock as input to the
> >>> PWM controller results in the best possible signal
> >>>
> >>> The second case won't be supported with Heiner's patches [0] that use
> >>> CCF (common clock framework) in the PWM controller driver.
> >>> In this series the parent clock is calculated using:
> >>> freq = div64_u64(NSEC_PER_SEC * (u64)0xffff, period);
> >>>
> >>> A 32kHz clock means a PWM period of 30518ns. So with the above
> >>
> >> To be precise: 30517,578125ns
> >> What means that the PWM framework can't say "I want 32768Hz",
> >> but just "I want something being very close to 32768Hz".
> >> So what you need is some simple heuristic to interpret the
> >> PWM request -> "PWM requests 30518ns, but supposedly it wants
> >> 32768Hz"
> >>
> >> NSEC_PER_SEC / 30518 = 32767 (rounded down from 32767,547)
> >> clk_round_rate(channel->clk, 32767) would return 0 (I *think*),
> >> because it tries to find the next lower clock.
> >>
> >> The SoC families I'm familiar with have fclkin2 as PWM parent.
> >> That's 1 GHz in my case, what results in a frequency of 32.767,547Hz
> >> for period = 30518n.
> >> What you're saying is that newer generations don't have PWM parents
> >>> 24MHz any longer?
> >
> > No, of course not. For example, a fixed PLL (with all fclk_divX
> > settings) has rates higher than 24MHz. However, we need to consider the
> > 'heavy' background of such PWM.
> >
> > However, we have a "lightweight" clkin (special rtc32k) with a rate of
> > 32kHz that we could potentially use as an input to produce a 32kHz
> > output on the PWM lines. I don't see any reason why we should not
> > support such special cases.
> >
>
> Two more things to consider:
> 1. When wanting a 32kHz (well, 32768Hz) output with a 50% duty cycle,
> then we need hi=0 and lo=0 with a 64kHz input clock.
> See point 2 for an explanation of why 0 and not 1.
> Means we couldn't use the RTC input clock. Did you consider this?
> Or do I miss something?

Nope, that's my fault. Using a 32kHz base clock for rates of 16kHz or
lower that can be divided evenly is a good choice. However, as you
mentioned in point 2, the problem with autoincrements should be fixed
first.

> 2. Seems the PWM block internally increments hi and lo, except the
> constant_en bit is set on newer PWM block versions.
> For bigger cnt values the impact is negligible, but for very small
> values it's something we have to consider.
> This was one additional motivation for me to choose an input
> frequency that creates big cnt values.

George has a patchset for this and he will send it very soon as he
mentioned in another email. Let's assume the code is already fixed and
no longer has any issues. In my opinion, if we want to generate rates of
16kHz, 8kHz, 4kHz, etc., the best parent clock to use is a 32kHz RTC.
High-rate clocks may not divide evenly and may not generate an accurate
output rate. What do you think about it?

>
> >>
> >>
> >>> calculation the PWM driver is asking for a clock rate of >=2GHz.
> >>> We concluded that letting the common clock framework choose the best
> >>> possible parent (meaning: removing CLK_SET_RATE_NO_REPARENT here) can
> >>> be a way forward.
> >>> But this means that the PWM controller driver must try to find the
> >>> best possible parent somehow. The easiest way we came up with
> >>> (pseudo-code):
> >>> freq = NSEC_PER_SEC / period;
> >>> fin_freq = clk_round_rate(channel->clk, freq);
> >>> if (fin_freq != freq) {
> >>> freq = div64_u64(NSEC_PER_SEC * (u64)0xffff, period);
> >>> fin_freq = clk_round_rate(channel->clk, freq);
> >>> }
> >>>
> >>> The idea is: for a requested 32kHz signal the PWM period is 30518ns.
> >>> The updated logic would find that there's a matching clock input and
> >>> use that directly. If not: use the original logic as suggested by
> >>> Heiner.
> >>>
> >>>
> >>> Best regards,
> >>> Martin
> >>>
> >>>
> >>> [0] https://lore.kernel.org/linux-amlogic/[email protected]/
> >>
> >
>

--
Thank you,
Dmitry

2023-05-23 19:40:44

by Heiner Kallweit

[permalink] [raw]
Subject: Re: [PATCH v14 6/6] clk: meson: a1: add Amlogic A1 Peripherals clock controller driver

On 23.05.2023 12:23, Dmitry Rokosov wrote:
> Heiner,
>
> On Mon, May 22, 2023 at 10:35:59PM +0200, Heiner Kallweit wrote:
>> On 22.05.2023 15:44, Dmitry Rokosov wrote:
>>> Heiner,
>>>
>>> On Fri, May 19, 2023 at 06:10:50PM +0200, Heiner Kallweit wrote:
>>>> On 18.05.2023 22:04, Martin Blumenstingl wrote:
>>>>> Hi Dmitry,
>>>>>
>>>>> On Wed, May 17, 2023 at 12:34 PM Dmitry Rokosov
>>>>> <[email protected]> wrote:
>>>>> [...]
>>>>>>>> Additionally, the CCF determines the best ancestor based on how close
>>>>>>>> its rate is to the given one, based on arithmetic calculations. However,
>>>>>>>> we have independent knowledge that a certain clock would be better, with
>>>>>>>> less jitter and fewer intermediaries, which will likely improve energy
>>>>>>>> efficiency. Sadly, the CCF cannot take this into account.
>>>>>>> I agree that the implementation in CCF is fairly simple. There's ways
>>>>>>> to trick it though: IIRC if there are multiple equally suitable clocks
>>>>>>> it picks the first one. For me all of this has worked so far which is
>>>>>>> what makes me curious in this case (not saying that anything is wrong
>>>>>>> with your approach).
>>>>>>>
>>>>>>> Do you have a (real world) example where the RTC clock should be
>>>>>>> preferred over another clock?
>>>>>>>
>>>>>>
>>>>>> Yes, a real-life example is the need for a 32Khz clock for an external
>>>>>> wifi chip. There is one option to provide this clock with high
>>>>>> precision, which is RTC + GENCLK.
>>>>>>
>>>>>>> I'm thinking about the following scenario.
>>>>>>> PWM parents:
>>>>>>> - XTAL: 24MHz
>>>>>>> - sys: not sure - let's say 166.67MHz
>>>>>>> - RTC: 32kHz
>>>>>>>
>>>>>>> Then after that there's a divider and a gate.
>>>>>>>
>>>>>>> Let's say the PWM controller needs a 1MHz clock: it can take that from
>>>>>>> XTAL or sys. Since XTAL is evenly divisible to 1MHz CCF will pick that
>>>>>>> and use the divider.
>>>>>>> But let's say the PWM controller needs a 32kHz clock: CCF would
>>>>>>> automatically pick the RTC clock.
>>>>>>> So is your implementation there to cover let's say 1kHz where
>>>>>>> mathematically 24MHz can be divided evenly to 1kHz (and thus should
>>>>>>> not result in any jitter) but RTC gives better precision in the real
>>>>>>> world (even though it's off by 24Hz)?
>>>>>>>
>>>>>>
>>>>>> I don't think so. The highest precision that RTC can provide is from a
>>>>>> 32KHz rate only. However, I believe that a 1kHz frequency can also be
>>>>>> achieved by using xtal 24MHz with a divider, which can provide high
>>>>>> precision as well.
>>>>> Thank you again for the great discussion on IRC today.
>>>>> Here's my short summary so I don't forget before you'll follow up on this.
>>>>>
>>>>> In general there's two known cases where the RTC clock needs to be used:
>>>>> a) When using the GENCLK output of the SoC to output the 32kHz RTC
>>>>> clock and connect that to an SDIO WiFi chip clock input (this seems
>>>>> useful in my understanding because the RTC clock provides high
>>>>> precision)
>>>>> b) When using the PWM controller to output a 32kHz clock signal. In
>>>>> this case my understanding is that using the RTC clock as input to the
>>>>> PWM controller results in the best possible signal
>>>>>
>>>>> The second case won't be supported with Heiner's patches [0] that use
>>>>> CCF (common clock framework) in the PWM controller driver.
>>>>> In this series the parent clock is calculated using:
>>>>> freq = div64_u64(NSEC_PER_SEC * (u64)0xffff, period);
>>>>>
>>>>> A 32kHz clock means a PWM period of 30518ns. So with the above
>>>>
>>>> To be precise: 30517,578125ns
>>>> What means that the PWM framework can't say "I want 32768Hz",
>>>> but just "I want something being very close to 32768Hz".
>>>> So what you need is some simple heuristic to interpret the
>>>> PWM request -> "PWM requests 30518ns, but supposedly it wants
>>>> 32768Hz"
>>>>
>>>> NSEC_PER_SEC / 30518 = 32767 (rounded down from 32767,547)
>>>> clk_round_rate(channel->clk, 32767) would return 0 (I *think*),
>>>> because it tries to find the next lower clock.
>>>>
>>>> The SoC families I'm familiar with have fclkin2 as PWM parent.
>>>> That's 1 GHz in my case, what results in a frequency of 32.767,547Hz
>>>> for period = 30518n.
>>>> What you're saying is that newer generations don't have PWM parents
>>>>> 24MHz any longer?
>>>
>>> No, of course not. For example, a fixed PLL (with all fclk_divX
>>> settings) has rates higher than 24MHz. However, we need to consider the
>>> 'heavy' background of such PWM.
>>>
>>> However, we have a "lightweight" clkin (special rtc32k) with a rate of
>>> 32kHz that we could potentially use as an input to produce a 32kHz
>>> output on the PWM lines. I don't see any reason why we should not
>>> support such special cases.
>>>
>>
>> Two more things to consider:
>> 1. When wanting a 32kHz (well, 32768Hz) output with a 50% duty cycle,
>> then we need hi=0 and lo=0 with a 64kHz input clock.
>> See point 2 for an explanation of why 0 and not 1.
>> Means we couldn't use the RTC input clock. Did you consider this?
>> Or do I miss something?
>
> Nope, that's my fault. Using a 32kHz base clock for rates of 16kHz or
> lower that can be divided evenly is a good choice. However, as you
> mentioned in point 2, the problem with autoincrements should be fixed
> first.
>
>> 2. Seems the PWM block internally increments hi and lo, except the
>> constant_en bit is set on newer PWM block versions.
>> For bigger cnt values the impact is negligible, but for very small
>> values it's something we have to consider.
>> This was one additional motivation for me to choose an input
>> frequency that creates big cnt values.
>
> George has a patchset for this and he will send it very soon as he
> mentioned in another email. Let's assume the code is already fixed and
> no longer has any issues. In my opinion, if we want to generate rates of
> 16kHz, 8kHz, 4kHz, etc., the best parent clock to use is a 32kHz RTC.
> High-rate clocks may not divide evenly and may not generate an accurate
> output rate. What do you think about it?
>
With the logic in the CCF conversion patch set for these rates the 24MHz
xtal mux input would be used, creating exactly the requested rates.
So I see no difference here. What I don't know is whether power
consumption may be higher with a higher input rate, or whether quality
of the mux parent clocks differs.

>>
>>>>
>>>>
>>>>> calculation the PWM driver is asking for a clock rate of >=2GHz.
>>>>> We concluded that letting the common clock framework choose the best
>>>>> possible parent (meaning: removing CLK_SET_RATE_NO_REPARENT here) can
>>>>> be a way forward.
>>>>> But this means that the PWM controller driver must try to find the
>>>>> best possible parent somehow. The easiest way we came up with
>>>>> (pseudo-code):
>>>>> freq = NSEC_PER_SEC / period;
>>>>> fin_freq = clk_round_rate(channel->clk, freq);
>>>>> if (fin_freq != freq) {
>>>>> freq = div64_u64(NSEC_PER_SEC * (u64)0xffff, period);
>>>>> fin_freq = clk_round_rate(channel->clk, freq);
>>>>> }
>>>>>
>>>>> The idea is: for a requested 32kHz signal the PWM period is 30518ns.
>>>>> The updated logic would find that there's a matching clock input and
>>>>> use that directly. If not: use the original logic as suggested by
>>>>> Heiner.
>>>>>
>>>>>
>>>>> Best regards,
>>>>> Martin
>>>>>
>>>>>
>>>>> [0] https://lore.kernel.org/linux-amlogic/[email protected]/
>>>>
>>>
>>
>


2023-05-24 01:10:07

by George Stark

[permalink] [raw]
Subject: Re: [PATCH v14 6/6] clk: meson: a1: add Amlogic A1 Peripherals clock controller driver

On 5/22/23 23:36, Heiner Kallweit wrote:
> On 22.05.2023 15:44, Dmitry Rokosov wrote:
>> Heiner,
>>
>> On Fri, May 19, 2023 at 06:10:50PM +0200, Heiner Kallweit wrote:
>>> On 18.05.2023 22:04, Martin Blumenstingl wrote:
>>>> Hi Dmitry,
>>>>
>>>> On Wed, May 17, 2023 at 12:34 PM Dmitry Rokosov
>>>> <[email protected]> wrote:
>>>> [...]
>>>>>>> Additionally, the CCF determines the best ancestor based on how close
>>>>>>> its rate is to the given one, based on arithmetic calculations. However,
>>>>>>> we have independent knowledge that a certain clock would be better, with
>>>>>>> less jitter and fewer intermediaries, which will likely improve energy
>>>>>>> efficiency. Sadly, the CCF cannot take this into account.
>>>>>> I agree that the implementation in CCF is fairly simple. There's ways
>>>>>> to trick it though: IIRC if there are multiple equally suitable clocks
>>>>>> it picks the first one. For me all of this has worked so far which is
>>>>>> what makes me curious in this case (not saying that anything is wrong
>>>>>> with your approach).
>>>>>>
>>>>>> Do you have a (real world) example where the RTC clock should be
>>>>>> preferred over another clock?
>>>>>>
>>>>> Yes, a real-life example is the need for a 32Khz clock for an external
>>>>> wifi chip. There is one option to provide this clock with high
>>>>> precision, which is RTC + GENCLK.
>>>>>
>>>>>> I'm thinking about the following scenario.
>>>>>> PWM parents:
>>>>>> - XTAL: 24MHz
>>>>>> - sys: not sure - let's say 166.67MHz
>>>>>> - RTC: 32kHz
>>>>>>
>>>>>> Then after that there's a divider and a gate.
>>>>>>
>>>>>> Let's say the PWM controller needs a 1MHz clock: it can take that from
>>>>>> XTAL or sys. Since XTAL is evenly divisible to 1MHz CCF will pick that
>>>>>> and use the divider.
>>>>>> But let's say the PWM controller needs a 32kHz clock: CCF would
>>>>>> automatically pick the RTC clock.
>>>>>> So is your implementation there to cover let's say 1kHz where
>>>>>> mathematically 24MHz can be divided evenly to 1kHz (and thus should
>>>>>> not result in any jitter) but RTC gives better precision in the real
>>>>>> world (even though it's off by 24Hz)?
>>>>>>
>>>>> I don't think so. The highest precision that RTC can provide is from a
>>>>> 32KHz rate only. However, I believe that a 1kHz frequency can also be
>>>>> achieved by using xtal 24MHz with a divider, which can provide high
>>>>> precision as well.
>>>> Thank you again for the great discussion on IRC today.
>>>> Here's my short summary so I don't forget before you'll follow up on this.
>>>>
>>>> In general there's two known cases where the RTC clock needs to be used:
>>>> a) When using the GENCLK output of the SoC to output the 32kHz RTC
>>>> clock and connect that to an SDIO WiFi chip clock input (this seems
>>>> useful in my understanding because the RTC clock provides high
>>>> precision)
>>>> b) When using the PWM controller to output a 32kHz clock signal. In
>>>> this case my understanding is that using the RTC clock as input to the
>>>> PWM controller results in the best possible signal
>>>>
>>>> The second case won't be supported with Heiner's patches [0] that use
>>>> CCF (common clock framework) in the PWM controller driver.
>>>> In this series the parent clock is calculated using:
>>>> freq = div64_u64(NSEC_PER_SEC * (u64)0xffff, period);
>>>>
>>>> A 32kHz clock means a PWM period of 30518ns. So with the above
>>> To be precise: 30517,578125ns
>>> What means that the PWM framework can't say "I want 32768Hz",
>>> but just "I want something being very close to 32768Hz".
>>> So what you need is some simple heuristic to interpret the
>>> PWM request -> "PWM requests 30518ns, but supposedly it wants
>>> 32768Hz"
>>>
>>> NSEC_PER_SEC / 30518 = 32767 (rounded down from 32767,547)
>>> clk_round_rate(channel->clk, 32767) would return 0 (I *think*),
>>> because it tries to find the next lower clock.
>>>
>>> The SoC families I'm familiar with have fclkin2 as PWM parent.
>>> That's 1 GHz in my case, what results in a frequency of 32.767,547Hz
>>> for period = 30518n.
>>> What you're saying is that newer generations don't have PWM parents
>>>> 24MHz any longer?
>> No, of course not. For example, a fixed PLL (with all fclk_divX
>> settings) has rates higher than 24MHz. However, we need to consider the
>> 'heavy' background of such PWM.
>>
>> However, we have a "lightweight" clkin (special rtc32k) with a rate of
>> 32kHz that we could potentially use as an input to produce a 32kHz
>> output on the PWM lines. I don't see any reason why we should not
>> support such special cases.
>>
> Two more things to consider:
> 1. When wanting a 32kHz (well, 32768Hz) output with a 50% duty cycle,
> then we need hi=0 and lo=0 with a 64kHz input clock.
> See point 2 for an explanation of why 0 and not 1.
> Means we couldn't use the RTC input clock. Did you consider this?
> Or do I miss something?
> 2. Seems the PWM block internally increments hi and lo, except the
> constant_en bit is set on newer PWM block versions.
> For bigger cnt values the impact is negligible, but for very small
> values it's something we have to consider.
> This was one additional motivation for me to choose an input
> frequency that creates big cnt values.
>
Hello Heiner

As I mentioned earlier I have some changes to take into account lo and hi regs incrementing.

But it's more convenient to base my patch on top on one of yours (https://lore.kernel.org/linux-amlogic/[email protected]/)

Is that ok if I resend your patch along with mine in series?

Best regards
George

>>>
>>>> calculation the PWM driver is asking for a clock rate of >=2GHz.
>>>> We concluded that letting the common clock framework choose the best
>>>> possible parent (meaning: removing CLK_SET_RATE_NO_REPARENT here) can
>>>> be a way forward.
>>>> But this means that the PWM controller driver must try to find the
>>>> best possible parent somehow. The easiest way we came up with
>>>> (pseudo-code):
>>>> freq = NSEC_PER_SEC / period;
>>>> fin_freq = clk_round_rate(channel->clk, freq);
>>>> if (fin_freq != freq) {
>>>> freq = div64_u64(NSEC_PER_SEC * (u64)0xffff, period);
>>>> fin_freq = clk_round_rate(channel->clk, freq);
>>>> }
>>>>
>>>> The idea is: for a requested 32kHz signal the PWM period is 30518ns.
>>>> The updated logic would find that there's a matching clock input and
>>>> use that directly. If not: use the original logic as suggested by
>>>> Heiner.
>>>>
>>>>
>>>> Best regards,
>>>> Martin
>>>>
>>>>
>>>> [0] https://lore.kernel.org/linux-amlogic/[email protected]/
>


2023-05-24 09:45:53

by Dmitry Rokosov

[permalink] [raw]
Subject: Re: [PATCH v14 6/6] clk: meson: a1: add Amlogic A1 Peripherals clock controller driver

On Tue, May 23, 2023 at 09:13:46PM +0200, Heiner Kallweit wrote:
> On 23.05.2023 12:23, Dmitry Rokosov wrote:
> > Heiner,
> >
> > On Mon, May 22, 2023 at 10:35:59PM +0200, Heiner Kallweit wrote:
> >> On 22.05.2023 15:44, Dmitry Rokosov wrote:
> >>> Heiner,
> >>>
> >>> On Fri, May 19, 2023 at 06:10:50PM +0200, Heiner Kallweit wrote:
> >>>> On 18.05.2023 22:04, Martin Blumenstingl wrote:
> >>>>> Hi Dmitry,
> >>>>>
> >>>>> On Wed, May 17, 2023 at 12:34 PM Dmitry Rokosov
> >>>>> <[email protected]> wrote:
> >>>>> [...]
> >>>>>>>> Additionally, the CCF determines the best ancestor based on how close
> >>>>>>>> its rate is to the given one, based on arithmetic calculations. However,
> >>>>>>>> we have independent knowledge that a certain clock would be better, with
> >>>>>>>> less jitter and fewer intermediaries, which will likely improve energy
> >>>>>>>> efficiency. Sadly, the CCF cannot take this into account.
> >>>>>>> I agree that the implementation in CCF is fairly simple. There's ways
> >>>>>>> to trick it though: IIRC if there are multiple equally suitable clocks
> >>>>>>> it picks the first one. For me all of this has worked so far which is
> >>>>>>> what makes me curious in this case (not saying that anything is wrong
> >>>>>>> with your approach).
> >>>>>>>
> >>>>>>> Do you have a (real world) example where the RTC clock should be
> >>>>>>> preferred over another clock?
> >>>>>>>
> >>>>>>
> >>>>>> Yes, a real-life example is the need for a 32Khz clock for an external
> >>>>>> wifi chip. There is one option to provide this clock with high
> >>>>>> precision, which is RTC + GENCLK.
> >>>>>>
> >>>>>>> I'm thinking about the following scenario.
> >>>>>>> PWM parents:
> >>>>>>> - XTAL: 24MHz
> >>>>>>> - sys: not sure - let's say 166.67MHz
> >>>>>>> - RTC: 32kHz
> >>>>>>>
> >>>>>>> Then after that there's a divider and a gate.
> >>>>>>>
> >>>>>>> Let's say the PWM controller needs a 1MHz clock: it can take that from
> >>>>>>> XTAL or sys. Since XTAL is evenly divisible to 1MHz CCF will pick that
> >>>>>>> and use the divider.
> >>>>>>> But let's say the PWM controller needs a 32kHz clock: CCF would
> >>>>>>> automatically pick the RTC clock.
> >>>>>>> So is your implementation there to cover let's say 1kHz where
> >>>>>>> mathematically 24MHz can be divided evenly to 1kHz (and thus should
> >>>>>>> not result in any jitter) but RTC gives better precision in the real
> >>>>>>> world (even though it's off by 24Hz)?
> >>>>>>>
> >>>>>>
> >>>>>> I don't think so. The highest precision that RTC can provide is from a
> >>>>>> 32KHz rate only. However, I believe that a 1kHz frequency can also be
> >>>>>> achieved by using xtal 24MHz with a divider, which can provide high
> >>>>>> precision as well.
> >>>>> Thank you again for the great discussion on IRC today.
> >>>>> Here's my short summary so I don't forget before you'll follow up on this.
> >>>>>
> >>>>> In general there's two known cases where the RTC clock needs to be used:
> >>>>> a) When using the GENCLK output of the SoC to output the 32kHz RTC
> >>>>> clock and connect that to an SDIO WiFi chip clock input (this seems
> >>>>> useful in my understanding because the RTC clock provides high
> >>>>> precision)
> >>>>> b) When using the PWM controller to output a 32kHz clock signal. In
> >>>>> this case my understanding is that using the RTC clock as input to the
> >>>>> PWM controller results in the best possible signal
> >>>>>
> >>>>> The second case won't be supported with Heiner's patches [0] that use
> >>>>> CCF (common clock framework) in the PWM controller driver.
> >>>>> In this series the parent clock is calculated using:
> >>>>> freq = div64_u64(NSEC_PER_SEC * (u64)0xffff, period);
> >>>>>
> >>>>> A 32kHz clock means a PWM period of 30518ns. So with the above
> >>>>
> >>>> To be precise: 30517,578125ns
> >>>> What means that the PWM framework can't say "I want 32768Hz",
> >>>> but just "I want something being very close to 32768Hz".
> >>>> So what you need is some simple heuristic to interpret the
> >>>> PWM request -> "PWM requests 30518ns, but supposedly it wants
> >>>> 32768Hz"
> >>>>
> >>>> NSEC_PER_SEC / 30518 = 32767 (rounded down from 32767,547)
> >>>> clk_round_rate(channel->clk, 32767) would return 0 (I *think*),
> >>>> because it tries to find the next lower clock.
> >>>>
> >>>> The SoC families I'm familiar with have fclkin2 as PWM parent.
> >>>> That's 1 GHz in my case, what results in a frequency of 32.767,547Hz
> >>>> for period = 30518n.
> >>>> What you're saying is that newer generations don't have PWM parents
> >>>>> 24MHz any longer?
> >>>
> >>> No, of course not. For example, a fixed PLL (with all fclk_divX
> >>> settings) has rates higher than 24MHz. However, we need to consider the
> >>> 'heavy' background of such PWM.
> >>>
> >>> However, we have a "lightweight" clkin (special rtc32k) with a rate of
> >>> 32kHz that we could potentially use as an input to produce a 32kHz
> >>> output on the PWM lines. I don't see any reason why we should not
> >>> support such special cases.
> >>>
> >>
> >> Two more things to consider:
> >> 1. When wanting a 32kHz (well, 32768Hz) output with a 50% duty cycle,
> >> then we need hi=0 and lo=0 with a 64kHz input clock.
> >> See point 2 for an explanation of why 0 and not 1.
> >> Means we couldn't use the RTC input clock. Did you consider this?
> >> Or do I miss something?
> >
> > Nope, that's my fault. Using a 32kHz base clock for rates of 16kHz or
> > lower that can be divided evenly is a good choice. However, as you
> > mentioned in point 2, the problem with autoincrements should be fixed
> > first.
> >
> >> 2. Seems the PWM block internally increments hi and lo, except the
> >> constant_en bit is set on newer PWM block versions.
> >> For bigger cnt values the impact is negligible, but for very small
> >> values it's something we have to consider.
> >> This was one additional motivation for me to choose an input
> >> frequency that creates big cnt values.
> >
> > George has a patchset for this and he will send it very soon as he
> > mentioned in another email. Let's assume the code is already fixed and
> > no longer has any issues. In my opinion, if we want to generate rates of
> > 16kHz, 8kHz, 4kHz, etc., the best parent clock to use is a 32kHz RTC.
> > High-rate clocks may not divide evenly and may not generate an accurate
> > output rate. What do you think about it?
> >
> With the logic in the CCF conversion patch set for these rates the 24MHz
> xtal mux input would be used, creating exactly the requested rates.
> So I see no difference here. What I don't know is whether power
> consumption may be higher with a higher input rate, or whether quality
> of the mux parent clocks differs.
>

We conducted some experiments on our end and tried using different
clocks as inputs for PWM to generate small rates like 16kHz. The clock
with the least amount of jitter for the resulting output was the RTC
parent at 32kHz, while using the 64MHz (system clock) or the 24MHz (xtal
clock) resulted in more jitter values but was still acceptable.

As for power consumption, I'm not knowledgeable about it; it's something
only Amlolgic has all the secrets to. However, I presume there was a
strong reason to create a separate RTC clock with good 32kHz quality.

> >>
> >>>>
> >>>>
> >>>>> calculation the PWM driver is asking for a clock rate of >=2GHz.
> >>>>> We concluded that letting the common clock framework choose the best
> >>>>> possible parent (meaning: removing CLK_SET_RATE_NO_REPARENT here) can
> >>>>> be a way forward.
> >>>>> But this means that the PWM controller driver must try to find the
> >>>>> best possible parent somehow. The easiest way we came up with
> >>>>> (pseudo-code):
> >>>>> freq = NSEC_PER_SEC / period;
> >>>>> fin_freq = clk_round_rate(channel->clk, freq);
> >>>>> if (fin_freq != freq) {
> >>>>> freq = div64_u64(NSEC_PER_SEC * (u64)0xffff, period);
> >>>>> fin_freq = clk_round_rate(channel->clk, freq);
> >>>>> }
> >>>>>
> >>>>> The idea is: for a requested 32kHz signal the PWM period is 30518ns.
> >>>>> The updated logic would find that there's a matching clock input and
> >>>>> use that directly. If not: use the original logic as suggested by
> >>>>> Heiner.
> >>>>>
> >>>>>
> >>>>> Best regards,
> >>>>> Martin
> >>>>>
> >>>>>
> >>>>> [0] https://lore.kernel.org/linux-amlogic/[email protected]/
> >>>>
> >>>
> >>
> >
>

--
Thank you,
Dmitry