From e25d8d35e8cbc1a4c04a8b86eed6aa7229f6449e Mon Sep 17 00:00:00 2001 From: Myeonghun Pak Date: Tue, 23 Jun 2026 18:40:18 +0900 Subject: [PATCH 001/112] 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 002/112] 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 003/112] 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 004/112] 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 005/112] 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 006/112] 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 007/112] 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 008/112] 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 009/112] 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 010/112] 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 011/112] 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 012/112] 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 013/112] 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 014/112] 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 015/112] 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 016/112] 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 017/112] 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 018/112] 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 019/112] 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 020/112] 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 021/112] 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 022/112] 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 023/112] 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 024/112] 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 025/112] 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 026/112] 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 027/112] 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 028/112] 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 029/112] 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 030/112] 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 031/112] 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 032/112] 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 033/112] 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 034/112] 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 035/112] 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 036/112] 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 037/112] 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 038/112] 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 039/112] 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 040/112] 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 041/112] 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 042/112] 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 043/112] 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 044/112] 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 045/112] 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 046/112] 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 047/112] 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 048/112] 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 049/112] 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 050/112] 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 051/112] 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 052/112] 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 053/112] 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 = { From 935ad6242c47b37380d0cb7ec366516fe11855b4 Mon Sep 17 00:00:00 2001 From: Myeonghun Pak Date: Wed, 24 Jun 2026 15:23:55 +0900 Subject: [PATCH 054/112] clk: mediatek: mt6735: Unregister PLLs on probe failure mtk_clk_register_plls() registers the apmixedsys PLL clocks manually, while clk_mt6735_apmixed_remove() unregisters them on driver removal. If devm_of_clk_add_hw_provider() fails after the PLL registration succeeds, probe returns the error directly and the remove callback is not run. This leaves the registered PLL clocks behind on the probe failure path. Unregister the PLLs in that failure branch before returning the error. Fixes: 43c04ed79189 ("clk: mediatek: Add drivers for MediaTek MT6735 main clock and reset drivers") 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/mediatek/clk-mt6735-apmixedsys.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/drivers/clk/mediatek/clk-mt6735-apmixedsys.c b/drivers/clk/mediatek/clk-mt6735-apmixedsys.c index 9e30c089a209..b6eb6a581c31 100644 --- a/drivers/clk/mediatek/clk-mt6735-apmixedsys.c +++ b/drivers/clk/mediatek/clk-mt6735-apmixedsys.c @@ -102,9 +102,12 @@ static int clk_mt6735_apmixed_probe(struct platform_device *pdev) ret = devm_of_clk_add_hw_provider(&pdev->dev, of_clk_hw_onecell_get, clk_data); - if (ret) + if (ret) { dev_err(&pdev->dev, "Failed to register clock provider: %d\n", ret); + mtk_clk_unregister_plls(apmixedsys_plls, ARRAY_SIZE(apmixedsys_plls), + clk_data); + } return ret; } From 9475ebe129329817e5a1ea9e0c4f945bde993bb5 Mon Sep 17 00:00:00 2001 From: Herve Codina Date: Tue, 30 Jun 2026 12:30:50 +0200 Subject: [PATCH 055/112] clk: lan966x: Add MCHP_LAN966X_PCI dependency The lan966x clock controller depends on the LAN969x architecture or the LAN966x SoC. This clock controller can be used by the LAN966x PCI device and so it needs to be available when the LAN966x PCI device is enabled. Signed-off-by: Herve Codina Acked-by: Stephen Boyd Reviewed-by: Brian Masney Signed-off-by: Brian Masney --- drivers/clk/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/clk/Kconfig b/drivers/clk/Kconfig index 1717ce75a907..682840e67798 100644 --- a/drivers/clk/Kconfig +++ b/drivers/clk/Kconfig @@ -278,7 +278,7 @@ config COMMON_CLK_LAN966X tristate "Generic Clock Controller driver for LAN966X SoC" depends on HAS_IOMEM depends on OF - depends on SOC_LAN966 || ARCH_LAN969X || COMPILE_TEST + depends on SOC_LAN966 || ARCH_LAN969X || MCHP_LAN966X_PCI || COMPILE_TEST help This driver provides support for Generic Clock Controller(GCK) on LAN966X SoC. GCK generates and supplies clock to various peripherals From d1828369fafb01678761de5712e38dbf6cfa7e28 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pavel=20L=C3=B6bl?= Date: Wed, 1 Jul 2026 16:51:00 +0200 Subject: [PATCH 056/112] dt-bindings: clock: si544: add si549 compatible MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Adding Si549 compatible, which uses different internal oscillator frequency. Like in Si544 case, there are three types ending with latters a,b and c. Each with different maximum output frequency. Also fix datasheet links after Silicon Labs acquisition by Skyworks Solutions. Signed-off-by: Pavel Löbl Acked-by: Conor Dooley Signed-off-by: Brian Masney --- .../devicetree/bindings/clock/silabs,si544.yaml | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/Documentation/devicetree/bindings/clock/silabs,si544.yaml b/Documentation/devicetree/bindings/clock/silabs,si544.yaml index f87e71867108..218f5abdef26 100644 --- a/Documentation/devicetree/bindings/clock/silabs,si544.yaml +++ b/Documentation/devicetree/bindings/clock/silabs,si544.yaml @@ -4,7 +4,7 @@ $id: http://devicetree.org/schemas/clock/silabs,si544.yaml# $schema: http://devicetree.org/meta-schemas/core.yaml# -title: Silicon Labs SI514/SI544 clock generator +title: Silicon Labs SI514/SI544/SI549 clock generator maintainers: - Mike Looijmans @@ -13,8 +13,9 @@ description: > Silicon Labs 514/544 programmable I2C clock generator. Details about the device can be found in the datasheet: - https://www.silabs.com/Support%20Documents/TechnicalDocs/si514.pdf - https://www.silabs.com/documents/public/data-sheets/si544-datasheet.pdf + https://www.skyworksinc.com/-/media/Skyworks/SL/documents/public/data-sheets/Si514.pdf + https://www.skyworksinc.com/-/media/Skyworks/SL/documents/public/data-sheets/si544-datasheet.pdf + https://www.skyworksinc.com/-/media/Skyworks/SL/documents/public/data-sheets/si549-datasheet.pdf properties: compatible: @@ -23,6 +24,9 @@ properties: - silabs,si544a - silabs,si544b - silabs,si544c + - silabs,si549a + - silabs,si549b + - silabs,si549c reg: maxItems: 1 From bdba63b59897e4f79eafd09601fa76a14c680b88 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pavel=20L=C3=B6bl?= Date: Wed, 1 Jul 2026 16:51:01 +0200 Subject: [PATCH 057/112] clk: si544: add support for si549 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add support for Si549, which only differs from Si544 in internal oscilator frequency. Si549 also comes in three types A,B and C. Each having different maximum output frequency. This patch also fixes maximum frequency of Si544 C. Which should be 325MHz according to datasheet. Signed-off-by: Pavel Löbl Reviewed-by: Brian Masney Signed-off-by: Brian Masney --- drivers/clk/Kconfig | 6 +-- drivers/clk/clk-si544.c | 82 ++++++++++++++++++++++++++++++++--------- 2 files changed, 68 insertions(+), 20 deletions(-) diff --git a/drivers/clk/Kconfig b/drivers/clk/Kconfig index 682840e67798..b80c957b0040 100644 --- a/drivers/clk/Kconfig +++ b/drivers/clk/Kconfig @@ -155,12 +155,12 @@ config COMMON_CLK_SI514 generator. config COMMON_CLK_SI544 - tristate "Clock driver for SiLabs 544 devices" + tristate "Clock driver for SiLabs 544 and compatible devices" depends on I2C select REGMAP_I2C help - This driver supports the Silicon Labs 544 programmable clock - generator. + This driver supports the Silicon Labs 544/549 programmable clock + generators. config COMMON_CLK_SI570 tristate "Clock driver for SiLabs 570 and compatible devices" diff --git a/drivers/clk/clk-si544.c b/drivers/clk/clk-si544.c index 09c06ecec1a5..b13aeead9c02 100644 --- a/drivers/clk/clk-si544.c +++ b/drivers/clk/clk-si544.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0 /* - * Driver for Silicon Labs Si544 Programmable Oscillator + * Driver for Silicon Labs Si544/Si549 Programmable Oscillator * Copyright (C) 2018 Topic Embedded Products * Author: Mike Looijmans */ @@ -40,7 +40,9 @@ #define SI544_MIN_FREQ 200000U /* Si544 Internal oscillator runs at 55.05 MHz */ -#define FXO 55050000U +#define SI544_XO_FREQ 55050000U +/* Si549 Internal oscilator runs at 152.60 MHz */ +#define SI549_XO_FREQ 152600000U /* VCO range is 10.8 .. 12.1 GHz, max depends on speed grade */ #define FVCO_MIN 10800000000ULL @@ -56,11 +58,16 @@ #define DELTA_M_FRAC_NUM 19 #define DELTA_M_FRAC_DEN 20000 +struct si544_clk_desc { + unsigned long max_freq; + unsigned long xo_freq; +}; + struct clk_si544 { struct clk_hw hw; struct regmap *regmap; struct i2c_client *i2c_client; - unsigned long max_freq; + const struct si544_clk_desc *chip_info; }; #define to_clk_si544(_hw) container_of(_hw, struct clk_si544, hw) @@ -79,6 +86,7 @@ struct clk_si544_muldiv { u16 hs_div; u8 ls_div_bits; s32 delta_m; + u32 xo_freq; }; /* Enables or disables the output driver */ @@ -145,6 +153,8 @@ static int si544_get_muldiv(struct clk_si544 *data, settings->delta_m = reg[0] << 8 | reg[1] << 16 | reg[2] << 24; settings->delta_m >>= 8; + settings->xo_freq = data->chip_info->xo_freq; + return 0; } @@ -193,7 +203,7 @@ static bool is_valid_frequency(const struct clk_si544 *data, if (frequency < SI544_MIN_FREQ) return false; - return frequency <= data->max_freq; + return frequency <= data->chip_info->max_freq; } /* Calculate divider settings for a given frequency */ @@ -201,6 +211,7 @@ static int si544_calc_muldiv(struct clk_si544_muldiv *settings, unsigned long frequency) { u64 vco; + u32 fxo = settings->xo_freq; u32 ls_freq; u32 tmp; u8 res; @@ -238,13 +249,13 @@ static int si544_calc_muldiv(struct clk_si544_muldiv *settings, vco = (u64)ls_freq * settings->hs_div; /* Calculate the integer part of the feedback divider */ - tmp = do_div(vco, FXO); + tmp = do_div(vco, fxo); settings->fb_div_int = vco; /* And the fractional bits using the remainder */ vco = (u64)tmp << 32; - vco += FXO / 2; /* Round to nearest multiple */ - do_div(vco, FXO); + vco += fxo / 2; /* Round to nearest multiple */ + do_div(vco, fxo); settings->fb_div_frac = vco; /* Reset the frequency adjustment */ @@ -258,15 +269,16 @@ static unsigned long si544_calc_center_rate( const struct clk_si544_muldiv *settings) { u32 d = settings->hs_div * BIT(settings->ls_div_bits); + u32 fxo = settings->xo_freq; u64 vco; /* Calculate VCO from the fractional part */ - vco = (u64)settings->fb_div_frac * FXO; - vco += (FXO / 2); + vco = (u64)settings->fb_div_frac * fxo; + vco += (fxo / 2); vco >>= 32; /* Add the integer part of the VCO frequency */ - vco += (u64)settings->fb_div_int * FXO; + vco += (u64)settings->fb_div_int * fxo; /* Apply divider to obtain the generated frequency */ do_div(vco, d); @@ -446,7 +458,7 @@ static int si544_probe(struct i2c_client *client) init.num_parents = 0; data->hw.init = &init; data->i2c_client = client; - data->max_freq = (uintptr_t)i2c_get_match_data(client); + data->chip_info = i2c_get_match_data(client); if (of_property_read_string(client->dev.of_node, "clock-output-names", &init.name)) @@ -478,18 +490,54 @@ static int si544_probe(struct i2c_client *client) return 0; } +static const struct si544_clk_desc clk_si544a_info = { + .xo_freq = SI544_XO_FREQ, + .max_freq = 1500000000, +}; + +static const struct si544_clk_desc clk_si544b_info = { + .xo_freq = SI544_XO_FREQ, + .max_freq = 800000000, +}; + +static const struct si544_clk_desc clk_si544c_info = { + .xo_freq = SI544_XO_FREQ, + .max_freq = 325000000, +}; + +static const struct si544_clk_desc clk_si549a_info = { + .xo_freq = SI549_XO_FREQ, + .max_freq = 1500000000, +}; + +static const struct si544_clk_desc clk_si549b_info = { + .xo_freq = SI549_XO_FREQ, + .max_freq = 800000000, +}; + +static const struct si544_clk_desc clk_si549c_info = { + .xo_freq = SI549_XO_FREQ, + .max_freq = 325000000, +}; + static const struct i2c_device_id si544_id[] = { - { "si544a", 1500000000 }, - { "si544b", 800000000 }, - { "si544c", 350000000 }, + { "si544a", (kernel_ulong_t)&clk_si544a_info }, + { "si544b", (kernel_ulong_t)&clk_si544b_info }, + { "si544c", (kernel_ulong_t)&clk_si544c_info }, + { "si549a", (kernel_ulong_t)&clk_si549a_info }, + { "si549b", (kernel_ulong_t)&clk_si549b_info }, + { "si549c", (kernel_ulong_t)&clk_si549c_info }, { } }; MODULE_DEVICE_TABLE(i2c, si544_id); static const struct of_device_id clk_si544_of_match[] = { - { .compatible = "silabs,si544a", .data = (void *)1500000000 }, - { .compatible = "silabs,si544b", .data = (void *)800000000 }, - { .compatible = "silabs,si544c", .data = (void *)350000000 }, + { .compatible = "silabs,si544a", .data = &clk_si544a_info }, + { .compatible = "silabs,si544b", .data = &clk_si544b_info }, + { .compatible = "silabs,si544c", .data = &clk_si544c_info }, + { .compatible = "silabs,si549a", .data = &clk_si549a_info }, + { .compatible = "silabs,si549b", .data = &clk_si549b_info }, + { .compatible = "silabs,si549c", .data = &clk_si549c_info }, { } }; MODULE_DEVICE_TABLE(of, clk_si544_of_match); From 096ab64a0e69460bfb3557d09e17d2933280a1c6 Mon Sep 17 00:00:00 2001 From: Pengpeng Hou Date: Sat, 4 Jul 2026 20:44:50 +0800 Subject: [PATCH 058/112] clk: mediatek: mt6735-vdecsys: add missing MODULE_DEVICE_TABLE() The driver has an OF match table wired to .of_match_table, but does not export the table with MODULE_DEVICE_TABLE(). Add the missing MODULE_DEVICE_TABLE(of, ...) entry so module alias information is generated for OF based module autoloading. This is a source-level fix. It does not claim dynamic hardware reproduction; the evidence is the driver-owned match table, its use by the platform driver, and the missing module alias publication. Signed-off-by: Pengpeng Hou Reviewed-by: Brian Masney Signed-off-by: Brian Masney --- drivers/clk/mediatek/clk-mt6735-vdecsys.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/clk/mediatek/clk-mt6735-vdecsys.c b/drivers/clk/mediatek/clk-mt6735-vdecsys.c index 8817085fc1db..d53ebf92e878 100644 --- a/drivers/clk/mediatek/clk-mt6735-vdecsys.c +++ b/drivers/clk/mediatek/clk-mt6735-vdecsys.c @@ -63,6 +63,7 @@ static const struct of_device_id of_match_mt6735_vdecsys[] = { { .compatible = "mediatek,mt6735-vdecsys", .data = &vdecsys_clks }, { /* sentinel */ } }; +MODULE_DEVICE_TABLE(of, of_match_mt6735_vdecsys); static struct platform_driver clk_mt6735_vdecsys = { .probe = mtk_clk_simple_probe, From 36cf8d279ff90835f4b002dec28eedb059554842 Mon Sep 17 00:00:00 2001 From: Pengpeng Hou Date: Sat, 4 Jul 2026 20:48:08 +0800 Subject: [PATCH 059/112] clk: mediatek: mt6735-vencsys: add missing MODULE_DEVICE_TABLE() The driver has an OF match table wired to .of_match_table, but does not export the table with MODULE_DEVICE_TABLE(). Add the missing MODULE_DEVICE_TABLE(of, ...) entry so module alias information is generated for OF based module autoloading. This is a source-level fix. It does not claim dynamic hardware reproduction; the evidence is the driver-owned match table, its use by the platform driver, and the missing module alias publication. Signed-off-by: Pengpeng Hou Reviewed-by: Brian Masney Signed-off-by: Brian Masney --- drivers/clk/mediatek/clk-mt6735-vencsys.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/clk/mediatek/clk-mt6735-vencsys.c b/drivers/clk/mediatek/clk-mt6735-vencsys.c index 8dec7f98492a..64ba9da9fd42 100644 --- a/drivers/clk/mediatek/clk-mt6735-vencsys.c +++ b/drivers/clk/mediatek/clk-mt6735-vencsys.c @@ -37,6 +37,7 @@ static const struct of_device_id of_match_mt6735_vencsys[] = { { .compatible = "mediatek,mt6735-vencsys", .data = &vencsys_clks }, { /* sentinel */ } }; +MODULE_DEVICE_TABLE(of, of_match_mt6735_vencsys); static struct platform_driver clk_mt6735_vencsys = { .probe = mtk_clk_simple_probe, From 23454c4635b9f01af303118cbffd8cb3b3b57b6b Mon Sep 17 00:00:00 2001 From: Wolfram Sang Date: Sun, 5 Jul 2026 18:35:24 +0200 Subject: [PATCH 060/112] clk: mediatek: remove unneeded 'fast_io' parameter in regmap_config When using MMIO with regmap, fast_io is implied. No need to set it again. Signed-off-by: Wolfram Sang Reviewed-by: Brian Masney Reviewed-by: AngeloGioacchino Del Regno Signed-off-by: Brian Masney --- drivers/clk/mediatek/clk-mt8196-vlpckgen.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/clk/mediatek/clk-mt8196-vlpckgen.c b/drivers/clk/mediatek/clk-mt8196-vlpckgen.c index 7dcc164627c5..ee88dc4a231a 100644 --- a/drivers/clk/mediatek/clk-mt8196-vlpckgen.c +++ b/drivers/clk/mediatek/clk-mt8196-vlpckgen.c @@ -629,7 +629,6 @@ static const struct regmap_config vlpckgen_regmap_config = { .val_bits = 32, .reg_stride = 4, .max_register = 0x1000, - .fast_io = true, }; static int clk_mt8196_vlp_probe(struct platform_device *pdev) From 584dfca970a256fb4af0121017862a9259cc019e Mon Sep 17 00:00:00 2001 From: Christian Marangi Date: Wed, 8 Jul 2026 10:40:44 +0200 Subject: [PATCH 061/112] clk: en7523: reset PCIE HB on init for AN7581 It was reported that PCIE HB should be reset for AN7581 or some instability or link training issue will be present on warm boot scenario. Reset PCIE HB on clk HW init to handle warm boot problem with PCIe. Signed-off-by: Christian Marangi Reviewed-by: Brian Masney Signed-off-by: Brian Masney --- drivers/clk/clk-en7523.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/drivers/clk/clk-en7523.c b/drivers/clk/clk-en7523.c index 1ab0e2eca5d3..c450d89bfa96 100644 --- a/drivers/clk/clk-en7523.c +++ b/drivers/clk/clk-en7523.c @@ -57,6 +57,7 @@ #define REG_RST_CTRL2 0x830 #define REG_RST_CTRL1 0x834 +#define REG_PCIE_HB_RST BIT(29) #define EN751221_REG_RST_DMT 0x84 #define EN751221_REG_RST_USB 0xec @@ -853,6 +854,12 @@ static int en7581_clk_hw_init(struct platform_device *pdev, val = readl(base + REG_NP_SCU_PCIC); writel(val | 3, base + REG_NP_SCU_PCIC); + val = readl(base + REG_RST_CTRL1); + val |= REG_PCIE_HB_RST; + writel(val, base + REG_RST_CTRL1); + val &= ~REG_PCIE_HB_RST; + writel(val, base + REG_RST_CTRL1); + return en7581_reset_register(&pdev->dev, base, en7581_rst_map, ARRAY_SIZE(en7581_rst_map), en7581_rst_ofs); From f726279f5eab813f9a8b6f38ddf2a4b062d038ff Mon Sep 17 00:00:00 2001 From: Guangshuo Li Date: Wed, 8 Jul 2026 19:20:58 +0800 Subject: [PATCH 062/112] clk: tegra: tegra124-emc: put EMC node on register failure tegra124_clk_register_emc() stores a device node reference returned by of_parse_phandle() in tegra->emc_node. If clk_register() fails, the function returns an error before that reference can be consumed and released by the normal runtime path. The tegra_clk_emc object is freed on this failure path, but freeing the object does not drop the OF node reference stored in it. Drop the EMC node reference before freeing the tegra_clk_emc object. of_node_put() is safe for a NULL node, so this also covers the case where the phandle is absent. Fixes: 2db04f16b589 ("clk: tegra: Add EMC clock driver") Signed-off-by: Guangshuo Li Reviewed-by: Brian Masney Signed-off-by: Brian Masney --- drivers/clk/tegra/clk-tegra124-emc.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/clk/tegra/clk-tegra124-emc.c b/drivers/clk/tegra/clk-tegra124-emc.c index f3b2c96fdcfc..94ac24ea1e6a 100644 --- a/drivers/clk/tegra/clk-tegra124-emc.c +++ b/drivers/clk/tegra/clk-tegra124-emc.c @@ -537,6 +537,7 @@ struct clk *tegra124_clk_register_emc(void __iomem *base, struct device_node *np clk = clk_register(NULL, &tegra->hw); if (IS_ERR(clk)) { + of_node_put(tegra->emc_node); kfree(tegra); return clk; } From 540d91480bcb1b28a62d7023aa70947ea44c55b9 Mon Sep 17 00:00:00 2001 From: Louis-Alexis Eyraud Date: Thu, 9 Jul 2026 14:23:00 +0200 Subject: [PATCH 063/112] clk: mediatek: pllfh: Fix IO remapping leak in register_pllfhs error path When mtk_clk_register_pllfhs function fails to register a PLL, it unregisters all PLLs and cleans up itself in its error path before returning, so the function callers don't need to do it. But contrary to mtk_clk_unregister_pllfhs function, that does almost the same sequence, it does not free the IO memory mapped on fhctl node, leading to a leak. Fix this leak by factorizing the cleanup sequence in a new private function and use it both mtk_clk_register_pllfhs and mtk_clk_unregister_pllfhs functions. Also, change the loop index start value to avoid the -1 operation on index at each loop. Fixes: d7964de8a8ea ("clk: mediatek: Add new clock driver to handle FHCTL hardware") Signed-off-by: Louis-Alexis Eyraud Reviewed-by: Brian Masney Reviewed-by: AngeloGioacchino Del Regno Signed-off-by: Brian Masney --- drivers/clk/mediatek/clk-pllfh.c | 98 ++++++++++++++++---------------- 1 file changed, 49 insertions(+), 49 deletions(-) diff --git a/drivers/clk/mediatek/clk-pllfh.c b/drivers/clk/mediatek/clk-pllfh.c index aa95cd9197b3..6249fb87b1f5 100644 --- a/drivers/clk/mediatek/clk-pllfh.c +++ b/drivers/clk/mediatek/clk-pllfh.c @@ -197,12 +197,56 @@ static void mtk_clk_unregister_pllfh(struct clk_hw *hw) kfree(fh); } +static void mtk_clk_cleanup_pllfhs(void __iomem *iomem_base, + const struct mtk_pll_data *plls, int num_plls, + void __iomem *iomem_fhctl_base, + struct mtk_pllfh_data *pllfhs, int num_fhs, + struct clk_hw_onecell_data *clk_data) +{ + void __iomem *base = iomem_base; + void __iomem *fhctl_base = iomem_fhctl_base; + int i; + + for (i = num_plls - 1; i >= 0; i--) { + const struct mtk_pll_data *pll = &plls[i]; + struct mtk_pllfh_data *pllfh; + bool use_fhctl; + + if (IS_ERR_OR_NULL(clk_data->hws[pll->id])) + continue; + + pllfh = get_pllfh_by_id(pllfhs, num_fhs, pll->id); + use_fhctl = fhctl_is_supported_and_enabled(pllfh); + + if (!base) + base = mtk_clk_pll_get_base(clk_data->hws[pll->id], + pll); + + if (use_fhctl) { + if (!fhctl_base) + fhctl_base = pllfh->state.base; + mtk_clk_unregister_pllfh(clk_data->hws[pll->id]); + } else { + mtk_clk_unregister_pll(clk_data->hws[pll->id]); + } + + clk_data->hws[pll->id] = ERR_PTR(-ENOENT); + } + + if (fhctl_base) + iounmap(fhctl_base); + + if (base) + iounmap(base); +} + + int mtk_clk_register_pllfhs(struct device *dev, const struct mtk_pll_data *plls, int num_plls, struct mtk_pllfh_data *pllfhs, int num_fhs, struct clk_hw_onecell_data *clk_data) { - void __iomem *base; + void __iomem *base, *fhctl_base = NULL; int i; struct clk_hw *hw; @@ -238,24 +282,8 @@ int mtk_clk_register_pllfhs(struct device *dev, return 0; err: - while (--i >= 0) { - const struct mtk_pll_data *pll = &plls[i]; - struct mtk_pllfh_data *pllfh; - bool use_fhctl; - - pllfh = get_pllfh_by_id(pllfhs, num_fhs, pll->id); - use_fhctl = fhctl_is_supported_and_enabled(pllfh); - - if (use_fhctl) - mtk_clk_unregister_pllfh(clk_data->hws[pll->id]); - else - mtk_clk_unregister_pll(clk_data->hws[pll->id]); - - clk_data->hws[pll->id] = ERR_PTR(-ENOENT); - } - - iounmap(base); - + mtk_clk_cleanup_pllfhs(base, plls, i, fhctl_base, pllfhs, num_fhs, + clk_data); return PTR_ERR(hw); } EXPORT_SYMBOL_GPL(mtk_clk_register_pllfhs); @@ -264,38 +292,10 @@ void mtk_clk_unregister_pllfhs(const struct mtk_pll_data *plls, int num_plls, struct mtk_pllfh_data *pllfhs, int num_fhs, struct clk_hw_onecell_data *clk_data) { - void __iomem *base = NULL, *fhctl_base = NULL; - int i; - if (!clk_data) return; - for (i = num_plls; i > 0; i--) { - const struct mtk_pll_data *pll = &plls[i - 1]; - struct mtk_pllfh_data *pllfh; - bool use_fhctl; - - if (IS_ERR_OR_NULL(clk_data->hws[pll->id])) - continue; - - pllfh = get_pllfh_by_id(pllfhs, num_fhs, pll->id); - use_fhctl = fhctl_is_supported_and_enabled(pllfh); - - if (use_fhctl) { - fhctl_base = pllfh->state.base; - mtk_clk_unregister_pllfh(clk_data->hws[pll->id]); - } else { - base = mtk_clk_pll_get_base(clk_data->hws[pll->id], - pll); - mtk_clk_unregister_pll(clk_data->hws[pll->id]); - } - - clk_data->hws[pll->id] = ERR_PTR(-ENOENT); - } - - if (fhctl_base) - iounmap(fhctl_base); - - iounmap(base); + mtk_clk_cleanup_pllfhs(NULL, plls, num_plls, NULL, pllfhs, + num_fhs, clk_data); } EXPORT_SYMBOL_GPL(mtk_clk_unregister_pllfhs); From 011d8de504bc84402aabc1dda1cf0552fe9a5af2 Mon Sep 17 00:00:00 2001 From: Kostas Damaskinakis Date: Fri, 10 Jul 2026 17:15:20 +0300 Subject: [PATCH 064/112] clk: eswin: Zero-initialize stack-allocated clk_init_data eswin_clk_register_pll() and eswin_register_clkdiv() declare a struct clk_init_data on the stack and only initialize some of its fields (parent_data respectively parent_hws). clk_core_populate_parent_map() checks parent_names first and parent_data second before falling back to parent_hws, so leftover stack garbage in the uninitialized fields hijacks parent resolution and the clk core dereferences a bogus pointer: Unable to handle kernel NULL pointer dereference at virtual address 000000000000000c Oops [#1] epc : __clk_register+0x31a/0x7f0 [] __clk_register+0x31a/0x7f0 [] devm_clk_hw_register+0x2a/0x94 [] eswin_register_clkdiv+0x80/0xd0 [] eswin_clk_register_clks+0x162/0x1a0 [] eic7700_clk_probe+0x146/0x180 [] platform_probe+0x3c/0x7a Observed on EIC7700 hardware (with the driver backported to a 6.17 tree); whether the bug triggers depends entirely on what the stack happens to contain when the registration helpers run. Zero-initialize both structures. Fixes: cd44f127c1d4 ("clk: eswin: Add eic7700 clock driver") Signed-off-by: Kostas Damaskinakis Reviewed-by: Brian Masney Acked-by: Xuyang Dong Signed-off-by: Brian Masney --- drivers/clk/eswin/clk.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/clk/eswin/clk.c b/drivers/clk/eswin/clk.c index e09a52cc3587..79d1e4c5e637 100644 --- a/drivers/clk/eswin/clk.c +++ b/drivers/clk/eswin/clk.c @@ -204,7 +204,7 @@ int eswin_clk_register_pll(struct device *dev, struct eswin_pll_clock *clks, int nums, struct eswin_clock_data *data) { struct eswin_clk_pll *p_clk = NULL; - struct clk_init_data init; + struct clk_init_data init = {}; struct clk_hw *clk_hw; int i, ret; @@ -419,7 +419,7 @@ struct clk_hw *eswin_register_clkdiv(struct device *dev, unsigned int id, unsigned long priv_flag, spinlock_t *lock) { struct eswin_divider_clock *dclk; - struct clk_init_data init; + struct clk_init_data init = {}; struct clk_hw *clk_hw; int ret; From ccda84fcbf3a972973f772384935928f41817b3a Mon Sep 17 00:00:00 2001 From: Myeonghun Pak Date: Sat, 11 Jul 2026 23:17:42 +0900 Subject: [PATCH 065/112] clk: palmas: Manage external-control prepare with devm palmas_clks_init_configure() prepares the clock when an external control pin is configured. The current driver only drops that prepare reference when external control configuration fails. If provider registration fails after that point, or if the driver is later removed, the prepare reference remains held. Register a device-managed action after clk_prepare() succeeds. This balances the prepare reference on subsequent probe failure and driver removal. Fixes: 942d1d674931 ("clk: Add driver for Palmas clk32kg and clk32kgaudio clocks") 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-palmas.c | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/drivers/clk/clk-palmas.c b/drivers/clk/clk-palmas.c index 39049f62dbbb..86a51edac827 100644 --- a/drivers/clk/clk-palmas.c +++ b/drivers/clk/clk-palmas.c @@ -194,6 +194,13 @@ static void palmas_clks_get_clk_data(struct platform_device *pdev, cinfo->ext_control_pin = prop; } +static void palmas_clks_unprepare_ext_control(void *data) +{ + struct palmas_clock_info *cinfo = data; + + clk_unprepare(cinfo->hw.clk); +} + static int palmas_clks_init_configure(struct palmas_clock_info *cinfo) { int ret; @@ -214,13 +221,18 @@ static int palmas_clks_init_configure(struct palmas_clock_info *cinfo) return ret; } + ret = devm_add_action_or_reset(cinfo->dev, + palmas_clks_unprepare_ext_control, + cinfo); + if (ret) + return ret; + ret = palmas_ext_control_req_config(cinfo->palmas, cinfo->clk_desc->sleep_reqstr_id, cinfo->ext_control_pin, true); if (ret < 0) { dev_err(cinfo->dev, "Ext config for %s failed, %d\n", cinfo->clk_desc->clk_name, ret); - clk_unprepare(cinfo->hw.clk); return ret; } } From 10f9dc786a1b53b81073b1b155ef4815e3b7b6f7 Mon Sep 17 00:00:00 2001 From: Myeonghun Pak Date: Sat, 11 Jul 2026 23:17:43 +0900 Subject: [PATCH 066/112] clk: palmas: Use devm_of_clk_add_hw_provider() Use devm_of_clk_add_hw_provider() to register the clock provider so its lifetime is tied to the device. This removes the need for a remove callback that only unregisters the provider. 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-palmas.c | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/drivers/clk/clk-palmas.c b/drivers/clk/clk-palmas.c index 86a51edac827..fdbf8445ebc2 100644 --- a/drivers/clk/clk-palmas.c +++ b/drivers/clk/clk-palmas.c @@ -242,7 +242,6 @@ static int palmas_clks_init_configure(struct palmas_clock_info *cinfo) static int palmas_clks_probe(struct platform_device *pdev) { struct palmas *palmas = dev_get_drvdata(pdev->dev.parent); - struct device_node *node = pdev->dev.of_node; const struct palmas_clks_of_match_data *match_data; struct palmas_clock_info *cinfo; int ret; @@ -276,24 +275,19 @@ static int palmas_clks_probe(struct platform_device *pdev) return ret; } - ret = of_clk_add_hw_provider(node, of_clk_hw_simple_get, &cinfo->hw); + ret = devm_of_clk_add_hw_provider(&pdev->dev, of_clk_hw_simple_get, + &cinfo->hw); if (ret < 0) dev_err(&pdev->dev, "Fail to add clock driver, %d\n", ret); return ret; } -static void palmas_clks_remove(struct platform_device *pdev) -{ - of_clk_del_provider(pdev->dev.of_node); -} - static struct platform_driver palmas_clks_driver = { .driver = { .name = "palmas-clk", .of_match_table = palmas_clks_of_match, }, .probe = palmas_clks_probe, - .remove = palmas_clks_remove, }; module_platform_driver(palmas_clks_driver); From 0cad34fd953082a1bef863a0e446dc2559b8eed0 Mon Sep 17 00:00:00 2001 From: Chen Ni Date: Tue, 14 Jul 2026 13:25:25 +0800 Subject: [PATCH 067/112] clk: mmp: pxa1908-apbc: Remove unneeded semicolon MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Remove unnecessary semicolons reported by Coccinelle/coccicheck and the semantic patch at scripts/coccinelle/misc/semicolon.cocci. Signed-off-by: Chen Ni Reviewed-by: Brian Masney Acked-by: Duje Mihanović Signed-off-by: Brian Masney --- drivers/clk/mmp/clk-pxa1908-apbc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/clk/mmp/clk-pxa1908-apbc.c b/drivers/clk/mmp/clk-pxa1908-apbc.c index 438ece4f047d..16c1f13bca1f 100644 --- a/drivers/clk/mmp/clk-pxa1908-apbc.c +++ b/drivers/clk/mmp/clk-pxa1908-apbc.c @@ -114,7 +114,7 @@ static void pxa1908_clk_reset_init(struct device_node *np, cells[i].bits = BIT(2); cells[i].flags = 0; cells[i].lock = apbc_gate_clks[i].lock; - }; + } mmp_clk_reset_register(np, cells, nr_cells); } From 2a97ca25f7d5fc7c7a475ff2a92b3fd2cfc8faf1 Mon Sep 17 00:00:00 2001 From: Chen Ni Date: Tue, 14 Jul 2026 13:30:59 +0800 Subject: [PATCH 068/112] clk: mmp: pxa1908-apbcp: Remove unneeded semicolon MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Remove unnecessary semicolons reported by Coccinelle/coccicheck and the semantic patch at scripts/coccinelle/misc/semicolon.cocci. Signed-off-by: Chen Ni Reviewed-by: Brian Masney Acked-by: Duje Mihanović Signed-off-by: Brian Masney --- drivers/clk/mmp/clk-pxa1908-apbcp.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/clk/mmp/clk-pxa1908-apbcp.c b/drivers/clk/mmp/clk-pxa1908-apbcp.c index 1aa476103553..4e646e8b7440 100644 --- a/drivers/clk/mmp/clk-pxa1908-apbcp.c +++ b/drivers/clk/mmp/clk-pxa1908-apbcp.c @@ -61,7 +61,7 @@ static void pxa1908_clk_reset_init(struct device_node *np, cells[i].bits = BIT(2); cells[i].flags = 0; cells[i].lock = apbcp_gate_clks[i].lock; - }; + } mmp_clk_reset_register(np, cells, nr_cells); } From 18e9d14cbac33db1c1fb933c26a736eef53dd538 Mon Sep 17 00:00:00 2001 From: longlong yan Date: Tue, 14 Jul 2026 14:51:32 +0800 Subject: [PATCH 069/112] clk/x86: pmc_atom: add kasprintf return value check The kasprintf() function returns NULL on memory allocation failure, but the code in plt_clk_register() was not checking this return value. If kasprintf fails, init.name would be NULL and could cause NULL pointer dereference when clkdev_hw_create() uses it. Add proper error checking for the kasprintf() return value and return ERR_PTR(-ENOMEM) on failure. Fixes: 1141d9d08184 ("clk: x86: Add Atom PMC platform clocks") Signed-off-by: longlong yan Reviewed-by: Brian Masney Signed-off-by: Brian Masney --- drivers/clk/x86/clk-pmc-atom.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/clk/x86/clk-pmc-atom.c b/drivers/clk/x86/clk-pmc-atom.c index 99291ba65da7..08c83e0abc41 100644 --- a/drivers/clk/x86/clk-pmc-atom.c +++ b/drivers/clk/x86/clk-pmc-atom.c @@ -160,6 +160,9 @@ static struct clk_plt *plt_clk_register(struct platform_device *pdev, int id, return ERR_PTR(-ENOMEM); init.name = kasprintf(GFP_KERNEL, "%s_%d", PLT_CLK_NAME_BASE, id); + if (!init.name) + return ERR_PTR(-ENOMEM); + init.ops = &plt_clk_ops; init.flags = 0; init.parent_names = parent_names; From d3045184699d6220e5c03c2d6b1dfc449c3b873e Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Tue, 14 Jul 2026 18:50:18 +0300 Subject: [PATCH 070/112] clk: document that clk_get_parent() returns NULL The documentation in the clk.h file says that clk_get_parent() returns error pointers but it doesn't. It's also not consistent with the comments next to the clk_get_parent() implementation which say that it returns NULL when the clk is NULL. Update the comments so they are consistent and accurate and say that it returns NULL. Signed-off-by: Dan Carpenter Reviewed-by: Brian Masney Signed-off-by: Brian Masney --- include/linux/clk.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/include/linux/clk.h b/include/linux/clk.h index 998ba3f261da..993fd6e916c7 100644 --- a/include/linux/clk.h +++ b/include/linux/clk.h @@ -951,8 +951,8 @@ int clk_set_parent(struct clk *clk, struct clk *parent); * clk_get_parent - get the parent clock source for this clock * @clk: clock source * - * Returns struct clk corresponding to parent clock source, or - * valid IS_ERR() condition containing errno. + * Returns struct clk corresponding to parent clock source, or NULL + * if clk is NULL. */ struct clk *clk_get_parent(struct clk *clk); From 438623bb9350609069ac9fdb3d87b64beb3e9407 Mon Sep 17 00:00:00 2001 From: Rosen Penev Date: Sun, 19 Jul 2026 14:50:39 -0700 Subject: [PATCH 071/112] 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 745dfe874d5b9136090af69eba3f61170ceaa168 Mon Sep 17 00:00:00 2001 From: Rosen Penev Date: Sun, 19 Jul 2026 14:50:40 -0700 Subject: [PATCH 072/112] clk: stm32: allow STM32MP COMPILE_TEST builds COMMON_CLK_STM32MP already allows COMPILE_TEST, but the parent clock Makefile only descends into drivers/clk/stm32 for ARCH_STM32. Use the STM32MP clock symbol for that directory gate instead. Tested with: make LLVM=1 ARCH=loongarch drivers/clk/stm32/ Assisted-by: Codex:GPT-5.5 Signed-off-by: Rosen Penev Reviewed-by: Brian Masney Signed-off-by: Brian Masney --- drivers/clk/Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/clk/Makefile b/drivers/clk/Makefile index cc108a75a900..7c928f962d0b 100644 --- a/drivers/clk/Makefile +++ b/drivers/clk/Makefile @@ -151,7 +151,7 @@ obj-y += spacemit/ obj-$(CONFIG_PLAT_SPEAR) += spear/ obj-y += sprd/ obj-$(CONFIG_ARCH_STI) += st/ -obj-$(CONFIG_ARCH_STM32) += stm32/ +obj-$(CONFIG_COMMON_CLK_STM32MP) += stm32/ obj-y += starfive/ obj-$(CONFIG_ARCH_SUNXI) += sunxi/ obj-y += sunxi-ng/ From 3194c0f961a4d198e28d055b0be3f597b85012c9 Mon Sep 17 00:00:00 2001 From: Rosen Penev Date: Sun, 19 Jul 2026 14:54:56 -0700 Subject: [PATCH 073/112] clk: hisilicon: hi3660-stub: use devm_platform_ioremap_resource Replace the open-coded platform_get_resource() plus devm_ioremap() sequence with devm_platform_ioremap_resource(), which fetches the resource, requests the region and maps it in one call. Switch the error check to IS_ERR()/PTR_ERR() and drop the now-unused struct resource pointer. The only in-tree user (stub_clock@e896b500 in hi3660.dtsi) has a single 0x100-byte reg region, so the region reservation added by devm_ioremap_resource() is exclusive and does not introduce overlap failures. Built for ARM (drivers/clk/hisilicon/clk-hi3660-stub.o) with LLVM=1. Assisted-by: opencode:hy3-free Signed-off-by: Rosen Penev Reviewed-by: Brian Masney Signed-off-by: Brian Masney --- drivers/clk/hisilicon/clk-hi3660-stub.c | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/drivers/clk/hisilicon/clk-hi3660-stub.c b/drivers/clk/hisilicon/clk-hi3660-stub.c index 7c8b00ee6019..1b81f26670ed 100644 --- a/drivers/clk/hisilicon/clk-hi3660-stub.c +++ b/drivers/clk/hisilicon/clk-hi3660-stub.c @@ -124,7 +124,6 @@ static struct clk_hw *hi3660_stub_clk_hw_get(struct of_phandle_args *clkspec, static int hi3660_stub_clk_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; - struct resource *res; unsigned int i; int ret; @@ -139,12 +138,9 @@ static int hi3660_stub_clk_probe(struct platform_device *pdev) if (IS_ERR(stub_clk_chan.mbox)) return PTR_ERR(stub_clk_chan.mbox); - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (!res) - return -EINVAL; - freq_reg = devm_ioremap(dev, res->start, resource_size(res)); - if (!freq_reg) - return -ENOMEM; + freq_reg = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(freq_reg)) + return PTR_ERR(freq_reg); freq_reg += HI3660_STUB_CLOCK_DATA; From f600fbdb2093b18f2e21401ba7c6325e4d2b5b16 Mon Sep 17 00:00:00 2001 From: Jia Wang Date: Mon, 20 Jul 2026 08:38:04 +0800 Subject: [PATCH 074/112] clk: composite: Export devm composite pdata helper Allow modular clock drivers to use devm_clk_hw_register_composite_pdata() by exporting the helper. The non-devm composite helpers are already available to modules. Export this devm variant as well so users do not need to open-code devres cleanup. Signed-off-by: Jia Wang Reviewed-by: Brian Masney Signed-off-by: Brian Masney --- drivers/clk/clk-composite.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/clk/clk-composite.c b/drivers/clk/clk-composite.c index 835b1e4e5869..11842bce5918 100644 --- a/drivers/clk/clk-composite.c +++ b/drivers/clk/clk-composite.c @@ -462,3 +462,4 @@ struct clk_hw *devm_clk_hw_register_composite_pdata(struct device *dev, rate_hw, rate_ops, gate_hw, gate_ops, flags); } +EXPORT_SYMBOL_GPL(devm_clk_hw_register_composite_pdata); From e1d57ac7fe53dc52bb8aab9db59008c7fec8d863 Mon Sep 17 00:00:00 2001 From: Jia Wang Date: Mon, 20 Jul 2026 08:38:05 +0800 Subject: [PATCH 075/112] dt-bindings: clock: ultrarisc: Add DP1000 Clock Controller Add doc for the clock controller on the UltraRISC DP1000 RISC-V SoC. Signed-off-by: Jia Wang Reviewed-by: Conor Dooley Signed-off-by: Brian Masney --- .../bindings/clock/ultrarisc,dp1000-clk.yaml | 60 +++++++++++++++++++ MAINTAINERS | 7 +++ .../dt-bindings/clock/ultrarisc,dp1000-clk.h | 27 +++++++++ 3 files changed, 94 insertions(+) create mode 100644 Documentation/devicetree/bindings/clock/ultrarisc,dp1000-clk.yaml create mode 100644 include/dt-bindings/clock/ultrarisc,dp1000-clk.h diff --git a/Documentation/devicetree/bindings/clock/ultrarisc,dp1000-clk.yaml b/Documentation/devicetree/bindings/clock/ultrarisc,dp1000-clk.yaml new file mode 100644 index 000000000000..ede565ec440c --- /dev/null +++ b/Documentation/devicetree/bindings/clock/ultrarisc,dp1000-clk.yaml @@ -0,0 +1,60 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/clock/ultrarisc,dp1000-clk.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: UltraRISC DP1000 Clock Controller + +maintainers: + - Jia Wang + +description: | + The UltraRISC DP1000 clock controller is driven from a single external + oscillator input. It provides a system PLL with fractional multiplier + and post-divider stages, several fixed-ratio derived clocks for + the on-chip subsystem, Clock Configuration Register (CCR) divider + outputs for GMAC and the UART, I2C, and SPI root clocks, and + per-instance gate clocks for UART0-3, I2C0-3, and SPI0-1. + + All available clocks are defined as preprocessor macros in + include/dt-bindings/clock/ultrarisc,dp1000-clk.h + +properties: + compatible: + const: ultrarisc,dp1000-clk + + reg: + maxItems: 1 + + clocks: + maxItems: 1 + description: + External oscillator input clock used as the parent of the PLLs. + + "#clock-cells": + const: 1 + +required: + - compatible + - reg + - clocks + - "#clock-cells" + +additionalProperties: false + +examples: + - | + #include + + soc { + #address-cells = <2>; + #size-cells = <2>; + + clock-controller@11080000 { + compatible = "ultrarisc,dp1000-clk"; + reg = <0x0 0x11080000 0x0 0x1000>; + clocks = <&osc>; + #clock-cells = <1>; + }; + }; diff --git a/MAINTAINERS b/MAINTAINERS index 15011f5752a9..ce93b28497e6 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -27669,6 +27669,13 @@ S: Maintained F: drivers/usb/common/ulpi.c F: include/linux/ulpi/ +ULTRARISC DP1000 CLOCK DRIVER +M: Jia Wang +L: linux-clk@vger.kernel.org +S: Maintained +F: Documentation/devicetree/bindings/clock/ultrarisc,dp1000-clk.yaml +F: include/dt-bindings/clock/ultrarisc,dp1000-clk.h + ULTRARISC DP1000 PINCTRL DRIVER M: Jia Wang L: linux-gpio@vger.kernel.org diff --git a/include/dt-bindings/clock/ultrarisc,dp1000-clk.h b/include/dt-bindings/clock/ultrarisc,dp1000-clk.h new file mode 100644 index 000000000000..751125f99965 --- /dev/null +++ b/include/dt-bindings/clock/ultrarisc,dp1000-clk.h @@ -0,0 +1,27 @@ +/* SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) */ +#ifndef _DT_BINDINGS_CLOCK_ULTRARISC_DP1000_CLK_H +#define _DT_BINDINGS_CLOCK_ULTRARISC_DP1000_CLK_H + +#define DP1000_CLK_SYSPLL 0 +#define DP1000_CLK_SYSPLL_DIV2 1 +#define DP1000_CLK_SUBSYS 2 +#define DP1000_CLK_GMAC 3 +#define DP1000_CLK_UART_ROOT 4 +#define DP1000_CLK_I2C_ROOT 5 +#define DP1000_CLK_SPI_ROOT 6 +#define DP1000_CLK_PCIE_DBI 7 +#define DP1000_CLK_PCIEX4_CORE 8 +#define DP1000_CLK_PCIEX16_CORE 9 +#define DP1000_CLK_PCIE_AUX 10 +#define DP1000_CLK_UART0 11 +#define DP1000_CLK_UART1 12 +#define DP1000_CLK_UART2 13 +#define DP1000_CLK_UART3 14 +#define DP1000_CLK_I2C0 15 +#define DP1000_CLK_I2C1 16 +#define DP1000_CLK_I2C2 17 +#define DP1000_CLK_I2C3 18 +#define DP1000_CLK_SPI0 19 +#define DP1000_CLK_SPI1 20 + +#endif /* _DT_BINDINGS_CLOCK_ULTRARISC_DP1000_CLK_H */ From 3948a36be42b67f43f1d3f113dad4c8b347efb9b Mon Sep 17 00:00:00 2001 From: Jia Wang Date: Mon, 20 Jul 2026 08:38:06 +0800 Subject: [PATCH 076/112] clk: ultrarisc: Add DP1000 clock driver Add a clock driver for the UltraRISC DP1000 SoC. The clock tree is driven by a SYSPLL and provides fixed-factor clocks for the subsystem and PCIe, divider-based root clocks for GMAC and the UART, I2C, and SPI blocks, and per-instance gate clocks for UART0-3, I2C0-3, and SPI0-1. Signed-off-by: Jia Wang Reviewed-by: Brian Masney Signed-off-by: Brian Masney --- MAINTAINERS | 1 + drivers/clk/Kconfig | 1 + drivers/clk/Makefile | 1 + drivers/clk/ultrarisc/Kconfig | 18 ++ drivers/clk/ultrarisc/Makefile | 4 + drivers/clk/ultrarisc/clk-dp1000.c | 154 ++++++++++ drivers/clk/ultrarisc/clk-ultrarisc.c | 399 ++++++++++++++++++++++++++ drivers/clk/ultrarisc/clk-ultrarisc.h | 71 +++++ 8 files changed, 649 insertions(+) create mode 100644 drivers/clk/ultrarisc/Kconfig create mode 100644 drivers/clk/ultrarisc/Makefile create mode 100644 drivers/clk/ultrarisc/clk-dp1000.c create mode 100644 drivers/clk/ultrarisc/clk-ultrarisc.c create mode 100644 drivers/clk/ultrarisc/clk-ultrarisc.h diff --git a/MAINTAINERS b/MAINTAINERS index ce93b28497e6..4d21a2dca3fc 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -27674,6 +27674,7 @@ M: Jia Wang L: linux-clk@vger.kernel.org S: Maintained F: Documentation/devicetree/bindings/clock/ultrarisc,dp1000-clk.yaml +F: drivers/clk/ultrarisc/* F: include/dt-bindings/clock/ultrarisc,dp1000-clk.h ULTRARISC DP1000 PINCTRL DRIVER diff --git a/drivers/clk/Kconfig b/drivers/clk/Kconfig index b80c957b0040..f83dda6ffc88 100644 --- a/drivers/clk/Kconfig +++ b/drivers/clk/Kconfig @@ -541,6 +541,7 @@ source "drivers/clk/tenstorrent/Kconfig" source "drivers/clk/thead/Kconfig" source "drivers/clk/stm32/Kconfig" source "drivers/clk/ti/Kconfig" +source "drivers/clk/ultrarisc/Kconfig" source "drivers/clk/uniphier/Kconfig" source "drivers/clk/visconti/Kconfig" source "drivers/clk/x86/Kconfig" diff --git a/drivers/clk/Makefile b/drivers/clk/Makefile index 7c928f962d0b..dc56d0249450 100644 --- a/drivers/clk/Makefile +++ b/drivers/clk/Makefile @@ -159,6 +159,7 @@ obj-$(CONFIG_ARCH_TEGRA) += tegra/ obj-y += tenstorrent/ obj-$(CONFIG_ARCH_THEAD) += thead/ obj-y += ti/ +obj-y += ultrarisc/ obj-$(CONFIG_CLK_UNIPHIER) += uniphier/ obj-$(CONFIG_ARCH_U8500) += ux500/ obj-y += versatile/ diff --git a/drivers/clk/ultrarisc/Kconfig b/drivers/clk/ultrarisc/Kconfig new file mode 100644 index 000000000000..2eecc6ac3119 --- /dev/null +++ b/drivers/clk/ultrarisc/Kconfig @@ -0,0 +1,18 @@ +# SPDX-License-Identifier: GPL-2.0-only + +config CLK_ULTRARISC + tristate + depends on OF + depends on ARCH_ULTRARISC || COMPILE_TEST + +config CLK_ULTRARISC_DP1000 + tristate "UltraRISC DP1000 clock controller" + select CLK_ULTRARISC + depends on OF && HAS_IOMEM + depends on ARCH_ULTRARISC || COMPILE_TEST + default ARCH_ULTRARISC + help + This driver provides the clock controller for the UltraRISC + DP1000 SoC. It exposes the PLL output, derived fixed-factor + clocks, programmable divider clocks, and peripheral gate + clocks to Linux consumers. diff --git a/drivers/clk/ultrarisc/Makefile b/drivers/clk/ultrarisc/Makefile new file mode 100644 index 000000000000..b013708c9444 --- /dev/null +++ b/drivers/clk/ultrarisc/Makefile @@ -0,0 +1,4 @@ +# SPDX-License-Identifier: GPL-2.0-only + +obj-$(CONFIG_CLK_ULTRARISC) += clk-ultrarisc.o +obj-$(CONFIG_CLK_ULTRARISC_DP1000) += clk-dp1000.o diff --git a/drivers/clk/ultrarisc/clk-dp1000.c b/drivers/clk/ultrarisc/clk-dp1000.c new file mode 100644 index 000000000000..ffea77c4cca0 --- /dev/null +++ b/drivers/clk/ultrarisc/clk-dp1000.c @@ -0,0 +1,154 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (C) 2026 UltraRISC Technology (Shanghai) Co., Ltd. + */ + +#include + +#include + +#include "clk-ultrarisc.h" + +#define DP1000_PLL_CFG1_OFFSET 0x400 +#define DP1000_PLL_CFG2_OFFSET 0x404 + +#define DP1000_CCR_UART_OFFSET 0x220 +#define DP1000_CCR_I2C_OFFSET 0x224 +#define DP1000_CCR_GMAC_OFFSET 0x228 +#define DP1000_CCR_SPI_OFFSET 0x22c +#define DP1000_PERI_CLKENA_OFFSET 0x270 + +#define DP1000_CCR_LOAD BIT(16) + +#define DP1000_PERI_MAX_RATE 62500000UL +#define DP1000_CLK_NUM 21 + +static const struct ultrarisc_pll_layout dp1000_pll_layout = { + .cfg1_offset = DP1000_PLL_CFG1_OFFSET, + .cfg2_offset = DP1000_PLL_CFG2_OFFSET, + .frac_mask = GENMASK(23, 0), + .m_mask = GENMASK(23, 16), + .n_mask = GENMASK(11, 6), + .oddiv1_mask = GENMASK(1, 0), + .oddiv2_mask = GENMASK(4, 3), +}; + +static const struct ultrarisc_pll_desc dp1000_plls[] = { + { + .id = DP1000_CLK_SYSPLL, + .name = "syspll_clk", + }, +}; + +#define DP1000_FIXED_FACTOR(_id, _name, _parent, _mult, _div) \ + { \ + .id = (_id), \ + .name = (_name), \ + .parent_id = (_parent), \ + .mult = (_mult), \ + .div = (_div), \ + } + +#define DP1000_DIV(_id, _name, _offset, _parent, _max_rate) \ + { \ + .id = (_id), \ + .name = (_name), \ + .offset = (_offset), \ + .parent_id = (_parent), \ + .max_rate = (_max_rate), \ + .load_mask = DP1000_CCR_LOAD, \ + .div_shift = 8, \ + .div_width = 4, \ + .gate_bit = 0, \ + .divider_flags = CLK_DIVIDER_ONE_BASED, \ + .gate_flags = 0, \ + } + +#define DP1000_GATE(_id, _name, _parent, _bit) \ + { \ + .id = (_id), \ + .name = (_name), \ + .offset = DP1000_PERI_CLKENA_OFFSET, \ + .parent_id = (_parent), \ + .gate_bit = (_bit), \ + .gate_flags = 0, \ + } + +static const struct ultrarisc_fixed_factor_desc dp1000_fixed_factor_clks[] = { + DP1000_FIXED_FACTOR(DP1000_CLK_SYSPLL_DIV2, "syspll_div2_clk", + DP1000_CLK_SYSPLL, 1, 2), + DP1000_FIXED_FACTOR(DP1000_CLK_SUBSYS, "subsys_clk", + DP1000_CLK_SYSPLL_DIV2, 1, 2), + DP1000_FIXED_FACTOR(DP1000_CLK_PCIE_DBI, "pcie_dbi_clk", + DP1000_CLK_SYSPLL, 1, 10), + DP1000_FIXED_FACTOR(DP1000_CLK_PCIEX4_CORE, "pciex4_core_clk", + DP1000_CLK_SYSPLL, 1, 2), + DP1000_FIXED_FACTOR(DP1000_CLK_PCIEX16_CORE, "pciex16_core_clk", + DP1000_CLK_SYSPLL, 1, 1), + DP1000_FIXED_FACTOR(DP1000_CLK_PCIE_AUX, "pcie_aux_clk", + DP1000_CLK_SYSPLL, 1, 40), +}; + +static const struct ultrarisc_divider_desc dp1000_divider_clks[] = { + DP1000_DIV(DP1000_CLK_GMAC, "gmac_clk", DP1000_CCR_GMAC_OFFSET, + DP1000_CLK_SYSPLL_DIV2, 0), + DP1000_DIV(DP1000_CLK_UART_ROOT, "uart_root_clk", + DP1000_CCR_UART_OFFSET, DP1000_CLK_SUBSYS, + DP1000_PERI_MAX_RATE), + DP1000_DIV(DP1000_CLK_I2C_ROOT, "i2c_root_clk", + DP1000_CCR_I2C_OFFSET, DP1000_CLK_SUBSYS, + DP1000_PERI_MAX_RATE), + DP1000_DIV(DP1000_CLK_SPI_ROOT, "spi_root_clk", + DP1000_CCR_SPI_OFFSET, DP1000_CLK_SUBSYS, + DP1000_PERI_MAX_RATE), +}; + +static const struct ultrarisc_gate_desc dp1000_gate_clks[] = { + DP1000_GATE(DP1000_CLK_UART0, "uart0_clk", DP1000_CLK_UART_ROOT, 0), + DP1000_GATE(DP1000_CLK_UART1, "uart1_clk", DP1000_CLK_UART_ROOT, 1), + DP1000_GATE(DP1000_CLK_UART2, "uart2_clk", DP1000_CLK_UART_ROOT, 2), + DP1000_GATE(DP1000_CLK_UART3, "uart3_clk", DP1000_CLK_UART_ROOT, 3), + DP1000_GATE(DP1000_CLK_I2C0, "i2c0_clk", DP1000_CLK_I2C_ROOT, 4), + DP1000_GATE(DP1000_CLK_I2C1, "i2c1_clk", DP1000_CLK_I2C_ROOT, 5), + DP1000_GATE(DP1000_CLK_I2C2, "i2c2_clk", DP1000_CLK_I2C_ROOT, 6), + DP1000_GATE(DP1000_CLK_I2C3, "i2c3_clk", DP1000_CLK_I2C_ROOT, 7), + DP1000_GATE(DP1000_CLK_SPI0, "spi0_clk", DP1000_CLK_SPI_ROOT, 8), + DP1000_GATE(DP1000_CLK_SPI1, "spi1_clk", DP1000_CLK_SPI_ROOT, 9), +}; + +static const struct ultrarisc_clk_soc_data dp1000_clk_soc_data = { + .num_clks = DP1000_CLK_NUM, + .pll_layout = &dp1000_pll_layout, + .plls = dp1000_plls, + .num_plls = ARRAY_SIZE(dp1000_plls), + .fixed_factors = dp1000_fixed_factor_clks, + .num_fixed_factors = ARRAY_SIZE(dp1000_fixed_factor_clks), + .dividers = dp1000_divider_clks, + .num_dividers = ARRAY_SIZE(dp1000_divider_clks), + .gates = dp1000_gate_clks, + .num_gates = ARRAY_SIZE(dp1000_gate_clks), +}; + +static int dp1000_clk_probe(struct platform_device *pdev) +{ + return ultrarisc_clk_probe(pdev, &dp1000_clk_soc_data); +} + +static const struct of_device_id dp1000_clk_of_match[] = { + { .compatible = "ultrarisc,dp1000-clk" }, + { } +}; +MODULE_DEVICE_TABLE(of, dp1000_clk_of_match); + +static struct platform_driver dp1000_clk_driver = { + .probe = dp1000_clk_probe, + .driver = { + .name = "ultrarisc-dp1000-clk", + .of_match_table = dp1000_clk_of_match, + }, +}; +module_platform_driver(dp1000_clk_driver); + +MODULE_IMPORT_NS("CLK_ULTRARISC"); +MODULE_DESCRIPTION("UltraRISC DP1000 clock controller"); +MODULE_LICENSE("GPL"); diff --git a/drivers/clk/ultrarisc/clk-ultrarisc.c b/drivers/clk/ultrarisc/clk-ultrarisc.c new file mode 100644 index 000000000000..bbcd2234ea71 --- /dev/null +++ b/drivers/clk/ultrarisc/clk-ultrarisc.c @@ -0,0 +1,399 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (C) 2026 UltraRISC Technology (Shanghai) Co., Ltd. + */ + +#include +#include +#include +#include +#include +#include + +#include "clk-ultrarisc.h" + +struct ultrarisc_pll_clk { + struct clk_hw hw; + void __iomem *base; + const struct ultrarisc_pll_layout *layout; +}; + +struct ultrarisc_divider_clk { + struct clk_divider divider; + struct clk_gate gate; + u32 load_mask; +}; + +#define to_ultrarisc_pll_clk(_hw) \ + container_of(_hw, struct ultrarisc_pll_clk, hw) + +static inline struct ultrarisc_divider_clk *to_ultrarisc_divider_clk(struct clk_hw *hw) +{ + struct clk_divider *divider = to_clk_divider(hw); + + return container_of(divider, struct ultrarisc_divider_clk, divider); +} + +static unsigned long ultrarisc_pll_recalc_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + struct ultrarisc_pll_clk *pll = to_ultrarisc_pll_clk(hw); + const struct ultrarisc_pll_layout *layout = pll->layout; + u32 oddiv1_div, oddiv2_div; + u64 mult, rate, den; + u32 frac, m, n; + u32 cfg1, cfg2; + + cfg1 = readl_relaxed(pll->base + layout->cfg1_offset); + cfg2 = readl_relaxed(pll->base + layout->cfg2_offset); + + frac = field_get(layout->frac_mask, cfg1); + m = field_get(layout->m_mask, cfg2); + n = field_get(layout->n_mask, cfg2); + if (!n) + return 0; + + oddiv1_div = 1U << field_get(layout->oddiv1_mask, cfg2); + oddiv2_div = 1U << field_get(layout->oddiv2_mask, cfg2); + + /* + * The output frequency is calculated as: + * fvco = parent * (m + frac / 2^24) / n + * fout = fvco / (2^oddiv1_raw * 2^oddiv2_raw) + * + * The output divider values are derived from the raw register field values as: + * oddivX_div = 1 << oddivX_raw + */ + mult = ((u64)m << 24) + frac; + rate = (u64)parent_rate * mult; + den = ((u64)n << 24) * oddiv1_div * oddiv2_div; + + return div64_u64(rate + (den >> 1), den); +} + +static const struct clk_ops ultrarisc_pll_ro_ops = { + .recalc_rate = ultrarisc_pll_recalc_rate, +}; + +static unsigned long ultrarisc_divider_recalc_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + struct clk_divider *divider = to_clk_divider(hw); + u32 val; + + val = readl_relaxed(divider->reg) >> divider->shift; + val &= clk_div_mask(divider->width); + + return divider_recalc_rate(hw, parent_rate, val, divider->table, + divider->flags, divider->width); +} + +static int ultrarisc_divider_determine_rate(struct clk_hw *hw, + struct clk_rate_request *req) +{ + struct clk_divider *divider = to_clk_divider(hw); + + return divider_determine_rate(hw, req, divider->table, divider->width, + divider->flags); +} + +static int ultrarisc_divider_set_rate(struct clk_hw *hw, unsigned long rate, + unsigned long parent_rate) +{ + struct ultrarisc_divider_clk *divider_clk = to_ultrarisc_divider_clk(hw); + struct clk_divider *divider = ÷r_clk->divider; + int value; + u32 val; + + value = divider_get_val(rate, parent_rate, divider->table, + divider->width, divider->flags); + if (value < 0) + return value; + + scoped_guard(spinlock_irqsave, divider->lock) { + val = readl_relaxed(divider->reg); + val &= ~(clk_div_mask(divider->width) << divider->shift); + val |= value << divider->shift; + writel_relaxed(val, divider->reg); + + if (divider_clk->load_mask) { + /* + * Program the new divider field, then write 1 to the + * load bit to trigger the update. The load bit is + * write-triggered and reads back as 0 on this hardware. + */ + writel_relaxed(val | divider_clk->load_mask, divider->reg); + } + } + + return 0; +} + +static const struct clk_ops ultrarisc_divider_ops = { + .recalc_rate = ultrarisc_divider_recalc_rate, + .determine_rate = ultrarisc_divider_determine_rate, + .set_rate = ultrarisc_divider_set_rate, +}; + +static struct clk_hw *ultrarisc_clk_register_pll(struct device *dev, + const struct ultrarisc_pll_desc *desc, + const struct ultrarisc_pll_layout *layout, + void __iomem *base) +{ + struct clk_parent_data pdata = { .index = 0 }; + struct ultrarisc_pll_clk *pll; + struct clk_init_data init = { + .name = desc->name, + .ops = &ultrarisc_pll_ro_ops, + .parent_data = &pdata, + .num_parents = 1, + .flags = CLK_GET_RATE_NOCACHE, + }; + int ret; + + pll = devm_kzalloc(dev, sizeof(*pll), GFP_KERNEL); + if (!pll) + return ERR_PTR(-ENOMEM); + + pll->base = base; + pll->layout = layout; + pll->hw.init = &init; + + ret = devm_clk_hw_register(dev, &pll->hw); + if (ret) + return ERR_PTR(ret); + + return &pll->hw; +} + +static struct clk_hw * +ultrarisc_clk_register_divider(struct device *dev, + const struct ultrarisc_divider_desc *desc, + struct clk_hw *parent_hw, void __iomem *base, + spinlock_t *lock) +{ + const struct clk_parent_data pdata = { .hw = parent_hw }; + void __iomem *reg = base + desc->offset; + struct ultrarisc_divider_clk *divider; + + if (!desc->div_width) + return ERR_PTR(-EINVAL); + + if (!lock) + return ERR_PTR(-EINVAL); + + divider = devm_kzalloc(dev, sizeof(*divider), GFP_KERNEL); + if (!divider) + return ERR_PTR(-ENOMEM); + + divider->divider.reg = reg; + divider->divider.shift = desc->div_shift; + divider->divider.width = desc->div_width; + divider->divider.flags = desc->divider_flags; + divider->divider.lock = lock; + divider->load_mask = desc->load_mask; + divider->gate.reg = reg; + divider->gate.bit_idx = desc->gate_bit; + divider->gate.flags = desc->gate_flags; + divider->gate.lock = lock; + + return devm_clk_hw_register_composite_pdata(dev, desc->name, + &pdata, 1, NULL, NULL, + ÷r->divider.hw, + &ultrarisc_divider_ops, + ÷r->gate.hw, + &clk_gate_ops, 0); +} + +static int ultrarisc_clk_register_fixed_factors(struct device *dev, + struct clk_hw_onecell_data *clk_data, + const struct ultrarisc_clk_soc_data *soc_data) +{ + u32 i; + + for (i = 0; i < soc_data->num_fixed_factors; i++) { + const struct ultrarisc_fixed_factor_desc *desc; + struct clk_hw *parent_hw; + struct clk_hw *hw; + + desc = &soc_data->fixed_factors[i]; + if (desc->id >= clk_data->num || desc->parent_id >= clk_data->num) + return -EINVAL; + + parent_hw = clk_data->hws[desc->parent_id]; + if (!parent_hw) + return -EINVAL; + + hw = devm_clk_hw_register_fixed_factor_parent_hw(dev, desc->name, + parent_hw, 0, + desc->mult, + desc->div); + if (IS_ERR(hw)) + return PTR_ERR(hw); + + clk_data->hws[desc->id] = hw; + } + + return 0; +} + +static int ultrarisc_clk_register_plls(struct platform_device *pdev, + struct clk_hw_onecell_data *clk_data, + const struct ultrarisc_clk_soc_data *soc_data, + void __iomem *base) +{ + struct device *dev = &pdev->dev; + u32 i; + + for (i = 0; i < soc_data->num_plls; i++) { + const struct ultrarisc_pll_desc *desc = &soc_data->plls[i]; + struct clk_hw *hw; + + if (desc->id >= clk_data->num) { + dev_err(dev, "%s invalid clock ID %u >= %u\n", + desc->name, desc->id, clk_data->num); + return -EINVAL; + } + + hw = ultrarisc_clk_register_pll(dev, desc, soc_data->pll_layout, base); + if (IS_ERR(hw)) + return PTR_ERR(hw); + + clk_data->hws[desc->id] = hw; + } + + return 0; +} + +static int ultrarisc_clk_register_dividers(struct platform_device *pdev, + struct clk_hw_onecell_data *clk_data, + const struct ultrarisc_clk_soc_data *soc_data, + void __iomem *base, + spinlock_t *lock) +{ + struct device *dev = &pdev->dev; + u32 i; + + for (i = 0; i < soc_data->num_dividers; i++) { + const struct ultrarisc_divider_desc *desc; + struct clk_hw *parent_hw; + struct clk_hw *hw; + + desc = &soc_data->dividers[i]; + if (desc->id >= clk_data->num || desc->parent_id >= clk_data->num) + return -EINVAL; + + parent_hw = clk_data->hws[desc->parent_id]; + if (!parent_hw) + return -EINVAL; + + hw = ultrarisc_clk_register_divider(dev, desc, parent_hw, base, + lock); + if (IS_ERR(hw)) + return PTR_ERR(hw); + + if (desc->max_rate) + clk_hw_set_rate_range(hw, 0, desc->max_rate); + + clk_data->hws[desc->id] = hw; + } + + return 0; +} + +static int ultrarisc_clk_register_gates(struct platform_device *pdev, + struct clk_hw_onecell_data *clk_data, + const struct ultrarisc_clk_soc_data *soc_data, + void __iomem *base, + spinlock_t *lock) +{ + struct device *dev = &pdev->dev; + u32 i; + + for (i = 0; i < soc_data->num_gates; i++) { + const struct ultrarisc_gate_desc *desc; + struct clk_hw *parent_hw; + struct clk_hw *hw; + + desc = &soc_data->gates[i]; + if (desc->id >= clk_data->num || desc->parent_id >= clk_data->num) + return -EINVAL; + + parent_hw = clk_data->hws[desc->parent_id]; + if (!parent_hw) + return -EINVAL; + + hw = devm_clk_hw_register_gate_parent_hw(dev, desc->name, + parent_hw, 0, + base + desc->offset, + desc->gate_bit, + desc->gate_flags, + lock); + if (IS_ERR(hw)) + return PTR_ERR(hw); + + clk_data->hws[desc->id] = hw; + } + + return 0; +} + +int ultrarisc_clk_probe(struct platform_device *pdev, + const struct ultrarisc_clk_soc_data *soc_data) +{ + struct clk_hw_onecell_data *clk_data; + struct device *dev = &pdev->dev; + void __iomem *base; + spinlock_t *lock; + int ret; + + if (!soc_data) + return -EINVAL; + + lock = devm_kzalloc(dev, sizeof(*lock), GFP_KERNEL); + if (!lock) + return -ENOMEM; + + spin_lock_init(lock); + + clk_data = devm_kzalloc(dev, struct_size(clk_data, hws, + soc_data->num_clks), + GFP_KERNEL); + if (!clk_data) + return -ENOMEM; + + clk_data->num = soc_data->num_clks; + + base = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(base)) + return PTR_ERR(base); + + ret = ultrarisc_clk_register_plls(pdev, clk_data, soc_data, base); + if (ret) + return ret; + + ret = ultrarisc_clk_register_fixed_factors(dev, clk_data, soc_data); + if (ret) + return ret; + + ret = ultrarisc_clk_register_dividers(pdev, clk_data, soc_data, base, lock); + if (ret) + return ret; + + ret = ultrarisc_clk_register_gates(pdev, clk_data, soc_data, base, lock); + if (ret) + return ret; + + for (int i = 0; i < clk_data->num; i++) { + if (!clk_data->hws[i]) { + dev_err(dev, "missing clock ID %u\n", i); + return -EINVAL; + } + } + + return devm_of_clk_add_hw_provider(dev, of_clk_hw_onecell_get, clk_data); +} +EXPORT_SYMBOL_NS_GPL(ultrarisc_clk_probe, "CLK_ULTRARISC"); + +MODULE_DESCRIPTION("UltraRISC clock core driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/clk/ultrarisc/clk-ultrarisc.h b/drivers/clk/ultrarisc/clk-ultrarisc.h new file mode 100644 index 000000000000..8202b7ed0e31 --- /dev/null +++ b/drivers/clk/ultrarisc/clk-ultrarisc.h @@ -0,0 +1,71 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +#ifndef __ULTRARISC_CLK_ULTRARISC_H +#define __ULTRARISC_CLK_ULTRARISC_H + +#include +#include +#include + +struct ultrarisc_pll_layout { + u32 cfg1_offset; + u32 cfg2_offset; + u32 frac_mask; + u32 m_mask; + u32 n_mask; + u32 oddiv1_mask; + u32 oddiv2_mask; +}; + +struct ultrarisc_pll_desc { + u32 id; + const char *name; +}; + +struct ultrarisc_fixed_factor_desc { + u32 id; + const char *name; + u32 parent_id; + u32 mult; + u32 div; +}; + +struct ultrarisc_divider_desc { + u32 id; + const char *name; + u32 offset; + u32 parent_id; + unsigned long max_rate; + u32 load_mask; + u8 div_shift; + u8 div_width; + u8 gate_bit; + u16 divider_flags; + u8 gate_flags; +}; + +struct ultrarisc_gate_desc { + u32 id; + const char *name; + u32 offset; + u32 parent_id; + u8 gate_bit; + u8 gate_flags; +}; + +struct ultrarisc_clk_soc_data { + const struct ultrarisc_pll_layout *pll_layout; + const struct ultrarisc_pll_desc *plls; + u32 num_plls; + const struct ultrarisc_fixed_factor_desc *fixed_factors; + u32 num_fixed_factors; + const struct ultrarisc_divider_desc *dividers; + u32 num_dividers; + const struct ultrarisc_gate_desc *gates; + u32 num_gates; + u32 num_clks; +}; + +int ultrarisc_clk_probe(struct platform_device *pdev, + const struct ultrarisc_clk_soc_data *soc_data); + +#endif /* __ULTRARISC_CLK_ULTRARISC_H */ From 48ceae43be9f3537eee179bcc53266eadbc40bcc Mon Sep 17 00:00:00 2001 From: Joakim Zhang Date: Thu, 23 Jul 2026 17:08:05 +0800 Subject: [PATCH 077/112] dt-bindings: soc: cix: add sky1 audss cru controller The Cix Sky1 Audio Subsystem (AUDSS) Clock and Reset Unit (CRU) groups clock muxing, gating and block-level software reset control in a single register block. Reviewed-by: Krzysztof Kozlowski Signed-off-by: Joakim Zhang Signed-off-by: Brian Masney --- .../bindings/soc/cix/cix,sky1-audss-cru.yaml | 92 +++++++++++++++++++ .../dt-bindings/clock/cix,sky1-audss-cru.h | 60 ++++++++++++ .../dt-bindings/reset/cix,sky1-audss-cru.h | 25 +++++ 3 files changed, 177 insertions(+) create mode 100644 Documentation/devicetree/bindings/soc/cix/cix,sky1-audss-cru.yaml create mode 100644 include/dt-bindings/clock/cix,sky1-audss-cru.h create mode 100644 include/dt-bindings/reset/cix,sky1-audss-cru.h diff --git a/Documentation/devicetree/bindings/soc/cix/cix,sky1-audss-cru.yaml b/Documentation/devicetree/bindings/soc/cix/cix,sky1-audss-cru.yaml new file mode 100644 index 000000000000..50dd0593e1d9 --- /dev/null +++ b/Documentation/devicetree/bindings/soc/cix/cix,sky1-audss-cru.yaml @@ -0,0 +1,92 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/soc/cix/cix,sky1-audss-cru.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Cix Sky1 audio subsystem clock and reset unit + +maintainers: + - Joakim Zhang + +description: | + The Cix Sky1 Audio Subsystem (AUDSS) Clock and Reset Unit (CRU) groups + audio-related clock muxing, gating and block-level software reset control + in a single register block. + + A single device node exposes both the clock controller and software reset + lines. The clock driver registers as a platform driver; the reset controller + is registered by an auxiliary driver bound from the clock driver. + + Four SoC-level reference clocks listed in clocks/clock-names feed the AUDSS + clock tree. Internal AUDSS clocks are exposed via #clock-cells; indices are + defined in include/dt-bindings/clock/cix,sky1-audss-cru.h. + + Block-level software reset indices are exposed via #reset-cells; indices + are defined in include/dt-bindings/reset/cix,sky1-audss-cru.h. + + The SoC syscon NoC (or bus) reset is described via resets. The audio + subsystem power domain is described via power-domains. + +properties: + compatible: + const: cix,sky1-audss-cru + + reg: + maxItems: 1 + + '#clock-cells': + const: 1 + description: + Clock indices are defined in include/dt-bindings/clock/cix,sky1-audss-cru.h. + + '#reset-cells': + const: 1 + description: + Reset indices are defined in include/dt-bindings/reset/cix,sky1-audss-cru.h. + + clocks: + items: + - description: I2S parent clock for sampling rates multiple of 8kHz. + - description: I2S parent clock for sampling rates multiple of 11.025kHz. + - description: Clock feeding most devices in AUDSS (NOC, DSP, SRAM, HDA, DMAC, I2S, and mailbox). + - description: Clock feeding HDA, timer and watchdog, which is a dedicated 48 MHz clock. + + clock-names: + items: + - const: x8k + - const: x11k + - const: sys + - const: 48m + + power-domains: + maxItems: 1 + + resets: + maxItems: 1 + +required: + - compatible + - reg + - '#clock-cells' + - '#reset-cells' + - clocks + - clock-names + - power-domains + - resets + +additionalProperties: false + +examples: + - | + audss_cru: clock-controller@7110000 { + compatible = "cix,sky1-audss-cru"; + reg = <0x7110000 0x10000>; + #clock-cells = <1>; + #reset-cells = <1>; + clocks = <&scmi_clk 76>, <&scmi_clk 78>, + <&scmi_clk 70>, <&scmi_clk 71>; + clock-names = "x8k", "x11k", "sys", "48m"; + power-domains = <&smc_devpd 0>; + resets = <&s5_syscon 31>; + }; diff --git a/include/dt-bindings/clock/cix,sky1-audss-cru.h b/include/dt-bindings/clock/cix,sky1-audss-cru.h new file mode 100644 index 000000000000..8c58ef8bf682 --- /dev/null +++ b/include/dt-bindings/clock/cix,sky1-audss-cru.h @@ -0,0 +1,60 @@ +/* SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) */ +/* + * Copyright 2026 Cix Technology Group Co., Ltd. + */ + +#ifndef _DT_BINDINGS_CLOCK_CIX_SKY1_AUDSS_CRU_H +#define _DT_BINDINGS_CLOCK_CIX_SKY1_AUDSS_CRU_H + +#define CLK_AUD_CLK4_DIV2 0 +#define CLK_AUD_CLK4_DIV4 1 +#define CLK_AUD_CLK5_DIV2 2 + +#define CLK_DSP_CLK 3 +#define CLK_DSP_BCLK 4 +#define CLK_DSP_PBCLK 5 + +#define CLK_SRAM_AXI 6 + +#define CLK_HDA_SYS 7 +#define CLK_HDA_HDA 8 + +#define CLK_DMAC_AXI 9 + +#define CLK_WDG_APB 10 +#define CLK_WDG_WDG 11 + +#define CLK_TIMER_APB 12 +#define CLK_TIMER_TIMER 13 + +#define CLK_MB_0_APB 14 /* MB0: ap->dsp */ +#define CLK_MB_1_APB 15 /* MB1: dsp->ap */ + +#define CLK_I2S0_APB 16 +#define CLK_I2S1_APB 17 +#define CLK_I2S2_APB 18 +#define CLK_I2S3_APB 19 +#define CLK_I2S4_APB 20 +#define CLK_I2S5_APB 21 +#define CLK_I2S6_APB 22 +#define CLK_I2S7_APB 23 +#define CLK_I2S8_APB 24 +#define CLK_I2S9_APB 25 +#define CLK_I2S0 26 +#define CLK_I2S1 27 +#define CLK_I2S2 28 +#define CLK_I2S3 29 +#define CLK_I2S4 30 +#define CLK_I2S5 31 +#define CLK_I2S6 32 +#define CLK_I2S7 33 +#define CLK_I2S8 34 +#define CLK_I2S9 35 + +#define CLK_MCLK0 36 +#define CLK_MCLK1 37 +#define CLK_MCLK2 38 +#define CLK_MCLK3 39 +#define CLK_MCLK4 40 + +#endif diff --git a/include/dt-bindings/reset/cix,sky1-audss-cru.h b/include/dt-bindings/reset/cix,sky1-audss-cru.h new file mode 100644 index 000000000000..55e9f3797b30 --- /dev/null +++ b/include/dt-bindings/reset/cix,sky1-audss-cru.h @@ -0,0 +1,25 @@ +/* SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) */ +/* + * Copyright 2026 Cix Technology Group Co., Ltd. + */ +#ifndef DT_BINDINGS_RESET_CIX_SKY1_AUDSS_CRU_H +#define DT_BINDINGS_RESET_CIX_SKY1_AUDSS_CRU_H + +#define AUDSS_I2S0_SW_RST 0 +#define AUDSS_I2S1_SW_RST 1 +#define AUDSS_I2S2_SW_RST 2 +#define AUDSS_I2S3_SW_RST 3 +#define AUDSS_I2S4_SW_RST 4 +#define AUDSS_I2S5_SW_RST 5 +#define AUDSS_I2S6_SW_RST 6 +#define AUDSS_I2S7_SW_RST 7 +#define AUDSS_I2S8_SW_RST 8 +#define AUDSS_I2S9_SW_RST 9 +#define AUDSS_WDT_SW_RST 10 +#define AUDSS_TIMER_SW_RST 11 +#define AUDSS_MB0_SW_RST 12 +#define AUDSS_MB1_SW_RST 13 +#define AUDSS_HDA_SW_RST 14 +#define AUDSS_DMAC_SW_RST 15 + +#endif From 3730fe6032d9a5e1140994693feefe0efa927acb Mon Sep 17 00:00:00 2001 From: Joakim Zhang Date: Thu, 23 Jul 2026 17:08:06 +0800 Subject: [PATCH 078/112] clk: cix: add sky1 audss clock controller Add a platform driver for the Cix Sky1 AUDSS CRU. The driver maps the CRU registers and registers mux, divider and gate clocks for DSP, SRAM, HDA, DMAC, I2S, mailbox, watchdog and timer blocks. Four SoC-level audio reference clocks are enabled as inputs to the internal clock tree. The driver releases the AUDSS NOC reset, enables runtime PM and instantiates the auxiliary reset device. Signed-off-by: Joakim Zhang Reviewed-by: Brian Masney Signed-off-by: Brian Masney --- drivers/clk/Kconfig | 1 + drivers/clk/Makefile | 1 + drivers/clk/cix/Kconfig | 16 + drivers/clk/cix/Makefile | 3 + drivers/clk/cix/clk-sky1-audss.c | 1206 ++++++++++++++++++++++++++++++ 5 files changed, 1227 insertions(+) create mode 100644 drivers/clk/cix/Kconfig create mode 100644 drivers/clk/cix/Makefile create mode 100644 drivers/clk/cix/clk-sky1-audss.c diff --git a/drivers/clk/Kconfig b/drivers/clk/Kconfig index f83dda6ffc88..057feabf2f23 100644 --- a/drivers/clk/Kconfig +++ b/drivers/clk/Kconfig @@ -509,6 +509,7 @@ source "drivers/clk/actions/Kconfig" source "drivers/clk/analogbits/Kconfig" source "drivers/clk/aspeed/Kconfig" source "drivers/clk/bcm/Kconfig" +source "drivers/clk/cix/Kconfig" source "drivers/clk/eswin/Kconfig" source "drivers/clk/hisilicon/Kconfig" source "drivers/clk/imgtec/Kconfig" diff --git a/drivers/clk/Makefile b/drivers/clk/Makefile index dc56d0249450..0b6241305d4e 100644 --- a/drivers/clk/Makefile +++ b/drivers/clk/Makefile @@ -119,6 +119,7 @@ obj-$(CONFIG_ARCH_ARTPEC) += axis/ obj-$(CONFIG_ARC_PLAT_AXS10X) += axs10x/ obj-y += bcm/ obj-$(CONFIG_ARCH_BERLIN) += berlin/ +obj-y += cix/ obj-$(CONFIG_ARCH_DAVINCI) += davinci/ obj-$(CONFIG_COMMON_CLK_ESWIN) += eswin/ obj-$(CONFIG_ARCH_HISI) += hisilicon/ diff --git a/drivers/clk/cix/Kconfig b/drivers/clk/cix/Kconfig new file mode 100644 index 000000000000..b057ef85fab6 --- /dev/null +++ b/drivers/clk/cix/Kconfig @@ -0,0 +1,16 @@ +# SPDX-License-Identifier: GPL-2.0 +# Audio subsystem clock support for Cixtech SoC family +menu "Cixtech Audio Subsystem Clock Driver" + +config CLK_SKY1_AUDSS + tristate "Cixtech Sky1 Audio Subsystem Clock Driver" + depends on ARCH_CIX || COMPILE_TEST + select AUXILIARY_BUS + select REGMAP_MMIO + select RESET_CONTROLLER + help + Support for the Audio Subsystem clock controller present on + Cixtech Sky1 SoC. This driver provides mux, divider and gate + clocks for DSP, I2S, HDA and related blocks in the audio + subsystem. Say M or Y here if you want to build this driver. +endmenu diff --git a/drivers/clk/cix/Makefile b/drivers/clk/cix/Makefile new file mode 100644 index 000000000000..bc612f1d08b2 --- /dev/null +++ b/drivers/clk/cix/Makefile @@ -0,0 +1,3 @@ +# SPDX-License-Identifier: GPL-2.0 + +obj-$(CONFIG_CLK_SKY1_AUDSS) += clk-sky1-audss.o diff --git a/drivers/clk/cix/clk-sky1-audss.c b/drivers/clk/cix/clk-sky1-audss.c new file mode 100644 index 000000000000..e096b8121df3 --- /dev/null +++ b/drivers/clk/cix/clk-sky1-audss.c @@ -0,0 +1,1206 @@ +// SPDX-License-Identifier: GPL-2.0-only +// Copyright 2026 Cix Technology Group Co., Ltd. + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#define INFO_HIFI0 0x00 +#define INFO_CLK_GATE 0x10 +#define INFO_CLK_DIV 0x14 +#define INFO_CLK_MUX 0x18 +#define INFO_MCLK 0x70 + +#define SKY1_AUDSS_CLK_PARENTS_CNT 4 +#define SKY1_AUDSS_NUM_CLKS (CLK_MCLK4 + 1) + +static u32 sky1_reg_save[][2] = { + { INFO_HIFI0, 0 }, + { INFO_CLK_GATE, 0 }, + { INFO_CLK_DIV, 0 }, + { INFO_CLK_MUX, 0 }, + { INFO_MCLK, 0 }, +}; + +static const char * const sky1_audss_clk_names[SKY1_AUDSS_CLK_PARENTS_CNT] = { + "x8k", "x11k", "sys", "48m", +}; + +static const u32 sky1_clk_rate_default[SKY1_AUDSS_CLK_PARENTS_CNT] = { + 294912000, + 270950400, + 800000000, + 48000000, +}; + +static const char * const dsp_clk_parent[] = { + "audio_clk4" +}; + +static const char * const dsp_bclk_parent[] = { + "audio_clk4_div2" +}; + +static const char * const dsp_pbclk_parent[] = { + "audio_clk4_div4" +}; + +static const char * const sram_axi_parent[] = { + "audio_clk4_div2" +}; + +static const char * const hda_sys_parent[] = { + "audio_clk4_div2" +}; + +static const char * const hda_hda_parent[] = { + "audio_clk5" +}; + +static const char * const dmac_axi_parent[] = { + "audio_clk4_div2" +}; + +static const char * const wdg_apb_parent[] = { + "audio_clk5_div2" +}; + +static const char * const wdg_wdg_parent[] = { + "audio_clk5_div2" +}; + +static const char * const timer_apb_parent[] = { + "audio_clk4_div4" +}; + +static const char * const timer_timer_parent[] = { + "audio_clk5_div2" +}; + +static const char * const mailbox_apb_parent[] = { + "audio_clk4_div4" +}; + +static const char * const i2s_apb_parent[] = { + "audio_clk4_div4" +}; + +static const char * const i2s0_parents[] = { + "audio_clk0", "audio_clk2" +}; + +static const char * const i2s1_parents[] = { + "audio_clk0", "audio_clk2" +}; + +static const char * const i2s2_parents[] = { + "audio_clk0", "audio_clk2" +}; + +static const char * const i2s3_parents[] = { + "audio_clk0", "audio_clk2" +}; + +static const char * const i2s4_parents[] = { + "audio_clk0", "audio_clk2" +}; + +static const char * const i2s5_parents[] = { + "audio_clk0", "audio_clk2" +}; + +static const char * const i2s6_parents[] = { + "audio_clk0", "audio_clk2" +}; + +static const char * const i2s7_parents[] = { + "audio_clk0", "audio_clk2" +}; + +static const char * const i2s8_parents[] = { + "audio_clk0", "audio_clk2" +}; + +static const char * const i2s9_parents[] = { + "audio_clk0", "audio_clk2" +}; + +static const char * const mclk_parents[] = { + "audio_clk0", "audio_clk2" +}; + +static const u32 i2s3_mux_table[] = { 0, 2 }; +static const u32 i2s4_mux_table[] = { 0, 2 }; + +/* + * audss composite clock definition + */ +struct muxdiv_cfg { + int offset; + u8 shift; + u8 width; + u8 flags; +}; + +struct gate_cfg { + int offset; + u8 shift; + u8 flags; +}; + +struct composite_clk_cfg { + u32 id; + const char * const name; + const char * const *parent_names; + int num_parents; + const u32 *mux_table; + struct muxdiv_cfg *mux_cfg; + struct muxdiv_cfg *div_cfg; + struct gate_cfg *gate_cfg; + unsigned long flags; +}; + +#define CFG(_id,\ + _name,\ + _parent_names,\ + _mux_table,\ + _mux_offset, _mux_shift, _mux_width, _mux_flags,\ + _div_offset, _div_shift, _div_width, _div_flags,\ + _gate_offset, _gate_shift, _gate_flags,\ + _flags)\ +{\ + .id = _id,\ + .name = _name,\ + .parent_names = _parent_names,\ + .num_parents = ARRAY_SIZE(_parent_names),\ + .mux_table = _mux_table,\ + .mux_cfg = &(struct muxdiv_cfg) { _mux_offset, _mux_shift, _mux_width, _mux_flags },\ + .div_cfg = &(struct muxdiv_cfg) { _div_offset, _div_shift, _div_width, _div_flags },\ + .gate_cfg = &(struct gate_cfg) { _gate_offset, _gate_shift, _gate_flags },\ + .flags = _flags,\ +} + +static const struct composite_clk_cfg sky1_audss_clks[] = { + /* dsp */ + CFG(CLK_DSP_CLK, + "audss_dsp_clk", + dsp_clk_parent, + NULL, + -1, 0, 0, 0, + INFO_CLK_DIV, 0, 2, 0, + INFO_HIFI0, 0, 0, + 0), + CFG(CLK_DSP_BCLK, + "audss_dsp_bclk", + dsp_bclk_parent, + NULL, + -1, 0, 0, 0, + INFO_CLK_DIV, 0, 2, 0, + -1, 0, 0, + 0), + CFG(CLK_DSP_PBCLK, + "audss_dsp_pbclk", + dsp_pbclk_parent, + NULL, + -1, 0, 0, 0, + INFO_CLK_DIV, 0, 2, 0, + -1, 0, 0, + 0), + /* sram */ + CFG(CLK_SRAM_AXI, + "audss_sram_axi", + sram_axi_parent, + NULL, + -1, 0, 0, 0, + INFO_CLK_DIV, 0, 2, 0, + INFO_CLK_GATE, 16, 0, + 0), + /* hda */ + CFG(CLK_HDA_SYS, + "audss_hda_sys", + hda_sys_parent, + NULL, + -1, 0, 0, 0, + INFO_CLK_DIV, 0, 2, 0, + INFO_CLK_GATE, 14, 0, + 0), + CFG(CLK_HDA_HDA, + "audss_hda_hda", + hda_hda_parent, + NULL, + -1, 0, 0, 0, + -1, 0, 0, 0, + INFO_CLK_GATE, 14, 0, + 0), + /* dmac */ + CFG(CLK_DMAC_AXI, + "audss_dmac_axi", + dmac_axi_parent, + NULL, + -1, 0, 0, 0, + INFO_CLK_DIV, 0, 2, 0, + INFO_CLK_GATE, 15, 0, + 0), + /* wdg */ + CFG(CLK_WDG_APB, + "audss_wdg_apb", + wdg_apb_parent, + NULL, + -1, 0, 0, 0, + -1, 0, 0, 0, + INFO_CLK_GATE, 10, 0, + 0), + CFG(CLK_WDG_WDG, + "audss_wdg_wdg", + wdg_wdg_parent, + NULL, + -1, 0, 0, 0, + -1, 0, 0, 0, + INFO_CLK_GATE, 10, 0, + 0), + /* timer */ + CFG(CLK_TIMER_APB, + "audss_timer_apb", + timer_apb_parent, + NULL, + -1, 0, 0, 0, + INFO_CLK_DIV, 0, 2, 0, + INFO_CLK_GATE, 11, 0, + 0), + CFG(CLK_TIMER_TIMER, + "audss_timer_timer", + timer_timer_parent, + NULL, + -1, 0, 0, 0, + -1, 0, 0, 0, + INFO_CLK_GATE, 11, 0, + 0), + /* mailbox: mb0(ap->dsp), mb1(dsp->ap) */ + CFG(CLK_MB_0_APB, + "audss_mb_0_apb", + mailbox_apb_parent, + NULL, + -1, 0, 0, 0, + -1, 0, 0, 0, + INFO_CLK_GATE, 12, 0, + 0), + CFG(CLK_MB_1_APB, + "audss_mb_1_apb", + mailbox_apb_parent, + NULL, + -1, 0, 0, 0, + -1, 0, 0, 0, + INFO_CLK_GATE, 13, 0, + 0), + /* i2s */ + CFG(CLK_I2S0_APB, + "audss_i2s0_apb", + i2s_apb_parent, + NULL, + -1, 0, 0, 0, + INFO_CLK_DIV, 0, 2, 0, + INFO_CLK_GATE, 0, 0, + 0), + CFG(CLK_I2S1_APB, + "audss_i2s1_apb", + i2s_apb_parent, + NULL, + -1, 0, 0, 0, + INFO_CLK_DIV, 0, 2, 0, + INFO_CLK_GATE, 1, 0, + 0), + CFG(CLK_I2S2_APB, + "audss_i2s2_apb", + i2s_apb_parent, + NULL, + -1, 0, 0, 0, + INFO_CLK_DIV, 0, 2, 0, + INFO_CLK_GATE, 2, 0, + 0), + CFG(CLK_I2S3_APB, + "audss_i2s3_apb", + i2s_apb_parent, + NULL, + -1, 0, 0, 0, + INFO_CLK_DIV, 0, 2, 0, + INFO_CLK_GATE, 3, 0, + 0), + CFG(CLK_I2S4_APB, + "audss_i2s4_apb", + i2s_apb_parent, + NULL, + -1, 0, 0, 0, + INFO_CLK_DIV, 0, 2, 0, + INFO_CLK_GATE, 4, 0, + 0), + CFG(CLK_I2S5_APB, + "audss_i2s5_apb", + i2s_apb_parent, + NULL, + -1, 0, 0, 0, + INFO_CLK_DIV, 0, 2, 0, + INFO_CLK_GATE, 5, 0, + 0), + CFG(CLK_I2S6_APB, + "audss_i2s6_apb", + i2s_apb_parent, + NULL, + -1, 0, 0, 0, + INFO_CLK_DIV, 0, 2, 0, + INFO_CLK_GATE, 6, 0, + 0), + CFG(CLK_I2S7_APB, + "audss_i2s7_apb", + i2s_apb_parent, + NULL, + -1, 0, 0, 0, + INFO_CLK_DIV, 0, 2, 0, + INFO_CLK_GATE, 7, 0, + 0), + CFG(CLK_I2S8_APB, + "audss_i2s8_apb", + i2s_apb_parent, + NULL, + -1, 0, 0, 0, + INFO_CLK_DIV, 0, 2, 0, + INFO_CLK_GATE, 8, 0, + 0), + CFG(CLK_I2S9_APB, + "audss_i2s9_apb", + i2s_apb_parent, + NULL, + -1, 0, 0, 0, + INFO_CLK_DIV, 0, 2, 0, + INFO_CLK_GATE, 9, 0, + 0), + CFG(CLK_I2S0, + "audss_i2s0", + i2s0_parents, + NULL, + INFO_CLK_MUX, 0, 2, 0, + INFO_CLK_DIV, 2, 2, 0, + INFO_CLK_GATE, 0, 0, + 0), + CFG(CLK_I2S1, + "audss_i2s1", + i2s1_parents, + NULL, + INFO_CLK_MUX, 2, 2, 0, + INFO_CLK_DIV, 4, 2, 0, + INFO_CLK_GATE, 1, 0, + 0), + CFG(CLK_I2S2, + "audss_i2s2", + i2s2_parents, + NULL, + INFO_CLK_MUX, 4, 2, 0, + INFO_CLK_DIV, 6, 2, 0, + INFO_CLK_GATE, 2, 0, + 0), + CFG(CLK_I2S3, + "audss_i2s3", + i2s3_parents, + i2s3_mux_table, + INFO_CLK_MUX, 6, 2, 0, + INFO_CLK_DIV, 8, 2, 0, + INFO_CLK_GATE, 3, 0, + 0), + CFG(CLK_I2S4, + "audss_i2s4", + i2s4_parents, + i2s4_mux_table, + INFO_CLK_MUX, 8, 2, 0, + INFO_CLK_DIV, 10, 2, 0, + INFO_CLK_GATE, 4, 0, + 0), + CFG(CLK_I2S5, + "audss_i2s5", + i2s5_parents, + NULL, + INFO_CLK_MUX, 10, 2, 0, + INFO_CLK_DIV, 12, 2, 0, + INFO_CLK_GATE, 5, 0, + 0), + CFG(CLK_I2S6, + "audss_i2s6", + i2s6_parents, + NULL, + INFO_CLK_MUX, 12, 2, 0, + INFO_CLK_DIV, 14, 2, 0, + INFO_CLK_GATE, 6, 0, + 0), + CFG(CLK_I2S7, + "audss_i2s7", + i2s7_parents, + NULL, + INFO_CLK_MUX, 14, 2, 0, + INFO_CLK_DIV, 16, 2, 0, + INFO_CLK_GATE, 7, 0, + 0), + CFG(CLK_I2S8, + "audss_i2s8", + i2s8_parents, + NULL, + INFO_CLK_MUX, 16, 2, 0, + INFO_CLK_DIV, 18, 2, 0, + INFO_CLK_GATE, 8, 0, + 0), + CFG(CLK_I2S9, + "audss_i2s9", + i2s9_parents, + NULL, + INFO_CLK_MUX, 18, 2, 0, + INFO_CLK_DIV, 20, 2, 0, + INFO_CLK_GATE, 9, 0, + 0), + /* mclk */ + CFG(CLK_MCLK0, + "audss_mclk0", + mclk_parents, + NULL, + INFO_MCLK, 5, 1, 0, + -1, 0, 0, 0, + INFO_MCLK, 0, 0, + 0), + CFG(CLK_MCLK1, + "audss_mclk1", + mclk_parents, + NULL, + INFO_MCLK, 6, 1, 0, + -1, 0, 0, 0, + INFO_MCLK, 1, 0, + 0), + CFG(CLK_MCLK2, + "audss_mclk2", + mclk_parents, + NULL, + INFO_MCLK, 7, 1, 0, + -1, 0, 0, 0, + INFO_MCLK, 2, 0, + 0), + CFG(CLK_MCLK3, + "audss_mclk3", + mclk_parents, + NULL, + INFO_MCLK, 8, 1, 0, + -1, 0, 0, 0, + INFO_MCLK, 3, 0, + 0), + CFG(CLK_MCLK4, + "audss_mclk4", + mclk_parents, + NULL, + INFO_MCLK, 9, 1, 0, + -1, 0, 0, 0, + INFO_MCLK, 4, 0, + 0), +}; + +struct sky1_audss_clks_devtype_data { + u32 (*reg_save)[2]; + size_t reg_save_size; + const char * const *clk_names; + size_t clk_num; + const u32 *clk_rate_default; + const struct composite_clk_cfg *clk_cfg; + size_t clk_cfg_size; +}; + +static const struct regmap_config sky1_audss_regmap_config = { + .reg_bits = 32, + .val_bits = 32, + .reg_stride = 4, +}; + +struct sky1_audss_clks_priv { + struct device *dev; + struct regmap *regmap_cru; + struct reset_control *rst_noc; + struct clk *clks[SKY1_AUDSS_CLK_PARENTS_CNT]; + const struct sky1_audss_clks_devtype_data *devtype_data; + spinlock_t lock; + struct clk_hw_onecell_data *clk_data; +}; + +#if IS_ENABLED(CONFIG_RESET_SKY1_AUDSS) + +static int sky1_audss_reset_controller_register(struct device *dev) +{ + struct auxiliary_device *adev; + + if (!of_property_present(dev->of_node, "#reset-cells")) + return 0; + + adev = devm_auxiliary_device_create(dev, "reset", NULL); + if (!adev) + return -ENODEV; + + return 0; +} + +#else + +static int sky1_audss_reset_controller_register(struct device *dev) +{ + return 0; +} + +#endif + +/* + * clk_ops for audss clock mux/divider/gate + */ +struct sky1_clk_divider { + struct clk_divider div; + struct regmap *regmap; + int offset; +}; + +struct sky1_clk_gate { + struct clk_gate gate; + struct regmap *regmap; + int offset; +}; + +struct sky1_clk_mux { + struct clk_mux mux; + struct regmap *regmap; + int offset; +}; + +static inline struct sky1_clk_mux *to_sky1_clk_mux(struct clk_mux *mux) +{ + return container_of(mux, struct sky1_clk_mux, mux); +} + +static u8 sky1_audss_clk_mux_get_parent(struct clk_hw *hw) +{ + struct clk_mux *mux = to_clk_mux(hw); + struct sky1_clk_mux *sky1_mux = to_sky1_clk_mux(mux); + u32 val; + + regmap_read(sky1_mux->regmap, sky1_mux->offset, &val); + val = val >> mux->shift; + val &= mux->mask; + + return clk_mux_val_to_index(hw, mux->table, mux->flags, val); +} + +static int sky1_audss_clk_mux_set_parent(struct clk_hw *hw, u8 index) +{ + struct clk_mux *mux = to_clk_mux(hw); + u32 val = clk_mux_index_to_val(mux->table, mux->flags, index); + struct sky1_clk_mux *sky1_mux = to_sky1_clk_mux(mux); + unsigned long flags = 0; + u32 reg; + + if (mux->lock) + spin_lock_irqsave(mux->lock, flags); + else + __acquire(mux->lock); + + if (mux->flags & CLK_MUX_HIWORD_MASK) { + reg = mux->mask << (mux->shift + 16); + } else { + regmap_read(sky1_mux->regmap, sky1_mux->offset, ®); + reg &= ~(mux->mask << mux->shift); + } + val = val << mux->shift; + reg |= val; + regmap_write(sky1_mux->regmap, sky1_mux->offset, reg); + + if (mux->lock) + spin_unlock_irqrestore(mux->lock, flags); + else + __release(mux->lock); + + return 0; +} + +static int sky1_audss_clk_mux_determine_rate(struct clk_hw *hw, + struct clk_rate_request *req) +{ + struct clk_mux *mux = to_clk_mux(hw); + + return clk_mux_determine_rate_flags(hw, req, mux->flags); +} + +static const struct clk_ops sky1_audss_clk_mux_ops = { + .get_parent = sky1_audss_clk_mux_get_parent, + .set_parent = sky1_audss_clk_mux_set_parent, + .determine_rate = sky1_audss_clk_mux_determine_rate, +}; + +static inline struct sky1_clk_divider *to_sky1_clk_divider(struct clk_divider *div) +{ + return container_of(div, struct sky1_clk_divider, div); +} + +static unsigned long sky1_audss_clk_divider_recalc_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + struct clk_divider *divider = to_clk_divider(hw); + struct sky1_clk_divider *sky1_div = to_sky1_clk_divider(divider); + unsigned int val; + + regmap_read(sky1_div->regmap, sky1_div->offset, &val); + val = val >> divider->shift; + val &= clk_div_mask(divider->width); + + return divider_recalc_rate(hw, parent_rate, val, divider->table, + divider->flags, divider->width); +} + +static int sky1_audss_clk_divider_determine_rate(struct clk_hw *hw, + struct clk_rate_request *req) +{ + struct clk_divider *divider = to_clk_divider(hw); + struct sky1_clk_divider *sky1_div = to_sky1_clk_divider(divider); + + /* if read only, just return current value */ + if (divider->flags & CLK_DIVIDER_READ_ONLY) { + u32 val; + + regmap_read(sky1_div->regmap, sky1_div->offset, &val); + val = val >> divider->shift; + val &= clk_div_mask(divider->width); + + return divider_ro_determine_rate(hw, req, divider->table, + divider->width, + divider->flags, val); + } + + return divider_determine_rate(hw, req, divider->table, divider->width, + divider->flags); +} + +static int sky1_audss_clk_divider_set_rate(struct clk_hw *hw, + unsigned long rate, + unsigned long parent_rate) +{ + struct clk_divider *divider = to_clk_divider(hw); + struct sky1_clk_divider *sky1_div = to_sky1_clk_divider(divider); + int value; + unsigned long flags = 0; + u32 val; + + value = divider_get_val(rate, parent_rate, divider->table, + divider->width, divider->flags); + if (value < 0) + return value; + + if (divider->lock) + spin_lock_irqsave(divider->lock, flags); + else + __acquire(divider->lock); + + if (divider->flags & CLK_DIVIDER_HIWORD_MASK) { + val = clk_div_mask(divider->width) << (divider->shift + 16); + } else { + regmap_read(sky1_div->regmap, sky1_div->offset, &val); + val &= ~(clk_div_mask(divider->width) << divider->shift); + } + val |= (u32)value << divider->shift; + regmap_write(sky1_div->regmap, sky1_div->offset, val); + + if (divider->lock) + spin_unlock_irqrestore(divider->lock, flags); + else + __release(divider->lock); + + return 0; +} + +static const struct clk_ops sky1_audss_clk_divider_ops = { + .recalc_rate = sky1_audss_clk_divider_recalc_rate, + .determine_rate = sky1_audss_clk_divider_determine_rate, + .set_rate = sky1_audss_clk_divider_set_rate, +}; + +static inline struct sky1_clk_gate *to_sky1_clk_gate(struct clk_gate *gate) +{ + return container_of(gate, struct sky1_clk_gate, gate); +} + +static void sky1_audss_clk_gate_endisable(struct clk_hw *hw, int enable) +{ + struct clk_gate *gate = to_clk_gate(hw); + struct sky1_clk_gate *sky1_gate = to_sky1_clk_gate(gate); + int set = gate->flags & CLK_GATE_SET_TO_DISABLE ? 1 : 0; + unsigned long flags = 0; + u32 reg; + + set ^= enable; + + if (gate->lock) + spin_lock_irqsave(gate->lock, flags); + else + __acquire(gate->lock); + + if (gate->flags & CLK_GATE_HIWORD_MASK) { + reg = BIT(gate->bit_idx + 16); + if (set) + reg |= BIT(gate->bit_idx); + } else { + regmap_read(sky1_gate->regmap, sky1_gate->offset, ®); + + if (set) + reg |= BIT(gate->bit_idx); + else + reg &= ~BIT(gate->bit_idx); + } + + regmap_write(sky1_gate->regmap, sky1_gate->offset, reg); + + if (gate->lock) + spin_unlock_irqrestore(gate->lock, flags); + else + __release(gate->lock); +} + +static int sky1_audss_clk_gate_enable(struct clk_hw *hw) +{ + sky1_audss_clk_gate_endisable(hw, 1); + + return 0; +} + +static void sky1_audss_clk_gate_disable(struct clk_hw *hw) +{ + sky1_audss_clk_gate_endisable(hw, 0); +} + +static int sky1_audss_clk_gate_is_enabled(struct clk_hw *hw) +{ + struct clk_gate *gate = to_clk_gate(hw); + struct sky1_clk_gate *sky1_gate = to_sky1_clk_gate(gate); + u32 reg; + + regmap_read(sky1_gate->regmap, sky1_gate->offset, ®); + + /* if a set bit disables this clk, flip it before masking */ + if (gate->flags & CLK_GATE_SET_TO_DISABLE) + reg ^= BIT(gate->bit_idx); + + reg &= BIT(gate->bit_idx); + + return !!reg; +} + +static const struct clk_ops sky1_audss_clk_gate_ops = { + .enable = sky1_audss_clk_gate_enable, + .disable = sky1_audss_clk_gate_disable, + .is_enabled = sky1_audss_clk_gate_is_enabled, +}; + +static struct clk_hw *sky1_audss_clk_register(struct device *dev, + const char *name, + const char * const *parent_names, + int num_parents, + struct regmap *regmap, + const u32 *mux_table, + struct muxdiv_cfg *mux_cfg, + struct muxdiv_cfg *div_cfg, + struct gate_cfg *gate_cfg, + unsigned long flags, + spinlock_t *lock) +{ + const struct clk_ops *sky1_gate_ops = NULL; + const struct clk_ops *sky1_mux_ops = NULL; + const struct clk_ops *sky1_div_ops = NULL; + struct sky1_clk_divider *sky1_div = NULL; + struct sky1_clk_gate *sky1_gate = NULL; + struct sky1_clk_mux *sky1_mux = NULL; + struct clk_hw *hw = ERR_PTR(-ENOMEM); + struct clk_parent_data *parent_data; + int i; + + parent_data = devm_kcalloc(dev, num_parents, sizeof(*parent_data), GFP_KERNEL); + if (!parent_data) + return ERR_PTR(-ENOMEM); + + for (i = 0; i < num_parents; i++) + parent_data[i].name = parent_names[i]; + + if (mux_cfg->offset >= 0) { + sky1_mux = devm_kzalloc(dev, sizeof(*sky1_mux), GFP_KERNEL); + if (!sky1_mux) + return ERR_PTR(-ENOMEM); + + sky1_mux->mux.reg = NULL; + sky1_mux->mux.shift = mux_cfg->shift; + sky1_mux->mux.mask = BIT(mux_cfg->width) - 1; + sky1_mux->mux.flags = mux_cfg->flags; + sky1_mux->mux.table = mux_table; + sky1_mux->mux.lock = lock; + sky1_mux_ops = &sky1_audss_clk_mux_ops; + sky1_mux->regmap = regmap; + sky1_mux->offset = mux_cfg->offset; + } + + if (div_cfg->offset >= 0) { + sky1_div = devm_kzalloc(dev, sizeof(*sky1_div), GFP_KERNEL); + if (!sky1_div) + return ERR_PTR(-ENOMEM); + + sky1_div->div.reg = NULL; + sky1_div->div.shift = div_cfg->shift; + sky1_div->div.width = div_cfg->width; + sky1_div->div.flags = div_cfg->flags | CLK_DIVIDER_POWER_OF_TWO; + sky1_div->div.lock = lock; + sky1_div_ops = &sky1_audss_clk_divider_ops; + sky1_div->regmap = regmap; + sky1_div->offset = div_cfg->offset; + } + + if (gate_cfg->offset >= 0) { + sky1_gate = devm_kzalloc(dev, sizeof(*sky1_gate), GFP_KERNEL); + if (!sky1_gate) + return ERR_PTR(-ENOMEM); + + sky1_gate->gate.reg = NULL; + sky1_gate->gate.bit_idx = gate_cfg->shift; + sky1_gate->gate.flags = gate_cfg->flags; + sky1_gate->gate.lock = lock; + sky1_gate_ops = &sky1_audss_clk_gate_ops; + sky1_gate->regmap = regmap; + sky1_gate->offset = gate_cfg->offset; + } + + hw = devm_clk_hw_register_composite_pdata(dev, name, parent_data, num_parents, + sky1_mux ? &sky1_mux->mux.hw : NULL, sky1_mux_ops, + sky1_div ? &sky1_div->div.hw : NULL, sky1_div_ops, + sky1_gate ? &sky1_gate->gate.hw : NULL, sky1_gate_ops, + flags); + if (IS_ERR(hw)) { + dev_err(dev, "register %s clock failed with err = %ld\n", + name, PTR_ERR(hw)); + return hw; + } + + return hw; +} + +static int sky1_audss_clks_get(struct sky1_audss_clks_priv *priv) +{ + const struct sky1_audss_clks_devtype_data *devtype_data = priv->devtype_data; + int i; + + for (i = 0; i < devtype_data->clk_num; i++) { + priv->clks[i] = devm_clk_get(priv->dev, devtype_data->clk_names[i]); + if (IS_ERR(priv->clks[i])) + return dev_err_probe(priv->dev, PTR_ERR(priv->clks[i]), + "failed to get clock %s", devtype_data->clk_names[i]); + } + + return 0; +} + +static int sky1_audss_clks_enable(struct sky1_audss_clks_priv *priv) +{ + const struct sky1_audss_clks_devtype_data *devtype_data = priv->devtype_data; + int i, err; + + for (i = 0; i < devtype_data->clk_num; i++) { + err = clk_prepare_enable(priv->clks[i]); + if (err) { + dev_err(priv->dev, "failed to enable clock %s\n", + devtype_data->clk_names[i]); + goto err_clks; + } + } + + return 0; + +err_clks: + while (--i >= 0) + clk_disable_unprepare(priv->clks[i]); + + return err; +} + +static void sky1_audss_clks_disable(struct sky1_audss_clks_priv *priv) +{ + const struct sky1_audss_clks_devtype_data *devtype_data = priv->devtype_data; + int i; + + for (i = 0; i < devtype_data->clk_num; i++) + clk_disable_unprepare(priv->clks[i]); +} + +static int sky1_audss_clks_set_rate(struct sky1_audss_clks_priv *priv) +{ + const struct sky1_audss_clks_devtype_data *devtype_data = priv->devtype_data; + int i, err; + + for (i = 0; i < devtype_data->clk_num; i++) { + err = clk_set_rate(priv->clks[i], devtype_data->clk_rate_default[i]); + if (err) { + dev_err(priv->dev, "failed to set clock rate %s\n", + devtype_data->clk_names[i]); + return err; + } + } + + return 0; +} + +static void sky1_audss_clk_rpm_cleanup(void *data) +{ + pm_runtime_force_suspend(data); +} + +/* register sky1 audio subsystem clocks */ +static int sky1_audss_clk_probe(struct platform_device *pdev) +{ + const struct sky1_audss_clks_devtype_data *devtype_data; + struct sky1_audss_clks_priv *priv; + struct device *dev = &pdev->dev; + struct clk_hw **clk_table; + void __iomem *base; + int i, ret; + + devtype_data = device_get_match_data(dev); + if (!devtype_data) + return -ENODEV; + + priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + spin_lock_init(&priv->lock); + + priv->clk_data = devm_kzalloc(dev, + struct_size(priv->clk_data, hws, SKY1_AUDSS_NUM_CLKS), + GFP_KERNEL); + if (!priv->clk_data) + return -ENOMEM; + + priv->clk_data->num = SKY1_AUDSS_NUM_CLKS; + clk_table = priv->clk_data->hws; + + base = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(base)) + return PTR_ERR(base); + + priv->regmap_cru = devm_regmap_init_mmio(dev, base, &sky1_audss_regmap_config); + if (IS_ERR(priv->regmap_cru)) + return dev_err_probe(dev, PTR_ERR(priv->regmap_cru), + "failed to initialize regmap\n"); + + priv->dev = dev; + priv->devtype_data = devtype_data; + + priv->rst_noc = devm_reset_control_get_exclusive(dev, NULL); + if (IS_ERR(priv->rst_noc)) + return dev_err_probe(dev, PTR_ERR(priv->rst_noc), + "failed to get audss noc reset"); + + platform_set_drvdata(pdev, priv); + + reset_control_assert(priv->rst_noc); + + ret = sky1_audss_clks_get(priv); + if (ret) + return ret; + + ret = sky1_audss_clks_enable(priv); + if (ret) + return ret; + + /* release noc reset after clock on */ + ret = reset_control_deassert(priv->rst_noc); + if (ret) { + sky1_audss_clks_disable(priv); + return ret; + } + + pm_runtime_get_noresume(dev); + pm_runtime_set_active(dev); + pm_runtime_enable(dev); + + /* + * Register cleanup action before clocks/provider (released last under LIFO). + */ + ret = devm_add_action_or_reset(dev, sky1_audss_clk_rpm_cleanup, dev); + if (ret) + return ret; + + ret = sky1_audss_clks_set_rate(priv); + if (ret) + goto fail_pm; + + /* audio_clk4 clock fixed divider */ + clk_table[CLK_AUD_CLK4_DIV2] = + devm_clk_hw_register_fixed_factor(dev, + "audio_clk4_div2", + "audio_clk4", + 0, + 1, 2); + if (IS_ERR(clk_table[CLK_AUD_CLK4_DIV2])) { + ret = PTR_ERR(clk_table[CLK_AUD_CLK4_DIV2]); + dev_err(dev, "failed to register clock %d, ret:%d\n", CLK_AUD_CLK4_DIV2, ret); + goto fail_pm; + } + + clk_table[CLK_AUD_CLK4_DIV4] = + devm_clk_hw_register_fixed_factor(dev, + "audio_clk4_div4", + "audio_clk4", + 0, + 1, 4); + if (IS_ERR(clk_table[CLK_AUD_CLK4_DIV4])) { + ret = PTR_ERR(clk_table[CLK_AUD_CLK4_DIV4]); + dev_err(dev, "failed to register clock %d, ret:%d\n", CLK_AUD_CLK4_DIV4, ret); + goto fail_pm; + } + + /* audio_clk5 clock fixed divider */ + clk_table[CLK_AUD_CLK5_DIV2] = + devm_clk_hw_register_fixed_factor(dev, + "audio_clk5_div2", + "audio_clk5", + 0, + 1, 2); + if (IS_ERR(clk_table[CLK_AUD_CLK5_DIV2])) { + ret = PTR_ERR(clk_table[CLK_AUD_CLK5_DIV2]); + dev_err(dev, "failed to register clock %d, ret:%d\n", CLK_AUD_CLK5_DIV2, ret); + goto fail_pm; + } + + for (i = 0; i < devtype_data->clk_cfg_size; i++) { + clk_table[devtype_data->clk_cfg[i].id] = + sky1_audss_clk_register(dev, + devtype_data->clk_cfg[i].name, + devtype_data->clk_cfg[i].parent_names, + devtype_data->clk_cfg[i].num_parents, + priv->regmap_cru, + devtype_data->clk_cfg[i].mux_table, + devtype_data->clk_cfg[i].mux_cfg, + devtype_data->clk_cfg[i].div_cfg, + devtype_data->clk_cfg[i].gate_cfg, + devtype_data->clk_cfg[i].flags, + &priv->lock); + if (IS_ERR(clk_table[devtype_data->clk_cfg[i].id])) { + ret = PTR_ERR(clk_table[devtype_data->clk_cfg[i].id]); + dev_err(dev, "failed to register clock %d, ret:%d\n", + devtype_data->clk_cfg[i].id, ret); + goto fail_pm; + } + } + + ret = devm_of_clk_add_hw_provider(dev, of_clk_hw_onecell_get, priv->clk_data); + if (ret) { + dev_err(dev, "failed to add clock provider: %d\n", ret); + goto fail_pm; + } + + ret = sky1_audss_reset_controller_register(dev); + if (ret) { + dev_err(dev, "failed to register reset controller: %d\n", ret); + goto fail_pm; + } + + pm_runtime_put_sync(dev); + + return 0; + +fail_pm: + pm_runtime_put_sync(dev); + return ret; +} + +static int __maybe_unused sky1_audss_clk_runtime_suspend(struct device *dev) +{ + struct sky1_audss_clks_priv *priv = dev_get_drvdata(dev); + const struct sky1_audss_clks_devtype_data *devtype_data = priv->devtype_data; + unsigned long flags; + int i; + + spin_lock_irqsave(&priv->lock, flags); + for (i = 0; i < devtype_data->reg_save_size; i++) + regmap_read(priv->regmap_cru, + devtype_data->reg_save[i][0], &devtype_data->reg_save[i][1]); + spin_unlock_irqrestore(&priv->lock, flags); + + reset_control_assert(priv->rst_noc); + + sky1_audss_clks_disable(priv); + + return 0; +} + +static int __maybe_unused sky1_audss_clk_runtime_resume(struct device *dev) +{ + struct sky1_audss_clks_priv *priv = dev_get_drvdata(dev); + const struct sky1_audss_clks_devtype_data *devtype_data = priv->devtype_data; + unsigned long flags; + int i, ret; + + ret = sky1_audss_clks_enable(priv); + if (ret) { + dev_err(dev, "failed to enable clocks\n"); + return ret; + } + + ret = reset_control_deassert(priv->rst_noc); + if (ret) { + sky1_audss_clks_disable(priv); + return ret; + } + + spin_lock_irqsave(&priv->lock, flags); + for (i = 0; i < devtype_data->reg_save_size; i++) + regmap_write(priv->regmap_cru, + devtype_data->reg_save[i][0], devtype_data->reg_save[i][1]); + spin_unlock_irqrestore(&priv->lock, flags); + + return 0; +} + +static const struct dev_pm_ops sky1_audss_clk_pm_ops = { + SET_RUNTIME_PM_OPS(sky1_audss_clk_runtime_suspend, + sky1_audss_clk_runtime_resume, NULL) + SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, + pm_runtime_force_resume) +}; + +static const struct sky1_audss_clks_devtype_data sky1_devtype_data = { + .reg_save = sky1_reg_save, + .reg_save_size = ARRAY_SIZE(sky1_reg_save), + .clk_names = sky1_audss_clk_names, + .clk_num = ARRAY_SIZE(sky1_audss_clk_names), + .clk_rate_default = sky1_clk_rate_default, + .clk_cfg = sky1_audss_clks, + .clk_cfg_size = ARRAY_SIZE(sky1_audss_clks), +}; + +static const struct of_device_id sky1_audss_clk_of_match[] = { + { .compatible = "cix,sky1-audss-cru", .data = &sky1_devtype_data, }, + { /* sentinel */ }, +}; +MODULE_DEVICE_TABLE(of, sky1_audss_clk_of_match); + +static struct platform_driver sky1_audss_clk_driver = { + .probe = sky1_audss_clk_probe, + .driver = { + .name = "sky1-audss-clk", + .suppress_bind_attrs = true, + .of_match_table = sky1_audss_clk_of_match, + .pm = pm_ptr(&sky1_audss_clk_pm_ops), + }, +}; +module_platform_driver(sky1_audss_clk_driver); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Joakim Zhang "); +MODULE_DESCRIPTION("Cixtech Sky1 Audio Subsystem Clock Controller Driver"); From b5fa9d3beec483d125757127b804470941e191af Mon Sep 17 00:00:00 2001 From: Joakim Zhang Date: Thu, 23 Jul 2026 17:08:07 +0800 Subject: [PATCH 079/112] reset: cix: add sky1 audss auxiliary reset driver Add an auxiliary reset controller driver for the AUDSS CRU. Sixteen software reset lines for audio subsystem peripherals are controlled through one register in the CRU register map. The driver is created by the AUDSS clock platform driver and registers the reset controller on the CRU device node. Signed-off-by: Joakim Zhang Reviewed-by: Philipp Zabel Acked-by: Philipp Zabel Signed-off-by: Brian Masney --- drivers/reset/Kconfig | 13 ++++ drivers/reset/Makefile | 1 + drivers/reset/reset-sky1-audss.c | 127 +++++++++++++++++++++++++++++++ 3 files changed, 141 insertions(+) create mode 100644 drivers/reset/reset-sky1-audss.c diff --git a/drivers/reset/Kconfig b/drivers/reset/Kconfig index d009eb0849a3..b19e719f2abe 100644 --- a/drivers/reset/Kconfig +++ b/drivers/reset/Kconfig @@ -300,6 +300,19 @@ config RESET_SKY1 help This enables the reset controller for Cix Sky1. +config RESET_SKY1_AUDSS + tristate "Cix Sky1 Audio Subsystem reset controller" + depends on ARCH_CIX || COMPILE_TEST + select AUXILIARY_BUS + default CLK_SKY1_AUDSS + help + Support for block-level software reset lines in the Cix Sky1 + Audio Subsystem (AUDSS) Clock and Reset Unit. Sixteen reset + outputs for audio peripherals are controlled through the CRU + register map. The driver binds as an auxiliary device from + the AUDSS clock driver. Say M or Y here if you want to build + this driver. + config RESET_SOCFPGA bool "SoCFPGA Reset Driver" if COMPILE_TEST && (!ARM || !ARCH_INTEL_SOCFPGA) default ARM && ARCH_INTEL_SOCFPGA diff --git a/drivers/reset/Makefile b/drivers/reset/Makefile index 3e52569bd276..e81407ea3e29 100644 --- a/drivers/reset/Makefile +++ b/drivers/reset/Makefile @@ -39,6 +39,7 @@ obj-$(CONFIG_RESET_RZV2H_USB2PHY) += reset-rzv2h-usb2phy.o obj-$(CONFIG_RESET_SCMI) += reset-scmi.o obj-$(CONFIG_RESET_SIMPLE) += reset-simple.o obj-$(CONFIG_RESET_SKY1) += reset-sky1.o +obj-$(CONFIG_RESET_SKY1_AUDSS) += reset-sky1-audss.o obj-$(CONFIG_RESET_SOCFPGA) += reset-socfpga.o obj-$(CONFIG_RESET_SUNPLUS) += reset-sunplus.o obj-$(CONFIG_RESET_SUNXI) += reset-sunxi.o diff --git a/drivers/reset/reset-sky1-audss.c b/drivers/reset/reset-sky1-audss.c new file mode 100644 index 000000000000..d9735db681c7 --- /dev/null +++ b/drivers/reset/reset-sky1-audss.c @@ -0,0 +1,127 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Cix Sky1 Audio Subsystem reset controller driver + * + * Copyright 2026 Cix Technology Group Co., Ltd. + */ + +#include + +#include +#include +#include +#include +#include +#include + +#define SKY1_RESET_SLEEP_US 50 + +#define AUDSS_SW_RST 0x78 + +struct sky1_audss_reset_map { + unsigned int offset; + unsigned int mask; +}; + +struct sky1_audss_reset { + struct reset_controller_dev rcdev; + struct regmap *regmap; + const struct sky1_audss_reset_map *map; +}; + +static const struct sky1_audss_reset_map sky1_audss_reset_map[] = { + [AUDSS_I2S0_SW_RST] = { AUDSS_SW_RST, BIT(0) }, + [AUDSS_I2S1_SW_RST] = { AUDSS_SW_RST, BIT(1) }, + [AUDSS_I2S2_SW_RST] = { AUDSS_SW_RST, BIT(2) }, + [AUDSS_I2S3_SW_RST] = { AUDSS_SW_RST, BIT(3) }, + [AUDSS_I2S4_SW_RST] = { AUDSS_SW_RST, BIT(4) }, + [AUDSS_I2S5_SW_RST] = { AUDSS_SW_RST, BIT(5) }, + [AUDSS_I2S6_SW_RST] = { AUDSS_SW_RST, BIT(6) }, + [AUDSS_I2S7_SW_RST] = { AUDSS_SW_RST, BIT(7) }, + [AUDSS_I2S8_SW_RST] = { AUDSS_SW_RST, BIT(8) }, + [AUDSS_I2S9_SW_RST] = { AUDSS_SW_RST, BIT(9) }, + [AUDSS_WDT_SW_RST] = { AUDSS_SW_RST, BIT(10) }, + [AUDSS_TIMER_SW_RST] = { AUDSS_SW_RST, BIT(11) }, + [AUDSS_MB0_SW_RST] = { AUDSS_SW_RST, BIT(12) }, + [AUDSS_MB1_SW_RST] = { AUDSS_SW_RST, BIT(13) }, + [AUDSS_HDA_SW_RST] = { AUDSS_SW_RST, BIT(14) }, + [AUDSS_DMAC_SW_RST] = { AUDSS_SW_RST, BIT(15) }, +}; + +static struct sky1_audss_reset *to_sky1_audss_reset(struct reset_controller_dev *rcdev) +{ + return container_of(rcdev, struct sky1_audss_reset, rcdev); +} + +static int sky1_audss_reset_set(struct reset_controller_dev *rcdev, + unsigned long id, bool assert) +{ + struct sky1_audss_reset *priv = to_sky1_audss_reset(rcdev); + const struct sky1_audss_reset_map *signal = &priv->map[id]; + int ret; + + ret = regmap_assign_bits(priv->regmap, signal->offset, + signal->mask, !assert); + if (ret) + return ret; + + fsleep(SKY1_RESET_SLEEP_US); + return 0; +} + +static int sky1_audss_reset_assert(struct reset_controller_dev *rcdev, + unsigned long id) +{ + return sky1_audss_reset_set(rcdev, id, true); +} + +static int sky1_audss_reset_deassert(struct reset_controller_dev *rcdev, + unsigned long id) +{ + return sky1_audss_reset_set(rcdev, id, false); +} + +static const struct reset_control_ops sky1_audss_reset_ops = { + .assert = sky1_audss_reset_assert, + .deassert = sky1_audss_reset_deassert, +}; + +static int sky1_audss_reset_probe(struct auxiliary_device *adev, + const struct auxiliary_device_id *id) +{ + struct sky1_audss_reset *priv; + struct device *dev = &adev->dev; + + priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + priv->regmap = dev_get_regmap(dev->parent, NULL); + if (!priv->regmap) + return dev_err_probe(dev, -ENODEV, "failed to get parent regmap\n"); + + priv->map = sky1_audss_reset_map; + priv->rcdev.owner = THIS_MODULE; + priv->rcdev.nr_resets = ARRAY_SIZE(sky1_audss_reset_map); + priv->rcdev.ops = &sky1_audss_reset_ops; + priv->rcdev.of_node = dev->of_node; + priv->rcdev.dev = dev; + + return devm_reset_controller_register(dev, &priv->rcdev); +} + +static const struct auxiliary_device_id sky1_audss_reset_ids[] = { + { .name = "clk_sky1_audss.reset" }, + { } +}; +MODULE_DEVICE_TABLE(auxiliary, sky1_audss_reset_ids); + +static struct auxiliary_driver sky1_audss_reset_driver = { + .probe = sky1_audss_reset_probe, + .id_table = sky1_audss_reset_ids, +}; +module_auxiliary_driver(sky1_audss_reset_driver); + +MODULE_AUTHOR("Joakim Zhang "); +MODULE_DESCRIPTION("Cix Sky1 Audio Subsystem reset driver"); +MODULE_LICENSE("GPL"); From a2b1c025cf288473d9604be0b75d3b3362dac113 Mon Sep 17 00:00:00 2001 From: Joakim Zhang Date: Thu, 23 Jul 2026 17:08:08 +0800 Subject: [PATCH 080/112] arm64: dts: cix: sky1: add audss cru Add the AUDSS CRU device node providing clocks and software resets for audio subsystem peripherals. Signed-off-by: Joakim Zhang Signed-off-by: Brian Masney --- arch/arm64/boot/dts/cix/sky1.dtsi | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/arch/arm64/boot/dts/cix/sky1.dtsi b/arch/arm64/boot/dts/cix/sky1.dtsi index a77c52296ebd..9b2d969f6bfe 100644 --- a/arch/arm64/boot/dts/cix/sky1.dtsi +++ b/arch/arm64/boot/dts/cix/sky1.dtsi @@ -6,6 +6,10 @@ #include #include +#include +#include +#include +#include #include "sky1-power.h" / { @@ -558,6 +562,20 @@ cix,mbox-dir = "rx"; }; + audss_cru: clock-controller@7110000 { + compatible = "cix,sky1-audss-cru"; + reg = <0x0 0x07110000 0x0 0x10000>; + #clock-cells = <1>; + #reset-cells = <1>; + clocks = <&scmi_clk CLK_TREE_AUDIO_CLK0>, + <&scmi_clk CLK_TREE_AUDIO_CLK2>, + <&scmi_clk CLK_TREE_AUDIO_CLK4>, + <&scmi_clk CLK_TREE_AUDIO_CLK5>; + clock-names = "x8k", "x11k", "sys", "48m"; + power-domains = <&smc_devpd SKY1_PD_AUDIO>; + resets = <&s5_syscon SKY1_AUDIO_HIFI5_NOC_RESET_N>; + }; + mbox_sfh2ap: mailbox@8090000 { compatible = "cix,sky1-mbox"; reg = <0x0 0x08090000 0x0 0x10000>; From e6850b69b674604d6d6dec980655e5e45a177c9b Mon Sep 17 00:00:00 2001 From: Christian Marangi Date: Thu, 23 Jul 2026 21:01:30 +0200 Subject: [PATCH 081/112] dt-bindings: clock: airoha: Add additional reset for PCIe PERSTOUT Add additional reset to control PCIe PERSTOUT reset line for each of the 3 PCIe lines. Signed-off-by: Christian Marangi Acked-by: Krzysztof Kozlowski Signed-off-by: Brian Masney --- include/dt-bindings/reset/airoha,en7581-reset.h | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/include/dt-bindings/reset/airoha,en7581-reset.h b/include/dt-bindings/reset/airoha,en7581-reset.h index 6544a1790b83..25e75534daa9 100644 --- a/include/dt-bindings/reset/airoha,en7581-reset.h +++ b/include/dt-bindings/reset/airoha,en7581-reset.h @@ -62,5 +62,9 @@ #define EN7581_CPU_TIMER_RST 50 #define EN7581_PCIE_HB_RST 51 #define EN7581_XPON_MAC_RST 52 +/* RST_PCIC */ +#define EN7581_PCIC_PERSTOUT0_RST 53 +#define EN7581_PCIC_PERSTOUT1_RST 54 +#define EN7581_PCIC_PERSTOUT2_RST 55 #endif /* __DT_BINDINGS_RESET_CONTROLLER_AIROHA_EN7581_H_ */ From 6712f48eb3a15842925171441505fa2e7699c57e Mon Sep 17 00:00:00 2001 From: Christian Marangi Date: Thu, 23 Jul 2026 21:01:31 +0200 Subject: [PATCH 082/112] clk: en7523: add support for dedicated PCIe PERSTOUT reset Add support for resetting the PCIe lines with the PERSTOUT reset. These special reset are controlled by the PCIC register and are specific to each of the 3 PCIe lines. Contrary to the dedicated reset register where 0 deassert and 1 assert the reset line for the related bit, for PCIe PERSTOUT reset in the PCIC register, the deassert and assert value is swapped, with 1 for deassert and 0 for assert. (HW have these bit set as 1 by default after a cold boot) This is internally handled in the reset function. PCI enable/disable are updated to drop PERSTOUT bits in favor dedicated reset handling. Signed-off-by: Christian Marangi Reviewed-by: Brian Masney Signed-off-by: Brian Masney --- drivers/clk/clk-en7523.c | 36 ++++++++++++++++++++++++++---------- 1 file changed, 26 insertions(+), 10 deletions(-) diff --git a/drivers/clk/clk-en7523.c b/drivers/clk/clk-en7523.c index c450d89bfa96..5c45aacb6409 100644 --- a/drivers/clk/clk-en7523.c +++ b/drivers/clk/clk-en7523.c @@ -339,6 +339,7 @@ static const struct en_clk_desc en7581_base_clks[] = { static const u16 en7581_rst_ofs[] = { REG_RST_CTRL2, REG_RST_CTRL1, + REG_NP_SCU_PCIC, }; static const u16 en751221_rst_ofs[] = { @@ -451,6 +452,11 @@ static const u16 en7581_rst_map[] = { [EN7581_CPU_TIMER_RST] = RST_NR_PER_BANK + 28, [EN7581_PCIE_HB_RST] = RST_NR_PER_BANK + 29, [EN7581_XPON_MAC_RST] = RST_NR_PER_BANK + 31, + + /* RST_PCIC */ + [EN7581_PCIC_PERSTOUT0_RST] = 2 * RST_NR_PER_BANK + 29, + [EN7581_PCIC_PERSTOUT1_RST] = 2 * RST_NR_PER_BANK + 26, + [EN7581_PCIC_PERSTOUT2_RST] = 2 * RST_NR_PER_BANK + 16, }; static const u16 en751221_rst_map[] = { @@ -636,9 +642,7 @@ static int en7581_pci_enable(struct clk_hw *hw) void __iomem *np_base = cg->base; u32 val, mask; - mask = REG_PCI_CONTROL_REFCLK_EN0 | REG_PCI_CONTROL_REFCLK_EN1 | - REG_PCI_CONTROL_PERSTOUT1 | REG_PCI_CONTROL_PERSTOUT2 | - REG_PCI_CONTROL_PERSTOUT; + mask = REG_PCI_CONTROL_REFCLK_EN0 | REG_PCI_CONTROL_REFCLK_EN1; val = readl(np_base + REG_PCI_CONTROL); writel(val | mask, np_base + REG_PCI_CONTROL); @@ -651,9 +655,7 @@ static void en7581_pci_disable(struct clk_hw *hw) void __iomem *np_base = cg->base; u32 val, mask; - mask = REG_PCI_CONTROL_REFCLK_EN0 | REG_PCI_CONTROL_REFCLK_EN1 | - REG_PCI_CONTROL_PERSTOUT1 | REG_PCI_CONTROL_PERSTOUT2 | - REG_PCI_CONTROL_PERSTOUT; + mask = REG_PCI_CONTROL_REFCLK_EN0 | REG_PCI_CONTROL_REFCLK_EN1; val = readl(np_base + REG_PCI_CONTROL); writel(val & ~mask, np_base + REG_PCI_CONTROL); usleep_range(1000, 2000); @@ -755,11 +757,17 @@ static int en7523_reset_update(struct reset_controller_dev *rcdev, unsigned long id, bool assert) { struct en_rst_data *rst_data = container_of(rcdev, struct en_rst_data, rcdev); - void __iomem *addr = rst_data->base + rst_data->bank_ofs[id / RST_NR_PER_BANK]; + u32 offset = rst_data->bank_ofs[id / RST_NR_PER_BANK]; + void __iomem *addr = rst_data->base + offset; + bool inverted = false; u32 val; + /* For PCIC reset logic is inverted, 0:assert 1:deassert */ + if (offset == REG_NP_SCU_PCIC) + inverted = true; + val = readl(addr); - if (assert) + if (assert ^ inverted) val |= BIT(id % RST_NR_PER_BANK); else val &= ~BIT(id % RST_NR_PER_BANK); @@ -784,9 +792,17 @@ static int en7523_reset_status(struct reset_controller_dev *rcdev, unsigned long id) { struct en_rst_data *rst_data = container_of(rcdev, struct en_rst_data, rcdev); - void __iomem *addr = rst_data->base + rst_data->bank_ofs[id / RST_NR_PER_BANK]; + u32 offset = rst_data->bank_ofs[id / RST_NR_PER_BANK]; + void __iomem *addr = rst_data->base + offset; + bool inverted = false; + u32 val; - return !!(readl(addr) & BIT(id % RST_NR_PER_BANK)); + /* For PCIC reset logic is inverted, 0:assert 1:deassert */ + if (offset == REG_NP_SCU_PCIC) + inverted = true; + + val = readl(addr) & BIT(id % RST_NR_PER_BANK); + return inverted ? !val : !!val; } static int en7523_reset_xlate(struct reset_controller_dev *rcdev, From b698927accb1c974040dc6596c8df1d72682b2ed Mon Sep 17 00:00:00 2001 From: Suraj Gupta Date: Thu, 23 Jul 2026 18:08:32 +0530 Subject: [PATCH 083/112] clk: Add devm_clk_bulk_get_enable() devm_clk_bulk_get_optional_enable() gets, prepares and enables a set of clocks with device-managed cleanup, but treats every clock as optional: a missing clock is silently returned as NULL instead of failing. Consumers that need a fixed set of mandatory clocks enabled for the lifetime of the device currently have to open-code devm_clk_bulk_get() followed by clk_bulk_prepare_enable(), which loses the managed disable on unbind, or fall back to per-clock devm_clk_get_enabled() calls. Add devm_clk_bulk_get_enable() as the non-optional counterpart. The underlying __devm_clk_bulk_get_enable() helper already supports the required (optional = false) path, so only export a thin wrapper for it. Signed-off-by: Suraj Gupta Reviewed-by: Brian Masney Signed-off-by: Brian Masney --- .../driver-api/driver-model/devres.rst | 1 + drivers/clk/clk-devres.c | 7 ++++++ include/linux/clk.h | 23 +++++++++++++++++++ 3 files changed, 31 insertions(+) diff --git a/Documentation/driver-api/driver-model/devres.rst b/Documentation/driver-api/driver-model/devres.rst index 017fb155a5bc..5067500ded5c 100644 --- a/Documentation/driver-api/driver-model/devres.rst +++ b/Documentation/driver-api/driver-model/devres.rst @@ -248,6 +248,7 @@ CLOCK devm_clk_put() devm_clk_bulk_get() devm_clk_bulk_get_all() + devm_clk_bulk_get_enable() devm_clk_bulk_get_optional() devm_get_clk_from_child() devm_clk_hw_register() diff --git a/drivers/clk/clk-devres.c b/drivers/clk/clk-devres.c index 994d5bc5168b..49d4782991eb 100644 --- a/drivers/clk/clk-devres.c +++ b/drivers/clk/clk-devres.c @@ -222,6 +222,13 @@ err_clk_get: return ret; } +int __must_check devm_clk_bulk_get_enable(struct device *dev, int num_clks, + struct clk_bulk_data *clks) +{ + return __devm_clk_bulk_get_enable(dev, num_clks, clks, false); +} +EXPORT_SYMBOL_GPL(devm_clk_bulk_get_enable); + int __must_check devm_clk_bulk_get_optional_enable(struct device *dev, int num_clks, struct clk_bulk_data *clks) { diff --git a/include/linux/clk.h b/include/linux/clk.h index 993fd6e916c7..db9cf184fd99 100644 --- a/include/linux/clk.h +++ b/include/linux/clk.h @@ -502,6 +502,22 @@ int __must_check devm_clk_bulk_get(struct device *dev, int num_clks, */ int __must_check devm_clk_bulk_get_optional(struct device *dev, int num_clks, struct clk_bulk_data *clks); +/** + * devm_clk_bulk_get_enable - Get and enable bulk clocks (managed) + * @dev: device for clock "consumer" + * @num_clks: the number of clk_bulk_data + * @clks: pointer to the clk_bulk_data table of consumer + * + * Behaves the same as devm_clk_bulk_get() but also prepares and enables the + * clocks in one operation with management. The clks will automatically be + * disabled, unprepared and freed when the device is unbound. + * + * Return: 0 if all clocks specified in clk_bulk_data table are obtained and + * enabled successfully. Otherwise returns valid IS_ERR() condition containing + * errno. + */ +int __must_check devm_clk_bulk_get_enable(struct device *dev, int num_clks, + struct clk_bulk_data *clks); /** * devm_clk_bulk_get_optional_enable - Get and enable optional bulk clocks (managed) * @dev: device for clock "consumer" @@ -1052,6 +1068,13 @@ static inline int __must_check devm_clk_bulk_get_optional(struct device *dev, return 0; } +static inline int __must_check devm_clk_bulk_get_enable(struct device *dev, + int num_clks, + struct clk_bulk_data *clks) +{ + return 0; +} + static inline int __must_check devm_clk_bulk_get_optional_enable(struct device *dev, int num_clks, struct clk_bulk_data *clks) From 4e324f1bc00390508444256b01e8568b5ddbdae7 Mon Sep 17 00:00:00 2001 From: Sang-Heon Jeon Date: Fri, 24 Jul 2026 03:45:19 +0900 Subject: [PATCH 084/112] clk: remove conditional return with no effect Both branches of the check return the same value, so the check has no effect. Remove it and return the value directly. In the lmk04832 driver, the hardware sequence comments are moved above the final return by hand. This is the result of running the Coccinelle script from scripts/coccinelle/misc/cond_return_no_effect.cocci. Signed-off-by: Sang-Heon Jeon Reviewed-by: Brian Masney Signed-off-by: Brian Masney --- drivers/clk/clk-cs2000-cp.c | 6 +----- drivers/clk/clk-lmk04832.c | 12 ++++-------- 2 files changed, 5 insertions(+), 13 deletions(-) diff --git a/drivers/clk/clk-cs2000-cp.c b/drivers/clk/clk-cs2000-cp.c index 8800472ba63f..4de581771d7d 100644 --- a/drivers/clk/clk-cs2000-cp.c +++ b/drivers/clk/clk-cs2000-cp.c @@ -404,11 +404,7 @@ static int cs2000_enable(struct clk_hw *hw) if (ret < 0) return ret; - ret = cs2000_wait_pll_lock(priv); - if (ret < 0) - return ret; - - return ret; + return cs2000_wait_pll_lock(priv); } static void cs2000_disable(struct clk_hw *hw) diff --git a/drivers/clk/clk-lmk04832.c b/drivers/clk/clk-lmk04832.c index 9bf86caad829..f006401c1586 100644 --- a/drivers/clk/clk-lmk04832.c +++ b/drivers/clk/clk-lmk04832.c @@ -822,13 +822,6 @@ static int lmk04832_sclk_sync_sequence(struct lmk04832 *lmk) if (ret) return ret; - ret = regmap_update_bits(lmk->regmap, LMK04832_REG_SYNC, - LMK04832_BIT_SYNC_MODE, - FIELD_PREP(LMK04832_BIT_SYNC_MODE, - lmk->sync_mode)); - if (ret) - return ret; - /* * 9. (optional) if SCLKx_y_DIS_MODE was used to mute SYSREF outputs * during the SYNC event, restore SCLKx_y_DIS_MODE=0 for active state, @@ -843,7 +836,10 @@ static int lmk04832_sclk_sync_sequence(struct lmk04832 *lmk) * SYNC pulse to delay the output by some number of VCO counts). */ - return ret; + return regmap_update_bits(lmk->regmap, LMK04832_REG_SYNC, + LMK04832_BIT_SYNC_MODE, + FIELD_PREP(LMK04832_BIT_SYNC_MODE, + lmk->sync_mode)); } static int lmk04832_sclk_is_enabled(struct clk_hw *hw) From ef6ca9c4df02753fac81d70ae4c3f02bf784a4f0 Mon Sep 17 00:00:00 2001 From: Jerome Brunet Date: Fri, 24 Jul 2026 11:03:52 +0200 Subject: [PATCH 085/112] clk: mvebu: clean-up simple provider misuse of the consumer API Clock provider should not be using the consumer interface. In other words, a provider should not be dealing with struct clk. This change targets occurrences for which the provider uses the consumer interface and corresponding clk_hw interface exist. Reviewed-by: Brian Masney Signed-off-by: Jerome Brunet Reviewed-by: Thierry Reding Signed-off-by: Brian Masney --- drivers/clk/mvebu/clk-cpu.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/clk/mvebu/clk-cpu.c b/drivers/clk/mvebu/clk-cpu.c index 26d52ecaac50..24196e288fe2 100644 --- a/drivers/clk/mvebu/clk-cpu.c +++ b/drivers/clk/mvebu/clk-cpu.c @@ -153,7 +153,7 @@ static int clk_cpu_on_set_rate(struct clk_hw *hwclk, unsigned long rate, static int clk_cpu_set_rate(struct clk_hw *hwclk, unsigned long rate, unsigned long parent_rate) { - if (__clk_is_enabled(hwclk->clk)) + if (clk_hw_is_enabled(hwclk)) return clk_cpu_on_set_rate(hwclk, rate, parent_rate); else return clk_cpu_off_set_rate(hwclk, rate, parent_rate); From 227d023952058976bf336a36079d49b8a50c9b02 Mon Sep 17 00:00:00 2001 From: Jerome Brunet Date: Fri, 24 Jul 2026 11:03:56 +0200 Subject: [PATCH 086/112] clk: st: clean-up simple provider misuse of the consumer API Clock provider should not be using the consumer interface. In other words, a provider should not be dealing with struct clk. This change targets occurrences for which the provider uses the consumer interface and corresponding clk_hw interface exist. Reviewed-by: Brian Masney Signed-off-by: Jerome Brunet Reviewed-by: Thierry Reding Signed-off-by: Brian Masney --- drivers/clk/st/clkgen-pll.c | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/drivers/clk/st/clkgen-pll.c b/drivers/clk/st/clkgen-pll.c index 9eb2cb83fa69..17551c6a879a 100644 --- a/drivers/clk/st/clkgen-pll.c +++ b/drivers/clk/st/clkgen-pll.c @@ -260,7 +260,7 @@ static int __clkgen_pll_enable(struct clk_hw *hw) if (pll->data->switch2pll_en) CLKGEN_WRITE(pll, switch2pll, 0); - pr_debug("%s:%s enabled\n", __clk_get_name(hw->clk), __func__); + pr_debug("%s:%s enabled\n", clk_hw_get_name(hw), __func__); } return ret; @@ -295,7 +295,7 @@ static void __clkgen_pll_disable(struct clk_hw *hw) CLKGEN_WRITE(pll, pdn_ctrl, 1); - pr_debug("%s:%s disabled\n", __clk_get_name(hw->clk), __func__); + pr_debug("%s:%s disabled\n", clk_hw_get_name(hw), __func__); } static void clkgen_pll_disable(struct clk_hw *hw) @@ -405,14 +405,14 @@ static int stm_pll3200c32_determine_rate(struct clk_hw *hw, &req->rate); else { pr_debug("%s: %s rate %ld Invalid\n", __func__, - __clk_get_name(hw->clk), req->rate); + clk_hw_get_name(hw), req->rate); req->rate = 0; return 0; } pr_debug("%s: %s new rate %ld [ndiv=%u] [idf=%u]\n", - __func__, __clk_get_name(hw->clk), + __func__, clk_hw_get_name(hw), req->rate, (unsigned int)params.ndiv, (unsigned int)params.idf); @@ -434,7 +434,7 @@ static int set_rate_stm_pll3200c32(struct clk_hw *hw, unsigned long rate, clk_pll3200c32_get_rate(parent_rate, ¶ms, &hwrate); pr_debug("%s: %s new rate %ld [ndiv=0x%x] [idf=0x%x]\n", - __func__, __clk_get_name(hw->clk), + __func__, clk_hw_get_name(hw), hwrate, (unsigned int)params.ndiv, (unsigned int)params.idf); @@ -547,7 +547,7 @@ static unsigned long recalc_stm_pll4600c28(struct clk_hw *hw, clk_pll4600c28_get_rate(parent_rate, ¶ms, &rate); - pr_debug("%s:%s rate %lu\n", __clk_get_name(hw->clk), __func__, rate); + pr_debug("%s:%s rate %lu\n", clk_hw_get_name(hw), __func__, rate); return rate; } @@ -562,14 +562,14 @@ static int stm_pll4600c28_determine_rate(struct clk_hw *hw, &req->rate); } else { pr_debug("%s: %s rate %ld Invalid\n", __func__, - __clk_get_name(hw->clk), req->rate); + clk_hw_get_name(hw), req->rate); req->rate = 0; return 0; } pr_debug("%s: %s new rate %ld [ndiv=%u] [idf=%u]\n", - __func__, __clk_get_name(hw->clk), + __func__, clk_hw_get_name(hw), req->rate, (unsigned int)params.ndiv, (unsigned int)params.idf); @@ -591,12 +591,12 @@ static int set_rate_stm_pll4600c28(struct clk_hw *hw, unsigned long rate, clk_pll4600c28_get_rate(parent_rate, ¶ms, &hwrate); } else { pr_debug("%s: %s rate %ld Invalid\n", __func__, - __clk_get_name(hw->clk), rate); + clk_hw_get_name(hw), rate); return -EINVAL; } pr_debug("%s: %s new rate %ld [ndiv=0x%x] [idf=0x%x]\n", - __func__, __clk_get_name(hw->clk), + __func__, clk_hw_get_name(hw), hwrate, (unsigned int)params.ndiv, (unsigned int)params.idf); From 3ed463b69be8120d8f674051f0fe3304ef9839cc Mon Sep 17 00:00:00 2001 From: Jerome Brunet Date: Fri, 24 Jul 2026 11:03:57 +0200 Subject: [PATCH 087/112] clk: tegra: clean-up simple provider misuse of the consumer API Clock provider should not be using the consumer interface. In other words, a provider should not be dealing with struct clk. This change targets occurrences for which the provider uses the consumer interface and corresponding clk_hw interface exist. Reviewed-by: Brian Masney Signed-off-by: Jerome Brunet Reviewed-by: Thierry Reding Signed-off-by: Brian Masney --- drivers/clk/tegra/clk-tegra210.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/clk/tegra/clk-tegra210.c b/drivers/clk/tegra/clk-tegra210.c index 0c86cff719e3..df1d7b471745 100644 --- a/drivers/clk/tegra/clk-tegra210.c +++ b/drivers/clk/tegra/clk-tegra210.c @@ -1460,7 +1460,7 @@ static int tegra210_pllx_dyn_ramp(struct tegra_clk_pll *pllx, udelay(1); pr_debug("%s: dynamic ramp to m = %u n = %u p = %u, Fout = %lu kHz\n", - __clk_get_name(pllx->hw.clk), cfg->m, cfg->n, cfg->p, + clk_hw_get_name(&pllx->hw), cfg->m, cfg->n, cfg->p, cfg->input_rate / cfg->m * cfg->n / pllx->params->pdiv_tohw[cfg->p].pdiv / 1000); From 442d24ce7f4c09a7d14b949d7a6b640bd63cc4e0 Mon Sep 17 00:00:00 2001 From: Chen-Yu Tsai Date: Tue, 28 Jul 2026 17:17:59 +0800 Subject: [PATCH 088/112] dt-bindings: clock: mediatek: Add mt8173 mfgtop The MFG (GPU) block on the MT8173 has a small glue layer, named MFG_TOP in the datasheet, that contains clock gates, some power sequence signal delays, and other unknown registers that get toggled when the GPU is powered on. The clock gates are exposed as clocks provided by a clock controller, while the power sequencing bits are exposed as one singular power domain. Reviewed-by: Conor Dooley Reviewed-by: AngeloGioacchino Del Regno Signed-off-by: Chen-Yu Tsai Signed-off-by: Brian Masney --- .../clock/mediatek,mt8173-mfgtop.yaml | 70 +++++++++++++++++++ include/dt-bindings/clock/mt8173-clk.h | 7 ++ 2 files changed, 77 insertions(+) create mode 100644 Documentation/devicetree/bindings/clock/mediatek,mt8173-mfgtop.yaml diff --git a/Documentation/devicetree/bindings/clock/mediatek,mt8173-mfgtop.yaml b/Documentation/devicetree/bindings/clock/mediatek,mt8173-mfgtop.yaml new file mode 100644 index 000000000000..03db1ee9e594 --- /dev/null +++ b/Documentation/devicetree/bindings/clock/mediatek,mt8173-mfgtop.yaml @@ -0,0 +1,70 @@ +# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause) +%YAML 1.2 +--- + +$id: http://devicetree.org/schemas/clock/mediatek,mt8173-mfgtop.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: MediaTek MT8173 MFG TOP controller + +maintainers: + - AngeloGioacchino Del Regno + +description: + The MFG TOP glue layer controls various signals going to the MFG (GPU) + block on the MT8173. + +properties: + compatible: + const: mediatek,mt8173-mfgtop + + reg: + maxItems: 1 + + clocks: + maxItems: 4 + + clock-names: + items: + - const: sys + - const: mem + - const: core + - const: clk26m + + power-domains: + maxItems: 1 + + '#clock-cells': + const: 1 + + '#power-domain-cells': + const: 0 + +required: + - compatible + - reg + - clocks + - clock-names + - power-domains + - '#clock-cells' + - '#power-domain-cells' + +additionalProperties: false + +examples: + - | + #include + #include + + clock-controller@13fff000 { + compatible = "mediatek,mt8173-mfgtop"; + reg = <0x13fff000 0x1000>; + clocks = <&topckgen CLK_TOP_AXI_MFG_IN_SEL>, + <&topckgen CLK_TOP_MEM_MFG_IN_SEL>, + <&topckgen CLK_TOP_MFG_SEL>, + <&clk26m>; + clock-names = "sys", "mem", "core", "clk26m"; + power-domains = <&spm MT8173_POWER_DOMAIN_MFG>; + #clock-cells = <1>; + #power-domain-cells = <0>; + }; diff --git a/include/dt-bindings/clock/mt8173-clk.h b/include/dt-bindings/clock/mt8173-clk.h index 3d00c98b9654..89e982f771db 100644 --- a/include/dt-bindings/clock/mt8173-clk.h +++ b/include/dt-bindings/clock/mt8173-clk.h @@ -243,6 +243,13 @@ #define CLK_IMG_FD 7 #define CLK_IMG_NR_CLK 8 +/* MFG_SYS */ + +#define CLK_MFG_AXI 0 +#define CLK_MFG_MEM 1 +#define CLK_MFG_G3D 2 +#define CLK_MFG_26M 3 + /* MM_SYS */ #define CLK_MM_SMI_COMMON 1 From ebd0b73d2137c2277440da04124a0ce7011b2d1a Mon Sep 17 00:00:00 2001 From: Chen-Yu Tsai Date: Tue, 28 Jul 2026 17:18:00 +0800 Subject: [PATCH 089/112] clk: mediatek: Add mt8173-mfgtop driver The MFG (GPU) block on the MT8173 has a small glue layer, named MFG_TOP in the datasheet, that contains clock gates, some power sequence signal delays, and other unknown registers that get toggled when the GPU is powered on. The clock gates are exposed as clocks provided by a clock controller, while the power sequencing bits are exposed as one singular power domain. Tested-by: Icenowy Zheng Reviewed-by: Brian Masney Reviewed-by: AngeloGioacchino Del Regno Signed-off-by: Chen-Yu Tsai Signed-off-by: Brian Masney --- drivers/clk/mediatek/Kconfig | 10 + drivers/clk/mediatek/Makefile | 1 + drivers/clk/mediatek/clk-mt8173-mfgtop.c | 242 +++++++++++++++++++++++ 3 files changed, 253 insertions(+) create mode 100644 drivers/clk/mediatek/clk-mt8173-mfgtop.c diff --git a/drivers/clk/mediatek/Kconfig b/drivers/clk/mediatek/Kconfig index 2c09fd729bab..8eea3766a910 100644 --- a/drivers/clk/mediatek/Kconfig +++ b/drivers/clk/mediatek/Kconfig @@ -537,6 +537,16 @@ config COMMON_CLK_MT8173_IMGSYS help This driver supports MediaTek MT8173 imgsys clocks. +config COMMON_CLK_MT8173_MFGTOP + tristate "Clock and power driver for MediaTek MT8173 mfgtop" + depends on COMMON_CLK_MT8173 + depends on PM + default COMMON_CLK_MT8173 + select PM_GENERIC_DOMAINS + select PM_GENERIC_DOMAINS_OF + help + This driver supports MediaTek MT8173 mfgtop clocks and power domain. + config COMMON_CLK_MT8173_MMSYS tristate "Clock driver for MediaTek MT8173 mmsys" depends on COMMON_CLK_MT8173 diff --git a/drivers/clk/mediatek/Makefile b/drivers/clk/mediatek/Makefile index d8736a060dbd..892a54eeb281 100644 --- a/drivers/clk/mediatek/Makefile +++ b/drivers/clk/mediatek/Makefile @@ -82,6 +82,7 @@ obj-$(CONFIG_COMMON_CLK_MT8167_VDECSYS) += clk-mt8167-vdec.o obj-$(CONFIG_COMMON_CLK_MT8173) += clk-mt8173-apmixedsys.o clk-mt8173-infracfg.o \ clk-mt8173-pericfg.o clk-mt8173-topckgen.o obj-$(CONFIG_COMMON_CLK_MT8173_IMGSYS) += clk-mt8173-img.o +obj-$(CONFIG_COMMON_CLK_MT8173_MFGTOP) += clk-mt8173-mfgtop.o obj-$(CONFIG_COMMON_CLK_MT8173_MMSYS) += clk-mt8173-mm.o obj-$(CONFIG_COMMON_CLK_MT8173_VDECSYS) += clk-mt8173-vdecsys.o obj-$(CONFIG_COMMON_CLK_MT8173_VENCSYS) += clk-mt8173-vencsys.o diff --git a/drivers/clk/mediatek/clk-mt8173-mfgtop.c b/drivers/clk/mediatek/clk-mt8173-mfgtop.c new file mode 100644 index 000000000000..5669ca9954dc --- /dev/null +++ b/drivers/clk/mediatek/clk-mt8173-mfgtop.c @@ -0,0 +1,242 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) 2024 Google LLC + * Author: Chen-Yu Tsai + * + * Based on driver in downstream ChromeOS v5.15 kernel. + * + * Copyright (c) 2014 MediaTek Inc. + * Author: Chiawen Lee + */ + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "clk-gate.h" +#include "clk-mtk.h" + +static const struct mtk_gate_regs mfg_cg_regs = { + .sta_ofs = 0x0000, + .clr_ofs = 0x0008, + .set_ofs = 0x0004, +}; + +#define GATE_MFG(_id, _name, _parent, _shift, _flags) \ + GATE_MTK_FLAGS(_id, _name, _parent, &mfg_cg_regs, _shift, &mtk_clk_gate_ops_setclr, _flags) + +/* TODO: The block actually has dividers for the core and mem clocks. */ +static const struct mtk_gate mfg_clks[] = { + GATE_MFG(CLK_MFG_AXI, "mfg_axi", "axi_mfg_in_sel", 0, CLK_SET_RATE_PARENT), + GATE_MFG(CLK_MFG_MEM, "mfg_mem", "mem_mfg_in_sel", 1, CLK_SET_RATE_PARENT), + GATE_MFG(CLK_MFG_G3D, "mfg_g3d", "mfg_sel", 2, CLK_SET_RATE_PARENT), + GATE_MFG(CLK_MFG_26M, "mfg_26m", "clk26m", 3, 0), +}; + +struct mt8173_mfgtop_data { + struct clk_hw_onecell_data *clk_data; + struct regmap *regmap; + struct generic_pm_domain genpd; + struct of_phandle_args parent_pd, child_pd; + struct clk *clk_26m; +}; + +/* Delay count in clock cycles */ +#define MFG_ACTIVE_POWER_CON0 0x24 + #define RST_B_DELAY_CNT GENMASK(7, 0) /* pwr_rst_b de-assert delay during power-up */ + #define CLK_EN_DELAY_CNT GENMASK(15, 8) /* CLK_DIS deassert delay during power-up */ + #define CLK_DIS_DELAY_CNT GENMASK(23, 16) /* CLK_DIS assert delay during power-down */ + #define FORCE_ABORT BIT(30) /* write 1 to force abort a power event */ + #define ACTIVE_PWRCTL_EN BIT(31) /* enable ACTIVE_POWER */ + +#define MFG_ACTIVE_POWER_CON1 0x28 + #define PWR_ON_S_DELAY_CNT GENMASK(7, 0) /* pwr_on_s assert delay during power-up */ + #define ISO_DELAY_CNT GENMASK(15, 8) /* ISO assert delay during power-down */ + #define ISOOFF_DELAY_CNT GENMASK(23, 16) /* ISO de-assert delay during power-up */ + #define RST_DELAY_CNT GENMASK(31, 24) /* pwr_rsb_b assert delay during power-down */ + +static int clk_mt8173_mfgtop_power_on(struct generic_pm_domain *domain) +{ + struct mt8173_mfgtop_data *data = container_of(domain, struct mt8173_mfgtop_data, genpd); + int ret; + + /* drives internal power management */ + ret = clk_prepare_enable(data->clk_26m); + if (ret) + return ret; + + /* Power on/off delays for various signals */ + regmap_write(data->regmap, MFG_ACTIVE_POWER_CON0, + FIELD_PREP(RST_B_DELAY_CNT, 77) | + FIELD_PREP(CLK_EN_DELAY_CNT, 61) | + FIELD_PREP(CLK_DIS_DELAY_CNT, 60) | + FIELD_PREP(ACTIVE_PWRCTL_EN, 0)); + regmap_write(data->regmap, MFG_ACTIVE_POWER_CON1, + FIELD_PREP(PWR_ON_S_DELAY_CNT, 11) | + FIELD_PREP(ISO_DELAY_CNT, 68) | + FIELD_PREP(ISOOFF_DELAY_CNT, 69) | + FIELD_PREP(RST_DELAY_CNT, 77)); + + /* Magic numbers related to core switch sequence and delays */ + regmap_write(data->regmap, 0xe0, 0x7a710184); + regmap_write(data->regmap, 0xe4, 0x835f6856); + regmap_write(data->regmap, 0xe8, 0x002b0234); + regmap_write(data->regmap, 0xec, 0x80000000); + regmap_write(data->regmap, 0xa0, 0x08000000); + + return 0; +} + +static int clk_mt8173_mfgtop_power_off(struct generic_pm_domain *domain) +{ + struct mt8173_mfgtop_data *data = container_of(domain, struct mt8173_mfgtop_data, genpd); + + /* Magic numbers related to core switch sequence and delays */ + regmap_write(data->regmap, 0xec, 0); + + /* drives internal power management */ + clk_disable_unprepare(data->clk_26m); + + return 0; +} + +static int clk_mt8173_mfgtop_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct device_node *node = dev->of_node; + struct mt8173_mfgtop_data *data; + int ret; + + data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL); + if (!data) + return -ENOMEM; + + platform_set_drvdata(pdev, data); + + data->clk_data = mtk_devm_alloc_clk_data(dev, ARRAY_SIZE(mfg_clks)); + if (!data->clk_data) + return -ENOMEM; + + /* MTK clock gates also uses regmap */ + data->regmap = device_node_to_regmap(node); + if (IS_ERR(data->regmap)) + return dev_err_probe(dev, PTR_ERR(data->regmap), "Failed to get regmap\n"); + + data->child_pd.np = node; + data->child_pd.args_count = 0; + ret = of_parse_phandle_with_args(node, "power-domains", "#power-domain-cells", 0, + &data->parent_pd); + if (ret) + return dev_err_probe(dev, ret, "Failed to parse power domain\n"); + + devm_pm_runtime_enable(dev); + /* + * Do a pm_runtime_resume_and_get() to workaround a possible + * deadlock between clk_register() and the genpd framework. + */ + ret = pm_runtime_resume_and_get(dev); + if (ret) { + dev_err_probe(dev, ret, "Failed to runtime resume device\n"); + goto put_of_node; + } + + ret = mtk_clk_register_gates(dev, node, mfg_clks, ARRAY_SIZE(mfg_clks), + data->clk_data); + if (ret) { + dev_err_probe(dev, ret, "Failed to register clock gates\n"); + goto put_pm_runtime; + } + + data->clk_26m = clk_hw_get_clk(data->clk_data->hws[CLK_MFG_26M], "26m"); + if (IS_ERR(data->clk_26m)) { + ret = dev_err_probe(dev, PTR_ERR(data->clk_26m), "Failed to get 26 MHz clock\n"); + goto unregister_clks; + } + + ret = of_clk_add_hw_provider(node, of_clk_hw_onecell_get, data->clk_data); + if (ret) { + dev_err_probe(dev, ret, "Failed to add clk OF provider\n"); + goto put_26m_clk; + } + + data->genpd.name = "mfg-top"; + data->genpd.power_on = clk_mt8173_mfgtop_power_on; + data->genpd.power_off = clk_mt8173_mfgtop_power_off; + ret = pm_genpd_init(&data->genpd, NULL, true); + if (ret) { + dev_err_probe(dev, ret, "Failed to add power domain\n"); + goto del_clk_provider; + } + + ret = of_genpd_add_provider_simple(node, &data->genpd); + if (ret) { + dev_err_probe(dev, ret, "Failed to add power domain OF provider\n"); + goto remove_pd; + } + + ret = of_genpd_add_subdomain(&data->parent_pd, &data->child_pd); + if (ret) { + dev_err_probe(dev, ret, "Failed to link PM domains\n"); + goto del_pd_provider; + } + + pm_runtime_put(dev); + return 0; + +del_pd_provider: + of_genpd_del_provider(node); +remove_pd: + pm_genpd_remove(&data->genpd); +del_clk_provider: + of_clk_del_provider(node); +put_26m_clk: + clk_put(data->clk_26m); +unregister_clks: + mtk_clk_unregister_gates(mfg_clks, ARRAY_SIZE(mfg_clks), data->clk_data); +put_pm_runtime: + pm_runtime_put_sync(dev); +put_of_node: + of_node_put(data->parent_pd.np); + return ret; +} + +static void clk_mt8173_mfgtop_remove(struct platform_device *pdev) +{ + struct mt8173_mfgtop_data *data = platform_get_drvdata(pdev); + struct device_node *node = pdev->dev.of_node; + + of_genpd_remove_subdomain(&data->parent_pd, &data->child_pd); + of_genpd_del_provider(node); + pm_genpd_remove(&data->genpd); + of_clk_del_provider(node); + clk_put(data->clk_26m); + mtk_clk_unregister_gates(mfg_clks, ARRAY_SIZE(mfg_clks), data->clk_data); + of_node_put(data->parent_pd.np); +} + +static const struct of_device_id of_match_clk_mt8173_mfgtop[] = { + { .compatible = "mediatek,mt8173-mfgtop" }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, of_match_clk_mt8173_mfgtop); + +static struct platform_driver clk_mt8173_mfgtop_drv = { + .probe = clk_mt8173_mfgtop_probe, + .remove = clk_mt8173_mfgtop_remove, + .driver = { + .name = "clk-mt8173-mfgtop", + .of_match_table = of_match_clk_mt8173_mfgtop, + }, +}; +module_platform_driver(clk_mt8173_mfgtop_drv); + +MODULE_DESCRIPTION("MediaTek MT8173 mfgtop clock driver"); +MODULE_LICENSE("GPL"); From 51be18310cd82fc4f1692d9cb7c4f53d26cacd74 Mon Sep 17 00:00:00 2001 From: Ryan Chen Date: Wed, 24 Jun 2026 13:44:59 +0800 Subject: [PATCH 090/112] dt-bindings: clock: ast2700: add PECI clock Add SCU1_CLK_PECI for the SoC1 PECI controller clock source, and SCU1_CLK_HPLL_DIV4 which serves as one of the PECI clock mux parents. Signed-off-by: Ryan Chen Acked-by: Conor Dooley Signed-off-by: Brian Masney --- include/dt-bindings/clock/aspeed,ast2700-scu.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/include/dt-bindings/clock/aspeed,ast2700-scu.h b/include/dt-bindings/clock/aspeed,ast2700-scu.h index bacf712e8e04..138f78ce5f07 100644 --- a/include/dt-bindings/clock/aspeed,ast2700-scu.h +++ b/include/dt-bindings/clock/aspeed,ast2700-scu.h @@ -163,5 +163,7 @@ #define SCU1_CLK_GATE_PORTDUSB2CLK 85 #define SCU1_CLK_GATE_LTPI1TXCLK 86 #define SCU1_CLK_I3C 87 +#define SCU1_CLK_HPLL_DIV4 88 +#define SCU1_CLK_PECI 89 #endif From 5ffa9d308c7a30192f1b05ee0bfd9e1976493a54 Mon Sep 17 00:00:00 2001 From: Ryan Chen Date: Wed, 24 Jun 2026 13:45:00 +0800 Subject: [PATCH 091/112] clk: aspeed: add AST2700 PECI clock Register the SoC1 PECI clock as a mux selected by SCU1_CLK_SEL2 bit 16, choosing between the 25MHz CLKIN and HPLL/4. Add the HPLL/4 fixed factor clock to serve as the second mux parent. Signed-off-by: Ryan Chen Signed-off-by: Brian Masney --- drivers/clk/aspeed/clk-ast2700.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/drivers/clk/aspeed/clk-ast2700.c b/drivers/clk/aspeed/clk-ast2700.c index 8b7b382f6f3e..5f499ec0e1d1 100644 --- a/drivers/clk/aspeed/clk-ast2700.c +++ b/drivers/clk/aspeed/clk-ast2700.c @@ -273,6 +273,13 @@ static const unsigned int sdclk_parent_ids[] = { static const struct clk_hw *sdclk_parent_hws[ARRAY_SIZE(sdclk_parent_ids)]; +static const unsigned int peciclk_parent_ids[] = { + SCU1_CLKIN, + SCU1_CLK_HPLL_DIV4 +}; + +static const struct clk_hw *peciclk_parent_hws[ARRAY_SIZE(peciclk_parent_ids)]; + #define FIXED_CLK(_id, _name, _rate) \ { \ .id = _id, \ @@ -458,6 +465,7 @@ static const struct ast2700_clk_info ast2700_scu1_clk_info[] __initconst = { PLL_CLK(SCU1_CLK_HPLL, CLK_PLL, "soc1-hpll", SCU1_CLKIN, SCU1_HPLL_PARAM), PLL_CLK(SCU1_CLK_APLL, CLK_PLL, "soc1-apll", SCU1_CLKIN, SCU1_APLL_PARAM), PLL_CLK(SCU1_CLK_DPLL, CLK_PLL, "soc1-dpll", SCU1_CLKIN, SCU1_DPLL_PARAM), + FIXED_FACTOR_CLK(SCU1_CLK_HPLL_DIV4, "soc1-hpll_div4", SCU1_CLK_HPLL, 1, 4), FIXED_FACTOR_CLK(SCU1_CLK_APLL_DIV2, "soc1-apll_div2", SCU1_CLK_APLL, 1, 2), FIXED_FACTOR_CLK(SCU1_CLK_APLL_DIV4, "soc1-apll_div4", SCU1_CLK_APLL, 1, 4), FIXED_FACTOR_CLK(SCU1_CLK_CAN, "canclk", SCU1_CLK_APLL, 1, 10), @@ -481,6 +489,8 @@ static const struct ast2700_clk_info ast2700_scu1_clk_info[] __initconst = { uxclk_parent_hws, SCU1_CLK_SEL2, 0, 2), MUX_CLK(SCU1_CLK_HUXCLK, "huxclk", uxclk_parent_ids, ARRAY_SIZE(uxclk_parent_ids), uxclk_parent_hws, SCU1_CLK_SEL2, 3, 2), + MUX_CLK(SCU1_CLK_PECI, "peciclk", peciclk_parent_ids, ARRAY_SIZE(peciclk_parent_ids), + peciclk_parent_hws, SCU1_CLK_SEL2, 16, 1), DIVIDER_CLK(SCU1_CLK_SDCLK, "sdclk", SCU1_CLK_SDMUX, SCU1_CLK_SEL1, 14, 3, ast2700_clk_div_table), PLL_CLK(SCU1_CLK_UARTX, CLK_UART_PLL, "uartxclk", SCU1_CLK_UXCLK, SCU1_UXCLK_CTRL), From 46e31d69a547530fbef499bde888b6bb45a1deac Mon Sep 17 00:00:00 2001 From: Dmitry Osipenko Date: Thu, 25 Jun 2026 11:30:52 +0300 Subject: [PATCH 092/112] clk: tegra: support 48MHz clock for pll_p_out1 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit UEFI on Surface2 sets pll_p_out1 to 48MHz which is not supported by kernel and causes BUG() early on. Add 48MHz clock support for pll_p_out1. Acked-by: Thierry Reding Signed-off-by: Dmitry Osipenko Signed-off-by: Jonas Schwöbel Signed-off-by: Svyatoslav Ryhel Signed-off-by: Brian Masney --- drivers/clk/tegra/clk-pll.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/clk/tegra/clk-pll.c b/drivers/clk/tegra/clk-pll.c index d86003b6d94f..adfb74f111ef 100644 --- a/drivers/clk/tegra/clk-pll.c +++ b/drivers/clk/tegra/clk-pll.c @@ -575,6 +575,7 @@ static int _calc_rate(struct clk_hw *hw, struct tegra_clk_pll_freq_table *cfg, break; case 9600000: case 28800000: + case 48000000: /* * PLL_P_OUT1 rate is not listed in PLLA table */ From d85028afefad94b4b2f9f7f36997f18d7e8984d2 Mon Sep 17 00:00:00 2001 From: Shubhrajyoti Datta Date: Mon, 29 Jun 2026 11:21:46 +0530 Subject: [PATCH 093/112] clk: clocking-wizard: Program PLL CP/RES and lock parameters on reconfig When dynamically reconfiguring the PLL multiplier, the charge pump (CP), loop filter resistance (RES), and lock timing parameters must be updated to match the new multiplier value. Without this, the PLL may fail to lock or exhibit jitter at certain multiply factors. Add lookup tables for CP/RES and lock timing values indexed by multiplier range, and program registers for chargepump and lock delay during dynamic reconfiguration. Signed-off-by: Shubhrajyoti Datta Signed-off-by: Brian Masney --- drivers/clk/xilinx/clk-xlnx-clock-wizard.c | 134 +++++++++++++++++++++ 1 file changed, 134 insertions(+) diff --git a/drivers/clk/xilinx/clk-xlnx-clock-wizard.c b/drivers/clk/xilinx/clk-xlnx-clock-wizard.c index 4a0136349f71..f5d14543167a 100644 --- a/drivers/clk/xilinx/clk-xlnx-clock-wizard.c +++ b/drivers/clk/xilinx/clk-xlnx-clock-wizard.c @@ -35,6 +35,10 @@ #define WZRD_DIVCLK 21 #define WZRD_CLKFBOUT_4 51 #define WZRD_CLKFBOUT_3 48 +#define WZRD_CP 18 +#define WZRD_LOCK 27 +#define WZRD_LOCK_REF_DLY 28 +#define WZRD_RES 30 #define WZRD_DUTY_CYCLE 2 #define WZRD_O_DIV 4 @@ -49,6 +53,10 @@ #define WZRD_P5FEDGE_SHIFT 15 #define WZRD_CLKOUT0_PREDIV2 BIT(11) #define WZRD_EDGE_SHIFT 8 +#define WZRD_CP_MASK GENMASK(3, 0) +#define WZRD_RES_MASK GENMASK(4, 1) +#define WZRD_LOCK_FB_DLY_MASK GENMASK(14, 10) +#define WZRD_LOCK_REF_DLY_LOCK_REF_DLY_MASK GENMASK(14, 10) #define WZRD_CLKFBOUT_MULT_SHIFT 8 #define WZRD_CLKFBOUT_MULT_MASK (0xff << WZRD_CLKFBOUT_MULT_SHIFT) @@ -440,6 +448,130 @@ static int clk_wzrd_reconfig(struct clk_wzrd_divider *divider, void __iomem *div WZRD_USEC_POLL, WZRD_TIMEOUT_POLL); } +struct wzrd_pll_filter { + u32 m_min; + u32 m_max; + u32 cp; + u32 res; +}; + +static const struct wzrd_pll_filter wzrd_cp_res_table[] = { + { 4, 4, 5, 15 }, + { 5, 5, 6, 15 }, + { 6, 6, 7, 15 }, + { 7, 7, 13, 15 }, + { 8, 8, 14, 15 }, + { 9, 9, 15, 15 }, + { 10, 10, 14, 7 }, + { 11, 11, 15, 7 }, + { 12, 13, 15, 11 }, + { 14, 14, 15, 13 }, + { 15, 15, 15, 3 }, + { 16, 17, 14, 5 }, + { 18, 19, 15, 5 }, + { 20, 21, 15, 9 }, + { 22, 23, 14, 14 }, + { 24, 26, 15, 14 }, + { 27, 28, 14, 1 }, + { 29, 33, 15, 1 }, + { 34, 37, 14, 6 }, + { 38, 44, 15, 6 }, + { 45, 57, 15, 10 }, + { 58, 63, 13, 12 }, + { 64, 70, 14, 12 }, + { 71, 86, 15, 12 }, + { 87, 94, 14, 2 }, + { 95, 145, 15, 2 }, + { 146, 163, 12, 4 }, + { 164, 181, 13, 4 }, + { 182, 200, 14, 4 }, + { 201, 273, 15, 4 }, + { 274, 300, 13, 8 }, + { 301, 325, 14, 8 }, + { 326, 432, 15, 8 }, +}; + +struct wzrd_lock_timing { + u32 m_min; + u32 m_max; + u32 ref_dly; + u32 fb_dly; + u32 lock_cnt; +}; + +static const struct wzrd_lock_timing wzrd_lock_table[] = { + { 4, 4, 4, 4, 1000 }, + { 5, 5, 6, 6, 1000 }, + { 6, 8, 7, 7, 1000 }, + { 9, 12, 8, 8, 1000 }, + { 13, 13, 10, 10, 1000 }, + { 14, 16, 13, 13, 1000 }, + { 17, 17, 16, 16, 825 }, + { 18, 18, 16, 16, 750 }, + { 19, 20, 16, 16, 700 }, + { 21, 21, 16, 16, 650 }, + { 22, 23, 16, 16, 625 }, + { 24, 24, 16, 16, 575 }, + { 25, 25, 16, 16, 550 }, + { 26, 28, 16, 16, 525 }, + { 29, 30, 16, 16, 475 }, + { 31, 31, 16, 16, 450 }, + { 32, 33, 16, 16, 425 }, + { 34, 36, 16, 16, 400 }, + { 37, 37, 16, 16, 375 }, + { 38, 40, 16, 16, 350 }, + { 41, 43, 16, 16, 325 }, + { 44, 47, 16, 16, 300 }, + { 48, 51, 16, 16, 275 }, + { 52, 205, 16, 16, 250 }, + { 206, 432, 16, 16, 225 }, +}; + +static void clk_wzrd_update_cp_res_lock(struct clk_wzrd_divider *divider, u32 m) +{ + u32 lock_ref_dly = 16, lock_fb_dly = 16, lock_cnt = 250, cp = 15, res = 15; + void __iomem *base = divider->base; + u32 reg; + int i; + + for (i = 0; i < ARRAY_SIZE(wzrd_cp_res_table); i++) { + if (m >= wzrd_cp_res_table[i].m_min && + m <= wzrd_cp_res_table[i].m_max) { + cp = wzrd_cp_res_table[i].cp; + res = wzrd_cp_res_table[i].res; + break; + } + } + + for (i = 0; i < ARRAY_SIZE(wzrd_lock_table); i++) { + if (m >= wzrd_lock_table[i].m_min && + m <= wzrd_lock_table[i].m_max) { + lock_ref_dly = wzrd_lock_table[i].ref_dly; + lock_fb_dly = wzrd_lock_table[i].fb_dly; + lock_cnt = wzrd_lock_table[i].lock_cnt; + break; + } + } + + reg = readl(base + WZRD_CLK_CFG_REG(1, WZRD_CP)); + reg &= ~WZRD_CP_MASK; + reg |= FIELD_PREP(WZRD_CP_MASK, cp); + writel(reg, base + WZRD_CLK_CFG_REG(1, WZRD_CP)); + + reg = readl(base + WZRD_CLK_CFG_REG(1, WZRD_RES)); + reg &= ~WZRD_RES_MASK; + reg |= FIELD_PREP(WZRD_RES_MASK, res); + writel(reg, base + WZRD_CLK_CFG_REG(1, WZRD_RES)); + + reg = lock_cnt | FIELD_PREP(WZRD_LOCK_FB_DLY_MASK, lock_fb_dly); + writel(reg, base + WZRD_CLK_CFG_REG(1, WZRD_LOCK)); + + reg = readl(base + WZRD_CLK_CFG_REG(1, WZRD_LOCK_REF_DLY)); + reg &= ~WZRD_LOCK_REF_DLY_LOCK_REF_DLY_MASK; + reg |= FIELD_PREP(WZRD_LOCK_REF_DLY_LOCK_REF_DLY_MASK, lock_ref_dly); + writel(reg, base + WZRD_CLK_CFG_REG(1, WZRD_LOCK_REF_DLY)); +} + static int clk_wzrd_dynamic_ver_all_nolock(struct clk_hw *hw, unsigned long rate, unsigned long parent_rate) { @@ -471,6 +603,8 @@ static int clk_wzrd_dynamic_ver_all_nolock(struct clk_hw *hw, unsigned long rate writel(regval1, divider->base + WZRD_CLK_CFG_REG(1, WZRD_CLKFBOUT_2)); + clk_wzrd_update_cp_res_lock(divider, m); + value2 = divider->d; edged = value2 % WZRD_DUTY_CYCLE; regh = (value2 / WZRD_DUTY_CYCLE); From fd0e3e4edea6a3e4da91be608ca2fb9b348f9e32 Mon Sep 17 00:00:00 2001 From: Akari Tsuyukusa Date: Mon, 29 Jun 2026 22:20:43 +0900 Subject: [PATCH 094/112] clk: mediatek: mt8135: Fix inverted gate control for devapc_ck The devapc_ck (CLK_INFRA_DEVAPC) on MT8135 is currently using "mtk_clk_gate_ops_setclr". However, checking the downstream kernel reveals that this clock is configured with set:enable and clr:disable making "mtk_clk_gate_ops_setclr_inv" the appropriate choice. But, it is strange that some downstream kernels are not like that. Amazon: INV ChromiumOS (early): not INV ChromiumOS 3.16 to 3.18-revew-v2: INV ChromiumOS 3.18-review-v3 and later (sent to kernel.org): not INV Link: https://github.com/amazon-oss/android_kernel_amazon_mt8135/blob/e2b2163a8ec4a7c8d961c89003a15b4ba0f0e371/arch/arm/mach-mt8135/mt_clkmgr.c#L1022-L1028 Link: https://github.com/mtk09422/chromiumos-third_party-kernel-mediatek/blob/4b624ee66e65d5dcd43fca36b313086efae8922a/arch/arm/boot/dts/mt8135-clocks.dtsi#L944-L948 Link: https://github.com/mtk09422/chromiumos-third_party-kernel-mediatek/blob/decd80c01d0dbe9f3afa8ff72273b5618b418180/drivers/clk/mediatek/clk-mt8135.c#L881-L882 Link: https://github.com/mtk09422/chromiumos-third_party-kernel-mediatek/blob/9b6f06cb7637100aa1a42e1fc351b36b384a1c54/drivers/clk/mediatek/clk-mt8135.c#L450 Fixes: a8aede794843 ("clk: mediatek: Add basic clocks for Mediatek MT8135.") Signed-off-by: Akari Tsuyukusa Signed-off-by: Brian Masney --- drivers/clk/mediatek/clk-mt8135.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/drivers/clk/mediatek/clk-mt8135.c b/drivers/clk/mediatek/clk-mt8135.c index 084e48a554c2..1d20e15608f7 100644 --- a/drivers/clk/mediatek/clk-mt8135.c +++ b/drivers/clk/mediatek/clk-mt8135.c @@ -409,6 +409,9 @@ static const struct mtk_gate_regs infra_cg_regs = { GATE_MTK_FLAGS(_id, _name, _parent, &infra_cg_regs, _shift, \ &mtk_clk_gate_ops_setclr, CLK_IS_CRITICAL) +#define GATE_ICG_INV(_id, _name, _parent, _shift) \ + GATE_MTK(_id, _name, _parent, &infra_cg_regs, _shift, &mtk_clk_gate_ops_setclr_inv) + static const struct mtk_gate infra_clks[] = { GATE_DUMMY(CLK_DUMMY, "infra_dummy"), GATE_ICG(CLK_INFRA_PMIC_WRAP, "pmic_wrap_ck", "axi_sel", 23), @@ -419,7 +422,7 @@ static const struct mtk_gate infra_clks[] = { GATE_ICG(CLK_INFRA_CPUM, "cpum_ck", "cpum_tck_in", 15), GATE_ICG_AO(CLK_INFRA_M4U, "m4u_ck", "mem_sel", 8), GATE_ICG(CLK_INFRA_MFGAXI, "mfgaxi_ck", "axi_sel", 7), - GATE_ICG(CLK_INFRA_DEVAPC, "devapc_ck", "axi_sel", 6), + GATE_ICG_INV(CLK_INFRA_DEVAPC, "devapc_ck", "axi_sel", 6), GATE_ICG(CLK_INFRA_AUDIO, "audio_ck", "aud_intbus_sel", 5), GATE_ICG(CLK_INFRA_MFG_BUS, "mfg_bus_ck", "axi_sel", 2), GATE_ICG(CLK_INFRA_SMI, "smi_ck", "smi_sel", 1), From 006ba909c240b832c876b7aed29c6beec9d1f5e5 Mon Sep 17 00:00:00 2001 From: Colin Foster Date: Mon, 29 Jun 2026 15:53:44 -0500 Subject: [PATCH 095/112] clk: clocking-wizard: fix clock difference detection Reviewed-by: Shubhrajyoti Datta Reviewed-by: Brian Masney Signed-off-by: Colin Foster Signed-off-by: Brian Masney --- drivers/clk/xilinx/clk-xlnx-clock-wizard.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/clk/xilinx/clk-xlnx-clock-wizard.c b/drivers/clk/xilinx/clk-xlnx-clock-wizard.c index f5d14543167a..05168de0c291 100644 --- a/drivers/clk/xilinx/clk-xlnx-clock-wizard.c +++ b/drivers/clk/xilinx/clk-xlnx-clock-wizard.c @@ -414,7 +414,7 @@ static int clk_wzrd_get_divisors(struct clk_hw *hw, unsigned long rate, if (o < omin || o > omax) continue; freq = DIV_ROUND_CLOSEST_ULL(vco_freq, o); - diff = freq - rate; + diff = abs(freq - rate); if (diff < best_diff) { best_diff = diff; divider->m = m >> 3; From 9329714414d1f3902fae7100adb358ec946484b5 Mon Sep 17 00:00:00 2001 From: Colin Foster Date: Mon, 29 Jun 2026 15:53:45 -0500 Subject: [PATCH 096/112] clk: clocking-wizard: optimize clock search When an exact clock match is found, there is no need to continue searching. This process was optimized for versal as part of 'commit e0a94c6bb5b4 ("clk: xilinx: Optimize divisor search in clk_wzrd_get_divisors_ver()")' but that logic wasn't applied to the non-versal driver. Apply this fast-exit logic to the non-versal driver. Reviewed-by: Shubhrajyoti Datta Reviewed-by: Brian Masney Signed-off-by: Colin Foster Signed-off-by: Brian Masney --- drivers/clk/xilinx/clk-xlnx-clock-wizard.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/clk/xilinx/clk-xlnx-clock-wizard.c b/drivers/clk/xilinx/clk-xlnx-clock-wizard.c index 05168de0c291..11010257ed2e 100644 --- a/drivers/clk/xilinx/clk-xlnx-clock-wizard.c +++ b/drivers/clk/xilinx/clk-xlnx-clock-wizard.c @@ -422,6 +422,9 @@ static int clk_wzrd_get_divisors(struct clk_hw *hw, unsigned long rate, divider->d = d; divider->o = o >> 3; divider->o_frac = (o - (divider->o << 3)) * 125; + + if (!diff) + return 0; } } } From 54ed94b709e03fb065d426f378e9d415fa7ee939 Mon Sep 17 00:00:00 2001 From: Colin Foster Date: Mon, 29 Jun 2026 15:53:46 -0500 Subject: [PATCH 097/112] clk: clocking-wizard: remove 20kHz restriction Reviewed-by: Shubhrajyoti Datta Reviewed-by: Brian Masney Signed-off-by: Colin Foster Signed-off-by: Brian Masney --- drivers/clk/xilinx/clk-xlnx-clock-wizard.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/clk/xilinx/clk-xlnx-clock-wizard.c b/drivers/clk/xilinx/clk-xlnx-clock-wizard.c index 11010257ed2e..1ee30a0c65b1 100644 --- a/drivers/clk/xilinx/clk-xlnx-clock-wizard.c +++ b/drivers/clk/xilinx/clk-xlnx-clock-wizard.c @@ -113,7 +113,6 @@ #define VER_WZRD_VCO_MAX 4320000000ULL #define VER_WZRD_O_MIN 2 #define VER_WZRD_O_MAX 511 -#define WZRD_MIN_ERR 20000 #define WZRD_FRAC_POINTS 1000 /* Get the mask from width */ @@ -428,7 +427,7 @@ static int clk_wzrd_get_divisors(struct clk_hw *hw, unsigned long rate, } } } - return best_diff < WZRD_MIN_ERR ? 0 : -EBUSY; + return best_diff != -1ULL ? 0 : -EBUSY; } static int clk_wzrd_reconfig(struct clk_wzrd_divider *divider, void __iomem *div_addr) From cf561283254dd9f55805800237e6f82f08d91497 Mon Sep 17 00:00:00 2001 From: Svyatoslav Ryhel Date: Thu, 25 Jun 2026 11:19:06 +0300 Subject: [PATCH 098/112] clk: tegra: set up proper EMC clock implementation for Tegra114 Remove current emc and emc_mux clocks and replace them with the proper EMC clock implementation for correct EMC driver support. Acked-by: Thierry Reding Signed-off-by: Svyatoslav Ryhel Reviewed-by: Mikko Perttunen Signed-off-by: Brian Masney --- drivers/clk/tegra/clk-tegra114.c | 39 ++++++++++++++++++++------------ 1 file changed, 25 insertions(+), 14 deletions(-) diff --git a/drivers/clk/tegra/clk-tegra114.c b/drivers/clk/tegra/clk-tegra114.c index a4f40533cc43..f1bca81b7776 100644 --- a/drivers/clk/tegra/clk-tegra114.c +++ b/drivers/clk/tegra/clk-tegra114.c @@ -620,10 +620,6 @@ static const char *mux_plld_out0_plld2_out0[] = { }; #define mux_plld_out0_plld2_out0_idx NULL -static const char *mux_pllmcp_clkm[] = { - "pll_m_out0", "pll_c_out0", "pll_p_out0", "clk_m", "pll_m_ud", -}; - static const struct clk_div_table pll_re_div_table[] = { { .val = 0, .div = 1 }, { .val = 1, .div = 2 }, @@ -670,7 +666,6 @@ static struct tegra_clk tegra114_clks[tegra_clk_max] __initdata = { [tegra_clk_csi] = { .dt_id = TEGRA114_CLK_CSI, .present = true }, [tegra_clk_i2c2] = { .dt_id = TEGRA114_CLK_I2C2, .present = true }, [tegra_clk_uartc] = { .dt_id = TEGRA114_CLK_UARTC, .present = true }, - [tegra_clk_emc] = { .dt_id = TEGRA114_CLK_EMC, .present = true }, [tegra_clk_usb2] = { .dt_id = TEGRA114_CLK_USB2, .present = true }, [tegra_clk_usb3] = { .dt_id = TEGRA114_CLK_USB3, .present = true }, [tegra_clk_vde_8] = { .dt_id = TEGRA114_CLK_VDE, .present = true }, @@ -1051,14 +1046,7 @@ static __init void tegra114_periph_clk_init(void __iomem *clk_base, periph_clk_enb_refcnt); clks[TEGRA114_CLK_CSUS] = clk; - /* emc mux */ - clk = clk_register_mux(NULL, "emc_mux", mux_pllmcp_clkm, - ARRAY_SIZE(mux_pllmcp_clkm), - CLK_SET_RATE_NO_REPARENT, - clk_base + CLK_SOURCE_EMC, - 29, 3, 0, &emc_lock); - - clk = tegra_clk_register_mc("mc", "emc_mux", clk_base + CLK_SOURCE_EMC, + clk = tegra_clk_register_mc("mc", "emc", clk_base + CLK_SOURCE_EMC, &emc_lock); clks[TEGRA114_CLK_MC] = clk; @@ -1324,6 +1312,26 @@ static int tegra114_reset_deassert(unsigned long id) return 0; } +static struct clk *tegra114_clk_src_onecell_get(struct of_phandle_args *clkspec, + void *data) +{ + struct clk_hw *hw; + struct clk *clk; + + clk = of_clk_src_onecell_get(clkspec, data); + if (IS_ERR(clk)) + return clk; + + hw = __clk_get_hw(clk); + + if (clkspec->args[0] == TEGRA114_CLK_EMC) { + if (!tegra124_clk_emc_driver_available(hw)) + return ERR_PTR(-EPROBE_DEFER); + } + + return clk; +} + static void __init tegra114_clock_init(struct device_node *np) { struct device_node *node; @@ -1371,7 +1379,10 @@ static void __init tegra114_clock_init(struct device_node *np) tegra_init_special_resets(1, tegra114_reset_assert, tegra114_reset_deassert); - tegra_add_of_provider(np, of_clk_src_onecell_get); + tegra_add_of_provider(np, tegra114_clk_src_onecell_get); + clks[TEGRA114_CLK_EMC] = tegra124_clk_register_emc(clk_base, np, + &emc_lock); + tegra_register_devclks(devclks, ARRAY_SIZE(devclks)); tegra_clk_apply_init_table = tegra114_clock_apply_init_table; From da3abd0445245077a1ac45b97ee0a59f8d300d31 Mon Sep 17 00:00:00 2001 From: "Rob Herring (Arm)" Date: Fri, 12 Jun 2026 16:52:51 -0500 Subject: [PATCH 099/112] 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 Link: https://patch.msgid.link/20260612215251.1888345-1-robh@kernel.org Signed-off-by: Claudiu Beznea --- 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 5d7ebde44469086af6c3486e0bbed2d8b5c522f1 Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Sat, 1 Aug 2026 21:55:41 +0200 Subject: [PATCH 100/112] dt-bindings: clock: Correct white-space style Correct a few white-space issues, like double space after '=' or before bracket '{' characters, which will be flagged by dt-check-style. No functional changes. Signed-off-by: Krzysztof Kozlowski Reviewed-by: Brian Masney Acked-by: Rob Herring (Arm) Signed-off-by: Stephen Boyd --- .../bindings/clock/mediatek,pericfg.yaml | 2 +- .../bindings/clock/st,stm32mp21-rcc.yaml | 140 +++++++-------- .../bindings/clock/st,stm32mp25-rcc.yaml | 160 +++++++++--------- .../bindings/clock/xlnx,clocking-wizard.yaml | 2 +- 4 files changed, 152 insertions(+), 152 deletions(-) diff --git a/Documentation/devicetree/bindings/clock/mediatek,pericfg.yaml b/Documentation/devicetree/bindings/clock/mediatek,pericfg.yaml index b98cf45efe2f..f0c8df4d10c8 100644 --- a/Documentation/devicetree/bindings/clock/mediatek,pericfg.yaml +++ b/Documentation/devicetree/bindings/clock/mediatek,pericfg.yaml @@ -65,7 +65,7 @@ examples: - | pericfg@10003000 { - compatible = "mediatek,mt7623-pericfg", "mediatek,mt2701-pericfg", "syscon"; + compatible = "mediatek,mt7623-pericfg", "mediatek,mt2701-pericfg", "syscon"; reg = <0x10003000 0x1000>; #clock-cells = <1>; #reset-cells = <1>; diff --git a/Documentation/devicetree/bindings/clock/st,stm32mp21-rcc.yaml b/Documentation/devicetree/bindings/clock/st,stm32mp21-rcc.yaml index 4368063c6709..e4d64d4b63c6 100644 --- a/Documentation/devicetree/bindings/clock/st,stm32mp21-rcc.yaml +++ b/Documentation/devicetree/bindings/clock/st,stm32mp21-rcc.yaml @@ -125,75 +125,75 @@ examples: reg = <0x44200000 0x10000>; #clock-cells = <1>; #reset-cells = <1>; - clocks = <&scmi_clk CK_SCMI_HSE>, - <&scmi_clk CK_SCMI_HSI>, - <&scmi_clk CK_SCMI_MSI>, - <&scmi_clk CK_SCMI_LSE>, - <&scmi_clk CK_SCMI_LSI>, - <&scmi_clk CK_SCMI_HSE_DIV2>, - <&scmi_clk CK_SCMI_ICN_HS_MCU>, - <&scmi_clk CK_SCMI_ICN_LS_MCU>, - <&scmi_clk CK_SCMI_ICN_SDMMC>, - <&scmi_clk CK_SCMI_ICN_DDR>, - <&scmi_clk CK_SCMI_ICN_DISPLAY>, - <&scmi_clk CK_SCMI_ICN_HSL>, - <&scmi_clk CK_SCMI_ICN_NIC>, - <&scmi_clk CK_SCMI_FLEXGEN_07>, - <&scmi_clk CK_SCMI_FLEXGEN_08>, - <&scmi_clk CK_SCMI_FLEXGEN_09>, - <&scmi_clk CK_SCMI_FLEXGEN_10>, - <&scmi_clk CK_SCMI_FLEXGEN_11>, - <&scmi_clk CK_SCMI_FLEXGEN_12>, - <&scmi_clk CK_SCMI_FLEXGEN_13>, - <&scmi_clk CK_SCMI_FLEXGEN_14>, - <&scmi_clk CK_SCMI_FLEXGEN_16>, - <&scmi_clk CK_SCMI_FLEXGEN_17>, - <&scmi_clk CK_SCMI_FLEXGEN_18>, - <&scmi_clk CK_SCMI_FLEXGEN_19>, - <&scmi_clk CK_SCMI_FLEXGEN_20>, - <&scmi_clk CK_SCMI_FLEXGEN_21>, - <&scmi_clk CK_SCMI_FLEXGEN_22>, - <&scmi_clk CK_SCMI_FLEXGEN_23>, - <&scmi_clk CK_SCMI_FLEXGEN_24>, - <&scmi_clk CK_SCMI_FLEXGEN_25>, - <&scmi_clk CK_SCMI_FLEXGEN_26>, - <&scmi_clk CK_SCMI_FLEXGEN_27>, - <&scmi_clk CK_SCMI_FLEXGEN_29>, - <&scmi_clk CK_SCMI_FLEXGEN_30>, - <&scmi_clk CK_SCMI_FLEXGEN_31>, - <&scmi_clk CK_SCMI_FLEXGEN_33>, - <&scmi_clk CK_SCMI_FLEXGEN_36>, - <&scmi_clk CK_SCMI_FLEXGEN_37>, - <&scmi_clk CK_SCMI_FLEXGEN_38>, - <&scmi_clk CK_SCMI_FLEXGEN_39>, - <&scmi_clk CK_SCMI_FLEXGEN_40>, - <&scmi_clk CK_SCMI_FLEXGEN_41>, - <&scmi_clk CK_SCMI_FLEXGEN_42>, - <&scmi_clk CK_SCMI_FLEXGEN_43>, - <&scmi_clk CK_SCMI_FLEXGEN_44>, - <&scmi_clk CK_SCMI_FLEXGEN_45>, - <&scmi_clk CK_SCMI_FLEXGEN_46>, - <&scmi_clk CK_SCMI_FLEXGEN_47>, - <&scmi_clk CK_SCMI_FLEXGEN_48>, - <&scmi_clk CK_SCMI_FLEXGEN_50>, - <&scmi_clk CK_SCMI_FLEXGEN_51>, - <&scmi_clk CK_SCMI_FLEXGEN_52>, - <&scmi_clk CK_SCMI_FLEXGEN_53>, - <&scmi_clk CK_SCMI_FLEXGEN_54>, - <&scmi_clk CK_SCMI_FLEXGEN_55>, - <&scmi_clk CK_SCMI_FLEXGEN_56>, - <&scmi_clk CK_SCMI_FLEXGEN_57>, - <&scmi_clk CK_SCMI_FLEXGEN_58>, - <&scmi_clk CK_SCMI_FLEXGEN_61>, - <&scmi_clk CK_SCMI_FLEXGEN_62>, - <&scmi_clk CK_SCMI_FLEXGEN_63>, - <&scmi_clk CK_SCMI_ICN_APB1>, - <&scmi_clk CK_SCMI_ICN_APB2>, - <&scmi_clk CK_SCMI_ICN_APB3>, - <&scmi_clk CK_SCMI_ICN_APB4>, - <&scmi_clk CK_SCMI_ICN_APB5>, - <&scmi_clk CK_SCMI_ICN_APBDBG>, - <&scmi_clk CK_SCMI_TIMG1>, - <&scmi_clk CK_SCMI_TIMG2>; + clocks = <&scmi_clk CK_SCMI_HSE>, + <&scmi_clk CK_SCMI_HSI>, + <&scmi_clk CK_SCMI_MSI>, + <&scmi_clk CK_SCMI_LSE>, + <&scmi_clk CK_SCMI_LSI>, + <&scmi_clk CK_SCMI_HSE_DIV2>, + <&scmi_clk CK_SCMI_ICN_HS_MCU>, + <&scmi_clk CK_SCMI_ICN_LS_MCU>, + <&scmi_clk CK_SCMI_ICN_SDMMC>, + <&scmi_clk CK_SCMI_ICN_DDR>, + <&scmi_clk CK_SCMI_ICN_DISPLAY>, + <&scmi_clk CK_SCMI_ICN_HSL>, + <&scmi_clk CK_SCMI_ICN_NIC>, + <&scmi_clk CK_SCMI_FLEXGEN_07>, + <&scmi_clk CK_SCMI_FLEXGEN_08>, + <&scmi_clk CK_SCMI_FLEXGEN_09>, + <&scmi_clk CK_SCMI_FLEXGEN_10>, + <&scmi_clk CK_SCMI_FLEXGEN_11>, + <&scmi_clk CK_SCMI_FLEXGEN_12>, + <&scmi_clk CK_SCMI_FLEXGEN_13>, + <&scmi_clk CK_SCMI_FLEXGEN_14>, + <&scmi_clk CK_SCMI_FLEXGEN_16>, + <&scmi_clk CK_SCMI_FLEXGEN_17>, + <&scmi_clk CK_SCMI_FLEXGEN_18>, + <&scmi_clk CK_SCMI_FLEXGEN_19>, + <&scmi_clk CK_SCMI_FLEXGEN_20>, + <&scmi_clk CK_SCMI_FLEXGEN_21>, + <&scmi_clk CK_SCMI_FLEXGEN_22>, + <&scmi_clk CK_SCMI_FLEXGEN_23>, + <&scmi_clk CK_SCMI_FLEXGEN_24>, + <&scmi_clk CK_SCMI_FLEXGEN_25>, + <&scmi_clk CK_SCMI_FLEXGEN_26>, + <&scmi_clk CK_SCMI_FLEXGEN_27>, + <&scmi_clk CK_SCMI_FLEXGEN_29>, + <&scmi_clk CK_SCMI_FLEXGEN_30>, + <&scmi_clk CK_SCMI_FLEXGEN_31>, + <&scmi_clk CK_SCMI_FLEXGEN_33>, + <&scmi_clk CK_SCMI_FLEXGEN_36>, + <&scmi_clk CK_SCMI_FLEXGEN_37>, + <&scmi_clk CK_SCMI_FLEXGEN_38>, + <&scmi_clk CK_SCMI_FLEXGEN_39>, + <&scmi_clk CK_SCMI_FLEXGEN_40>, + <&scmi_clk CK_SCMI_FLEXGEN_41>, + <&scmi_clk CK_SCMI_FLEXGEN_42>, + <&scmi_clk CK_SCMI_FLEXGEN_43>, + <&scmi_clk CK_SCMI_FLEXGEN_44>, + <&scmi_clk CK_SCMI_FLEXGEN_45>, + <&scmi_clk CK_SCMI_FLEXGEN_46>, + <&scmi_clk CK_SCMI_FLEXGEN_47>, + <&scmi_clk CK_SCMI_FLEXGEN_48>, + <&scmi_clk CK_SCMI_FLEXGEN_50>, + <&scmi_clk CK_SCMI_FLEXGEN_51>, + <&scmi_clk CK_SCMI_FLEXGEN_52>, + <&scmi_clk CK_SCMI_FLEXGEN_53>, + <&scmi_clk CK_SCMI_FLEXGEN_54>, + <&scmi_clk CK_SCMI_FLEXGEN_55>, + <&scmi_clk CK_SCMI_FLEXGEN_56>, + <&scmi_clk CK_SCMI_FLEXGEN_57>, + <&scmi_clk CK_SCMI_FLEXGEN_58>, + <&scmi_clk CK_SCMI_FLEXGEN_61>, + <&scmi_clk CK_SCMI_FLEXGEN_62>, + <&scmi_clk CK_SCMI_FLEXGEN_63>, + <&scmi_clk CK_SCMI_ICN_APB1>, + <&scmi_clk CK_SCMI_ICN_APB2>, + <&scmi_clk CK_SCMI_ICN_APB3>, + <&scmi_clk CK_SCMI_ICN_APB4>, + <&scmi_clk CK_SCMI_ICN_APB5>, + <&scmi_clk CK_SCMI_ICN_APBDBG>, + <&scmi_clk CK_SCMI_TIMG1>, + <&scmi_clk CK_SCMI_TIMG2>; }; ... diff --git a/Documentation/devicetree/bindings/clock/st,stm32mp25-rcc.yaml b/Documentation/devicetree/bindings/clock/st,stm32mp25-rcc.yaml index 1e3b5d218bb0..3b7925d2c175 100644 --- a/Documentation/devicetree/bindings/clock/st,stm32mp25-rcc.yaml +++ b/Documentation/devicetree/bindings/clock/st,stm32mp25-rcc.yaml @@ -135,85 +135,85 @@ examples: reg = <0x44200000 0x10000>; #clock-cells = <1>; #reset-cells = <1>; - clocks = <&scmi_clk CK_SCMI_HSE>, - <&scmi_clk CK_SCMI_HSI>, - <&scmi_clk CK_SCMI_MSI>, - <&scmi_clk CK_SCMI_LSE>, - <&scmi_clk CK_SCMI_LSI>, - <&scmi_clk CK_SCMI_HSE_DIV2>, - <&scmi_clk CK_SCMI_ICN_HS_MCU>, - <&scmi_clk CK_SCMI_ICN_LS_MCU>, - <&scmi_clk CK_SCMI_ICN_SDMMC>, - <&scmi_clk CK_SCMI_ICN_DDR>, - <&scmi_clk CK_SCMI_ICN_DISPLAY>, - <&scmi_clk CK_SCMI_ICN_HSL>, - <&scmi_clk CK_SCMI_ICN_NIC>, - <&scmi_clk CK_SCMI_ICN_VID>, - <&scmi_clk CK_SCMI_FLEXGEN_07>, - <&scmi_clk CK_SCMI_FLEXGEN_08>, - <&scmi_clk CK_SCMI_FLEXGEN_09>, - <&scmi_clk CK_SCMI_FLEXGEN_10>, - <&scmi_clk CK_SCMI_FLEXGEN_11>, - <&scmi_clk CK_SCMI_FLEXGEN_12>, - <&scmi_clk CK_SCMI_FLEXGEN_13>, - <&scmi_clk CK_SCMI_FLEXGEN_14>, - <&scmi_clk CK_SCMI_FLEXGEN_15>, - <&scmi_clk CK_SCMI_FLEXGEN_16>, - <&scmi_clk CK_SCMI_FLEXGEN_17>, - <&scmi_clk CK_SCMI_FLEXGEN_18>, - <&scmi_clk CK_SCMI_FLEXGEN_19>, - <&scmi_clk CK_SCMI_FLEXGEN_20>, - <&scmi_clk CK_SCMI_FLEXGEN_21>, - <&scmi_clk CK_SCMI_FLEXGEN_22>, - <&scmi_clk CK_SCMI_FLEXGEN_23>, - <&scmi_clk CK_SCMI_FLEXGEN_24>, - <&scmi_clk CK_SCMI_FLEXGEN_25>, - <&scmi_clk CK_SCMI_FLEXGEN_26>, - <&scmi_clk CK_SCMI_FLEXGEN_27>, - <&scmi_clk CK_SCMI_FLEXGEN_28>, - <&scmi_clk CK_SCMI_FLEXGEN_29>, - <&scmi_clk CK_SCMI_FLEXGEN_30>, - <&scmi_clk CK_SCMI_FLEXGEN_31>, - <&scmi_clk CK_SCMI_FLEXGEN_32>, - <&scmi_clk CK_SCMI_FLEXGEN_33>, - <&scmi_clk CK_SCMI_FLEXGEN_34>, - <&scmi_clk CK_SCMI_FLEXGEN_35>, - <&scmi_clk CK_SCMI_FLEXGEN_36>, - <&scmi_clk CK_SCMI_FLEXGEN_37>, - <&scmi_clk CK_SCMI_FLEXGEN_38>, - <&scmi_clk CK_SCMI_FLEXGEN_39>, - <&scmi_clk CK_SCMI_FLEXGEN_40>, - <&scmi_clk CK_SCMI_FLEXGEN_41>, - <&scmi_clk CK_SCMI_FLEXGEN_42>, - <&scmi_clk CK_SCMI_FLEXGEN_43>, - <&scmi_clk CK_SCMI_FLEXGEN_44>, - <&scmi_clk CK_SCMI_FLEXGEN_45>, - <&scmi_clk CK_SCMI_FLEXGEN_46>, - <&scmi_clk CK_SCMI_FLEXGEN_47>, - <&scmi_clk CK_SCMI_FLEXGEN_48>, - <&scmi_clk CK_SCMI_FLEXGEN_49>, - <&scmi_clk CK_SCMI_FLEXGEN_50>, - <&scmi_clk CK_SCMI_FLEXGEN_51>, - <&scmi_clk CK_SCMI_FLEXGEN_52>, - <&scmi_clk CK_SCMI_FLEXGEN_53>, - <&scmi_clk CK_SCMI_FLEXGEN_54>, - <&scmi_clk CK_SCMI_FLEXGEN_55>, - <&scmi_clk CK_SCMI_FLEXGEN_56>, - <&scmi_clk CK_SCMI_FLEXGEN_57>, - <&scmi_clk CK_SCMI_FLEXGEN_58>, - <&scmi_clk CK_SCMI_FLEXGEN_59>, - <&scmi_clk CK_SCMI_FLEXGEN_60>, - <&scmi_clk CK_SCMI_FLEXGEN_61>, - <&scmi_clk CK_SCMI_FLEXGEN_62>, - <&scmi_clk CK_SCMI_FLEXGEN_63>, - <&scmi_clk CK_SCMI_ICN_APB1>, - <&scmi_clk CK_SCMI_ICN_APB2>, - <&scmi_clk CK_SCMI_ICN_APB3>, - <&scmi_clk CK_SCMI_ICN_APB4>, - <&scmi_clk CK_SCMI_ICN_APBDBG>, - <&scmi_clk CK_SCMI_TIMG1>, - <&scmi_clk CK_SCMI_TIMG2>, - <&scmi_clk CK_SCMI_PLL3>, - <&clk_dsi_txbyte>; + clocks = <&scmi_clk CK_SCMI_HSE>, + <&scmi_clk CK_SCMI_HSI>, + <&scmi_clk CK_SCMI_MSI>, + <&scmi_clk CK_SCMI_LSE>, + <&scmi_clk CK_SCMI_LSI>, + <&scmi_clk CK_SCMI_HSE_DIV2>, + <&scmi_clk CK_SCMI_ICN_HS_MCU>, + <&scmi_clk CK_SCMI_ICN_LS_MCU>, + <&scmi_clk CK_SCMI_ICN_SDMMC>, + <&scmi_clk CK_SCMI_ICN_DDR>, + <&scmi_clk CK_SCMI_ICN_DISPLAY>, + <&scmi_clk CK_SCMI_ICN_HSL>, + <&scmi_clk CK_SCMI_ICN_NIC>, + <&scmi_clk CK_SCMI_ICN_VID>, + <&scmi_clk CK_SCMI_FLEXGEN_07>, + <&scmi_clk CK_SCMI_FLEXGEN_08>, + <&scmi_clk CK_SCMI_FLEXGEN_09>, + <&scmi_clk CK_SCMI_FLEXGEN_10>, + <&scmi_clk CK_SCMI_FLEXGEN_11>, + <&scmi_clk CK_SCMI_FLEXGEN_12>, + <&scmi_clk CK_SCMI_FLEXGEN_13>, + <&scmi_clk CK_SCMI_FLEXGEN_14>, + <&scmi_clk CK_SCMI_FLEXGEN_15>, + <&scmi_clk CK_SCMI_FLEXGEN_16>, + <&scmi_clk CK_SCMI_FLEXGEN_17>, + <&scmi_clk CK_SCMI_FLEXGEN_18>, + <&scmi_clk CK_SCMI_FLEXGEN_19>, + <&scmi_clk CK_SCMI_FLEXGEN_20>, + <&scmi_clk CK_SCMI_FLEXGEN_21>, + <&scmi_clk CK_SCMI_FLEXGEN_22>, + <&scmi_clk CK_SCMI_FLEXGEN_23>, + <&scmi_clk CK_SCMI_FLEXGEN_24>, + <&scmi_clk CK_SCMI_FLEXGEN_25>, + <&scmi_clk CK_SCMI_FLEXGEN_26>, + <&scmi_clk CK_SCMI_FLEXGEN_27>, + <&scmi_clk CK_SCMI_FLEXGEN_28>, + <&scmi_clk CK_SCMI_FLEXGEN_29>, + <&scmi_clk CK_SCMI_FLEXGEN_30>, + <&scmi_clk CK_SCMI_FLEXGEN_31>, + <&scmi_clk CK_SCMI_FLEXGEN_32>, + <&scmi_clk CK_SCMI_FLEXGEN_33>, + <&scmi_clk CK_SCMI_FLEXGEN_34>, + <&scmi_clk CK_SCMI_FLEXGEN_35>, + <&scmi_clk CK_SCMI_FLEXGEN_36>, + <&scmi_clk CK_SCMI_FLEXGEN_37>, + <&scmi_clk CK_SCMI_FLEXGEN_38>, + <&scmi_clk CK_SCMI_FLEXGEN_39>, + <&scmi_clk CK_SCMI_FLEXGEN_40>, + <&scmi_clk CK_SCMI_FLEXGEN_41>, + <&scmi_clk CK_SCMI_FLEXGEN_42>, + <&scmi_clk CK_SCMI_FLEXGEN_43>, + <&scmi_clk CK_SCMI_FLEXGEN_44>, + <&scmi_clk CK_SCMI_FLEXGEN_45>, + <&scmi_clk CK_SCMI_FLEXGEN_46>, + <&scmi_clk CK_SCMI_FLEXGEN_47>, + <&scmi_clk CK_SCMI_FLEXGEN_48>, + <&scmi_clk CK_SCMI_FLEXGEN_49>, + <&scmi_clk CK_SCMI_FLEXGEN_50>, + <&scmi_clk CK_SCMI_FLEXGEN_51>, + <&scmi_clk CK_SCMI_FLEXGEN_52>, + <&scmi_clk CK_SCMI_FLEXGEN_53>, + <&scmi_clk CK_SCMI_FLEXGEN_54>, + <&scmi_clk CK_SCMI_FLEXGEN_55>, + <&scmi_clk CK_SCMI_FLEXGEN_56>, + <&scmi_clk CK_SCMI_FLEXGEN_57>, + <&scmi_clk CK_SCMI_FLEXGEN_58>, + <&scmi_clk CK_SCMI_FLEXGEN_59>, + <&scmi_clk CK_SCMI_FLEXGEN_60>, + <&scmi_clk CK_SCMI_FLEXGEN_61>, + <&scmi_clk CK_SCMI_FLEXGEN_62>, + <&scmi_clk CK_SCMI_FLEXGEN_63>, + <&scmi_clk CK_SCMI_ICN_APB1>, + <&scmi_clk CK_SCMI_ICN_APB2>, + <&scmi_clk CK_SCMI_ICN_APB3>, + <&scmi_clk CK_SCMI_ICN_APB4>, + <&scmi_clk CK_SCMI_ICN_APBDBG>, + <&scmi_clk CK_SCMI_TIMG1>, + <&scmi_clk CK_SCMI_TIMG2>, + <&scmi_clk CK_SCMI_PLL3>, + <&clk_dsi_txbyte>; }; ... diff --git a/Documentation/devicetree/bindings/clock/xlnx,clocking-wizard.yaml b/Documentation/devicetree/bindings/clock/xlnx,clocking-wizard.yaml index b497c28e8094..3f311f90294e 100644 --- a/Documentation/devicetree/bindings/clock/xlnx,clocking-wizard.yaml +++ b/Documentation/devicetree/bindings/clock/xlnx,clocking-wizard.yaml @@ -70,7 +70,7 @@ additionalProperties: false examples: - | - clock-controller@b0000000 { + clock-controller@b0000000 { compatible = "xlnx,clocking-wizard"; reg = <0xb0000000 0x10000>; #clock-cells = <1>; From 3a6deb47cdd47e50b25779ad8321a7aaf20e9d23 Mon Sep 17 00:00:00 2001 From: Bhargav Joshi Date: Tue, 11 Aug 2026 00:24:59 +0530 Subject: [PATCH 101/112] dt-bindings: clock: ti,clockdomain: Convert to DT schema Convert TI clockdomain to yaml DT schema. Drop '#clock-cells' from the required list as this binding doesn't define a new clock binding type, it is used to group existing clock nodes under hardware hierarchy. Most existing dts omit '#clock-cells'. Update the reference to the old legacy text binding in the description of bindings/clock/ti/ti,gate-clock.yaml to point to the new YAML file. Reviewed-by: Rob Herring (Arm) Signed-off-by: Bhargav Joshi Signed-off-by: Stephen Boyd --- .../bindings/clock/ti/clockdomain.txt | 25 ----------- .../bindings/clock/ti/ti,clockdomain.yaml | 44 +++++++++++++++++++ .../bindings/clock/ti/ti,gate-clock.yaml | 2 +- 3 files changed, 45 insertions(+), 26 deletions(-) delete mode 100644 Documentation/devicetree/bindings/clock/ti/clockdomain.txt create mode 100644 Documentation/devicetree/bindings/clock/ti/ti,clockdomain.yaml diff --git a/Documentation/devicetree/bindings/clock/ti/clockdomain.txt b/Documentation/devicetree/bindings/clock/ti/clockdomain.txt deleted file mode 100644 index edf0b5d42768..000000000000 --- a/Documentation/devicetree/bindings/clock/ti/clockdomain.txt +++ /dev/null @@ -1,25 +0,0 @@ -Binding for Texas Instruments clockdomain. - -This binding uses the common clock binding[1] in consumer role. -Every clock on TI SoC belongs to one clockdomain, but software -only needs this information for specific clocks which require -their parent clockdomain to be controlled when the clock is -enabled/disabled. This binding doesn't define a new clock -binding type, it is used to group existing clock nodes under -hardware hierarchy. - -[1] Documentation/devicetree/bindings/clock/clock-bindings.txt - -Required properties: -- compatible : shall be "ti,clockdomain" -- #clock-cells : from common clock binding; shall be set to 0. -- clocks : link phandles of clocks within this domain - -Optional properties: -- clock-output-names : from common clock binding. - -Examples: - dss_clkdm: dss_clkdm { - compatible = "ti,clockdomain"; - clocks = <&dss1_alwon_fck_3430es2>, <&dss_ick_3430es2>; - }; diff --git a/Documentation/devicetree/bindings/clock/ti/ti,clockdomain.yaml b/Documentation/devicetree/bindings/clock/ti/ti,clockdomain.yaml new file mode 100644 index 000000000000..95746bfdced4 --- /dev/null +++ b/Documentation/devicetree/bindings/clock/ti/ti,clockdomain.yaml @@ -0,0 +1,44 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/clock/ti/ti,clockdomain.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Texas Instruments clockdomain + +maintainers: + - Tero Kristo + +description: + This binding uses the common clock binding in consumer role. Every clock on TI + SoC belongs to one clockdomain, but software only needs this information for + specific clocks which require their parent clockdomain to be controlled when + the clock is enabled/disabled. This binding doesn't define a new clock binding + type, it is used to group existing clock nodes under hardware hierarchy. + +properties: + compatible: + const: ti,clockdomain + + "#clock-cells": + const: 0 + + clocks: + minItems: 1 + maxItems: 64 + + clock-output-names: + maxItems: 1 + +required: + - compatible + - clocks + +additionalProperties: false + +examples: + - | + dss_clkdm { + compatible = "ti,clockdomain"; + clocks = <&dss1_alwon_fck_3430es2>, <&dss_ick_3430es2>; + }; diff --git a/Documentation/devicetree/bindings/clock/ti/ti,gate-clock.yaml b/Documentation/devicetree/bindings/clock/ti/ti,gate-clock.yaml index eaa727ab0d7f..438e190d1067 100644 --- a/Documentation/devicetree/bindings/clock/ti/ti,gate-clock.yaml +++ b/Documentation/devicetree/bindings/clock/ti/ti,gate-clock.yaml @@ -19,7 +19,7 @@ description: | that is used. [1] Documentation/devicetree/bindings/clock/gpio-gate-clock.yaml - [2] Documentation/devicetree/bindings/clock/ti/clockdomain.txt + [2] Documentation/devicetree/bindings/clock/ti/ti,clockdomain.yaml properties: compatible: From 3cf908dd13968e099163ed0db236443b7bdec835 Mon Sep 17 00:00:00 2001 From: Babanpreet Singh Date: Mon, 3 Aug 2026 06:31:26 +0000 Subject: [PATCH 102/112] clk: zynq: pll: Fix kernel-doc after determine_rate() conversion Commit 1547747b55d5 ("clk: zynq: pll: convert from round_rate() to determine_rate()") replaced zynq_pll_round_rate() with zynq_pll_determine_rate() but left its kernel-doc block behind, so the block still names the old function and documents the removed @rate and @prate parameters instead of @req. Retitle the block and describe @req. The Return: line described a frequency and referenced the removed @rate; the function returns 0 unconditionally and the rounded rate comes back in @req. No functional changes. Assisted-by: Claude:claude-opus-5 [kernel-doc] Signed-off-by: Babanpreet Singh Reviewed-by: Michal Simek Reviewed-by: Brian Masney Signed-off-by: Stephen Boyd --- drivers/clk/zynq/pll.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/clk/zynq/pll.c b/drivers/clk/zynq/pll.c index 44c609378364..fe90b50e1545 100644 --- a/drivers/clk/zynq/pll.c +++ b/drivers/clk/zynq/pll.c @@ -42,11 +42,11 @@ struct zynq_pll { #define PLL_FBDIV_MAX 66 /** - * zynq_pll_round_rate() - Round a clock frequency + * zynq_pll_determine_rate() - Round a clock frequency * @hw: Handle between common and hardware-specific interfaces - * @rate: Desired clock frequency - * @prate: Clock frequency of parent clock - * Return: frequency closest to @rate the hardware can generate. + * @req: Clock rate request, updated with the frequency closest to the + * requested one that the hardware can generate + * Return: 0 always */ static int zynq_pll_determine_rate(struct clk_hw *hw, struct clk_rate_request *req) From 16f212563f86646a07ca97b334e1c7d97d3ee6fd Mon Sep 17 00:00:00 2001 From: Eduard Bostina Date: Thu, 30 Jul 2026 11:30:19 +0300 Subject: [PATCH 103/112] dt-bindings: clock: ti: Convert APLL clock to DT schema Convert the Texas Instruments APLL clock bindings to DT schema. Signed-off-by: Eduard Bostina Signed-off-by: Stephen Boyd --- .../devicetree/bindings/clock/ti/apll.txt | 43 --------- .../bindings/clock/ti/ti,apll-clock.yaml | 89 +++++++++++++++++++ 2 files changed, 89 insertions(+), 43 deletions(-) delete mode 100644 Documentation/devicetree/bindings/clock/ti/apll.txt create mode 100644 Documentation/devicetree/bindings/clock/ti/ti,apll-clock.yaml diff --git a/Documentation/devicetree/bindings/clock/ti/apll.txt b/Documentation/devicetree/bindings/clock/ti/apll.txt deleted file mode 100644 index bbd505c1199d..000000000000 --- a/Documentation/devicetree/bindings/clock/ti/apll.txt +++ /dev/null @@ -1,43 +0,0 @@ -Binding for Texas Instruments APLL clock. - -This binding uses the common clock binding[1]. It assumes a -register-mapped APLL with usually two selectable input clocks -(reference clock and bypass clock), with analog phase locked -loop logic for multiplying the input clock to a desired output -clock. This clock also typically supports different operation -modes (locked, low power stop etc.) APLL mostly behaves like -a subtype of a DPLL [2], although a simplified one at that. - -[1] Documentation/devicetree/bindings/clock/clock-bindings.txt -[2] Documentation/devicetree/bindings/clock/ti/dpll.txt - -Required properties: -- compatible : shall be "ti,dra7-apll-clock" or "ti,omap2-apll-clock" -- #clock-cells : from common clock binding; shall be set to 0. -- clocks : link phandles of parent clocks (clk-ref and clk-bypass) -- reg : address and length of the register set for controlling the APLL. - It contains the information of registers in the following order: - "control" - contains the control register offset - "idlest" - contains the idlest register offset - "autoidle" - contains the autoidle register offset (OMAP2 only) -- ti,clock-frequency : static clock frequency for the clock (OMAP2 only) -- ti,idlest-shift : bit-shift for the idlest field (OMAP2 only) -- ti,bit-shift : bit-shift for enable and autoidle fields (OMAP2 only) - -Examples: - apll_pcie_ck: apll_pcie_ck { - #clock-cells = <0>; - clocks = <&apll_pcie_in_clk_mux>, <&dpll_pcie_ref_ck>; - reg = <0x021c>, <0x0220>; - compatible = "ti,dra7-apll-clock"; - }; - - apll96_ck: apll96_ck { - #clock-cells = <0>; - compatible = "ti,omap2-apll-clock"; - clocks = <&sys_ck>; - ti,bit-shift = <2>; - ti,idlest-shift = <8>; - ti,clock-frequency = <96000000>; - reg = <0x0500>, <0x0530>, <0x0520>; - }; diff --git a/Documentation/devicetree/bindings/clock/ti/ti,apll-clock.yaml b/Documentation/devicetree/bindings/clock/ti/ti,apll-clock.yaml new file mode 100644 index 000000000000..e14a76254ce7 --- /dev/null +++ b/Documentation/devicetree/bindings/clock/ti/ti,apll-clock.yaml @@ -0,0 +1,89 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/clock/ti/ti,apll-clock.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Texas Instruments APLL clock + +maintainers: + - Eduard Bostina + +allOf: + - $ref: /schemas/clock/clock.yaml# + +description: + This binding assumes a register-mapped APLL with usually two selectable + input clocks (reference clock and bypass clock), with analog phase locked + loop logic for multiplying the input clock to a desired output clock. + This clock also typically supports different operation modes (locked, low + power stop etc.) APLL mostly behaves like a subtype of a DPLL, although + a simplified one at that. + +properties: + compatible: + enum: + - ti,dra7-apll-clock + - ti,omap2-apll-clock + + "#clock-cells": + const: 0 + + clocks: + minItems: 1 + maxItems: 2 + description: Link phandles of parent clocks (clk-ref and clk-bypass) + + reg: + minItems: 2 + maxItems: 3 + description: | + Address and length of the register set for controlling the APLL. + It contains the information of registers in the following order: + "control" - contains the control register offset + "idlest" - contains the idlest register offset + "autoidle" - contains the autoidle register offset (OMAP2 only) + + ti,clock-frequency: + $ref: /schemas/types.yaml#/definitions/uint32 + description: static clock frequency for the clock (OMAP2 only) + + ti,idlest-shift: + $ref: /schemas/types.yaml#/definitions/uint32 + description: bit-shift for the idlest field (OMAP2 only) + + ti,bit-shift: + $ref: /schemas/types.yaml#/definitions/uint32 + description: bit-shift for enable and autoidle fields (OMAP2 only) + +required: + - compatible + - "#clock-cells" + - clocks + - reg + +unevaluatedProperties: false + +examples: + - | + bus { + #address-cells = <1>; + #size-cells = <0>; + + clock@21c { + #clock-cells = <0>; + compatible = "ti,dra7-apll-clock"; + clocks = <&apll_pcie_in_clk_mux>, <&dpll_pcie_ref_ck>; + reg = <0x021c>, <0x0220>; + }; + + clock@500 { + #clock-cells = <0>; + compatible = "ti,omap2-apll-clock"; + clocks = <&sys_ck>; + ti,bit-shift = <2>; + ti,idlest-shift = <8>; + ti,clock-frequency = <96000000>; + reg = <0x0500>, <0x0530>, <0x0520>; + }; + }; From 368500fcb3679af69b244e394a35be47a799a725 Mon Sep 17 00:00:00 2001 From: Bhargav Joshi Date: Sun, 26 Jul 2026 01:34:08 +0530 Subject: [PATCH 104/112] dt-bindings: clock: ti,keystone-gate: Convert to DT schema Convert Texas Instruments keystone PSC clock binding to DT schema. The vendor-specific 'domain-id' property is intentionally kept without a "ti," prefix to preserve existing ABI since driver and existing board file use property without "ti," prefix. Also update the reference to this binding in ti,keystone-rproc.txt to point at the new filename. Signed-off-by: Bhargav Joshi Reviewed-by: Rob Herring (Arm) Signed-off-by: Stephen Boyd --- .../bindings/clock/keystone-gate.txt | 27 --------- .../bindings/clock/ti,keystone,psc-clock.yaml | 60 +++++++++++++++++++ .../bindings/remoteproc/ti,keystone-rproc.txt | 2 +- 3 files changed, 61 insertions(+), 28 deletions(-) delete mode 100644 Documentation/devicetree/bindings/clock/keystone-gate.txt create mode 100644 Documentation/devicetree/bindings/clock/ti,keystone,psc-clock.yaml diff --git a/Documentation/devicetree/bindings/clock/keystone-gate.txt b/Documentation/devicetree/bindings/clock/keystone-gate.txt deleted file mode 100644 index 43f6fb6c9392..000000000000 --- a/Documentation/devicetree/bindings/clock/keystone-gate.txt +++ /dev/null @@ -1,27 +0,0 @@ -Binding for Keystone gate control driver which uses PSC controller IP. - -This binding uses the common clock binding[1]. - -[1] Documentation/devicetree/bindings/clock/clock-bindings.txt - -Required properties: -- compatible : shall be "ti,keystone,psc-clock". -- #clock-cells : from common clock binding; shall be set to 0. -- clocks : parent clock phandle -- reg : psc control and domain address address space -- reg-names : psc control and domain registers -- domain-id : psc domain id needed to check the transition state register - -Optional properties: -- clock-output-names : From common clock binding to override the - default output clock name -Example: - clkusb: clkusb { - #clock-cells = <0>; - compatible = "ti,keystone,psc-clock"; - clocks = <&chipclk16>; - clock-output-names = "usb"; - reg = <0x02350008 0xb00>, <0x02350000 0x400>; - reg-names = "control", "domain"; - domain-id = <0>; - }; diff --git a/Documentation/devicetree/bindings/clock/ti,keystone,psc-clock.yaml b/Documentation/devicetree/bindings/clock/ti,keystone,psc-clock.yaml new file mode 100644 index 000000000000..ed42adb27832 --- /dev/null +++ b/Documentation/devicetree/bindings/clock/ti,keystone,psc-clock.yaml @@ -0,0 +1,60 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/clock/ti,keystone,psc-clock.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: TI Keystone PSC clock + +maintainers: + - Santosh Shilimkar + - Murali Karicheri + +properties: + compatible: + const: ti,keystone,psc-clock + + reg: + items: + - description: PSC control + - description: Domain + + reg-names: + items: + - const: control + - const: domain + + domain-id: + $ref: /schemas/types.yaml#/definitions/uint32 + description: PSC domain id needed to check the transition state register + + clocks: + maxItems: 1 + + clock-output-names: + maxItems: 1 + + "#clock-cells": + const: 0 + +required: + - compatible + - reg + - reg-names + - domain-id + - clocks + - "#clock-cells" + +additionalProperties: false + +examples: + - | + clkusb@2350008 { + compatible = "ti,keystone,psc-clock"; + reg = <0x02350008 0xb00>, <0x02350000 0x400>; + reg-names = "control", "domain"; + domain-id = <0>; + clocks = <&chipclk16>; + clock-output-names = "usb"; + #clock-cells = <0>; + }; diff --git a/Documentation/devicetree/bindings/remoteproc/ti,keystone-rproc.txt b/Documentation/devicetree/bindings/remoteproc/ti,keystone-rproc.txt index 91f0a3b0c0b2..847552133633 100644 --- a/Documentation/devicetree/bindings/remoteproc/ti,keystone-rproc.txt +++ b/Documentation/devicetree/bindings/remoteproc/ti,keystone-rproc.txt @@ -75,7 +75,7 @@ SoCs only: - clocks: Should contain the device's input clock, and should be defined as per the bindings in, - Documentation/devicetree/bindings/clock/keystone-gate.txt + Documentation/devicetree/bindings/clock/ti,keystone,psc-clock.yaml The following are mandatory properties for Keystone 2 66AK2G SoCs only: From 0d4d262c1664365e17e0a5ba2ab79f4db484b44e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Onur=20=C3=96zkan?= Date: Sat, 1 Aug 2026 14:16:34 +0300 Subject: [PATCH 105/112] clk: devres: fix cleanup in devm_clk_get_optional_enabled_with_rate() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit devm_clk_get_optional_enabled_with_rate() registers its cleanup action before setting the clock rate. If setting the rate fails, it attempts to disable and unprepare a clock that was never enabled. This issue was spotted while reviewing "rust: clk: add devres-managed clks" [1]. Register the cleanup action only after successfully preparing and enabling the clock. [1]: https://lore.kernel.org/rust-for-linux/20260706-clk-type-state-v5-3-67c5f326a16c@collabora.com Fixes: 9934a1bd45b2 ("clk: provide devm_clk_get_optional_enabled_with_rate()") Signed-off-by: Onur Özkan Reviewed-by: Brian Masney Signed-off-by: Stephen Boyd --- drivers/clk/clk-devres.c | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/drivers/clk/clk-devres.c b/drivers/clk/clk-devres.c index 49d4782991eb..4cc3863bd7da 100644 --- a/drivers/clk/clk-devres.c +++ b/drivers/clk/clk-devres.c @@ -99,6 +99,11 @@ struct clk *devm_clk_get_optional_enabled(struct device *dev, const char *id) } EXPORT_SYMBOL_GPL(devm_clk_get_optional_enabled); +static void devm_clk_disable_unprepare(void *data) +{ + clk_disable_unprepare(data); +} + struct clk *devm_clk_get_optional_enabled_with_rate(struct device *dev, const char *id, unsigned long rate) @@ -106,8 +111,7 @@ struct clk *devm_clk_get_optional_enabled_with_rate(struct device *dev, struct clk *clk; int ret; - clk = __devm_clk_get(dev, id, clk_get_optional, NULL, - clk_disable_unprepare); + clk = devm_clk_get_optional(dev, id); if (IS_ERR(clk)) return ERR_CAST(clk); @@ -119,6 +123,10 @@ struct clk *devm_clk_get_optional_enabled_with_rate(struct device *dev, if (ret) goto out_put_clk; + ret = devm_add_action_or_reset(dev, devm_clk_disable_unprepare, clk); + if (ret) + goto out_put_clk; + return clk; out_put_clk: From 667f420c09f1417c9f07798e32432c901915209d Mon Sep 17 00:00:00 2001 From: Mathieu Dubois-Briand Date: Mon, 27 Jul 2026 09:41:40 +0200 Subject: [PATCH 106/112] clk: ti: mux: resolve parent clocks by DT index, not by name Resolve parent clocks by their index into the device tree "clocks" property rather than matching names as strings. Name-based matching is fragile because a clock's "clock-output-names" value in its provider node can differ from the name used to reference it in a consumer node, and because names must be globally unique across all clock providers. On AM335x, this caused broken clock trees where some clocks failed to enable because their parents could not be found. Replace of_clk_parent_fill() with a clk_parent_data array that sets .index to the array position. Fixes: ec7aa25fa483 ("ARM: dts: Use clock-output-names for am3") Signed-off-by: Mathieu Dubois-Briand Reviewed-by: Brian Masney Signed-off-by: Stephen Boyd --- drivers/clk/ti/mux.c | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/drivers/clk/ti/mux.c b/drivers/clk/ti/mux.c index d6a0ccfd81db..ded4432f7528 100644 --- a/drivers/clk/ti/mux.c +++ b/drivers/clk/ti/mux.c @@ -119,7 +119,7 @@ const struct clk_ops ti_clk_mux_ops = { }; static struct clk *_register_mux(struct device_node *node, const char *name, - const char * const *parent_names, + const struct clk_parent_data *parent_data, u8 num_parents, unsigned long flags, struct clk_omap_reg *reg, u8 shift, u32 mask, s8 latch, u8 clk_mux_flags, u32 *table) @@ -136,7 +136,7 @@ static struct clk *_register_mux(struct device_node *node, const char *name, init.name = name; init.ops = &ti_clk_mux_ops; init.flags = flags; - init.parent_names = parent_names; + init.parent_data = parent_data; init.num_parents = num_parents; /* struct clk_mux assignments */ @@ -167,24 +167,26 @@ static void of_mux_clk_setup(struct device_node *node) struct clk *clk; struct clk_omap_reg reg; unsigned int num_parents; - const char **parent_names; + struct clk_parent_data *parent_data; const char *name; u8 clk_mux_flags = 0; u32 mask = 0; u32 shift = 0; s32 latch = -EINVAL; u32 flags = CLK_SET_RATE_NO_REPARENT; + int i; num_parents = of_clk_get_parent_count(node); if (num_parents < 2) { pr_err("mux-clock %pOFn must have parents\n", node); return; } - parent_names = kcalloc(num_parents, sizeof(char *), GFP_KERNEL); - if (!parent_names) - goto cleanup; + parent_data = kcalloc(num_parents, sizeof(*parent_data), GFP_KERNEL); + if (!parent_data) + return; - of_clk_parent_fill(node, parent_names, num_parents); + for (i = 0; i < num_parents; i++) + parent_data[i].index = i; if (ti_clk_get_reg_addr(node, 0, ®)) goto cleanup; @@ -207,7 +209,7 @@ static void of_mux_clk_setup(struct device_node *node) mask = (1 << fls(mask)) - 1; name = ti_dt_clk_name(node); - clk = _register_mux(node, name, parent_names, num_parents, + clk = _register_mux(node, name, parent_data, num_parents, flags, ®, shift, mask, latch, clk_mux_flags, NULL); @@ -215,7 +217,7 @@ static void of_mux_clk_setup(struct device_node *node) of_clk_add_provider(node, of_clk_src_simple_get, clk); cleanup: - kfree(parent_names); + kfree(parent_data); } CLK_OF_DECLARE(mux_clk, "ti,mux-clock", of_mux_clk_setup); From fe3dd92ac54a043ab79ff06623530b0c9611593c Mon Sep 17 00:00:00 2001 From: Mathieu Dubois-Briand Date: Mon, 27 Jul 2026 09:41:41 +0200 Subject: [PATCH 107/112] clk: ti: composite: resolve parent clocks by DT index, not by name Resolve parent clocks by their index into the device tree "clocks" property rather than matching names as strings. This makes it consistent with other parts of the same driver. Signed-off-by: Mathieu Dubois-Briand Reviewed-by: Brian Masney Signed-off-by: Stephen Boyd --- drivers/clk/ti/composite.c | 26 ++++++++++++++------------ 1 file changed, 14 insertions(+), 12 deletions(-) diff --git a/drivers/clk/ti/composite.c b/drivers/clk/ti/composite.c index c379bbdae25a..01eae8995254 100644 --- a/drivers/clk/ti/composite.c +++ b/drivers/clk/ti/composite.c @@ -52,7 +52,7 @@ static const struct clk_ops ti_composite_gate_ops = { struct component_clk { int num_parents; - const char **parent_names; + struct clk_parent_data *parent_data; struct device_node *node; int type; struct clk_hw *hw; @@ -116,7 +116,7 @@ static void __init _register_composite(void *user, struct clk_hw_omap_comp *cclk = to_clk_hw_comp(hw); struct component_clk *comp; int num_parents = 0; - const char **parent_names = NULL; + struct clk_parent_data *parent_data = NULL; const char *name; int i; int ret; @@ -155,7 +155,7 @@ static void __init _register_composite(void *user, continue; if (comp->num_parents) { num_parents = comp->num_parents; - parent_names = comp->parent_names; + parent_data = comp->parent_data; break; } } @@ -166,8 +166,8 @@ static void __init _register_composite(void *user, } name = ti_dt_clk_name(node); - clk = clk_register_composite(NULL, name, - parent_names, num_parents, + clk = clk_register_composite_pdata(NULL, name, + parent_data, num_parents, _get_hw(cclk, CLK_COMPONENT_TYPE_MUX), &ti_clk_mux_ops, _get_hw(cclk, CLK_COMPONENT_TYPE_DIVIDER), @@ -190,7 +190,7 @@ cleanup: if (!cclk->comp_clks[i]) continue; list_del(&cclk->comp_clks[i]->link); - kfree(cclk->comp_clks[i]->parent_names); + kfree(cclk->comp_clks[i]->parent_data); kfree(cclk->comp_clks[i]); } @@ -237,8 +237,9 @@ int __init ti_clk_add_component(struct device_node *node, struct clk_hw *hw, int type) { unsigned int num_parents; - const char **parent_names; + struct clk_parent_data *parent_data; struct component_clk *clk; + unsigned int i; num_parents = of_clk_get_parent_count(node); @@ -247,20 +248,21 @@ int __init ti_clk_add_component(struct device_node *node, struct clk_hw *hw, return -EINVAL; } - parent_names = kcalloc(num_parents, sizeof(char *), GFP_KERNEL); - if (!parent_names) + parent_data = kcalloc(num_parents, sizeof(*parent_data), GFP_KERNEL); + if (!parent_data) return -ENOMEM; - of_clk_parent_fill(node, parent_names, num_parents); + for (i = 0; i < num_parents; i++) + parent_data[i].index = i; clk = kzalloc_obj(*clk); if (!clk) { - kfree(parent_names); + kfree(parent_data); return -ENOMEM; } clk->num_parents = num_parents; - clk->parent_names = parent_names; + clk->parent_data = parent_data; clk->hw = hw; clk->node = node; clk->type = type; From be8e785db92c1cb448943b908900b324c93b5b08 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Uwe=20Kleine-K=C3=B6nig?= Date: Wed, 29 Jul 2026 14:16:40 +0200 Subject: [PATCH 108/112] Drop Michael Turquette's clk maintainer entry MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Mike wasn't publically active for the clk subsystem since years. Stop pretending that there is a maintainer team caring for clk. Signed-off-by: Uwe Kleine-König Acked-by: Michael Turquette Signed-off-by: Stephen Boyd --- CREDITS | 4 ++++ MAINTAINERS | 1 - 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/CREDITS b/CREDITS index 84793a967a0b..c8a67ab8efe5 100644 --- a/CREDITS +++ b/CREDITS @@ -4085,6 +4085,10 @@ S: 4226 Landgreen Street S: Rockville, Maryland 20853 S: USA +N: Michael Turquette +E: mturquette@baylibre.com +D: common clk maintenance + N: Stephen Tweedie E: sct@redhat.com P: 1024/E7A417AD E2 FE A4 20 34 EC ED FC 7D 7E 67 8D E0 31 D1 69 diff --git a/MAINTAINERS b/MAINTAINERS index d15f20dbc92d..6654ed8a17ad 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -6447,7 +6447,6 @@ F: include/linux/comedi/ F: include/uapi/linux/comedi.h COMMON CLK FRAMEWORK -M: Michael Turquette M: Stephen Boyd R: Brian Masney L: linux-clk@vger.kernel.org From 8bc7ceadce1f362a8b0e88075ef3d926b286c01c Mon Sep 17 00:00:00 2001 From: Brian Masney Date: Fri, 14 Aug 2026 18:27:49 +0200 Subject: [PATCH 109/112] MAINTAINERS: Add Brian Masney and Jerome Brunet as co-maintainers for clk subsystem With Michael Turquette stepping down as a clk co-maintainer, this subsystem is big enough that it needs multiple people to keep the clk community healthy. Add Brian and Jerome as co-maintainers. Link: https://lore.kernel.org/linux-clk/anW-jTldkWSJK3wl@redhat.com/T/#m0ae01b8dd23c452d82f34b61844a8305977a53a4 Acked-by: Michael Turquette Signed-off-by: Brian Masney Signed-off-by: Jerome Brunet Signed-off-by: Stephen Boyd --- MAINTAINERS | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/MAINTAINERS b/MAINTAINERS index 6654ed8a17ad..5b0c1c4d7804 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -6448,7 +6448,8 @@ F: include/uapi/linux/comedi.h COMMON CLK FRAMEWORK M: Stephen Boyd -R: Brian Masney +M: Brian Masney +M: Jerome Brunet L: linux-clk@vger.kernel.org S: Maintained Q: http://patchwork.kernel.org/project/linux-clk/list/ From 3ae1674975d48fb6d343d03ff7961abd6904af1a Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Wed, 19 Aug 2026 21:05:15 +0200 Subject: [PATCH 110/112] clk: ti: Make sure clk_init_data is fully initialized The clk_init_data structure contains several mutually-exclusive members for different methods to specify the possible parents of a clock, prompting drivers to initialize only the members they need. However, not initializing all members may cause subtle issues, which are only exposed when CONFIG_INIT_STACK_ALL_PATTERN or CONFIG_INIT_STACK_NONE is enabled. _register_mux() fills in init.parent_data, and assumes that init.parent_names is NULL. However, the latter is uninitialized, and thus may cause a crash. Make sure all members are fully initialized, to fix such bugs, and to avoid future breakage when converting drivers to a different method for specifying the parents. Fixes: 667f420c09f1417c ("clk: ti: mux: resolve parent clocks by DT index, not by name") Closes: https://lore.kernel.org/CAMuHMdU3yVqoyHC4eNF2NuYo8wy+6ODLoYat4R71X99Mxc_=kw@mail.gmail.com Signed-off-by: Geert Uytterhoeven Reviewed-by: Brian Masney Reviewed-by: Mathieu Dubois-Briand Signed-off-by: Stephen Boyd --- drivers/clk/ti/adpll.c | 4 ++-- drivers/clk/ti/divider.c | 2 +- drivers/clk/ti/mux.c | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/clk/ti/adpll.c b/drivers/clk/ti/adpll.c index e305fcbac647..8885d28face5 100644 --- a/drivers/clk/ti/adpll.c +++ b/drivers/clk/ti/adpll.c @@ -483,7 +483,7 @@ static const struct clk_ops ti_adpll_ops = { static int ti_adpll_init_dco(struct ti_adpll_data *d) { - struct clk_init_data init; + struct clk_init_data init = {}; struct clk *clock; const char *postfix; int width, err; @@ -576,7 +576,7 @@ static int ti_adpll_init_clkout(struct ti_adpll_data *d, struct clk *clk1) { struct ti_adpll_clkout_data *co; - struct clk_init_data init; + struct clk_init_data init = {}; struct clk_ops *ops; const char *parent_names[2]; const char *child_name; diff --git a/drivers/clk/ti/divider.c b/drivers/clk/ti/divider.c index af923b8cb0ed..3b438c2d68aa 100644 --- a/drivers/clk/ti/divider.c +++ b/drivers/clk/ti/divider.c @@ -311,7 +311,7 @@ static struct clk *_register_divider(struct device_node *node, u32 flags, struct clk_omap_divider *div) { - struct clk_init_data init; + struct clk_init_data init = {}; const char *parent_name; const char *name; diff --git a/drivers/clk/ti/mux.c b/drivers/clk/ti/mux.c index ded4432f7528..0fef60e82107 100644 --- a/drivers/clk/ti/mux.c +++ b/drivers/clk/ti/mux.c @@ -124,9 +124,9 @@ static struct clk *_register_mux(struct device_node *node, const char *name, struct clk_omap_reg *reg, u8 shift, u32 mask, s8 latch, u8 clk_mux_flags, u32 *table) { + struct clk_init_data init = {}; struct clk_omap_mux *mux; struct clk *clk; - struct clk_init_data init; /* allocate the mux */ mux = kzalloc_obj(*mux); From 39c0e6c844a14945040cca4ccf6997792ab764c4 Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Wed, 19 Aug 2026 21:05:16 +0200 Subject: [PATCH 111/112] clk: visconti: Make sure clk_init_data is fully initialized The clk_init_data structure contains several mutually-exclusive members for different methods to specify the possible parents of a clock, prompting drivers to initialize only the members they need. However, not initializing all members may cause subtle issues, which are only exposed when CONFIG_INIT_STACK_ALL_PATTERN or CONFIG_INIT_STACK_NONE is enabled. visconti_clk_register_gate() fills in init.parent_data, and assumes that init.parent_names is NULL. However, the latter in uninitialized, and thus may cause a crash. Make sure all members are fully initialized, to fix such bugs, and to avoid future breakage when converting drivers to a different method for specifying the parents. Fixes: b4cbe606dc3674b2 ("clk: visconti: Add support common clock driver and reset driver") Signed-off-by: Geert Uytterhoeven Reviewed-by: Brian Masney Reviewed-by: Nobuhiro Iwamatsu Signed-off-by: Stephen Boyd --- drivers/clk/visconti/clkc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/clk/visconti/clkc.c b/drivers/clk/visconti/clkc.c index d0b193b5d0b3..4018d1298880 100644 --- a/drivers/clk/visconti/clkc.c +++ b/drivers/clk/visconti/clkc.c @@ -81,9 +81,9 @@ static struct clk_hw *visconti_clk_register_gate(struct device *dev, u8 rs_idx, spinlock_t *lock) { + struct clk_init_data init = {}; struct visconti_clk_gate *gate; struct clk_parent_data *pdata; - struct clk_init_data init; struct clk_hw *hw; int ret; From fbfa013eeac299ecc015cb14fa40e382a23fb489 Mon Sep 17 00:00:00 2001 From: Pedro Kopper Date: Fri, 14 Aug 2026 10:22:13 +0100 Subject: [PATCH 112/112] clk: microchip: mpfs: fix regmap_update_bits() mask/val order mpfs_cfg_clk_set_rate() passes the mask and value arguments to regmap_update_bits() in the wrong order. The resulting write becomes reg = orig_reg | val, causing bits to not be cleared if the clock divider changes. Pass the arguments in the correct order so the divider field is updated as intended. Fixes: c6f2dddfa7f9 ("clk: microchip: mpfs: use regmap for clocks") Signed-off-by: Pedro Kopper Reviewed-by: Conor Dooley Cc: stable@vger.kernel.org Signed-off-by: Stephen Boyd --- drivers/clk/microchip/clk-mpfs.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/clk/microchip/clk-mpfs.c b/drivers/clk/microchip/clk-mpfs.c index ee58304913ef..7f044775aea8 100644 --- a/drivers/clk/microchip/clk-mpfs.c +++ b/drivers/clk/microchip/clk-mpfs.c @@ -285,7 +285,7 @@ static int mpfs_cfg_clk_set_rate(struct clk_hw *hw, unsigned long rate, unsigned mask = clk_div_mask(cfg->width) << cfg->shift; val = divider_setting << cfg->shift; - regmap_update_bits(cfg->map, cfg->map_offset, val, mask); + regmap_update_bits(cfg->map, cfg->map_offset, mask, val); return 0; }