diff options
author | Stephen Boyd <sboyd@kernel.org> | 2020-01-17 11:01:09 -0800 |
---|---|---|
committer | Stephen Boyd <sboyd@kernel.org> | 2020-01-17 11:01:09 -0800 |
commit | 31ef091770da008b4b181e70858e2742ed0c3e39 (patch) | |
tree | 4a558c4f24fd9baa0148e05a231d10cd06294c5c | |
parent | e42617b825f8073569da76dc4510bfa019b1c35a (diff) | |
parent | 64c76b31774db5a0c0ce8df13aef618912136e32 (diff) | |
download | linux-31ef091770da008b4b181e70858e2742ed0c3e39.tar.bz2 |
Merge tag 'clk-meson-v5.6-1' of https://github.com/BayLibre/clk-meson into clk-amlogic
Pull Amlogic clk driver updates from Jerome Brunet:
- Add meson8b DDR clock controller
- Add input clocks to meson8b controllers
- Fix meson8b mali clock update using the glitch free mux
- Fix pll driver division by zero init
* tag 'clk-meson-v5.6-1' of https://github.com/BayLibre/clk-meson:
clk: clarify that clk_set_rate() does updates from top to bottom
clk: meson: meson8b: make the CCF use the glitch-free mali mux
clk: meson: pll: Fix by 0 division in __pll_params_to_rate()
clk: meson: g12a: fix missing uart2 in regmap table
clk: meson: meson8b: use of_clk_hw_register to register the clocks
clk: meson: meson8b: don't register the XTAL clock when provided via OF
clk: meson: meson8b: change references to the XTAL clock to use [fw_]name
clk: meson: meson8b: use clk_hw_set_parent in the CPU clock notifier
clk: meson: add a driver for the Meson8/8b/8m2 DDR clock controller
dt-bindings: clock: meson8b: add the clock inputs
dt-bindings: clock: add the Amlogic Meson8 DDR clock controller binding
-rw-r--r-- | Documentation/devicetree/bindings/clock/amlogic,meson8-ddr-clkc.yaml | 50 | ||||
-rw-r--r-- | Documentation/devicetree/bindings/clock/amlogic,meson8b-clkc.txt | 5 | ||||
-rw-r--r-- | drivers/clk/meson/Makefile | 2 | ||||
-rw-r--r-- | drivers/clk/meson/clk-pll.c | 9 | ||||
-rw-r--r-- | drivers/clk/meson/g12a.c | 1 | ||||
-rw-r--r-- | drivers/clk/meson/meson8-ddr.c | 149 | ||||
-rw-r--r-- | drivers/clk/meson/meson8b.c | 124 | ||||
-rw-r--r-- | include/dt-bindings/clock/meson8-ddr-clkc.h | 4 | ||||
-rw-r--r-- | include/linux/clk.h | 3 |
9 files changed, 291 insertions, 56 deletions
diff --git a/Documentation/devicetree/bindings/clock/amlogic,meson8-ddr-clkc.yaml b/Documentation/devicetree/bindings/clock/amlogic,meson8-ddr-clkc.yaml new file mode 100644 index 000000000000..4b8669f870ec --- /dev/null +++ b/Documentation/devicetree/bindings/clock/amlogic,meson8-ddr-clkc.yaml @@ -0,0 +1,50 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/clock/amlogic,meson8-ddr-clkc.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Amlogic DDR Clock Controller Device Tree Bindings + +maintainers: + - Martin Blumenstingl <martin.blumenstingl@googlemail.com> + +properties: + compatible: + enum: + - amlogic,meson8-ddr-clkc + - amlogic,meson8b-ddr-clkc + + reg: + maxItems: 1 + + clocks: + maxItems: 1 + + clock-names: + items: + - const: xtal + + "#clock-cells": + const: 1 + +required: + - compatible + - reg + - clocks + - clock-names + - "#clock-cells" + +additionalProperties: false + +examples: + - | + ddr_clkc: clock-controller@400 { + compatible = "amlogic,meson8-ddr-clkc"; + reg = <0x400 0x20>; + clocks = <&xtal>; + clock-names = "xtal"; + #clock-cells = <1>; + }; + +... diff --git a/Documentation/devicetree/bindings/clock/amlogic,meson8b-clkc.txt b/Documentation/devicetree/bindings/clock/amlogic,meson8b-clkc.txt index 4d94091c1d2d..cc51e4746b3b 100644 --- a/Documentation/devicetree/bindings/clock/amlogic,meson8b-clkc.txt +++ b/Documentation/devicetree/bindings/clock/amlogic,meson8b-clkc.txt @@ -11,6 +11,11 @@ Required Properties: - "amlogic,meson8m2-clkc" for Meson8m2 (S812) SoCs - #clock-cells: should be 1. - #reset-cells: should be 1. +- clocks: list of clock phandles, one for each entry in clock-names +- clock-names: should contain the following: + * "xtal": the 24MHz system oscillator + * "ddr_pll": the DDR PLL clock + * "clk_32k": (if present) the 32kHz clock signal from GPIOAO_6 (CLK_32K_IN) Parent node should have the following properties : - compatible: "amlogic,meson-hhi-sysctrl", "simple-mfd", "syscon" diff --git a/drivers/clk/meson/Makefile b/drivers/clk/meson/Makefile index 3939f218587a..6eca2a406ee3 100644 --- a/drivers/clk/meson/Makefile +++ b/drivers/clk/meson/Makefile @@ -18,4 +18,4 @@ obj-$(CONFIG_COMMON_CLK_AXG) += axg.o axg-aoclk.o obj-$(CONFIG_COMMON_CLK_AXG_AUDIO) += axg-audio.o obj-$(CONFIG_COMMON_CLK_GXBB) += gxbb.o gxbb-aoclk.o obj-$(CONFIG_COMMON_CLK_G12A) += g12a.o g12a-aoclk.o -obj-$(CONFIG_COMMON_CLK_MESON8B) += meson8b.o +obj-$(CONFIG_COMMON_CLK_MESON8B) += meson8b.o meson8-ddr.o diff --git a/drivers/clk/meson/clk-pll.c b/drivers/clk/meson/clk-pll.c index ddb1e5634739..3a5853ca98c6 100644 --- a/drivers/clk/meson/clk-pll.c +++ b/drivers/clk/meson/clk-pll.c @@ -77,6 +77,15 @@ static unsigned long meson_clk_pll_recalc_rate(struct clk_hw *hw, unsigned int m, n, frac; n = meson_parm_read(clk->map, &pll->n); + + /* + * On some HW, N is set to zero on init. This value is invalid as + * it would result in a division by zero. The rate can't be + * calculated in this case + */ + if (n == 0) + return 0; + m = meson_parm_read(clk->map, &pll->m); frac = MESON_PARM_APPLICABLE(&pll->frac) ? diff --git a/drivers/clk/meson/g12a.c b/drivers/clk/meson/g12a.c index b3af61cc6fb9..d2760a021301 100644 --- a/drivers/clk/meson/g12a.c +++ b/drivers/clk/meson/g12a.c @@ -4692,6 +4692,7 @@ static struct clk_regmap *const g12a_clk_regmaps[] = { &g12a_bt656, &g12a_usb1_to_ddr, &g12a_mmc_pclk, + &g12a_uart2, &g12a_vpu_intr, &g12a_gic, &g12a_sd_emmc_a_clk0, diff --git a/drivers/clk/meson/meson8-ddr.c b/drivers/clk/meson/meson8-ddr.c new file mode 100644 index 000000000000..4b73ea244b63 --- /dev/null +++ b/drivers/clk/meson/meson8-ddr.c @@ -0,0 +1,149 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Amlogic Meson8 DDR clock controller + * + * Copyright (C) 2019 Martin Blumenstingl <martin.blumenstingl@googlemail.com> + */ + +#include <dt-bindings/clock/meson8-ddr-clkc.h> + +#include <linux/clk-provider.h> +#include <linux/platform_device.h> + +#include "clk-regmap.h" +#include "clk-pll.h" + +#define AM_DDR_PLL_CNTL 0x00 +#define AM_DDR_PLL_CNTL1 0x04 +#define AM_DDR_PLL_CNTL2 0x08 +#define AM_DDR_PLL_CNTL3 0x0c +#define AM_DDR_PLL_CNTL4 0x10 +#define AM_DDR_PLL_STS 0x14 +#define DDR_CLK_CNTL 0x18 +#define DDR_CLK_STS 0x1c + +static struct clk_regmap meson8_ddr_pll_dco = { + .data = &(struct meson_clk_pll_data){ + .en = { + .reg_off = AM_DDR_PLL_CNTL, + .shift = 30, + .width = 1, + }, + .m = { + .reg_off = AM_DDR_PLL_CNTL, + .shift = 0, + .width = 9, + }, + .n = { + .reg_off = AM_DDR_PLL_CNTL, + .shift = 9, + .width = 5, + }, + .l = { + .reg_off = AM_DDR_PLL_CNTL, + .shift = 31, + .width = 1, + }, + .rst = { + .reg_off = AM_DDR_PLL_CNTL, + .shift = 29, + .width = 1, + }, + }, + .hw.init = &(struct clk_init_data){ + .name = "ddr_pll_dco", + .ops = &meson_clk_pll_ro_ops, + .parent_data = &(const struct clk_parent_data) { + .fw_name = "xtal", + }, + .num_parents = 1, + }, +}; + +static struct clk_regmap meson8_ddr_pll = { + .data = &(struct clk_regmap_div_data){ + .offset = AM_DDR_PLL_CNTL, + .shift = 16, + .width = 2, + .flags = CLK_DIVIDER_POWER_OF_TWO, + }, + .hw.init = &(struct clk_init_data){ + .name = "ddr_pll", + .ops = &clk_regmap_divider_ro_ops, + .parent_hws = (const struct clk_hw *[]) { + &meson8_ddr_pll_dco.hw + }, + .num_parents = 1, + }, +}; + +static struct clk_hw_onecell_data meson8_ddr_clk_hw_onecell_data = { + .hws = { + [DDR_CLKID_DDR_PLL_DCO] = &meson8_ddr_pll_dco.hw, + [DDR_CLKID_DDR_PLL] = &meson8_ddr_pll.hw, + }, + .num = 2, +}; + +static struct clk_regmap *const meson8_ddr_clk_regmaps[] = { + &meson8_ddr_pll_dco, + &meson8_ddr_pll, +}; + +static const struct regmap_config meson8_ddr_clkc_regmap_config = { + .reg_bits = 8, + .val_bits = 32, + .reg_stride = 4, + .max_register = DDR_CLK_STS, +}; + +static int meson8_ddr_clkc_probe(struct platform_device *pdev) +{ + struct regmap *regmap; + void __iomem *base; + struct clk_hw *hw; + int ret, i; + + base = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(base)) + return PTR_ERR(base); + + regmap = devm_regmap_init_mmio(&pdev->dev, base, + &meson8_ddr_clkc_regmap_config); + if (IS_ERR(regmap)) + return PTR_ERR(regmap); + + /* Populate regmap */ + for (i = 0; i < ARRAY_SIZE(meson8_ddr_clk_regmaps); i++) + meson8_ddr_clk_regmaps[i]->map = regmap; + + /* Register all clks */ + for (i = 0; i < meson8_ddr_clk_hw_onecell_data.num; i++) { + hw = meson8_ddr_clk_hw_onecell_data.hws[i]; + + ret = devm_clk_hw_register(&pdev->dev, hw); + if (ret) { + dev_err(&pdev->dev, "Clock registration failed\n"); + return ret; + } + } + + return devm_of_clk_add_hw_provider(&pdev->dev, of_clk_hw_onecell_get, + &meson8_ddr_clk_hw_onecell_data); +} + +static const struct of_device_id meson8_ddr_clkc_match_table[] = { + { .compatible = "amlogic,meson8-ddr-clkc" }, + { .compatible = "amlogic,meson8b-ddr-clkc" }, + { /* sentinel */ } +}; + +static struct platform_driver meson8_ddr_clkc_driver = { + .probe = meson8_ddr_clkc_probe, + .driver = { + .name = "meson8-ddr-clkc", + .of_match_table = meson8_ddr_clkc_match_table, + }, +}; + +builtin_platform_driver(meson8_ddr_clkc_driver); diff --git a/drivers/clk/meson/meson8b.c b/drivers/clk/meson/meson8b.c index 67e6691e080c..9fd31f23b2a9 100644 --- a/drivers/clk/meson/meson8b.c +++ b/drivers/clk/meson/meson8b.c @@ -97,8 +97,10 @@ static struct clk_regmap meson8b_fixed_pll_dco = { .hw.init = &(struct clk_init_data){ .name = "fixed_pll_dco", .ops = &meson_clk_pll_ro_ops, - .parent_hws = (const struct clk_hw *[]) { - &meson8b_xtal.hw + .parent_data = &(const struct clk_parent_data) { + .fw_name = "xtal", + .name = "xtal", + .index = -1, }, .num_parents = 1, }, @@ -162,8 +164,10 @@ static struct clk_regmap meson8b_hdmi_pll_dco = { /* sometimes also called "HPLL" or "HPLL PLL" */ .name = "hdmi_pll_dco", .ops = &meson_clk_pll_ro_ops, - .parent_hws = (const struct clk_hw *[]) { - &meson8b_xtal.hw + .parent_data = &(const struct clk_parent_data) { + .fw_name = "xtal", + .name = "xtal", + .index = -1, }, .num_parents = 1, }, @@ -237,8 +241,10 @@ static struct clk_regmap meson8b_sys_pll_dco = { .hw.init = &(struct clk_init_data){ .name = "sys_pll_dco", .ops = &meson_clk_pll_ops, - .parent_hws = (const struct clk_hw *[]) { - &meson8b_xtal.hw + .parent_data = &(const struct clk_parent_data) { + .fw_name = "xtal", + .name = "xtal", + .index = -1, }, .num_parents = 1, }, @@ -631,9 +637,9 @@ static struct clk_regmap meson8b_cpu_in_sel = { .hw.init = &(struct clk_init_data){ .name = "cpu_in_sel", .ops = &clk_regmap_mux_ops, - .parent_hws = (const struct clk_hw *[]) { - &meson8b_xtal.hw, - &meson8b_sys_pll.hw, + .parent_data = (const struct clk_parent_data[]) { + { .fw_name = "xtal", .name = "xtal", .index = -1, }, + { .hw = &meson8b_sys_pll.hw, }, }, .num_parents = 2, .flags = (CLK_SET_RATE_PARENT | @@ -736,9 +742,9 @@ static struct clk_regmap meson8b_cpu_clk = { .hw.init = &(struct clk_init_data){ .name = "cpu_clk", .ops = &clk_regmap_mux_ops, - .parent_hws = (const struct clk_hw *[]) { - &meson8b_xtal.hw, - &meson8b_cpu_scale_out_sel.hw, + .parent_data = (const struct clk_parent_data[]) { + { .fw_name = "xtal", .name = "xtal", .index = -1, }, + { .hw = &meson8b_cpu_scale_out_sel.hw, }, }, .num_parents = 2, .flags = (CLK_SET_RATE_PARENT | @@ -758,12 +764,12 @@ static struct clk_regmap meson8b_nand_clk_sel = { .name = "nand_clk_sel", .ops = &clk_regmap_mux_ops, /* FIXME all other parents are unknown: */ - .parent_hws = (const struct clk_hw *[]) { - &meson8b_fclk_div4.hw, - &meson8b_fclk_div3.hw, - &meson8b_fclk_div5.hw, - &meson8b_fclk_div7.hw, - &meson8b_xtal.hw, + .parent_data = (const struct clk_parent_data[]) { + { .hw = &meson8b_fclk_div4.hw, }, + { .hw = &meson8b_fclk_div3.hw, }, + { .hw = &meson8b_fclk_div5.hw, }, + { .hw = &meson8b_fclk_div7.hw, }, + { .fw_name = "xtal", .name = "xtal", .index = -1, }, }, .num_parents = 5, .flags = CLK_SET_RATE_PARENT, @@ -1721,8 +1727,10 @@ static struct clk_regmap meson8b_hdmi_sys_sel = { .name = "hdmi_sys_sel", .ops = &clk_regmap_mux_ro_ops, /* FIXME: all other parents are unknown */ - .parent_hws = (const struct clk_hw *[]) { - &meson8b_xtal.hw + .parent_data = &(const struct clk_parent_data) { + .fw_name = "xtal", + .name = "xtal", + .index = -1, }, .num_parents = 1, .flags = CLK_SET_RATE_NO_REPARENT, @@ -1764,17 +1772,20 @@ static struct clk_regmap meson8b_hdmi_sys = { /* * The MALI IP is clocked by two identical clocks (mali_0 and mali_1) - * muxed by a glitch-free switch on Meson8b and Meson8m2. Meson8 only - * has mali_0 and no glitch-free mux. + * muxed by a glitch-free switch on Meson8b and Meson8m2. The CCF can + * actually manage this glitch-free mux because it does top-to-bottom + * updates the each clock tree and switches to the "inactive" one when + * CLK_SET_RATE_GATE is set. + * Meson8 only has mali_0 and no glitch-free mux. */ -static const struct clk_hw *meson8b_mali_0_1_parent_hws[] = { - &meson8b_xtal.hw, - &meson8b_mpll2.hw, - &meson8b_mpll1.hw, - &meson8b_fclk_div7.hw, - &meson8b_fclk_div4.hw, - &meson8b_fclk_div3.hw, - &meson8b_fclk_div5.hw, +static const struct clk_parent_data meson8b_mali_0_1_parent_data[] = { + { .fw_name = "xtal", .name = "xtal", .index = -1, }, + { .hw = &meson8b_mpll2.hw, }, + { .hw = &meson8b_mpll1.hw, }, + { .hw = &meson8b_fclk_div7.hw, }, + { .hw = &meson8b_fclk_div4.hw, }, + { .hw = &meson8b_fclk_div3.hw, }, + { .hw = &meson8b_fclk_div5.hw, }, }; static u32 meson8b_mali_0_1_mux_table[] = { 0, 2, 3, 4, 5, 6, 7 }; @@ -1789,8 +1800,8 @@ static struct clk_regmap meson8b_mali_0_sel = { .hw.init = &(struct clk_init_data){ .name = "mali_0_sel", .ops = &clk_regmap_mux_ops, - .parent_hws = meson8b_mali_0_1_parent_hws, - .num_parents = ARRAY_SIZE(meson8b_mali_0_1_parent_hws), + .parent_data = meson8b_mali_0_1_parent_data, + .num_parents = ARRAY_SIZE(meson8b_mali_0_1_parent_data), /* * Don't propagate rate changes up because the only changeable * parents are mpll1 and mpll2 but we need those for audio and @@ -1830,7 +1841,7 @@ static struct clk_regmap meson8b_mali_0 = { &meson8b_mali_0_div.hw }, .num_parents = 1, - .flags = CLK_SET_RATE_PARENT, + .flags = CLK_SET_RATE_GATE | CLK_SET_RATE_PARENT, }, }; @@ -1844,8 +1855,8 @@ static struct clk_regmap meson8b_mali_1_sel = { .hw.init = &(struct clk_init_data){ .name = "mali_1_sel", .ops = &clk_regmap_mux_ops, - .parent_hws = meson8b_mali_0_1_parent_hws, - .num_parents = ARRAY_SIZE(meson8b_mali_0_1_parent_hws), + .parent_data = meson8b_mali_0_1_parent_data, + .num_parents = ARRAY_SIZE(meson8b_mali_0_1_parent_data), /* * Don't propagate rate changes up because the only changeable * parents are mpll1 and mpll2 but we need those for audio and @@ -1885,7 +1896,7 @@ static struct clk_regmap meson8b_mali_1 = { &meson8b_mali_1_div.hw }, .num_parents = 1, - .flags = CLK_SET_RATE_PARENT, + .flags = CLK_SET_RATE_GATE | CLK_SET_RATE_PARENT, }, }; @@ -1944,8 +1955,10 @@ static struct clk_regmap meson8m2_gp_pll_dco = { .hw.init = &(struct clk_init_data){ .name = "gp_pll_dco", .ops = &meson_clk_pll_ops, - .parent_hws = (const struct clk_hw *[]) { - &meson8b_xtal.hw + .parent_data = &(const struct clk_parent_data) { + .fw_name = "xtal", + .name = "xtal", + .index = -1, }, .num_parents = 1, }, @@ -3585,7 +3598,7 @@ static const struct reset_control_ops meson8b_clk_reset_ops = { struct meson8b_nb_data { struct notifier_block nb; - struct clk_hw_onecell_data *onecell_data; + struct clk_hw *cpu_clk; }; static int meson8b_cpu_clk_notifier_cb(struct notifier_block *nb, @@ -3593,30 +3606,25 @@ static int meson8b_cpu_clk_notifier_cb(struct notifier_block *nb, { struct meson8b_nb_data *nb_data = container_of(nb, struct meson8b_nb_data, nb); - struct clk_hw **hws = nb_data->onecell_data->hws; - struct clk_hw *cpu_clk_hw, *parent_clk_hw; - struct clk *cpu_clk, *parent_clk; + struct clk_hw *parent_clk; int ret; switch (event) { case PRE_RATE_CHANGE: - parent_clk_hw = hws[CLKID_XTAL]; + /* xtal */ + parent_clk = clk_hw_get_parent_by_index(nb_data->cpu_clk, 0); break; case POST_RATE_CHANGE: - parent_clk_hw = hws[CLKID_CPU_SCALE_OUT_SEL]; + /* cpu_scale_out_sel */ + parent_clk = clk_hw_get_parent_by_index(nb_data->cpu_clk, 1); break; default: return NOTIFY_DONE; } - cpu_clk_hw = hws[CLKID_CPUCLK]; - cpu_clk = __clk_lookup(clk_hw_get_name(cpu_clk_hw)); - - parent_clk = __clk_lookup(clk_hw_get_name(parent_clk_hw)); - - ret = clk_set_parent(cpu_clk, parent_clk); + ret = clk_hw_set_parent(nb_data->cpu_clk, parent_clk); if (ret) return notifier_from_errno(ret); @@ -3682,20 +3690,26 @@ static void __init meson8b_clkc_init_common(struct device_node *np, meson8b_clk_regmaps[i]->map = map; /* - * register all clks - * CLKID_UNUSED = 0, so skip it and start with CLKID_XTAL = 1 + * always skip CLKID_UNUSED and also skip XTAL if the .dtb provides the + * XTAL clock as input. */ - for (i = CLKID_XTAL; i < CLK_NR_CLKS; i++) { + if (!IS_ERR(of_clk_get_by_name(np, "xtal"))) + i = CLKID_PLL_FIXED; + else + i = CLKID_XTAL; + + /* register all clks */ + for (; i < CLK_NR_CLKS; i++) { /* array might be sparse */ if (!clk_hw_onecell_data->hws[i]) continue; - ret = clk_hw_register(NULL, clk_hw_onecell_data->hws[i]); + ret = of_clk_hw_register(np, clk_hw_onecell_data->hws[i]); if (ret) return; } - meson8b_cpu_nb_data.onecell_data = clk_hw_onecell_data; + meson8b_cpu_nb_data.cpu_clk = clk_hw_onecell_data->hws[CLKID_CPUCLK]; /* * FIXME we shouldn't program the muxes in notifier handlers. The diff --git a/include/dt-bindings/clock/meson8-ddr-clkc.h b/include/dt-bindings/clock/meson8-ddr-clkc.h new file mode 100644 index 000000000000..a8e0fa2987ab --- /dev/null +++ b/include/dt-bindings/clock/meson8-ddr-clkc.h @@ -0,0 +1,4 @@ +/* SPDX-License-Identifier: GPL-2.0 */ + +#define DDR_CLKID_DDR_PLL_DCO 0 +#define DDR_CLKID_DDR_PLL 1 diff --git a/include/linux/clk.h b/include/linux/clk.h index 18b7b95a8253..7fd6a1febcf4 100644 --- a/include/linux/clk.h +++ b/include/linux/clk.h @@ -627,6 +627,9 @@ long clk_round_rate(struct clk *clk, unsigned long rate); * @clk: clock source * @rate: desired clock rate in Hz * + * Updating the rate starts at the top-most affected clock and then + * walks the tree down to the bottom-most clock that needs updating. + * * Returns success (0) or negative errno. */ int clk_set_rate(struct clk *clk, unsigned long rate); |