This is based on the uCCF version with trivial changes to use the full CCF API and demonstrate how clk drivers can be ported from U-Boot CCF to full CCF.
Notably, the line count drops by about 20% with the removal of the different U_BOOT_DRIVER definitions. It also becomes possible to register clocks with direct references to their parent clocks, avoiding the need for global string lookups. The test dts is also updated to give the clk-ccf device a proper handle to the fixed osc clock, enabling proper lookup via the clock-names DT property. Signed-off-by: Casey Connolly <[email protected]> --- arch/sandbox/dts/test.dts | 4 +- arch/sandbox/include/asm/clk.h | 2 +- drivers/clk/ccf/Makefile | 1 + drivers/clk/ccf/clk_sandbox_ccf_full.c | 220 +++++++++++++++++++++++++++++++++ drivers/clk/clk_sandbox.c | 43 ++++++- drivers/clk/uccf/clk_sandbox_uccf.c | 4 +- include/sandbox-clk.h | 3 + test/dm/clk_ccf.c | 2 +- 8 files changed, 273 insertions(+), 6 deletions(-) diff --git a/arch/sandbox/dts/test.dts b/arch/sandbox/dts/test.dts index 1652848a6f5e..8ea9176ae68c 100644 --- a/arch/sandbox/dts/test.dts +++ b/arch/sandbox/dts/test.dts @@ -697,9 +697,9 @@ clock-mult = <2>; clocks = <&clk_fixed>; }; - osc { + xo_board: osc { compatible = "fixed-clock"; #clock-cells = <0>; clock-frequency = <20000000>; }; @@ -740,8 +740,10 @@ }; ccf: clk-ccf { compatible = "sandbox,clk-ccf"; + clocks = <&xo_board>; + clock-names = "osc"; #clock-cells = <1>; }; efi-media { diff --git a/arch/sandbox/include/asm/clk.h b/arch/sandbox/include/asm/clk.h index 37fe49c7fcf6..c67ea92d3331 100644 --- a/arch/sandbox/include/asm/clk.h +++ b/arch/sandbox/include/asm/clk.h @@ -145,9 +145,9 @@ ulong sandbox_clk_test_round_rate(struct udevice *dev, int id, ulong rate); * @dev: The sandbox clock test (client) device. * @id: The test device's clock ID to configure. * @return: The new rate of the clock. */ -ulong sandbox_clk_test_set_rate(struct udevice *dev, int id, ulong rate); +long sandbox_clk_test_set_rate(struct udevice *dev, int id, ulong rate); /** * sandbox_clk_test_enable - Ask the sandbox clock test device to enable a * clock. * diff --git a/drivers/clk/ccf/Makefile b/drivers/clk/ccf/Makefile index a337b9f29d7a..2d319eb24fff 100644 --- a/drivers/clk/ccf/Makefile +++ b/drivers/clk/ccf/Makefile @@ -14,4 +14,5 @@ obj-y += clk.o \ clk-fixed-rate.o obj-$(CONFIG_CLK_COMPOSITE_CCF) += clk-composite.o +obj-$(CONFIG_SANDBOX_CLK_CCF) += clk_sandbox_ccf_full.o diff --git a/drivers/clk/ccf/clk_sandbox_ccf_full.c b/drivers/clk/ccf/clk_sandbox_ccf_full.c new file mode 100644 index 000000000000..6e3b4fd69646 --- /dev/null +++ b/drivers/clk/ccf/clk_sandbox_ccf_full.c @@ -0,0 +1,220 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (C) 2019 + * Lukasz Majewski, DENX Software Engineering, [email protected] + * + * Common Clock Framework [CCF] driver for Sandbox + */ + +#include <dm.h> +#include <clk.h> +#include <malloc.h> +#include <asm/clk.h> +#include <clk-uclass.h> +#include <dm/devres.h> +#include <linux/bitops.h> +#include <linux/clk-provider.h> +#include <sandbox-clk.h> +#include <linux/err.h> + +/* + * Sandbox implementation of CCF primitives necessary for clk-uclass testing + * + * --- Sandbox PLLv3 --- + */ +struct clk_pllv3 { + struct clk_hw clk; + u32 div_mask; + u32 div_shift; +}; + +#if CONFIG_IS_ENABLED(UNIT_TEST) +int clk_get_enable_count(struct clk *clk); +#else +#define clk_get_enable_count(clk) 0 +#endif + +int sandbox_clk_enable_count(struct clk *clk) +{ + return clk_get_enable_count(clk); +} + +static ulong clk_pllv3_recalc_rate(struct clk_hw *hw, unsigned long parent_rate) +{ + return parent_rate * 24; +} + +static const struct clk_ops clk_pllv3_generic_ops = { + .recalc_rate = clk_pllv3_recalc_rate, +}; + +static struct clk_hw *sb_clk_pllv3(enum sandbox_pllv3_type type, ofnode node, const char *name, + const char *parent_name, void __iomem *base, + u32 div_mask) +{ + struct clk_pllv3 *pll; + struct clk_hw *hw; + struct clk_init_data init = { 0 }; + int ret; + + pll = kzalloc(sizeof(*pll), GFP_KERNEL); + if (!pll) + return ERR_PTR(-ENOMEM); + + pll->div_mask = div_mask; + hw = &pll->clk; + hw->init = &init; + + init.name = name; + init.ops = &clk_pllv3_generic_ops; + init.flags = CLK_SET_RATE_PARENT; + init.parent_data = &(struct clk_parent_data){ + .fw_name = parent_name, + }; + init.num_parents = 1; + + ret = of_clk_hw_register(node, hw); + if (ret) { + kfree(pll); + return ERR_PTR(ret); + } + + return hw; +} + +/* --- Sandbox PLLv3 --- */ + +struct clk_hw *sandbox_clk_composite(const char *name, + const char * const *parent_names, + int num_parents, void __iomem *reg, + unsigned long flags) +{ + struct clk_hw *hw = ERR_PTR(-ENOMEM); + struct clk_divider *div = NULL; + struct clk_gate *gate = NULL; + struct clk_mux *mux = NULL; + + mux = kzalloc(sizeof(*mux), GFP_KERNEL); + if (!mux) + goto fail; + + mux->reg = reg; + mux->shift = 24; + mux->mask = 0x7; + mux->flags = flags; + + div = kzalloc(sizeof(*div), GFP_KERNEL); + if (!div) + goto fail; + + div->reg = reg; + div->shift = 16; + div->width = 3; + div->flags = CLK_DIVIDER_ROUND_CLOSEST | flags; + + gate = kzalloc(sizeof(*gate), GFP_KERNEL); + if (!gate) + goto fail; + + gate->reg = reg; + gate->bit_idx = 28; + gate->flags = flags; + + hw = clk_hw_register_composite(NULL, name, + parent_names, num_parents, + &mux->hw, &clk_mux_ops, &div->hw, + &clk_divider_ro_ops, + &gate->hw, &clk_gate_ops, flags); + if (IS_ERR(hw)) + goto fail; + + return hw; + +fail: + kfree(gate); + kfree(div); + kfree(mux); + return ERR_CAST(hw); +} + +/* --- Sandbox Gate --- */ +/* The CCF core driver itself */ +static const struct udevice_id sandbox_clk_ccf_test_ids[] = { + { .compatible = "sandbox,clk-ccf" }, + { } +}; + +static const char *const usdhc_sels[] = { "pll3_60m", "pll3_80m", }; +static const char *const i2c_sels[] = { "pll3_60m", "pll3_80m", }; + +static int sandbox_clk_ccf_probe(struct udevice *dev) +{ + ofnode node = dev_ofnode(dev); + struct clk_hw_onecell_data *data; + struct clk_hw **clks; + void *base = NULL; + u32 reg; + u32 *clk_regs; + + data = devm_kzalloc(dev, sizeof(*data) + sizeof(struct clk_hw) * SANDBOX_CLK_MAX, __GFP_ZERO); + if (!data) + return -ENOMEM; + + clk_regs = devm_kzalloc(dev, sizeof(u32) * SANDBOX_CLK_MAX, __GFP_ZERO); + if (!clk_regs) + return -ENOMEM; + + data->num = SANDBOX_CLK_MAX; + clks = data->hws; + + clks[SANDBOX_CLK_PLL3] = sb_clk_pllv3(SANDBOX_PLLV3_USB, node, "pll3_usb_otg", "osc", + base + 0x10, 0x3); + + clks[SANDBOX_CLK_PLL3_60M] = + clk_hw_register_fixed_factor_parent_hw(dev, "pll3_60m", + clks[SANDBOX_CLK_PLL3], + CLK_SET_RATE_PARENT, 1, 8); + + clks[SANDBOX_CLK_PLL3_80M] = + clk_hw_register_fixed_factor_parent_hw(dev, "pll3_80m", + clks[SANDBOX_CLK_PLL3], + CLK_SET_RATE_PARENT, 1, 6); + + /* The HW adds +1 to the divider value (2+1) is the divider */ + clk_regs[SANDBOX_CLK_ECSPI_ROOT] = (2 << 19); + clks[SANDBOX_CLK_ECSPI_ROOT] = clk_hw_register_divider_parent_hw(dev, "ecspi_root", clks[SANDBOX_CLK_PLL3_60M], + CLK_SET_RATE_PARENT, &clk_regs[SANDBOX_CLK_ECSPI_ROOT], 19, 6, CLK_DIVIDER_READ_ONLY); + clk_regs[SANDBOX_CLK_ECSPI0] = 0; + clks[SANDBOX_CLK_ECSPI0] = clk_hw_register_gate_parent_hw(dev, "ecspi0", clks[SANDBOX_CLK_ECSPI_ROOT], + CLK_SET_RATE_PARENT, &clk_regs[SANDBOX_CLK_ECSPI0], 0, 0); + + clks[SANDBOX_CLK_ECSPI1] = clk_hw_register_gate_parent_hw(dev, "ecspi1", clks[SANDBOX_CLK_ECSPI_ROOT], CLK_SET_RATE_PARENT, base + 0x6c, 0, 0); + + /* Select 'pll3_60m' */ + reg = 0; + clk_regs[SANDBOX_CLK_USDHC1_SEL] = 0; + clks[SANDBOX_CLK_USDHC1_SEL] = clk_hw_register_mux(dev, "usdhc1_sel", usdhc_sels, ARRAY_SIZE(usdhc_sels), + CLK_SET_RATE_NO_REPARENT, &clk_regs[SANDBOX_CLK_USDHC1_SEL], 16, 1, 0); + + /* Select 'pll3_80m' */ + clk_regs[SANDBOX_CLK_USDHC2_SEL] = BIT(17); + clks[SANDBOX_CLK_USDHC2_SEL] = clk_hw_register_mux(dev, "usdhc2_sel", usdhc_sels, ARRAY_SIZE(usdhc_sels), + CLK_SET_RATE_NO_REPARENT, &clk_regs[SANDBOX_CLK_USDHC2_SEL], 17, 1, 0); + + clk_regs[SANDBOX_CLK_I2C] = BIT(28) | BIT(24) | BIT(16); + clks[SANDBOX_CLK_I2C] = sandbox_clk_composite("i2c2", i2c_sels, ARRAY_SIZE(i2c_sels), + &clk_regs[SANDBOX_CLK_I2C], CLK_SET_RATE_UNGATE); + + + clk_regs[SANDBOX_CLK_I2C_ROOT] = 0; + clks[SANDBOX_CLK_I2C_ROOT] = clk_hw_register_gate(dev, "i2c_root", "i2c2", 0, &clk_regs[SANDBOX_CLK_I2C_ROOT], 0, 0); + + return of_clk_add_hw_provider(node, of_clk_hw_onecell_get, data); +} + +U_BOOT_DRIVER(sandbox_clk_ccf_full) = { + .name = "sandbox_clk_ccf_full", + .id = UCLASS_NOP, + .probe = sandbox_clk_ccf_probe, + .of_match = sandbox_clk_ccf_test_ids, +}; diff --git a/drivers/clk/clk_sandbox.c b/drivers/clk/clk_sandbox.c index 667526810fc2..36ca128892b5 100644 --- a/drivers/clk/clk_sandbox.c +++ b/drivers/clk/clk_sandbox.c @@ -165,9 +165,10 @@ int sandbox_clk_query_requested(struct udevice *dev, int id) return -EINVAL; return priv->requested[id]; } -int clk_fixed_rate_of_to_plat(struct udevice *dev) +#if !CONFIG_IS_ENABLED(CLK_CCF_FULL) +static int clk_fixed_rate_of_to_plat(struct udevice *dev) { struct clk_fixed_rate *cplat; #if CONFIG_IS_ENABLED(OF_PLATDATA) @@ -196,4 +197,44 @@ U_BOOT_DRIVER(sandbox_fixed_clock) = { .plat_auto = sizeof(struct sandbox_clk_fixed_rate_plat), .ops = &clk_fixed_rate_ops, .flags = DM_FLAG_PRE_RELOC, }; +#else +static int clk_fixed_rate_probe(struct udevice *dev) +{ + ofnode node = dev_ofnode(dev); + const char *clk_name; + struct clk_hw *hw; + u32 rate = 0; + int ret; + + ofnode_read_u32(node, "clock-frequency", &rate); + + clk_name = ofnode_read_string(node, "clock-output-names"); + if (!clk_name) + clk_name = ofnode_get_name(node); + + hw = clk_hw_register_fixed_rate(dev, clk_name, NULL, 0, rate); + if (IS_ERR(hw)) + return PTR_ERR(hw); + + ret = of_clk_add_hw_provider(node, of_clk_hw_simple_get, hw); + if (ret) { + clk_hw_unregister_fixed_rate(hw); + return ret; + } + + return 0; +} + +static const struct udevice_id sandbox_clk_fixed_rate_match[] = { + { .compatible = "sandbox,fixed-clock" }, + { /* sentinel */ } +}; + +U_BOOT_DRIVER(sandbox_fixed_clock) = { + .name = "sandbox_fixed_clock", + .id = UCLASS_NOP, + .of_match = sandbox_clk_fixed_rate_match, + .probe = clk_fixed_rate_probe, +}; +#endif diff --git a/drivers/clk/uccf/clk_sandbox_uccf.c b/drivers/clk/uccf/clk_sandbox_uccf.c index 9c7dc5007134..90e0eaa93247 100644 --- a/drivers/clk/uccf/clk_sandbox_uccf.c +++ b/drivers/clk/uccf/clk_sandbox_uccf.c @@ -270,13 +270,13 @@ static int sandbox_clk_ccf_probe(struct udevice *dev) ARRAY_SIZE(usdhc_sels))); reg = BIT(28) | BIT(24) | BIT(16); dev_clk_dm(dev, SANDBOX_CLK_I2C, - sandbox_clk_composite("i2c", i2c_sels, ARRAY_SIZE(i2c_sels), + sandbox_clk_composite("i2c2", i2c_sels, ARRAY_SIZE(i2c_sels), ®, CLK_SET_RATE_UNGATE)); dev_clk_dm(dev, SANDBOX_CLK_I2C_ROOT, - sandbox_clk_gate2("i2c_root", "i2c", base + 0x7c, 0)); + sandbox_clk_gate2("i2c_root", "i2c2", base + 0x7c, 0)); return 0; } diff --git a/include/sandbox-clk.h b/include/sandbox-clk.h index eb02a474c741..fd16aacf1a73 100644 --- a/include/sandbox-clk.h +++ b/include/sandbox-clk.h @@ -20,8 +20,11 @@ enum { SANDBOX_CLK_USDHC1_SEL, SANDBOX_CLK_USDHC2_SEL, SANDBOX_CLK_I2C, SANDBOX_CLK_I2C_ROOT, + + _SANDBOX_CLK_MAX, + SANDBOX_CLK_MAX = _SANDBOX_CLK_MAX, }; enum sandbox_pllv3_type { SANDBOX_PLLV3_GENERIC, diff --git a/test/dm/clk_ccf.c b/test/dm/clk_ccf.c index 9c06aadb7ed3..13f90ce53acf 100644 --- a/test/dm/clk_ccf.c +++ b/test/dm/clk_ccf.c @@ -108,9 +108,9 @@ static int dm_test_clk_ccf(struct unit_test_state *uts) /* Test the composite of CCF */ ret = clk_get_by_id(CLK_ID(dev, SANDBOX_CLK_I2C), &clk); ut_assertok(ret); - ut_asserteq_str("i2c", clk->dev->name); + ut_asserteq_str("i2c2", clk->dev->name); ut_asserteq(CLK_SET_RATE_UNGATE, clk->flags); rate = clk_get_rate(clk); ut_asserteq_64(60000000, rate); -- 2.55.0
