From e25d8d35e8cbc1a4c04a8b86eed6aa7229f6449e Mon Sep 17 00:00:00 2001 From: Myeonghun Pak Date: Tue, 23 Jun 2026 18:40:18 +0900 Subject: [PATCH 01/53] clk: versaclock7: Fix APLL clock leak on probe failure vc7_probe() registers the APLL with clk_register_fixed_rate(), which is not devm-managed and must be explicitly unregistered on probe failure. Most later errors already unwind through err_clk, but a failure from vc7_get_bank_clk() in the output registration loop returned directly. That skipped clk_unregister_fixed_rate() and leaked the APLL clock. Route that error through the existing err_clk label so the fixed-rate clock is released consistently with the other probe failure paths. This issue was identified during our ongoing static-analysis research while reviewing kernel code. Fixes: 48c5e98fedd9 ("clk: Renesas versaclock7 ccf device driver") Co-developed-by: Ijae Kim Signed-off-by: Ijae Kim Signed-off-by: Myeonghun Pak Reviewed-by: Brian Masney Signed-off-by: Brian Masney --- drivers/clk/clk-versaclock7.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/clk/clk-versaclock7.c b/drivers/clk/clk-versaclock7.c index adcc603e3259..e3a36dcd98b8 100644 --- a/drivers/clk/clk-versaclock7.c +++ b/drivers/clk/clk-versaclock7.c @@ -1197,7 +1197,7 @@ static int vc7_probe(struct i2c_client *client) if (ret) { dev_err_probe(&client->dev, ret, "unable to register output %d\n", i); - return ret; + goto err_clk; } switch (bank_src_map.type) { From d4873b9181345f089d4bdd0019e5744567a755f0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Uwe=20Kleine-K=C3=B6nig=20=28The=20Capable=20Hub=29?= Date: Thu, 18 Jun 2026 18:04:55 +0200 Subject: [PATCH 02/53] clk: clk-lmk04832: Simplify device abstraction MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The driver was introduced in 2021 and since then only supports a single chip variant. Simplify the driver by hard-coding the device properties instead of using the id_table's abstraction for a single chip type. While touching the id table, use a single space in the table terminator to match the most used style. Signed-off-by: Uwe Kleine-König (The Capable Hub) Reviewed-by: Brian Masney Signed-off-by: Brian Masney --- drivers/clk/clk-lmk04832.c | 27 ++++++++++----------------- 1 file changed, 10 insertions(+), 17 deletions(-) diff --git a/drivers/clk/clk-lmk04832.c b/drivers/clk/clk-lmk04832.c index 9bf86caad829..c644d31b223e 100644 --- a/drivers/clk/clk-lmk04832.c +++ b/drivers/clk/clk-lmk04832.c @@ -175,10 +175,6 @@ #define LMK04832_REG_RB_HOLDOVER 0x188 #define LMK04832_REG_SPI_LOCK 0x555 -enum lmk04832_device_types { - LMK04832, -}; - /** * struct lmk04832_device_info - Holds static device information that is * specific to the chip revision @@ -197,14 +193,12 @@ struct lmk04832_device_info { unsigned int vco1_range[2]; }; -static const struct lmk04832_device_info lmk04832_device_info[] = { - [LMK04832] = { - .pid = 0x63d1, /* WARNING PROD_ID is inverted in the datasheet */ - .maskrev = 0x70, - .num_channels = 14, - .vco0_range = { 2440, 2580 }, - .vco1_range = { 2945, 3255 }, - }, +static const struct lmk04832_device_info lmk04832_device_info = { + .pid = 0x63d1, /* WARNING PROD_ID is inverted in the datasheet */ + .maskrev = 0x70, + .num_channels = 14, + .vco0_range = { 2440, 2580 }, + .vco1_range = { 2945, 3255 }, }; enum lmk04832_rdbk_type { @@ -422,11 +416,10 @@ static unsigned long lmk04832_vco_recalc_rate(struct clk_hw *hw, */ static int lmk04832_check_vco_ranges(struct lmk04832 *lmk, unsigned long rate) { - struct spi_device *spi = to_spi_device(lmk->dev); const struct lmk04832_device_info *info; unsigned long mhz = rate / 1000000; - info = &lmk04832_device_info[spi_get_device_id(spi)->driver_data]; + info = &lmk04832_device_info; if (mhz >= info->vco0_range[0] && mhz <= info->vco0_range[1]) return LMK04832_VAL_VCO_MUX_VCO0; @@ -1405,7 +1398,7 @@ static int lmk04832_probe(struct spi_device *spi) int ret; int i; - info = &lmk04832_device_info[spi_get_device_id(spi)->driver_data]; + info = &lmk04832_device_info; lmk = devm_kzalloc(&spi->dev, sizeof(struct lmk04832), GFP_KERNEL); if (!lmk) @@ -1553,8 +1546,8 @@ static int lmk04832_probe(struct spi_device *spi) } static const struct spi_device_id lmk04832_id[] = { - { "lmk04832", LMK04832 }, - {} + { .name = "lmk04832" }, + { } }; MODULE_DEVICE_TABLE(spi, lmk04832_id); From 804bac4a2654ac69e328d68e1961eb984fb18a01 Mon Sep 17 00:00:00 2001 From: David Carlier Date: Thu, 18 Jun 2026 05:50:30 +0100 Subject: [PATCH 03/53] clk: canaan: Clear rate fields before reprogramming dividers The rate set_rate helpers perform a read-modify-write on the divider and multiplier registers but only ever OR the new value in, without first masking off the existing field. The first write after reset lands on a zeroed field and looks correct, but any later reprogramming leaves the old bits set: the field becomes the bitwise OR of the previous and new encodings, corrupting the divider or multiplier. Mask off each field before writing the new value so reprogramming a clock to a different rate produces the intended register contents. Fixes: a7b7c7c6c016 ("clk: canaan: Add clock driver for Canaan K230") Signed-off-by: David Carlier Reviewed-by: Brian Masney Acked-by: Xukai Wang Signed-off-by: Brian Masney --- drivers/clk/clk-k230.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/clk/clk-k230.c b/drivers/clk/clk-k230.c index cfc437038e4e..f34a3e6d3bca 100644 --- a/drivers/clk/clk-k230.c +++ b/drivers/clk/clk-k230.c @@ -2227,6 +2227,7 @@ static int k230_clk_set_rate_mul(struct clk_hw *hw, unsigned long rate, guard(spinlock)(rate_self->lock); mul_reg = readl(rate_self->reg + clk->mul_reg_off); + mul_reg &= ~(rate_self->mul_mask << rate_self->mul_shift); mul_reg |= ((mul - 1) & rate_self->mul_mask) << (rate_self->mul_shift); mul_reg |= BIT(rate_self->write_enable_bit); writel(mul_reg, rate_self->reg + clk->mul_reg_off); @@ -2257,6 +2258,7 @@ static int k230_clk_set_rate_div(struct clk_hw *hw, unsigned long rate, guard(spinlock)(rate_self->lock); div_reg = readl(rate_self->reg + clk->div_reg_off); + div_reg &= ~(rate_self->div_mask << rate_self->div_shift); div_reg |= ((div - 1) & rate_self->div_mask) << (rate_self->div_shift); div_reg |= BIT(rate_self->write_enable_bit); writel(div_reg, rate_self->reg + clk->div_reg_off); @@ -2287,11 +2289,13 @@ static int k230_clk_set_rate_mul_div(struct clk_hw *hw, unsigned long rate, guard(spinlock)(rate_self->lock); div_reg = readl(rate_self->reg + clk->div_reg_off); + div_reg &= ~(rate_self->div_mask << rate_self->div_shift); div_reg |= ((div - 1) & rate_self->div_mask) << (rate_self->div_shift); div_reg |= BIT(rate_self->write_enable_bit); writel(div_reg, rate_self->reg + clk->div_reg_off); mul_reg = readl(rate_self->reg + clk->mul_reg_off); + mul_reg &= ~(rate_self->mul_mask << rate_self->mul_shift); mul_reg |= ((mul - 1) & rate_self->mul_mask) << (rate_self->mul_shift); mul_reg |= BIT(rate_self->write_enable_bit); writel(mul_reg, rate_self->reg + clk->mul_reg_off); From 03221f26ed7cb566131cf798bc653b7ff2ef23aa Mon Sep 17 00:00:00 2001 From: "Rob Herring (Arm)" Date: Fri, 12 Jun 2026 16:52:51 -0500 Subject: [PATCH 04/53] clk: at91: Read "reg" with helper The "reg" property is an address-sized DT cell property. The AT91 compat clock parser only uses a small bus id from it, but reading it with the u8 helper does not match the property encoding. Use of_property_read_reg() so the code goes through the helper for "reg" properties, then keep the existing range check before passing the bus id to the clock registration code. Assisted-by: Codex:gpt-5-5 Signed-off-by: Rob Herring (Arm) Reviewed-by: Brian Masney Signed-off-by: Brian Masney --- drivers/clk/at91/dt-compat.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/drivers/clk/at91/dt-compat.c b/drivers/clk/at91/dt-compat.c index 5d543e807843..dad26dd5e71d 100644 --- a/drivers/clk/at91/dt-compat.c +++ b/drivers/clk/at91/dt-compat.c @@ -2,6 +2,7 @@ #include #include #include +#include #include #include #include @@ -217,7 +218,7 @@ CLK_OF_DECLARE(of_sama5d4_clk_h32mx_setup, "atmel,sama5d4-clk-h32mx", static void __init of_sama5d2_clk_i2s_mux_setup(struct device_node *np) { struct regmap *regmap_sfr; - u8 bus_id; + u64 bus_id; const char *parent_names[2]; struct device_node *i2s_mux_np; struct clk_hw *hw; @@ -228,7 +229,7 @@ static void __init of_sama5d2_clk_i2s_mux_setup(struct device_node *np) return; for_each_child_of_node(np, i2s_mux_np) { - if (of_property_read_u8(i2s_mux_np, "reg", &bus_id)) + if (of_property_read_reg(i2s_mux_np, 0, &bus_id, NULL)) continue; if (bus_id > I2S_BUS_NR) From ae794aa6e88d43289ab9a9b682f5cc5f6ac92657 Mon Sep 17 00:00:00 2001 From: Peng Fan Date: Fri, 12 Jun 2026 16:46:23 +0800 Subject: [PATCH 05/53] dt-bindings: clock: Add spread spectrum definition Per dt-schema, the modulation methods are: down-spread(3), up-spread(2), center-spread(1), no-spread(0). So define them in dt-bindings to avoid write the magic number in device tree. Reviewed-by: Brian Masney Acked-by: Rob Herring (Arm) Reviewed-by: Sebin Francis Signed-off-by: Peng Fan Signed-off-by: Brian Masney --- include/dt-bindings/clock/clock.h | 14 ++++++++++++++ 1 file changed, 14 insertions(+) create mode 100644 include/dt-bindings/clock/clock.h diff --git a/include/dt-bindings/clock/clock.h b/include/dt-bindings/clock/clock.h new file mode 100644 index 000000000000..155e2653a120 --- /dev/null +++ b/include/dt-bindings/clock/clock.h @@ -0,0 +1,14 @@ +/* SPDX-License-Identifier: GPL-2.0-only OR MIT */ +/* + * Copyright 2025 NXP + */ + +#ifndef __DT_BINDINGS_CLOCK_H +#define __DT_BINDINGS_CLOCK_H + +#define CLK_SSC_NO_SPREAD 0 +#define CLK_SSC_CENTER_SPREAD 1 +#define CLK_SSC_UP_SPREAD 2 +#define CLK_SSC_DOWN_SPREAD 3 + +#endif /* __DT_BINDINGS_CLOCK_H */ From c86814e70390a48bd4323ba4318cd8c0246e019b Mon Sep 17 00:00:00 2001 From: Peng Fan Date: Fri, 12 Jun 2026 16:46:24 +0800 Subject: [PATCH 06/53] clk: Introduce clk_hw_set_spread_spectrum Add clk_hw_set_spread_spectrum to configure a clock to enable spread spectrum feature. set_spread_spectrum ops is added for clk drivers to have their own hardware specific implementation. Reviewed-by: Brian Masney Reviewed-by: Sebin Francis Signed-off-by: Peng Fan Signed-off-by: Brian Masney --- drivers/clk/clk.c | 27 +++++++++++++++++++++++++++ include/linux/clk-provider.h | 31 +++++++++++++++++++++++++++++++ 2 files changed, 58 insertions(+) diff --git a/drivers/clk/clk.c b/drivers/clk/clk.c index 048adfa86a5d..8c78621cde25 100644 --- a/drivers/clk/clk.c +++ b/drivers/clk/clk.c @@ -2774,6 +2774,33 @@ int clk_set_max_rate(struct clk *clk, unsigned long rate) } EXPORT_SYMBOL_GPL(clk_set_max_rate); +int clk_hw_set_spread_spectrum(struct clk_hw *hw, const struct clk_spread_spectrum *ss_conf) +{ + struct clk_core *core; + int ret; + + if (!hw) + return 0; + + core = hw->core; + + clk_prepare_lock(); + + ret = clk_pm_runtime_get(core); + if (ret) + goto fail; + + if (core->ops->set_spread_spectrum) + ret = core->ops->set_spread_spectrum(hw, ss_conf); + + clk_pm_runtime_put(core); + +fail: + clk_prepare_unlock(); + return ret; +} +EXPORT_SYMBOL_GPL(clk_hw_set_spread_spectrum); + /** * clk_get_parent - return the parent of a clk * @clk: the clk whose parent gets returned diff --git a/include/linux/clk-provider.h b/include/linux/clk-provider.h index b01a38fef8cf..7d3747378739 100644 --- a/include/linux/clk-provider.h +++ b/include/linux/clk-provider.h @@ -6,6 +6,7 @@ #ifndef __LINUX_CLK_PROVIDER_H #define __LINUX_CLK_PROVIDER_H +#include #include #include @@ -84,6 +85,26 @@ struct clk_duty { unsigned int den; }; +enum clk_ssc_method { + CLK_SPREAD_NO = CLK_SSC_NO_SPREAD, + CLK_SPREAD_CENTER = CLK_SSC_CENTER_SPREAD, + CLK_SPREAD_UP = CLK_SSC_UP_SPREAD, + CLK_SPREAD_DOWN = CLK_SSC_DOWN_SPREAD, +}; + +/** + * struct clk_spread_spectrum - Structure encoding spread spectrum of a clock + * + * @modfreq_hz: Modulation frequency + * @spread_bp: Modulation percent in permyriad + * @method: Modulation method + */ +struct clk_spread_spectrum { + u32 modfreq_hz; + u32 spread_bp; + enum clk_ssc_method method; +}; + /** * struct clk_ops - Callback operations for hardware clocks; these are to * be provided by the clock implementation, and will be called by drivers @@ -174,6 +195,12 @@ struct clk_duty { * separately via calls to .set_parent and .set_rate. * Returns 0 on success, -EERROR otherwise. * + * @set_spread_spectrum: Optional callback used to configure the spread + * spectrum modulation frequency, percentage, and method + * to reduce EMI by spreading the clock frequency over a + * wider range. + * Returns 0 on success, -EERROR otherwise. + * * @recalc_accuracy: Recalculate the accuracy of this clock. The clock accuracy * is expressed in ppb (parts per billion). The parent accuracy is * an input parameter. @@ -249,6 +276,8 @@ struct clk_ops { int (*set_rate_and_parent)(struct clk_hw *hw, unsigned long rate, unsigned long parent_rate, u8 index); + int (*set_spread_spectrum)(struct clk_hw *hw, + const struct clk_spread_spectrum *ss_conf); unsigned long (*recalc_accuracy)(struct clk_hw *hw, unsigned long parent_accuracy); int (*get_phase)(struct clk_hw *hw); @@ -1436,6 +1465,8 @@ void clk_hw_get_rate_range(struct clk_hw *hw, unsigned long *min_rate, unsigned long *max_rate); void clk_hw_set_rate_range(struct clk_hw *hw, unsigned long min_rate, unsigned long max_rate); +int clk_hw_set_spread_spectrum(struct clk_hw *hw, + const struct clk_spread_spectrum *ss_conf); static inline void __clk_hw_set_clk(struct clk_hw *dst, struct clk_hw *src) { From a7f16660af39f80dd3ed3916458abc7f2b90e0de Mon Sep 17 00:00:00 2001 From: Peng Fan Date: Fri, 12 Jun 2026 16:46:25 +0800 Subject: [PATCH 07/53] clk: conf: Support assigned-clock-sscs Parse the Spread Spectrum Configuration(SSC) from device tree and configure them before using the clock. Each SSC is three u32 elements which means '', so assigned-clock-sscs is an array of multiple three u32 elements. Reviewed-by: Brian Masney Reviewed-by: Sebin Francis Signed-off-by: Peng Fan Signed-off-by: Brian Masney --- drivers/clk/clk-conf.c | 76 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 76 insertions(+) diff --git a/drivers/clk/clk-conf.c b/drivers/clk/clk-conf.c index 303a0bb26e54..550b8ae375a2 100644 --- a/drivers/clk/clk-conf.c +++ b/drivers/clk/clk-conf.c @@ -155,6 +155,78 @@ static int __set_clk_rates(struct device_node *node, bool clk_supplier) return 0; } +static int __set_clk_spread_spectrum(struct device_node *node, bool clk_supplier) +{ + u32 elem_size = sizeof(struct clk_spread_spectrum); + struct clk_spread_spectrum *sscs; + struct of_phandle_args clkspec; + int rc, count, index; + struct clk *clk; + + /* modfreq, spreadPercent, modmethod */ + count = of_property_count_elems_of_size(node, "assigned-clock-sscs", elem_size); + if (count <= 0) + return 0; + + sscs = kcalloc(count, elem_size, GFP_KERNEL); + if (!sscs) + return -ENOMEM; + + rc = of_property_read_u32_array(node, "assigned-clock-sscs", (u32 *)sscs, + count * 3); + if (rc) + goto free_sscs; + + for (index = 0; index < count; index++) { + struct clk_spread_spectrum *conf = &sscs[index]; + struct clk_hw *hw; + + if (!conf->modfreq_hz && !conf->spread_bp && !conf->method) + continue; + + rc = of_parse_phandle_with_args(node, "assigned-clocks", "#clock-cells", + index, &clkspec); + if (rc < 0) { + /* skip empty (null) phandles */ + if (rc == -ENOENT) { + rc = 0; + continue; + } else + goto free_sscs; + } + + if (clkspec.np == node && !clk_supplier) { + of_node_put(clkspec.np); + goto free_sscs; + } + + clk = of_clk_get_from_provider(&clkspec); + of_node_put(clkspec.np); + if (IS_ERR(clk)) { + if (PTR_ERR(clk) != -EPROBE_DEFER) + pr_warn("clk: couldn't get clock %d for %pOF\n", + index, node); + rc = PTR_ERR(clk); + goto free_sscs; + } + + hw = __clk_get_hw(clk); + rc = clk_hw_set_spread_spectrum(hw, conf); + if (rc < 0) { + pr_err("clk: couldn't set %s clk spread spectrum %u %u %u: %d\n", + __clk_get_name(clk), conf->modfreq_hz, conf->spread_bp, + conf->method, rc); + /* Do not fail */ + rc = 0; + } + clk_put(clk); + } + +free_sscs: + kfree(sscs); + return rc; +} + /** * of_clk_set_defaults() - parse and set assigned clocks configuration * @node: device node to apply clock settings for @@ -174,6 +246,10 @@ int of_clk_set_defaults(struct device_node *node, bool clk_supplier) if (!node) return 0; + rc = __set_clk_spread_spectrum(node, clk_supplier); + if (rc < 0) + return rc; + rc = __set_clk_parents(node, clk_supplier); if (rc < 0) return rc; From 7d98de6ffdab5bf65854f2464be362f33a606238 Mon Sep 17 00:00:00 2001 From: Peng Fan Date: Fri, 12 Jun 2026 16:46:26 +0800 Subject: [PATCH 08/53] clk: Add KUnit tests for assigned-clock-sscs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add KUnit test coverage for the assigned-clock-sscs DT property that configures spread spectrum on clocks before they are used. Extend the existing test infrastructure to support spread spectrum: - Add struct clk_spread_spectrum field to clk_dummy_context and a clk_dummy_set_spread_spectrum callback - Wire set_spread_spectrum into all dummy clock ops - Extend clk_assigned_rates_register_clk and test parameter struct to propagate initial SSCS values Add a new separate test suite clk_assigned_sscs with three categories: 1. clk_assigned_sscs_assigns_one — verifies that a single assigned-clock-sscs entry correctly configures spread spectrum on one clock, testing both provider and consumer paths 2. clk_assigned_sscs_assigns_multiple — verifies that multiple assigned-clock-sscs entries configure spread spectrum on two clocks, testing both provider and consumer paths 3. clk_assigned_sscs_skips — verifies that malformed DT properties are correctly skipped without error: missing assigned-clocks, zero-valued SSCS, and null phandles, tested for both provider and consumer scenarios New DT overlays are added for all test scenarios: - kunit_clk_assigned_sscs_one{,consumer} — single valid entry - kunit_clk_assigned_sscs_multiple{,consumer} — two valid entries - kunit_clk_assigned_sscs_without{,consumer} — missing assigned-clocks - kunit_clk_assigned_sscs_zero{,consumer} — all-zero SSCS values - kunit_clk_assigned_sscs_null{,consumer} — null phandle Co-developed-by: Brian Masney Signed-off-by: Peng Fan Signed-off-by: Brian Masney --- drivers/clk/Makefile | 10 + drivers/clk/clk_test.c | 203 +++++++++++++++++- drivers/clk/kunit_clk_assigned_rates.h | 10 + ...kunit_clk_assigned_rates_u64_multiple.dtso | 6 + ..._assigned_rates_u64_multiple_consumer.dtso | 6 + .../clk/kunit_clk_assigned_rates_u64_one.dtso | 3 + ...t_clk_assigned_rates_u64_one_consumer.dtso | 3 + .../clk/kunit_clk_assigned_sscs_multiple.dtso | 20 ++ ...t_clk_assigned_sscs_multiple_consumer.dtso | 24 +++ drivers/clk/kunit_clk_assigned_sscs_null.dtso | 16 ++ ...kunit_clk_assigned_sscs_null_consumer.dtso | 20 ++ drivers/clk/kunit_clk_assigned_sscs_one.dtso | 16 ++ .../kunit_clk_assigned_sscs_one_consumer.dtso | 20 ++ .../clk/kunit_clk_assigned_sscs_without.dtso | 15 ++ ...it_clk_assigned_sscs_without_consumer.dtso | 19 ++ drivers/clk/kunit_clk_assigned_sscs_zero.dtso | 12 ++ ...kunit_clk_assigned_sscs_zero_consumer.dtso | 16 ++ 17 files changed, 416 insertions(+), 3 deletions(-) create mode 100644 drivers/clk/kunit_clk_assigned_sscs_multiple.dtso create mode 100644 drivers/clk/kunit_clk_assigned_sscs_multiple_consumer.dtso create mode 100644 drivers/clk/kunit_clk_assigned_sscs_null.dtso create mode 100644 drivers/clk/kunit_clk_assigned_sscs_null_consumer.dtso create mode 100644 drivers/clk/kunit_clk_assigned_sscs_one.dtso create mode 100644 drivers/clk/kunit_clk_assigned_sscs_one_consumer.dtso create mode 100644 drivers/clk/kunit_clk_assigned_sscs_without.dtso create mode 100644 drivers/clk/kunit_clk_assigned_sscs_without_consumer.dtso create mode 100644 drivers/clk/kunit_clk_assigned_sscs_zero.dtso create mode 100644 drivers/clk/kunit_clk_assigned_sscs_zero_consumer.dtso diff --git a/drivers/clk/Makefile b/drivers/clk/Makefile index cc108a75a900..6a726331b6c9 100644 --- a/drivers/clk/Makefile +++ b/drivers/clk/Makefile @@ -18,6 +18,16 @@ clk-test-y := clk_test.o \ kunit_clk_assigned_rates_without_consumer.dtbo.o \ kunit_clk_assigned_rates_zero.dtbo.o \ kunit_clk_assigned_rates_zero_consumer.dtbo.o \ + kunit_clk_assigned_sscs_one.dtbo.o \ + kunit_clk_assigned_sscs_one_consumer.dtbo.o \ + kunit_clk_assigned_sscs_multiple.dtbo.o \ + kunit_clk_assigned_sscs_multiple_consumer.dtbo.o \ + kunit_clk_assigned_sscs_null.dtbo.o \ + kunit_clk_assigned_sscs_null_consumer.dtbo.o \ + kunit_clk_assigned_sscs_without.dtbo.o \ + kunit_clk_assigned_sscs_without_consumer.dtbo.o \ + kunit_clk_assigned_sscs_zero.dtbo.o \ + kunit_clk_assigned_sscs_zero_consumer.dtbo.o \ kunit_clk_hw_get_dev_of_node.dtbo.o \ kunit_clk_parent_data_test.dtbo.o obj-$(CONFIG_COMMON_CLK) += clk-divider.o diff --git a/drivers/clk/clk_test.c b/drivers/clk/clk_test.c index b1961daac5e2..824adc95e0b2 100644 --- a/drivers/clk/clk_test.c +++ b/drivers/clk/clk_test.c @@ -28,6 +28,7 @@ static const struct clk_ops empty_clk_ops = { }; struct clk_dummy_context { struct clk_hw hw; unsigned long rate; + struct clk_spread_spectrum sscs; }; static unsigned long clk_dummy_recalc_rate(struct clk_hw *hw, @@ -83,6 +84,17 @@ static int clk_dummy_set_rate(struct clk_hw *hw, return 0; } +static int clk_dummy_set_spread_spectrum(struct clk_hw *hw, + const struct clk_spread_spectrum *ss_conf) +{ + struct clk_dummy_context *ctx = + container_of(hw, struct clk_dummy_context, hw); + + ctx->sscs = *ss_conf; + + return 0; +} + static int clk_dummy_single_set_parent(struct clk_hw *hw, u8 index) { if (index >= clk_hw_get_num_parents(hw)) @@ -100,18 +112,21 @@ static const struct clk_ops clk_dummy_rate_ops = { .recalc_rate = clk_dummy_recalc_rate, .determine_rate = clk_dummy_determine_rate, .set_rate = clk_dummy_set_rate, + .set_spread_spectrum = clk_dummy_set_spread_spectrum, }; static const struct clk_ops clk_dummy_maximize_rate_ops = { .recalc_rate = clk_dummy_recalc_rate, .determine_rate = clk_dummy_maximize_rate, .set_rate = clk_dummy_set_rate, + .set_spread_spectrum = clk_dummy_set_spread_spectrum, }; static const struct clk_ops clk_dummy_minimize_rate_ops = { .recalc_rate = clk_dummy_recalc_rate, .determine_rate = clk_dummy_minimize_rate, .set_rate = clk_dummy_set_rate, + .set_spread_spectrum = clk_dummy_set_spread_spectrum, }; static const struct clk_ops clk_dummy_single_parent_ops = { @@ -3097,6 +3112,7 @@ struct clk_assigned_rates_context { * @overlay_end: Pointer to end of DT overlay to apply for test * @rate0: Initial rate of first clk * @rate1: Initial rate of second clk + * @sscs: Initial spread spectrum settings * @consumer_test: true if a consumer is being tested */ struct clk_assigned_rates_test_param { @@ -3105,6 +3121,7 @@ struct clk_assigned_rates_test_param { u8 *overlay_end; unsigned long rate0; unsigned long rate1; + struct clk_spread_spectrum sscs; bool consumer_test; }; @@ -3116,7 +3133,7 @@ static void clk_assigned_rates_register_clk(struct kunit *test, struct clk_dummy_context *ctx, struct device_node *np, const char *name, - unsigned long rate) + unsigned long rate, const struct clk_spread_spectrum *sscs) { struct clk_init_data init = { }; @@ -3124,6 +3141,7 @@ clk_assigned_rates_register_clk(struct kunit *test, init.ops = &clk_dummy_rate_ops; ctx->hw.init = &init; ctx->rate = rate; + ctx->sscs = *sscs; KUNIT_ASSERT_EQ(test, 0, of_clk_hw_register_kunit(test, np, &ctx->hw)); KUNIT_ASSERT_EQ(test, ctx->rate, rate); @@ -3167,14 +3185,16 @@ static int clk_assigned_rates_test_init(struct kunit *test) KUNIT_ASSERT_LT(test, clk_cells, 2); clk_assigned_rates_register_clk(test, &ctx->clk0, np, - "test_assigned_rate0", test_param->rate0); + "test_assigned_rate0", test_param->rate0, + &test_param->sscs); if (clk_cells == 0) { KUNIT_ASSERT_EQ(test, 0, of_clk_add_hw_provider_kunit(test, np, of_clk_hw_simple_get, &ctx->clk0.hw)); } else if (clk_cells == 1) { clk_assigned_rates_register_clk(test, &ctx->clk1, np, - "test_assigned_rate1", test_param->rate1); + "test_assigned_rate1", test_param->rate1, + &test_param->sscs); KUNIT_ASSERT_NOT_ERR_OR_NULL(test, data = kunit_kzalloc(test, struct_size(data, hws, 2), GFP_KERNEL)); @@ -3403,6 +3423,182 @@ static struct kunit_suite clk_assigned_rates_suite = { .init = clk_assigned_rates_test_init, }; +OF_OVERLAY_DECLARE(kunit_clk_assigned_sscs_one); +OF_OVERLAY_DECLARE(kunit_clk_assigned_sscs_one_consumer); +OF_OVERLAY_DECLARE(kunit_clk_assigned_sscs_multiple); +OF_OVERLAY_DECLARE(kunit_clk_assigned_sscs_multiple_consumer); +OF_OVERLAY_DECLARE(kunit_clk_assigned_sscs_without); +OF_OVERLAY_DECLARE(kunit_clk_assigned_sscs_without_consumer); +OF_OVERLAY_DECLARE(kunit_clk_assigned_sscs_zero); +OF_OVERLAY_DECLARE(kunit_clk_assigned_sscs_zero_consumer); +OF_OVERLAY_DECLARE(kunit_clk_assigned_sscs_null); +OF_OVERLAY_DECLARE(kunit_clk_assigned_sscs_null_consumer); + +static void clk_assigned_sscs_assigns_one(struct kunit *test) +{ + struct clk_assigned_rates_context *ctx = test->priv; + + KUNIT_EXPECT_EQ(test, ctx->clk0.sscs.modfreq_hz, ASSIGNED_SSCS_0_MODFREQ); + KUNIT_EXPECT_EQ(test, ctx->clk0.sscs.spread_bp, ASSIGNED_SSCS_0_SPREAD); + KUNIT_EXPECT_EQ(test, ctx->clk0.sscs.method, ASSIGNED_SSCS_0_METHOD); +} + +/* Test cases that assign sscs for one clk */ +static const struct clk_assigned_rates_test_param clk_assigned_sscs_assigns_one_test_params[] = { + { + /* + * Test that a single cell assigned-clock-sscs property + * assigns the sscs when the property is in the provider. + */ + .desc = "provider assigns", + TEST_PARAM_OVERLAY(kunit_clk_assigned_sscs_one), + }, + { + /* + * Test that a single cell assigned-clock-sscs property + * assigns the sscs when the property is in the consumer. + */ + .desc = "consumer assigns", + TEST_PARAM_OVERLAY(kunit_clk_assigned_sscs_one_consumer), + .consumer_test = true, + }, +}; +KUNIT_ARRAY_PARAM_DESC(clk_assigned_sscs_assigns_one, + clk_assigned_sscs_assigns_one_test_params, desc) + +static void clk_assigned_sscs_assigns_multiple(struct kunit *test) +{ + struct clk_assigned_rates_context *ctx = test->priv; + + KUNIT_EXPECT_EQ(test, ctx->clk0.sscs.modfreq_hz, ASSIGNED_SSCS_0_MODFREQ); + KUNIT_EXPECT_EQ(test, ctx->clk0.sscs.spread_bp, ASSIGNED_SSCS_0_SPREAD); + KUNIT_EXPECT_EQ(test, ctx->clk0.sscs.method, ASSIGNED_SSCS_0_METHOD); + KUNIT_EXPECT_EQ(test, ctx->clk1.sscs.modfreq_hz, ASSIGNED_SSCS_1_MODFREQ); + KUNIT_EXPECT_EQ(test, ctx->clk1.sscs.spread_bp, ASSIGNED_SSCS_1_SPREAD); + KUNIT_EXPECT_EQ(test, ctx->clk1.sscs.method, ASSIGNED_SSCS_1_METHOD); +} + +/* Test cases that assign sscs for multiple clks */ +static const +struct clk_assigned_rates_test_param clk_assigned_sscs_assigns_multiple_test_params[] = { + { + /* + * Test that a multiple cell assigned-clock-sscs property + * assigns the sscs when the property is in the provider. + */ + .desc = "provider assigns", + TEST_PARAM_OVERLAY(kunit_clk_assigned_sscs_multiple), + }, + { + /* + * Test that a multiple cell assigned-clock-sscs property + * assigns the sscs when the property is in the consumer. + */ + .desc = "consumer assigns", + TEST_PARAM_OVERLAY(kunit_clk_assigned_sscs_multiple_consumer), + .consumer_test = true, + }, +}; +KUNIT_ARRAY_PARAM_DESC(clk_assigned_sscs_assigns_multiple, + clk_assigned_sscs_assigns_multiple_test_params, + desc) + +static void clk_assigned_sscs_skips(struct kunit *test) +{ + struct clk_assigned_rates_context *ctx = test->priv; + const struct clk_assigned_rates_test_param *test_param = test->param_value; + + KUNIT_EXPECT_NE(test, ctx->clk0.sscs.modfreq_hz, ASSIGNED_SSCS_0_MODFREQ); + KUNIT_EXPECT_NE(test, ctx->clk0.sscs.spread_bp, ASSIGNED_SSCS_0_SPREAD); + KUNIT_EXPECT_NE(test, ctx->clk0.sscs.method, ASSIGNED_SSCS_0_METHOD); + KUNIT_EXPECT_EQ(test, ctx->clk0.sscs.modfreq_hz, test_param->sscs.modfreq_hz); + KUNIT_EXPECT_EQ(test, ctx->clk0.sscs.spread_bp, test_param->sscs.spread_bp); + KUNIT_EXPECT_EQ(test, ctx->clk0.sscs.method, test_param->sscs.method); +} + +/* Test cases that skip changing the sscs due to malformed DT */ +static const struct clk_assigned_rates_test_param clk_assigned_sscs_skips_test_params[] = { + { + /* + * Test that an assigned-clock-sscs property without an assigned-clocks + * property fails when the property is in the provider. + */ + .desc = "provider missing assigned-clocks", + TEST_PARAM_OVERLAY(kunit_clk_assigned_sscs_without), + .sscs = {50000, 60000, 3}, + }, + { + /* + * Test that an assigned-clock-sscs property without an assigned-clocks + * property fails when the property is in the consumer. + */ + .desc = "consumer missing assigned-clocks", + TEST_PARAM_OVERLAY(kunit_clk_assigned_sscs_without_consumer), + .sscs = {50000, 60000, 3}, + .consumer_test = true, + }, + { + /* + * Test that an assigned-clock-sscs property of zero doesn't + * set sscs when the property is in the provider. + */ + .desc = "provider assigned-clock-sscs of zero", + TEST_PARAM_OVERLAY(kunit_clk_assigned_sscs_zero), + .sscs = {50000, 60000, 3}, + }, + { + /* + * Test that an assigned-clock-sscs property of zero doesn't + * set sscs when the property is in the consumer. + */ + .desc = "consumer assigned-clock-sscs of zero", + TEST_PARAM_OVERLAY(kunit_clk_assigned_sscs_zero_consumer), + .sscs = {50000, 60000, 3}, + .consumer_test = true, + }, + { + /* + * Test that an assigned-clocks property with a null phandle + * doesn't set sscs when the property is in the provider. + */ + .desc = "provider assigned-clocks null phandle", + TEST_PARAM_OVERLAY(kunit_clk_assigned_sscs_null), + .sscs = {50000, 60000, 3}, + }, + { + /* + * Test that an assigned-clocks property with a null phandle + * doesn't set sscs when the property is in the consumer. + */ + .desc = "consumer assigned-clocks null phandle", + TEST_PARAM_OVERLAY(kunit_clk_assigned_sscs_null_consumer), + .sscs = {50000, 60000, 3}, + .consumer_test = true, + }, +}; +KUNIT_ARRAY_PARAM_DESC(clk_assigned_sscs_skips, + clk_assigned_sscs_skips_test_params, + desc) + +static struct kunit_case clk_assigned_sscs_test_cases[] = { + KUNIT_CASE_PARAM(clk_assigned_sscs_assigns_one, + clk_assigned_sscs_assigns_one_gen_params), + KUNIT_CASE_PARAM(clk_assigned_sscs_assigns_multiple, + clk_assigned_sscs_assigns_multiple_gen_params), + KUNIT_CASE_PARAM(clk_assigned_sscs_skips, + clk_assigned_sscs_skips_gen_params), + {} +}; + +/* + * Test suite for assigned-clock-sscs DT property. + */ +static struct kunit_suite clk_assigned_sscs_suite = { + .name = "clk_assigned_sscs", + .test_cases = clk_assigned_sscs_test_cases, + .init = clk_assigned_rates_test_init, +}; + static const struct clk_init_data clk_hw_get_dev_of_node_init_data = { .name = "clk_hw_get_dev_of_node", .ops = &empty_clk_ops, @@ -3544,6 +3740,7 @@ static struct kunit_suite clk_hw_get_dev_of_node_test_suite = { kunit_test_suites( &clk_assigned_rates_suite, + &clk_assigned_sscs_suite, &clk_hw_get_dev_of_node_test_suite, &clk_leaf_mux_set_rate_parent_test_suite, &clk_test_suite, diff --git a/drivers/clk/kunit_clk_assigned_rates.h b/drivers/clk/kunit_clk_assigned_rates.h index df2d84dcaa93..d7ae5ec2d25b 100644 --- a/drivers/clk/kunit_clk_assigned_rates.h +++ b/drivers/clk/kunit_clk_assigned_rates.h @@ -1,8 +1,18 @@ /* SPDX-License-Identifier: GPL-2.0 */ + +#include + #ifndef _KUNIT_CLK_ASSIGNED_RATES_H #define _KUNIT_CLK_ASSIGNED_RATES_H #define ASSIGNED_RATES_0_RATE 1600000 #define ASSIGNED_RATES_1_RATE 9700000 +#define ASSIGNED_SSCS_0_MODFREQ 10000 +#define ASSIGNED_SSCS_0_SPREAD 30000 +#define ASSIGNED_SSCS_0_METHOD CLK_SSC_CENTER_SPREAD +#define ASSIGNED_SSCS_1_MODFREQ 20000 +#define ASSIGNED_SSCS_1_SPREAD 40000 +#define ASSIGNED_SSCS_1_METHOD CLK_SSC_UP_SPREAD + #endif diff --git a/drivers/clk/kunit_clk_assigned_rates_u64_multiple.dtso b/drivers/clk/kunit_clk_assigned_rates_u64_multiple.dtso index 389b4e2eb7f7..3a717dab2d00 100644 --- a/drivers/clk/kunit_clk_assigned_rates_u64_multiple.dtso +++ b/drivers/clk/kunit_clk_assigned_rates_u64_multiple.dtso @@ -12,5 +12,11 @@ <&clk 1>; assigned-clock-rates-u64 = /bits/ 64 , /bits/ 64 ; + assigned-clock-sscs = , + ; }; }; diff --git a/drivers/clk/kunit_clk_assigned_rates_u64_multiple_consumer.dtso b/drivers/clk/kunit_clk_assigned_rates_u64_multiple_consumer.dtso index 3e117fd59b7d..cbee7cbad068 100644 --- a/drivers/clk/kunit_clk_assigned_rates_u64_multiple_consumer.dtso +++ b/drivers/clk/kunit_clk_assigned_rates_u64_multiple_consumer.dtso @@ -16,5 +16,11 @@ <&clk 1>; assigned-clock-rates-u64 = /bits/ 64 , /bits/ 64 ; + assigned-clock-sscs = , + ; }; }; diff --git a/drivers/clk/kunit_clk_assigned_rates_u64_one.dtso b/drivers/clk/kunit_clk_assigned_rates_u64_one.dtso index 87041264e8f5..9b04d6927f08 100644 --- a/drivers/clk/kunit_clk_assigned_rates_u64_one.dtso +++ b/drivers/clk/kunit_clk_assigned_rates_u64_one.dtso @@ -10,5 +10,8 @@ #clock-cells = <0>; assigned-clocks = <&clk>; assigned-clock-rates-u64 = /bits/ 64 ; + assigned-clock-sscs = ; }; }; diff --git a/drivers/clk/kunit_clk_assigned_rates_u64_one_consumer.dtso b/drivers/clk/kunit_clk_assigned_rates_u64_one_consumer.dtso index 3259c003aec0..4784d40520f4 100644 --- a/drivers/clk/kunit_clk_assigned_rates_u64_one_consumer.dtso +++ b/drivers/clk/kunit_clk_assigned_rates_u64_one_consumer.dtso @@ -14,5 +14,8 @@ compatible = "test,clk-consumer"; assigned-clocks = <&clk>; assigned-clock-rates-u64 = /bits/ 64 ; + assigned-clock-sscs = ; }; }; diff --git a/drivers/clk/kunit_clk_assigned_sscs_multiple.dtso b/drivers/clk/kunit_clk_assigned_sscs_multiple.dtso new file mode 100644 index 000000000000..e3472f95987c --- /dev/null +++ b/drivers/clk/kunit_clk_assigned_sscs_multiple.dtso @@ -0,0 +1,20 @@ +// SPDX-License-Identifier: GPL-2.0 +/dts-v1/; +/plugin/; + +#include "kunit_clk_assigned_rates.h" + +&{/} { + clk: kunit-clock { + compatible = "test,clk-assigned-rates"; + #clock-cells = <1>; + assigned-clocks = <&clk 0>, + <&clk 1>; + assigned-clock-sscs = , + ; + }; +}; diff --git a/drivers/clk/kunit_clk_assigned_sscs_multiple_consumer.dtso b/drivers/clk/kunit_clk_assigned_sscs_multiple_consumer.dtso new file mode 100644 index 000000000000..6e8971bd272a --- /dev/null +++ b/drivers/clk/kunit_clk_assigned_sscs_multiple_consumer.dtso @@ -0,0 +1,24 @@ +// SPDX-License-Identifier: GPL-2.0 +/dts-v1/; +/plugin/; + +#include "kunit_clk_assigned_rates.h" + +&{/} { + clk: kunit-clock { + compatible = "test,clk-assigned-rates"; + #clock-cells = <1>; + }; + + kunit-clock-consumer { + compatible = "test,clk-consumer"; + assigned-clocks = <&clk 0>, + <&clk 1>; + assigned-clock-sscs = , + ; + }; +}; diff --git a/drivers/clk/kunit_clk_assigned_sscs_null.dtso b/drivers/clk/kunit_clk_assigned_sscs_null.dtso new file mode 100644 index 000000000000..43b2068c845d --- /dev/null +++ b/drivers/clk/kunit_clk_assigned_sscs_null.dtso @@ -0,0 +1,16 @@ +// SPDX-License-Identifier: GPL-2.0 +/dts-v1/; +/plugin/; + +#include "kunit_clk_assigned_rates.h" + +&{/} { + clk: kunit-clock { + compatible = "test,clk-assigned-rates"; + #clock-cells = <0>; + assigned-clocks = <0>; + assigned-clock-sscs = ; + }; +}; diff --git a/drivers/clk/kunit_clk_assigned_sscs_null_consumer.dtso b/drivers/clk/kunit_clk_assigned_sscs_null_consumer.dtso new file mode 100644 index 000000000000..bda008f5aaa3 --- /dev/null +++ b/drivers/clk/kunit_clk_assigned_sscs_null_consumer.dtso @@ -0,0 +1,20 @@ +// SPDX-License-Identifier: GPL-2.0 +/dts-v1/; +/plugin/; + +#include "kunit_clk_assigned_rates.h" + +&{/} { + clk: kunit-clock { + compatible = "test,clk-assigned-rates"; + #clock-cells = <0>; + }; + + kunit-clock-consumer { + compatible = "test,clk-consumer"; + assigned-clocks = <0>; + assigned-clock-sscs = ; + }; +}; diff --git a/drivers/clk/kunit_clk_assigned_sscs_one.dtso b/drivers/clk/kunit_clk_assigned_sscs_one.dtso new file mode 100644 index 000000000000..91f585b5d8c9 --- /dev/null +++ b/drivers/clk/kunit_clk_assigned_sscs_one.dtso @@ -0,0 +1,16 @@ +// SPDX-License-Identifier: GPL-2.0 +/dts-v1/; +/plugin/; + +#include "kunit_clk_assigned_rates.h" + +&{/} { + clk: kunit-clock { + compatible = "test,clk-assigned-rates"; + #clock-cells = <0>; + assigned-clocks = <&clk>; + assigned-clock-sscs = ; + }; +}; diff --git a/drivers/clk/kunit_clk_assigned_sscs_one_consumer.dtso b/drivers/clk/kunit_clk_assigned_sscs_one_consumer.dtso new file mode 100644 index 000000000000..0bc8a03c2041 --- /dev/null +++ b/drivers/clk/kunit_clk_assigned_sscs_one_consumer.dtso @@ -0,0 +1,20 @@ +// SPDX-License-Identifier: GPL-2.0 +/dts-v1/; +/plugin/; + +#include "kunit_clk_assigned_rates.h" + +&{/} { + clk: kunit-clock { + compatible = "test,clk-assigned-rates"; + #clock-cells = <0>; + }; + + kunit-clock-consumer { + compatible = "test,clk-consumer"; + assigned-clocks = <&clk>; + assigned-clock-sscs = ; + }; +}; diff --git a/drivers/clk/kunit_clk_assigned_sscs_without.dtso b/drivers/clk/kunit_clk_assigned_sscs_without.dtso new file mode 100644 index 000000000000..08660846b55c --- /dev/null +++ b/drivers/clk/kunit_clk_assigned_sscs_without.dtso @@ -0,0 +1,15 @@ +// SPDX-License-Identifier: GPL-2.0 +/dts-v1/; +/plugin/; + +#include "kunit_clk_assigned_rates.h" + +&{/} { + clk: kunit-clock { + compatible = "test,clk-assigned-rates"; + #clock-cells = <0>; + assigned-clock-sscs = ; + }; +}; diff --git a/drivers/clk/kunit_clk_assigned_sscs_without_consumer.dtso b/drivers/clk/kunit_clk_assigned_sscs_without_consumer.dtso new file mode 100644 index 000000000000..e1c089c6f0c0 --- /dev/null +++ b/drivers/clk/kunit_clk_assigned_sscs_without_consumer.dtso @@ -0,0 +1,19 @@ +// SPDX-License-Identifier: GPL-2.0 +/dts-v1/; +/plugin/; + +#include "kunit_clk_assigned_rates.h" + +&{/} { + clk: kunit-clock { + compatible = "test,clk-assigned-rates"; + #clock-cells = <0>; + }; + + kunit-clock-consumer { + compatible = "test,clk-consumer"; + assigned-clock-sscs = ; + }; +}; diff --git a/drivers/clk/kunit_clk_assigned_sscs_zero.dtso b/drivers/clk/kunit_clk_assigned_sscs_zero.dtso new file mode 100644 index 000000000000..f39f4e754e53 --- /dev/null +++ b/drivers/clk/kunit_clk_assigned_sscs_zero.dtso @@ -0,0 +1,12 @@ +// SPDX-License-Identifier: GPL-2.0 +/dts-v1/; +/plugin/; + +&{/} { + clk: kunit-clock { + compatible = "test,clk-assigned-rates"; + #clock-cells = <0>; + assigned-clocks = <&clk>; + assigned-clock-sscs = <0 0 0>; + }; +}; diff --git a/drivers/clk/kunit_clk_assigned_sscs_zero_consumer.dtso b/drivers/clk/kunit_clk_assigned_sscs_zero_consumer.dtso new file mode 100644 index 000000000000..d6bd7dfada7e --- /dev/null +++ b/drivers/clk/kunit_clk_assigned_sscs_zero_consumer.dtso @@ -0,0 +1,16 @@ +// SPDX-License-Identifier: GPL-2.0 +/dts-v1/; +/plugin/; + +&{/} { + clk: kunit-clock { + compatible = "test,clk-assigned-rates"; + #clock-cells = <0>; + }; + + kunit-clock-consumer { + compatible = "test,clk-consumer"; + assigned-clocks = <&clk>; + assigned-clock-sscs = <0 0 0>; + }; +}; From a73edd3ed8d3c633be4fbe101ae9756222249a5f Mon Sep 17 00:00:00 2001 From: Peng Fan Date: Fri, 12 Jun 2026 16:46:27 +0800 Subject: [PATCH 09/53] clk: scmi: Introduce common header for SCMI clock interface Added a new header file 'clk-scmi.h' to define common structures and interfaces for the SCMI clock driver. This header will also be used by OEM-specific extensions to ensure consistency and reusability. Moved relevant structure definitions from the driver implementation to 'clk-scmi.h' to facilitate shared usage. Reviewed-by: Sebin Francis Reviewed-by: Cristian Marussi Signed-off-by: Peng Fan Signed-off-by: Brian Masney --- drivers/clk/clk-scmi.c | 27 +-------------------------- drivers/clk/clk-scmi.h | 40 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 41 insertions(+), 26 deletions(-) create mode 100644 drivers/clk/clk-scmi.h diff --git a/drivers/clk/clk-scmi.c b/drivers/clk/clk-scmi.c index 7c562559ad8b..d88e78cc9a12 100644 --- a/drivers/clk/clk-scmi.c +++ b/drivers/clk/clk-scmi.c @@ -13,32 +13,7 @@ #include #include -#define NOT_ATOMIC false -#define ATOMIC true - -enum scmi_clk_feats { - SCMI_CLK_ATOMIC_SUPPORTED, - SCMI_CLK_STATE_CTRL_SUPPORTED, - SCMI_CLK_RATE_CTRL_SUPPORTED, - SCMI_CLK_PARENT_CTRL_SUPPORTED, - SCMI_CLK_DUTY_CYCLE_SUPPORTED, - SCMI_CLK_FEATS_COUNT -}; - -#define SCMI_MAX_CLK_OPS BIT(SCMI_CLK_FEATS_COUNT) - -static const struct scmi_clk_proto_ops *scmi_proto_clk_ops; - -struct scmi_clk { - u32 id; - struct device *dev; - struct clk_hw hw; - const struct scmi_clock_info *info; - const struct scmi_protocol_handle *ph; - struct clk_parent_data *parent_data; -}; - -#define to_scmi_clk(clk) container_of(clk, struct scmi_clk, hw) +const struct scmi_clk_proto_ops *scmi_proto_clk_ops; static unsigned long scmi_clk_recalc_rate(struct clk_hw *hw, unsigned long parent_rate) diff --git a/drivers/clk/clk-scmi.h b/drivers/clk/clk-scmi.h new file mode 100644 index 000000000000..6ef6adc77c83 --- /dev/null +++ b/drivers/clk/clk-scmi.h @@ -0,0 +1,40 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright 2025 NXP + */ + +#ifndef __SCMI_CLK_H +#define __SCMI_CLK_H + +#include +#include +#include + +#define NOT_ATOMIC false +#define ATOMIC true + +enum scmi_clk_feats { + SCMI_CLK_ATOMIC_SUPPORTED, + SCMI_CLK_STATE_CTRL_SUPPORTED, + SCMI_CLK_RATE_CTRL_SUPPORTED, + SCMI_CLK_PARENT_CTRL_SUPPORTED, + SCMI_CLK_DUTY_CYCLE_SUPPORTED, + SCMI_CLK_FEATS_COUNT +}; + +#define SCMI_MAX_CLK_OPS BIT(SCMI_CLK_FEATS_COUNT) + +struct scmi_clk { + u32 id; + struct device *dev; + struct clk_hw hw; + const struct scmi_clock_info *info; + const struct scmi_protocol_handle *ph; + struct clk_parent_data *parent_data; +}; + +#define to_scmi_clk(clk) container_of(clk, struct scmi_clk, hw) + +extern const struct scmi_clk_proto_ops *scmi_proto_clk_ops; + +#endif From 77369b1e6a37134f4c7bf9c175c7c4ec36c3ee11 Mon Sep 17 00:00:00 2001 From: Peng Fan Date: Fri, 12 Jun 2026 16:46:28 +0800 Subject: [PATCH 10/53] clk: scmi: Add i.MX95 OEM extension support for SCMI clock driver - Introduce 'clk-scmi-oem.c' to support vendor-specific OEM extensions for the SCMI clock driver, allows clean integration of vendor-specific features without impacting the core SCMI clock driver logic. - Extend 'clk-scmi.h' with 'scmi_clk_oem' structure and related declarations. - Initialize OEM extensions via 'scmi_clk_oem_init()'. - Support querying OEM-specific features and setting spread spectrum. - Pass 'scmi_device' to 'scmi_clk_ops_select()' for OEM data access. Reviewed-by: Sebin Francis Reviewed-by: Cristian Marussi Signed-off-by: Peng Fan Signed-off-by: Brian Masney --- drivers/clk/Makefile | 2 +- drivers/clk/clk-scmi-oem.c | 108 +++++++++++++++++++++++++++++++++++++ drivers/clk/clk-scmi.c | 19 +++++-- drivers/clk/clk-scmi.h | 11 ++++ 4 files changed, 136 insertions(+), 4 deletions(-) create mode 100644 drivers/clk/clk-scmi-oem.c diff --git a/drivers/clk/Makefile b/drivers/clk/Makefile index 6a726331b6c9..c2ae700ec0f2 100644 --- a/drivers/clk/Makefile +++ b/drivers/clk/Makefile @@ -98,7 +98,7 @@ obj-$(CONFIG_COMMON_CLK_RP1) += clk-rp1.o obj-$(CONFIG_COMMON_CLK_RPMI) += clk-rpmi.o obj-$(CONFIG_COMMON_CLK_HI655X) += clk-hi655x.o obj-$(CONFIG_COMMON_CLK_S2MPS11) += clk-s2mps11.o -obj-$(CONFIG_COMMON_CLK_SCMI) += clk-scmi.o +obj-$(CONFIG_COMMON_CLK_SCMI) += clk-scmi.o clk-scmi-oem.o obj-$(CONFIG_COMMON_CLK_SCPI) += clk-scpi.o obj-$(CONFIG_COMMON_CLK_SI5341) += clk-si5341.o obj-$(CONFIG_COMMON_CLK_SI5351) += clk-si5351.o diff --git a/drivers/clk/clk-scmi-oem.c b/drivers/clk/clk-scmi-oem.c new file mode 100644 index 000000000000..be11d359b4ec --- /dev/null +++ b/drivers/clk/clk-scmi-oem.c @@ -0,0 +1,108 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * The Vendor OEM extension for System Control and Power Interface (SCMI) + * Protocol based clock driver + * + * Copyright 2025 NXP + */ + +#include +#include +#include +#include + +#include "clk-scmi.h" + +#define SCMI_CLOCK_CFG_IMX_SSC 0x80 +#define SCMI_CLOCK_IMX_SS_PERCENTAGE_MASK GENMASK(7, 0) +#define SCMI_CLOCK_IMX_SS_MOD_FREQ_MASK GENMASK(23, 8) +#define SCMI_CLOCK_IMX_SS_ENABLE_MASK BIT(24) + +/* + * Selection is based on SCMI vendor_id/sub_vendor_id and optional machine + * compatible string, without involving impl_ver. impl_ver‑specific behavior + * should be considered a bug and handled via SCMI Quirk framework. + */ +struct scmi_clk_oem_info { + char *vendor_id; + char *sub_vendor_id; + char *compatible; + const void *data; +}; + +static int +scmi_clk_imx_set_spread_spectrum(struct clk_hw *hw, + const struct clk_spread_spectrum *ss_conf) +{ + struct scmi_clk *clk = to_scmi_clk(hw); + int ret; + u32 val; + + /* + * extConfigValue[7:0] - spread percentage (%) + * extConfigValue[23:8] - Modulation Frequency + * extConfigValue[24] - Enable/Disable + * extConfigValue[31:25] - Reserved + */ + val = FIELD_PREP(SCMI_CLOCK_IMX_SS_PERCENTAGE_MASK, ss_conf->spread_bp / 10000); + val |= FIELD_PREP(SCMI_CLOCK_IMX_SS_MOD_FREQ_MASK, ss_conf->modfreq_hz); + if (ss_conf->method != CLK_SPREAD_NO) + val |= SCMI_CLOCK_IMX_SS_ENABLE_MASK; + ret = scmi_proto_clk_ops->config_oem_set(clk->ph, clk->id, + SCMI_CLOCK_CFG_IMX_SSC, + val, false); + if (ret) + dev_warn(clk->dev, + "Failed to set spread spectrum(%u,%u,%u) for clock ID %d\n", + ss_conf->modfreq_hz, ss_conf->spread_bp, ss_conf->method, + clk->id); + + return ret; +} + +static int +scmi_clk_imx_query_oem_feats(const struct scmi_protocol_handle *ph, u32 id, + unsigned int *feats_key) +{ + int ret; + u32 val; + + ret = scmi_proto_clk_ops->config_oem_get(ph, id, + SCMI_CLOCK_CFG_IMX_SSC, + &val, NULL, false); + if (!ret) + *feats_key |= BIT(SCMI_CLK_EXT_OEM_SSC_SUPPORTED); + + return 0; +} + +static const struct scmi_clk_oem scmi_clk_oem_imx = { + .query_ext_oem_feats = scmi_clk_imx_query_oem_feats, + .set_spread_spectrum = scmi_clk_imx_set_spread_spectrum, +}; + +static const struct scmi_clk_oem_info info[] = { + { SCMI_IMX_VENDOR, SCMI_IMX_SUBVENDOR, NULL, &scmi_clk_oem_imx }, +}; + +int scmi_clk_oem_init(struct scmi_device *sdev) +{ + const struct scmi_handle *handle = sdev->handle; + int i, size = ARRAY_SIZE(info); + + for (i = 0; i < size; i++) { + if (strcmp(handle->version->vendor_id, info[i].vendor_id) || + strcmp(handle->version->sub_vendor_id, info[i].sub_vendor_id)) + continue; + if (info[i].compatible && + !of_machine_is_compatible(info[i].compatible)) + continue; + + break; + } + + if (i < size) + dev_set_drvdata(&sdev->dev, (void *)info[i].data); + + return 0; +} diff --git a/drivers/clk/clk-scmi.c b/drivers/clk/clk-scmi.c index d88e78cc9a12..2dd50c5b4ea8 100644 --- a/drivers/clk/clk-scmi.c +++ b/drivers/clk/clk-scmi.c @@ -13,6 +13,8 @@ #include #include +#include "clk-scmi.h" + const struct scmi_clk_proto_ops *scmi_proto_clk_ops; static unsigned long scmi_clk_recalc_rate(struct clk_hw *hw, @@ -210,6 +212,7 @@ static int scmi_clk_ops_init(struct device *dev, struct scmi_clk *sclk, static const struct clk_ops * scmi_clk_ops_alloc(struct device *dev, unsigned long feats_key) { + struct scmi_clk_oem *oem_data = dev_get_drvdata(dev); struct clk_ops *ops; ops = devm_kzalloc(dev, sizeof(*ops), GFP_KERNEL); @@ -256,11 +259,15 @@ scmi_clk_ops_alloc(struct device *dev, unsigned long feats_key) ops->set_duty_cycle = scmi_clk_set_duty_cycle; } + if (oem_data && (feats_key & BIT(SCMI_CLK_EXT_OEM_SSC_SUPPORTED))) + ops->set_spread_spectrum = oem_data->set_spread_spectrum; + return ops; } /** * scmi_clk_ops_select() - Select a proper set of clock operations + * @sdev: pointer to the SCMI device * @sclk: A reference to an SCMI clock descriptor * @atomic_capable: A flag to indicate if atomic mode is supported by the * transport @@ -285,8 +292,8 @@ scmi_clk_ops_alloc(struct device *dev, unsigned long feats_key) * NULL otherwise. */ static const struct clk_ops * -scmi_clk_ops_select(struct scmi_clk *sclk, bool atomic_capable, - unsigned int atomic_threshold_us, +scmi_clk_ops_select(struct scmi_device *sdev, struct scmi_clk *sclk, + bool atomic_capable, unsigned int atomic_threshold_us, const struct clk_ops **clk_ops_db, size_t db_size) { int ret; @@ -294,6 +301,7 @@ scmi_clk_ops_select(struct scmi_clk *sclk, bool atomic_capable, const struct scmi_clock_info *ci = sclk->info; unsigned int feats_key = 0; const struct clk_ops *ops; + struct scmi_clk_oem *oem_data = dev_get_drvdata(&sdev->dev); /* * Note that when transport is atomic but SCMI protocol did not @@ -318,6 +326,9 @@ scmi_clk_ops_select(struct scmi_clk *sclk, bool atomic_capable, &val, NULL, false); if (!ret) feats_key |= BIT(SCMI_CLK_DUTY_CYCLE_SUPPORTED); + + if (oem_data && oem_data->query_ext_oem_feats) + oem_data->query_ext_oem_feats(sclk->ph, sclk->id, &feats_key); } if (WARN_ON(feats_key >= db_size)) @@ -375,6 +386,8 @@ static int scmi_clocks_probe(struct scmi_device *sdev) clk_data->num = count; hws = clk_data->hws; + scmi_clk_oem_init(sdev); + transport_is_atomic = handle->is_transport_atomic(handle, &atomic_threshold_us); @@ -406,7 +419,7 @@ static int scmi_clocks_probe(struct scmi_device *sdev) * to avoid sharing the devm_ allocated clk_ops between multiple * SCMI clk driver instances. */ - scmi_ops = scmi_clk_ops_select(sclk, transport_is_atomic, + scmi_ops = scmi_clk_ops_select(sdev, sclk, transport_is_atomic, atomic_threshold_us, scmi_clk_ops_db, ARRAY_SIZE(scmi_clk_ops_db)); diff --git a/drivers/clk/clk-scmi.h b/drivers/clk/clk-scmi.h index 6ef6adc77c83..d7f63f36c56d 100644 --- a/drivers/clk/clk-scmi.h +++ b/drivers/clk/clk-scmi.h @@ -7,6 +7,7 @@ #define __SCMI_CLK_H #include +#include #include #include @@ -19,6 +20,7 @@ enum scmi_clk_feats { SCMI_CLK_RATE_CTRL_SUPPORTED, SCMI_CLK_PARENT_CTRL_SUPPORTED, SCMI_CLK_DUTY_CYCLE_SUPPORTED, + SCMI_CLK_EXT_OEM_SSC_SUPPORTED, SCMI_CLK_FEATS_COUNT }; @@ -37,4 +39,13 @@ struct scmi_clk { extern const struct scmi_clk_proto_ops *scmi_proto_clk_ops; +struct scmi_clk_oem { + int (*query_ext_oem_feats)(const struct scmi_protocol_handle *ph, + u32 id, unsigned int *feats_key); + int (*set_spread_spectrum)(struct clk_hw *hw, + const struct clk_spread_spectrum *ss_conf); +}; + +int scmi_clk_oem_init(struct scmi_device *dev); + #endif From 9cb19714779f7fc53583e2b074a794329e96df0d Mon Sep 17 00:00:00 2001 From: Rosen Penev Date: Sun, 7 Jun 2026 22:16:02 -0700 Subject: [PATCH 11/53] clk: kirkwood: use kzalloc_flex Simplify allocation by using a flexible array member and kzalloc_flex to combine allocations. Add __counted_by for extra runtime analysis. Move counting variable assignment to right after allocation. kzalloc_flex does this automatically with GCC >= 15. Signed-off-by: Rosen Penev Reviewed-by: Brian Masney Signed-off-by: Brian Masney --- drivers/clk/mvebu/kirkwood.c | 21 ++++++++------------- 1 file changed, 8 insertions(+), 13 deletions(-) diff --git a/drivers/clk/mvebu/kirkwood.c b/drivers/clk/mvebu/kirkwood.c index ed061d82fb65..f4f62b241193 100644 --- a/drivers/clk/mvebu/kirkwood.c +++ b/drivers/clk/mvebu/kirkwood.c @@ -253,8 +253,8 @@ struct clk_muxing_soc_desc { struct clk_muxing_ctrl { spinlock_t *lock; - struct clk **muxes; int num_muxes; + struct clk *muxes[] __counted_by(num_muxes); }; static const char *powersave_parents[] = { @@ -297,21 +297,18 @@ static void __init kirkwood_clk_muxing_setup(struct device_node *np, if (WARN_ON(!base)) return; - ctrl = kzalloc_obj(*ctrl); - if (WARN_ON(!ctrl)) - goto ctrl_out; - - /* lock must already be initialized */ - ctrl->lock = &ctrl_gating_lock; - /* Count, allocate, and register clock muxes */ for (n = 0; desc[n].name;) n++; + ctrl = kzalloc_flex(*ctrl, muxes, n); + if (WARN_ON(!ctrl)) + goto ctrl_out; + ctrl->num_muxes = n; - ctrl->muxes = kzalloc_objs(struct clk *, ctrl->num_muxes); - if (WARN_ON(!ctrl->muxes)) - goto muxes_out; + + /* lock must already be initialized */ + ctrl->lock = &ctrl_gating_lock; for (n = 0; n < ctrl->num_muxes; n++) { ctrl->muxes[n] = clk_register_mux(NULL, desc[n].name, @@ -324,8 +321,6 @@ static void __init kirkwood_clk_muxing_setup(struct device_node *np, of_clk_add_provider(np, clk_muxing_get_src, ctrl); return; -muxes_out: - kfree(ctrl); ctrl_out: iounmap(base); } From 91d00e377f65933d92a6d8aa9a4ab5a4bc5b84c4 Mon Sep 17 00:00:00 2001 From: David Laight Date: Sat, 6 Jun 2026 21:26:29 +0100 Subject: [PATCH 12/53] drivers/clk/clk_test: Use strscpy() to copy the test description The buffer length is known to be KUNIT_PARAM_DESC_SIZE Signed-off-by: David Laight Reviewed-by: Brian Masney Signed-off-by: Brian Masney --- drivers/clk/clk_test.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/clk/clk_test.c b/drivers/clk/clk_test.c index 824adc95e0b2..94972f38e2e1 100644 --- a/drivers/clk/clk_test.c +++ b/drivers/clk/clk_test.c @@ -2247,7 +2247,7 @@ static void clk_leaf_mux_set_rate_parent_determine_rate_test_case_to_desc( const struct clk_leaf_mux_set_rate_parent_determine_rate_test_case *t, char *desc) { - strcpy(desc, t->desc); + strscpy(desc, t->desc, KUNIT_PARAM_DESC_SIZE); } static const struct clk_leaf_mux_set_rate_parent_determine_rate_test_case @@ -2659,7 +2659,7 @@ static void clk_register_clk_parent_data_test_case_to_desc( const struct clk_register_clk_parent_data_test_case *t, char *desc) { - strcpy(desc, t->desc); + strscpy(desc, t->desc, KUNIT_PARAM_DESC_SIZE); } static const struct clk_register_clk_parent_data_test_case From 4adf593c6fc5aed4639add011f71a074a1bd3966 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pavel=20L=C3=B6bl?= Date: Fri, 5 Jun 2026 15:03:40 +0200 Subject: [PATCH 13/53] clk: clocking-wizard: fix integer overflow in rate calculation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When using driver on Zynq-7000 (32-bit) determine_rate calculation overflows. For instance requesting 32MHz with 100MHz parent clock results in 100000000*(4*1000+0) 32-bit multiplication. Replace the expression with mult_frac which is already used in clk_wzrd_recalc_ratef. Cc: stable@vger.kernel.org Fixes: 7681f64e6404 ("clk: clocking-wizard: calculate dividers fractional parts") Signed-off-by: Pale Löbl Reviewed-by: Brian Masney Signed-off-by: Brian Masney --- drivers/clk/xilinx/clk-xlnx-clock-wizard.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/clk/xilinx/clk-xlnx-clock-wizard.c b/drivers/clk/xilinx/clk-xlnx-clock-wizard.c index 4a0136349f71..dbef983eb425 100644 --- a/drivers/clk/xilinx/clk-xlnx-clock-wizard.c +++ b/drivers/clk/xilinx/clk-xlnx-clock-wizard.c @@ -663,8 +663,8 @@ static int clk_wzrd_determine_rate_all(struct clk_hw *hw, d = divider->d; o = divider->o; - req->rate = div_u64(req->best_parent_rate * (m * 1000 + divider->m_frac), - d * (o * 1000 + divider->o_frac)); + req->rate = mult_frac(req->best_parent_rate, m * 1000 + divider->m_frac, + d * (o * 1000 + divider->o_frac)); return 0; } From 22109b7329f9b3bf2fee73087c73ce46e9bf3751 Mon Sep 17 00:00:00 2001 From: Xuyang Dong Date: Fri, 5 Jun 2026 17:21:18 +0800 Subject: [PATCH 14/53] clk: eswin: Add CLK_IGNORE_UNUSED to NoC clock The gate_noc_nsp_clk provides the essential clock source for NPU, DSP, and PCIe subsystems. During kernel init, the clock framework attempts to disable unused clocks when clk_ignore_unused kernel parameter is not set. However, gate_noc_nsp_clk is required to remain enabled for these critical subsystems to function properly, causing PCIe boot failures when auto-disabled. Add CLK_IGNORE_UNUSED flag to gate_noc_nsp_clk to ensure it stays enabled even when clk_ignore_unused is not specified in kernel command line. Fixes: cd44f127c1d4 ("clk: eswin: Add eic7700 clock driver") Signed-off-by: Xuyang Dong Reviewed-by: Brian Masney Signed-off-by: Brian Masney --- drivers/clk/eswin/clk-eic7700.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/clk/eswin/clk-eic7700.c b/drivers/clk/eswin/clk-eic7700.c index be81d74192da..43a47fe16ab1 100644 --- a/drivers/clk/eswin/clk-eic7700.c +++ b/drivers/clk/eswin/clk-eic7700.c @@ -791,7 +791,8 @@ static struct eswin_clk_info eic7700_clks[] = { EIC7700_CLK_MUX_CPU_ROOT_3MUX1_GFREE, CLK_SET_RATE_PARENT, EIC7700_REG_OFFSET_U84, 27, 0), ESWIN_GATE_TYPE(EIC7700_CLK_GATE_NOC_NSP_CLK, "gate_noc_nsp_clk", - EIC7700_CLK_DIV_NOC_NSP_DYNM, CLK_SET_RATE_PARENT, + EIC7700_CLK_DIV_NOC_NSP_DYNM, + CLK_SET_RATE_PARENT | CLK_IGNORE_UNUSED, EIC7700_REG_OFFSET_NOC, 31, 0), ESWIN_GATE_TYPE(EIC7700_CLK_GATE_BOOTSPI, "gate_clk_bootspi", EIC7700_CLK_MUX_BOOTSPI_CLK_2MUX1_GFREE, From fc8c21a2b5247a56c3b5e2a40e3c59b6805c2212 Mon Sep 17 00:00:00 2001 From: Xuyang Dong Date: Fri, 5 Jun 2026 14:10:02 +0800 Subject: [PATCH 15/53] dt-bindings: clock: Add ESWIN eic7700 HSP clock and reset generator Add bindings for the high-speed peripherals clock and reset generator on the ESWIN EIC7700 HSP. Acked-by: Conor Dooley Signed-off-by: Xuyang Dong Signed-off-by: Brian Masney --- .../bindings/clock/eswin,eic7700-hspcrg.yaml | 63 +++++++++++++++++++ MAINTAINERS | 5 +- .../dt-bindings/clock/eswin,eic7700-hspcrg.h | 33 ++++++++++ .../dt-bindings/reset/eswin,eic7700-hspcrg.h | 21 +++++++ 4 files changed, 120 insertions(+), 2 deletions(-) create mode 100644 Documentation/devicetree/bindings/clock/eswin,eic7700-hspcrg.yaml create mode 100644 include/dt-bindings/clock/eswin,eic7700-hspcrg.h create mode 100644 include/dt-bindings/reset/eswin,eic7700-hspcrg.h diff --git a/Documentation/devicetree/bindings/clock/eswin,eic7700-hspcrg.yaml b/Documentation/devicetree/bindings/clock/eswin,eic7700-hspcrg.yaml new file mode 100644 index 000000000000..43df689ae647 --- /dev/null +++ b/Documentation/devicetree/bindings/clock/eswin,eic7700-hspcrg.yaml @@ -0,0 +1,63 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/clock/eswin,eic7700-hspcrg.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: ESWIN EIC7700 HSP Clock and Reset Generator + +maintainers: + - Xuyang Dong + +description: + Clock and reset generator for the ESWIN EIC7700 HSP (high-speed peripherals). + +properties: + compatible: + const: eswin,eic7700-hspcrg + + reg: + maxItems: 1 + + clocks: + items: + - description: HSP configuration top clock + - description: MMC top clock + - description: SATA top clock + + clock-names: + items: + - const: cfg + - const: mmc + - const: sata + + '#clock-cells': + const: 1 + description: + See for valid indices. + + '#reset-cells': + const: 1 + description: + See for valid indices. + +required: + - compatible + - reg + - clocks + - clock-names + - '#clock-cells' + - '#reset-cells' + +additionalProperties: false + +examples: + - | + clock-controller@50440000 { + compatible = "eswin,eic7700-hspcrg"; + reg = <0x50440000 0x2000>; + clocks = <&clock 171>, <&clock 254>, <&clock 187>; + clock-names = "cfg", "mmc", "sata"; + #clock-cells = <1>; + #reset-cells = <1>; + }; diff --git a/MAINTAINERS b/MAINTAINERS index 15011f5752a9..2b8151f366ec 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -9660,9 +9660,10 @@ ESWIN EIC7700 CLOCK DRIVER M: Yifeng Huang M: Xuyang Dong S: Maintained -F: Documentation/devicetree/bindings/clock/eswin,eic7700-clock.yaml +F: Documentation/devicetree/bindings/clock/eswin,eic7700* F: drivers/clk/eswin/ -F: include/dt-bindings/clock/eswin,eic7700-clock.h +F: include/dt-bindings/clock/eswin,eic7700* +F: include/dt-bindings/reset/eswin,eic7700-hspcrg.h ET131X NETWORK DRIVER M: Mark Einon diff --git a/include/dt-bindings/clock/eswin,eic7700-hspcrg.h b/include/dt-bindings/clock/eswin,eic7700-hspcrg.h new file mode 100644 index 000000000000..1d1ff15c1154 --- /dev/null +++ b/include/dt-bindings/clock/eswin,eic7700-hspcrg.h @@ -0,0 +1,33 @@ +/* SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) */ +/* + * Copyright 2026, Beijing ESWIN Computing Technology Co., Ltd.. + * All rights reserved. + * + * Device Tree binding constants for EIC7700 HSP clock controller. + * + * Authors: Xuyang Dong + */ + +#ifndef _DT_BINDINGS_ESWIN_EIC7700_HSPCRG_CLOCK_H_ +#define _DT_BINDINGS_ESWIN_EIC7700_HSPCRG_CLOCK_H_ + +#define EIC7700_HSP_CLK_FAC_CFG_DIV2 0 +#define EIC7700_HSP_CLK_FAC_CFG_DIV4 1 +#define EIC7700_HSP_CLK_FAC_MMC_DIV10 2 +#define EIC7700_HSP_CLK_MUX_EMMC_3MUX1 3 +#define EIC7700_HSP_CLK_MUX_SD0_3MUX1 4 +#define EIC7700_HSP_CLK_MUX_SD1_3MUX1 5 +#define EIC7700_HSP_CLK_MUX_EMMC_CQE_2MUX1 6 +#define EIC7700_HSP_CLK_MUX_SD0_CQE_2MUX1 7 +#define EIC7700_HSP_CLK_MUX_SD1_CQE_2MUX1 8 +#define EIC7700_HSP_CLK_GATE_MSHC0_TMR 9 +#define EIC7700_HSP_CLK_GATE_EMMC 10 +#define EIC7700_HSP_CLK_GATE_MSHC1_TMR 11 +#define EIC7700_HSP_CLK_GATE_SD0 12 +#define EIC7700_HSP_CLK_GATE_MSHC2_TMR 13 +#define EIC7700_HSP_CLK_GATE_SD1 14 +#define EIC7700_HSP_CLK_GATE_USB0 15 +#define EIC7700_HSP_CLK_GATE_USB1 16 +#define EIC7700_HSP_CLK_GATE_SATA 17 + +#endif /* _DT_BINDINGS_ESWIN_EIC7700_HSPCRG_CLOCK_H_ */ diff --git a/include/dt-bindings/reset/eswin,eic7700-hspcrg.h b/include/dt-bindings/reset/eswin,eic7700-hspcrg.h new file mode 100644 index 000000000000..413fcd08c701 --- /dev/null +++ b/include/dt-bindings/reset/eswin,eic7700-hspcrg.h @@ -0,0 +1,21 @@ +/* SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) */ +/* + * Copyright 2026, Beijing ESWIN Computing Technology Co., Ltd.. + * All rights reserved. + * + * Device Tree binding constants for EIC7700 HSP reset controller. + * + * Authors: Xuyang Dong + */ + +#ifndef _DT_BINDINGS_ESWIN_EIC7700_HSPCRG_RESET_H_ +#define _DT_BINDINGS_ESWIN_EIC7700_HSPCRG_RESET_H_ + +#define EIC7700_HSP_RST_SATA_P0 0 +#define EIC7700_HSP_RST_SATA_PHY 1 +#define EIC7700_HSP_RST_USB0 2 +#define EIC7700_HSP_RST_USB1 3 +#define EIC7700_HSP_RST_USB0_PHY 4 +#define EIC7700_HSP_RST_USB1_PHY 5 + +#endif /* _DT_BINDINGS_ESWIN_EIC7700_HSPCRG_RESET_H_ */ From 8d92085fd79a7c13b72e8a05c5f89c800f575339 Mon Sep 17 00:00:00 2001 From: Xuyang Dong Date: Fri, 5 Jun 2026 14:13:34 +0800 Subject: [PATCH 16/53] clk: eswin: Add eic7700 HSP clock driver Add driver for the ESWIN EIC7700 high-speed peripherals system clock controller and register an auxiliary device for system reset controller which is named as "hsp-reset". Reviewed-by: Brian Masney Signed-off-by: Xuyang Dong Signed-off-by: Brian Masney --- drivers/clk/eswin/Kconfig | 13 ++ drivers/clk/eswin/Makefile | 1 + drivers/clk/eswin/clk-eic7700-hsp.c | 345 ++++++++++++++++++++++++++++ 3 files changed, 359 insertions(+) create mode 100644 drivers/clk/eswin/clk-eic7700-hsp.c diff --git a/drivers/clk/eswin/Kconfig b/drivers/clk/eswin/Kconfig index 0406ec499ec9..37857bd77335 100644 --- a/drivers/clk/eswin/Kconfig +++ b/drivers/clk/eswin/Kconfig @@ -13,3 +13,16 @@ config COMMON_CLK_EIC7700 SoC. The clock controller generates and supplies clocks to various peripherals within the SoC. Say yes here to support the clock controller on the EIC7700 SoC. + +config COMMON_CLK_EIC7700_HSP + tristate "EIC7700 HSP Clock Driver" + depends on ARCH_ESWIN || COMPILE_TEST + select AUXILIARY_BUS + select COMMON_CLK_EIC7700 + select RESET_EIC7700_HSP if RESET_CONTROLLER + select REGMAP_MMIO + help + This driver provides support for clock controller on ESWIN EIC7700 + HSP. The clock controller generates and supplies clocks to high + speed peripherals within the SoC. + Say yes here to support the clock controller on the EIC7700 HSP. diff --git a/drivers/clk/eswin/Makefile b/drivers/clk/eswin/Makefile index 4a7c2af82164..21a09a3396df 100644 --- a/drivers/clk/eswin/Makefile +++ b/drivers/clk/eswin/Makefile @@ -6,3 +6,4 @@ obj-$(CONFIG_COMMON_CLK_ESWIN) += clk.o obj-$(CONFIG_COMMON_CLK_EIC7700) += clk-eic7700.o +obj-$(CONFIG_COMMON_CLK_EIC7700_HSP) += clk-eic7700-hsp.o diff --git a/drivers/clk/eswin/clk-eic7700-hsp.c b/drivers/clk/eswin/clk-eic7700-hsp.c new file mode 100644 index 000000000000..6ddb8fbd605e --- /dev/null +++ b/drivers/clk/eswin/clk-eic7700-hsp.c @@ -0,0 +1,345 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright 2026, Beijing ESWIN Computing Technology Co., Ltd.. + * All rights reserved. + * + * ESWIN EIC7700 HSP Clock Driver + * + * Authors: Xuyang Dong + */ + +#include +#include +#include +#include + +#include + +#include "common.h" + +#define EIC7700_HSP_SATA_REG 0x300 +#define EIC7700_HSP_MSHC0_REG 0x510 +#define EIC7700_HSP_MSHC1_REG 0x610 +#define EIC7700_HSP_MSHC2_REG 0x710 +#define EIC7700_HSP_USB0_REG 0x800 +#define EIC7700_HSP_USB0_REF_REG 0x83c +#define EIC7700_HSP_USB1_REG 0x900 +#define EIC7700_HSP_USB1_REF_REG 0x93c + +#define USB_REF_XTAL24M 0x2a +#define EIC7700_HSP_NR_CLKS (EIC7700_HSP_CLK_GATE_SATA + 1) + +struct eic7700_hsp_clk_gate { + struct clk_hw hw; + unsigned int id; + struct regmap *regmap; + unsigned int reg; + unsigned int ref_reg; + const char *name; + const struct clk_parent_data *parent_data; + unsigned long flags; + unsigned int offset; + unsigned int ref_offset; + u8 bit_idx; +}; + +static const struct regmap_config eic7700_hsp_regmap_config = { + .reg_bits = 32, + .val_bits = 32, + .max_register = 0x1ffc, + .reg_stride = 4, + .fast_io = true, + .use_raw_spinlock = true, +}; + +static inline struct eic7700_hsp_clk_gate *to_gate_clk(struct clk_hw *hw) +{ + return container_of(hw, struct eic7700_hsp_clk_gate, hw); +} + +#define EIC7700_HSP_GATE(_id, _name, _pdata, _flags, _offset, _idx, \ + _ref_offset) \ + { \ + .id = _id, \ + .name = _name, \ + .parent_data = _pdata, \ + .flags = _flags, \ + .offset = _offset, \ + .ref_offset = _ref_offset, \ + .bit_idx = _idx, \ + } + +static void hsp_clk_gate_endisable(struct clk_hw *hw, bool enable) +{ + struct eic7700_hsp_clk_gate *gate = to_gate_clk(hw); + + if (enable) { + /* + * Hardware bug: The USB reference clock must be 24MHz. + * The default register value after reset is invalid. + * Workaround: Rewrite the correct value before enabling + * the USB gate clock. + */ + regmap_update_bits(gate->regmap, gate->ref_reg, 0x3f, + USB_REF_XTAL24M); + } + regmap_assign_bits(gate->regmap, gate->reg, BIT(gate->bit_idx), enable); +} + +static int hsp_clk_gate_enable(struct clk_hw *hw) +{ + hsp_clk_gate_endisable(hw, true); + + return 0; +} + +static void hsp_clk_gate_disable(struct clk_hw *hw) +{ + hsp_clk_gate_endisable(hw, false); +} + +static int hsp_clk_gate_is_enabled(struct clk_hw *hw) +{ + struct eic7700_hsp_clk_gate *gate = to_gate_clk(hw); + unsigned int val; + int ret; + + ret = regmap_read(gate->regmap, gate->reg, &val); + if (ret != 0) + return ret; + + return !!(val & BIT(gate->bit_idx)); +} + +static const struct clk_ops hsp_clk_gate_ops = { + .enable = hsp_clk_gate_enable, + .disable = hsp_clk_gate_disable, + .is_enabled = hsp_clk_gate_is_enabled, +}; + +static struct clk_hw * +hsp_clk_register_gate(struct device *dev, unsigned int id, const char *name, + const struct clk_parent_data *parent_data, + unsigned long flags, struct regmap *regmap, + unsigned int reg, unsigned int ref_reg, u8 bit_idx) +{ + struct eic7700_hsp_clk_gate *gate; + struct clk_init_data init = {}; + struct clk_hw *hw; + int ret; + + gate = devm_kzalloc(dev, sizeof(*gate), GFP_KERNEL); + if (!gate) + return ERR_PTR(-ENOMEM); + + init.name = name; + init.ops = &hsp_clk_gate_ops; + init.flags = flags; + init.parent_data = parent_data; + init.num_parents = 1; + + gate->id = id; + gate->regmap = regmap; + gate->reg = reg; + gate->ref_reg = ref_reg; + gate->bit_idx = bit_idx; + gate->hw.init = &init; + + hw = &gate->hw; + ret = devm_clk_hw_register(dev, hw); + if (ret) + hw = ERR_PTR(ret); + + return hw; +} + +static const struct clk_parent_data hsp_cfg[] = { + { .index = 0 } +}; + +static const struct clk_parent_data hsp_mmc[] = { + { .index = 1 } +}; + +static const struct clk_parent_data hsp_usb_sata[] = { + { .index = 2 } +}; + +static struct eswin_fixed_factor_clock eic7700_hsp_factor_clks[] = { + ESWIN_FACTOR(EIC7700_HSP_CLK_FAC_CFG_DIV2, "factor_hsp_cfg_div2", + hsp_cfg, 1, 2, 0), + ESWIN_FACTOR(EIC7700_HSP_CLK_FAC_CFG_DIV4, "factor_hsp_cfg_div4", + hsp_cfg, 1, 4, 0), + ESWIN_FACTOR(EIC7700_HSP_CLK_FAC_MMC_DIV10, "factor_hsp_mmc_div10", + hsp_mmc, 1, 10, 0), +}; + +static struct eswin_gate_clock eic7700_hsp_gate_clks[] = { + ESWIN_GATE(EIC7700_HSP_CLK_GATE_SATA, "gate_clk_hsp_sata", hsp_usb_sata, + CLK_SET_RATE_PARENT | CLK_IGNORE_UNUSED, + EIC7700_HSP_SATA_REG, 28, 0), + ESWIN_GATE(EIC7700_HSP_CLK_GATE_MSHC0_TMR, "gate_clk_hsp_mshc0_tmr", + hsp_mmc, CLK_SET_RATE_PARENT | CLK_IGNORE_UNUSED, + EIC7700_HSP_MSHC0_REG, 8, 0), + ESWIN_GATE(EIC7700_HSP_CLK_GATE_MSHC1_TMR, "gate_clk_hsp_mshc1_tmr", + hsp_mmc, CLK_SET_RATE_PARENT | CLK_IGNORE_UNUSED, + EIC7700_HSP_MSHC1_REG, 8, 0), + ESWIN_GATE(EIC7700_HSP_CLK_GATE_MSHC2_TMR, "gate_clk_hsp_mshc2_tmr", + hsp_mmc, CLK_SET_RATE_PARENT | CLK_IGNORE_UNUSED, + EIC7700_HSP_MSHC2_REG, 8, 0), +}; + +static struct eic7700_hsp_clk_gate eic7700_hsp_spec_gate_clks[] = { + EIC7700_HSP_GATE(EIC7700_HSP_CLK_GATE_USB0, "gate_clk_hsp_usb0", + hsp_usb_sata, CLK_SET_RATE_PARENT | CLK_IGNORE_UNUSED, + EIC7700_HSP_USB0_REG, 28, EIC7700_HSP_USB0_REF_REG), + EIC7700_HSP_GATE(EIC7700_HSP_CLK_GATE_USB1, "gate_clk_hsp_usb1", + hsp_usb_sata, CLK_SET_RATE_PARENT | CLK_IGNORE_UNUSED, + EIC7700_HSP_USB1_REG, 28, EIC7700_HSP_USB1_REF_REG), +}; + +static const struct clk_parent_data mux_mmc_3mux1_p[] = { + { .fw_name = "cfg" }, + { .hw = &eic7700_hsp_factor_clks[0].hw }, + { .hw = &eic7700_hsp_factor_clks[1].hw }, +}; + +static const struct clk_parent_data mux_mmc_2mux1_p[] = { + { .fw_name = "mmc" }, + { .hw = &eic7700_hsp_factor_clks[2].hw }, +}; + +static u32 mux_mmc_3mux1_tbl[] = { 0x0, 0x1, 0x3 }; + +static struct eswin_mux_clock eic7700_hsp_mux_clks[] = { + ESWIN_MUX_TBL(EIC7700_HSP_CLK_MUX_EMMC_3MUX1, "mux_hsp_emmc_3mux1", + mux_mmc_3mux1_p, ARRAY_SIZE(mux_mmc_3mux1_p), + CLK_SET_RATE_PARENT, EIC7700_HSP_MSHC0_REG, 16, 2, 0, + mux_mmc_3mux1_tbl), + ESWIN_MUX_TBL(EIC7700_HSP_CLK_MUX_SD0_3MUX1, "mux_hsp_sd0_3mux1", + mux_mmc_3mux1_p, ARRAY_SIZE(mux_mmc_3mux1_p), + CLK_SET_RATE_PARENT, EIC7700_HSP_MSHC1_REG, 16, 2, 0, + mux_mmc_3mux1_tbl), + ESWIN_MUX_TBL(EIC7700_HSP_CLK_MUX_SD1_3MUX1, "mux_hsp_sd1_3mux1", + mux_mmc_3mux1_p, ARRAY_SIZE(mux_mmc_3mux1_p), + CLK_SET_RATE_PARENT, EIC7700_HSP_MSHC2_REG, 16, 2, 0, + mux_mmc_3mux1_tbl), + ESWIN_MUX(EIC7700_HSP_CLK_MUX_EMMC_CQE_2MUX1, "mux_hsp_emmc_cqe_2mux1", + mux_mmc_2mux1_p, ARRAY_SIZE(mux_mmc_2mux1_p), + CLK_SET_RATE_PARENT, EIC7700_HSP_MSHC0_REG, 0, 1, 0), + ESWIN_MUX(EIC7700_HSP_CLK_MUX_SD0_CQE_2MUX1, "mux_hsp_sd0_cqe_2mux1", + mux_mmc_2mux1_p, ARRAY_SIZE(mux_mmc_2mux1_p), + CLK_SET_RATE_PARENT, EIC7700_HSP_MSHC1_REG, 0, 1, 0), + ESWIN_MUX(EIC7700_HSP_CLK_MUX_SD1_CQE_2MUX1, "mux_hsp_sd1_cqe_2mux1", + mux_mmc_2mux1_p, ARRAY_SIZE(mux_mmc_2mux1_p), + CLK_SET_RATE_PARENT, EIC7700_HSP_MSHC2_REG, 0, 1, 0), +}; + +static struct eswin_clk_info eic7700_hsp_clks[] = { + ESWIN_GATE_TYPE(EIC7700_HSP_CLK_GATE_EMMC, "gate_clk_hsp_emmc", + EIC7700_HSP_CLK_MUX_EMMC_3MUX1, + CLK_SET_RATE_PARENT | CLK_IGNORE_UNUSED, + EIC7700_HSP_MSHC0_REG, 24, 0), + ESWIN_GATE_TYPE(EIC7700_HSP_CLK_GATE_SD0, "gate_clk_hsp_sd0", + EIC7700_HSP_CLK_MUX_SD0_3MUX1, + CLK_SET_RATE_PARENT | CLK_IGNORE_UNUSED, + EIC7700_HSP_MSHC1_REG, 24, 0), + ESWIN_GATE_TYPE(EIC7700_HSP_CLK_GATE_SD1, "gate_clk_hsp_sd1", + EIC7700_HSP_CLK_MUX_SD1_3MUX1, + CLK_SET_RATE_PARENT | CLK_IGNORE_UNUSED, + EIC7700_HSP_MSHC2_REG, 24, 0), +}; + +static int eic7700_hsp_clk_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct auxiliary_device *adev; + struct eswin_clock_data *data; + struct regmap *regmap; + struct clk_hw *hw; + int i, ret; + + data = eswin_clk_init(pdev, EIC7700_HSP_NR_CLKS); + if (IS_ERR(data)) + return dev_err_probe(dev, PTR_ERR(data), + "failed to get clk data!\n"); + + regmap = devm_regmap_init_mmio(dev, data->base, + &eic7700_hsp_regmap_config); + if (IS_ERR(regmap)) + return dev_err_probe(dev, PTR_ERR(regmap), + "failed to get regmap!\n"); + + ret = eswin_clk_register_fixed_factor(dev, eic7700_hsp_factor_clks, + ARRAY_SIZE(eic7700_hsp_factor_clks), + data); + if (ret) + return dev_err_probe(dev, ret, + "failed to register fixed factor clock\n"); + + ret = eswin_clk_register_gate(dev, eic7700_hsp_gate_clks, + ARRAY_SIZE(eic7700_hsp_gate_clks), data); + if (ret) + return dev_err_probe(dev, ret, + "failed to register gate clock\n"); + + ret = eswin_clk_register_mux(dev, eic7700_hsp_mux_clks, + ARRAY_SIZE(eic7700_hsp_mux_clks), + data); + if (ret) + return dev_err_probe(dev, ret, + "failed to register mux clock\n"); + + ret = eswin_clk_register_clks(dev, eic7700_hsp_clks, + ARRAY_SIZE(eic7700_hsp_clks), data); + if (ret) + return dev_err_probe(dev, ret, + "failed to register clock\n"); + + for (i = 0; i < ARRAY_SIZE(eic7700_hsp_spec_gate_clks); i++) { + struct eic7700_hsp_clk_gate *gate; + + gate = &eic7700_hsp_spec_gate_clks[i]; + hw = hsp_clk_register_gate(dev, gate->id, gate->name, + gate->parent_data, gate->flags, + regmap, gate->offset, + gate->ref_offset, gate->bit_idx); + if (IS_ERR(hw)) + return dev_err_probe(dev, PTR_ERR(hw), + "failed to register gate clock\n"); + + data->clk_data.hws[gate->id] = hw; + } + + ret = devm_of_clk_add_hw_provider(dev, of_clk_hw_onecell_get, + &data->clk_data); + if (ret) + return dev_err_probe(dev, ret, "add clk provider failed\n"); + + adev = devm_auxiliary_device_create(dev, "hsp-reset", NULL); + if (!adev) + return dev_err_probe(dev, -ENODEV, + "register hsp-reset device failed\n"); + + return 0; +} + +static const struct of_device_id eic7700_hsp_clock_dt_ids[] = { + { .compatible = "eswin,eic7700-hspcrg", }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, eic7700_hsp_clock_dt_ids); + +static struct platform_driver eic7700_hsp_clock_driver = { + .probe = eic7700_hsp_clk_probe, + .driver = { + .name = "eic7700-hsp-clock", + .of_match_table = eic7700_hsp_clock_dt_ids, + }, +}; + +module_platform_driver(eic7700_hsp_clock_driver); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Xuyang Dong "); +MODULE_DESCRIPTION("ESWIN EIC7700 HSP clock controller driver"); From b37fe0ee3691b9da358cdfbbdad67874e562ac90 Mon Sep 17 00:00:00 2001 From: Xuyang Dong Date: Fri, 5 Jun 2026 14:14:17 +0800 Subject: [PATCH 17/53] reset: eswin: Add eic7700 HSP reset driver Add auxiliary driver to support ESWIN EIC7700 high-speed peripherals system. The reset controller is created using the auxiliary device framework and set up in the clock driver. Reviewed-by: Philipp Zabel Signed-off-by: Xuyang Dong Signed-off-by: Brian Masney --- drivers/reset/Kconfig | 11 +++ drivers/reset/Makefile | 1 + drivers/reset/reset-eic7700-hsp.c | 113 ++++++++++++++++++++++++++++++ 3 files changed, 125 insertions(+) create mode 100644 drivers/reset/reset-eic7700-hsp.c diff --git a/drivers/reset/Kconfig b/drivers/reset/Kconfig index d009eb0849a3..f63e89ed6a4e 100644 --- a/drivers/reset/Kconfig +++ b/drivers/reset/Kconfig @@ -83,6 +83,17 @@ config RESET_EIC7700 The driver supports eic7700 series chips and provides functionality for asserting and deasserting resets on the chip. +config RESET_EIC7700_HSP + tristate "EIC7700 HSP Reset controller" + depends on ARCH_ESWIN || COMPILE_TEST + select AUXILIARY_BUS + help + This enables the HSP reset controller driver for ESWIN SoCs. This + driver is specific to ESWIN SoCs and should only be enabled if using + such hardware. + The driver supports EIC7700 series chips and provides functionality + for asserting and deasserting resets on the chip. + config RESET_EYEQ bool "Mobileye EyeQ reset controller" depends on EYEQ || COMPILE_TEST diff --git a/drivers/reset/Makefile b/drivers/reset/Makefile index 3e52569bd276..a75af831ef58 100644 --- a/drivers/reset/Makefile +++ b/drivers/reset/Makefile @@ -15,6 +15,7 @@ obj-$(CONFIG_RESET_BERLIN) += reset-berlin.o obj-$(CONFIG_RESET_BRCMSTB) += reset-brcmstb.o obj-$(CONFIG_RESET_BRCMSTB_RESCAL) += reset-brcmstb-rescal.o obj-$(CONFIG_RESET_EIC7700) += reset-eic7700.o +obj-$(CONFIG_RESET_EIC7700_HSP) += reset-eic7700-hsp.o obj-$(CONFIG_RESET_EYEQ) += reset-eyeq.o obj-$(CONFIG_RESET_GPIO) += reset-gpio.o obj-$(CONFIG_RESET_HSDK) += reset-hsdk.o diff --git a/drivers/reset/reset-eic7700-hsp.c b/drivers/reset/reset-eic7700-hsp.c new file mode 100644 index 000000000000..1575ee4a20d6 --- /dev/null +++ b/drivers/reset/reset-eic7700-hsp.c @@ -0,0 +1,113 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright 2026, Beijing ESWIN Computing Technology Co., Ltd.. + * All rights reserved. + * + * ESWIN EIC7700 HSP Reset Driver + * + * Authors: Xuyang Dong + */ + +#include +#include +#include +#include +#include + +#include + +/** + * struct eic7700_hsp_reset_data - reset controller information structure + * @rcdev: reset controller entity + * @regmap: regmap handle containing the memory-mapped reset registers + */ +struct eic7700_hsp_reset_data { + struct reset_controller_dev rcdev; + struct regmap *regmap; +}; + +struct eic7700_hsp_reg { + u32 reg; + u32 bit; + bool active_low; +}; + +static inline struct eic7700_hsp_reset_data * +to_eic7700_hsp_reset(struct reset_controller_dev *rcdev) +{ + return container_of(rcdev, struct eic7700_hsp_reset_data, rcdev); +} + +static const struct eic7700_hsp_reg eic7700_hsp_reset[] = { + [EIC7700_HSP_RST_SATA_P0] = {0x340, BIT(0), false}, + [EIC7700_HSP_RST_SATA_PHY] = {0x340, BIT(1), false}, + [EIC7700_HSP_RST_USB0] = {0x800, BIT(24), true}, + [EIC7700_HSP_RST_USB1] = {0x900, BIT(24), true}, + [EIC7700_HSP_RST_USB0_PHY] = {0x800, BIT(25), false}, + [EIC7700_HSP_RST_USB1_PHY] = {0x900, BIT(25), false}, +}; + +static int eic7700_hsp_reset_assert(struct reset_controller_dev *rcdev, + unsigned long id) +{ + struct eic7700_hsp_reset_data *data = to_eic7700_hsp_reset(rcdev); + + return regmap_assign_bits(data->regmap, eic7700_hsp_reset[id].reg, + eic7700_hsp_reset[id].bit, + !eic7700_hsp_reset[id].active_low); +} + +static int eic7700_hsp_reset_deassert(struct reset_controller_dev *rcdev, + unsigned long id) +{ + struct eic7700_hsp_reset_data *data = to_eic7700_hsp_reset(rcdev); + + return regmap_assign_bits(data->regmap, eic7700_hsp_reset[id].reg, + eic7700_hsp_reset[id].bit, + eic7700_hsp_reset[id].active_low); +} + +static const struct reset_control_ops eic7700_hsp_reset_ops = { + .assert = eic7700_hsp_reset_assert, + .deassert = eic7700_hsp_reset_deassert, +}; + +static int eic7700_hsp_reset_probe(struct auxiliary_device *adev, + const struct auxiliary_device_id *id) +{ + struct eic7700_hsp_reset_data *data; + struct device *dev = &adev->dev; + + data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL); + if (!data) + return -ENOMEM; + + data->regmap = dev_get_regmap(dev->parent, NULL); + if (!data->regmap) + return dev_err_probe(dev, -ENODEV, "failed to get regmap!\n"); + + data->rcdev.owner = THIS_MODULE; + data->rcdev.ops = &eic7700_hsp_reset_ops; + data->rcdev.of_node = dev->parent->of_node; + data->rcdev.dev = dev; + data->rcdev.nr_resets = ARRAY_SIZE(eic7700_hsp_reset); + + return devm_reset_controller_register(dev, &data->rcdev); +} + +static const struct auxiliary_device_id eic7700_hsp_reset_ids[] = { + { .name = "clk_eic7700_hsp.hsp-reset", }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(auxiliary, eic7700_hsp_reset_ids); + +static struct auxiliary_driver eic7700_hsp_reset_driver = { + .probe = eic7700_hsp_reset_probe, + .id_table = eic7700_hsp_reset_ids, +}; + +module_auxiliary_driver(eic7700_hsp_reset_driver); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Xuyang Dong "); +MODULE_DESCRIPTION("ESWIN EIC7700 HSP Reset Controller Driver"); From 50ca555d6ae8205374e406b411ae0e8e9447568d Mon Sep 17 00:00:00 2001 From: Lad Prabhakar Date: Tue, 2 Jun 2026 21:02:42 +0100 Subject: [PATCH 18/53] clk: divider: Add KUnit tests for clk_divider_bestdiv() ULONG_MAX handling Add KUnit tests to verify the behaviour of clk_divider_bestdiv() when clk_round_rate() is called with ULONG_MAX, which is the canonical way to probe the maximum rate a clock can produce. Two test cases are introduced: - clk_divider_bestdiv_ulong_max_returns_max_rate: registers a 1 GHz fixed-rate parent driving a table-based divider whose smallest entry is div=2 (entries: 2, 4, 8). Calls clk_hw_round_rate(div_hw, ULONG_MAX) and checks the result. - clk_divider_bestdiv_mux_ulong_max_returns_max_rate: places a two-input mux (4 GHz and 2 GHz fixed-rate parents, CLK_SET_RATE_PARENT) ahead of the same table-based divider to verify correct parent selection under ULONG_MAX. Both tests use an explicit clk_div_table with a minimum divider of 2 so that the pre-loop maxdiv clamping in clk_divider_bestdiv(): maxdiv = min(ULONG_MAX / rate, maxdiv); clamps maxdiv to 1, causing _next_div() to return 2 on the first iteration and skip the loop body entirely. This makes bestdiv fall back to the maximum divider, returning the minimum rate rather than the maximum. The expected values intentionally reflect the buggy output: - test 1: PARENT_RATE_1GHZ / 8 (minimum rate, not maximum) - test 2: 0 (invalid, loop never populated bestdiv) These will be corrected to PARENT_RATE_1GHZ / 2 and PARENT_RATE_4GHZ / 2 respectively once the fix to clk_divider_bestdiv() is applied. Signed-off-by: Lad Prabhakar Reviewed-by: Brian Masney Signed-off-by: Brian Masney --- drivers/clk/.kunitconfig | 1 + drivers/clk/Kconfig | 8 ++ drivers/clk/Makefile | 1 + drivers/clk/clk-divider_test.c | 153 +++++++++++++++++++++++++++++++++ 4 files changed, 163 insertions(+) create mode 100644 drivers/clk/clk-divider_test.c diff --git a/drivers/clk/.kunitconfig b/drivers/clk/.kunitconfig index 8a0ea41934a2..ea05b9a28c80 100644 --- a/drivers/clk/.kunitconfig +++ b/drivers/clk/.kunitconfig @@ -4,6 +4,7 @@ CONFIG_OF=y CONFIG_OF_OVERLAY=y CONFIG_COMMON_CLK=y CONFIG_CLK_KUNIT_TEST=y +CONFIG_CLK_DIVIDER_KUNIT_TEST=y CONFIG_CLK_FIXED_RATE_KUNIT_TEST=y CONFIG_CLK_GATE_KUNIT_TEST=y CONFIG_CLK_FD_KUNIT_TEST=y diff --git a/drivers/clk/Kconfig b/drivers/clk/Kconfig index 1717ce75a907..3d94c7611356 100644 --- a/drivers/clk/Kconfig +++ b/drivers/clk/Kconfig @@ -579,4 +579,12 @@ config CLK_FD_KUNIT_TEST help Kunit test for the clk-fractional-divider type. +config CLK_DIVIDER_KUNIT_TEST + tristate "KUnit tests for clk divider bestdiv" if !KUNIT_ALL_TESTS + depends on KUNIT + depends on !S390 + default KUNIT_ALL_TESTS + help + Kunit test for the clk-divider type. + endif diff --git a/drivers/clk/Makefile b/drivers/clk/Makefile index c2ae700ec0f2..d042b4aa9c0f 100644 --- a/drivers/clk/Makefile +++ b/drivers/clk/Makefile @@ -31,6 +31,7 @@ clk-test-y := clk_test.o \ kunit_clk_hw_get_dev_of_node.dtbo.o \ kunit_clk_parent_data_test.dtbo.o obj-$(CONFIG_COMMON_CLK) += clk-divider.o +obj-$(CONFIG_CLK_DIVIDER_KUNIT_TEST) += clk-divider_test.o obj-$(CONFIG_COMMON_CLK) += clk-fixed-factor.o obj-$(CONFIG_COMMON_CLK) += clk-fixed-rate.o obj-$(CONFIG_CLK_FIXED_RATE_KUNIT_TEST) += clk-fixed-rate-test.o diff --git a/drivers/clk/clk-divider_test.c b/drivers/clk/clk-divider_test.c new file mode 100644 index 000000000000..cb56931456b8 --- /dev/null +++ b/drivers/clk/clk-divider_test.c @@ -0,0 +1,153 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * KUnit tests for clk_divider_bestdiv() + */ +#include +#include +#include +#include +#include + +#define PARENT_RATE_1GHZ GIGA +#define PARENT_RATE_2GHZ (2 * GIGA) +#define PARENT_RATE_4GHZ (4 * GIGA) + +KUNIT_DEFINE_ACTION_WRAPPER(clk_hw_unregister_fixed_rate_wrapper, + clk_hw_unregister_fixed_rate, struct clk_hw *); +KUNIT_DEFINE_ACTION_WRAPPER(clk_hw_unregister_divider_wrapper, + clk_hw_unregister_divider, struct clk_hw *); +KUNIT_DEFINE_ACTION_WRAPPER(clk_hw_unregister_mux_wrapper, + clk_hw_unregister_mux, struct clk_hw *); + +static const struct clk_div_table bestdiv_table[] = { + { .val = 0, .div = 2 }, + { .val = 1, .div = 4 }, + { .val = 2, .div = 8 }, + { /* sentinel */ } +}; + +/* + * Test that clk_round_rate(clk, ULONG_MAX) returns the maximum achievable + * rate for a divider clock. + */ +static void clk_divider_bestdiv_ulong_max_returns_max_rate(struct kunit *test) +{ + struct clk_hw *parent_hw, *div_hw; + unsigned long rate; + u32 *fake_reg; + + fake_reg = kunit_kzalloc(test, sizeof(*fake_reg), GFP_KERNEL); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, fake_reg); + + parent_hw = clk_hw_register_fixed_rate(NULL, "bestdiv-parent", + NULL, 0, PARENT_RATE_1GHZ); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, parent_hw); + KUNIT_ASSERT_EQ(test, 0, + kunit_add_action_or_reset(test, clk_hw_unregister_fixed_rate_wrapper, + parent_hw)); + + div_hw = clk_hw_register_divider_table(NULL, "bestdiv-div", + "bestdiv-parent", + CLK_SET_RATE_PARENT, + (void __iomem __force *)fake_reg, + 0, 2, 0, bestdiv_table, NULL); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, div_hw); + KUNIT_ASSERT_EQ(test, 0, + kunit_add_action_or_reset(test, clk_hw_unregister_divider_wrapper, + div_hw)); + + /* + * ULONG_MAX is the canonical way to probe the maximum rate a clock + * can produce. + */ + rate = clk_hw_round_rate(div_hw, ULONG_MAX); + KUNIT_EXPECT_EQ(test, rate, PARENT_RATE_1GHZ / 8); +} + +/* + * Test that clk_round_rate(clk, ULONG_MAX) returns the correct maximum rate + * when a mux clock sits between a divider and its parent candidates. + * + * Topology: + * + * [fixed 4 GHz] --\ + * +--> [mux CLK_SET_RATE_PARENT] --> [div {2,4,8} CLK_SET_RATE_PARENT] + * [fixed 2 GHz] --/ + * + */ +static void clk_divider_bestdiv_mux_ulong_max_returns_max_rate(struct kunit *test) +{ + static const char * const mux_parents[] = { + "bestdiv-mux-parent-a", + "bestdiv-mux-parent-b", + }; + struct clk_hw *parent_a_hw, *parent_b_hw, *mux_hw, *div_hw; + u32 *fake_reg_mux, *fake_reg_div; + unsigned long rate; + + fake_reg_mux = kunit_kzalloc(test, sizeof(*fake_reg_mux), GFP_KERNEL); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, fake_reg_mux); + + fake_reg_div = kunit_kzalloc(test, sizeof(*fake_reg_div), GFP_KERNEL); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, fake_reg_div); + + /* Higher-rate parent: the mux should select this for ULONG_MAX. */ + parent_a_hw = clk_hw_register_fixed_rate(NULL, "bestdiv-mux-parent-a", + NULL, 0, PARENT_RATE_4GHZ); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, parent_a_hw); + KUNIT_ASSERT_EQ(test, 0, + kunit_add_action_or_reset(test, clk_hw_unregister_fixed_rate_wrapper, + parent_a_hw)); + + /* Lower-rate parent: should not be selected. */ + parent_b_hw = clk_hw_register_fixed_rate(NULL, "bestdiv-mux-parent-b", + NULL, 0, PARENT_RATE_2GHZ); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, parent_b_hw); + KUNIT_ASSERT_EQ(test, 0, + kunit_add_action_or_reset(test, clk_hw_unregister_fixed_rate_wrapper, + parent_b_hw)); + + /* + * 1-bit mux register selects between the two parents. + * CLK_SET_RATE_PARENT allows the divider's rate request to + * propagate into clk_mux_determine_rate(). + */ + mux_hw = clk_hw_register_mux(NULL, "bestdiv-mux", + mux_parents, ARRAY_SIZE(mux_parents), + CLK_SET_RATE_PARENT, + (void __iomem __force *)fake_reg_mux, + 0, 1, 0, NULL); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, mux_hw); + KUNIT_ASSERT_EQ(test, 0, + kunit_add_action_or_reset(test, clk_hw_unregister_mux_wrapper, + mux_hw)); + + div_hw = clk_hw_register_divider_table(NULL, "bestdiv-mux-div", + "bestdiv-mux", + CLK_SET_RATE_PARENT, + (void __iomem __force *)fake_reg_div, + 0, 2, 0, bestdiv_table, NULL); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, div_hw); + KUNIT_ASSERT_EQ(test, 0, + kunit_add_action_or_reset(test, clk_hw_unregister_divider_wrapper, + div_hw)); + + rate = clk_hw_round_rate(div_hw, ULONG_MAX); + KUNIT_EXPECT_EQ(test, rate, 0); +} + +static struct kunit_case clk_divider_bestdiv_test_cases[] = { + KUNIT_CASE(clk_divider_bestdiv_ulong_max_returns_max_rate), + KUNIT_CASE(clk_divider_bestdiv_mux_ulong_max_returns_max_rate), + {} +}; + +static struct kunit_suite clk_divider_bestdiv_test_suite = { + .name = "clk_divider_bestdiv", + .test_cases = clk_divider_bestdiv_test_cases, +}; + +kunit_test_suite(clk_divider_bestdiv_test_suite); + +MODULE_DESCRIPTION("KUnit tests for clk divider"); +MODULE_LICENSE("GPL"); From e129a400d8a787e5968e30309fa72859580b263a Mon Sep 17 00:00:00 2001 From: Lad Prabhakar Date: Tue, 2 Jun 2026 21:02:43 +0100 Subject: [PATCH 19/53] clk: divider: Fix clk_divider_bestdiv() returning min rate for large rate requests clk_divider_bestdiv() clamps maxdiv using: maxdiv = min(ULONG_MAX / rate, maxdiv); to avoid overflow in rate * i. However, requests like clk_round_rate(clk, ULONG_MAX), which are used to determine the maximum supported rate of a clock, result in maxdiv being clamped to 1. If no valid divider of 1 exists in the table the loop is never entered and bestdiv falls back to the maximum divider with the minimum parent rate, causing clk_round_rate(clk, ULONG_MAX) to incorrectly return the minimum supported rate instead of the maximum. Fix this by removing the pre-loop maxdiv clamping and replacing the unprotected rate * i multiplication with check_mul_overflow(). Guard the exact-match short-circuit with !overflow to prevent a clamped target_parent_rate of ULONG_MAX from falsely matching parent_rate_saved and causing premature loop exit. Break out of the loop after evaluating the first overflowing divider since clk_hw_round_rate(parent, ULONG_MAX) returns a constant for all subsequent iterations, meaning no better candidate can be found, and continuing would cause exponential recursive calls in chained divider clocks. Update the KUnit test expected values to reflect the corrected behaviour: - clk_divider_bestdiv_ulong_max_returns_max_rate: PARENT_RATE_1GHZ / 8 (minimum rate, pre-fix) -> PARENT_RATE_1GHZ / 2 (maximum rate) - clk_divider_bestdiv_mux_ulong_max_returns_max_rate: 0 (invalid, pre-fix) -> PARENT_RATE_4GHZ / 2 (maximum rate with mux selecting the 4 GHz parent and applying the smallest table divider of 2) Signed-off-by: Lad Prabhakar Reviewed-by: Brian Masney Signed-off-by: Brian Masney --- drivers/clk/clk-divider.c | 25 +++++++++++++++++-------- drivers/clk/clk-divider_test.c | 4 ++-- 2 files changed, 19 insertions(+), 10 deletions(-) diff --git a/drivers/clk/clk-divider.c b/drivers/clk/clk-divider.c index b3b485d23ea8..f68216c65d3d 100644 --- a/drivers/clk/clk-divider.c +++ b/drivers/clk/clk-divider.c @@ -15,6 +15,7 @@ #include #include #include +#include /* * DOC: basic adjustable divider clock that cannot gate @@ -301,6 +302,7 @@ static int clk_divider_bestdiv(struct clk_hw *hw, struct clk_hw *parent, int i, bestdiv = 0; unsigned long parent_rate, best = 0, now, maxdiv; unsigned long parent_rate_saved = *best_parent_rate; + unsigned long target_parent_rate; if (!rate) rate = 1; @@ -315,15 +317,11 @@ static int clk_divider_bestdiv(struct clk_hw *hw, struct clk_hw *parent, return bestdiv; } - /* - * The maximum divider we can use without overflowing - * unsigned long in rate * i below - */ - maxdiv = min(ULONG_MAX / rate, maxdiv); - for (i = _next_div(table, 0, flags); i <= maxdiv; i = _next_div(table, i, flags)) { - if (rate * i == parent_rate_saved) { + bool overflow = check_mul_overflow(rate, (unsigned long)i, &target_parent_rate); + + if (!overflow && target_parent_rate == parent_rate_saved) { /* * It's the most ideal case if the requested rate can be * divided from parent clock without needing to change @@ -332,13 +330,24 @@ static int clk_divider_bestdiv(struct clk_hw *hw, struct clk_hw *parent, *best_parent_rate = parent_rate_saved; return i; } - parent_rate = clk_hw_round_rate(parent, rate * i); + /* + * Clamp target_parent_rate to ULONG_MAX on overflow. The true + * required parent rate exceeds what can be represented, so ask + * the parent for the highest rate it can produce. There is no + * point continuing the loop past this since larger dividers + * only move further from the requested rate. + */ + if (overflow) + target_parent_rate = ULONG_MAX; + parent_rate = clk_hw_round_rate(parent, target_parent_rate); now = DIV_ROUND_UP_ULL((u64)parent_rate, i); if (_is_best_div(rate, now, best, flags)) { bestdiv = i; best = now; *best_parent_rate = parent_rate; } + if (overflow) + break; } if (!bestdiv) { diff --git a/drivers/clk/clk-divider_test.c b/drivers/clk/clk-divider_test.c index cb56931456b8..8ee1614128d0 100644 --- a/drivers/clk/clk-divider_test.c +++ b/drivers/clk/clk-divider_test.c @@ -61,7 +61,7 @@ static void clk_divider_bestdiv_ulong_max_returns_max_rate(struct kunit *test) * can produce. */ rate = clk_hw_round_rate(div_hw, ULONG_MAX); - KUNIT_EXPECT_EQ(test, rate, PARENT_RATE_1GHZ / 8); + KUNIT_EXPECT_EQ(test, rate, PARENT_RATE_1GHZ / 2); } /* @@ -133,7 +133,7 @@ static void clk_divider_bestdiv_mux_ulong_max_returns_max_rate(struct kunit *tes div_hw)); rate = clk_hw_round_rate(div_hw, ULONG_MAX); - KUNIT_EXPECT_EQ(test, rate, 0); + KUNIT_EXPECT_EQ(test, rate, PARENT_RATE_4GHZ / 2); } static struct kunit_case clk_divider_bestdiv_test_cases[] = { From ff29670735e0d9ac683529e316b8d4dcd09177ac Mon Sep 17 00:00:00 2001 From: Jon Hunter Date: Tue, 2 Jun 2026 09:43:34 +0100 Subject: [PATCH 20/53] clk: tegra: Support unique names for multi-socket platforms On multi-socket platforms each socket has its own BPMP which exposes the same clock names. Fix this by using the NUMA ID as a prefix for the clock names on multi-socket platforms. Use 'sizeof(info->name)' in the strscpy() and snprintf() functions to future proof against anyone changing the size of the 'name' array. Co-developed-by: Timo Alho Signed-off-by: Timo Alho Signed-off-by: Jon Hunter Reviewed-by: Brian Masney Reviewed-by: Thierry Reding Signed-off-by: Brian Masney --- drivers/clk/tegra/clk-bpmp.c | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/drivers/clk/tegra/clk-bpmp.c b/drivers/clk/tegra/clk-bpmp.c index f6d2b934228b..067a8555c4e7 100644 --- a/drivers/clk/tegra/clk-bpmp.c +++ b/drivers/clk/tegra/clk-bpmp.c @@ -367,7 +367,15 @@ static int tegra_bpmp_clk_get_info(struct tegra_bpmp *bpmp, unsigned int id, if (err < 0) return err; - strscpy(info->name, response.name, MRQ_CLK_NAME_MAXLEN); + if (dev_to_node(bpmp->dev) == NUMA_NO_NODE) { + strscpy(info->name, response.name, sizeof(info->name)); + } else { + err = snprintf(info->name, sizeof(info->name), "%d-%s", + dev_to_node(bpmp->dev), response.name); + if (WARN_ON(err >= sizeof(info->name))) + return -E2BIG; + } + info->num_parents = response.num_parents; for (i = 0; i < info->num_parents; i++) From d4d0d335e8d37c6c7e1c5664c7664288eca5a09b Mon Sep 17 00:00:00 2001 From: William Theesfeld Date: Mon, 1 Jun 2026 16:35:00 -0400 Subject: [PATCH 21/53] clk: zynq: handle kasprintf() failure in periph_clk registration zynq_clk_register_periph_clk() ignores the return value of the two kasprintf() calls used to build the mux and divider clock names, and passes the resulting (possibly NULL) pointers straight into clk_register_mux(), clk_register_divider() and clk_register_gate() as the clock '"'name'"' argument. On allocation failure that name later gets dereferenced by the clock framework (e.g. in debugfs name formatting), causing a NULL-pointer dereference. Check both kasprintf() returns. On failure unwind any allocated name buffer and the spinlock, then fall through to the existing err label which sets clks[] to ERR_PTR(-ENOMEM). Freeing the spinlock on the error path is correct here because no clk_register_*() call has had a chance to take ownership of it; the success path intentionally hands it off to the registered clocks. The neighbouring zynq_clk_register_fclk() in the same file already uses this per-allocation goto-label cleanup pattern; this change brings periph_clk into line with it. Signed-off-by: William Theesfeld Reviewed-by: Brian Masney Reviewed-by: Michal Simek Signed-off-by: Brian Masney --- drivers/clk/zynq/clkc.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/drivers/clk/zynq/clkc.c b/drivers/clk/zynq/clkc.c index 6a22cbbc1235..777187744f4e 100644 --- a/drivers/clk/zynq/clkc.c +++ b/drivers/clk/zynq/clkc.c @@ -186,7 +186,11 @@ static void __init zynq_clk_register_periph_clk(enum zynq_clk clk0, spin_lock_init(lock); mux_name = kasprintf(GFP_KERNEL, "%s_mux", clk_name0); + if (!mux_name) + goto err_mux_name; div_name = kasprintf(GFP_KERNEL, "%s_div", clk_name0); + if (!div_name) + goto err_div_name; clk_register_mux(NULL, mux_name, parents, 4, CLK_SET_RATE_NO_REPARENT, clk_ctrl, 4, 2, 0, lock); @@ -205,6 +209,10 @@ static void __init zynq_clk_register_periph_clk(enum zynq_clk clk0, return; +err_div_name: + kfree(mux_name); +err_mux_name: + kfree(lock); err: clks[clk0] = ERR_PTR(-ENOMEM); if (two_gates) From 483932d86b0063a9a7f0eefa4b2e557d753fe086 Mon Sep 17 00:00:00 2001 From: Yu-Chun Lin Date: Sun, 31 May 2026 21:18:25 +0800 Subject: [PATCH 22/53] clk: bcm: kona: Stop defaulting to parent index 0 on error Stop defaulting to 0 on BAD_CLK_INDEX (U8_MAX). Passing the out-of-bounds index directly allows the clk core to fail gracefully during parent lookup. Link: https://lore.kernel.org/lkml/177431305509.5403.15386021337517970667@lazor/ Signed-off-by: Yu-Chun Lin Reviewed-by: Brian Masney Signed-off-by: Brian Masney --- drivers/clk/bcm/clk-kona.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/clk/bcm/clk-kona.c b/drivers/clk/bcm/clk-kona.c index 0171e6b2bfca..d6f5872d7d6a 100644 --- a/drivers/clk/bcm/clk-kona.c +++ b/drivers/clk/bcm/clk-kona.c @@ -1098,8 +1098,7 @@ static u8 kona_peri_clk_get_parent(struct clk_hw *hw) index = selector_read_index(bcm_clk->ccu, &data->sel); - /* Not all callers would handle an out-of-range value gracefully */ - return index == BAD_CLK_INDEX ? 0 : index; + return index; } static int kona_peri_clk_set_rate(struct clk_hw *hw, unsigned long rate, From 4f04b40ae7e27970041375193b312274b8293ae1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Uwe=20Kleine-K=C3=B6nig=20=28The=20Capable=20Hub=29?= Date: Thu, 28 May 2026 12:29:38 +0200 Subject: [PATCH 23/53] clk: Use named initializers for platform_device_id arrays MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Named initializers are better readable and more robust to changes of the struct definition. This robustness is relevant for a planned change to struct platform_device_id replacing .driver_data by an anonymous union. While touching these arrays unify spacing and usage of commas. Signed-off-by: Uwe Kleine-König (The Capable Hub) Reviewed-by: Matti Vaittinen Reviewed-by: Brian Masney Reviewed-by: Peter Griffin Reviewed-by: Tudor Ambarus Signed-off-by: Brian Masney --- drivers/clk/clk-bd718x7.c | 12 ++++++------ drivers/clk/clk-max77686.c | 8 ++++---- drivers/clk/clk-s2mps11.c | 12 ++++++------ drivers/clk/samsung/clk-acpm.c | 4 ++-- 4 files changed, 18 insertions(+), 18 deletions(-) diff --git a/drivers/clk/clk-bd718x7.c b/drivers/clk/clk-bd718x7.c index 1cae974e6d1d..8ddb50c67757 100644 --- a/drivers/clk/clk-bd718x7.c +++ b/drivers/clk/clk-bd718x7.c @@ -147,12 +147,12 @@ static int bd71837_clk_probe(struct platform_device *pdev) } static const struct platform_device_id bd718x7_clk_id[] = { - { "bd71837-clk", ROHM_CHIP_TYPE_BD71837 }, - { "bd71847-clk", ROHM_CHIP_TYPE_BD71847 }, - { "bd71828-clk", ROHM_CHIP_TYPE_BD71828 }, - { "bd71815-clk", ROHM_CHIP_TYPE_BD71815 }, - { "bd72720-clk", ROHM_CHIP_TYPE_BD72720 }, - { }, + { .name = "bd71837-clk", .driver_data = ROHM_CHIP_TYPE_BD71837 }, + { .name = "bd71847-clk", .driver_data = ROHM_CHIP_TYPE_BD71847 }, + { .name = "bd71828-clk", .driver_data = ROHM_CHIP_TYPE_BD71828 }, + { .name = "bd71815-clk", .driver_data = ROHM_CHIP_TYPE_BD71815 }, + { .name = "bd72720-clk", .driver_data = ROHM_CHIP_TYPE_BD72720 }, + { } }; MODULE_DEVICE_TABLE(platform, bd718x7_clk_id); diff --git a/drivers/clk/clk-max77686.c b/drivers/clk/clk-max77686.c index 9149ce4f702d..e6fde914c5ef 100644 --- a/drivers/clk/clk-max77686.c +++ b/drivers/clk/clk-max77686.c @@ -264,10 +264,10 @@ static int max77686_clk_probe(struct platform_device *pdev) } static const struct platform_device_id max77686_clk_id[] = { - { "max77686-clk", .driver_data = CHIP_MAX77686, }, - { "max77802-clk", .driver_data = CHIP_MAX77802, }, - { "max77620-clock", .driver_data = CHIP_MAX77620, }, - {}, + { .name = "max77686-clk", .driver_data = CHIP_MAX77686 }, + { .name = "max77802-clk", .driver_data = CHIP_MAX77802 }, + { .name = "max77620-clock", .driver_data = CHIP_MAX77620 }, + { } }; MODULE_DEVICE_TABLE(platform, max77686_clk_id); diff --git a/drivers/clk/clk-s2mps11.c b/drivers/clk/clk-s2mps11.c index ff7ce12a5da6..fa5ac8f673f6 100644 --- a/drivers/clk/clk-s2mps11.c +++ b/drivers/clk/clk-s2mps11.c @@ -225,12 +225,12 @@ static void s2mps11_clk_remove(struct platform_device *pdev) } static const struct platform_device_id s2mps11_clk_id[] = { - { "s2mpg10-clk", S2MPG10}, - { "s2mps11-clk", S2MPS11X}, - { "s2mps13-clk", S2MPS13X}, - { "s2mps14-clk", S2MPS14X}, - { "s5m8767-clk", S5M8767X}, - { }, + { .name = "s2mpg10-clk", .driver_data = S2MPG10 }, + { .name = "s2mps11-clk", .driver_data = S2MPS11X }, + { .name = "s2mps13-clk", .driver_data = S2MPS13X }, + { .name = "s2mps14-clk", .driver_data = S2MPS14X }, + { .name = "s5m8767-clk", .driver_data = S5M8767X }, + { } }; MODULE_DEVICE_TABLE(platform, s2mps11_clk_id); diff --git a/drivers/clk/samsung/clk-acpm.c b/drivers/clk/samsung/clk-acpm.c index 953ca8d5720a..25cfa953ccac 100644 --- a/drivers/clk/samsung/clk-acpm.c +++ b/drivers/clk/samsung/clk-acpm.c @@ -166,8 +166,8 @@ static int acpm_clk_probe(struct platform_device *pdev) } static const struct platform_device_id acpm_clk_id[] = { - { "gs101-acpm-clk" }, - {} + { .name = "gs101-acpm-clk" }, + { } }; MODULE_DEVICE_TABLE(platform, acpm_clk_id); From 9f275f2ee9ca60ea4c092bdc0195987945ad8ad8 Mon Sep 17 00:00:00 2001 From: "Alexander A. Klimov" Date: Tue, 26 May 2026 22:42:26 +0200 Subject: [PATCH 24/53] clk: moxart: remove unused variables, fix refcount leak Not only these error checks are redundand, those of_clk_get() return values weren't cleaned up via clk_put(). Fixes: c7bb4fc16ead ("clk: add MOXA ART SoCs clock driver") Signed-off-by: Alexander A. Klimov Reviewed-by: Brian Masney Signed-off-by: Brian Masney --- drivers/clk/clk-moxart.c | 14 -------------- 1 file changed, 14 deletions(-) diff --git a/drivers/clk/clk-moxart.c b/drivers/clk/clk-moxart.c index 3786a0153ad1..40663ef3ef0a 100644 --- a/drivers/clk/clk-moxart.c +++ b/drivers/clk/clk-moxart.c @@ -17,7 +17,6 @@ static void __init moxart_of_pll_clk_init(struct device_node *node) { void __iomem *base; struct clk_hw *hw; - struct clk *ref_clk; unsigned int mul; const char *name = node->name; const char *parent_name; @@ -34,12 +33,6 @@ static void __init moxart_of_pll_clk_init(struct device_node *node) mul = readl(base + 0x30) >> 3 & 0x3f; iounmap(base); - ref_clk = of_clk_get(node, 0); - if (IS_ERR(ref_clk)) { - pr_err("%pOF: of_clk_get failed\n", node); - return; - } - hw = clk_hw_register_fixed_factor(NULL, name, parent_name, 0, mul, 1); if (IS_ERR(hw)) { pr_err("%pOF: failed to register clock\n", node); @@ -56,7 +49,6 @@ static void __init moxart_of_apb_clk_init(struct device_node *node) { void __iomem *base; struct clk_hw *hw; - struct clk *pll_clk; unsigned int div, val; unsigned int div_idx[] = { 2, 3, 4, 6, 8}; const char *name = node->name; @@ -78,12 +70,6 @@ static void __init moxart_of_apb_clk_init(struct device_node *node) val = 0; div = div_idx[val] * 2; - pll_clk = of_clk_get(node, 0); - if (IS_ERR(pll_clk)) { - pr_err("%pOF: of_clk_get failed\n", node); - return; - } - hw = clk_hw_register_fixed_factor(NULL, name, parent_name, 0, 1, div); if (IS_ERR(hw)) { pr_err("%pOF: failed to register clock\n", node); From f63aecdb45e9dd19c340fd62df698374d95b9024 Mon Sep 17 00:00:00 2001 From: Akari Tsuyukusa Date: Fri, 22 May 2026 22:30:23 +0900 Subject: [PATCH 25/53] clk: mediatek: mt8196: Select REGMAP_MMIO for vlpckgen The MediaTek MT8196 vlpckgen clock driver uses __devm_regmap_init_mmio_clk() by devm_regmap_init_mmio(), which is defined in drivers/base/regmap/regmap-mmio.c. However, the driver's Kconfig entry does not select REGMAP_MMIO. This causes a linker error when REGMAP_MMIO is not enabled. Fix this by selecting REGMAP_MMIO in the Kconfig entry. Fixes: 2f8b3ae6f0cb ("clk: mediatek: Add MT8196 vlpckgen clock support") Cc: stable@vger.kernel.org Signed-off-by: Akari Tsuyukusa Reviewed-by: Brian Masney Reviewed-by: AngeloGioacchino Del Regno Signed-off-by: Brian Masney --- drivers/clk/mediatek/Kconfig | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/clk/mediatek/Kconfig b/drivers/clk/mediatek/Kconfig index 2c09fd729bab..fd8440122ec2 100644 --- a/drivers/clk/mediatek/Kconfig +++ b/drivers/clk/mediatek/Kconfig @@ -1006,6 +1006,7 @@ config COMMON_CLK_MT8196 tristate "Clock driver for MediaTek MT8196" depends on ARM64 || COMPILE_TEST select COMMON_CLK_MEDIATEK + select REGMAP_MMIO default ARCH_MEDIATEK help This driver supports MediaTek MT8196 basic clocks. From b3a2223a7805c7e6759a32a5d6ca574ad07e2710 Mon Sep 17 00:00:00 2001 From: Joey Lu Date: Thu, 21 May 2026 09:42:18 +0800 Subject: [PATCH 26/53] clk: nuvoton: ma35d1: fix ignored div_u64 return values in PLL freq calculation div_u64() does not modify its argument in place; the return value must be assigned. Both ma35d1_calc_smic_pll_freq() and ma35d1_calc_pll_freq() called div_u64() and discarded the result, leaving pll_freq holding the undivided product and thus returning a frequency orders of magnitude too high. Fixes: 691521a367cf ("clk: nuvoton: Add clock driver for ma35d1 clock controller") Reviewed-by: Brian Masney Signed-off-by: Joey Lu Signed-off-by: Brian Masney --- drivers/clk/nuvoton/clk-ma35d1-pll.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/clk/nuvoton/clk-ma35d1-pll.c b/drivers/clk/nuvoton/clk-ma35d1-pll.c index 4620acfe47e8..bfedd45bd04b 100644 --- a/drivers/clk/nuvoton/clk-ma35d1-pll.c +++ b/drivers/clk/nuvoton/clk-ma35d1-pll.c @@ -92,7 +92,7 @@ static unsigned long ma35d1_calc_smic_pll_freq(u32 pll0_ctl0, p = FIELD_GET(SPLL0_CTL0_OUTDIV, pll0_ctl0); outdiv = 1 << p; pll_freq = (u64)parent_rate * n; - div_u64(pll_freq, m * outdiv); + pll_freq = div_u64(pll_freq, m * outdiv); return pll_freq; } @@ -110,7 +110,7 @@ static unsigned long ma35d1_calc_pll_freq(u8 mode, u32 *reg_ctl, unsigned long p if (mode == PLL_MODE_INT) { pll_freq = (u64)parent_rate * n; - div_u64(pll_freq, m * p); + pll_freq = div_u64(pll_freq, m * p); } else { x = FIELD_GET(PLL_CTL1_FRAC, reg_ctl[1]); /* 2 decimal places floating to integer (ex. 1.23 to 123) */ From 26de5aed72d80bd8aec2583134aca3597c64fda9 Mon Sep 17 00:00:00 2001 From: Joey Lu Date: Thu, 21 May 2026 09:42:19 +0800 Subject: [PATCH 27/53] clk: nuvoton: ma35d1: fix PLL_CTL1_FRAC bit field width and fractional calc PLL_CTL1_FRAC was defined as GENMASK(31, 24), covering only 8 bits. The hardware fractional field occupies bits [31:8] (24 bits), so the mask must be GENMASK(31, 8). The previous fractional-mode calculation used FIELD_MAX(PLL_CTL1_FRAC) as the denominator to obtain 2 decimal places. With the corrected 24-bit mask the old divisor is wrong; replace the arithmetic with a proper 24-bit fixed-point rounding to 3 decimal places using the kernel's DIV_ROUND_CLOSEST_ULL helper: n_frac = n * 1000 + DIV_ROUND_CLOSEST_ULL(x * 1000, 1 << 24) Fixes: 691521a367cf ("clk: nuvoton: Add clock driver for ma35d1 clock controller") Signed-off-by: Joey Lu Reviewed-by: Brian Masney Signed-off-by: Brian Masney --- drivers/clk/nuvoton/clk-ma35d1-pll.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/clk/nuvoton/clk-ma35d1-pll.c b/drivers/clk/nuvoton/clk-ma35d1-pll.c index bfedd45bd04b..eb9d69d2077b 100644 --- a/drivers/clk/nuvoton/clk-ma35d1-pll.c +++ b/drivers/clk/nuvoton/clk-ma35d1-pll.c @@ -48,7 +48,7 @@ #define PLL_CTL1_PD BIT(0) #define PLL_CTL1_BP BIT(1) #define PLL_CTL1_OUTDIV GENMASK(6, 4) -#define PLL_CTL1_FRAC GENMASK(31, 24) +#define PLL_CTL1_FRAC GENMASK(31, 8) #define PLL_CTL2_SLOPE GENMASK(23, 0) #define INDIV_MIN 1 @@ -113,9 +113,9 @@ static unsigned long ma35d1_calc_pll_freq(u8 mode, u32 *reg_ctl, unsigned long p pll_freq = div_u64(pll_freq, m * p); } else { x = FIELD_GET(PLL_CTL1_FRAC, reg_ctl[1]); - /* 2 decimal places floating to integer (ex. 1.23 to 123) */ - n = n * 100 + ((x * 100) / FIELD_MAX(PLL_CTL1_FRAC)); - pll_freq = div_u64(parent_rate * n, 100 * m * p); + /* convert 24-bit fraction to 3 decimal digits, rounding to closest */ + n = n * 1000 + DIV_ROUND_CLOSEST_ULL((u64)x * 1000, 1ULL << 24); + pll_freq = div_u64((u64)parent_rate * n, 1000 * m * p); } return pll_freq; } From e1311954cb600d5f95cd9e2fe9a7376edc2ac3c5 Mon Sep 17 00:00:00 2001 From: Joey Lu Date: Thu, 21 May 2026 09:42:20 +0800 Subject: [PATCH 28/53] clk: nuvoton: ma35d1: fix ma35d1_clk_pll_determine_rate logic ma35d1_clk_pll_determine_rate() called ma35d1_pll_find_closest() unconditionally before the switch statement, and then every case branch overwrote pll_freq by reading the current hardware registers. For CAPLL and DDRPLL this means find_closest() ran unnecessarily (and incorrectly, since those PLLs are read-only) and its result was silently discarded. Fix by moving the find_closest() call inside the APLL/EPLL/VPLL branch where it belongs. Group CAPLL and DDRPLL together as read-only PLLs that simply report their current rate; handle them with an explicit if/else to keep the CAPLL (SMIC design) and DDRPLL (standard design) paths distinct. Fixes: 691521a367cf ("clk: nuvoton: Add clock driver for ma35d1 clock controller") Signed-off-by: Joey Lu Signed-off-by: Brian Masney --- drivers/clk/nuvoton/clk-ma35d1-pll.c | 30 ++++++++++++++-------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/drivers/clk/nuvoton/clk-ma35d1-pll.c b/drivers/clk/nuvoton/clk-ma35d1-pll.c index eb9d69d2077b..c7c0dc91a012 100644 --- a/drivers/clk/nuvoton/clk-ma35d1-pll.c +++ b/drivers/clk/nuvoton/clk-ma35d1-pll.c @@ -255,32 +255,32 @@ static int ma35d1_clk_pll_determine_rate(struct clk_hw *hw, if (req->best_parent_rate < PLL_FREF_MIN_FREQ || req->best_parent_rate > PLL_FREF_MAX_FREQ) return -EINVAL; - ret = ma35d1_pll_find_closest(pll, req->rate, req->best_parent_rate, - reg_ctl, &pll_freq); - if (ret < 0) - return ret; - switch (pll->id) { case CAPLL: - reg_ctl[0] = readl_relaxed(pll->ctl0_base); - pll_freq = ma35d1_calc_smic_pll_freq(reg_ctl[0], req->best_parent_rate); - req->rate = pll_freq; - - return 0; case DDRPLL: + /* Read-only PLLs: return current rate */ + reg_ctl[0] = readl_relaxed(pll->ctl0_base); + if (pll->id == CAPLL) { + pll_freq = ma35d1_calc_smic_pll_freq(reg_ctl[0], req->best_parent_rate); + } else { + reg_ctl[1] = readl_relaxed(pll->ctl1_base); + pll_freq = ma35d1_calc_pll_freq(pll->mode, reg_ctl, req->best_parent_rate); + } + req->rate = pll_freq; + return 0; case APLL: case EPLL: case VPLL: - reg_ctl[0] = readl_relaxed(pll->ctl0_base); - reg_ctl[1] = readl_relaxed(pll->ctl1_base); - pll_freq = ma35d1_calc_pll_freq(pll->mode, reg_ctl, req->best_parent_rate); + /* Configurable PLLs: find closest achievable rate */ + ret = ma35d1_pll_find_closest(pll, req->rate, req->best_parent_rate, + reg_ctl, &pll_freq); + if (ret < 0) + return ret; req->rate = pll_freq; - return 0; } req->rate = 0; - return 0; } From 0bf68e8dcb843f094ed73c2c54e9fe58a7a4f774 Mon Sep 17 00:00:00 2001 From: Rosen Penev Date: Tue, 19 May 2026 15:26:39 -0700 Subject: [PATCH 29/53] clk: stm32: add missing bitfield.h header It seems some ARM header includes this and the build passes there, but nowhere else. Note that the driver has COMPILE_TEST in depends. Fixes: 37ae8501cdb0 ("clk: stm32: introduce clocks for STM32MP21 platfor") Signed-off-by: Rosen Penev Reviewed-by: Brian Masney Signed-off-by: Brian Masney --- drivers/clk/stm32/clk-stm32mp21.c | 1 + drivers/clk/stm32/clk-stm32mp25.c | 1 + 2 files changed, 2 insertions(+) diff --git a/drivers/clk/stm32/clk-stm32mp21.c b/drivers/clk/stm32/clk-stm32mp21.c index c8a37b716bd5..bdb17419908c 100644 --- a/drivers/clk/stm32/clk-stm32mp21.c +++ b/drivers/clk/stm32/clk-stm32mp21.c @@ -4,6 +4,7 @@ * Author: Gabriel Fernandez for STMicroelectronics. */ +#include #include #include #include diff --git a/drivers/clk/stm32/clk-stm32mp25.c b/drivers/clk/stm32/clk-stm32mp25.c index 52f0e8a12926..eb0bc918ecee 100644 --- a/drivers/clk/stm32/clk-stm32mp25.c +++ b/drivers/clk/stm32/clk-stm32mp25.c @@ -4,6 +4,7 @@ * Author: Gabriel Fernandez for STMicroelectronics. */ +#include #include #include #include From f5964864856c3422c8193e22d9ae80e2edbbcf18 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Uwe=20Kleine-K=C3=B6nig=20=28The=20Capable=20Hub=29?= Date: Fri, 15 May 2026 17:10:39 +0200 Subject: [PATCH 30/53] clk: si5341: Drop unused i2c driver_data MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The driver doesn't make use of the value that was explicitly assigned to the .driver_data member. Drop the assignment. Signed-off-by: Uwe Kleine-König (The Capable Hub) Reviewed-by: Brian Masney Signed-off-by: Brian Masney --- drivers/clk/clk-si5341.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/drivers/clk/clk-si5341.c b/drivers/clk/clk-si5341.c index 2499b771cd83..5311c23532b4 100644 --- a/drivers/clk/clk-si5341.c +++ b/drivers/clk/clk-si5341.c @@ -1825,11 +1825,11 @@ static void si5341_remove(struct i2c_client *client) } static const struct i2c_device_id si5341_id[] = { - { "si5340", 0 }, - { "si5341", 1 }, - { "si5342", 2 }, - { "si5344", 4 }, - { "si5345", 5 }, + { .name = "si5340" }, + { .name = "si5341" }, + { .name = "si5342" }, + { .name = "si5344" }, + { .name = "si5345" }, { } }; MODULE_DEVICE_TABLE(i2c, si5341_id); From cae51b908edc6edca381f4cf9fb139ee40968a88 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Uwe=20Kleine-K=C3=B6nig=20=28The=20Capable=20Hub=29?= Date: Fri, 15 May 2026 17:10:40 +0200 Subject: [PATCH 31/53] clk: Use named initializers for arrays of i2c_device_data MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit While being less compact, using named initializers allows to more easily see which members of the structs are assigned which value without having to lookup the declaration of the struct. And it's also more robust against changes to the struct definition. The mentioned robustness is relevant for a planned change to struct i2c_device_id that replaces .driver_data by an anonymous union. While touching all these arrays, unify usage of whitespace and commas. This patch doesn't modify the compiled arrays, only their representation in source form benefits. The former was confirmed with x86 and arm64 builds. Signed-off-by: Uwe Kleine-König (The Capable Hub) Reviewed-by: Luca Ceresoli # clk-versaclock5 Reviewed-by: Brian Masney Signed-off-by: Brian Masney --- drivers/clk/clk-cdce706.c | 2 +- drivers/clk/clk-cdce925.c | 8 ++++---- drivers/clk/clk-cs2000-cp.c | 4 ++-- drivers/clk/clk-renesas-pcie.c | 6 +++--- drivers/clk/clk-si514.c | 2 +- drivers/clk/clk-si521xx.c | 6 +++--- drivers/clk/clk-si5351.c | 8 ++++---- drivers/clk/clk-si544.c | 6 +++--- drivers/clk/clk-si570.c | 8 ++++---- drivers/clk/clk-versaclock5.c | 16 ++++++++-------- drivers/clk/clk-versaclock7.c | 4 ++-- 11 files changed, 35 insertions(+), 35 deletions(-) diff --git a/drivers/clk/clk-cdce706.c b/drivers/clk/clk-cdce706.c index a495d313b02f..425a0481f46f 100644 --- a/drivers/clk/clk-cdce706.c +++ b/drivers/clk/clk-cdce706.c @@ -680,7 +680,7 @@ MODULE_DEVICE_TABLE(of, cdce706_dt_match); #endif static const struct i2c_device_id cdce706_id[] = { - { "cdce706" }, + { .name = "cdce706" }, { } }; MODULE_DEVICE_TABLE(i2c, cdce706_id); diff --git a/drivers/clk/clk-cdce925.c b/drivers/clk/clk-cdce925.c index 0b2ad21e6e4d..30d0493c7718 100644 --- a/drivers/clk/clk-cdce925.c +++ b/drivers/clk/clk-cdce925.c @@ -826,10 +826,10 @@ static const struct clk_cdce925_chip_info clk_cdce949_info = { }; static const struct i2c_device_id cdce925_id[] = { - { "cdce913", (kernel_ulong_t)&clk_cdce913_info }, - { "cdce925", (kernel_ulong_t)&clk_cdce925_info }, - { "cdce937", (kernel_ulong_t)&clk_cdce937_info }, - { "cdce949", (kernel_ulong_t)&clk_cdce949_info }, + { .name = "cdce913", .driver_data = (kernel_ulong_t)&clk_cdce913_info }, + { .name = "cdce925", .driver_data = (kernel_ulong_t)&clk_cdce925_info }, + { .name = "cdce937", .driver_data = (kernel_ulong_t)&clk_cdce937_info }, + { .name = "cdce949", .driver_data = (kernel_ulong_t)&clk_cdce949_info }, { } }; MODULE_DEVICE_TABLE(i2c, cdce925_id); diff --git a/drivers/clk/clk-cs2000-cp.c b/drivers/clk/clk-cs2000-cp.c index 8800472ba63f..fd277dcefad7 100644 --- a/drivers/clk/clk-cs2000-cp.c +++ b/drivers/clk/clk-cs2000-cp.c @@ -122,8 +122,8 @@ static const struct of_device_id cs2000_of_match[] = { MODULE_DEVICE_TABLE(of, cs2000_of_match); static const struct i2c_device_id cs2000_id[] = { - { "cs2000-cp", }, - {} + { .name = "cs2000-cp" }, + { } }; MODULE_DEVICE_TABLE(i2c, cs2000_id); diff --git a/drivers/clk/clk-renesas-pcie.c b/drivers/clk/clk-renesas-pcie.c index aa108df12e44..b59607e3e659 100644 --- a/drivers/clk/clk-renesas-pcie.c +++ b/drivers/clk/clk-renesas-pcie.c @@ -395,9 +395,9 @@ static const struct rs9_chip_info renesas_9fgv0841_info = { }; static const struct i2c_device_id rs9_id[] = { - { "9fgv0241", .driver_data = (kernel_ulong_t)&renesas_9fgv0241_info }, - { "9fgv0441", .driver_data = (kernel_ulong_t)&renesas_9fgv0441_info }, - { "9fgv0841", .driver_data = (kernel_ulong_t)&renesas_9fgv0841_info }, + { .name = "9fgv0241", .driver_data = (kernel_ulong_t)&renesas_9fgv0241_info }, + { .name = "9fgv0441", .driver_data = (kernel_ulong_t)&renesas_9fgv0441_info }, + { .name = "9fgv0841", .driver_data = (kernel_ulong_t)&renesas_9fgv0841_info }, { } }; MODULE_DEVICE_TABLE(i2c, rs9_id); diff --git a/drivers/clk/clk-si514.c b/drivers/clk/clk-si514.c index f61590d70575..c0b433ad9372 100644 --- a/drivers/clk/clk-si514.c +++ b/drivers/clk/clk-si514.c @@ -379,7 +379,7 @@ static int si514_probe(struct i2c_client *client) } static const struct i2c_device_id si514_id[] = { - { "si514" }, + { .name = "si514" }, { } }; MODULE_DEVICE_TABLE(i2c, si514_id); diff --git a/drivers/clk/clk-si521xx.c b/drivers/clk/clk-si521xx.c index 4ed4e1a5f4f2..e41d922437ee 100644 --- a/drivers/clk/clk-si521xx.c +++ b/drivers/clk/clk-si521xx.c @@ -365,9 +365,9 @@ static int __maybe_unused si521xx_resume(struct device *dev) } static const struct i2c_device_id si521xx_id[] = { - { "si52144", .driver_data = SI521XX_OE_MAP(0x5, 0xc0) }, - { "si52146", .driver_data = SI521XX_OE_MAP(0x15, 0xe0) }, - { "si52147", .driver_data = SI521XX_OE_MAP(0x17, 0xf8) }, + { .name = "si52144", .driver_data = SI521XX_OE_MAP(0x5, 0xc0) }, + { .name = "si52146", .driver_data = SI521XX_OE_MAP(0x15, 0xe0) }, + { .name = "si52147", .driver_data = SI521XX_OE_MAP(0x17, 0xf8) }, { } }; MODULE_DEVICE_TABLE(i2c, si521xx_id); diff --git a/drivers/clk/clk-si5351.c b/drivers/clk/clk-si5351.c index e755db545e2e..20b0c3c48b86 100644 --- a/drivers/clk/clk-si5351.c +++ b/drivers/clk/clk-si5351.c @@ -1425,10 +1425,10 @@ si53351_of_clk_get(struct of_phandle_args *clkspec, void *data) #endif /* CONFIG_OF */ static const struct i2c_device_id si5351_i2c_ids[] = { - { "si5351a", SI5351_VARIANT_A }, - { "si5351a-msop", SI5351_VARIANT_A3 }, - { "si5351b", SI5351_VARIANT_B }, - { "si5351c", SI5351_VARIANT_C }, + { .name = "si5351a", .driver_data = SI5351_VARIANT_A }, + { .name = "si5351a-msop", .driver_data = SI5351_VARIANT_A3 }, + { .name = "si5351b", .driver_data = SI5351_VARIANT_B }, + { .name = "si5351c", .driver_data = SI5351_VARIANT_C }, { } }; MODULE_DEVICE_TABLE(i2c, si5351_i2c_ids); diff --git a/drivers/clk/clk-si544.c b/drivers/clk/clk-si544.c index 09c06ecec1a5..f45f3f73aa82 100644 --- a/drivers/clk/clk-si544.c +++ b/drivers/clk/clk-si544.c @@ -479,9 +479,9 @@ static int si544_probe(struct i2c_client *client) } static const struct i2c_device_id si544_id[] = { - { "si544a", 1500000000 }, - { "si544b", 800000000 }, - { "si544c", 350000000 }, + { .name = "si544a", .driver_data = 1500000000 }, + { .name = "si544b", .driver_data = 800000000 }, + { .name = "si544c", .driver_data = 350000000 }, { } }; MODULE_DEVICE_TABLE(i2c, si544_id); diff --git a/drivers/clk/clk-si570.c b/drivers/clk/clk-si570.c index b0b1830dd430..3063e38c66da 100644 --- a/drivers/clk/clk-si570.c +++ b/drivers/clk/clk-si570.c @@ -503,10 +503,10 @@ static const struct clk_si570_info clk_si590_info = { }; static const struct i2c_device_id si570_id[] = { - { "si570", (kernel_ulong_t)&clk_si570_info }, - { "si571", (kernel_ulong_t)&clk_si570_info }, - { "si598", (kernel_ulong_t)&clk_si590_info }, - { "si599", (kernel_ulong_t)&clk_si590_info }, + { .name = "si570", .driver_data = (kernel_ulong_t)&clk_si570_info }, + { .name = "si571", .driver_data = (kernel_ulong_t)&clk_si570_info }, + { .name = "si598", .driver_data = (kernel_ulong_t)&clk_si590_info }, + { .name = "si599", .driver_data = (kernel_ulong_t)&clk_si590_info }, { } }; MODULE_DEVICE_TABLE(i2c, si570_id); diff --git a/drivers/clk/clk-versaclock5.c b/drivers/clk/clk-versaclock5.c index 57228e88e81d..484b02726b3a 100644 --- a/drivers/clk/clk-versaclock5.c +++ b/drivers/clk/clk-versaclock5.c @@ -1311,14 +1311,14 @@ static const struct vc5_chip_info idt_5p49v6975_info = { }; static const struct i2c_device_id vc5_id[] = { - { "5p49v5923", .driver_data = (kernel_ulong_t)&idt_5p49v5923_info }, - { "5p49v5925", .driver_data = (kernel_ulong_t)&idt_5p49v5925_info }, - { "5p49v5933", .driver_data = (kernel_ulong_t)&idt_5p49v5933_info }, - { "5p49v5935", .driver_data = (kernel_ulong_t)&idt_5p49v5935_info }, - { "5p49v60", .driver_data = (kernel_ulong_t)&idt_5p49v60_info }, - { "5p49v6901", .driver_data = (kernel_ulong_t)&idt_5p49v6901_info }, - { "5p49v6965", .driver_data = (kernel_ulong_t)&idt_5p49v6965_info }, - { "5p49v6975", .driver_data = (kernel_ulong_t)&idt_5p49v6975_info }, + { .name = "5p49v5923", .driver_data = (kernel_ulong_t)&idt_5p49v5923_info }, + { .name = "5p49v5925", .driver_data = (kernel_ulong_t)&idt_5p49v5925_info }, + { .name = "5p49v5933", .driver_data = (kernel_ulong_t)&idt_5p49v5933_info }, + { .name = "5p49v5935", .driver_data = (kernel_ulong_t)&idt_5p49v5935_info }, + { .name = "5p49v60", .driver_data = (kernel_ulong_t)&idt_5p49v60_info }, + { .name = "5p49v6901", .driver_data = (kernel_ulong_t)&idt_5p49v6901_info }, + { .name = "5p49v6965", .driver_data = (kernel_ulong_t)&idt_5p49v6965_info }, + { .name = "5p49v6975", .driver_data = (kernel_ulong_t)&idt_5p49v6975_info }, { } }; MODULE_DEVICE_TABLE(i2c, vc5_id); diff --git a/drivers/clk/clk-versaclock7.c b/drivers/clk/clk-versaclock7.c index e3a36dcd98b8..1340d2f512be 100644 --- a/drivers/clk/clk-versaclock7.c +++ b/drivers/clk/clk-versaclock7.c @@ -1288,8 +1288,8 @@ static const struct regmap_config vc7_regmap_config = { }; static const struct i2c_device_id vc7_i2c_id[] = { - { "rc21008a", .driver_data = (kernel_ulong_t)&vc7_rc21008a_info }, - {} + { .name = "rc21008a", .driver_data = (kernel_ulong_t)&vc7_rc21008a_info }, + { } }; MODULE_DEVICE_TABLE(i2c, vc7_i2c_id); From 003d9ba31a5d21712a98f43a431fc00ff66c3d21 Mon Sep 17 00:00:00 2001 From: Randy Dunlap Date: Tue, 12 May 2026 14:24:53 -0700 Subject: [PATCH 32/53] clk: add missing function short descriptions for kernel-doc Add short descriptions for 3 functions which are missing it. Modify the parameter name in comments to be @core instead of clk. Use corrected function names (with leading "__") in a few places. Warning: drivers/clk/clk.c:1899 missing initial short description on line: * __clk_recalc_accuracies Warning: drivers/clk/clk.c:1972 missing initial short description on line: * __clk_recalc_rates Warning: drivers/clk/clk.c:2244 missing initial short description on line: * __clk_speculate_rates This eliminates warnings in this file except for missing return value warnings, of which there are around 70. Signed-off-by: Randy Dunlap Reviewed-by: Brian Masney Signed-off-by: Brian Masney --- drivers/clk/clk.c | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/drivers/clk/clk.c b/drivers/clk/clk.c index 8c78621cde25..83a80e5cff2e 100644 --- a/drivers/clk/clk.c +++ b/drivers/clk/clk.c @@ -1846,10 +1846,10 @@ static int __clk_notify(struct clk_core *core, unsigned long msg, } /** - * __clk_recalc_accuracies + * __clk_recalc_accuracies - recalculate all accuracies in the clk subtree * @core: first clk in the subtree * - * Walks the subtree of clks starting with clk and recalculates accuracies as + * Walks the subtree of clks starting with @core and recalculates accuracies as * it goes. Note that if a clk does not implement the .recalc_accuracy * callback then it is assumed that the clock will take on the accuracy of its * parent. @@ -1919,16 +1919,16 @@ static unsigned long clk_recalc(struct clk_core *core, } /** - * __clk_recalc_rates + * __clk_recalc_rates - recalculate all rates in the clk subtree * @core: first clk in the subtree * @update_req: Whether req_rate should be updated with the new rate * @msg: notification type (see include/linux/clk.h) * - * Walks the subtree of clks starting with clk and recalculates rates as it + * Walks the subtree of clks starting with @core and recalculates rates as it * goes. Note that if a clk does not implement the .recalc_rate callback then * it is assumed that the clock will take on the rate of its parent. * - * clk_recalc_rates also propagates the POST_RATE_CHANGE notification, + * __clk_recalc_rates also propagates the POST_RATE_CHANGE notification, * if necessary. */ static void __clk_recalc_rates(struct clk_core *core, bool update_req, @@ -2191,14 +2191,14 @@ static int __clk_set_parent(struct clk_core *core, struct clk_core *parent, } /** - * __clk_speculate_rates + * __clk_speculate_rates - speculate all rates in the clk subtree * @core: first clk in the subtree * @parent_rate: the "future" rate of clk's parent * - * Walks the subtree of clks starting with clk, speculating rates as it + * Walks the subtree of clks starting with @core, speculating rates as it * goes and firing off PRE_RATE_CHANGE notifications as necessary. * - * Unlike clk_recalc_rates, clk_speculate_rates exists only for sending + * Unlike __clk_recalc_rates, __clk_speculate_rates exists only for sending * pre-rate change notifications and returns early if no clks in the * subtree have subscribed to the notifications. Note that if a clk does not * implement the .recalc_rate callback then it is assumed that the clock will From a8036f4591542de4b38ec81d3e2ba47bc0b2652b Mon Sep 17 00:00:00 2001 From: Min zhang Date: Tue, 12 May 2026 08:06:42 +0800 Subject: [PATCH 33/53] clk: hisilicon: reset: Use devm_kzalloc to initialize hisi_reset_controller Using devm_kmalloc() does not zero-initialize the allocated structure. Uninitialized members in struct hisi_reset_controller may contain garbage data, which can cause reset_controller_register() to fail unexpectedly. Replace devm_kmalloc() with devm_kzalloc() to ensure all structure fields are properly zero-initialized. Fixes: 97b7129cd2afb ("reset: hisilicon: change the definition of hisi_reset_init") Reviewed-by: Brian Masney Signed-off-by: Min zhang Signed-off-by: Brian Masney --- drivers/clk/hisilicon/reset.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/clk/hisilicon/reset.c b/drivers/clk/hisilicon/reset.c index 93cee17db8b1..c3b7daac9313 100644 --- a/drivers/clk/hisilicon/reset.c +++ b/drivers/clk/hisilicon/reset.c @@ -91,7 +91,7 @@ struct hisi_reset_controller *hisi_reset_init(struct platform_device *pdev) { struct hisi_reset_controller *rstc; - rstc = devm_kmalloc(&pdev->dev, sizeof(*rstc), GFP_KERNEL); + rstc = devm_kzalloc(&pdev->dev, sizeof(*rstc), GFP_KERNEL); if (!rstc) return NULL; From 48d5f26f1d5c14e2edf21c843e815a105badd1ba Mon Sep 17 00:00:00 2001 From: Rosen Penev Date: Fri, 8 May 2026 22:11:10 -0700 Subject: [PATCH 34/53] clk: hisilicon: allow COMPILE_TEST builds The HiSilicon clock symbols already depend on ARCH_HISI or COMPILE_TEST, but the parent clock Makefile only descends into the hisilicon directory when ARCH_HISI is enabled. Add a hidden family gate selected by the HiSilicon clock and reset symbols, default it for ARCH_HISI, and use it for the parent Makefile descent. This keeps existing platform builds unchanged while allowing compile-test coverage. Tested with: make LLVM=1 ARCH=loongarch drivers/clk/hisilicon/ Assisted-by: Codex:GPT-5.5 Signed-off-by: Rosen Penev Reviewed-by: Brian Masney Signed-off-by: Brian Masney --- drivers/clk/Makefile | 2 +- drivers/clk/hisilicon/Kconfig | 14 ++++++++++++++ 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/drivers/clk/Makefile b/drivers/clk/Makefile index d042b4aa9c0f..c55e323d3d92 100644 --- a/drivers/clk/Makefile +++ b/drivers/clk/Makefile @@ -132,7 +132,7 @@ obj-y += bcm/ obj-$(CONFIG_ARCH_BERLIN) += berlin/ obj-$(CONFIG_ARCH_DAVINCI) += davinci/ obj-$(CONFIG_COMMON_CLK_ESWIN) += eswin/ -obj-$(CONFIG_ARCH_HISI) += hisilicon/ +obj-$(CONFIG_COMMON_CLK_HISI) += hisilicon/ obj-y += imgtec/ obj-y += imx/ obj-y += ingenic/ diff --git a/drivers/clk/hisilicon/Kconfig b/drivers/clk/hisilicon/Kconfig index c1ec75aa4ccd..e70bcfa9105b 100644 --- a/drivers/clk/hisilicon/Kconfig +++ b/drivers/clk/hisilicon/Kconfig @@ -1,8 +1,13 @@ # SPDX-License-Identifier: GPL-2.0-only +config COMMON_CLK_HISI + bool + default ARCH_HISI + config COMMON_CLK_HI3516CV300 tristate "HI3516CV300 Clock Driver" depends on ARCH_HISI || COMPILE_TEST select RESET_HISI + select COMMON_CLK_HISI default ARCH_HISI help Build the clock driver for hi3516cv300. @@ -11,6 +16,7 @@ config COMMON_CLK_HI3519 tristate "Hi3519 Clock Driver" depends on ARCH_HISI || COMPILE_TEST select RESET_HISI + select COMMON_CLK_HISI default ARCH_HISI help Build the clock driver for hi3519. @@ -19,6 +25,7 @@ config COMMON_CLK_HI3559A bool "Hi3559A Clock Driver" depends on ARCH_HISI || COMPILE_TEST select RESET_HISI + select COMMON_CLK_HISI default ARCH_HISI help Build the clock driver for hi3559a. @@ -26,6 +33,7 @@ config COMMON_CLK_HI3559A config COMMON_CLK_HI3660 bool "Hi3660 Clock Driver" depends on ARCH_HISI || COMPILE_TEST + select COMMON_CLK_HISI default ARCH_HISI help Build the clock driver for hi3660. @@ -33,6 +41,7 @@ config COMMON_CLK_HI3660 config COMMON_CLK_HI3670 bool "Hi3670 Clock Driver" depends on ARCH_HISI || COMPILE_TEST + select COMMON_CLK_HISI default ARCH_HISI help Build the clock driver for hi3670. @@ -41,6 +50,7 @@ config COMMON_CLK_HI3798CV200 tristate "Hi3798CV200 Clock Driver" depends on ARCH_HISI || COMPILE_TEST select RESET_HISI + select COMMON_CLK_HISI default ARCH_HISI help Build the clock driver for hi3798cv200. @@ -48,6 +58,7 @@ config COMMON_CLK_HI3798CV200 config COMMON_CLK_HI6220 bool "Hi6220 Clock Driver" depends on ARCH_HISI || COMPILE_TEST + select COMMON_CLK_HISI default ARCH_HISI help Build the Hisilicon Hi6220 clock driver based on the common clock framework. @@ -55,6 +66,7 @@ config COMMON_CLK_HI6220 config RESET_HISI bool "HiSilicon Reset Controller Driver" depends on ARCH_HISI || COMPILE_TEST + select COMMON_CLK_HISI select RESET_CONTROLLER help Build reset controller driver for HiSilicon device chipsets. @@ -63,6 +75,7 @@ config STUB_CLK_HI6220 bool "Hi6220 Stub Clock Driver" if EXPERT depends on (COMMON_CLK_HI6220 || COMPILE_TEST) depends on MAILBOX + select COMMON_CLK_HISI default COMMON_CLK_HI6220 help Build the Hisilicon Hi6220 stub clock driver. @@ -71,6 +84,7 @@ config STUB_CLK_HI3660 bool "Hi3660 Stub Clock Driver" if EXPERT depends on (COMMON_CLK_HI3660 || COMPILE_TEST) depends on MAILBOX + select COMMON_CLK_HISI default COMMON_CLK_HI3660 help Build the Hisilicon Hi3660 stub clock driver. From ef9f74ee4ccc5f98108eb2dfbf293f64db61ccb1 Mon Sep 17 00:00:00 2001 From: Brian Masney Date: Mon, 11 May 2026 21:35:04 -0400 Subject: [PATCH 35/53] clk: add kernel docs for the core flags Let's add a DOC section for the clk core flags, and move the documentation for each flag into the doc header so that it can be easily referenced in the generated kernel documentation. Note: The comment about "Please update clk_flags..." is included as a separate comment so it doesn't show up in the generated documents. Reviewed-by: Maxime Ripard Signed-off-by: Brian Masney --- include/linux/clk-provider.h | 46 +++++++++++++++++++++++------------- 1 file changed, 29 insertions(+), 17 deletions(-) diff --git a/include/linux/clk-provider.h b/include/linux/clk-provider.h index 7d3747378739..a2348b961538 100644 --- a/include/linux/clk-provider.h +++ b/include/linux/clk-provider.h @@ -10,28 +10,40 @@ #include #include -/* - * flags used across common struct clk. these flags should only affect the - * top-level framework. custom flags for dealing with hardware specifics - * belong in struct clk_foo +/** + * DOC: clk framework flags * - * Please update clk_flags[] in drivers/clk/clk.c when making changes here! + * Flags used across common struct clk. These flags should only affect the + * top-level framework. Custom flags for dealing with hardware specifics + * belong in struct clk_foo. + * + * * CLK_SET_RATE_GATE - must be gated across rate change + * * CLK_SET_PARENT_GATE - must be gated across re-parent + * * CLK_SET_RATE_PARENT - propagate rate change up one level + * * CLK_IGNORE_UNUSED - do not gate even if unused + * * CLK_GET_RATE_NOCACHE - do not use the cached clk rate + * * CLK_SET_RATE_NO_REPARENT - don't re-parent on rate change + * * CLK_GET_ACCURACY_NOCACHE - do not use the cached clk accuracy + * * CLK_RECALC_NEW_RATES - recalc rates after notifications + * * CLK_SET_RATE_UNGATE - clock needs to run to set rate + * * CLK_IS_CRITICAL - do not gate, ever + * * CLK_OPS_PARENT_ENABLE - parents need enable during gate/ungate, set rate and re-parent + * * CLK_DUTY_CYCLE_PARENT - duty cycle call may be forwarded to the parent clock */ -#define CLK_SET_RATE_GATE BIT(0) /* must be gated across rate change */ -#define CLK_SET_PARENT_GATE BIT(1) /* must be gated across re-parent */ -#define CLK_SET_RATE_PARENT BIT(2) /* propagate rate change up one level */ -#define CLK_IGNORE_UNUSED BIT(3) /* do not gate even if unused */ +/* Please update clk_flags[] in drivers/clk/clk.c when making changes here! */ +#define CLK_SET_RATE_GATE BIT(0) +#define CLK_SET_PARENT_GATE BIT(1) +#define CLK_SET_RATE_PARENT BIT(2) +#define CLK_IGNORE_UNUSED BIT(3) /* unused */ /* unused */ -#define CLK_GET_RATE_NOCACHE BIT(6) /* do not use the cached clk rate */ -#define CLK_SET_RATE_NO_REPARENT BIT(7) /* don't re-parent on rate change */ -#define CLK_GET_ACCURACY_NOCACHE BIT(8) /* do not use the cached clk accuracy */ -#define CLK_RECALC_NEW_RATES BIT(9) /* recalc rates after notifications */ -#define CLK_SET_RATE_UNGATE BIT(10) /* clock needs to run to set rate */ -#define CLK_IS_CRITICAL BIT(11) /* do not gate, ever */ -/* parents need enable during gate/ungate, set rate and re-parent */ +#define CLK_GET_RATE_NOCACHE BIT(6) +#define CLK_SET_RATE_NO_REPARENT BIT(7) +#define CLK_GET_ACCURACY_NOCACHE BIT(8) +#define CLK_RECALC_NEW_RATES BIT(9) +#define CLK_SET_RATE_UNGATE BIT(10) +#define CLK_IS_CRITICAL BIT(11) #define CLK_OPS_PARENT_ENABLE BIT(12) -/* duty cycle call may be forwarded to the parent clock */ #define CLK_DUTY_CYCLE_PARENT BIT(13) struct clk; From d131daafa99ffb4ce47e1d0e8a01c8d61a7b7142 Mon Sep 17 00:00:00 2001 From: Brian Masney Date: Mon, 11 May 2026 21:35:05 -0400 Subject: [PATCH 36/53] clk: add kernel docs for struct clk_core Document all of the members of struct clk_core. Reviewed-by: Maxime Ripard Acked-by: Randy Dunlap Tested-by: Randy Dunlap Signed-off-by: Brian Masney --- drivers/clk/clk.c | 51 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 51 insertions(+) diff --git a/drivers/clk/clk.c b/drivers/clk/clk.c index 83a80e5cff2e..f97a7cecb200 100644 --- a/drivers/clk/clk.c +++ b/drivers/clk/clk.c @@ -63,6 +63,57 @@ struct clk_parent_map { int index; }; +/** + * struct clk_core - The internal state of a clk in the clk tree. + * @name: Unique name of the clk for identification. + * @ops: Pointer to hardware-specific operations for this clk. + * @hw: Pointer for traversing from a struct clk to its + * corresponding hardware-specific structure. + * @owner: Kernel module owning this clk (for reference counting). + * @dev: Device associated with this clk (optional) + * @rpm_node: Node for runtime power management list management. + * @of_node: Device tree node associated with this clk (if applicable) + * @parent: Pointer to the current parent in the clock tree. + * @parents: Array of possible parents (for muxes/selectable parents). + * @num_parents: Number of possible parents. + * @new_parent_index: Index of the new parent during parent change operations. + * @rate: Current cached clock rate (Hz). + * @req_rate: The last rate requested by a call to clk_set_rate(). It's + * initialized to clk_core->rate. It's also updated to + * clk_core->rate every time the clock is reparented, and + * when we're doing the orphan -> !orphan transition. + * @new_rate: New rate to be set during a rate change operation. + * @new_parent: Pointer to new parent during parent change. This is also + * used when a clk's rate is changed. + * @new_child: Pointer to new child during reparenting. This is also + * used when a clk's rate is changed. + * @flags: Clock property and capability flags. See + * `clk framework flags`. + * @orphan: True if this clk is currently orphaned. + * @rpm_enabled: True if runtime power management is enabled for this clk. + * @enable_count: Reference count of enables. + * @prepare_count: Reference count of prepares. + * @protect_count: Protection reference count against disable. + * @min_rate: Minimum supported clock rate (Hz). + * @max_rate: Maximum supported clock rate (Hz). + * @accuracy: Accuracy of the clock rate (parts per billion). + * @phase: Current phase (degrees). + * @duty: Current duty cycle configuration (as ratio: num/den). + * @children: All of the children of this clk. + * @child_node: Node for linking as a child in the parent's list. + * @hashtable_node: Node for hash table that allows fast clk lookup by name. + * @clks: All of the clk consumers registered. + * @notifier_count: Number of notifiers registered for this clk. + * @dentry: DebugFS entry for this clk. + * @debug_node: DebugFS node for this clk. + * @ref: Reference count for structure lifetime management. + * + * Managed by the clk framework. Clk providers and consumers do not interact + * with this structure directly. Instead, clk operations flow through the + * framework and the framework manipulates this structure to keep track of + * parent/child relationships, rate, enable state, etc. + * + */ struct clk_core { const char *name; const struct clk_ops *ops; From 378ca82af05ade38ff4fa63bc2bbbcb51cc882b6 Mon Sep 17 00:00:00 2001 From: Brian Masney Date: Mon, 11 May 2026 21:35:06 -0400 Subject: [PATCH 37/53] docs: clk: include some identifiers to keep documentation up to date The clk documentation currently has a separate list of some members of struct clk_core and struct clk_ops. Now that all of these structures have proper kernel docs, let's go ahead and just include them here via the identifiers statement in kerneldoc. While changes are being made here, let's also include the clk flags. Reviewed-by: Maxime Ripard Acked-by: Randy Dunlap Tested-by: Randy Dunlap Signed-off-by: Brian Masney --- Documentation/driver-api/clk.rst | 58 +++++++------------------------- 1 file changed, 12 insertions(+), 46 deletions(-) diff --git a/Documentation/driver-api/clk.rst b/Documentation/driver-api/clk.rst index c6aca8186a78..294ead519bbf 100644 --- a/Documentation/driver-api/clk.rst +++ b/Documentation/driver-api/clk.rst @@ -42,21 +42,8 @@ clock interface. Common data structures and api ============================== -Below is the common struct clk_core definition from -drivers/clk/clk.c, modified for brevity:: - - struct clk_core { - const char *name; - const struct clk_ops *ops; - struct clk_hw *hw; - struct module *owner; - struct clk_core *parent; - const char **parent_names; - struct clk_core **parents; - u8 num_parents; - u8 new_parent_index; - ... - }; +.. kernel-doc:: drivers/clk/clk.c + :identifiers: struct clk_core The members above make up the core of the clk tree topology. The clk api itself defines several driver-facing functions which operate on @@ -64,38 +51,17 @@ struct clk. That api is documented in include/linux/clk.h. Platforms and devices utilizing the common struct clk_core use the struct clk_ops pointer in struct clk_core to perform the hardware-specific parts of -the operations defined in clk-provider.h:: +the operations defined in clk-provider.h, and can set one or more +framework-level flags documented below. - struct clk_ops { - int (*prepare)(struct clk_hw *hw); - void (*unprepare)(struct clk_hw *hw); - int (*is_prepared)(struct clk_hw *hw); - void (*unprepare_unused)(struct clk_hw *hw); - int (*enable)(struct clk_hw *hw); - void (*disable)(struct clk_hw *hw); - int (*is_enabled)(struct clk_hw *hw); - void (*disable_unused)(struct clk_hw *hw); - unsigned long (*recalc_rate)(struct clk_hw *hw, - unsigned long parent_rate); - int (*determine_rate)(struct clk_hw *hw, - struct clk_rate_request *req); - int (*set_parent)(struct clk_hw *hw, u8 index); - u8 (*get_parent)(struct clk_hw *hw); - int (*set_rate)(struct clk_hw *hw, - unsigned long rate, - unsigned long parent_rate); - int (*set_rate_and_parent)(struct clk_hw *hw, - unsigned long rate, - unsigned long parent_rate, - u8 index); - unsigned long (*recalc_accuracy)(struct clk_hw *hw, - unsigned long parent_accuracy); - int (*get_phase)(struct clk_hw *hw); - int (*set_phase)(struct clk_hw *hw, int degrees); - void (*init)(struct clk_hw *hw); - void (*debug_init)(struct clk_hw *hw, - struct dentry *dentry); - }; +.. kernel-doc:: include/linux/clk-provider.h + :identifiers: struct clk_ops + +Core flags +========== + +.. kernel-doc:: include/linux/clk-provider.h + :doc: clk framework flags Hardware clk implementations ============================ From 4750122e2f6baa7b46e39510cd97624516a7cc62 Mon Sep 17 00:00:00 2001 From: Brian Masney Date: Mon, 11 May 2026 21:35:07 -0400 Subject: [PATCH 38/53] clk: test: convert constants to use HZ_PER_MHZ Convert the DUMMY_CLOCK_* constants over to use HZ_PER_MHZ. Reviewed-by: Maxime Ripard Signed-off-by: Brian Masney --- drivers/clk/clk_test.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/drivers/clk/clk_test.c b/drivers/clk/clk_test.c index 94972f38e2e1..1c5c8b7c1f3c 100644 --- a/drivers/clk/clk_test.c +++ b/drivers/clk/clk_test.c @@ -7,6 +7,7 @@ #include #include #include +#include /* Needed for clk_hw_get_clk() */ #include "clk.h" @@ -21,9 +22,9 @@ static const struct clk_ops empty_clk_ops = { }; -#define DUMMY_CLOCK_INIT_RATE (42 * 1000 * 1000) -#define DUMMY_CLOCK_RATE_1 (142 * 1000 * 1000) -#define DUMMY_CLOCK_RATE_2 (242 * 1000 * 1000) +#define DUMMY_CLOCK_INIT_RATE (42 * HZ_PER_MHZ) +#define DUMMY_CLOCK_RATE_1 (142 * HZ_PER_MHZ) +#define DUMMY_CLOCK_RATE_2 (242 * HZ_PER_MHZ) struct clk_dummy_context { struct clk_hw hw; From 5f2db1ce201216e81333ecc2ab51494410b2fe0d Mon Sep 17 00:00:00 2001 From: Brian Masney Date: Tue, 5 May 2026 20:48:57 -0400 Subject: [PATCH 39/53] clk: imx: scu: drop redundant init.ops variable assignment The init.ops is assigned a default value, however right below it is an if, else if, and else where all of them also assign a value to init.ops. Drop the redundant init.ops assignment at the top. Fixes: 3b9ea606cda53 ("clk: imx: scu: add cpu frequency scaling support") Reviewed-by: Peng Fan Signed-off-by: Brian Masney --- drivers/clk/imx/clk-scu.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/clk/imx/clk-scu.c b/drivers/clk/imx/clk-scu.c index 9b33df9967ec..658b6d94de9b 100644 --- a/drivers/clk/imx/clk-scu.c +++ b/drivers/clk/imx/clk-scu.c @@ -475,7 +475,6 @@ struct clk_hw *__imx_clk_scu(struct device *dev, const char *name, clk->clk_type = clk_type; init.name = name; - init.ops = &clk_scu_ops; if (rsrc_id == IMX_SC_R_A35 || rsrc_id == IMX_SC_R_A53 || rsrc_id == IMX_SC_R_A72) init.ops = &clk_scu_cpu_ops; else if (rsrc_id == IMX_SC_R_PI_0_PLL) From 862e0773f130d21a65c991f493b01f4ec040e821 Mon Sep 17 00:00:00 2001 From: Brian Masney Date: Tue, 5 May 2026 20:48:58 -0400 Subject: [PATCH 40/53] clk: add clk_determine_rate_noop() Add a new helper clk_determine_rate_noop() that's for clocks where the rate rounding is handled by the firmware/hardware, or the clock is capable of any rate. The requested rate is passed through unchanged, and the actual rate will be learned via recalc_rate() after the rate is set. This shared helper will be used to get rid of the driver-specific empty determine rate implementations that are present in the tree. Signed-off-by: Brian Masney --- drivers/clk/clk.c | 18 ++++++++++++++++++ include/linux/clk-provider.h | 1 + 2 files changed, 19 insertions(+) diff --git a/drivers/clk/clk.c b/drivers/clk/clk.c index f97a7cecb200..fef87167a60b 100644 --- a/drivers/clk/clk.c +++ b/drivers/clk/clk.c @@ -933,6 +933,24 @@ int clk_hw_determine_rate_no_reparent(struct clk_hw *hw, } EXPORT_SYMBOL_GPL(clk_hw_determine_rate_no_reparent); +/** + * clk_determine_rate_noop - clk_ops::determine_rate noop implementation + * @hw: clk to determine rate on + * @req: rate request + * + * Noop determine rate for clocks where the rate rounding is handled by the + * firmware/hardware, or the clock is capable of any rate. The requested rate is + * passed through unchanged, and the actual rate will be learned via + * recalc_rate() after the rate is set. + * + * Returns: 0 always + */ +int clk_determine_rate_noop(struct clk_hw *hw, struct clk_rate_request *req) +{ + return 0; +} +EXPORT_SYMBOL_GPL(clk_determine_rate_noop); + /*** clk api ***/ static void clk_core_rate_unprotect(struct clk_core *core) diff --git a/include/linux/clk-provider.h b/include/linux/clk-provider.h index a2348b961538..9d32e4a16eb8 100644 --- a/include/linux/clk-provider.h +++ b/include/linux/clk-provider.h @@ -1472,6 +1472,7 @@ int clk_mux_determine_rate_flags(struct clk_hw *hw, unsigned long flags); int clk_hw_determine_rate_no_reparent(struct clk_hw *hw, struct clk_rate_request *req); +int clk_determine_rate_noop(struct clk_hw *hw, struct clk_rate_request *req); void clk_hw_reparent(struct clk_hw *hw, struct clk_hw *new_parent); void clk_hw_get_rate_range(struct clk_hw *hw, unsigned long *min_rate, unsigned long *max_rate); From 7ea6726c91b51a196d09fafd40b801103c6fa555 Mon Sep 17 00:00:00 2001 From: Brian Masney Date: Tue, 5 May 2026 20:48:59 -0400 Subject: [PATCH 41/53] clk: hisilicon: hi3660-stub: use clk_determine_rate_noop() Drop the driver-specific empty determine_rate() function and use the new shared clk_determine_rate_noop() helper. Signed-off-by: Brian Masney --- drivers/clk/hisilicon/clk-hi3660-stub.c | 12 +----------- 1 file changed, 1 insertion(+), 11 deletions(-) diff --git a/drivers/clk/hisilicon/clk-hi3660-stub.c b/drivers/clk/hisilicon/clk-hi3660-stub.c index 7c8b00ee6019..0cb1c0538eae 100644 --- a/drivers/clk/hisilicon/clk-hi3660-stub.c +++ b/drivers/clk/hisilicon/clk-hi3660-stub.c @@ -67,16 +67,6 @@ static unsigned long hi3660_stub_clk_recalc_rate(struct clk_hw *hw, return stub_clk->rate; } -static int hi3660_stub_clk_determine_rate(struct clk_hw *hw, - struct clk_rate_request *req) -{ - /* - * LPM3 handles rate rounding so just return whatever - * rate is requested. - */ - return 0; -} - static int hi3660_stub_clk_set_rate(struct clk_hw *hw, unsigned long rate, unsigned long parent_rate) { @@ -97,7 +87,7 @@ static int hi3660_stub_clk_set_rate(struct clk_hw *hw, unsigned long rate, static const struct clk_ops hi3660_stub_clk_ops = { .recalc_rate = hi3660_stub_clk_recalc_rate, - .determine_rate = hi3660_stub_clk_determine_rate, + .determine_rate = clk_determine_rate_noop, .set_rate = hi3660_stub_clk_set_rate, }; From af137873866bce94a076001bde5a81a0a18eedb5 Mon Sep 17 00:00:00 2001 From: Brian Masney Date: Tue, 5 May 2026 20:49:00 -0400 Subject: [PATCH 42/53] clk: imx: scu: use clk_determine_rate_noop() Drop the driver-specific empty determine_rate() function and use the new shared clk_determine_rate_noop() helper. Acked-by: Abel Vesa Reviewed-by: Peng Fan Signed-off-by: Brian Masney --- drivers/clk/imx/clk-scu.c | 23 +++-------------------- 1 file changed, 3 insertions(+), 20 deletions(-) diff --git a/drivers/clk/imx/clk-scu.c b/drivers/clk/imx/clk-scu.c index 658b6d94de9b..44220e9c5c2a 100644 --- a/drivers/clk/imx/clk-scu.c +++ b/drivers/clk/imx/clk-scu.c @@ -262,23 +262,6 @@ static unsigned long clk_scu_recalc_rate(struct clk_hw *hw, return le32_to_cpu(msg.data.resp.rate); } -/* - * clk_scu_determine_rate - Returns the closest rate for a SCU clock - * @hw: clock to round rate for - * @req: clock rate request - * - * Returns 0 on success, a negative error on failure - */ -static int clk_scu_determine_rate(struct clk_hw *hw, - struct clk_rate_request *req) -{ - /* - * Assume we support all the requested rate and let the SCU firmware - * to handle the left work - */ - return 0; -} - static int clk_scu_atf_set_cpu_rate(struct clk_hw *hw, unsigned long rate, unsigned long parent_rate) { @@ -436,7 +419,7 @@ static void clk_scu_unprepare(struct clk_hw *hw) static const struct clk_ops clk_scu_ops = { .recalc_rate = clk_scu_recalc_rate, - .determine_rate = clk_scu_determine_rate, + .determine_rate = clk_determine_rate_noop, .set_rate = clk_scu_set_rate, .get_parent = clk_scu_get_parent, .set_parent = clk_scu_set_parent, @@ -446,7 +429,7 @@ static const struct clk_ops clk_scu_ops = { static const struct clk_ops clk_scu_cpu_ops = { .recalc_rate = clk_scu_recalc_rate, - .determine_rate = clk_scu_determine_rate, + .determine_rate = clk_determine_rate_noop, .set_rate = clk_scu_atf_set_cpu_rate, .prepare = clk_scu_prepare, .unprepare = clk_scu_unprepare, @@ -454,7 +437,7 @@ static const struct clk_ops clk_scu_cpu_ops = { static const struct clk_ops clk_scu_pi_ops = { .recalc_rate = clk_scu_recalc_rate, - .determine_rate = clk_scu_determine_rate, + .determine_rate = clk_determine_rate_noop, .set_rate = clk_scu_set_rate, }; From 42334774a5c85bdf8270f1b01e68578d27611426 Mon Sep 17 00:00:00 2001 From: Brian Masney Date: Tue, 5 May 2026 20:49:01 -0400 Subject: [PATCH 43/53] clk: qcom: rpm: use clk_determine_rate_noop() Drop the driver-specific empty determine_rate() function and use the new shared clk_determine_rate_noop() helper. Reviewed-by: Konrad Dybcio Signed-off-by: Brian Masney --- drivers/clk/qcom/clk-rpm.c | 15 ++------------- 1 file changed, 2 insertions(+), 13 deletions(-) diff --git a/drivers/clk/qcom/clk-rpm.c b/drivers/clk/qcom/clk-rpm.c index be0145631197..74f569b58017 100644 --- a/drivers/clk/qcom/clk-rpm.c +++ b/drivers/clk/qcom/clk-rpm.c @@ -351,17 +351,6 @@ static int clk_rpm_set_rate(struct clk_hw *hw, return 0; } -static int clk_rpm_determine_rate(struct clk_hw *hw, - struct clk_rate_request *req) -{ - /* - * RPM handles rate rounding and we don't have a way to - * know what the rate will be, so just return whatever - * rate is requested. - */ - return 0; -} - static unsigned long clk_rpm_recalc_rate(struct clk_hw *hw, unsigned long parent_rate) { @@ -383,7 +372,7 @@ static const struct clk_ops clk_rpm_xo_ops = { static const struct clk_ops clk_rpm_fixed_ops = { .prepare = clk_rpm_fixed_prepare, .unprepare = clk_rpm_fixed_unprepare, - .determine_rate = clk_rpm_determine_rate, + .determine_rate = clk_determine_rate_noop, .recalc_rate = clk_rpm_recalc_rate, }; @@ -391,7 +380,7 @@ static const struct clk_ops clk_rpm_ops = { .prepare = clk_rpm_prepare, .unprepare = clk_rpm_unprepare, .set_rate = clk_rpm_set_rate, - .determine_rate = clk_rpm_determine_rate, + .determine_rate = clk_determine_rate_noop, .recalc_rate = clk_rpm_recalc_rate, }; From bc88bae2529b26ea6e93639eacda39ae76e4f030 Mon Sep 17 00:00:00 2001 From: Brian Masney Date: Tue, 5 May 2026 20:49:02 -0400 Subject: [PATCH 44/53] clk: qcom: rpmh: use clk_determine_rate_noop() Drop the driver-specific empty determine_rate() function and use the new shared clk_determine_rate_noop() helper. Reviewed-by: Konrad Dybcio Signed-off-by: Brian Masney --- drivers/clk/qcom/clk-rpmh.c | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/drivers/clk/qcom/clk-rpmh.c b/drivers/clk/qcom/clk-rpmh.c index 6367b2a5a4e0..a224a94ae273 100644 --- a/drivers/clk/qcom/clk-rpmh.c +++ b/drivers/clk/qcom/clk-rpmh.c @@ -319,12 +319,6 @@ static int clk_rpmh_bcm_set_rate(struct clk_hw *hw, unsigned long rate, return 0; } -static int clk_rpmh_determine_rate(struct clk_hw *hw, - struct clk_rate_request *req) -{ - return 0; -} - static unsigned long clk_rpmh_bcm_recalc_rate(struct clk_hw *hw, unsigned long prate) { @@ -337,7 +331,7 @@ static const struct clk_ops clk_rpmh_bcm_ops = { .prepare = clk_rpmh_bcm_prepare, .unprepare = clk_rpmh_bcm_unprepare, .set_rate = clk_rpmh_bcm_set_rate, - .determine_rate = clk_rpmh_determine_rate, + .determine_rate = clk_determine_rate_noop, .recalc_rate = clk_rpmh_bcm_recalc_rate, }; From 972ff9a2510201a6752b865761a1a4355bcf0e3e Mon Sep 17 00:00:00 2001 From: Brian Masney Date: Tue, 5 May 2026 20:49:03 -0400 Subject: [PATCH 45/53] clk: qcom: smd-rpm: use clk_determine_rate_noop() Drop the driver-specific empty determine_rate() function and use the new shared clk_determine_rate_noop() helper. Reviewed-by: Konrad Dybcio Signed-off-by: Brian Masney --- drivers/clk/qcom/clk-smd-rpm.c | 13 +------------ 1 file changed, 1 insertion(+), 12 deletions(-) diff --git a/drivers/clk/qcom/clk-smd-rpm.c b/drivers/clk/qcom/clk-smd-rpm.c index 103db984a40b..f28cf9646cb9 100644 --- a/drivers/clk/qcom/clk-smd-rpm.c +++ b/drivers/clk/qcom/clk-smd-rpm.c @@ -370,17 +370,6 @@ static int clk_smd_rpm_set_rate(struct clk_hw *hw, unsigned long rate, return 0; } -static int clk_smd_rpm_determine_rate(struct clk_hw *hw, - struct clk_rate_request *req) -{ - /* - * RPM handles rate rounding and we don't have a way to - * know what the rate will be, so just return whatever - * rate is requested. - */ - return 0; -} - static unsigned long clk_smd_rpm_recalc_rate(struct clk_hw *hw, unsigned long parent_rate) { @@ -427,7 +416,7 @@ static const struct clk_ops clk_smd_rpm_ops = { .prepare = clk_smd_rpm_prepare, .unprepare = clk_smd_rpm_unprepare, .set_rate = clk_smd_rpm_set_rate, - .determine_rate = clk_smd_rpm_determine_rate, + .determine_rate = clk_determine_rate_noop, .recalc_rate = clk_smd_rpm_recalc_rate, }; From 917079a285e7085c1ec663683622607e31e8ebbb Mon Sep 17 00:00:00 2001 From: Brian Masney Date: Tue, 5 May 2026 20:49:04 -0400 Subject: [PATCH 46/53] clk: renesas: rzg2l-cpg: use clk_determine_rate_noop() Drop the driver-specific empty determine_rate() function and use the new shared clk_determine_rate_noop() helper. Reviewed-by: Geert Uytterhoeven Acked-by: Geert Uytterhoeven Signed-off-by: Brian Masney --- drivers/clk/renesas/rzg2l-cpg.c | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/drivers/clk/renesas/rzg2l-cpg.c b/drivers/clk/renesas/rzg2l-cpg.c index 51c9e19e1575..584680509a0a 100644 --- a/drivers/clk/renesas/rzg2l-cpg.c +++ b/drivers/clk/renesas/rzg2l-cpg.c @@ -945,12 +945,6 @@ static unsigned long rzg2l_cpg_sipll5_recalc_rate(struct clk_hw *hw, return pll5_rate; } -static int rzg2l_cpg_sipll5_determine_rate(struct clk_hw *hw, - struct clk_rate_request *req) -{ - return 0; -} - static int rzg2l_cpg_sipll5_set_rate(struct clk_hw *hw, unsigned long rate, unsigned long parent_rate) @@ -1022,7 +1016,7 @@ static int rzg2l_cpg_sipll5_set_rate(struct clk_hw *hw, static const struct clk_ops rzg2l_cpg_sipll5_ops = { .recalc_rate = rzg2l_cpg_sipll5_recalc_rate, - .determine_rate = rzg2l_cpg_sipll5_determine_rate, + .determine_rate = clk_determine_rate_noop, .set_rate = rzg2l_cpg_sipll5_set_rate, }; From df5c96aa570e769aaa0f283c8c8dc462a62341d0 Mon Sep 17 00:00:00 2001 From: Brian Masney Date: Tue, 5 May 2026 20:49:05 -0400 Subject: [PATCH 47/53] clk: rp1: use clk_determine_rate_noop() Drop the driver-specific empty determine_rate() function and use the new shared clk_determine_rate_noop() helper. Reviewed-by: Andrea della Porta Signed-off-by: Brian Masney --- drivers/clk/clk-rp1.c | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/drivers/clk/clk-rp1.c b/drivers/clk/clk-rp1.c index fd144755b879..0f7d9fd77481 100644 --- a/drivers/clk/clk-rp1.c +++ b/drivers/clk/clk-rp1.c @@ -1174,12 +1174,6 @@ static unsigned long rp1_varsrc_recalc_rate(struct clk_hw *hw, return clock->cached_rate; } -static int rp1_varsrc_determine_rate(struct clk_hw *hw, - struct clk_rate_request *req) -{ - return 0; -} - static const struct clk_ops rp1_pll_core_ops = { .is_prepared = rp1_pll_core_is_on, .prepare = rp1_pll_core_on, @@ -1227,7 +1221,7 @@ static const struct clk_ops rp1_clk_ops = { static const struct clk_ops rp1_varsrc_ops = { .set_rate = rp1_varsrc_set_rate, .recalc_rate = rp1_varsrc_recalc_rate, - .determine_rate = rp1_varsrc_determine_rate, + .determine_rate = clk_determine_rate_noop, }; static struct clk_hw *rp1_register_pll(struct rp1_clockman *clockman, From d0f1d7ed5fa97c364f3abd54a35478d6188b28dc Mon Sep 17 00:00:00 2001 From: Brian Masney Date: Tue, 5 May 2026 20:49:06 -0400 Subject: [PATCH 48/53] clk: samsung: acpm: use clk_determine_rate_noop() Drop the driver-specific empty determine_rate() function and use the new shared clk_determine_rate_noop() helper. Acked-by: Tudor Ambarus Signed-off-by: Brian Masney --- drivers/clk/samsung/clk-acpm.c | 14 +------------- 1 file changed, 1 insertion(+), 13 deletions(-) diff --git a/drivers/clk/samsung/clk-acpm.c b/drivers/clk/samsung/clk-acpm.c index 25cfa953ccac..7680bc8c5275 100644 --- a/drivers/clk/samsung/clk-acpm.c +++ b/drivers/clk/samsung/clk-acpm.c @@ -72,18 +72,6 @@ static unsigned long acpm_clk_recalc_rate(struct clk_hw *hw, clk->id); } -static int acpm_clk_determine_rate(struct clk_hw *hw, - struct clk_rate_request *req) -{ - /* - * We can't figure out what rate it will be, so just return the - * rate back to the caller. acpm_clk_recalc_rate() will be called - * after the rate is set and we'll know what rate the clock is - * running at then. - */ - return 0; -} - static int acpm_clk_set_rate(struct clk_hw *hw, unsigned long rate, unsigned long parent_rate) { @@ -95,7 +83,7 @@ static int acpm_clk_set_rate(struct clk_hw *hw, unsigned long rate, static const struct clk_ops acpm_clk_ops = { .recalc_rate = acpm_clk_recalc_rate, - .determine_rate = acpm_clk_determine_rate, + .determine_rate = clk_determine_rate_noop, .set_rate = acpm_clk_set_rate, }; From 6dae6cf18bc3e5e5b09ea46c9149f6031afeb7e9 Mon Sep 17 00:00:00 2001 From: Brian Masney Date: Tue, 5 May 2026 20:49:07 -0400 Subject: [PATCH 49/53] clk: scpi: use clk_determine_rate_noop() Drop the driver-specific empty determine_rate() function and use the new shared clk_determine_rate_noop() helper. Signed-off-by: Brian Masney --- drivers/clk/clk-scpi.c | 14 +------------- 1 file changed, 1 insertion(+), 13 deletions(-) diff --git a/drivers/clk/clk-scpi.c b/drivers/clk/clk-scpi.c index 24cee7c9fda6..2328d2abf6d8 100644 --- a/drivers/clk/clk-scpi.c +++ b/drivers/clk/clk-scpi.c @@ -32,18 +32,6 @@ static unsigned long scpi_clk_recalc_rate(struct clk_hw *hw, return clk->scpi_ops->clk_get_val(clk->id); } -static int scpi_clk_determine_rate(struct clk_hw *hw, - struct clk_rate_request *req) -{ - /* - * We can't figure out what rate it will be, so just return the - * rate back to the caller. scpi_clk_recalc_rate() will be called - * after the rate is set and we'll know what rate the clock is - * running at then. - */ - return 0; -} - static int scpi_clk_set_rate(struct clk_hw *hw, unsigned long rate, unsigned long parent_rate) { @@ -54,7 +42,7 @@ static int scpi_clk_set_rate(struct clk_hw *hw, unsigned long rate, static const struct clk_ops scpi_clk_ops = { .recalc_rate = scpi_clk_recalc_rate, - .determine_rate = scpi_clk_determine_rate, + .determine_rate = clk_determine_rate_noop, .set_rate = scpi_clk_set_rate, }; From f934c2c10e741bd96fcc2621dfdf98e932a83527 Mon Sep 17 00:00:00 2001 From: Brian Masney Date: Tue, 5 May 2026 20:49:08 -0400 Subject: [PATCH 50/53] clk: sprd: use clk_determine_rate_noop() Drop the driver-specific empty determine_rate() function and use the new shared clk_determine_rate_noop() helper. Signed-off-by: Brian Masney --- drivers/clk/sprd/pll.c | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/drivers/clk/sprd/pll.c b/drivers/clk/sprd/pll.c index bc2db19aec0e..4aec30777d8a 100644 --- a/drivers/clk/sprd/pll.c +++ b/drivers/clk/sprd/pll.c @@ -254,16 +254,10 @@ static int sprd_pll_clk_prepare(struct clk_hw *hw) return 0; } -static int sprd_pll_determine_rate(struct clk_hw *hw, - struct clk_rate_request *req) -{ - return 0; -} - const struct clk_ops sprd_pll_ops = { .prepare = sprd_pll_clk_prepare, .recalc_rate = sprd_pll_recalc_rate, - .determine_rate = sprd_pll_determine_rate, + .determine_rate = clk_determine_rate_noop, .set_rate = sprd_pll_set_rate, }; EXPORT_SYMBOL_GPL(sprd_pll_ops); From a621e3dfd4cb7b1eebae435ff6b976063e89e8b3 Mon Sep 17 00:00:00 2001 From: Brian Masney Date: Tue, 5 May 2026 20:49:09 -0400 Subject: [PATCH 51/53] phy: mediatek: phy-mtk-hdmi-mt2701: use clk_determine_rate_noop() Drop the driver-specific empty determine_rate() function and use the new shared clk_determine_rate_noop() helper. Acked-by: Vinod Koul Signed-off-by: Brian Masney --- drivers/phy/mediatek/phy-mtk-hdmi-mt2701.c | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/drivers/phy/mediatek/phy-mtk-hdmi-mt2701.c b/drivers/phy/mediatek/phy-mtk-hdmi-mt2701.c index b0b6497e7eed..c9a1f20e939b 100644 --- a/drivers/phy/mediatek/phy-mtk-hdmi-mt2701.c +++ b/drivers/phy/mediatek/phy-mtk-hdmi-mt2701.c @@ -90,12 +90,6 @@ static void mtk_hdmi_pll_unprepare(struct clk_hw *hw) usleep_range(80, 100); } -static int mtk_hdmi_pll_determine_rate(struct clk_hw *hw, - struct clk_rate_request *req) -{ - return 0; -} - static int mtk_hdmi_pll_set_rate(struct clk_hw *hw, unsigned long rate, unsigned long parent_rate) { @@ -170,7 +164,7 @@ static const struct clk_ops mtk_hdmi_phy_pll_ops = { .prepare = mtk_hdmi_pll_prepare, .unprepare = mtk_hdmi_pll_unprepare, .set_rate = mtk_hdmi_pll_set_rate, - .determine_rate = mtk_hdmi_pll_determine_rate, + .determine_rate = clk_determine_rate_noop, .recalc_rate = mtk_hdmi_pll_recalc_rate, }; From b37966e116cb0456e5804c485f7d0872d0c17449 Mon Sep 17 00:00:00 2001 From: Brian Masney Date: Tue, 5 May 2026 20:49:10 -0400 Subject: [PATCH 52/53] pmdomain: mediatek: airoha: use clk_determine_rate_noop() Drop the driver-specific empty determine_rate() function and use the new shared clk_determine_rate_noop() helper. Acked-by: Ulf Hansson Signed-off-by: Brian Masney --- drivers/pmdomain/mediatek/airoha-cpu-pmdomain.c | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/drivers/pmdomain/mediatek/airoha-cpu-pmdomain.c b/drivers/pmdomain/mediatek/airoha-cpu-pmdomain.c index 3b1d202f89dc..5efd7a38f4da 100644 --- a/drivers/pmdomain/mediatek/airoha-cpu-pmdomain.c +++ b/drivers/pmdomain/mediatek/airoha-cpu-pmdomain.c @@ -21,12 +21,6 @@ struct airoha_cpu_pmdomain_priv { struct generic_pm_domain pd; }; -static int airoha_cpu_pmdomain_clk_determine_rate(struct clk_hw *hw, - struct clk_rate_request *req) -{ - return 0; -} - static unsigned long airoha_cpu_pmdomain_clk_get(struct clk_hw *hw, unsigned long parent_rate) { @@ -48,7 +42,7 @@ static int airoha_cpu_pmdomain_clk_is_enabled(struct clk_hw *hw) static const struct clk_ops airoha_cpu_pmdomain_clk_ops = { .recalc_rate = airoha_cpu_pmdomain_clk_get, .is_enabled = airoha_cpu_pmdomain_clk_is_enabled, - .determine_rate = airoha_cpu_pmdomain_clk_determine_rate, + .determine_rate = clk_determine_rate_noop, }; static int airoha_cpu_pmdomain_set_performance_state(struct generic_pm_domain *domain, From e3a9ad35af677ecdff0aed12891f9360d1a493bd Mon Sep 17 00:00:00 2001 From: Brian Masney Date: Tue, 5 May 2026 20:49:11 -0400 Subject: [PATCH 53/53] pmdomain: mediatek: mtk-mfg: use clk_determine_rate_noop() Drop the driver-specific empty determine_rate() function and use the new shared clk_determine_rate_noop() helper. Acked-by: Ulf Hansson Signed-off-by: Brian Masney --- drivers/pmdomain/mediatek/mtk-mfg-pmdomain.c | 23 ++------------------ 1 file changed, 2 insertions(+), 21 deletions(-) diff --git a/drivers/pmdomain/mediatek/mtk-mfg-pmdomain.c b/drivers/pmdomain/mediatek/mtk-mfg-pmdomain.c index 53bdab66cf15..cdcd1d1a812f 100644 --- a/drivers/pmdomain/mediatek/mtk-mfg-pmdomain.c +++ b/drivers/pmdomain/mediatek/mtk-mfg-pmdomain.c @@ -310,25 +310,6 @@ static unsigned long mtk_mfg_recalc_rate_gpu(struct clk_hw *hw, return readl(mfg->shared_mem + GF_REG_FREQ_OUT_GPU) * HZ_PER_KHZ; } -static int mtk_mfg_determine_rate(struct clk_hw *hw, - struct clk_rate_request *req) -{ - /* - * The determine_rate callback needs to be implemented to avoid returning - * the current clock frequency, rather than something even remotely - * close to the frequency that was asked for. - * - * Instead of writing considerable amounts of possibly slow code just to - * somehow figure out which of the three PLLs to round for, or even to - * do a search through one of two OPP tables in order to find the closest - * OPP of a frequency, just return the rate as-is. This avoids devfreq - * "rounding" a request for the lowest frequency to the possibly very - * high current frequency, breaking the powersave governor in the process. - */ - - return 0; -} - static unsigned long mtk_mfg_recalc_rate_stack(struct clk_hw *hw, unsigned long parent_rate) { @@ -339,12 +320,12 @@ static unsigned long mtk_mfg_recalc_rate_stack(struct clk_hw *hw, static const struct clk_ops mtk_mfg_clk_gpu_ops = { .recalc_rate = mtk_mfg_recalc_rate_gpu, - .determine_rate = mtk_mfg_determine_rate, + .determine_rate = clk_determine_rate_noop, }; static const struct clk_ops mtk_mfg_clk_stack_ops = { .recalc_rate = mtk_mfg_recalc_rate_stack, - .determine_rate = mtk_mfg_determine_rate, + .determine_rate = clk_determine_rate_noop, }; static const struct clk_init_data mtk_mfg_clk_gpu_init = {