From b5a87e692c044c42bf9309cde4bc455bec246c60 Mon Sep 17 00:00:00 2001 From: Alain Volmat Date: Wed, 31 Mar 2021 22:16:26 +0200 Subject: [PATCH 01/18] clk: st: clkgen-pll: remove unused variable of struct clkgen_pll ODF field within the struct clkgen_pll is never used by the driver and can thus be removed. Signed-off-by: Alain Volmat Reviewed-by: Patrice Chotard Link: https://lore.kernel.org/r/20210331201632.24530-2-avolmat@me.com Signed-off-by: Stephen Boyd --- drivers/clk/st/clkgen-pll.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/clk/st/clkgen-pll.c b/drivers/clk/st/clkgen-pll.c index 119c5b33080c..f6c56ca90c7d 100644 --- a/drivers/clk/st/clkgen-pll.c +++ b/drivers/clk/st/clkgen-pll.c @@ -146,7 +146,6 @@ struct clkgen_pll { u32 ndiv; u32 idf; - u32 odf; u32 cp; }; From 574dffc2995cc96f1c13e802576d1fb146ebd387 Mon Sep 17 00:00:00 2001 From: Alain Volmat Date: Wed, 31 Mar 2021 22:16:27 +0200 Subject: [PATCH 02/18] clk: st: flexgen: embed soc clock outputs within compatible data In order to avoid relying on the old style description via the DT clock-output-names, add compatible data describing the flexgen outputs clocks for all STiH407/STiH410 and STiH418 SOCs. In order to ease transition between the two methods, this commit introduce the new compatible without removing the old method. Once DTs will be fixed, the method relying on DT clock-output-names will be removed from this driver as well as old compatibles. Signed-off-by: Alain Volmat Reviewed-by: Patrice Chotard Link: https://lore.kernel.org/r/20210331201632.24530-3-avolmat@me.com Signed-off-by: Stephen Boyd --- drivers/clk/st/clk-flexgen.c | 367 +++++++++++++++++++++++++++++++++-- 1 file changed, 353 insertions(+), 14 deletions(-) diff --git a/drivers/clk/st/clk-flexgen.c b/drivers/clk/st/clk-flexgen.c index 55873d4b7603..7ae4f656191e 100644 --- a/drivers/clk/st/clk-flexgen.c +++ b/drivers/clk/st/clk-flexgen.c @@ -16,9 +16,16 @@ #include #include +struct clkgen_clk_out { + const char *name; + unsigned long flags; +}; + struct clkgen_data { unsigned long flags; bool mode; + const struct clkgen_clk_out *outputs; + const unsigned int outputs_nb; }; struct flexgen { @@ -295,6 +302,290 @@ static const struct clkgen_data clkgen_video = { .mode = 1, }; +static const struct clkgen_clk_out clkgen_stih407_a0_clk_out[] = { + /* This clk needs to be on so that memory interface is accessible */ + { .name = "clk-ic-lmi0", .flags = CLK_IS_CRITICAL }, +}; + +static const struct clkgen_data clkgen_stih407_a0 = { + .outputs = clkgen_stih407_a0_clk_out, + .outputs_nb = ARRAY_SIZE(clkgen_stih407_a0_clk_out), +}; + +static const struct clkgen_clk_out clkgen_stih410_a0_clk_out[] = { + /* Those clks need to be on so that memory interface is accessible */ + { .name = "clk-ic-lmi0", .flags = CLK_IS_CRITICAL }, + { .name = "clk-ic-lmi1", .flags = CLK_IS_CRITICAL }, +}; + +static const struct clkgen_data clkgen_stih410_a0 = { + .outputs = clkgen_stih410_a0_clk_out, + .outputs_nb = ARRAY_SIZE(clkgen_stih410_a0_clk_out), +}; + +static const struct clkgen_clk_out clkgen_stih407_c0_clk_out[] = { + { .name = "clk-icn-gpu", }, + { .name = "clk-fdma", }, + { .name = "clk-nand", }, + { .name = "clk-hva", }, + { .name = "clk-proc-stfe", }, + { .name = "clk-proc-tp", }, + { .name = "clk-rx-icn-dmu", }, + { .name = "clk-rx-icn-hva", }, + /* This clk needs to be on to keep bus interconnect alive */ + { .name = "clk-icn-cpu", .flags = CLK_IS_CRITICAL }, + /* This clk needs to be on to keep bus interconnect alive */ + { .name = "clk-tx-icn-dmu", .flags = CLK_IS_CRITICAL }, + { .name = "clk-mmc-0", }, + { .name = "clk-mmc-1", }, + { .name = "clk-jpegdec", }, + /* This clk needs to be on to keep A9 running */ + { .name = "clk-ext2fa9", .flags = CLK_IS_CRITICAL }, + { .name = "clk-ic-bdisp-0", }, + { .name = "clk-ic-bdisp-1", }, + { .name = "clk-pp-dmu", }, + { .name = "clk-vid-dmu", }, + { .name = "clk-dss-lpc", }, + { .name = "clk-st231-aud-0", }, + { .name = "clk-st231-gp-1", }, + { .name = "clk-st231-dmu", }, + /* This clk needs to be on to keep bus interconnect alive */ + { .name = "clk-icn-lmi", .flags = CLK_IS_CRITICAL }, + { .name = "clk-tx-icn-disp-1", }, + /* This clk needs to be on to keep bus interconnect alive */ + { .name = "clk-icn-sbc", .flags = CLK_IS_CRITICAL }, + { .name = "clk-stfe-frc2", }, + { .name = "clk-eth-phy", }, + { .name = "clk-eth-ref-phyclk", }, + { .name = "clk-flash-promip", }, + { .name = "clk-main-disp", }, + { .name = "clk-aux-disp", }, + { .name = "clk-compo-dvp", }, +}; + +static const struct clkgen_data clkgen_stih407_c0 = { + .outputs = clkgen_stih407_c0_clk_out, + .outputs_nb = ARRAY_SIZE(clkgen_stih407_c0_clk_out), +}; + +static const struct clkgen_clk_out clkgen_stih410_c0_clk_out[] = { + { .name = "clk-icn-gpu", }, + { .name = "clk-fdma", }, + { .name = "clk-nand", }, + { .name = "clk-hva", }, + { .name = "clk-proc-stfe", }, + { .name = "clk-proc-tp", }, + { .name = "clk-rx-icn-dmu", }, + { .name = "clk-rx-icn-hva", }, + /* This clk needs to be on to keep bus interconnect alive */ + { .name = "clk-icn-cpu", .flags = CLK_IS_CRITICAL }, + /* This clk needs to be on to keep bus interconnect alive */ + { .name = "clk-tx-icn-dmu", .flags = CLK_IS_CRITICAL }, + { .name = "clk-mmc-0", }, + { .name = "clk-mmc-1", }, + { .name = "clk-jpegdec", }, + /* This clk needs to be on to keep A9 running */ + { .name = "clk-ext2fa9", .flags = CLK_IS_CRITICAL }, + { .name = "clk-ic-bdisp-0", }, + { .name = "clk-ic-bdisp-1", }, + { .name = "clk-pp-dmu", }, + { .name = "clk-vid-dmu", }, + { .name = "clk-dss-lpc", }, + { .name = "clk-st231-aud-0", }, + { .name = "clk-st231-gp-1", }, + { .name = "clk-st231-dmu", }, + /* This clk needs to be on to keep bus interconnect alive */ + { .name = "clk-icn-lmi", .flags = CLK_IS_CRITICAL }, + { .name = "clk-tx-icn-disp-1", }, + /* This clk needs to be on to keep bus interconnect alive */ + { .name = "clk-icn-sbc", .flags = CLK_IS_CRITICAL }, + { .name = "clk-stfe-frc2", }, + { .name = "clk-eth-phy", }, + { .name = "clk-eth-ref-phyclk", }, + { .name = "clk-flash-promip", }, + { .name = "clk-main-disp", }, + { .name = "clk-aux-disp", }, + { .name = "clk-compo-dvp", }, + { .name = "clk-tx-icn-hades", }, + { .name = "clk-rx-icn-hades", }, + /* This clk needs to be on to keep bus interconnect alive */ + { .name = "clk-icn-reg-16", .flags = CLK_IS_CRITICAL }, + { .name = "clk-pp-hades", }, + { .name = "clk-clust-hades", }, + { .name = "clk-hwpe-hades", }, + { .name = "clk-fc-hades", }, +}; + +static const struct clkgen_data clkgen_stih410_c0 = { + .outputs = clkgen_stih410_c0_clk_out, + .outputs_nb = ARRAY_SIZE(clkgen_stih410_c0_clk_out), +}; + +static const struct clkgen_clk_out clkgen_stih418_c0_clk_out[] = { + { .name = "clk-icn-gpu", }, + { .name = "clk-fdma", }, + { .name = "clk-nand", }, + { .name = "clk-hva", }, + { .name = "clk-proc-stfe", }, + { .name = "clk-tp", }, + /* This clk needs to be on to keep bus interconnect alive */ + { .name = "clk-rx-icn-dmu", .flags = CLK_IS_CRITICAL }, + /* This clk needs to be on to keep bus interconnect alive */ + { .name = "clk-rx-icn-hva", .flags = CLK_IS_CRITICAL }, + { .name = "clk-icn-cpu", .flags = CLK_IS_CRITICAL }, + /* This clk needs to be on to keep bus interconnect alive */ + { .name = "clk-tx-icn-dmu", .flags = CLK_IS_CRITICAL }, + { .name = "clk-mmc-0", }, + { .name = "clk-mmc-1", }, + { .name = "clk-jpegdec", }, + /* This clk needs to be on to keep bus interconnect alive */ + { .name = "clk-icn-reg", .flags = CLK_IS_CRITICAL }, + { .name = "clk-proc-bdisp-0", }, + { .name = "clk-proc-bdisp-1", }, + { .name = "clk-pp-dmu", }, + { .name = "clk-vid-dmu", }, + { .name = "clk-dss-lpc", }, + { .name = "clk-st231-aud-0", }, + { .name = "clk-st231-gp-1", }, + { .name = "clk-st231-dmu", }, + /* This clk needs to be on to keep bus interconnect alive */ + { .name = "clk-icn-lmi", .flags = CLK_IS_CRITICAL }, + /* This clk needs to be on to keep bus interconnect alive */ + { .name = "clk-tx-icn-1", .flags = CLK_IS_CRITICAL }, + /* This clk needs to be on to keep bus interconnect alive */ + { .name = "clk-icn-sbc", .flags = CLK_IS_CRITICAL }, + { .name = "clk-stfe-frc2", }, + { .name = "clk-eth-phyref", }, + { .name = "clk-eth-ref-phyclk", }, + { .name = "clk-flash-promip", }, + { .name = "clk-main-disp", }, + { .name = "clk-aux-disp", }, + { .name = "clk-compo-dvp", }, + /* This clk needs to be on to keep bus interconnect alive */ + { .name = "clk-tx-icn-hades", .flags = CLK_IS_CRITICAL }, + /* This clk needs to be on to keep bus interconnect alive */ + { .name = "clk-rx-icn-hades", .flags = CLK_IS_CRITICAL }, + /* This clk needs to be on to keep bus interconnect alive */ + { .name = "clk-icn-reg-16", .flags = CLK_IS_CRITICAL }, + { .name = "clk-pp-hevc", }, + { .name = "clk-clust-hevc", }, + { .name = "clk-hwpe-hevc", }, + { .name = "clk-fc-hevc", }, + { .name = "clk-proc-mixer", }, + { .name = "clk-proc-sc", }, + { .name = "clk-avsp-hevc", }, +}; + +static const struct clkgen_data clkgen_stih418_c0 = { + .outputs = clkgen_stih418_c0_clk_out, + .outputs_nb = ARRAY_SIZE(clkgen_stih418_c0_clk_out), +}; + +static const struct clkgen_clk_out clkgen_stih407_d0_clk_out[] = { + { .name = "clk-pcm-0", }, + { .name = "clk-pcm-1", }, + { .name = "clk-pcm-2", }, + { .name = "clk-spdiff", }, +}; + +static const struct clkgen_data clkgen_stih407_d0 = { + .flags = CLK_SET_RATE_PARENT, + .outputs = clkgen_stih407_d0_clk_out, + .outputs_nb = ARRAY_SIZE(clkgen_stih407_d0_clk_out), +}; + +static const struct clkgen_clk_out clkgen_stih410_d0_clk_out[] = { + { .name = "clk-pcm-0", }, + { .name = "clk-pcm-1", }, + { .name = "clk-pcm-2", }, + { .name = "clk-spdiff", }, + { .name = "clk-pcmr10-master", }, + { .name = "clk-usb2-phy", }, +}; + +static const struct clkgen_data clkgen_stih410_d0 = { + .flags = CLK_SET_RATE_PARENT, + .outputs = clkgen_stih410_d0_clk_out, + .outputs_nb = ARRAY_SIZE(clkgen_stih410_d0_clk_out), +}; + +static const struct clkgen_clk_out clkgen_stih407_d2_clk_out[] = { + { .name = "clk-pix-main-disp", }, + { .name = "clk-pix-pip", }, + { .name = "clk-pix-gdp1", }, + { .name = "clk-pix-gdp2", }, + { .name = "clk-pix-gdp3", }, + { .name = "clk-pix-gdp4", }, + { .name = "clk-pix-aux-disp", }, + { .name = "clk-denc", }, + { .name = "clk-pix-hddac", }, + { .name = "clk-hddac", }, + { .name = "clk-sddac", }, + { .name = "clk-pix-dvo", }, + { .name = "clk-dvo", }, + { .name = "clk-pix-hdmi", }, + { .name = "clk-tmds-hdmi", }, + { .name = "clk-ref-hdmiphy", }, +}; + +static const struct clkgen_data clkgen_stih407_d2 = { + .outputs = clkgen_stih407_d2_clk_out, + .outputs_nb = ARRAY_SIZE(clkgen_stih407_d2_clk_out), + .flags = CLK_SET_RATE_PARENT, + .mode = 1, +}; + +static const struct clkgen_clk_out clkgen_stih418_d2_clk_out[] = { + { .name = "clk-pix-main-disp", }, + { .name = "", }, + { .name = "", }, + { .name = "", }, + { .name = "", }, + { .name = "clk-tmds-hdmi-div2", }, + { .name = "clk-pix-aux-disp", }, + { .name = "clk-denc", }, + { .name = "clk-pix-hddac", }, + { .name = "clk-hddac", }, + { .name = "clk-sddac", }, + { .name = "clk-pix-dvo", }, + { .name = "clk-dvo", }, + { .name = "clk-pix-hdmi", }, + { .name = "clk-tmds-hdmi", }, + { .name = "clk-ref-hdmiphy", }, + { .name = "", }, { .name = "", }, { .name = "", }, { .name = "", }, + { .name = "", }, { .name = "", }, { .name = "", }, { .name = "", }, + { .name = "", }, { .name = "", }, { .name = "", }, { .name = "", }, + { .name = "", }, { .name = "", }, { .name = "", }, { .name = "", }, + { .name = "", }, { .name = "", }, { .name = "", }, { .name = "", }, + { .name = "", }, { .name = "", }, { .name = "", }, { .name = "", }, + { .name = "", }, { .name = "", }, { .name = "", }, { .name = "", }, + { .name = "", }, { .name = "", }, { .name = "", }, + { .name = "clk-vp9", }, +}; + +static const struct clkgen_data clkgen_stih418_d2 = { + .outputs = clkgen_stih418_d2_clk_out, + .outputs_nb = ARRAY_SIZE(clkgen_stih418_d2_clk_out), + .flags = CLK_SET_RATE_PARENT, + .mode = 1, +}; + +static const struct clkgen_clk_out clkgen_stih407_d3_clk_out[] = { + { .name = "clk-stfe-frc1", }, + { .name = "clk-tsout-0", }, + { .name = "clk-tsout-1", }, + { .name = "clk-mchi", }, + { .name = "clk-vsens-compo", }, + { .name = "clk-frc1-remote", }, + { .name = "clk-lpc-0", }, + { .name = "clk-lpc-1", }, +}; + +static const struct clkgen_data clkgen_stih407_d3 = { + .outputs = clkgen_stih407_d3_clk_out, + .outputs_nb = ARRAY_SIZE(clkgen_stih407_d3_clk_out), +}; + static const struct of_device_id flexgen_of_match[] = { { .compatible = "st,flexgen-audio", @@ -304,6 +595,46 @@ static const struct of_device_id flexgen_of_match[] = { .compatible = "st,flexgen-video", .data = &clkgen_video, }, + { + .compatible = "st,flexgen-stih407-a0", + .data = &clkgen_stih407_a0, + }, + { + .compatible = "st,flexgen-stih410-a0", + .data = &clkgen_stih410_a0, + }, + { + .compatible = "st,flexgen-stih407-c0", + .data = &clkgen_stih407_c0, + }, + { + .compatible = "st,flexgen-stih410-c0", + .data = &clkgen_stih410_c0, + }, + { + .compatible = "st,flexgen-stih418-c0", + .data = &clkgen_stih418_c0, + }, + { + .compatible = "st,flexgen-stih407-d0", + .data = &clkgen_stih407_d0, + }, + { + .compatible = "st,flexgen-stih410-d0", + .data = &clkgen_stih410_d0, + }, + { + .compatible = "st,flexgen-stih407-d2", + .data = &clkgen_stih407_d2, + }, + { + .compatible = "st,flexgen-stih418-d2", + .data = &clkgen_stih418_d2, + }, + { + .compatible = "st,flexgen-stih407-d3", + .data = &clkgen_stih407_d3, + }, {} }; @@ -320,6 +651,7 @@ static void __init st_of_flexgen_setup(struct device_node *np) unsigned long flex_flags = 0; int ret; bool clk_mode = 0; + const char *clk_name; pnode = of_get_parent(np); if (!pnode) @@ -347,13 +679,17 @@ static void __init st_of_flexgen_setup(struct device_node *np) if (!clk_data) goto err; - ret = of_property_count_strings(np, "clock-output-names"); - if (ret <= 0) { - pr_err("%s: Failed to get number of output clocks (%d)", - __func__, clk_data->clk_num); - goto err; - } - clk_data->clk_num = ret; + /* First try to get output information from the compatible data */ + if (!data || !data->outputs_nb || !data->outputs) { + ret = of_property_count_strings(np, "clock-output-names"); + if (ret <= 0) { + pr_err("%s: Failed to get number of output clocks (%d)", + __func__, clk_data->clk_num); + goto err; + } + clk_data->clk_num = ret; + } else + clk_data->clk_num = data->outputs_nb; clk_data->clks = kcalloc(clk_data->clk_num, sizeof(struct clk *), GFP_KERNEL); @@ -368,16 +704,19 @@ static void __init st_of_flexgen_setup(struct device_node *np) for (i = 0; i < clk_data->clk_num; i++) { struct clk *clk; - const char *clk_name; - if (of_property_read_string_index(np, "clock-output-names", - i, &clk_name)) { - break; + if (!data || !data->outputs_nb || !data->outputs) { + if (of_property_read_string_index(np, + "clock-output-names", + i, &clk_name)) + break; + flex_flags &= ~CLK_IS_CRITICAL; + of_clk_detect_critical(np, i, &flex_flags); + } else { + clk_name = data->outputs[i].name; + flex_flags = data->flags | data->outputs[i].flags; } - flex_flags &= ~CLK_IS_CRITICAL; - of_clk_detect_critical(np, i, &flex_flags); - /* * If we read an empty clock name then the output is unused */ From fa745c71b8e75e85ce129dd9097a00ac7a9df47f Mon Sep 17 00:00:00 2001 From: Alain Volmat Date: Wed, 31 Mar 2021 22:16:28 +0200 Subject: [PATCH 03/18] dt-bindings: clock: st: flexgen: add new introduced compatible New compatible are added, supporting various kind of flexgen in STiH407, STiH410 and STiH418 Signed-off-by: Alain Volmat Acked-by: Rob Herring Link: https://lore.kernel.org/r/20210331201632.24530-4-avolmat@me.com Signed-off-by: Stephen Boyd --- .../devicetree/bindings/clock/st/st,flexgen.txt | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/Documentation/devicetree/bindings/clock/st/st,flexgen.txt b/Documentation/devicetree/bindings/clock/st/st,flexgen.txt index 7ff77fc57dff..55a18939bddd 100644 --- a/Documentation/devicetree/bindings/clock/st/st,flexgen.txt +++ b/Documentation/devicetree/bindings/clock/st/st,flexgen.txt @@ -64,6 +64,16 @@ Required properties: audio use case) "st,flexgen-video", "st,flexgen" (enable clock propagation on parent and activate synchronous mode) + "st,flexgen-stih407-a0" + "st,flexgen-stih410-a0" + "st,flexgen-stih407-c0" + "st,flexgen-stih410-c0" + "st,flexgen-stih418-c0" + "st,flexgen-stih407-d0" + "st,flexgen-stih410-d0" + "st,flexgen-stih407-d2" + "st,flexgen-stih418-d2" + "st,flexgen-stih407-d3" - #clock-cells : from common clock binding; shall be set to 1 (multiple clock outputs). From 92ef1b2beb109c23e2348de8b7ef9d0736fa0b3d Mon Sep 17 00:00:00 2001 From: Alain Volmat Date: Wed, 31 Mar 2021 22:16:29 +0200 Subject: [PATCH 04/18] clk: st: clkgen-pll: embed soc clock outputs within compatible data In order to avoid relying on the old style description via the DT clock-output-names, add compatible data describing the flexgen outputs clocks for all STiH407/STiH410 and STiH418 SOCs. In order to ease transition between the two methods, this commit introduce the new compatible without removing the old method. Once DTs will be fixed, the method relying on DT clock-output-names will be removed from this driver as well as old compatibles. Signed-off-by: Alain Volmat Reviewed-by: Patrice Chotard Link: https://lore.kernel.org/r/20210331201632.24530-5-avolmat@me.com Signed-off-by: Stephen Boyd --- drivers/clk/st/clkgen-pll.c | 120 +++++++++++++++++++++++++++++++----- 1 file changed, 106 insertions(+), 14 deletions(-) diff --git a/drivers/clk/st/clkgen-pll.c b/drivers/clk/st/clkgen-pll.c index f6c56ca90c7d..b36e4d803636 100644 --- a/drivers/clk/st/clkgen-pll.c +++ b/drivers/clk/st/clkgen-pll.c @@ -57,6 +57,17 @@ struct clkgen_pll_data { const struct clk_ops *ops; }; +struct clkgen_clk_out { + const char *name; + unsigned long flags; +}; + +struct clkgen_pll_data_clks { + struct clkgen_pll_data *data; + const struct clkgen_clk_out *outputs; +}; + + static const struct clk_ops stm_pll3200c32_ops; static const struct clk_ops stm_pll3200c32_a9_ops; static const struct clk_ops stm_pll4600c28_ops; @@ -74,6 +85,28 @@ static const struct clkgen_pll_data st_pll3200c32_cx_0 = { .ops = &stm_pll3200c32_ops, }; +static const struct clkgen_pll_data_clks st_pll3200c32_cx_0_legacy_data = { + .data = (struct clkgen_pll_data *)&st_pll3200c32_cx_0, +}; + +static const struct clkgen_clk_out st_pll3200c32_ax_0_clks[] = { + { .name = "clk-s-a0-pll-odf-0", }, +}; + +static const struct clkgen_pll_data_clks st_pll3200c32_a0_data = { + .data = (struct clkgen_pll_data *)&st_pll3200c32_cx_0, + .outputs = st_pll3200c32_ax_0_clks, +}; + +static const struct clkgen_clk_out st_pll3200c32_cx_0_clks[] = { + { .name = "clk-s-c0-pll0-odf-0", }, +}; + +static const struct clkgen_pll_data_clks st_pll3200c32_c0_data = { + .data = (struct clkgen_pll_data *)&st_pll3200c32_cx_0, + .outputs = st_pll3200c32_cx_0_clks, +}; + static const struct clkgen_pll_data st_pll3200c32_cx_1 = { /* 407 C0 PLL1 */ .pdn_status = CLKGEN_FIELD(0x2c8, 0x1, 8), @@ -87,6 +120,19 @@ static const struct clkgen_pll_data st_pll3200c32_cx_1 = { .ops = &stm_pll3200c32_ops, }; +static const struct clkgen_pll_data_clks st_pll3200c32_cx_1_legacy_data = { + .data = (struct clkgen_pll_data *)&st_pll3200c32_cx_1, +}; + +static const struct clkgen_clk_out st_pll3200c32_cx_1_clks[] = { + { .name = "clk-s-c0-pll1-odf-0", }, +}; + +static const struct clkgen_pll_data_clks st_pll3200c32_c1_data = { + .data = (struct clkgen_pll_data *)&st_pll3200c32_cx_1, + .outputs = st_pll3200c32_cx_1_clks, +}; + static const struct clkgen_pll_data st_pll3200c32_407_a9 = { /* 407 A9 */ .pdn_status = CLKGEN_FIELD(0x1a8, 0x1, 0), @@ -104,6 +150,15 @@ static const struct clkgen_pll_data st_pll3200c32_407_a9 = { .ops = &stm_pll3200c32_a9_ops, }; +static const struct clkgen_clk_out st_pll3200c32_407_a9_clks[] = { + { .name = "clockgen-a9-pll-odf", }, +}; + +static const struct clkgen_pll_data_clks st_pll3200c32_407_a9_data = { + .data = (struct clkgen_pll_data *)&st_pll3200c32_407_a9, + .outputs = st_pll3200c32_407_a9_clks, +}; + static struct clkgen_pll_data st_pll4600c28_418_a9 = { /* 418 A9 */ .pdn_status = CLKGEN_FIELD(0x1a8, 0x1, 0), @@ -120,6 +175,15 @@ static struct clkgen_pll_data st_pll4600c28_418_a9 = { .ops = &stm_pll4600c28_ops, }; +static const struct clkgen_clk_out st_pll4600c28_418_a9_clks[] = { + { .name = "clockgen-a9-pll-odf", }, +}; + +static const struct clkgen_pll_data_clks st_pll4600c28_418_a9_data = { + .data = (struct clkgen_pll_data *)&st_pll4600c28_418_a9, + .outputs = st_pll4600c28_418_a9_clks, +}; + /** * DOC: Clock Generated by PLL, rate set and enabled by bootloader * @@ -684,7 +748,7 @@ static struct clk * __init clkgen_odf_register(const char *parent_name, static void __init clkgen_c32_pll_setup(struct device_node *np, - struct clkgen_pll_data *data) + struct clkgen_pll_data_clks *datac) { struct clk *clk; const char *parent_name, *pll_name; @@ -704,14 +768,14 @@ static void __init clkgen_c32_pll_setup(struct device_node *np, of_clk_detect_critical(np, 0, &pll_flags); - clk = clkgen_pll_register(parent_name, data, pll_base, pll_flags, - np->name, data->lock); + clk = clkgen_pll_register(parent_name, datac->data, pll_base, pll_flags, + np->name, datac->data->lock); if (IS_ERR(clk)) return; pll_name = __clk_get_name(clk); - num_odfs = data->num_odfs; + num_odfs = datac->data->num_odfs; clk_data = kzalloc(sizeof(*clk_data), GFP_KERNEL); if (!clk_data) @@ -729,14 +793,21 @@ static void __init clkgen_c32_pll_setup(struct device_node *np, const char *clk_name; unsigned long odf_flags = 0; - if (of_property_read_string_index(np, "clock-output-names", - odf, &clk_name)) - return; + if (datac->outputs) { + clk_name = datac->outputs[odf].name; + odf_flags = datac->outputs[odf].flags; + } else { + if (of_property_read_string_index(np, + "clock-output-names", + odf, &clk_name)) + return; - of_clk_detect_critical(np, odf, &odf_flags); + of_clk_detect_critical(np, odf, &odf_flags); + } - clk = clkgen_odf_register(pll_name, pll_base, data, odf_flags, - odf, &clkgena_c32_odf_lock, clk_name); + clk = clkgen_odf_register(pll_name, pll_base, datac->data, + odf_flags, odf, &clkgena_c32_odf_lock, + clk_name); if (IS_ERR(clk)) goto err; @@ -754,27 +825,48 @@ static void __init clkgen_c32_pll_setup(struct device_node *np, static void __init clkgen_c32_pll0_setup(struct device_node *np) { clkgen_c32_pll_setup(np, - (struct clkgen_pll_data *) &st_pll3200c32_cx_0); + (struct clkgen_pll_data_clks *) &st_pll3200c32_cx_0_legacy_data); } CLK_OF_DECLARE(c32_pll0, "st,clkgen-pll0", clkgen_c32_pll0_setup); +static void __init clkgen_c32_pll0_a0_setup(struct device_node *np) +{ + clkgen_c32_pll_setup(np, + (struct clkgen_pll_data_clks *) &st_pll3200c32_a0_data); +} +CLK_OF_DECLARE(c32_pll0_a0, "st,clkgen-pll0-a0", clkgen_c32_pll0_a0_setup); + +static void __init clkgen_c32_pll0_c0_setup(struct device_node *np) +{ + clkgen_c32_pll_setup(np, + (struct clkgen_pll_data_clks *) &st_pll3200c32_c0_data); +} +CLK_OF_DECLARE(c32_pll0_c0, "st,clkgen-pll0-c0", clkgen_c32_pll0_c0_setup); + static void __init clkgen_c32_pll1_setup(struct device_node *np) { clkgen_c32_pll_setup(np, - (struct clkgen_pll_data *) &st_pll3200c32_cx_1); + (struct clkgen_pll_data_clks *) &st_pll3200c32_cx_1_legacy_data); } CLK_OF_DECLARE(c32_pll1, "st,clkgen-pll1", clkgen_c32_pll1_setup); +static void __init clkgen_c32_pll1_c0_setup(struct device_node *np) +{ + clkgen_c32_pll_setup(np, + (struct clkgen_pll_data_clks *) &st_pll3200c32_c1_data); +} +CLK_OF_DECLARE(c32_pll1_c0, "st,clkgen-pll1-c0", clkgen_c32_pll1_c0_setup); + static void __init clkgen_c32_plla9_setup(struct device_node *np) { clkgen_c32_pll_setup(np, - (struct clkgen_pll_data *) &st_pll3200c32_407_a9); + (struct clkgen_pll_data_clks *) &st_pll3200c32_407_a9_data); } CLK_OF_DECLARE(c32_plla9, "st,stih407-clkgen-plla9", clkgen_c32_plla9_setup); static void __init clkgen_c28_plla9_setup(struct device_node *np) { clkgen_c32_pll_setup(np, - (struct clkgen_pll_data *) &st_pll4600c28_418_a9); + (struct clkgen_pll_data_clks *) &st_pll4600c28_418_a9_data); } CLK_OF_DECLARE(c28_plla9, "st,stih418-clkgen-plla9", clkgen_c28_plla9_setup); From 8df309e9c5e173eea83909d5575eab89965541af Mon Sep 17 00:00:00 2001 From: Alain Volmat Date: Wed, 31 Mar 2021 22:16:30 +0200 Subject: [PATCH 05/18] dt-bindings: clock: st: clkgen-pll: add new introduced compatible New compatible are added, supporting various kind of clkgen-pll used for STiH407, STiH410 and STiH418 Signed-off-by: Alain Volmat Acked-by: Rob Herring Link: https://lore.kernel.org/r/20210331201632.24530-6-avolmat@me.com Signed-off-by: Stephen Boyd --- Documentation/devicetree/bindings/clock/st/st,clkgen-pll.txt | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Documentation/devicetree/bindings/clock/st/st,clkgen-pll.txt b/Documentation/devicetree/bindings/clock/st/st,clkgen-pll.txt index f207053e0550..d0fa1e02d06d 100644 --- a/Documentation/devicetree/bindings/clock/st/st,clkgen-pll.txt +++ b/Documentation/devicetree/bindings/clock/st/st,clkgen-pll.txt @@ -10,7 +10,10 @@ Required properties: - compatible : shall be: "st,clkgen-pll0" + "st,clkgen-pll0-a0" + "st,clkgen-pll0-c0" "st,clkgen-pll1" + "st,clkgen-pll1-c0" "st,stih407-clkgen-plla9" "st,stih418-clkgen-plla9" From 5dc1a12711b3338e3227f30c5ac15921d719d5c4 Mon Sep 17 00:00:00 2001 From: Alain Volmat Date: Wed, 31 Mar 2021 22:16:31 +0200 Subject: [PATCH 06/18] clk: st: clkgen-fsyn: embed soc clock outputs within compatible data In order to avoid relying on the old style description via the DT clock-output-names, add compatible data describing the flexgen outputs clocks for all STiH407/STiH410 and STiH418 SOCs. In order to ease transition between the two methods, this commit introduce the new compatible without removing the old method. Once DTs will be fixed, the method relying on DT clock-output-names will be removed from this driver as well as old compatibles. Signed-off-by: Alain Volmat Reviewed-by: Patrice Chotard Link: https://lore.kernel.org/r/20210331201632.24530-7-avolmat@me.com Signed-off-by: Stephen Boyd --- drivers/clk/st/clkgen-fsyn.c | 113 +++++++++++++++++++++++++++++++---- 1 file changed, 101 insertions(+), 12 deletions(-) diff --git a/drivers/clk/st/clkgen-fsyn.c b/drivers/clk/st/clkgen-fsyn.c index dd6062e043e0..164285d6be97 100644 --- a/drivers/clk/st/clkgen-fsyn.c +++ b/drivers/clk/st/clkgen-fsyn.c @@ -66,6 +66,16 @@ struct clkgen_quadfs_data { unsigned long *); }; +struct clkgen_clk_out { + const char *name; + unsigned long flags; +}; + +struct clkgen_quadfs_data_clks { + struct clkgen_quadfs_data *data; + const struct clkgen_clk_out *outputs; +}; + static const struct clk_ops st_quadfs_pll_c32_ops; static int clk_fs660c32_dig_get_params(unsigned long input, @@ -115,6 +125,18 @@ static const struct clkgen_quadfs_data st_fs660c32_C = { .get_rate = clk_fs660c32_dig_get_rate, }; +static const struct clkgen_clk_out st_fs660c32_C_clks[] = { + { .name = "clk-s-c0-fs0-ch0", }, + { .name = "clk-s-c0-fs0-ch1", }, + { .name = "clk-s-c0-fs0-ch2", }, + { .name = "clk-s-c0-fs0-ch3", }, +}; + +static const struct clkgen_quadfs_data_clks st_fs660c32_C_data = { + .data = (struct clkgen_quadfs_data *)&st_fs660c32_C, + .outputs = st_fs660c32_C_clks, +}; + static const struct clkgen_quadfs_data st_fs660c32_D = { .nrst_present = true, .nrst = { CLKGEN_FIELD(0x2a0, 0x1, 0), @@ -156,6 +178,46 @@ static const struct clkgen_quadfs_data st_fs660c32_D = { .get_params = clk_fs660c32_dig_get_params, .get_rate = clk_fs660c32_dig_get_rate,}; +static const struct clkgen_quadfs_data_clks st_fs660c32_D_data = { + .data = (struct clkgen_quadfs_data *)&st_fs660c32_D, +}; + +static const struct clkgen_clk_out st_fs660c32_D0_clks[] = { + { .name = "clk-s-d0-fs0-ch0", }, + { .name = "clk-s-d0-fs0-ch1", }, + { .name = "clk-s-d0-fs0-ch2", }, + { .name = "clk-s-d0-fs0-ch3", }, +}; + +static const struct clkgen_quadfs_data_clks st_fs660c32_D0_data = { + .data = (struct clkgen_quadfs_data *)&st_fs660c32_D, + .outputs = st_fs660c32_D0_clks, +}; + +static const struct clkgen_clk_out st_fs660c32_D2_clks[] = { + { .name = "clk-s-d2-fs0-ch0", }, + { .name = "clk-s-d2-fs0-ch1", }, + { .name = "clk-s-d2-fs0-ch2", }, + { .name = "clk-s-d2-fs0-ch3", }, +}; + +static const struct clkgen_quadfs_data_clks st_fs660c32_D2_data = { + .data = (struct clkgen_quadfs_data *)&st_fs660c32_D, + .outputs = st_fs660c32_D2_clks, +}; + +static const struct clkgen_clk_out st_fs660c32_D3_clks[] = { + { .name = "clk-s-d3-fs0-ch0", }, + { .name = "clk-s-d3-fs0-ch1", }, + { .name = "clk-s-d3-fs0-ch2", }, + { .name = "clk-s-d3-fs0-ch3", }, +}; + +static const struct clkgen_quadfs_data_clks st_fs660c32_D3_data = { + .data = (struct clkgen_quadfs_data *)&st_fs660c32_D, + .outputs = st_fs660c32_D3_clks, +}; + /** * DOC: A Frequency Synthesizer that multiples its input clock by a fixed factor * @@ -857,7 +919,7 @@ static struct clk * __init st_clk_register_quadfs_fsynth( static void __init st_of_create_quadfs_fsynths( struct device_node *np, const char *pll_name, - struct clkgen_quadfs_data *quadfs, void __iomem *reg, + struct clkgen_quadfs_data_clks *quadfs, void __iomem *reg, spinlock_t *lock) { struct clk_onecell_data *clk_data; @@ -881,9 +943,15 @@ static void __init st_of_create_quadfs_fsynths( const char *clk_name; unsigned long flags = 0; - if (of_property_read_string_index(np, "clock-output-names", - fschan, &clk_name)) { - break; + if (quadfs->outputs) { + clk_name = quadfs->outputs[fschan].name; + flags = quadfs->outputs[fschan].flags; + } else { + if (of_property_read_string_index(np, + "clock-output-names", + fschan, &clk_name)) + break; + of_clk_detect_critical(np, fschan, &flags); } /* @@ -892,10 +960,8 @@ static void __init st_of_create_quadfs_fsynths( if (*clk_name == '\0') continue; - of_clk_detect_critical(np, fschan, &flags); - clk = st_clk_register_quadfs_fsynth(clk_name, pll_name, - quadfs, reg, fschan, + quadfs->data, reg, fschan, flags, lock); /* @@ -915,7 +981,7 @@ static void __init st_of_create_quadfs_fsynths( } static void __init st_of_quadfs_setup(struct device_node *np, - struct clkgen_quadfs_data *data) + struct clkgen_quadfs_data_clks *datac) { struct clk *clk; const char *pll_name, *clk_parent_name; @@ -940,7 +1006,7 @@ static void __init st_of_quadfs_setup(struct device_node *np, spin_lock_init(lock); - clk = st_clk_register_quadfs_pll(pll_name, clk_parent_name, data, + clk = st_clk_register_quadfs_pll(pll_name, clk_parent_name, datac->data, reg, lock); if (IS_ERR(clk)) goto err_exit; @@ -950,7 +1016,7 @@ static void __init st_of_quadfs_setup(struct device_node *np, __clk_get_name(clk_get_parent(clk)), (unsigned int)clk_get_rate(clk)); - st_of_create_quadfs_fsynths(np, pll_name, data, reg, lock); + st_of_create_quadfs_fsynths(np, pll_name, datac, reg, lock); err_exit: kfree(pll_name); /* No longer need local copy of the PLL name */ @@ -958,12 +1024,35 @@ static void __init st_of_quadfs_setup(struct device_node *np, static void __init st_of_quadfs660C_setup(struct device_node *np) { - st_of_quadfs_setup(np, (struct clkgen_quadfs_data *) &st_fs660c32_C); + st_of_quadfs_setup(np, + (struct clkgen_quadfs_data_clks *) &st_fs660c32_C_data); } CLK_OF_DECLARE(quadfs660C, "st,quadfs-pll", st_of_quadfs660C_setup); static void __init st_of_quadfs660D_setup(struct device_node *np) { - st_of_quadfs_setup(np, (struct clkgen_quadfs_data *) &st_fs660c32_D); + st_of_quadfs_setup(np, + (struct clkgen_quadfs_data_clks *) &st_fs660c32_D_data); } CLK_OF_DECLARE(quadfs660D, "st,quadfs", st_of_quadfs660D_setup); + +static void __init st_of_quadfs660D0_setup(struct device_node *np) +{ + st_of_quadfs_setup(np, + (struct clkgen_quadfs_data_clks *) &st_fs660c32_D0_data); +} +CLK_OF_DECLARE(quadfs660D0, "st,quadfs-d0", st_of_quadfs660D0_setup); + +static void __init st_of_quadfs660D2_setup(struct device_node *np) +{ + st_of_quadfs_setup(np, + (struct clkgen_quadfs_data_clks *) &st_fs660c32_D2_data); +} +CLK_OF_DECLARE(quadfs660D2, "st,quadfs-d2", st_of_quadfs660D2_setup); + +static void __init st_of_quadfs660D3_setup(struct device_node *np) +{ + st_of_quadfs_setup(np, + (struct clkgen_quadfs_data_clks *) &st_fs660c32_D3_data); +} +CLK_OF_DECLARE(quadfs660D3, "st,quadfs-d3", st_of_quadfs660D3_setup); From 301035c32e6754c2293d3aba77e6de3e3dded7fe Mon Sep 17 00:00:00 2001 From: Alain Volmat Date: Wed, 31 Mar 2021 22:16:32 +0200 Subject: [PATCH 07/18] dt-bindings: clock: st: clkgen-fsyn: add new introduced compatible New compatible are added, supporting various kind of clkgen-fsyn used for STiH407, STiH410 and STiH418 Signed-off-by: Alain Volmat Link: https://lore.kernel.org/r/20210331201632.24530-8-avolmat@me.com Acked-by: Rob Herring Signed-off-by: Stephen Boyd --- Documentation/devicetree/bindings/clock/st/st,quadfs.txt | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Documentation/devicetree/bindings/clock/st/st,quadfs.txt b/Documentation/devicetree/bindings/clock/st/st,quadfs.txt index d93d49342e60..c4ba2adb0b4f 100644 --- a/Documentation/devicetree/bindings/clock/st/st,quadfs.txt +++ b/Documentation/devicetree/bindings/clock/st/st,quadfs.txt @@ -12,6 +12,9 @@ This binding uses the common clock binding[1]. Required properties: - compatible : shall be: "st,quadfs" + "st,quadfs-d0" + "st,quadfs-d2" + "st,quadfs-d3" "st,quadfs-pll" From c832bb98d3706f843a207fff44ddf8a6422289a1 Mon Sep 17 00:00:00 2001 From: Robert Hancock Date: Thu, 25 Mar 2021 13:26:35 -0600 Subject: [PATCH 08/18] dt-bindings: clock: clk-si5341: Add new attributes Add new silabs,xaxb-ext-clk and silabs,iovdd-33 properties. Changed vdd-supply on top-level node to optional since it is not actually used by the driver. Removed vdd-supply from output sub-nodes, as it was not supported by the driver and it is not easily possible to support this in that location with the kernel regulator infrastructure. Changed to have vddoX-supply attributes for each output on the top-level device node. Signed-off-by: Robert Hancock Link: https://lore.kernel.org/r/20210325192643.2190069-2-robert.hancock@calian.com Reviewed-by: Rob Herring Signed-off-by: Stephen Boyd --- .../devicetree/bindings/clock/silabs,si5341.txt | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/Documentation/devicetree/bindings/clock/silabs,si5341.txt b/Documentation/devicetree/bindings/clock/silabs,si5341.txt index 504cce3abe46..ce55aba0ce22 100644 --- a/Documentation/devicetree/bindings/clock/silabs,si5341.txt +++ b/Documentation/devicetree/bindings/clock/silabs,si5341.txt @@ -24,9 +24,8 @@ it. The device type, speed grade and revision are determined runtime by probing. -The driver currently only supports XTAL input mode, and does not support any -fancy input configurations. They can still be programmed into the chip and -the driver will leave them "as is". +The driver currently does not support any fancy input configurations. They can +still be programmed into the chip and the driver will leave them "as is". ==I2C device node== @@ -45,9 +44,9 @@ Required properties: corresponding to inputs. Use a fixed clock for the "xtal" input. At least one must be present. - clock-names: One of: "xtal", "in0", "in1", "in2" -- vdd-supply: Regulator node for VDD Optional properties: +- vdd-supply: Regulator node for VDD - vdda-supply: Regulator node for VDDA - vdds-supply: Regulator node for VDDS - silabs,pll-m-num, silabs,pll-m-den: Numerator and denominator for PLL @@ -60,7 +59,14 @@ Optional properties: be initialized, and always performs the soft-reset routine. Since this will temporarily stop all output clocks, don't do this if the chip is generating the CPU clock for example. +- silabs,xaxb-ext-clk: When present, indicates that the XA/XB pins are used + in EXTCLK (external reference clock) rather than XTAL (crystal) mode. - interrupts: Interrupt for INTRb pin. +- silabs,iovdd-33: When present, indicates that the I2C lines are using 3.3V + rather than 1.8V thresholds. +- vddoX-supply (where X is an output index): Regulator node for VDDO for the + specified output. The driver selects the output VDD_SEL setting based on this + voltage. - #address-cells: shall be set to 1. - #size-cells: shall be set to 0. @@ -77,8 +83,6 @@ Required child node properties: - reg: number of clock output. Optional child node properties: -- vdd-supply: Regulator node for VDD for this output. The driver selects default - values for common-mode and amplitude based on the voltage. - silabs,format: Output format, one of: 1 = differential (defaults to LVDS levels) 2 = low-power (defaults to HCSL levels) From 6e7d2de1e000d36990923ed80d2e78dfcb545cee Mon Sep 17 00:00:00 2001 From: Robert Hancock Date: Thu, 25 Mar 2021 13:26:36 -0600 Subject: [PATCH 09/18] clk: si5341: Wait for DEVICE_READY on startup The Si5341 datasheet warns that before accessing any other registers, including the PAGE register, we need to wait for the DEVICE_READY register to indicate the device is ready, or the process of the device loading its state from NVM can be corrupted. Wait for DEVICE_READY on startup before continuing initialization. This is done using a raw I2C register read prior to setting up regmap to avoid any potential unwanted automatic PAGE register accesses from regmap at this stage. Fixes: 3044a860fd ("clk: Add Si5341/Si5340 driver") Signed-off-by: Robert Hancock Link: https://lore.kernel.org/r/20210325192643.2190069-3-robert.hancock@calian.com Signed-off-by: Stephen Boyd --- drivers/clk/clk-si5341.c | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/drivers/clk/clk-si5341.c b/drivers/clk/clk-si5341.c index e0446e66fa64..b8a960e927bc 100644 --- a/drivers/clk/clk-si5341.c +++ b/drivers/clk/clk-si5341.c @@ -94,6 +94,7 @@ struct clk_si5341_output_config { #define SI5341_STATUS 0x000C #define SI5341_SOFT_RST 0x001C #define SI5341_IN_SEL 0x0021 +#define SI5341_DEVICE_READY 0x00FE #define SI5341_XAXB_CFG 0x090E #define SI5341_IN_EN 0x0949 #define SI5341_INX_TO_PFD_EN 0x094A @@ -1189,6 +1190,32 @@ static const struct regmap_range_cfg si5341_regmap_ranges[] = { }, }; +static int si5341_wait_device_ready(struct i2c_client *client) +{ + int count; + + /* Datasheet warns: Any attempt to read or write any register other + * than DEVICE_READY before DEVICE_READY reads as 0x0F may corrupt the + * NVM programming and may corrupt the register contents, as they are + * read from NVM. Note that this includes accesses to the PAGE register. + * Also: DEVICE_READY is available on every register page, so no page + * change is needed to read it. + * Do this outside regmap to avoid automatic PAGE register access. + * May take up to 300ms to complete. + */ + for (count = 0; count < 15; ++count) { + s32 result = i2c_smbus_read_byte_data(client, + SI5341_DEVICE_READY); + if (result < 0) + return result; + if (result == 0x0F) + return 0; + msleep(20); + } + dev_err(&client->dev, "timeout waiting for DEVICE_READY\n"); + return -EIO; +} + static const struct regmap_config si5341_regmap_config = { .reg_bits = 8, .val_bits = 8, @@ -1385,6 +1412,11 @@ static int si5341_probe(struct i2c_client *client, data->i2c_client = client; + /* Must be done before otherwise touching hardware */ + err = si5341_wait_device_ready(client); + if (err) + return err; + for (i = 0; i < SI5341_NUM_INPUTS; ++i) { input = devm_clk_get(&client->dev, si5341_input_clock_names[i]); if (IS_ERR(input)) { From 78f6f406026d688868223d5dbeb197a4f7e9a9fd Mon Sep 17 00:00:00 2001 From: Robert Hancock Date: Thu, 25 Mar 2021 13:26:37 -0600 Subject: [PATCH 10/18] clk: si5341: Avoid divide errors due to bogus register contents If the Si5341 is being initially programmed and has no stored NVM configuration, some of the register contents may contain unexpected values, such as zeros, which could cause divide by zero errors during driver initialization. Trap errors caused by zero registers or zero clock rates which could result in divide errors later in the code. Fixes: 3044a860fd ("clk: Add Si5341/Si5340 driver") Signed-off-by: Robert Hancock Link: https://lore.kernel.org/r/20210325192643.2190069-4-robert.hancock@calian.com Signed-off-by: Stephen Boyd --- drivers/clk/clk-si5341.c | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/drivers/clk/clk-si5341.c b/drivers/clk/clk-si5341.c index b8a960e927bc..ac1ccec2b681 100644 --- a/drivers/clk/clk-si5341.c +++ b/drivers/clk/clk-si5341.c @@ -624,6 +624,9 @@ static unsigned long si5341_synth_clk_recalc_rate(struct clk_hw *hw, SI5341_SYNTH_N_NUM(synth->index), &n_num, &n_den); if (err < 0) return err; + /* Check for bogus/uninitialized settings */ + if (!n_num || !n_den) + return 0; /* * n_num and n_den are shifted left as much as possible, so to prevent @@ -807,6 +810,9 @@ static long si5341_output_clk_round_rate(struct clk_hw *hw, unsigned long rate, { unsigned long r; + if (!rate) + return 0; + r = *parent_rate >> 1; /* If rate is an even divisor, no changes to parent required */ @@ -835,11 +841,16 @@ static int si5341_output_clk_set_rate(struct clk_hw *hw, unsigned long rate, unsigned long parent_rate) { struct clk_si5341_output *output = to_clk_si5341_output(hw); - /* Frequency divider is (r_div + 1) * 2 */ - u32 r_div = (parent_rate / rate) >> 1; + u32 r_div; int err; u8 r[3]; + if (!rate) + return -EINVAL; + + /* Frequency divider is (r_div + 1) * 2 */ + r_div = (parent_rate / rate) >> 1; + if (r_div <= 1) r_div = 0; else if (r_div >= BIT(24)) From 71dcc4d1f7d2ad97ff7ab831281bc6893ff713a2 Mon Sep 17 00:00:00 2001 From: Robert Hancock Date: Thu, 25 Mar 2021 13:26:38 -0600 Subject: [PATCH 11/18] clk: si5341: Check for input clock presence and PLL lock on startup After initializing the device, wait for it to report that the input clock is present and the PLL has locked before declaring success. Fixes: 3044a860fd ("clk: Add Si5341/Si5340 driver") Signed-off-by: Robert Hancock Link: https://lore.kernel.org/r/20210325192643.2190069-5-robert.hancock@calian.com Signed-off-by: Stephen Boyd --- drivers/clk/clk-si5341.c | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/drivers/clk/clk-si5341.c b/drivers/clk/clk-si5341.c index ac1ccec2b681..da40b90c2aa8 100644 --- a/drivers/clk/clk-si5341.c +++ b/drivers/clk/clk-si5341.c @@ -92,6 +92,9 @@ struct clk_si5341_output_config { #define SI5341_PN_BASE 0x0002 #define SI5341_DEVICE_REV 0x0005 #define SI5341_STATUS 0x000C +#define SI5341_LOS 0x000D +#define SI5341_STATUS_STICKY 0x0011 +#define SI5341_LOS_STICKY 0x0012 #define SI5341_SOFT_RST 0x001C #define SI5341_IN_SEL 0x0021 #define SI5341_DEVICE_READY 0x00FE @@ -99,6 +102,12 @@ struct clk_si5341_output_config { #define SI5341_IN_EN 0x0949 #define SI5341_INX_TO_PFD_EN 0x094A +/* Status bits */ +#define SI5341_STATUS_SYSINCAL BIT(0) +#define SI5341_STATUS_LOSXAXB BIT(1) +#define SI5341_STATUS_LOSREF BIT(2) +#define SI5341_STATUS_LOL BIT(3) + /* Input selection */ #define SI5341_IN_SEL_MASK 0x06 #define SI5341_IN_SEL_SHIFT 1 @@ -1416,6 +1425,7 @@ static int si5341_probe(struct i2c_client *client, unsigned int i; struct clk_si5341_output_config config[SI5341_MAX_NUM_OUTPUTS]; bool initialization_required; + u32 status; data = devm_kzalloc(&client->dev, sizeof(*data), GFP_KERNEL); if (!data) @@ -1583,6 +1593,22 @@ static int si5341_probe(struct i2c_client *client, return err; } + /* wait for device to report input clock present and PLL lock */ + err = regmap_read_poll_timeout(data->regmap, SI5341_STATUS, status, + !(status & (SI5341_STATUS_LOSREF | SI5341_STATUS_LOL)), + 10000, 250000); + if (err) { + dev_err(&client->dev, "Error waiting for input clock or PLL lock\n"); + return err; + } + + /* clear sticky alarm bits from initialization */ + err = regmap_write(data->regmap, SI5341_STATUS_STICKY, 0); + if (err) { + dev_err(&client->dev, "unable to clear sticky status\n"); + return err; + } + /* Free the names, clk framework makes copies */ for (i = 0; i < data->num_synth; ++i) devm_kfree(&client->dev, (void *)synth_clock_names[i]); From 3c9b49b0031aefb81adfdba5ab0ddf3ca3a2cdc9 Mon Sep 17 00:00:00 2001 From: Robert Hancock Date: Thu, 25 Mar 2021 13:26:39 -0600 Subject: [PATCH 12/18] clk: si5341: Update initialization magic Update the default register settings to include the VCO_RESET_CALCODE settings (set by the SiLabs ClockBuilder software but not described in the datasheet). Also update part of the initialization sequence to match ClockBuilder and the datasheet. Fixes: 3044a860fd ("clk: Add Si5341/Si5340 driver") Signed-off-by: Robert Hancock Link: https://lore.kernel.org/r/20210325192643.2190069-6-robert.hancock@calian.com Signed-off-by: Stephen Boyd --- drivers/clk/clk-si5341.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/clk/clk-si5341.c b/drivers/clk/clk-si5341.c index da40b90c2aa8..eb22f4fdbc6b 100644 --- a/drivers/clk/clk-si5341.c +++ b/drivers/clk/clk-si5341.c @@ -350,6 +350,8 @@ static const struct si5341_reg_default si5341_reg_defaults[] = { { 0x094A, 0x00 }, /* INx_TO_PFD_EN (disabled) */ { 0x0A02, 0x00 }, /* Not in datasheet */ { 0x0B44, 0x0F }, /* PDIV_ENB (datasheet does not mention what it is) */ + { 0x0B57, 0x10 }, /* VCO_RESET_CALCODE (not described in datasheet) */ + { 0x0B58, 0x05 }, /* VCO_RESET_CALCODE (not described in datasheet) */ }; /* Read and interpret a 44-bit followed by a 32-bit value in the regmap */ @@ -1104,7 +1106,7 @@ static const struct si5341_reg_default si5341_preamble[] = { { 0x0B25, 0x00 }, { 0x0502, 0x01 }, { 0x0505, 0x03 }, - { 0x0957, 0x1F }, + { 0x0957, 0x17 }, { 0x0B4E, 0x1A }, }; From b7bbf6ec4940d1a69811ec354edeeb9751fa8e85 Mon Sep 17 00:00:00 2001 From: Robert Hancock Date: Thu, 25 Mar 2021 13:26:40 -0600 Subject: [PATCH 13/18] clk: si5341: Allow different output VDD_SEL values The driver was not previously programming the VDD_SEL values for each output to indicate what external VDDO voltage was used for each. Add ability to specify a regulator supplying the VDDO pin for each output of the device. The voltage of the regulator is used to automatically set the VDD_SEL value appropriately. If no regulator is specified and the chip is being reconfigured, assume 2.5V which appears to be the chip default. Signed-off-by: Robert Hancock Link: https://lore.kernel.org/r/20210325192643.2190069-7-robert.hancock@calian.com Signed-off-by: Stephen Boyd --- drivers/clk/clk-si5341.c | 136 +++++++++++++++++++++++++++++++-------- 1 file changed, 110 insertions(+), 26 deletions(-) diff --git a/drivers/clk/clk-si5341.c b/drivers/clk/clk-si5341.c index eb22f4fdbc6b..28ac7085f362 100644 --- a/drivers/clk/clk-si5341.c +++ b/drivers/clk/clk-si5341.c @@ -19,6 +19,7 @@ #include #include #include +#include #include #include @@ -59,6 +60,7 @@ struct clk_si5341_synth { struct clk_si5341_output { struct clk_hw hw; struct clk_si5341 *data; + struct regulator *vddo_reg; u8 index; }; #define to_clk_si5341_output(_hw) \ @@ -84,6 +86,7 @@ struct clk_si5341 { struct clk_si5341_output_config { u8 out_format_drv_bits; u8 out_cm_ampl_bits; + u8 vdd_sel_bits; bool synth_master; bool always_on; }; @@ -136,6 +139,8 @@ struct clk_si5341_output_config { #define SI5341_OUT_R_REG(output) \ ((output)->data->reg_rdiv_offset[(output)->index]) +#define SI5341_OUT_MUX_VDD_SEL_MASK 0x38 + /* Synthesize N divider */ #define SI5341_SYNTH_N_NUM(x) (0x0302 + ((x) * 11)) #define SI5341_SYNTH_N_DEN(x) (0x0308 + ((x) * 11)) @@ -1248,11 +1253,11 @@ static const struct regmap_config si5341_regmap_config = { .volatile_table = &si5341_regmap_volatile, }; -static int si5341_dt_parse_dt(struct i2c_client *client, - struct clk_si5341_output_config *config) +static int si5341_dt_parse_dt(struct clk_si5341 *data, + struct clk_si5341_output_config *config) { struct device_node *child; - struct device_node *np = client->dev.of_node; + struct device_node *np = data->i2c_client->dev.of_node; u32 num; u32 val; @@ -1261,13 +1266,13 @@ static int si5341_dt_parse_dt(struct i2c_client *client, for_each_child_of_node(np, child) { if (of_property_read_u32(child, "reg", &num)) { - dev_err(&client->dev, "missing reg property of %s\n", + dev_err(&data->i2c_client->dev, "missing reg property of %s\n", child->name); goto put_child; } if (num >= SI5341_MAX_NUM_OUTPUTS) { - dev_err(&client->dev, "invalid clkout %d\n", num); + dev_err(&data->i2c_client->dev, "invalid clkout %d\n", num); goto put_child; } @@ -1286,7 +1291,7 @@ static int si5341_dt_parse_dt(struct i2c_client *client, config[num].out_format_drv_bits |= 0xc0; break; default: - dev_err(&client->dev, + dev_err(&data->i2c_client->dev, "invalid silabs,format %u for %u\n", val, num); goto put_child; @@ -1299,7 +1304,7 @@ static int si5341_dt_parse_dt(struct i2c_client *client, if (!of_property_read_u32(child, "silabs,common-mode", &val)) { if (val > 0xf) { - dev_err(&client->dev, + dev_err(&data->i2c_client->dev, "invalid silabs,common-mode %u\n", val); goto put_child; @@ -1310,7 +1315,7 @@ static int si5341_dt_parse_dt(struct i2c_client *client, if (!of_property_read_u32(child, "silabs,amplitude", &val)) { if (val > 0xf) { - dev_err(&client->dev, + dev_err(&data->i2c_client->dev, "invalid silabs,amplitude %u\n", val); goto put_child; @@ -1327,6 +1332,34 @@ static int si5341_dt_parse_dt(struct i2c_client *client, config[num].always_on = of_property_read_bool(child, "always-on"); + + config[num].vdd_sel_bits = 0x08; + if (data->clk[num].vddo_reg) { + int vdd = regulator_get_voltage(data->clk[num].vddo_reg); + + switch (vdd) { + case 3300000: + config[num].vdd_sel_bits |= 0 << 4; + break; + case 1800000: + config[num].vdd_sel_bits |= 1 << 4; + break; + case 2500000: + config[num].vdd_sel_bits |= 2 << 4; + break; + default: + dev_err(&data->i2c_client->dev, + "unsupported vddo voltage %d for %s\n", + vdd, child->name); + goto put_child; + } + } else { + /* chip seems to default to 2.5V when not set */ + dev_warn(&data->i2c_client->dev, + "no regulator set, defaulting vdd_sel to 2.5V for %s\n", + child->name); + config[num].vdd_sel_bits |= 2 << 4; + } } return 0; @@ -1452,9 +1485,33 @@ static int si5341_probe(struct i2c_client *client, } } - err = si5341_dt_parse_dt(client, config); + for (i = 0; i < SI5341_MAX_NUM_OUTPUTS; ++i) { + char reg_name[10]; + + snprintf(reg_name, sizeof(reg_name), "vddo%d", i); + data->clk[i].vddo_reg = devm_regulator_get_optional( + &client->dev, reg_name); + if (IS_ERR(data->clk[i].vddo_reg)) { + err = PTR_ERR(data->clk[i].vddo_reg); + data->clk[i].vddo_reg = NULL; + if (err == -ENODEV) + continue; + goto cleanup; + } else { + err = regulator_enable(data->clk[i].vddo_reg); + if (err) { + dev_err(&client->dev, + "failed to enable %s regulator: %d\n", + reg_name, err); + data->clk[i].vddo_reg = NULL; + goto cleanup; + } + } + } + + err = si5341_dt_parse_dt(data, config); if (err) - return err; + goto cleanup; if (of_property_read_string(client->dev.of_node, "clock-output-names", &init.name)) @@ -1462,21 +1519,23 @@ static int si5341_probe(struct i2c_client *client, root_clock_name = init.name; data->regmap = devm_regmap_init_i2c(client, &si5341_regmap_config); - if (IS_ERR(data->regmap)) - return PTR_ERR(data->regmap); + if (IS_ERR(data->regmap)) { + err = PTR_ERR(data->regmap); + goto cleanup; + } i2c_set_clientdata(client, data); err = si5341_probe_chip_id(data); if (err < 0) - return err; + goto cleanup; if (of_property_read_bool(client->dev.of_node, "silabs,reprogram")) { initialization_required = true; } else { err = si5341_is_programmed_already(data); if (err < 0) - return err; + goto cleanup; initialization_required = !err; } @@ -1485,11 +1544,11 @@ static int si5341_probe(struct i2c_client *client, /* Populate the regmap cache in preparation for "cache only" */ err = si5341_read_settings(data); if (err < 0) - return err; + goto cleanup; err = si5341_send_preamble(data); if (err < 0) - return err; + goto cleanup; /* * We intend to send all 'final' register values in a single @@ -1502,19 +1561,19 @@ static int si5341_probe(struct i2c_client *client, err = si5341_write_multiple(data, si5341_reg_defaults, ARRAY_SIZE(si5341_reg_defaults)); if (err < 0) - return err; + goto cleanup; } /* Input must be up and running at this point */ err = si5341_clk_select_active_input(data); if (err < 0) - return err; + goto cleanup; if (initialization_required) { /* PLL configuration is required */ err = si5341_initialize_pll(data); if (err < 0) - return err; + goto cleanup; } /* Register the PLL */ @@ -1527,7 +1586,7 @@ static int si5341_probe(struct i2c_client *client, err = devm_clk_hw_register(&client->dev, &data->hw); if (err) { dev_err(&client->dev, "clock registration failed\n"); - return err; + goto cleanup; } init.num_parents = 1; @@ -1564,13 +1623,17 @@ static int si5341_probe(struct i2c_client *client, regmap_write(data->regmap, SI5341_OUT_CM(&data->clk[i]), config[i].out_cm_ampl_bits); + regmap_update_bits(data->regmap, + SI5341_OUT_MUX_SEL(&data->clk[i]), + SI5341_OUT_MUX_VDD_SEL_MASK, + config[i].vdd_sel_bits); } err = devm_clk_hw_register(&client->dev, &data->clk[i].hw); kfree(init.name); /* clock framework made a copy of the name */ if (err) { dev_err(&client->dev, "output %u registration failed\n", i); - return err; + goto cleanup; } if (config[i].always_on) clk_prepare(data->clk[i].hw.clk); @@ -1580,7 +1643,7 @@ static int si5341_probe(struct i2c_client *client, data); if (err) { dev_err(&client->dev, "unable to add clk provider\n"); - return err; + goto cleanup; } if (initialization_required) { @@ -1588,11 +1651,11 @@ static int si5341_probe(struct i2c_client *client, regcache_cache_only(data->regmap, false); err = regcache_sync(data->regmap); if (err < 0) - return err; + goto cleanup; err = si5341_finalize_defaults(data); if (err < 0) - return err; + goto cleanup; } /* wait for device to report input clock present and PLL lock */ @@ -1601,14 +1664,14 @@ static int si5341_probe(struct i2c_client *client, 10000, 250000); if (err) { dev_err(&client->dev, "Error waiting for input clock or PLL lock\n"); - return err; + goto cleanup; } /* clear sticky alarm bits from initialization */ err = regmap_write(data->regmap, SI5341_STATUS_STICKY, 0); if (err) { dev_err(&client->dev, "unable to clear sticky status\n"); - return err; + goto cleanup; } /* Free the names, clk framework makes copies */ @@ -1616,6 +1679,26 @@ static int si5341_probe(struct i2c_client *client, devm_kfree(&client->dev, (void *)synth_clock_names[i]); return 0; + +cleanup: + for (i = 0; i < SI5341_MAX_NUM_OUTPUTS; ++i) { + if (data->clk[i].vddo_reg) + regulator_disable(data->clk[i].vddo_reg); + } + return err; +} + +static int si5341_remove(struct i2c_client *client) +{ + struct clk_si5341 *data = i2c_get_clientdata(client); + int i; + + for (i = 0; i < SI5341_MAX_NUM_OUTPUTS; ++i) { + if (data->clk[i].vddo_reg) + regulator_disable(data->clk[i].vddo_reg); + } + + return 0; } static const struct i2c_device_id si5341_id[] = { @@ -1644,6 +1727,7 @@ static struct i2c_driver si5341_driver = { .of_match_table = clk_si5341_of_match, }, .probe = si5341_probe, + .remove = si5341_remove, .id_table = si5341_id, }; module_i2c_driver(si5341_driver); From ab89a3439ffa61913a625c34758a46c8ca0c0713 Mon Sep 17 00:00:00 2001 From: Robert Hancock Date: Thu, 25 Mar 2021 13:26:41 -0600 Subject: [PATCH 14/18] clk: si5341: Add silabs,xaxb-ext-clk property Add a property to allow specifying that the device XA/XB pins are used for an external clock input rather than for a clock crystal. Signed-off-by: Robert Hancock Link: https://lore.kernel.org/r/20210325192643.2190069-8-robert.hancock@calian.com Signed-off-by: Stephen Boyd --- drivers/clk/clk-si5341.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/drivers/clk/clk-si5341.c b/drivers/clk/clk-si5341.c index 28ac7085f362..c5ab4a5ae6a5 100644 --- a/drivers/clk/clk-si5341.c +++ b/drivers/clk/clk-si5341.c @@ -80,6 +80,7 @@ struct clk_si5341 { u8 num_outputs; u8 num_synth; u16 chip_id; + bool xaxb_ext_clk; }; #define to_clk_si5341(_hw) container_of(_hw, struct clk_si5341, hw) @@ -529,9 +530,11 @@ static int si5341_clk_reparent(struct clk_si5341 *data, u8 index) if (err < 0) return err; - /* Power up XTAL oscillator and buffer */ + /* Power up XTAL oscillator and buffer, select clock mode */ err = regmap_update_bits(data->regmap, SI5341_XAXB_CFG, - SI5341_XAXB_CFG_PDNB, SI5341_XAXB_CFG_PDNB); + SI5341_XAXB_CFG_PDNB | SI5341_XAXB_CFG_EXTCLK_EN, + SI5341_XAXB_CFG_PDNB | (data->xaxb_ext_clk ? + SI5341_XAXB_CFG_EXTCLK_EN : 0)); if (err < 0) return err; } @@ -1539,6 +1542,8 @@ static int si5341_probe(struct i2c_client *client, initialization_required = !err; } + data->xaxb_ext_clk = of_property_read_bool(client->dev.of_node, + "silabs,xaxb-ext-clk"); if (initialization_required) { /* Populate the regmap cache in preparation for "cache only" */ From 2f02c5e42a55fbdb1b4f113df2c26a5c99e8d578 Mon Sep 17 00:00:00 2001 From: Robert Hancock Date: Thu, 25 Mar 2021 13:26:42 -0600 Subject: [PATCH 15/18] clk: si5341: Add silabs,iovdd-33 property Add a property to allow specifying that the external I2C IO pins are using 3.3V voltage thresholds rather than 1.8V. Signed-off-by: Robert Hancock Link: https://lore.kernel.org/r/20210325192643.2190069-9-robert.hancock@calian.com Signed-off-by: Stephen Boyd --- drivers/clk/clk-si5341.c | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/drivers/clk/clk-si5341.c b/drivers/clk/clk-si5341.c index c5ab4a5ae6a5..d4aa67a4dc66 100644 --- a/drivers/clk/clk-si5341.c +++ b/drivers/clk/clk-si5341.c @@ -81,6 +81,7 @@ struct clk_si5341 { u8 num_synth; u16 chip_id; bool xaxb_ext_clk; + bool iovdd_33; }; #define to_clk_si5341(_hw) container_of(_hw, struct clk_si5341, hw) @@ -103,6 +104,7 @@ struct clk_si5341_output_config { #define SI5341_IN_SEL 0x0021 #define SI5341_DEVICE_READY 0x00FE #define SI5341_XAXB_CFG 0x090E +#define SI5341_IO_VDD_SEL 0x0943 #define SI5341_IN_EN 0x0949 #define SI5341_INX_TO_PFD_EN 0x094A @@ -351,7 +353,6 @@ static const struct si5341_reg_default si5341_reg_defaults[] = { { 0x0804, 0x00 }, /* Not in datasheet */ { 0x090E, 0x02 }, /* XAXB_EXTCLK_EN=0 XAXB_PDNB=1 (use XTAL) */ { 0x091C, 0x04 }, /* ZDM_EN=4 (Normal mode) */ - { 0x0943, 0x00 }, /* IO_VDD_SEL=0 (0=1v8, use 1=3v3) */ { 0x0949, 0x00 }, /* IN_EN (disable input clocks) */ { 0x094A, 0x00 }, /* INx_TO_PFD_EN (disabled) */ { 0x0A02, 0x00 }, /* Not in datasheet */ @@ -1160,6 +1161,11 @@ static int si5341_finalize_defaults(struct clk_si5341 *data) int res; u32 revision; + res = regmap_write(data->regmap, SI5341_IO_VDD_SEL, + data->iovdd_33 ? 1 : 0); + if (res < 0) + return res; + res = regmap_read(data->regmap, SI5341_DEVICE_REV, &revision); if (res < 0) return res; @@ -1544,6 +1550,8 @@ static int si5341_probe(struct i2c_client *client, } data->xaxb_ext_clk = of_property_read_bool(client->dev.of_node, "silabs,xaxb-ext-clk"); + data->iovdd_33 = of_property_read_bool(client->dev.of_node, + "silabs,iovdd-33"); if (initialization_required) { /* Populate the regmap cache in preparation for "cache only" */ From 9b13ff4340dff30f361462999a6a122fcc4e473f Mon Sep 17 00:00:00 2001 From: Robert Hancock Date: Thu, 25 Mar 2021 13:26:43 -0600 Subject: [PATCH 16/18] clk: si5341: Add sysfs properties to allow checking/resetting device faults Add sysfs property files to allow viewing the current and latched states of the input present and PLL lock bits, and allow resetting the latched fault state. This allows manual checks or automated userspace polling for faults occurring after initialization. Signed-off-by: Robert Hancock Link: https://lore.kernel.org/r/20210325192643.2190069-10-robert.hancock@calian.com Signed-off-by: Stephen Boyd --- drivers/clk/clk-si5341.c | 96 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 96 insertions(+) diff --git a/drivers/clk/clk-si5341.c b/drivers/clk/clk-si5341.c index d4aa67a4dc66..57ae183982d8 100644 --- a/drivers/clk/clk-si5341.c +++ b/drivers/clk/clk-si5341.c @@ -1457,6 +1457,94 @@ static int si5341_clk_select_active_input(struct clk_si5341 *data) return res; } +static ssize_t input_present_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct clk_si5341 *data = dev_get_drvdata(dev); + u32 status; + int res = regmap_read(data->regmap, SI5341_STATUS, &status); + + if (res < 0) + return res; + res = !(status & SI5341_STATUS_LOSREF); + return snprintf(buf, PAGE_SIZE, "%d\n", res); +} +static DEVICE_ATTR_RO(input_present); + +static ssize_t input_present_sticky_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct clk_si5341 *data = dev_get_drvdata(dev); + u32 status; + int res = regmap_read(data->regmap, SI5341_STATUS_STICKY, &status); + + if (res < 0) + return res; + res = !(status & SI5341_STATUS_LOSREF); + return snprintf(buf, PAGE_SIZE, "%d\n", res); +} +static DEVICE_ATTR_RO(input_present_sticky); + +static ssize_t pll_locked_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct clk_si5341 *data = dev_get_drvdata(dev); + u32 status; + int res = regmap_read(data->regmap, SI5341_STATUS, &status); + + if (res < 0) + return res; + res = !(status & SI5341_STATUS_LOL); + return snprintf(buf, PAGE_SIZE, "%d\n", res); +} +static DEVICE_ATTR_RO(pll_locked); + +static ssize_t pll_locked_sticky_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct clk_si5341 *data = dev_get_drvdata(dev); + u32 status; + int res = regmap_read(data->regmap, SI5341_STATUS_STICKY, &status); + + if (res < 0) + return res; + res = !(status & SI5341_STATUS_LOL); + return snprintf(buf, PAGE_SIZE, "%d\n", res); +} +static DEVICE_ATTR_RO(pll_locked_sticky); + +static ssize_t clear_sticky_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct clk_si5341 *data = dev_get_drvdata(dev); + long val; + + if (kstrtol(buf, 10, &val)) + return -EINVAL; + if (val) { + int res = regmap_write(data->regmap, SI5341_STATUS_STICKY, 0); + + if (res < 0) + return res; + } + return count; +} +static DEVICE_ATTR_WO(clear_sticky); + +static const struct attribute *si5341_attributes[] = { + &dev_attr_input_present.attr, + &dev_attr_input_present_sticky.attr, + &dev_attr_pll_locked.attr, + &dev_attr_pll_locked_sticky.attr, + &dev_attr_clear_sticky.attr, + NULL +}; + static int si5341_probe(struct i2c_client *client, const struct i2c_device_id *id) { @@ -1687,6 +1775,12 @@ static int si5341_probe(struct i2c_client *client, goto cleanup; } + err = sysfs_create_files(&client->dev.kobj, si5341_attributes); + if (err) { + dev_err(&client->dev, "unable to create sysfs files\n"); + goto cleanup; + } + /* Free the names, clk framework makes copies */ for (i = 0; i < data->num_synth; ++i) devm_kfree(&client->dev, (void *)synth_clock_names[i]); @@ -1706,6 +1800,8 @@ static int si5341_remove(struct i2c_client *client) struct clk_si5341 *data = i2c_get_clientdata(client); int i; + sysfs_remove_files(&client->dev.kobj, si5341_attributes); + for (i = 0; i < SI5341_MAX_NUM_OUTPUTS; ++i) { if (data->clk[i].vddo_reg) regulator_disable(data->clk[i].vddo_reg); From b87111da42f328c0e658e36e6782bb341ee742ea Mon Sep 17 00:00:00 2001 From: Dongjiu Geng Date: Tue, 23 Mar 2021 19:29:32 +0800 Subject: [PATCH 17/18] dt-bindings: Document the hi3559a clock bindings Add DT bindings documentation for hi3559a SoC clock. Signed-off-by: Dongjiu Geng Reviewed-by: Rob Herring Link: https://lore.kernel.org/r/1616498973-47067-2-git-send-email-gengdongjiu1@gmail.com Signed-off-by: Stephen Boyd --- .../clock/hisilicon,hi3559av100-clock.yaml | 59 +++++++ include/dt-bindings/clock/hi3559av100-clock.h | 165 ++++++++++++++++++ 2 files changed, 224 insertions(+) create mode 100644 Documentation/devicetree/bindings/clock/hisilicon,hi3559av100-clock.yaml create mode 100644 include/dt-bindings/clock/hi3559av100-clock.h diff --git a/Documentation/devicetree/bindings/clock/hisilicon,hi3559av100-clock.yaml b/Documentation/devicetree/bindings/clock/hisilicon,hi3559av100-clock.yaml new file mode 100644 index 000000000000..3ceb29cec704 --- /dev/null +++ b/Documentation/devicetree/bindings/clock/hisilicon,hi3559av100-clock.yaml @@ -0,0 +1,59 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/clock/hisilicon,hi3559av100-clock.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Hisilicon SOC Clock for HI3559AV100 + +maintainers: + - Dongjiu Geng + +description: | + Hisilicon SOC clock control module which supports the clocks, resets and + power domains on HI3559AV100. + + See also: + dt-bindings/clock/hi3559av100-clock.h + +properties: + compatible: + enum: + - hisilicon,hi3559av100-clock + - hisilicon,hi3559av100-shub-clock + + reg: + minItems: 1 + maxItems: 2 + + '#clock-cells': + const: 1 + + '#reset-cells': + const: 2 + description: | + First cell is reset request register offset. + Second cell is bit offset in reset request register. + +required: + - compatible + - reg + - '#clock-cells' + - '#reset-cells' + +additionalProperties: false + +examples: + - | + soc { + #address-cells = <2>; + #size-cells = <2>; + + clock-controller@12010000 { + compatible = "hisilicon,hi3559av100-clock"; + #clock-cells = <1>; + #reset-cells = <2>; + reg = <0x0 0x12010000 0x0 0x10000>; + }; + }; +... diff --git a/include/dt-bindings/clock/hi3559av100-clock.h b/include/dt-bindings/clock/hi3559av100-clock.h new file mode 100644 index 000000000000..5fe7689010a0 --- /dev/null +++ b/include/dt-bindings/clock/hi3559av100-clock.h @@ -0,0 +1,165 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later or BSD-2-Clause */ +/* + * Copyright (c) 2019-2020, Huawei Tech. Co., Ltd. + * + * Author: Dongjiu Geng + */ + +#ifndef __DTS_HI3559AV100_CLOCK_H +#define __DTS_HI3559AV100_CLOCK_H + +/* fixed rate */ +#define HI3559AV100_FIXED_1188M 1 +#define HI3559AV100_FIXED_1000M 2 +#define HI3559AV100_FIXED_842M 3 +#define HI3559AV100_FIXED_792M 4 +#define HI3559AV100_FIXED_750M 5 +#define HI3559AV100_FIXED_710M 6 +#define HI3559AV100_FIXED_680M 7 +#define HI3559AV100_FIXED_667M 8 +#define HI3559AV100_FIXED_631M 9 +#define HI3559AV100_FIXED_600M 10 +#define HI3559AV100_FIXED_568M 11 +#define HI3559AV100_FIXED_500M 12 +#define HI3559AV100_FIXED_475M 13 +#define HI3559AV100_FIXED_428M 14 +#define HI3559AV100_FIXED_400M 15 +#define HI3559AV100_FIXED_396M 16 +#define HI3559AV100_FIXED_300M 17 +#define HI3559AV100_FIXED_250M 18 +#define HI3559AV100_FIXED_198M 19 +#define HI3559AV100_FIXED_187p5M 20 +#define HI3559AV100_FIXED_150M 21 +#define HI3559AV100_FIXED_148p5M 22 +#define HI3559AV100_FIXED_125M 23 +#define HI3559AV100_FIXED_107M 24 +#define HI3559AV100_FIXED_100M 25 +#define HI3559AV100_FIXED_99M 26 +#define HI3559AV100_FIXED_74p25M 27 +#define HI3559AV100_FIXED_72M 28 +#define HI3559AV100_FIXED_60M 29 +#define HI3559AV100_FIXED_54M 30 +#define HI3559AV100_FIXED_50M 31 +#define HI3559AV100_FIXED_49p5M 32 +#define HI3559AV100_FIXED_37p125M 33 +#define HI3559AV100_FIXED_36M 34 +#define HI3559AV100_FIXED_32p4M 35 +#define HI3559AV100_FIXED_27M 36 +#define HI3559AV100_FIXED_25M 37 +#define HI3559AV100_FIXED_24M 38 +#define HI3559AV100_FIXED_12M 39 +#define HI3559AV100_FIXED_3M 40 +#define HI3559AV100_FIXED_1p6M 41 +#define HI3559AV100_FIXED_400K 42 +#define HI3559AV100_FIXED_100K 43 +#define HI3559AV100_FIXED_200M 44 +#define HI3559AV100_FIXED_75M 75 + +#define HI3559AV100_I2C0_CLK 50 +#define HI3559AV100_I2C1_CLK 51 +#define HI3559AV100_I2C2_CLK 52 +#define HI3559AV100_I2C3_CLK 53 +#define HI3559AV100_I2C4_CLK 54 +#define HI3559AV100_I2C5_CLK 55 +#define HI3559AV100_I2C6_CLK 56 +#define HI3559AV100_I2C7_CLK 57 +#define HI3559AV100_I2C8_CLK 58 +#define HI3559AV100_I2C9_CLK 59 +#define HI3559AV100_I2C10_CLK 60 +#define HI3559AV100_I2C11_CLK 61 + +#define HI3559AV100_SPI0_CLK 62 +#define HI3559AV100_SPI1_CLK 63 +#define HI3559AV100_SPI2_CLK 64 +#define HI3559AV100_SPI3_CLK 65 +#define HI3559AV100_SPI4_CLK 66 +#define HI3559AV100_SPI5_CLK 67 +#define HI3559AV100_SPI6_CLK 68 + +#define HI3559AV100_EDMAC_CLK 69 +#define HI3559AV100_EDMAC_AXICLK 70 +#define HI3559AV100_EDMAC1_CLK 71 +#define HI3559AV100_EDMAC1_AXICLK 72 +#define HI3559AV100_VDMAC_CLK 73 + +/* mux clocks */ +#define HI3559AV100_FMC_MUX 80 +#define HI3559AV100_SYSAPB_MUX 81 +#define HI3559AV100_UART_MUX 82 +#define HI3559AV100_SYSBUS_MUX 83 +#define HI3559AV100_A73_MUX 84 +#define HI3559AV100_MMC0_MUX 85 +#define HI3559AV100_MMC1_MUX 86 +#define HI3559AV100_MMC2_MUX 87 +#define HI3559AV100_MMC3_MUX 88 + +/* gate clocks */ +#define HI3559AV100_FMC_CLK 90 +#define HI3559AV100_UART0_CLK 91 +#define HI3559AV100_UART1_CLK 92 +#define HI3559AV100_UART2_CLK 93 +#define HI3559AV100_UART3_CLK 94 +#define HI3559AV100_UART4_CLK 95 +#define HI3559AV100_MMC0_CLK 96 +#define HI3559AV100_MMC1_CLK 97 +#define HI3559AV100_MMC2_CLK 98 +#define HI3559AV100_MMC3_CLK 99 + +#define HI3559AV100_ETH_CLK 100 +#define HI3559AV100_ETH_MACIF_CLK 101 +#define HI3559AV100_ETH1_CLK 102 +#define HI3559AV100_ETH1_MACIF_CLK 103 + +/* complex */ +#define HI3559AV100_MAC0_CLK 110 +#define HI3559AV100_MAC1_CLK 111 +#define HI3559AV100_SATA_CLK 112 +#define HI3559AV100_USB_CLK 113 +#define HI3559AV100_USB1_CLK 114 + +/* pll clocks */ +#define HI3559AV100_APLL_CLK 250 +#define HI3559AV100_GPLL_CLK 251 + +#define HI3559AV100_CRG_NR_CLKS 256 + +#define HI3559AV100_SHUB_SOURCE_SOC_24M 0 +#define HI3559AV100_SHUB_SOURCE_SOC_200M 1 +#define HI3559AV100_SHUB_SOURCE_SOC_300M 2 +#define HI3559AV100_SHUB_SOURCE_PLL 3 +#define HI3559AV100_SHUB_SOURCE_CLK 4 + +#define HI3559AV100_SHUB_I2C0_CLK 10 +#define HI3559AV100_SHUB_I2C1_CLK 11 +#define HI3559AV100_SHUB_I2C2_CLK 12 +#define HI3559AV100_SHUB_I2C3_CLK 13 +#define HI3559AV100_SHUB_I2C4_CLK 14 +#define HI3559AV100_SHUB_I2C5_CLK 15 +#define HI3559AV100_SHUB_I2C6_CLK 16 +#define HI3559AV100_SHUB_I2C7_CLK 17 + +#define HI3559AV100_SHUB_SPI_SOURCE_CLK 20 +#define HI3559AV100_SHUB_SPI4_SOURCE_CLK 21 +#define HI3559AV100_SHUB_SPI0_CLK 22 +#define HI3559AV100_SHUB_SPI1_CLK 23 +#define HI3559AV100_SHUB_SPI2_CLK 24 +#define HI3559AV100_SHUB_SPI3_CLK 25 +#define HI3559AV100_SHUB_SPI4_CLK 26 + +#define HI3559AV100_SHUB_UART_CLK_32K 30 +#define HI3559AV100_SHUB_UART_SOURCE_CLK 31 +#define HI3559AV100_SHUB_UART_DIV_CLK 32 +#define HI3559AV100_SHUB_UART0_CLK 33 +#define HI3559AV100_SHUB_UART1_CLK 34 +#define HI3559AV100_SHUB_UART2_CLK 35 +#define HI3559AV100_SHUB_UART3_CLK 36 +#define HI3559AV100_SHUB_UART4_CLK 37 +#define HI3559AV100_SHUB_UART5_CLK 38 +#define HI3559AV100_SHUB_UART6_CLK 39 + +#define HI3559AV100_SHUB_EDMAC_CLK 40 + +#define HI3559AV100_SHUB_NR_CLKS 50 + +#endif /* __DTS_HI3559AV100_CLOCK_H */ + From 6c81966107dc0caa5d2ebedbcebb5f10d865064d Mon Sep 17 00:00:00 2001 From: Dongjiu Geng Date: Tue, 23 Mar 2021 19:29:33 +0800 Subject: [PATCH 18/18] clk: hisilicon: Add clock driver for hi3559A SoC Add clock drivers for hi3559A SoC, this driver controls the SoC registers to supply different clocks to different IPs in the SoC. Signed-off-by: Dongjiu Geng Link: https://lore.kernel.org/r/1616498973-47067-3-git-send-email-gengdongjiu1@gmail.com [sboyd@kernel.org: Mark arrays static, add __iomem, drop unused array, avoid kfree of devm memory] Signed-off-by: Stephen Boyd --- drivers/clk/hisilicon/Kconfig | 7 + drivers/clk/hisilicon/Makefile | 1 + drivers/clk/hisilicon/clk-hi3559a.c | 846 ++++++++++++++++++++++++++++ drivers/clk/hisilicon/clk.c | 2 +- drivers/clk/hisilicon/clk.h | 2 +- 5 files changed, 856 insertions(+), 2 deletions(-) create mode 100644 drivers/clk/hisilicon/clk-hi3559a.c diff --git a/drivers/clk/hisilicon/Kconfig b/drivers/clk/hisilicon/Kconfig index 6a9e93a0bb95..5ecc37aaa118 100644 --- a/drivers/clk/hisilicon/Kconfig +++ b/drivers/clk/hisilicon/Kconfig @@ -15,6 +15,13 @@ config COMMON_CLK_HI3519 help Build the clock driver for hi3519. +config COMMON_CLK_HI3559A + bool "Hi3559A Clock Driver" + depends on ARCH_HISI || COMPILE_TEST + default ARCH_HISI + help + Build the clock driver for hi3559a. + config COMMON_CLK_HI3660 bool "Hi3660 Clock Driver" depends on ARCH_HISI || COMPILE_TEST diff --git a/drivers/clk/hisilicon/Makefile b/drivers/clk/hisilicon/Makefile index b2441b99f3d5..2978e56cb876 100644 --- a/drivers/clk/hisilicon/Makefile +++ b/drivers/clk/hisilicon/Makefile @@ -10,6 +10,7 @@ obj-$(CONFIG_ARCH_HIP04) += clk-hip04.o obj-$(CONFIG_ARCH_HIX5HD2) += clk-hix5hd2.o obj-$(CONFIG_COMMON_CLK_HI3516CV300) += crg-hi3516cv300.o obj-$(CONFIG_COMMON_CLK_HI3519) += clk-hi3519.o +obj-$(CONFIG_COMMON_CLK_HI3559A) += clk-hi3559a.o obj-$(CONFIG_COMMON_CLK_HI3660) += clk-hi3660.o obj-$(CONFIG_COMMON_CLK_HI3670) += clk-hi3670.o obj-$(CONFIG_COMMON_CLK_HI3798CV200) += crg-hi3798cv200.o diff --git a/drivers/clk/hisilicon/clk-hi3559a.c b/drivers/clk/hisilicon/clk-hi3559a.c new file mode 100644 index 000000000000..b1f19c43b558 --- /dev/null +++ b/drivers/clk/hisilicon/clk-hi3559a.c @@ -0,0 +1,846 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Hisilicon Hi3559A clock driver + * + * Copyright (c) 2019-2020, Huawei Tech. Co., Ltd. + * + * Author: Dongjiu Geng + */ + +#include +#include +#include +#include +#include + +#include + +#include "clk.h" +#include "crg.h" +#include "reset.h" + +#define CRG_BASE_ADDR 0x18020000 +#define PLL_MASK_WIDTH 24 + +struct hi3559av100_pll_clock { + u32 id; + const char *name; + const char *parent_name; + const u32 ctrl_reg1; + const u8 frac_shift; + const u8 frac_width; + const u8 postdiv1_shift; + const u8 postdiv1_width; + const u8 postdiv2_shift; + const u8 postdiv2_width; + const u32 ctrl_reg2; + const u8 fbdiv_shift; + const u8 fbdiv_width; + const u8 refdiv_shift; + const u8 refdiv_width; +}; + +struct hi3559av100_clk_pll { + struct clk_hw hw; + u32 id; + void __iomem *ctrl_reg1; + u8 frac_shift; + u8 frac_width; + u8 postdiv1_shift; + u8 postdiv1_width; + u8 postdiv2_shift; + u8 postdiv2_width; + void __iomem *ctrl_reg2; + u8 fbdiv_shift; + u8 fbdiv_width; + u8 refdiv_shift; + u8 refdiv_width; +}; + +/* soc clk config */ +static const struct hisi_fixed_rate_clock hi3559av100_fixed_rate_clks_crg[] = { + { HI3559AV100_FIXED_1188M, "1188m", NULL, 0, 1188000000, }, + { HI3559AV100_FIXED_1000M, "1000m", NULL, 0, 1000000000, }, + { HI3559AV100_FIXED_842M, "842m", NULL, 0, 842000000, }, + { HI3559AV100_FIXED_792M, "792m", NULL, 0, 792000000, }, + { HI3559AV100_FIXED_750M, "750m", NULL, 0, 750000000, }, + { HI3559AV100_FIXED_710M, "710m", NULL, 0, 710000000, }, + { HI3559AV100_FIXED_680M, "680m", NULL, 0, 680000000, }, + { HI3559AV100_FIXED_667M, "667m", NULL, 0, 667000000, }, + { HI3559AV100_FIXED_631M, "631m", NULL, 0, 631000000, }, + { HI3559AV100_FIXED_600M, "600m", NULL, 0, 600000000, }, + { HI3559AV100_FIXED_568M, "568m", NULL, 0, 568000000, }, + { HI3559AV100_FIXED_500M, "500m", NULL, 0, 500000000, }, + { HI3559AV100_FIXED_475M, "475m", NULL, 0, 475000000, }, + { HI3559AV100_FIXED_428M, "428m", NULL, 0, 428000000, }, + { HI3559AV100_FIXED_400M, "400m", NULL, 0, 400000000, }, + { HI3559AV100_FIXED_396M, "396m", NULL, 0, 396000000, }, + { HI3559AV100_FIXED_300M, "300m", NULL, 0, 300000000, }, + { HI3559AV100_FIXED_250M, "250m", NULL, 0, 250000000, }, + { HI3559AV100_FIXED_200M, "200m", NULL, 0, 200000000, }, + { HI3559AV100_FIXED_198M, "198m", NULL, 0, 198000000, }, + { HI3559AV100_FIXED_187p5M, "187p5m", NULL, 0, 187500000, }, + { HI3559AV100_FIXED_150M, "150m", NULL, 0, 150000000, }, + { HI3559AV100_FIXED_148p5M, "148p5m", NULL, 0, 1485000000, }, + { HI3559AV100_FIXED_125M, "125m", NULL, 0, 125000000, }, + { HI3559AV100_FIXED_107M, "107m", NULL, 0, 107000000, }, + { HI3559AV100_FIXED_100M, "100m", NULL, 0, 100000000, }, + { HI3559AV100_FIXED_99M, "99m", NULL, 0, 99000000, }, + { HI3559AV100_FIXED_75M, "75m", NULL, 0, 75000000, }, + { HI3559AV100_FIXED_74p25M, "74p25m", NULL, 0, 74250000, }, + { HI3559AV100_FIXED_72M, "72m", NULL, 0, 72000000, }, + { HI3559AV100_FIXED_60M, "60m", NULL, 0, 60000000, }, + { HI3559AV100_FIXED_54M, "54m", NULL, 0, 54000000, }, + { HI3559AV100_FIXED_50M, "50m", NULL, 0, 50000000, }, + { HI3559AV100_FIXED_49p5M, "49p5m", NULL, 0, 49500000, }, + { HI3559AV100_FIXED_37p125M, "37p125m", NULL, 0, 37125000, }, + { HI3559AV100_FIXED_36M, "36m", NULL, 0, 36000000, }, + { HI3559AV100_FIXED_32p4M, "32p4m", NULL, 0, 32400000, }, + { HI3559AV100_FIXED_27M, "27m", NULL, 0, 27000000, }, + { HI3559AV100_FIXED_25M, "25m", NULL, 0, 25000000, }, + { HI3559AV100_FIXED_24M, "24m", NULL, 0, 24000000, }, + { HI3559AV100_FIXED_12M, "12m", NULL, 0, 12000000, }, + { HI3559AV100_FIXED_3M, "3m", NULL, 0, 3000000, }, + { HI3559AV100_FIXED_1p6M, "1p6m", NULL, 0, 1600000, }, + { HI3559AV100_FIXED_400K, "400k", NULL, 0, 400000, }, + { HI3559AV100_FIXED_100K, "100k", NULL, 0, 100000, }, +}; + + +static const char *fmc_mux_p[] __initconst = { + "24m", "75m", "125m", "150m", "200m", "250m", "300m", "400m" +}; + +static const char *mmc_mux_p[] __initconst = { + "100k", "25m", "49p5m", "99m", "187p5m", "150m", "198m", "400k" +}; + +static const char *sysapb_mux_p[] __initconst = { + "24m", "50m", +}; + +static const char *sysbus_mux_p[] __initconst = { + "24m", "300m" +}; + +static const char *uart_mux_p[] __initconst = { "50m", "24m", "3m" }; + +static const char *a73_clksel_mux_p[] __initconst = { + "24m", "apll", "1000m" +}; + +static const u32 fmc_mux_table[] = { 0, 1, 2, 3, 4, 5, 6, 7 }; +static const u32 mmc_mux_table[] = { 0, 1, 2, 3, 4, 5, 6, 7 }; +static const u32 sysapb_mux_table[] = { 0, 1 }; +static const u32 sysbus_mux_table[] = { 0, 1 }; +static const u32 uart_mux_table[] = { 0, 1, 2 }; +static const u32 a73_clksel_mux_table[] = { 0, 1, 2 }; + +static struct hisi_mux_clock hi3559av100_mux_clks_crg[] __initdata = { + { + HI3559AV100_FMC_MUX, "fmc_mux", fmc_mux_p, ARRAY_SIZE(fmc_mux_p), + CLK_SET_RATE_PARENT, 0x170, 2, 3, 0, fmc_mux_table, + }, + { + HI3559AV100_MMC0_MUX, "mmc0_mux", mmc_mux_p, ARRAY_SIZE(mmc_mux_p), + CLK_SET_RATE_PARENT, 0x1a8, 24, 3, 0, mmc_mux_table, + }, + { + HI3559AV100_MMC1_MUX, "mmc1_mux", mmc_mux_p, ARRAY_SIZE(mmc_mux_p), + CLK_SET_RATE_PARENT, 0x1ec, 24, 3, 0, mmc_mux_table, + }, + + { + HI3559AV100_MMC2_MUX, "mmc2_mux", mmc_mux_p, ARRAY_SIZE(mmc_mux_p), + CLK_SET_RATE_PARENT, 0x214, 24, 3, 0, mmc_mux_table, + }, + + { + HI3559AV100_MMC3_MUX, "mmc3_mux", mmc_mux_p, ARRAY_SIZE(mmc_mux_p), + CLK_SET_RATE_PARENT, 0x23c, 24, 3, 0, mmc_mux_table, + }, + + { + HI3559AV100_SYSAPB_MUX, "sysapb_mux", sysapb_mux_p, ARRAY_SIZE(sysapb_mux_p), + CLK_SET_RATE_PARENT, 0xe8, 3, 1, 0, sysapb_mux_table + }, + + { + HI3559AV100_SYSBUS_MUX, "sysbus_mux", sysbus_mux_p, ARRAY_SIZE(sysbus_mux_p), + CLK_SET_RATE_PARENT, 0xe8, 0, 1, 0, sysbus_mux_table + }, + + { + HI3559AV100_UART_MUX, "uart_mux", uart_mux_p, ARRAY_SIZE(uart_mux_p), + CLK_SET_RATE_PARENT, 0x198, 28, 2, 0, uart_mux_table + }, + + { + HI3559AV100_A73_MUX, "a73_mux", a73_clksel_mux_p, ARRAY_SIZE(a73_clksel_mux_p), + CLK_SET_RATE_PARENT, 0xe4, 0, 2, 0, a73_clksel_mux_table + }, +}; + +static struct hisi_gate_clock hi3559av100_gate_clks[] __initdata = { + { + HI3559AV100_FMC_CLK, "clk_fmc", "fmc_mux", + CLK_SET_RATE_PARENT, 0x170, 1, 0, + }, + { + HI3559AV100_MMC0_CLK, "clk_mmc0", "mmc0_mux", + CLK_SET_RATE_PARENT, 0x1a8, 28, 0, + }, + { + HI3559AV100_MMC1_CLK, "clk_mmc1", "mmc1_mux", + CLK_SET_RATE_PARENT, 0x1ec, 28, 0, + }, + { + HI3559AV100_MMC2_CLK, "clk_mmc2", "mmc2_mux", + CLK_SET_RATE_PARENT, 0x214, 28, 0, + }, + { + HI3559AV100_MMC3_CLK, "clk_mmc3", "mmc3_mux", + CLK_SET_RATE_PARENT, 0x23c, 28, 0, + }, + { + HI3559AV100_UART0_CLK, "clk_uart0", "uart_mux", + CLK_SET_RATE_PARENT, 0x198, 23, 0, + }, + { + HI3559AV100_UART1_CLK, "clk_uart1", "uart_mux", + CLK_SET_RATE_PARENT, 0x198, 24, 0, + }, + { + HI3559AV100_UART2_CLK, "clk_uart2", "uart_mux", + CLK_SET_RATE_PARENT, 0x198, 25, 0, + }, + { + HI3559AV100_UART3_CLK, "clk_uart3", "uart_mux", + CLK_SET_RATE_PARENT, 0x198, 26, 0, + }, + { + HI3559AV100_UART4_CLK, "clk_uart4", "uart_mux", + CLK_SET_RATE_PARENT, 0x198, 27, 0, + }, + { + HI3559AV100_ETH_CLK, "clk_eth", NULL, + CLK_SET_RATE_PARENT, 0x0174, 1, 0, + }, + { + HI3559AV100_ETH_MACIF_CLK, "clk_eth_macif", NULL, + CLK_SET_RATE_PARENT, 0x0174, 5, 0, + }, + { + HI3559AV100_ETH1_CLK, "clk_eth1", NULL, + CLK_SET_RATE_PARENT, 0x0174, 3, 0, + }, + { + HI3559AV100_ETH1_MACIF_CLK, "clk_eth1_macif", NULL, + CLK_SET_RATE_PARENT, 0x0174, 7, 0, + }, + { + HI3559AV100_I2C0_CLK, "clk_i2c0", "50m", + CLK_SET_RATE_PARENT, 0x01a0, 16, 0, + }, + { + HI3559AV100_I2C1_CLK, "clk_i2c1", "50m", + CLK_SET_RATE_PARENT, 0x01a0, 17, 0, + }, + { + HI3559AV100_I2C2_CLK, "clk_i2c2", "50m", + CLK_SET_RATE_PARENT, 0x01a0, 18, 0, + }, + { + HI3559AV100_I2C3_CLK, "clk_i2c3", "50m", + CLK_SET_RATE_PARENT, 0x01a0, 19, 0, + }, + { + HI3559AV100_I2C4_CLK, "clk_i2c4", "50m", + CLK_SET_RATE_PARENT, 0x01a0, 20, 0, + }, + { + HI3559AV100_I2C5_CLK, "clk_i2c5", "50m", + CLK_SET_RATE_PARENT, 0x01a0, 21, 0, + }, + { + HI3559AV100_I2C6_CLK, "clk_i2c6", "50m", + CLK_SET_RATE_PARENT, 0x01a0, 22, 0, + }, + { + HI3559AV100_I2C7_CLK, "clk_i2c7", "50m", + CLK_SET_RATE_PARENT, 0x01a0, 23, 0, + }, + { + HI3559AV100_I2C8_CLK, "clk_i2c8", "50m", + CLK_SET_RATE_PARENT, 0x01a0, 24, 0, + }, + { + HI3559AV100_I2C9_CLK, "clk_i2c9", "50m", + CLK_SET_RATE_PARENT, 0x01a0, 25, 0, + }, + { + HI3559AV100_I2C10_CLK, "clk_i2c10", "50m", + CLK_SET_RATE_PARENT, 0x01a0, 26, 0, + }, + { + HI3559AV100_I2C11_CLK, "clk_i2c11", "50m", + CLK_SET_RATE_PARENT, 0x01a0, 27, 0, + }, + { + HI3559AV100_SPI0_CLK, "clk_spi0", "100m", + CLK_SET_RATE_PARENT, 0x0198, 16, 0, + }, + { + HI3559AV100_SPI1_CLK, "clk_spi1", "100m", + CLK_SET_RATE_PARENT, 0x0198, 17, 0, + }, + { + HI3559AV100_SPI2_CLK, "clk_spi2", "100m", + CLK_SET_RATE_PARENT, 0x0198, 18, 0, + }, + { + HI3559AV100_SPI3_CLK, "clk_spi3", "100m", + CLK_SET_RATE_PARENT, 0x0198, 19, 0, + }, + { + HI3559AV100_SPI4_CLK, "clk_spi4", "100m", + CLK_SET_RATE_PARENT, 0x0198, 20, 0, + }, + { + HI3559AV100_SPI5_CLK, "clk_spi5", "100m", + CLK_SET_RATE_PARENT, 0x0198, 21, 0, + }, + { + HI3559AV100_SPI6_CLK, "clk_spi6", "100m", + CLK_SET_RATE_PARENT, 0x0198, 22, 0, + }, + { + HI3559AV100_EDMAC_AXICLK, "axi_clk_edmac", NULL, + CLK_SET_RATE_PARENT, 0x16c, 6, 0, + }, + { + HI3559AV100_EDMAC_CLK, "clk_edmac", NULL, + CLK_SET_RATE_PARENT, 0x16c, 5, 0, + }, + { + HI3559AV100_EDMAC1_AXICLK, "axi_clk_edmac1", NULL, + CLK_SET_RATE_PARENT, 0x16c, 9, 0, + }, + { + HI3559AV100_EDMAC1_CLK, "clk_edmac1", NULL, + CLK_SET_RATE_PARENT, 0x16c, 8, 0, + }, + { + HI3559AV100_VDMAC_CLK, "clk_vdmac", NULL, + CLK_SET_RATE_PARENT, 0x14c, 5, 0, + }, +}; + +static struct hi3559av100_pll_clock hi3559av100_pll_clks[] __initdata = { + { + HI3559AV100_APLL_CLK, "apll", NULL, 0x0, 0, 24, 24, 3, 28, 3, + 0x4, 0, 12, 12, 6 + }, + { + HI3559AV100_GPLL_CLK, "gpll", NULL, 0x20, 0, 24, 24, 3, 28, 3, + 0x24, 0, 12, 12, 6 + }, +}; + +#define to_pll_clk(_hw) container_of(_hw, struct hi3559av100_clk_pll, hw) +static void hi3559av100_calc_pll(u32 *frac_val, u32 *postdiv1_val, + u32 *postdiv2_val, + u32 *fbdiv_val, u32 *refdiv_val, u64 rate) +{ + u64 rem; + + *postdiv1_val = 2; + *postdiv2_val = 1; + + rate = rate * ((*postdiv1_val) * (*postdiv2_val)); + + *frac_val = 0; + rem = do_div(rate, 1000000); + rem = do_div(rate, PLL_MASK_WIDTH); + *fbdiv_val = rate; + *refdiv_val = 1; + rem = rem * (1 << PLL_MASK_WIDTH); + do_div(rem, PLL_MASK_WIDTH); + *frac_val = rem; +} + +static int clk_pll_set_rate(struct clk_hw *hw, + unsigned long rate, + unsigned long parent_rate) +{ + struct hi3559av100_clk_pll *clk = to_pll_clk(hw); + u32 frac_val, postdiv1_val, postdiv2_val, fbdiv_val, refdiv_val; + u32 val; + + postdiv1_val = postdiv2_val = 0; + + hi3559av100_calc_pll(&frac_val, &postdiv1_val, &postdiv2_val, + &fbdiv_val, &refdiv_val, (u64)rate); + + val = readl_relaxed(clk->ctrl_reg1); + val &= ~(((1 << clk->frac_width) - 1) << clk->frac_shift); + val &= ~(((1 << clk->postdiv1_width) - 1) << clk->postdiv1_shift); + val &= ~(((1 << clk->postdiv2_width) - 1) << clk->postdiv2_shift); + + val |= frac_val << clk->frac_shift; + val |= postdiv1_val << clk->postdiv1_shift; + val |= postdiv2_val << clk->postdiv2_shift; + writel_relaxed(val, clk->ctrl_reg1); + + val = readl_relaxed(clk->ctrl_reg2); + val &= ~(((1 << clk->fbdiv_width) - 1) << clk->fbdiv_shift); + val &= ~(((1 << clk->refdiv_width) - 1) << clk->refdiv_shift); + + val |= fbdiv_val << clk->fbdiv_shift; + val |= refdiv_val << clk->refdiv_shift; + writel_relaxed(val, clk->ctrl_reg2); + + return 0; +} + +static unsigned long clk_pll_recalc_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + struct hi3559av100_clk_pll *clk = to_pll_clk(hw); + u64 frac_val, fbdiv_val, refdiv_val; + u32 postdiv1_val, postdiv2_val; + u32 val; + u64 tmp, rate; + + val = readl_relaxed(clk->ctrl_reg1); + val = val >> clk->frac_shift; + val &= ((1 << clk->frac_width) - 1); + frac_val = val; + + val = readl_relaxed(clk->ctrl_reg1); + val = val >> clk->postdiv1_shift; + val &= ((1 << clk->postdiv1_width) - 1); + postdiv1_val = val; + + val = readl_relaxed(clk->ctrl_reg1); + val = val >> clk->postdiv2_shift; + val &= ((1 << clk->postdiv2_width) - 1); + postdiv2_val = val; + + val = readl_relaxed(clk->ctrl_reg2); + val = val >> clk->fbdiv_shift; + val &= ((1 << clk->fbdiv_width) - 1); + fbdiv_val = val; + + val = readl_relaxed(clk->ctrl_reg2); + val = val >> clk->refdiv_shift; + val &= ((1 << clk->refdiv_width) - 1); + refdiv_val = val; + + /* rate = 24000000 * (fbdiv + frac / (1<<24) ) / refdiv */ + rate = 0; + tmp = 24000000 * fbdiv_val + (24000000 * frac_val) / (1 << 24); + rate += tmp; + do_div(rate, refdiv_val); + do_div(rate, postdiv1_val * postdiv2_val); + + return rate; +} + +static const struct clk_ops hisi_clk_pll_ops = { + .set_rate = clk_pll_set_rate, + .recalc_rate = clk_pll_recalc_rate, +}; + +static void hisi_clk_register_pll(struct hi3559av100_pll_clock *clks, + int nums, struct hisi_clock_data *data, struct device *dev) +{ + void __iomem *base = data->base; + struct hi3559av100_clk_pll *p_clk = NULL; + struct clk *clk = NULL; + struct clk_init_data init; + int i; + + p_clk = devm_kzalloc(dev, sizeof(*p_clk) * nums, GFP_KERNEL); + + if (!p_clk) + return; + + for (i = 0; i < nums; i++) { + init.name = clks[i].name; + init.flags = 0; + init.parent_names = + (clks[i].parent_name ? &clks[i].parent_name : NULL); + init.num_parents = (clks[i].parent_name ? 1 : 0); + init.ops = &hisi_clk_pll_ops; + + p_clk->ctrl_reg1 = base + clks[i].ctrl_reg1; + p_clk->frac_shift = clks[i].frac_shift; + p_clk->frac_width = clks[i].frac_width; + p_clk->postdiv1_shift = clks[i].postdiv1_shift; + p_clk->postdiv1_width = clks[i].postdiv1_width; + p_clk->postdiv2_shift = clks[i].postdiv2_shift; + p_clk->postdiv2_width = clks[i].postdiv2_width; + + p_clk->ctrl_reg2 = base + clks[i].ctrl_reg2; + p_clk->fbdiv_shift = clks[i].fbdiv_shift; + p_clk->fbdiv_width = clks[i].fbdiv_width; + p_clk->refdiv_shift = clks[i].refdiv_shift; + p_clk->refdiv_width = clks[i].refdiv_width; + p_clk->hw.init = &init; + + clk = clk_register(NULL, &p_clk->hw); + if (IS_ERR(clk)) { + devm_kfree(dev, p_clk); + dev_err(dev, "%s: failed to register clock %s\n", + __func__, clks[i].name); + continue; + } + + data->clk_data.clks[clks[i].id] = clk; + p_clk++; + } +} + +static __init struct hisi_clock_data *hi3559av100_clk_register( + struct platform_device *pdev) +{ + struct hisi_clock_data *clk_data; + int ret; + + clk_data = hisi_clk_alloc(pdev, HI3559AV100_CRG_NR_CLKS); + if (!clk_data) + return ERR_PTR(-ENOMEM); + + ret = hisi_clk_register_fixed_rate(hi3559av100_fixed_rate_clks_crg, + ARRAY_SIZE(hi3559av100_fixed_rate_clks_crg), clk_data); + if (ret) + return ERR_PTR(ret); + + hisi_clk_register_pll(hi3559av100_pll_clks, + ARRAY_SIZE(hi3559av100_pll_clks), clk_data, &pdev->dev); + + ret = hisi_clk_register_mux(hi3559av100_mux_clks_crg, + ARRAY_SIZE(hi3559av100_mux_clks_crg), clk_data); + if (ret) + goto unregister_fixed_rate; + + ret = hisi_clk_register_gate(hi3559av100_gate_clks, + ARRAY_SIZE(hi3559av100_gate_clks), clk_data); + if (ret) + goto unregister_mux; + + ret = of_clk_add_provider(pdev->dev.of_node, + of_clk_src_onecell_get, &clk_data->clk_data); + if (ret) + goto unregister_gate; + + return clk_data; + +unregister_gate: + hisi_clk_unregister_gate(hi3559av100_gate_clks, + ARRAY_SIZE(hi3559av100_gate_clks), clk_data); +unregister_mux: + hisi_clk_unregister_mux(hi3559av100_mux_clks_crg, + ARRAY_SIZE(hi3559av100_mux_clks_crg), clk_data); +unregister_fixed_rate: + hisi_clk_unregister_fixed_rate(hi3559av100_fixed_rate_clks_crg, + ARRAY_SIZE(hi3559av100_fixed_rate_clks_crg), clk_data); + return ERR_PTR(ret); +} + +static __init void hi3559av100_clk_unregister(struct platform_device *pdev) +{ + struct hisi_crg_dev *crg = platform_get_drvdata(pdev); + + of_clk_del_provider(pdev->dev.of_node); + + hisi_clk_unregister_gate(hi3559av100_gate_clks, + ARRAY_SIZE(hi3559av100_gate_clks), crg->clk_data); + hisi_clk_unregister_mux(hi3559av100_mux_clks_crg, + ARRAY_SIZE(hi3559av100_mux_clks_crg), crg->clk_data); + hisi_clk_unregister_fixed_rate(hi3559av100_fixed_rate_clks_crg, + ARRAY_SIZE(hi3559av100_fixed_rate_clks_crg), crg->clk_data); +} + +static const struct hisi_crg_funcs hi3559av100_crg_funcs = { + .register_clks = hi3559av100_clk_register, + .unregister_clks = hi3559av100_clk_unregister, +}; + +static struct hisi_fixed_rate_clock hi3559av100_shub_fixed_rate_clks[] + __initdata = { + { HI3559AV100_SHUB_SOURCE_SOC_24M, "clk_source_24M", NULL, 0, 24000000UL, }, + { HI3559AV100_SHUB_SOURCE_SOC_200M, "clk_source_200M", NULL, 0, 200000000UL, }, + { HI3559AV100_SHUB_SOURCE_SOC_300M, "clk_source_300M", NULL, 0, 300000000UL, }, + { HI3559AV100_SHUB_SOURCE_PLL, "clk_source_PLL", NULL, 0, 192000000UL, }, + { HI3559AV100_SHUB_I2C0_CLK, "clk_shub_i2c0", NULL, 0, 48000000UL, }, + { HI3559AV100_SHUB_I2C1_CLK, "clk_shub_i2c1", NULL, 0, 48000000UL, }, + { HI3559AV100_SHUB_I2C2_CLK, "clk_shub_i2c2", NULL, 0, 48000000UL, }, + { HI3559AV100_SHUB_I2C3_CLK, "clk_shub_i2c3", NULL, 0, 48000000UL, }, + { HI3559AV100_SHUB_I2C4_CLK, "clk_shub_i2c4", NULL, 0, 48000000UL, }, + { HI3559AV100_SHUB_I2C5_CLK, "clk_shub_i2c5", NULL, 0, 48000000UL, }, + { HI3559AV100_SHUB_I2C6_CLK, "clk_shub_i2c6", NULL, 0, 48000000UL, }, + { HI3559AV100_SHUB_I2C7_CLK, "clk_shub_i2c7", NULL, 0, 48000000UL, }, + { HI3559AV100_SHUB_UART_CLK_32K, "clk_uart_32K", NULL, 0, 32000UL, }, +}; + +/* shub mux clk */ +static u32 shub_source_clk_mux_table[] = {0, 1, 2, 3}; +static const char *shub_source_clk_mux_p[] __initconst = { + "clk_source_24M", "clk_source_200M", "clk_source_300M", "clk_source_PLL" +}; + +static u32 shub_uart_source_clk_mux_table[] = {0, 1, 2, 3}; +static const char *shub_uart_source_clk_mux_p[] __initconst = { + "clk_uart_32K", "clk_uart_div_clk", "clk_uart_div_clk", "clk_source_24M" +}; + +static struct hisi_mux_clock hi3559av100_shub_mux_clks[] __initdata = { + { + HI3559AV100_SHUB_SOURCE_CLK, "shub_clk", shub_source_clk_mux_p, + ARRAY_SIZE(shub_source_clk_mux_p), + 0, 0x0, 0, 2, 0, shub_source_clk_mux_table, + }, + + { + HI3559AV100_SHUB_UART_SOURCE_CLK, "shub_uart_source_clk", + shub_uart_source_clk_mux_p, ARRAY_SIZE(shub_uart_source_clk_mux_p), + 0, 0x1c, 28, 2, 0, shub_uart_source_clk_mux_table, + }, +}; + + +/* shub div clk */ +static struct clk_div_table shub_spi_clk_table[] = {{0, 8}, {1, 4}, {2, 2}}; +static struct clk_div_table shub_uart_div_clk_table[] = {{1, 8}, {2, 4}}; + +static struct hisi_divider_clock hi3559av100_shub_div_clks[] __initdata = { + { HI3559AV100_SHUB_SPI_SOURCE_CLK, "clk_spi_clk", "shub_clk", 0, 0x20, 24, 2, + CLK_DIVIDER_ALLOW_ZERO, shub_spi_clk_table, + }, + { HI3559AV100_SHUB_UART_DIV_CLK, "clk_uart_div_clk", "shub_clk", 0, 0x1c, 28, 2, + CLK_DIVIDER_ALLOW_ZERO, shub_uart_div_clk_table, + }, +}; + +/* shub gate clk */ +static struct hisi_gate_clock hi3559av100_shub_gate_clks[] __initdata = { + { + HI3559AV100_SHUB_SPI0_CLK, "clk_shub_spi0", "clk_spi_clk", + 0, 0x20, 1, 0, + }, + { + HI3559AV100_SHUB_SPI1_CLK, "clk_shub_spi1", "clk_spi_clk", + 0, 0x20, 5, 0, + }, + { + HI3559AV100_SHUB_SPI2_CLK, "clk_shub_spi2", "clk_spi_clk", + 0, 0x20, 9, 0, + }, + + { + HI3559AV100_SHUB_UART0_CLK, "clk_shub_uart0", "shub_uart_source_clk", + 0, 0x1c, 1, 0, + }, + { + HI3559AV100_SHUB_UART1_CLK, "clk_shub_uart1", "shub_uart_source_clk", + 0, 0x1c, 5, 0, + }, + { + HI3559AV100_SHUB_UART2_CLK, "clk_shub_uart2", "shub_uart_source_clk", + 0, 0x1c, 9, 0, + }, + { + HI3559AV100_SHUB_UART3_CLK, "clk_shub_uart3", "shub_uart_source_clk", + 0, 0x1c, 13, 0, + }, + { + HI3559AV100_SHUB_UART4_CLK, "clk_shub_uart4", "shub_uart_source_clk", + 0, 0x1c, 17, 0, + }, + { + HI3559AV100_SHUB_UART5_CLK, "clk_shub_uart5", "shub_uart_source_clk", + 0, 0x1c, 21, 0, + }, + { + HI3559AV100_SHUB_UART6_CLK, "clk_shub_uart6", "shub_uart_source_clk", + 0, 0x1c, 25, 0, + }, + + { + HI3559AV100_SHUB_EDMAC_CLK, "clk_shub_dmac", "shub_clk", + 0, 0x24, 4, 0, + }, +}; + +static int hi3559av100_shub_default_clk_set(void) +{ + void __iomem *crg_base; + unsigned int val; + + crg_base = ioremap(CRG_BASE_ADDR, SZ_4K); + + /* SSP: 192M/2 */ + val = readl_relaxed(crg_base + 0x20); + val |= (0x2 << 24); + writel_relaxed(val, crg_base + 0x20); + + /* UART: 192M/8 */ + val = readl_relaxed(crg_base + 0x1C); + val |= (0x1 << 28); + writel_relaxed(val, crg_base + 0x1C); + + iounmap(crg_base); + crg_base = NULL; + + return 0; +} + +static __init struct hisi_clock_data *hi3559av100_shub_clk_register( + struct platform_device *pdev) +{ + struct hisi_clock_data *clk_data = NULL; + int ret; + + hi3559av100_shub_default_clk_set(); + + clk_data = hisi_clk_alloc(pdev, HI3559AV100_SHUB_NR_CLKS); + if (!clk_data) + return ERR_PTR(-ENOMEM); + + ret = hisi_clk_register_fixed_rate(hi3559av100_shub_fixed_rate_clks, + ARRAY_SIZE(hi3559av100_shub_fixed_rate_clks), clk_data); + if (ret) + return ERR_PTR(ret); + + ret = hisi_clk_register_mux(hi3559av100_shub_mux_clks, + ARRAY_SIZE(hi3559av100_shub_mux_clks), clk_data); + if (ret) + goto unregister_fixed_rate; + + ret = hisi_clk_register_divider(hi3559av100_shub_div_clks, + ARRAY_SIZE(hi3559av100_shub_div_clks), clk_data); + if (ret) + goto unregister_mux; + + ret = hisi_clk_register_gate(hi3559av100_shub_gate_clks, + ARRAY_SIZE(hi3559av100_shub_gate_clks), clk_data); + if (ret) + goto unregister_factor; + + ret = of_clk_add_provider(pdev->dev.of_node, + of_clk_src_onecell_get, &clk_data->clk_data); + if (ret) + goto unregister_gate; + + return clk_data; + +unregister_gate: + hisi_clk_unregister_gate(hi3559av100_shub_gate_clks, + ARRAY_SIZE(hi3559av100_shub_gate_clks), clk_data); +unregister_factor: + hisi_clk_unregister_divider(hi3559av100_shub_div_clks, + ARRAY_SIZE(hi3559av100_shub_div_clks), clk_data); +unregister_mux: + hisi_clk_unregister_mux(hi3559av100_shub_mux_clks, + ARRAY_SIZE(hi3559av100_shub_mux_clks), clk_data); +unregister_fixed_rate: + hisi_clk_unregister_fixed_rate(hi3559av100_shub_fixed_rate_clks, + ARRAY_SIZE(hi3559av100_shub_fixed_rate_clks), clk_data); + return ERR_PTR(ret); +} + +static __init void hi3559av100_shub_clk_unregister(struct platform_device *pdev) +{ + struct hisi_crg_dev *crg = platform_get_drvdata(pdev); + + of_clk_del_provider(pdev->dev.of_node); + + hisi_clk_unregister_gate(hi3559av100_shub_gate_clks, + ARRAY_SIZE(hi3559av100_shub_gate_clks), crg->clk_data); + hisi_clk_unregister_divider(hi3559av100_shub_div_clks, + ARRAY_SIZE(hi3559av100_shub_div_clks), crg->clk_data); + hisi_clk_unregister_mux(hi3559av100_shub_mux_clks, + ARRAY_SIZE(hi3559av100_shub_mux_clks), crg->clk_data); + hisi_clk_unregister_fixed_rate(hi3559av100_shub_fixed_rate_clks, + ARRAY_SIZE(hi3559av100_shub_fixed_rate_clks), crg->clk_data); +} + +static const struct hisi_crg_funcs hi3559av100_shub_crg_funcs = { + .register_clks = hi3559av100_shub_clk_register, + .unregister_clks = hi3559av100_shub_clk_unregister, +}; + +static const struct of_device_id hi3559av100_crg_match_table[] = { + { + .compatible = "hisilicon,hi3559av100-clock", + .data = &hi3559av100_crg_funcs + }, + { + .compatible = "hisilicon,hi3559av100-shub-clock", + .data = &hi3559av100_shub_crg_funcs + }, + { } +}; +MODULE_DEVICE_TABLE(of, hi3559av100_crg_match_table); + +static int hi3559av100_crg_probe(struct platform_device *pdev) +{ + struct hisi_crg_dev *crg; + + crg = devm_kmalloc(&pdev->dev, sizeof(*crg), GFP_KERNEL); + if (!crg) + return -ENOMEM; + + crg->funcs = of_device_get_match_data(&pdev->dev); + if (!crg->funcs) + return -ENOENT; + + crg->rstc = hisi_reset_init(pdev); + if (!crg->rstc) + return -ENOMEM; + + crg->clk_data = crg->funcs->register_clks(pdev); + if (IS_ERR(crg->clk_data)) { + hisi_reset_exit(crg->rstc); + return PTR_ERR(crg->clk_data); + } + + platform_set_drvdata(pdev, crg); + return 0; +} + +static int hi3559av100_crg_remove(struct platform_device *pdev) +{ + struct hisi_crg_dev *crg = platform_get_drvdata(pdev); + + hisi_reset_exit(crg->rstc); + crg->funcs->unregister_clks(pdev); + return 0; +} + +static struct platform_driver hi3559av100_crg_driver = { + .probe = hi3559av100_crg_probe, + .remove = hi3559av100_crg_remove, + .driver = { + .name = "hi3559av100-clock", + .of_match_table = hi3559av100_crg_match_table, + }, +}; + +static int __init hi3559av100_crg_init(void) +{ + return platform_driver_register(&hi3559av100_crg_driver); +} +core_initcall(hi3559av100_crg_init); + +static void __exit hi3559av100_crg_exit(void) +{ + platform_driver_unregister(&hi3559av100_crg_driver); +} +module_exit(hi3559av100_crg_exit); + + +MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION("HiSilicon Hi3559AV100 CRG Driver"); diff --git a/drivers/clk/hisilicon/clk.c b/drivers/clk/hisilicon/clk.c index 54d9fdc93599..9361fba7cd4c 100644 --- a/drivers/clk/hisilicon/clk.c +++ b/drivers/clk/hisilicon/clk.c @@ -162,7 +162,7 @@ int hisi_clk_register_mux(const struct hisi_mux_clock *clks, clks[i].num_parents, clks[i].flags, base + clks[i].offset, clks[i].shift, mask, clks[i].mux_flags, - clks[i].table, &hisi_clk_lock); + (u32 *)clks[i].table, &hisi_clk_lock); if (IS_ERR(clk)) { pr_err("%s: failed to register clock %s\n", __func__, clks[i].name); diff --git a/drivers/clk/hisilicon/clk.h b/drivers/clk/hisilicon/clk.h index 61cbd90d1213..7a9b42e1b027 100644 --- a/drivers/clk/hisilicon/clk.h +++ b/drivers/clk/hisilicon/clk.h @@ -50,7 +50,7 @@ struct hisi_mux_clock { u8 shift; u8 width; u8 mux_flags; - u32 *table; + const u32 *table; const char *alias; };