From 6bbac64095dfa606fac5fe87e9161913cae92294 Mon Sep 17 00:00:00 2001 From: YuXin Xiao Date: Tue, 25 Aug 2026 02:15:59 +0800 Subject: [PATCH 01/37] i2c: acpi: Add DELL0A86 to i2c_acpi_force_100khz_device_ids The ELAN 04F3:3185 touchpad exposed as DELL0A86 on the Dell Inspiron 13 5310 intermittently exhibits excessive smoothing when the I2C bus runs at 400 kHz. During an affected period pointer movement becomes severely sluggish and sticky for tens of seconds. The ACPI firmware configures the touchpad bus for 400 kHz. Add DELL0A86 to i2c_acpi_force_100khz_device_ids so that the bus runs at 100 kHz, as is already done for other touchpads exhibiting the same excessive smoothing problem. With the quirk applied, the kernel reports that the firmware requested 400 kHz and that the bus is forced to 100 kHz. The problem did not recur during extended heavy use, including an S4 hibernate/resume cycle. An independent 2021 report from another Dell Inspiron 13 5310 user describes the same intermittent sticky behavior and identifies the same DELL0A86 / 04F3:3185 touchpad. Link: https://www.reddit.com/r/linuxquestions/comments/nsso5b/help_needed_with_sticky_touchpad_spoiling_brand/ Signed-off-by: YuXin Xiao Assisted-by: Codex:ChatGPT-5.6-Sol Assisted-by: OpenCode:Ox Alpha (x-preview-f-free) Acked-by: Mika Westerberg Signed-off-by: Andi Shyti Link: https://patch.msgid.link/20260824181559.146133-1-xiaoyueyoqwq@gmail.com --- drivers/i2c/i2c-core-acpi.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/i2c/i2c-core-acpi.c b/drivers/i2c/i2c-core-acpi.c index 8f3bdd50186e..3af0e0551185 100644 --- a/drivers/i2c/i2c-core-acpi.c +++ b/drivers/i2c/i2c-core-acpi.c @@ -371,6 +371,7 @@ static const struct acpi_device_id i2c_acpi_force_100khz_device_ids[] = { * the device works without issues on Windows at what is expected to be * a 400KHz frequency. The root cause of the issue is not known. */ + { "DELL0A86", 0 }, { "DLL0945", 0 }, { "ELAN0678", 0 }, { "ELAN06FA", 0 }, From 7659ba45c535cb43c7a354aa8a43ca9ae57915fb Mon Sep 17 00:00:00 2001 From: Alex Tran Date: Wed, 26 Aug 2026 10:19:26 -0700 Subject: [PATCH 02/37] i2c: busses: Use pm_runtime_resume_and_get() Utilize the provided pm_runtime_resume_and_get api to increase the usage count and call the rpm resume callback. Upon failure, the function takes care of calling pm_runtime_put_noidle. Remove the explicit call to put no idle. No functional change added. Signed-off-by: Alex Tran Reviewed-by: Mukesh Kumar Savaliya Signed-off-by: Andi Shyti Link: https://patch.msgid.link/20260826-i2c-qcom-geni-pm-runtime-resume-get-v1-1-25ee55d1f0c8@oss.qualcomm.com --- drivers/i2c/busses/i2c-qcom-geni.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/i2c/busses/i2c-qcom-geni.c b/drivers/i2c/busses/i2c-qcom-geni.c index 00013b41a6f5..942cfeee99ba 100644 --- a/drivers/i2c/busses/i2c-qcom-geni.c +++ b/drivers/i2c/busses/i2c-qcom-geni.c @@ -963,10 +963,9 @@ static int geni_i2c_xfer(struct i2c_adapter *adap, struct geni_i2c_dev *gi2c = i2c_get_adapdata(adap); int ret; - ret = pm_runtime_get_sync(gi2c->se.dev); + ret = pm_runtime_resume_and_get(gi2c->se.dev); if (ret < 0) { dev_err(gi2c->se.dev, "error turning SE resources:%d\n", ret); - pm_runtime_put_noidle(gi2c->se.dev); /* Set device in suspended since resume failed */ pm_runtime_set_suspended(gi2c->se.dev); return ret; From 7159c712c182fc03c41cdfcab84fca88b3fc4900 Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Wed, 26 Aug 2026 12:30:43 +0200 Subject: [PATCH 03/37] dt-bindings: i2c: xlnx,xps-iic-2.00.a: Drop bouncing mocean-labs.com Address info@mocean-labs.com bounces permanently (reason: 550 Host unknown), so switch the maintainer to Michal Simek. Signed-off-by: Krzysztof Kozlowski Acked-by: Conor Dooley Acked-by: Michal Simek Signed-off-by: Andi Shyti Link: https://patch.msgid.link/20260826103042.147735-2-krzysztof.kozlowski@oss.qualcomm.com --- Documentation/devicetree/bindings/i2c/xlnx,xps-iic-2.00.a.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Documentation/devicetree/bindings/i2c/xlnx,xps-iic-2.00.a.yaml b/Documentation/devicetree/bindings/i2c/xlnx,xps-iic-2.00.a.yaml index 658ae92fa86d..d1f26d5015f7 100644 --- a/Documentation/devicetree/bindings/i2c/xlnx,xps-iic-2.00.a.yaml +++ b/Documentation/devicetree/bindings/i2c/xlnx,xps-iic-2.00.a.yaml @@ -7,7 +7,7 @@ $schema: http://devicetree.org/meta-schemas/core.yaml# title: Xilinx IIC controller maintainers: - - info@mocean-labs.com + - Michal Simek allOf: - $ref: /schemas/i2c/i2c-controller.yaml# From cba655492c205f3e65b5fe94ae5bb2a8ced3170b Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Fri, 21 Aug 2026 12:03:30 +0200 Subject: [PATCH 04/37] i2c: bcm2835: 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. Make sure all members are fully initialized, to avoid such bugs, and to prevent future breakage when converting drivers to a different method for specifying the parents. Signed-off-by: Geert Uytterhoeven Reviewed-by: Florian Fainelli Reviewed-by: Brian Masney Signed-off-by: Andi Shyti Link: https://patch.msgid.link/9f82f37e6d6c069cd44326bcd5e5a2a8069a13a9.1787239980.git.geert+renesas@glider.be --- drivers/i2c/busses/i2c-bcm2835.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/i2c/busses/i2c-bcm2835.c b/drivers/i2c/busses/i2c-bcm2835.c index 0d7e2654a534..b30f7e0e45f1 100644 --- a/drivers/i2c/busses/i2c-bcm2835.c +++ b/drivers/i2c/busses/i2c-bcm2835.c @@ -166,7 +166,7 @@ static struct clk *bcm2835_i2c_register_div(struct device *dev, struct clk *mclk, struct bcm2835_i2c_dev *i2c_dev) { - struct clk_init_data init; + struct clk_init_data init = {}; struct clk_bcm2835_i2c *priv; char name[32]; const char *mclk_name; From 95bfa7c11ad43e86e2e16e1d5e8aafba19703392 Mon Sep 17 00:00:00 2001 From: Thorsten Blum Date: Wed, 9 Sep 2026 10:20:55 +0200 Subject: [PATCH 05/37] media: i2c: isl7998x: Inline i2c_check_functionality() check Inline the i2c_check_functionality() check, since the function returns a boolean status rather than an error code. Reviewed-by: Michael Tretter Signed-off-by: Thorsten Blum Signed-off-by: Andi Shyti Link: https://patch.msgid.link/20260909082053.410739-4-blum@kernel.org --- drivers/media/i2c/isl7998x.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/media/i2c/isl7998x.c b/drivers/media/i2c/isl7998x.c index a77538d2343c..8244d4296a02 100644 --- a/drivers/media/i2c/isl7998x.c +++ b/drivers/media/i2c/isl7998x.c @@ -1460,8 +1460,7 @@ static int isl7998x_probe(struct i2c_client *client) int nr_inputs; int ret; - ret = i2c_check_functionality(adapter, I2C_FUNC_SMBUS_WORD_DATA); - if (!ret) { + if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_WORD_DATA)) { dev_warn(&adapter->dev, "I2C-Adapter doesn't support I2C_FUNC_SMBUS_WORD\n"); return -EIO; From 4c76dc90f2d0f324b0f72faf1c82f8ed17d2ef6b Mon Sep 17 00:00:00 2001 From: Thorsten Blum Date: Wed, 9 Sep 2026 10:20:56 +0200 Subject: [PATCH 06/37] i2c: Change return type to bool for i2c_check_functionality() i2c_check_functionality() returns a bool - change the return type from int to bool and update the comment accordingly. Signed-off-by: Thorsten Blum Acked-by: Mukesh Kumar Savaliya Signed-off-by: Andi Shyti Link: https://patch.msgid.link/20260909082053.410739-5-blum@kernel.org --- include/linux/i2c.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/include/linux/i2c.h b/include/linux/i2c.h index 14ab4d3055af..4af2df969a05 100644 --- a/include/linux/i2c.h +++ b/include/linux/i2c.h @@ -919,8 +919,8 @@ static inline u32 i2c_get_functionality(struct i2c_adapter *adap) return adap->algo->functionality(adap); } -/* Return 1 if adapter supports everything we need, 0 if not. */ -static inline int i2c_check_functionality(struct i2c_adapter *adap, u32 func) +/* Return true if adapter supports everything we need, false if not. */ +static inline bool i2c_check_functionality(struct i2c_adapter *adap, u32 func) { return (func & i2c_get_functionality(adap)) == func; } From 3d067add128157a8d812a9882c15b7e81097a4f2 Mon Sep 17 00:00:00 2001 From: Jia Wang Date: Mon, 7 Sep 2026 17:54:45 +0800 Subject: [PATCH 07/37] dt-bindings: i2c: dw: Add compatible for UltraRISC DP1000 SoC The DP1000 integrates four DesignWare APB I2C controllers. I2C1 supports SMBus, while I2C0, I2C2, and I2C3 support standard I2C transfers. Each controller has one register range and uses separate reference and APB interface clocks. Add the DP1000-specific compatible string with the generic one as a fallback. Signed-off-by: Jia Wang Acked-by: Conor Dooley Acked-by: Mika Westerberg Signed-off-by: Andi Shyti Link: https://patch.msgid.link/20260907-ultrarisc-dts-v2-6-5eb4c97477c5@ultrarisc.com --- .../bindings/i2c/snps,designware-i2c.yaml | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/Documentation/devicetree/bindings/i2c/snps,designware-i2c.yaml b/Documentation/devicetree/bindings/i2c/snps,designware-i2c.yaml index 467bdcbb8538..8a7c0a44601b 100644 --- a/Documentation/devicetree/bindings/i2c/snps,designware-i2c.yaml +++ b/Documentation/devicetree/bindings/i2c/snps,designware-i2c.yaml @@ -21,6 +21,20 @@ allOf: properties: reg: maxItems: 1 + - if: + properties: + compatible: + contains: + const: ultrarisc,dp1000-i2c + then: + properties: + clocks: + minItems: 2 + clock-names: + minItems: 2 + required: + - clocks + - clock-names properties: compatible: @@ -44,6 +58,7 @@ properties: - mscc,ocelot-i2c - sophgo,sg2044-i2c - thead,th1520-i2c + - ultrarisc,dp1000-i2c - const: snps,designware-i2c reg: From b1218b8e5fe5202c6b6bc3d9ce7b0c390f195274 Mon Sep 17 00:00:00 2001 From: Mukesh Kumar Savaliya Date: Wed, 29 Jul 2026 13:00:40 +0530 Subject: [PATCH 08/37] i2c: qcom-geni: Use modern PM operation macros The driver uses the legacy SET_NOIRQ_SYSTEM_SLEEP_PM_OPS() and SET_RUNTIME_PM_OPS() helpers to initialize struct dev_pm_ops. Switch to the modern NOIRQ_SYSTEM_SLEEP_PM_OPS() and RUNTIME_PM_OPS() macros instead. These macros keep PM callbacks referenced by the compiler and help avoid potential unused-function warnings in configurations where PM support is disabled or partially enabled. No functional change intended. Signed-off-by: Mukesh Kumar Savaliya Reviewed-by: Viken Dadhaniya Signed-off-by: Andi Shyti Link: https://patch.msgid.link/20260729073040.3227692-1-mukesh.savaliya@oss.qualcomm.com --- drivers/i2c/busses/i2c-qcom-geni.c | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/drivers/i2c/busses/i2c-qcom-geni.c b/drivers/i2c/busses/i2c-qcom-geni.c index 942cfeee99ba..7e4e46a30b58 100644 --- a/drivers/i2c/busses/i2c-qcom-geni.c +++ b/drivers/i2c/busses/i2c-qcom-geni.c @@ -1212,7 +1212,7 @@ static void geni_i2c_shutdown(struct platform_device *pdev) i2c_mark_adapter_suspended(&gi2c->adap); } -static int __maybe_unused geni_i2c_runtime_suspend(struct device *dev) +static int geni_i2c_runtime_suspend(struct device *dev) { int ret = 0; struct geni_i2c_dev *gi2c = dev_get_drvdata(dev); @@ -1230,7 +1230,7 @@ static int __maybe_unused geni_i2c_runtime_suspend(struct device *dev) return 0; } -static int __maybe_unused geni_i2c_runtime_resume(struct device *dev) +static int geni_i2c_runtime_resume(struct device *dev) { int ret = 0; struct geni_i2c_dev *gi2c = dev_get_drvdata(dev); @@ -1246,7 +1246,7 @@ static int __maybe_unused geni_i2c_runtime_resume(struct device *dev) return 0; } -static int __maybe_unused geni_i2c_suspend_noirq(struct device *dev) +static int geni_i2c_suspend_noirq(struct device *dev) { struct geni_i2c_dev *gi2c = dev_get_drvdata(dev); int ret; @@ -1260,7 +1260,7 @@ static int __maybe_unused geni_i2c_suspend_noirq(struct device *dev) return ret; } -static int __maybe_unused geni_i2c_resume_noirq(struct device *dev) +static int geni_i2c_resume_noirq(struct device *dev) { struct geni_i2c_dev *gi2c = dev_get_drvdata(dev); int ret; @@ -1274,9 +1274,10 @@ static int __maybe_unused geni_i2c_resume_noirq(struct device *dev) } static const struct dev_pm_ops geni_i2c_pm_ops = { - SET_NOIRQ_SYSTEM_SLEEP_PM_OPS(geni_i2c_suspend_noirq, geni_i2c_resume_noirq) - SET_RUNTIME_PM_OPS(geni_i2c_runtime_suspend, geni_i2c_runtime_resume, - NULL) + NOIRQ_SYSTEM_SLEEP_PM_OPS(geni_i2c_suspend_noirq, + geni_i2c_resume_noirq) + RUNTIME_PM_OPS(geni_i2c_runtime_suspend, geni_i2c_runtime_resume, + NULL) }; static const struct geni_i2c_desc geni_i2c = { From 309520d97b41466712335c227c150c17fd8f1ecb Mon Sep 17 00:00:00 2001 From: Mukesh Kumar Savaliya Date: Wed, 5 Aug 2026 12:30:13 +0530 Subject: [PATCH 09/37] i2c: qcom-geni: Simplify PM resume error handling The driver currently logs pm_runtime_resume_and_get() failures with dev_err() and returns the error code separately. Replace this with dev_err_probe(), which combines the dev_err() log message and the return value propagation into a single call, consistent with how all other error paths in this driver already report failures. No functional change intended. Signed-off-by: Mukesh Kumar Savaliya Acked-by: Praveen Talari Reviewed-by: Viken Dadhaniya Signed-off-by: Andi Shyti Link: https://patch.msgid.link/20260805070013.494426-1-mukesh.savaliya@oss.qualcomm.com --- drivers/i2c/busses/i2c-qcom-geni.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/drivers/i2c/busses/i2c-qcom-geni.c b/drivers/i2c/busses/i2c-qcom-geni.c index 7e4e46a30b58..ea854e78ef9c 100644 --- a/drivers/i2c/busses/i2c-qcom-geni.c +++ b/drivers/i2c/busses/i2c-qcom-geni.c @@ -1040,10 +1040,8 @@ static int geni_i2c_init(struct geni_i2c_dev *gi2c) int ret; ret = pm_runtime_resume_and_get(gi2c->se.dev); - if (ret < 0) { - dev_err(gi2c->se.dev, "error turning on device :%d\n", ret); - return ret; - } + if (ret < 0) + return dev_err_probe(gi2c->se.dev, ret, "error turning on device\n"); proto = geni_se_read_proto(&gi2c->se); if (proto == GENI_SE_INVALID_PROTO) { From 04fc8ea03be182b31d86cbf1f5afea3b859d348f Mon Sep 17 00:00:00 2001 From: Wolfram Sang Date: Wed, 9 Sep 2026 20:35:32 +0200 Subject: [PATCH 10/37] dt-bindings: i2c: renesas,rcar-i2c: allow 6 DMA channels R-Car Gen5 can make use of it. Signed-off-by: Wolfram Sang Reviewed-by: Geert Uytterhoeven Acked-by: Krzysztof Kozlowski Signed-off-by: Andi Shyti Link: https://patch.msgid.link/20260909184200.3319-2-wsa+renesas@sang-engineering.com --- Documentation/devicetree/bindings/i2c/renesas,rcar-i2c.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Documentation/devicetree/bindings/i2c/renesas,rcar-i2c.yaml b/Documentation/devicetree/bindings/i2c/renesas,rcar-i2c.yaml index f9ede03c3287..4148081e3d84 100644 --- a/Documentation/devicetree/bindings/i2c/renesas,rcar-i2c.yaml +++ b/Documentation/devicetree/bindings/i2c/renesas,rcar-i2c.yaml @@ -83,14 +83,14 @@ properties: dmas: minItems: 2 - maxItems: 4 + maxItems: 6 description: Must contain a list of pairs of references to DMA specifiers, one for transmission, and one for reception. dma-names: minItems: 2 - maxItems: 4 + maxItems: 6 items: enum: - tx From ff9abb51615c7eb4160a0b2222650a51a3c90bc2 Mon Sep 17 00:00:00 2001 From: Ahmad Byagowi Date: Mon, 7 Sep 2026 23:26:22 -0700 Subject: [PATCH 11/37] i2c: mux: Factor out channel node lookup Move the existing Device Tree channel-node lookup into a helper in preparation for using generic firmware-node operations. This is a pure refactoring with no functional change. Signed-off-by: Ahmad Byagowi Reviewed-by: Andy Shevchenko Acked-by: Peter Rosin Signed-off-by: Andi Shyti Link: https://patch.msgid.link/adf12f66229d5faa0bc75571da1c3f53a1c81dd8.1788623619.git.ahmadexp@gmail.com --- drivers/i2c/i2c-mux.c | 84 +++++++++++++++++++++++-------------------- 1 file changed, 46 insertions(+), 38 deletions(-) diff --git a/drivers/i2c/i2c-mux.c b/drivers/i2c/i2c-mux.c index 68a4c34b5987..d207f0e81807 100644 --- a/drivers/i2c/i2c-mux.c +++ b/drivers/i2c/i2c-mux.c @@ -264,6 +264,51 @@ static const struct i2c_lock_operations i2c_parent_lock_ops = { .unlock_bus = i2c_parent_unlock_bus, }; +static struct device_node * +i2c_mux_get_channel_node(struct i2c_mux_core *muxc, u32 chan_id) +{ + struct device_node *dev_node; + struct device_node *mux_node; + struct device_node *child; + u32 reg; + int ret; + + dev_node = dev_of_node(muxc->dev); + if (!dev_node) + return NULL; + + if (muxc->arbitrator) + mux_node = of_get_child_by_name(dev_node, "i2c-arb"); + else if (muxc->gate) + mux_node = of_get_child_by_name(dev_node, "i2c-gate"); + else + mux_node = of_get_child_by_name(dev_node, "i2c-mux"); + + if (mux_node) { + /* A "reg" property indicates an old-style DT entry */ + if (!of_property_read_u32(mux_node, "reg", ®)) { + of_node_put(mux_node); + mux_node = NULL; + } + } + + if (!mux_node) + mux_node = of_node_get(dev_node); + else if (muxc->arbitrator || muxc->gate) + return mux_node; + + for_each_child_of_node(mux_node, child) { + ret = of_property_read_u32(child, "reg", ®); + if (ret) + continue; + if (chan_id == reg) + break; + } + + of_node_put(mux_node); + return child; +} + int i2c_mux_add_adapter(struct i2c_mux_core *muxc, u32 force_nr, u32 chan_id) { @@ -327,44 +372,7 @@ int i2c_mux_add_adapter(struct i2c_mux_core *muxc, * Try to populate the mux adapter's of_node, expands to * nothing if !CONFIG_OF. */ - if (muxc->dev->of_node) { - struct device_node *dev_node = muxc->dev->of_node; - struct device_node *mux_node, *child = NULL; - u32 reg; - - if (muxc->arbitrator) - mux_node = of_get_child_by_name(dev_node, "i2c-arb"); - else if (muxc->gate) - mux_node = of_get_child_by_name(dev_node, "i2c-gate"); - else - mux_node = of_get_child_by_name(dev_node, "i2c-mux"); - - if (mux_node) { - /* A "reg" property indicates an old-style DT entry */ - if (!of_property_read_u32(mux_node, "reg", ®)) { - of_node_put(mux_node); - mux_node = NULL; - } - } - - if (!mux_node) - mux_node = of_node_get(dev_node); - else if (muxc->arbitrator || muxc->gate) - child = of_node_get(mux_node); - - if (!child) { - for_each_child_of_node(mux_node, child) { - ret = of_property_read_u32(child, "reg", ®); - if (ret) - continue; - if (chan_id == reg) - break; - } - } - - priv->adap.dev.of_node = child; - of_node_put(mux_node); - } + priv->adap.dev.of_node = i2c_mux_get_channel_node(muxc, chan_id); /* * Associate the mux channel with an ACPI node. From 25083d5c01ec56046acfa84b2db084aec0275810 Mon Sep 17 00:00:00 2001 From: Ahmad Byagowi Date: Mon, 7 Sep 2026 23:26:23 -0700 Subject: [PATCH 12/37] i2c: mux: Propagate firmware nodes to channel adapters Device Tree channel nodes are associated with the adapters created by i2c-mux, but equivalent firmware-node descriptions are not. Use generic firmware-node operations for the existing channel lookup and associate the returned node with the adapter. Do not restrict the lookup by firmware-node type, so Device Tree, software nodes, and ACPI descriptions all follow the same property traversal. The existing acpi_preset_companion() call remains in place for the standard ACPI channel association. Keep a separate reference to the node returned by the generic lookup because acpi_preset_companion() may replace the device's primary firmware node. Release the saved reference after adapter deletion. Signed-off-by: Ahmad Byagowi Reviewed-by: Andy Shevchenko Acked-by: Peter Rosin Signed-off-by: Andi Shyti Link: https://patch.msgid.link/00b33b4b352848ff87dd9089190b35d8732676f3.1788623619.git.ahmadexp@gmail.com --- drivers/i2c/i2c-mux.c | 46 +++++++++++++++++++++---------------------- 1 file changed, 22 insertions(+), 24 deletions(-) diff --git a/drivers/i2c/i2c-mux.c b/drivers/i2c/i2c-mux.c index d207f0e81807..53428098ddc7 100644 --- a/drivers/i2c/i2c-mux.c +++ b/drivers/i2c/i2c-mux.c @@ -24,7 +24,7 @@ #include #include #include -#include +#include #include #include @@ -33,6 +33,7 @@ struct i2c_mux_priv { struct i2c_adapter adap; struct i2c_algorithm algo; struct i2c_mux_core *muxc; + struct fwnode_handle *channel_node; u32 chan_id; }; @@ -264,48 +265,48 @@ static const struct i2c_lock_operations i2c_parent_lock_ops = { .unlock_bus = i2c_parent_unlock_bus, }; -static struct device_node * +static struct fwnode_handle * i2c_mux_get_channel_node(struct i2c_mux_core *muxc, u32 chan_id) { - struct device_node *dev_node; - struct device_node *mux_node; - struct device_node *child; + struct fwnode_handle *dev_node; + struct fwnode_handle *mux_node; + struct fwnode_handle *child; u32 reg; int ret; - dev_node = dev_of_node(muxc->dev); + dev_node = dev_fwnode(muxc->dev); if (!dev_node) return NULL; if (muxc->arbitrator) - mux_node = of_get_child_by_name(dev_node, "i2c-arb"); + mux_node = fwnode_get_named_child_node(dev_node, "i2c-arb"); else if (muxc->gate) - mux_node = of_get_child_by_name(dev_node, "i2c-gate"); + mux_node = fwnode_get_named_child_node(dev_node, "i2c-gate"); else - mux_node = of_get_child_by_name(dev_node, "i2c-mux"); + mux_node = fwnode_get_named_child_node(dev_node, "i2c-mux"); if (mux_node) { - /* A "reg" property indicates an old-style DT entry */ - if (!of_property_read_u32(mux_node, "reg", ®)) { - of_node_put(mux_node); + /* A "reg" property indicates an old-style firmware entry. */ + if (!fwnode_property_read_u32(mux_node, "reg", ®)) { + fwnode_handle_put(mux_node); mux_node = NULL; } } if (!mux_node) - mux_node = of_node_get(dev_node); + mux_node = fwnode_handle_get(dev_node); else if (muxc->arbitrator || muxc->gate) return mux_node; - for_each_child_of_node(mux_node, child) { - ret = of_property_read_u32(child, "reg", ®); + fwnode_for_each_child_node(mux_node, child) { + ret = fwnode_property_read_u32(child, "reg", ®); if (ret) continue; if (chan_id == reg) break; } - of_node_put(mux_node); + fwnode_handle_put(mux_node); return child; } @@ -368,11 +369,9 @@ int i2c_mux_add_adapter(struct i2c_mux_core *muxc, else priv->adap.lock_ops = &i2c_parent_lock_ops; - /* - * Try to populate the mux adapter's of_node, expands to - * nothing if !CONFIG_OF. - */ - priv->adap.dev.of_node = i2c_mux_get_channel_node(muxc, chan_id); + /* Associate the mux adapter with its channel firmware node. */ + priv->channel_node = i2c_mux_get_channel_node(muxc, chan_id); + device_set_node(&priv->adap.dev, priv->channel_node); /* * Associate the mux channel with an ACPI node. @@ -416,7 +415,7 @@ int i2c_mux_add_adapter(struct i2c_mux_core *muxc, return 0; err_free_priv: - of_node_put(priv->adap.dev.of_node); + fwnode_handle_put(priv->channel_node); kfree(priv); return ret; } @@ -429,7 +428,6 @@ void i2c_mux_del_adapters(struct i2c_mux_core *muxc) while (muxc->num_adapters) { struct i2c_adapter *adap = muxc->adapter[--muxc->num_adapters]; struct i2c_mux_priv *priv = adap->algo_data; - struct device_node *np = adap->dev.of_node; muxc->adapter[muxc->num_adapters] = NULL; @@ -439,7 +437,7 @@ void i2c_mux_del_adapters(struct i2c_mux_core *muxc) sysfs_remove_link(&priv->adap.dev.kobj, "mux_device"); i2c_del_adapter(adap); - of_node_put(np); + fwnode_handle_put(priv->channel_node); kfree(priv); } } From 5e6e005ffb9770f103a694b2f1cf60a622320b83 Mon Sep 17 00:00:00 2001 From: Felix Gu Date: Sat, 5 Sep 2026 21:25:15 +0800 Subject: [PATCH 13/37] i2c: ljca: drop redundant explicit adapter deletion This driver uses devm_i2c_add_adapter(), so manual i2c_del_adapter() in remove() is unnecessary. Remove ljca_i2c_remove() along with the now unneeded auxiliary_setdrvdata(). Signed-off-by: Felix Gu Acked-by: Sakari Ailus Signed-off-by: Andi Shyti Link: https://patch.msgid.link/20260905-ljca-v1-1-9109616ff1c6@gmail.com --- drivers/i2c/busses/i2c-ljca.c | 9 --------- 1 file changed, 9 deletions(-) diff --git a/drivers/i2c/busses/i2c-ljca.c b/drivers/i2c/busses/i2c-ljca.c index 93274f0c2d72..da3370968c8e 100644 --- a/drivers/i2c/busses/i2c-ljca.c +++ b/drivers/i2c/busses/i2c-ljca.c @@ -297,7 +297,6 @@ static int ljca_i2c_probe(struct auxiliary_device *auxdev, device_set_node(&ljca_i2c->adap.dev, dev_fwnode(&auxdev->dev)); i2c_set_adapdata(&ljca_i2c->adap, ljca_i2c); - auxiliary_set_drvdata(auxdev, ljca_i2c); ret = ljca_i2c_init(ljca_i2c, ljca_i2c->i2c_info->id); if (ret) @@ -315,13 +314,6 @@ static int ljca_i2c_probe(struct auxiliary_device *auxdev, return 0; } -static void ljca_i2c_remove(struct auxiliary_device *auxdev) -{ - struct ljca_i2c_dev *ljca_i2c = auxiliary_get_drvdata(auxdev); - - i2c_del_adapter(&ljca_i2c->adap); -} - static const struct auxiliary_device_id ljca_i2c_id_table[] = { { "usb_ljca.ljca-i2c", 0 }, { /* sentinel */ } @@ -330,7 +322,6 @@ MODULE_DEVICE_TABLE(auxiliary, ljca_i2c_id_table); static struct auxiliary_driver ljca_i2c_driver = { .probe = ljca_i2c_probe, - .remove = ljca_i2c_remove, .id_table = ljca_i2c_id_table, }; module_auxiliary_driver(ljca_i2c_driver); From 0da2c96cf69df277275b3e7e57118a34b50c8177 Mon Sep 17 00:00:00 2001 From: Jerome Brunet Date: Thu, 24 Sep 2026 18:05:50 +0200 Subject: [PATCH 14/37] dt-bindings: i2c: mv64xxx: Add Allwinner A733 compatible string The I2C controller IP used in the Allwinner A733 SoC is compatible with the ones used in the other recent Allwinner SoCs. Add the A733 specific compatible string to the list of existing names falling back to the allwinner,sun8i-v536-i2c string. Signed-off-by: Jerome Brunet Acked-by: Conor Dooley Signed-off-by: Andi Shyti Link: https://patch.msgid.link/20260924-allwinner-a733-i2c-compatible-v2-1-0f5f1e9af969@baylibre.com --- Documentation/devicetree/bindings/i2c/marvell,mv64xxx-i2c.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/Documentation/devicetree/bindings/i2c/marvell,mv64xxx-i2c.yaml b/Documentation/devicetree/bindings/i2c/marvell,mv64xxx-i2c.yaml index c4efcef59133..2a9e723a724f 100644 --- a/Documentation/devicetree/bindings/i2c/marvell,mv64xxx-i2c.yaml +++ b/Documentation/devicetree/bindings/i2c/marvell,mv64xxx-i2c.yaml @@ -34,6 +34,7 @@ properties: - allwinner,sun50i-h616-i2c - allwinner,sun50i-r329-i2c - allwinner,sun55i-a523-i2c + - allwinner,sun60i-a733-i2c - const: allwinner,sun8i-v536-i2c - const: allwinner,sun6i-a31-i2c - const: marvell,mv64xxx-i2c From 68f382641f8c8034a7867f7ece8e29ca1ce18601 Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Tue, 28 Jul 2026 23:00:23 -0700 Subject: [PATCH 15/37] i2c: smbus: make i2c_smbus_read_block_data() safer i2c_smbus_read_block_data() is dangerous to use because it may deliver up to I2C_SMBUS_BLOCK_MAX (32) bytes, which may be surprising to the caller. Callers tend to allocate buffers of sizes big enough to hold data from a well-behaving device and do not expect that i2c_smbus_read_block_data() may attempt to write more data than expected. To make i2c_smbus_read_block_data() safer to use, change it so that it accepts size of the supplied buffer as another argument and ensure that it will not copy more data than the size of the buffer. Signal oversized responses with -EMSGSIZE. To allow users to gradually transition to the new API employ some macro trickery allowing calling i2c_smbus_read_block_data() with either 3 or 4 arguments. When called with 3 arguments it is assumed that the buffer size is I2C_SMBUS_BLOCK_MAX bytes. Once everyone is transitioned to the 4 argument form the macros should be removed. Signed-off-by: Dmitry Torokhov Signed-off-by: Andi Shyti Link: https://patch.msgid.link/ammWUROtUGeriY8G@google.com --- drivers/i2c/i2c-core-smbus.c | 24 ++++++++++++++++-------- include/linux/i2c.h | 19 +++++++++++++++++-- 2 files changed, 33 insertions(+), 10 deletions(-) diff --git a/drivers/i2c/i2c-core-smbus.c b/drivers/i2c/i2c-core-smbus.c index fa63bee0b345..57f55bbab8a1 100644 --- a/drivers/i2c/i2c-core-smbus.c +++ b/drivers/i2c/i2c-core-smbus.c @@ -208,11 +208,11 @@ s32 i2c_smbus_write_word_data(const struct i2c_client *client, u8 command, EXPORT_SYMBOL(i2c_smbus_write_word_data); /** - * i2c_smbus_read_block_data - SMBus "block read" protocol + * __i2c_smbus_read_block_data - SMBus "block read" protocol * @client: Handle to slave device * @command: Byte interpreted by slave - * @values: Byte array into which data will be read; big enough to hold - * the data returned by the slave. SMBus allows at most 32 bytes. + * @length: size of the @values array. SMBus allows at most 32 bytes + * @values: Byte array into which data will be read * * This executes the SMBus "block read" protocol, returning negative errno * else the number of data bytes in the slave's response. @@ -222,22 +222,30 @@ EXPORT_SYMBOL(i2c_smbus_write_word_data); * support this; its emulation through I2C messaging relies on a specific * mechanism (I2C_M_RECV_LEN) which may not be implemented. */ -s32 i2c_smbus_read_block_data(const struct i2c_client *client, u8 command, - u8 *values) +s32 __i2c_smbus_read_block_data(const struct i2c_client *client, u8 command, + u8 length, u8 *values) { union i2c_smbus_data data; + int ret_len; int status; + if (length > I2C_SMBUS_BLOCK_MAX) + return -EINVAL; + status = i2c_smbus_xfer(client->adapter, client->addr, client->flags, I2C_SMBUS_READ, command, I2C_SMBUS_BLOCK_DATA, &data); if (status) return status; - memcpy(values, &data.block[1], data.block[0]); - return data.block[0]; + ret_len = data.block[0]; + if (ret_len > length) + return -EMSGSIZE; + + memcpy(values, &data.block[1], ret_len); + return ret_len; } -EXPORT_SYMBOL(i2c_smbus_read_block_data); +EXPORT_SYMBOL(__i2c_smbus_read_block_data); /** * i2c_smbus_write_block_data - SMBus "block write" protocol diff --git a/include/linux/i2c.h b/include/linux/i2c.h index 4af2df969a05..f3192ab1982e 100644 --- a/include/linux/i2c.h +++ b/include/linux/i2c.h @@ -174,8 +174,23 @@ i2c_smbus_write_word_swapped(const struct i2c_client *client, } /* Returns the number of read bytes */ -s32 i2c_smbus_read_block_data(const struct i2c_client *client, - u8 command, u8 *values); +s32 __i2c_smbus_read_block_data(const struct i2c_client *client, + u8 command, u8 length, u8 *values); +/* + * This monstrosity allows to call i2c_smbus_read_block_data() with either + * 3 or 4 arguments and will be removed once all users have been switched + * to the 4 argument version. + */ +#define __i2c_smbus_read_block_data_3arg(client, cmd, values) \ + __i2c_smbus_read_block_data(client, cmd, I2C_SMBUS_BLOCK_MAX, values) +#define __i2c_smbus_read_block_data_4arg(client, cmd, length, values) \ + __i2c_smbus_read_block_data(client, cmd, length, values) +#define __i2c_smbus_read_block_data_impl(_1, _2, _3, _4, impl, ...) impl +#define i2c_smbus_read_block_data(client, cmd, varargs...) \ + __i2c_smbus_read_block_data_impl(client, cmd, varargs, \ + __i2c_smbus_read_block_data_4arg, \ + __i2c_smbus_read_block_data_3arg) \ + (client, cmd, varargs) s32 i2c_smbus_write_block_data(const struct i2c_client *client, u8 command, u8 length, const u8 *values); /* Returns the number of read bytes */ From 8027cc6835c7b4546a88c09e1a871bbf4e780000 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Sat, 19 Sep 2026 16:14:34 +0200 Subject: [PATCH 16/37] i2c: qcom-geni: Use device_set_node() helper Use the device_set_node() helper instead of manually setting adap.dev.of_node. This: 1. Ensures that if the parent has an of_node, adap.dev.fwnode also points to the parent's of_node rather than remaining NULL. 2. Removes the need to handle a parent with an ACPI fwnode specially, since the generic helpers take care of this too. Signed-off-by: Hans de Goede Reviewed-by: Konrad Dybcio Signed-off-by: Andi Shyti Link: https://patch.msgid.link/20260919141434.22999-1-johannes.goede@oss.qualcomm.com --- drivers/i2c/busses/i2c-qcom-geni.c | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/drivers/i2c/busses/i2c-qcom-geni.c b/drivers/i2c/busses/i2c-qcom-geni.c index ea854e78ef9c..01d1fdab5163 100644 --- a/drivers/i2c/busses/i2c-qcom-geni.c +++ b/drivers/i2c/busses/i2c-qcom-geni.c @@ -1142,9 +1142,6 @@ static int geni_i2c_probe(struct platform_device *pdev) gi2c->clk_freq_out = I2C_MAX_STANDARD_MODE_FREQ; } - if (has_acpi_companion(dev)) - ACPI_COMPANION_SET(&gi2c->adap.dev, ACPI_COMPANION(dev)); - gi2c->irq = platform_get_irq(pdev, 0); if (gi2c->irq < 0) return gi2c->irq; @@ -1170,7 +1167,7 @@ static int geni_i2c_probe(struct platform_device *pdev) i2c_set_adapdata(&gi2c->adap, gi2c); gi2c->adap.dev.parent = dev; - gi2c->adap.dev.of_node = dev->of_node; + device_set_node(&gi2c->adap.dev, dev_fwnode(dev)); strscpy(gi2c->adap.name, "Geni-I2C", sizeof(gi2c->adap.name)); pm_runtime_set_suspended(dev); From d07c7775ca17c4123164806d6210f22dd62cef4f Mon Sep 17 00:00:00 2001 From: Loic Poulain Date: Tue, 29 Sep 2026 14:25:45 +0200 Subject: [PATCH 17/37] i2c: qcom-cci: Switch msm8953 to the CCI v2 timing/rate config The msm8953 CCI timing table is internally inconsistent. Its Standard and Fast timings match v1/v1.5, which are calibrated for a 19.2 MHz CCI clock, but its Fast+ timings are essentially the 'v2' values, which are calibrated for 37.5 MHz. Since all masters share a single CCI clock, no single rate can satisfy all three modes with the current table, and the DT assigns 19.2 MHz, so Fast+ timings are wrong. The msm8953 CCI is the same hardware version as msm8996/sdm630, which already use the cci_v2_data config (37.5 MHz). 37.5 MHz is supported by the msm8953 CCI RCG, so reuse cci_v2_data for msm8953 as well and drop the redundant, inconsistent standalone table. This makes all three I2C modes self-consistent under a single clock rate. Note this requires the CCI clock to run at 37.5 MHz, the driver selects and enforces the proper rate in following patches. Signed-off-by: Loic Poulain Reviewed-by: Konrad Dybcio Reviewed-by: Vladimir Zapolskiy Signed-off-by: Andi Shyti Link: https://patch.msgid.link/20260929-cci-clk-fix-v6-1-62a268cccb0f@oss.qualcomm.com --- drivers/i2c/busses/i2c-qcom-cci.c | 47 +------------------------------ 1 file changed, 1 insertion(+), 46 deletions(-) diff --git a/drivers/i2c/busses/i2c-qcom-cci.c b/drivers/i2c/busses/i2c-qcom-cci.c index 25b6e4e9e3fa..6716c7f730fb 100644 --- a/drivers/i2c/busses/i2c-qcom-cci.c +++ b/drivers/i2c/busses/i2c-qcom-cci.c @@ -753,54 +753,9 @@ static const struct cci_data cci_v2_data = { }, }; -static const struct cci_data cci_msm8953_data = { - .num_masters = 2, - .queue_size = { 64, 16 }, - .quirks = { - .max_write_len = 11, - .max_read_len = 12, - }, - .params[I2C_MODE_STANDARD] = { - .thigh = 78, - .tlow = 114, - .tsu_sto = 28, - .tsu_sta = 28, - .thd_dat = 10, - .thd_sta = 77, - .tbuf = 118, - .scl_stretch_en = 0, - .trdhld = 6, - .tsp = 1 - }, - .params[I2C_MODE_FAST] = { - .thigh = 20, - .tlow = 28, - .tsu_sto = 21, - .tsu_sta = 21, - .thd_dat = 13, - .thd_sta = 18, - .tbuf = 32, - .scl_stretch_en = 0, - .trdhld = 6, - .tsp = 3 - }, - .params[I2C_MODE_FAST_PLUS] = { - .thigh = 16, - .tlow = 22, - .tsu_sto = 17, - .tsu_sta = 18, - .thd_dat = 16, - .thd_sta = 15, - .tbuf = 19, - .scl_stretch_en = 1, - .trdhld = 3, - .tsp = 3 - }, -}; - static const struct of_device_id cci_dt_match[] = { { .compatible = "qcom,msm8226-cci", .data = &cci_v1_data}, - { .compatible = "qcom,msm8953-cci", .data = &cci_msm8953_data}, + { .compatible = "qcom,msm8953-cci", .data = &cci_v2_data}, { .compatible = "qcom,msm8974-cci", .data = &cci_v1_5_data}, { .compatible = "qcom,msm8996-cci", .data = &cci_v2_data}, From e2ac856c74518976c76b8736363c64a84ebbd155 Mon Sep 17 00:00:00 2001 From: Loic Poulain Date: Tue, 29 Sep 2026 14:25:46 +0200 Subject: [PATCH 18/37] i2c: qcom-cci: Support per-mode CCI clock rates The CCI hw_params timing values (thigh, tlow, etc.) are expressed in clock ticks and are only valid at the specific CCI clock rate they were calibrated for. Different I2C modes may be calibrated for different rates, and the single CCI clock is shared by all masters. Turn the timing table into a two-dimensional [rate][mode] matrix so a given rate can carry timing sets for each mode, and select the entry matching the currently running clock rate at init time. The existing per-variant values are moved under their calibrated rate (19.2 MHz for v1/v1.5, 37.5 MHz for v2), no timing values are changed. At this stage the driver only validates the running rate against the table, the timings are only valid at the exact rate they were calibrated for, so if the current rate has no matching entry for a master's mode, fail initialization rather than program incorrect timings. A following patch actively enforces the required rate so this becomes a safety net. Suggested-by: Vladimir Zapolskiy Signed-off-by: Loic Poulain Reviewed-by: Konrad Dybcio Reviewed-by: Vladimir Zapolskiy Signed-off-by: Andi Shyti Link: https://patch.msgid.link/20260929-cci-clk-fix-v6-2-62a268cccb0f@oss.qualcomm.com --- drivers/i2c/busses/i2c-qcom-cci.c | 74 ++++++++++++++++++++++++------- 1 file changed, 59 insertions(+), 15 deletions(-) diff --git a/drivers/i2c/busses/i2c-qcom-cci.c b/drivers/i2c/busses/i2c-qcom-cci.c index 6716c7f730fb..ed26f4acfd9a 100644 --- a/drivers/i2c/busses/i2c-qcom-cci.c +++ b/drivers/i2c/busses/i2c-qcom-cci.c @@ -82,6 +82,13 @@ enum { I2C_MODE_STANDARD, I2C_MODE_FAST, I2C_MODE_FAST_PLUS, + NUM_I2C_MODES, +}; + +enum { + CCI_CLK_RATE_19_2MHZ, + CCI_CLK_RATE_37_5MHZ, + NUM_CCI_CLK_RATES, }; enum cci_i2c_queue_t { @@ -117,7 +124,7 @@ struct cci_data { unsigned int num_masters; struct i2c_adapter_quirks quirks; u16 queue_size[NUM_QUEUES]; - struct hw_params params[3]; + struct hw_params params[NUM_CCI_CLK_RATES][NUM_I2C_MODES]; }; struct cci { @@ -127,6 +134,7 @@ struct cci { const struct cci_data *data; struct clk_bulk_data *clocks; int nclocks; + struct clk *cci_clk; struct cci_master master[NUM_MASTERS]; }; @@ -225,7 +233,34 @@ static int cci_halt(struct cci *cci, u8 master_num) return 0; } -static void cci_init(struct cci *cci) +static const unsigned long cci_clk_rates[NUM_CCI_CLK_RATES] = { + [CCI_CLK_RATE_19_2MHZ] = 19200000, + [CCI_CLK_RATE_37_5MHZ] = 37500000, +}; + +static int cci_clk_rate_idx(unsigned long rate) +{ + int i; + + for (i = 0; i < NUM_CCI_CLK_RATES; i++) + if (cci_clk_rates[i] == rate) + return i; + + return -EINVAL; +} + +static const struct hw_params *cci_get_hw_params(struct cci *cci, int mode) +{ + unsigned long rate = clk_get_rate(cci->cci_clk); + int ri = cci_clk_rate_idx(rate); + + if (ri >= 0 && cci->data->params[ri][mode].thigh) + return &cci->data->params[ri][mode]; + + return NULL; +} + +static int cci_init(struct cci *cci) { u32 val = CCI_IRQ_MASK_0_I2C_M0_RD_DONE | CCI_IRQ_MASK_0_I2C_M0_Q0_REPORT | @@ -249,7 +284,12 @@ static void cci_init(struct cci *cci) if (!cci->master[i].cci) continue; - hw = &cci->data->params[mode]; + hw = cci_get_hw_params(cci, mode); + if (!hw) { + dev_err(cci->dev, "no timing for mode %d at CCI clock %lu Hz\n", + mode, clk_get_rate(cci->cci_clk)); + return -EOPNOTSUPP; + } val = hw->thigh << 16 | hw->tlow; writel(val, cci->base + CCI_I2C_Mm_SCL_CTL(i)); @@ -266,6 +306,8 @@ static void cci_init(struct cci *cci) val = hw->scl_stretch_en << 8 | hw->trdhld << 4 | hw->tsp; writel(val, cci->base + CCI_I2C_Mm_MISC_CTL(i)); } + + return 0; } static int cci_reset(struct cci *cci) @@ -283,9 +325,7 @@ static int cci_reset(struct cci *cci) return -ETIMEDOUT; } - cci_init(cci); - - return 0; + return cci_init(cci); } static int cci_run_queue(struct cci *cci, u8 master, u8 queue) @@ -488,8 +528,7 @@ static int __maybe_unused cci_resume_runtime(struct device *dev) if (ret) return ret; - cci_init(cci); - return 0; + return cci_init(cci); } static const struct dev_pm_ops qcom_cci_pm = { @@ -570,6 +609,11 @@ static int cci_probe(struct platform_device *pdev) return dev_err_probe(dev, -EINVAL, "not enough clocks in DT\n"); cci->nclocks = ret; + cci->cci_clk = devm_clk_get(dev, "cci"); + if (IS_ERR(cci->cci_clk)) + return dev_err_probe(dev, PTR_ERR(cci->cci_clk), + "failed to get CCI clock\n"); + ret = cci_enable_clocks(cci); if (ret < 0) return ret; @@ -649,7 +693,7 @@ static const struct cci_data cci_v1_data = { .max_write_len = 10, .max_read_len = 12, }, - .params[I2C_MODE_STANDARD] = { + .params[CCI_CLK_RATE_19_2MHZ][I2C_MODE_STANDARD] = { .thigh = 78, .tlow = 114, .tsu_sto = 28, @@ -661,7 +705,7 @@ static const struct cci_data cci_v1_data = { .trdhld = 6, .tsp = 1 }, - .params[I2C_MODE_FAST] = { + .params[CCI_CLK_RATE_19_2MHZ][I2C_MODE_FAST] = { .thigh = 20, .tlow = 28, .tsu_sto = 21, @@ -682,7 +726,7 @@ static const struct cci_data cci_v1_5_data = { .max_write_len = 10, .max_read_len = 12, }, - .params[I2C_MODE_STANDARD] = { + .params[CCI_CLK_RATE_19_2MHZ][I2C_MODE_STANDARD] = { .thigh = 78, .tlow = 114, .tsu_sto = 28, @@ -694,7 +738,7 @@ static const struct cci_data cci_v1_5_data = { .trdhld = 6, .tsp = 1 }, - .params[I2C_MODE_FAST] = { + .params[CCI_CLK_RATE_19_2MHZ][I2C_MODE_FAST] = { .thigh = 20, .tlow = 28, .tsu_sto = 21, @@ -715,7 +759,7 @@ static const struct cci_data cci_v2_data = { .max_write_len = 11, .max_read_len = 12, }, - .params[I2C_MODE_STANDARD] = { + .params[CCI_CLK_RATE_37_5MHZ][I2C_MODE_STANDARD] = { .thigh = 201, .tlow = 174, .tsu_sto = 204, @@ -727,7 +771,7 @@ static const struct cci_data cci_v2_data = { .trdhld = 6, .tsp = 3 }, - .params[I2C_MODE_FAST] = { + .params[CCI_CLK_RATE_37_5MHZ][I2C_MODE_FAST] = { .thigh = 38, .tlow = 56, .tsu_sto = 40, @@ -739,7 +783,7 @@ static const struct cci_data cci_v2_data = { .trdhld = 6, .tsp = 3 }, - .params[I2C_MODE_FAST_PLUS] = { + .params[CCI_CLK_RATE_37_5MHZ][I2C_MODE_FAST_PLUS] = { .thigh = 16, .tlow = 22, .tsu_sto = 17, From 04ec5e09518b2fafe03540a0c93df858be99cefd Mon Sep 17 00:00:00 2001 From: Loic Poulain Date: Tue, 29 Sep 2026 14:25:47 +0200 Subject: [PATCH 19/37] i2c: qcom-cci: Add 19.2 MHz timings for the v2 CCI The v2 CCI (msm8996/sdm630/msm8953/...) is normally clocked at 37.5 MHz, but its CCI clock can also be configured and run at 19.2 MHz. Add the Standard and Fast timing sets calibrated for 19.2 MHz so that a v2 controller left at that rate still has valid timings for those modes (Fast+ requires 37.5 MHz). The values mirror the v1/v1.5 19.2 MHz Standard/Fast timings, which share the same CCI clock rate. Note the "v1"/"v1.5"/"v2" labels are arbitrary driver-internal names for the cci_data configs, not actual CCI hardware version numbers (the real hw versions are e.g. v1.0.7 on msm8974, v1.4.0 on msm8996, v1.6.3 on sdm630/660, ...). They only distinguish the timing/mode configurations used by the driver, and will be subsequently removed. Signed-off-by: Loic Poulain Reviewed-by: Konrad Dybcio Reviewed-by: Vladimir Zapolskiy Signed-off-by: Andi Shyti Link: https://patch.msgid.link/20260929-cci-clk-fix-v6-3-62a268cccb0f@oss.qualcomm.com --- drivers/i2c/busses/i2c-qcom-cci.c | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/drivers/i2c/busses/i2c-qcom-cci.c b/drivers/i2c/busses/i2c-qcom-cci.c index ed26f4acfd9a..e3c971e3c188 100644 --- a/drivers/i2c/busses/i2c-qcom-cci.c +++ b/drivers/i2c/busses/i2c-qcom-cci.c @@ -759,6 +759,30 @@ static const struct cci_data cci_v2_data = { .max_write_len = 11, .max_read_len = 12, }, + .params[CCI_CLK_RATE_19_2MHZ][I2C_MODE_STANDARD] = { + .thigh = 78, + .tlow = 114, + .tsu_sto = 28, + .tsu_sta = 28, + .thd_dat = 10, + .thd_sta = 77, + .tbuf = 118, + .scl_stretch_en = 0, + .trdhld = 6, + .tsp = 1 + }, + .params[CCI_CLK_RATE_19_2MHZ][I2C_MODE_FAST] = { + .thigh = 20, + .tlow = 28, + .tsu_sto = 21, + .tsu_sta = 21, + .thd_dat = 13, + .thd_sta = 18, + .tbuf = 32, + .scl_stretch_en = 0, + .trdhld = 6, + .tsp = 3 + }, .params[CCI_CLK_RATE_37_5MHZ][I2C_MODE_STANDARD] = { .thigh = 201, .tlow = 174, From 826a1d134c3ea80d6cc2cd763bda7a374ae2be9f Mon Sep 17 00:00:00 2001 From: Loic Poulain Date: Tue, 29 Sep 2026 14:25:48 +0200 Subject: [PATCH 20/37] i2c: qcom-cci: Share the timing table across CCI revisions The hw_params timing values only depend on the CCI clock rate and the I2C mode, not on the hardware revision: every per-variant table used identical values for a given [rate][mode]. Only the set of supported modes differs between revisions. Move the timings into a single shared cci_hw_params[rate][mode] table and describe each variant's highest supported mode in cci_data with max_mode instead of duplicating the timing values. This removes the per-variant timing tables without any functional change. Suggested-by: Vladimir Zapolskiy Signed-off-by: Loic Poulain Reviewed-by: Konrad Dybcio Reviewed-by: Vladimir Zapolskiy Signed-off-by: Andi Shyti Link: https://patch.msgid.link/20260929-cci-clk-fix-v6-4-62a268cccb0f@oss.qualcomm.com --- drivers/i2c/busses/i2c-qcom-cci.c | 181 ++++++++++++------------------ 1 file changed, 70 insertions(+), 111 deletions(-) diff --git a/drivers/i2c/busses/i2c-qcom-cci.c b/drivers/i2c/busses/i2c-qcom-cci.c index e3c971e3c188..a9287063c245 100644 --- a/drivers/i2c/busses/i2c-qcom-cci.c +++ b/drivers/i2c/busses/i2c-qcom-cci.c @@ -124,7 +124,8 @@ struct cci_data { unsigned int num_masters; struct i2c_adapter_quirks quirks; u16 queue_size[NUM_QUEUES]; - struct hw_params params[NUM_CCI_CLK_RATES][NUM_I2C_MODES]; + /* Highest I2C mode supported by this variant. */ + u8 max_mode; }; struct cci { @@ -249,13 +250,76 @@ static int cci_clk_rate_idx(unsigned long rate) return -EINVAL; } +static const struct hw_params cci_hw_params[NUM_CCI_CLK_RATES][NUM_I2C_MODES] = { + [CCI_CLK_RATE_19_2MHZ][I2C_MODE_STANDARD] = { + .thigh = 78, + .tlow = 114, + .tsu_sto = 28, + .tsu_sta = 28, + .thd_dat = 10, + .thd_sta = 77, + .tbuf = 118, + .scl_stretch_en = 0, + .trdhld = 6, + .tsp = 1 + }, + [CCI_CLK_RATE_19_2MHZ][I2C_MODE_FAST] = { + .thigh = 20, + .tlow = 28, + .tsu_sto = 21, + .tsu_sta = 21, + .thd_dat = 13, + .thd_sta = 18, + .tbuf = 32, + .scl_stretch_en = 0, + .trdhld = 6, + .tsp = 3 + }, + [CCI_CLK_RATE_37_5MHZ][I2C_MODE_STANDARD] = { + .thigh = 201, + .tlow = 174, + .tsu_sto = 204, + .tsu_sta = 231, + .thd_dat = 22, + .thd_sta = 162, + .tbuf = 227, + .scl_stretch_en = 0, + .trdhld = 6, + .tsp = 3 + }, + [CCI_CLK_RATE_37_5MHZ][I2C_MODE_FAST] = { + .thigh = 38, + .tlow = 56, + .tsu_sto = 40, + .tsu_sta = 40, + .thd_dat = 22, + .thd_sta = 35, + .tbuf = 62, + .scl_stretch_en = 0, + .trdhld = 6, + .tsp = 3 + }, + [CCI_CLK_RATE_37_5MHZ][I2C_MODE_FAST_PLUS] = { + .thigh = 16, + .tlow = 22, + .tsu_sto = 17, + .tsu_sta = 18, + .thd_dat = 16, + .thd_sta = 15, + .tbuf = 24, + .scl_stretch_en = 0, + .trdhld = 3, + .tsp = 3 + }, +}; + static const struct hw_params *cci_get_hw_params(struct cci *cci, int mode) { unsigned long rate = clk_get_rate(cci->cci_clk); int ri = cci_clk_rate_idx(rate); - if (ri >= 0 && cci->data->params[ri][mode].thigh) - return &cci->data->params[ri][mode]; + if (ri >= 0 && mode <= cci->data->max_mode && cci_hw_params[ri][mode].thigh) + return &cci_hw_params[ri][mode]; return NULL; } @@ -693,30 +757,7 @@ static const struct cci_data cci_v1_data = { .max_write_len = 10, .max_read_len = 12, }, - .params[CCI_CLK_RATE_19_2MHZ][I2C_MODE_STANDARD] = { - .thigh = 78, - .tlow = 114, - .tsu_sto = 28, - .tsu_sta = 28, - .thd_dat = 10, - .thd_sta = 77, - .tbuf = 118, - .scl_stretch_en = 0, - .trdhld = 6, - .tsp = 1 - }, - .params[CCI_CLK_RATE_19_2MHZ][I2C_MODE_FAST] = { - .thigh = 20, - .tlow = 28, - .tsu_sto = 21, - .tsu_sta = 21, - .thd_dat = 13, - .thd_sta = 18, - .tbuf = 32, - .scl_stretch_en = 0, - .trdhld = 6, - .tsp = 3 - }, + .max_mode = I2C_MODE_FAST, }; static const struct cci_data cci_v1_5_data = { @@ -726,30 +767,7 @@ static const struct cci_data cci_v1_5_data = { .max_write_len = 10, .max_read_len = 12, }, - .params[CCI_CLK_RATE_19_2MHZ][I2C_MODE_STANDARD] = { - .thigh = 78, - .tlow = 114, - .tsu_sto = 28, - .tsu_sta = 28, - .thd_dat = 10, - .thd_sta = 77, - .tbuf = 118, - .scl_stretch_en = 0, - .trdhld = 6, - .tsp = 1 - }, - .params[CCI_CLK_RATE_19_2MHZ][I2C_MODE_FAST] = { - .thigh = 20, - .tlow = 28, - .tsu_sto = 21, - .tsu_sta = 21, - .thd_dat = 13, - .thd_sta = 18, - .tbuf = 32, - .scl_stretch_en = 0, - .trdhld = 6, - .tsp = 3 - }, + .max_mode = I2C_MODE_FAST, }; static const struct cci_data cci_v2_data = { @@ -759,66 +777,7 @@ static const struct cci_data cci_v2_data = { .max_write_len = 11, .max_read_len = 12, }, - .params[CCI_CLK_RATE_19_2MHZ][I2C_MODE_STANDARD] = { - .thigh = 78, - .tlow = 114, - .tsu_sto = 28, - .tsu_sta = 28, - .thd_dat = 10, - .thd_sta = 77, - .tbuf = 118, - .scl_stretch_en = 0, - .trdhld = 6, - .tsp = 1 - }, - .params[CCI_CLK_RATE_19_2MHZ][I2C_MODE_FAST] = { - .thigh = 20, - .tlow = 28, - .tsu_sto = 21, - .tsu_sta = 21, - .thd_dat = 13, - .thd_sta = 18, - .tbuf = 32, - .scl_stretch_en = 0, - .trdhld = 6, - .tsp = 3 - }, - .params[CCI_CLK_RATE_37_5MHZ][I2C_MODE_STANDARD] = { - .thigh = 201, - .tlow = 174, - .tsu_sto = 204, - .tsu_sta = 231, - .thd_dat = 22, - .thd_sta = 162, - .tbuf = 227, - .scl_stretch_en = 0, - .trdhld = 6, - .tsp = 3 - }, - .params[CCI_CLK_RATE_37_5MHZ][I2C_MODE_FAST] = { - .thigh = 38, - .tlow = 56, - .tsu_sto = 40, - .tsu_sta = 40, - .thd_dat = 22, - .thd_sta = 35, - .tbuf = 62, - .scl_stretch_en = 0, - .trdhld = 6, - .tsp = 3 - }, - .params[CCI_CLK_RATE_37_5MHZ][I2C_MODE_FAST_PLUS] = { - .thigh = 16, - .tlow = 22, - .tsu_sto = 17, - .tsu_sta = 18, - .thd_dat = 16, - .thd_sta = 15, - .tbuf = 24, - .scl_stretch_en = 0, - .trdhld = 3, - .tsp = 3 - }, + .max_mode = I2C_MODE_FAST_PLUS, }; static const struct of_device_id cci_dt_match[] = { From fa1eb3af12fa71964ff965726bd65c69a76d1de7 Mon Sep 17 00:00:00 2001 From: Loic Poulain Date: Tue, 29 Sep 2026 14:25:49 +0200 Subject: [PATCH 21/37] i2c: qcom-cci: Enforce the required CCI clock rate The CCI hw_params timing values are only valid at the specific clock rate they were calibrated for. A previous change made the driver select the timing set matching the currently running clock rate, but the rate itself was still left to the DT (assigned-clock-rates) or the bootloader, which is fragile: if no rate is enforced the timings may not match and violate the I2C specification. Actively drive the CCI clock to the rate required by the configured modes. The single CCI clock is shared by all masters, which may run in different modes, so cci_get_required_rate() picks the lowest rate that has a valid timing set for every active master's mode. This avoids clocking the bus faster than necessary while still satisfying every master (e.g. a Fast+ master forces 37.5 MHz). Apply the rate through the OPP framework so that boards describing an opp table also get the required power-domain/regulator votes for that rate. Boards without an OPP table simply fall back to plain clk_set_rate() behavior, so existing DTs keep working. Drop the now-redundant hw_params[].thigh check in cci_get_hw_params(), cci_get_required_rate() already rejects rates lacking a valid timing set for an active master's mode. Suggested-by: Konrad Dybcio Signed-off-by: Loic Poulain Signed-off-by: Andi Shyti Link: https://patch.msgid.link/20260929-cci-clk-fix-v6-5-62a268cccb0f@oss.qualcomm.com --- drivers/i2c/busses/Kconfig | 1 + drivers/i2c/busses/i2c-qcom-cci.c | 76 ++++++++++++++++++++++++++++++- 2 files changed, 76 insertions(+), 1 deletion(-) diff --git a/drivers/i2c/busses/Kconfig b/drivers/i2c/busses/Kconfig index d7b89508311f..14eb7e802bc8 100644 --- a/drivers/i2c/busses/Kconfig +++ b/drivers/i2c/busses/Kconfig @@ -1050,6 +1050,7 @@ config I2C_PXA_SLAVE config I2C_QCOM_CCI tristate "Qualcomm Camera Control Interface" depends on ARCH_QCOM || COMPILE_TEST + select PM_OPP help If you say yes to this option, support will be included for the built-in camera control interface on the Qualcomm SoCs. diff --git a/drivers/i2c/busses/i2c-qcom-cci.c b/drivers/i2c/busses/i2c-qcom-cci.c index a9287063c245..d692f72c50a1 100644 --- a/drivers/i2c/busses/i2c-qcom-cci.c +++ b/drivers/i2c/busses/i2c-qcom-cci.c @@ -11,6 +11,7 @@ #include #include #include +#include #define CCI_HW_VERSION 0x0 #define CCI_RESET_CMD 0x004 @@ -318,7 +319,7 @@ static const struct hw_params *cci_get_hw_params(struct cci *cci, int mode) unsigned long rate = clk_get_rate(cci->cci_clk); int ri = cci_clk_rate_idx(rate); - if (ri >= 0 && mode <= cci->data->max_mode && cci_hw_params[ri][mode].thigh) + if (ri >= 0 && mode <= cci->data->max_mode) return &cci_hw_params[ri][mode]; return NULL; @@ -575,6 +576,66 @@ static void cci_disable_clocks(struct cci *cci) clk_bulk_disable_unprepare(cci->nclocks, cci->clocks); } +/* + * The single CCI clock is shared by all masters, which may run in different + * modes. Pick the lowest rate that has a valid timing set for every active + * master's mode. + */ +static int cci_get_required_rate(struct cci *cci, unsigned long *rate) +{ + for (int ri = 0; ri < NUM_CCI_CLK_RATES; ri++) { + bool supported = true; + + for (int i = 0; i < cci->data->num_masters; i++) { + int mode = cci->master[i].mode; + + if (!cci->master[i].cci) + continue; + + if (mode > cci->data->max_mode || + !cci_hw_params[ri][mode].thigh) { + supported = false; + break; + } + } + + if (supported) { + *rate = cci_clk_rates[ri]; + return 0; + } + } + + return -EOPNOTSUPP; +} + +static int cci_set_core_rate(struct cci *cci) +{ + struct device *dev = cci->dev; + unsigned long rate; + int ret; + + ret = cci_get_required_rate(cci, &rate); + if (ret) { + dev_err(dev, "no CCI clock rate satisfies all masters\n"); + return ret; + } + + ret = dev_pm_opp_set_rate(dev, rate); + if (ret) { + dev_err(dev, "CCI clock could not be set to %lu Hz\n", rate); + return ret; + } + + /* + * Sanity: The hw_params timings are only valid at the exact + * expected rate, verify what landed on the hardware. + */ + if (clk_get_rate(cci->cci_clk) != rate) + dev_warn(dev, "CCI clock is not at expected %lu Hz\n", rate); + + return 0; +} + static int __maybe_unused cci_suspend_runtime(struct device *dev) { struct cci *cci = dev_get_drvdata(dev); @@ -678,6 +739,19 @@ static int cci_probe(struct platform_device *pdev) return dev_err_probe(dev, PTR_ERR(cci->cci_clk), "failed to get CCI clock\n"); + ret = devm_pm_opp_set_clkname(dev, "cci"); + if (ret) + return dev_err_probe(dev, ret, "failed to set CCI OPP clk\n"); + + /* OPP table is optional */ + ret = devm_pm_opp_of_add_table(dev); + if (ret && ret != -ENODEV) + return dev_err_probe(dev, ret, "failed to add OPP table\n"); + + ret = cci_set_core_rate(cci); + if (ret) + return ret; + ret = cci_enable_clocks(cci); if (ret < 0) return ret; From 249fa862deccee0de8071e61fe9946f2fdeb3a8e Mon Sep 17 00:00:00 2001 From: Harinn Date: Sun, 4 Oct 2026 17:59:53 +0700 Subject: [PATCH 22/37] i2c: acpi: Force ASUE140D touchpads to 100 kHz The ASUE140D touchpad on the ASUS Zenbook UX3404VC (BIOS UX3404VC.303) starts lagging shortly after boot when the bus runs at the 400 kHz the DSDT asks for, and only recovers after rebinding i2c-hid. Forcing 100 kHz fixes it. Signed-off-by: Harinn Acked-by: Mika Westerberg Signed-off-by: Andi Shyti Link: https://patch.msgid.link/20261004-i2c-acpi-asue140d-100khz-v1-1-cc2dbad00470@pm.me --- drivers/i2c/i2c-core-acpi.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/i2c/i2c-core-acpi.c b/drivers/i2c/i2c-core-acpi.c index 3af0e0551185..564e6ad66398 100644 --- a/drivers/i2c/i2c-core-acpi.c +++ b/drivers/i2c/i2c-core-acpi.c @@ -371,6 +371,7 @@ static const struct acpi_device_id i2c_acpi_force_100khz_device_ids[] = { * the device works without issues on Windows at what is expected to be * a 400KHz frequency. The root cause of the issue is not known. */ + { "ASUE140D", 0 }, { "DELL0A86", 0 }, { "DLL0945", 0 }, { "ELAN0678", 0 }, From af8fd5766b77132a4fc8f11681846a9c480fe8bc Mon Sep 17 00:00:00 2001 From: Ben Huang Date: Thu, 10 Sep 2026 11:01:20 +0800 Subject: [PATCH 23/37] dt-bindings: i2c: Add Novatek NT72676 I2C controller Add Devicetree binding documentation for Novatek NT72676 I2C controller. Signed-off-by: Ben Huang Signed-off-by: Nina Kuo Reviewed-by: Rob Herring (Arm) Signed-off-by: Andi Shyti Link: https://patch.msgid.link/20260910030123.215609-2-nina_kuo@novatek.com.tw --- .../bindings/i2c/novatek,nt72676-i2c.yaml | 76 +++++++++++++++++++ 1 file changed, 76 insertions(+) create mode 100644 Documentation/devicetree/bindings/i2c/novatek,nt72676-i2c.yaml diff --git a/Documentation/devicetree/bindings/i2c/novatek,nt72676-i2c.yaml b/Documentation/devicetree/bindings/i2c/novatek,nt72676-i2c.yaml new file mode 100644 index 000000000000..7d9275b1039c --- /dev/null +++ b/Documentation/devicetree/bindings/i2c/novatek,nt72676-i2c.yaml @@ -0,0 +1,76 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/i2c/novatek,nt72676-i2c.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Novatek NT72676 I2C Controller + +maintainers: + - Ben Huang + - Jason JJ Wu + +description: > + Novatek NT72676 SoC provides multiple I2C controller instances that share + the same register layout and transfer programming model. + + Generic I2C controllers are directly accessible by the host CPU and driven + by a 96 MHz AHB clock. + + STBC I2C controllers are driven by a 12 MHz clock from the STBC (Standby + Controller, an 8051-based microcontroller). Before accessing an STBC I2C + controller, the host CPU must authenticate with the STBC and switch + ownership of the controller to the host CPU. The presence of the + novatek,stbc-syscon property identifies an STBC I2C controller requiring + this initialization sequence. + +properties: + compatible: + const: novatek,nt72676-i2c + + reg: + maxItems: 1 + + interrupts: + maxItems: 1 + + clocks: + maxItems: 1 + + clock-frequency: + default: 100000 + enum: [ 100000, 400000 ] + + novatek,stbc-syscon: + $ref: /schemas/types.yaml#/definitions/phandle + description: > + Phandle to the STBC system controller used to authenticate host + access and switch ownership of the STBC I2C controller to the host + CPU during initialization. + +required: + - compatible + - reg + - interrupts + - clocks + +allOf: + - $ref: /schemas/i2c/i2c-controller.yaml# + +unevaluatedProperties: false + +examples: + - | + #include + #include + + i2c@fc040a00 { + compatible = "novatek,nt72676-i2c"; + reg = <0xfc040a00 0x40>; + interrupts = ; + clocks = <&stbc_i2c_clock>; + clock-frequency = <400000>; + novatek,stbc-syscon = <&stbc>; + #address-cells = <1>; + #size-cells = <0>; + }; From d2a045b06de3d03687245b574d8e7527dbc181a8 Mon Sep 17 00:00:00 2001 From: Ben Huang Date: Thu, 10 Sep 2026 11:01:21 +0800 Subject: [PATCH 24/37] i2c: nt72676: Add Novatek NT72676 I2C controller driver This driver performs the fundamental read/write functions as an I2C controller and supports Standard-mode and Fast-mode. Default operation is Standard-mode. Signed-off-by: Ben Huang Signed-off-by: Nina Kuo Signed-off-by: Andi Shyti Link: https://patch.msgid.link/20260910030123.215609-3-nina_kuo@novatek.com.tw --- drivers/i2c/busses/Kconfig | 11 + drivers/i2c/busses/Makefile | 1 + drivers/i2c/busses/i2c-nt72676.c | 725 +++++++++++++++++++++++++++++++ 3 files changed, 737 insertions(+) create mode 100644 drivers/i2c/busses/i2c-nt72676.c diff --git a/drivers/i2c/busses/Kconfig b/drivers/i2c/busses/Kconfig index 14eb7e802bc8..275aaa7f3583 100644 --- a/drivers/i2c/busses/Kconfig +++ b/drivers/i2c/busses/Kconfig @@ -962,6 +962,17 @@ config I2C_NPCM controllers. Driver can also support slave mode (select I2C_SLAVE). +config I2C_NT72676 + tristate "Novatek NT72676 I2C controller" + depends on OF + depends on COMMON_CLK || COMPILE_TEST + help + Say Y here if you want to enable I2C bus controller on + Novatek NT72676 SoC. + This driver performs fundamental read/write functions + as an I2C bus controller and supports Standard-mode and + Fast-mode. Default operation is Standard-mode. + config I2C_OCORES tristate "OpenCores I2C Controller" help diff --git a/drivers/i2c/busses/Makefile b/drivers/i2c/busses/Makefile index 3755c54b3d82..e5440de0fe14 100644 --- a/drivers/i2c/busses/Makefile +++ b/drivers/i2c/busses/Makefile @@ -90,6 +90,7 @@ obj-$(CONFIG_I2C_MV64XXX) += i2c-mv64xxx.o obj-$(CONFIG_I2C_MXS) += i2c-mxs.o obj-$(CONFIG_I2C_NOMADIK) += i2c-nomadik.o obj-$(CONFIG_I2C_NPCM) += i2c-npcm7xx.o +obj-$(CONFIG_I2C_NT72676) += i2c-nt72676.o obj-$(CONFIG_I2C_OCORES) += i2c-ocores.o obj-$(CONFIG_I2C_OMAP) += i2c-omap.o obj-$(CONFIG_I2C_OWL) += i2c-owl.o diff --git a/drivers/i2c/busses/i2c-nt72676.c b/drivers/i2c/busses/i2c-nt72676.c new file mode 100644 index 000000000000..1308d692fc40 --- /dev/null +++ b/drivers/i2c/busses/i2c-nt72676.c @@ -0,0 +1,725 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (c) 2026 Novatek Microelectronics Corp. + * Author: Ben Huang + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define I2C_REG_CTRL 0x00 +#define I2C_REG_CLK 0x04 +#define I2C_REG_ACK 0x08 +#define I2C_REG_SIZE 0x0C +#define I2C_REG_FIFO1 0x10 +#define I2C_REG_SUBADDR 0x20 +#define I2C_REG_PINGPONG 0x24 +#define I2C_REG_INTR 0x28 +#define I2C_REG_FIFO2 0x2C +#define I2C_REG_DUTY 0x3C +#define I2C_CLR_FIFO1 BIT(7) +#define I2C_CLR_FIFO2 BIT(23) +#define I2C_BUF_LITTLE_ENDIAN BIT(13) +#define I2C_BUSY BIT(1) +#define I2C_ENABLE BIT(2) +#define I2C_REPEAT_ENABLE BIT(7) +#define I2C_READ_OPERATION BIT(8) +#define I2C_NACK BIT(24) +#define I2C_CLOCK_DUTY_ENABLE BIT(21) +#define I2C_CLOCK_STRETCH_ENABLE BIT(27) +#define I2C_MASTER_CLK_STRETCH_ENABLE BIT(28) +#define I2C_TRIGGER BIT(0) +#define I2C_IRQ_CLK_STR_TIMEOUT BIT(12) +#define I2C_IRQ_NACK BIT(11) +#define I2C_IRQ_RX_FULL BIT(10) +#define I2C_IRQ_TX_EMPTY BIT(9) +#define I2C_IRQ_FINISH BIT(8) +#define I2C_IRQ_FLAG (I2C_IRQ_CLK_STR_TIMEOUT |\ + I2C_IRQ_NACK | \ + I2C_IRQ_RX_FULL | \ + I2C_IRQ_TX_EMPTY | \ + I2C_IRQ_FINISH) +#define I2C_IRQ_CLEAR_ALL GENMASK(20, 16) +#define I2C_IRQ_ENABLE_SETTING GENMASK(4, 0) +#define I2C_SUBADDR_ENABLE BIT(6) +#define I2C_16BITSUBADDR_ENABLE BIT(16) +#define I2C_24BITSUBADDR_ENABLE BIT(17) +#define I2C_32BITSUBADDR_ENABLE BIT(18) +#define I2C_ACK_CTRL_COUNTER 0x000001E0 +#define I2C_TX_EMPTY_FIFO1 BIT(6) +#define I2C_TX_EMPTY_FIFO2 BIT(22) +#define I2C_RX_FULL_FIFO1 BIT(5) +#define I2C_RX_FULL_FIFO2 BIT(21) + +#define STBC_REG_PSWD 0x0204 +#define STBC_REG_KEYPASS 0x0208 +#define STBC_REG_I2C_SWITCH 0x0220 +#define STBC_PSWD_DATA1 0x72682 +#define STBC_PSWD_DATA2 0x28627 +#define STBC_KEYPASS_ENABLE BIT(0) +#define STBC_AGPIO_SWITCH_TO_CPU BIT(14) + +#define FIFO_CHUNK_SIZE 16 +#define FIFO_WORD_BYTES 4 + +enum { + SUBADDR_DISABLE = 0, + SUBADDR_8BITS, + SUBADDR_16BITS, + SUBADDR_24BITS, + SUBADDR_32BITS, +}; + +#define SUBADDR_MAX_LEN SUBADDR_32BITS + +enum { + FIFO_1 = 0, + FIFO_2, + FIFO_ALL +}; + +struct nvt_i2c_bus { + void __iomem *base; + struct i2c_adapter adapter; + struct device *dev; + struct completion msg_complete; + struct clk *clk; + unsigned long input_clk_rate; + unsigned int bus_clk_rate; + bool stbc_i2c; + struct regmap *stbc_regmap; + int irq; + /* lock for msg and reg protection */ + spinlock_t lock; + /* used for xfer, protected by lock */ + struct i2c_msg *current_msg; + int subaddr_mode; + int remaining; + int write_ptr; + int read_ptr; + int fifo_idx; + int error_code; +}; + +static void nt72676_i2c_writel(u32 val, struct nvt_i2c_bus *i2c, unsigned int reg) +{ + writel(val, i2c->base + reg); +} + +static u32 nt72676_i2c_readl(struct nvt_i2c_bus *i2c, unsigned int reg) +{ + return readl(i2c->base + reg); +} + +static int nvt_i2c_stbc_auth(struct nvt_i2c_bus *i2c) +{ + int ret; + + if (!i2c->stbc_i2c) + return 0; + + ret = regmap_write(i2c->stbc_regmap, STBC_REG_PSWD, STBC_PSWD_DATA1); + if (ret) + return ret; + + ret = regmap_write(i2c->stbc_regmap, STBC_REG_PSWD, STBC_PSWD_DATA2); + if (ret) + return ret; + + ret = regmap_write(i2c->stbc_regmap, STBC_REG_KEYPASS, STBC_KEYPASS_ENABLE); + if (ret) + return ret; + + return regmap_update_bits(i2c->stbc_regmap, STBC_REG_I2C_SWITCH, + STBC_AGPIO_SWITCH_TO_CPU, STBC_AGPIO_SWITCH_TO_CPU); +} + +static void nvt_i2c_set_clk(struct nvt_i2c_bus *i2c) +{ + unsigned int clk_div = 0; + unsigned int duty = 0; + + clk_div = i2c->input_clk_rate / i2c->bus_clk_rate; + nt72676_i2c_writel(clk_div << 1, i2c, I2C_REG_CLK); + + duty = (clk_div * 9 + 10) / 20; + nt72676_i2c_writel(duty << 16, i2c, I2C_REG_DUTY); + + nt72676_i2c_writel(I2C_ACK_CTRL_COUNTER, i2c, I2C_REG_ACK); +} + +static int nvt_i2c_set_subaddr(struct nvt_i2c_bus *i2c, + const struct i2c_msg *msgs, + int num) +{ + unsigned int i, subaddr = 0; + + /* Sub-address can only be a write with maximum size of 4 bytes. */ + if (WARN_ON(num != 2)) + return -EINVAL; + if (!msgs || !msgs[0].buf || + msgs[0].len > i2c->adapter.quirks->max_comb_1st_msg_len || + (msgs[0].flags & I2C_M_RD) || !(msgs[1].flags & I2C_M_RD) || + msgs[0].addr != msgs[1].addr) { + dev_dbg(i2c->dev, "Invalid sub-address format\n"); + return -EOPNOTSUPP; + } + + i2c->subaddr_mode = msgs[0].len; + for (i = 0; i < msgs[0].len; i++) + subaddr |= (unsigned int)msgs[0].buf[i] << (8 * (msgs[0].len - 1 - i)); + nt72676_i2c_writel(subaddr, i2c, I2C_REG_SUBADDR); + + return 0; +} + +static int nvt_i2c_init(struct nvt_i2c_bus *i2c) +{ + if (i2c->stbc_i2c) { + int ret = nvt_i2c_stbc_auth(i2c); + + if (ret) + return ret; + } + nt72676_i2c_writel(I2C_IRQ_CLEAR_ALL, i2c, I2C_REG_INTR); + nvt_i2c_set_clk(i2c); + nt72676_i2c_writel(I2C_BUF_LITTLE_ENDIAN, i2c, I2C_REG_PINGPONG); + + return 0; +} + +static void nvt_i2c_reset(struct nvt_i2c_bus *i2c) +{ + nt72676_i2c_writel(I2C_IRQ_CLEAR_ALL, i2c, I2C_REG_INTR); + if (i2c->error_code) { + nt72676_i2c_writel(nt72676_i2c_readl(i2c, I2C_REG_CTRL) & ~I2C_ENABLE, + i2c, I2C_REG_CTRL); + nt72676_i2c_writel(nt72676_i2c_readl(i2c, I2C_REG_CTRL) | I2C_ENABLE, + i2c, I2C_REG_CTRL); + } +} + +static void nvt_i2c_release(struct nvt_i2c_bus *i2c) +{ + nt72676_i2c_writel(nt72676_i2c_readl(i2c, I2C_REG_CTRL) & ~I2C_ENABLE, + i2c, I2C_REG_CTRL); + nt72676_i2c_writel(I2C_IRQ_CLEAR_ALL, i2c, I2C_REG_INTR); +} + +static int nvt_i2c_suspend(struct device *dev) +{ + struct nvt_i2c_bus *i2c = dev_get_drvdata(dev); + unsigned long flags; + + i2c_mark_adapter_suspended(&i2c->adapter); + + spin_lock_irqsave(&i2c->lock, flags); + i2c->current_msg = NULL; + nvt_i2c_release(i2c); + spin_unlock_irqrestore(&i2c->lock, flags); + + return 0; +} + +static int nvt_i2c_resume(struct device *dev) +{ + struct nvt_i2c_bus *i2c = dev_get_drvdata(dev); + int ret = nvt_i2c_init(i2c); + + if (ret) { + dev_err(i2c->dev, "Failed to resume\n"); + return ret; + } + + i2c_mark_adapter_resumed(&i2c->adapter); + + return 0; +} + +static void nvt_i2c_clear_fifo(struct nvt_i2c_bus *i2c, unsigned int which) +{ + unsigned int regval = nt72676_i2c_readl(i2c, I2C_REG_PINGPONG); + + switch (which) { + case FIFO_1: + regval |= I2C_CLR_FIFO1; + break; + case FIFO_2: + regval |= I2C_CLR_FIFO2; + break; + case FIFO_ALL: + regval |= I2C_CLR_FIFO1 | I2C_CLR_FIFO2; + break; + default: + break; + } + nt72676_i2c_writel(regval, i2c, I2C_REG_PINGPONG); +} + +static void nvt_i2c_write_fifo(struct nvt_i2c_bus *i2c, + unsigned int fifo_reg, + const unsigned char *buf, + unsigned int buf_offset, + unsigned int length) +{ + unsigned int reg_idx = 0, copy_bytes = 0, j = 0, value = 0; + + while (length > 0) { + value = 0; + copy_bytes = min(FIFO_WORD_BYTES, length); + for (j = 0; j < copy_bytes; j++) + value |= ((unsigned int)buf[buf_offset + j]) << (j * 8); + + nt72676_i2c_writel(value, i2c, fifo_reg + reg_idx * 4); + buf_offset += copy_bytes; + length -= copy_bytes; + reg_idx++; + } +} + +static void nvt_i2c_read_fifo(struct nvt_i2c_bus *i2c, + unsigned int fifo_reg, + unsigned char *buf, + unsigned int buf_offset, + unsigned int length) +{ + unsigned int reg_idx = 0, copy_bytes = 0, j = 0, value = 0; + + while (length > 0) { + value = nt72676_i2c_readl(i2c, fifo_reg + reg_idx * 4); + copy_bytes = min(FIFO_WORD_BYTES, length); + for (j = 0; j < copy_bytes; j++) + buf[buf_offset + j] = (unsigned char)(value >> (j * 8)); + + buf_offset += copy_bytes; + length -= copy_bytes; + reg_idx++; + } +} + +static void nvt_i2c_handle(struct nvt_i2c_bus *i2c, struct i2c_msg *msg, bool is_read) +{ + unsigned int bytes, fiforeg; + + if (WARN_ON(!msg->buf)) + return; + + bytes = min(FIFO_CHUNK_SIZE, i2c->remaining); + fiforeg = i2c->fifo_idx == FIFO_1 ? I2C_REG_FIFO1 : I2C_REG_FIFO2; + + if (is_read) { + nvt_i2c_read_fifo(i2c, fiforeg, msg->buf, i2c->read_ptr, bytes); + i2c->read_ptr += bytes; + } else { + nvt_i2c_write_fifo(i2c, fiforeg, msg->buf, i2c->write_ptr, bytes); + i2c->write_ptr += bytes; + } + nvt_i2c_clear_fifo(i2c, i2c->fifo_idx); + i2c->remaining -= bytes; + i2c->fifo_idx ^= 1; +} + +static irqreturn_t nvt_i2c_isr(int irq, void *dev_id) +{ + struct nvt_i2c_bus *i2c = dev_id; + unsigned int status, clr = 0; + struct i2c_msg *msg; + int do_complete = 0; + + spin_lock(&i2c->lock); + status = nt72676_i2c_readl(i2c, I2C_REG_INTR); + /* IRQ from other I2C, ignored */ + if (!(status & I2C_IRQ_FLAG)) { + spin_unlock(&i2c->lock); + return IRQ_NONE; + } + + msg = i2c->current_msg; + if (!msg) { + nt72676_i2c_writel(I2C_IRQ_CLEAR_ALL, i2c, I2C_REG_INTR); + spin_unlock(&i2c->lock); + return IRQ_HANDLED; + } + + if (status & I2C_IRQ_CLK_STR_TIMEOUT) { + i2c->error_code = -ETIMEDOUT; + clr |= I2C_IRQ_CLK_STR_TIMEOUT << 8; + } else if (status & I2C_IRQ_NACK) { + i2c->error_code = -ENXIO; + clr |= I2C_IRQ_NACK << 8; + } else if (status & I2C_IRQ_RX_FULL) { + if (i2c->remaining > 0) + nvt_i2c_handle(i2c, msg, true); + clr |= I2C_IRQ_RX_FULL << 8; + } else if (status & I2C_IRQ_TX_EMPTY) { + if (i2c->remaining > 0) + nvt_i2c_handle(i2c, msg, false); + clr |= I2C_IRQ_TX_EMPTY << 8; + } else if (status & I2C_IRQ_FINISH) { + if (i2c->remaining > 0 && (msg->flags & I2C_M_RD)) + nvt_i2c_handle(i2c, msg, true); + clr |= I2C_IRQ_FINISH << 8; + do_complete = 1; + } + if (i2c->error_code) + do_complete = 1; + + nt72676_i2c_writel(status | clr, i2c, I2C_REG_INTR); + if (do_complete) + complete(&i2c->msg_complete); + spin_unlock(&i2c->lock); + + return IRQ_HANDLED; +} + +static void nvt_i2c_ctrl_init(struct nvt_i2c_bus *i2c) +{ + int i = 0; + + nt72676_i2c_writel(0, i2c, I2C_REG_CTRL); + for (i = 0; i < 4; i++) { + nt72676_i2c_writel(0, i2c, I2C_REG_FIFO1 + i * 4); + nt72676_i2c_writel(0, i2c, I2C_REG_FIFO2 + i * 4); + } + nvt_i2c_clear_fifo(i2c, FIFO_ALL); + nt72676_i2c_writel(0, i2c, I2C_REG_SUBADDR); + i2c->subaddr_mode = SUBADDR_DISABLE; +} + +static int nvt_i2c_check_msg(const struct i2c_msg *msg) +{ + if (!msg || !msg->buf || !msg->len) + return -EINVAL; + + return 0; +} + +static void nvt_i2c_prepare_xfer(struct nvt_i2c_bus *i2c, struct i2c_msg *msg) +{ + reinit_completion(&i2c->msg_complete); + i2c->remaining = msg->len; + i2c->current_msg = msg; + i2c->write_ptr = 0; + i2c->read_ptr = 0; + i2c->error_code = 0; + i2c->fifo_idx = FIFO_1; +} + +static int nvt_i2c_write(struct nvt_i2c_bus *i2c, struct i2c_msg *msg) +{ + int ret, offset = 0, write_bytes, fifo_num; + unsigned int ctrl_mask = 0; + unsigned long flags; + + ret = nvt_i2c_check_msg(msg); + if (ret) + return ret; + + spin_lock_irqsave(&i2c->lock, flags); + nvt_i2c_prepare_xfer(i2c, msg); + + nt72676_i2c_writel((msg->len * 8) << 8, i2c, I2C_REG_SIZE); + + /* Write FIFO data first */ + for (fifo_num = FIFO_1; fifo_num < FIFO_ALL && offset < msg->len; fifo_num++) { + write_bytes = min(FIFO_CHUNK_SIZE, msg->len - offset); + nvt_i2c_write_fifo(i2c, fifo_num == FIFO_1 ? I2C_REG_FIFO1 : I2C_REG_FIFO2, + msg->buf, offset, write_bytes); + offset += write_bytes; + } + i2c->write_ptr = offset; + i2c->remaining = msg->len - offset; + i2c->fifo_idx = FIFO_1; + + /* Enable interrupt */ + nt72676_i2c_writel(I2C_IRQ_ENABLE_SETTING | I2C_IRQ_CLEAR_ALL, + i2c, I2C_REG_INTR); + + /* Trigger */ + switch (i2c->subaddr_mode) { + case SUBADDR_8BITS: + ctrl_mask |= I2C_SUBADDR_ENABLE; + break; + case SUBADDR_16BITS: + ctrl_mask |= I2C_SUBADDR_ENABLE | I2C_16BITSUBADDR_ENABLE; + break; + case SUBADDR_24BITS: + ctrl_mask |= I2C_SUBADDR_ENABLE | I2C_24BITSUBADDR_ENABLE; + break; + case SUBADDR_32BITS: + ctrl_mask |= I2C_SUBADDR_ENABLE | I2C_32BITSUBADDR_ENABLE; + break; + default: + break; + } + ctrl_mask |= (((msg->addr << 1) << 8) | I2C_ENABLE | + I2C_CLOCK_DUTY_ENABLE | I2C_CLOCK_STRETCH_ENABLE | + I2C_MASTER_CLK_STRETCH_ENABLE | I2C_TRIGGER); + nt72676_i2c_writel(ctrl_mask, i2c, I2C_REG_CTRL); + spin_unlock_irqrestore(&i2c->lock, flags); + + ret = wait_for_completion_timeout(&i2c->msg_complete, i2c->adapter.timeout); + + spin_lock_irqsave(&i2c->lock, flags); + if (!ret) + i2c->error_code = -ETIMEDOUT; + nvt_i2c_reset(i2c); + ret = i2c->error_code; + i2c->current_msg = NULL; + spin_unlock_irqrestore(&i2c->lock, flags); + + if (ret) + dev_dbg(i2c->dev, "Write failed (err:%d); SA[0x%X]\n", + i2c->error_code, msg->addr); + + return ret; +} + +static int nvt_i2c_read(struct nvt_i2c_bus *i2c, struct i2c_msg *msg) +{ + unsigned int ctrl_mask = 0; + unsigned long flags; + int ret; + + ret = nvt_i2c_check_msg(msg); + if (ret) + return ret; + + spin_lock_irqsave(&i2c->lock, flags); + nvt_i2c_prepare_xfer(i2c, msg); + + nt72676_i2c_writel((msg->len * 8) << 8, i2c, I2C_REG_SIZE); + + /* Enable interrupt */ + nt72676_i2c_writel(I2C_IRQ_ENABLE_SETTING | I2C_IRQ_CLEAR_ALL, + i2c, I2C_REG_INTR); + + /* Trigger */ + switch (i2c->subaddr_mode) { + case SUBADDR_8BITS: + ctrl_mask |= I2C_SUBADDR_ENABLE; + break; + case SUBADDR_16BITS: + ctrl_mask |= I2C_SUBADDR_ENABLE | I2C_16BITSUBADDR_ENABLE; + break; + case SUBADDR_24BITS: + ctrl_mask |= I2C_SUBADDR_ENABLE | I2C_24BITSUBADDR_ENABLE; + break; + case SUBADDR_32BITS: + ctrl_mask |= I2C_SUBADDR_ENABLE | I2C_32BITSUBADDR_ENABLE; + break; + default: + break; + } + ctrl_mask |= (((msg->addr << 1) << 8) | I2C_ENABLE | + I2C_REPEAT_ENABLE | I2C_READ_OPERATION | + I2C_CLOCK_DUTY_ENABLE | I2C_CLOCK_STRETCH_ENABLE | + I2C_MASTER_CLK_STRETCH_ENABLE | I2C_TRIGGER); + nt72676_i2c_writel(ctrl_mask, i2c, I2C_REG_CTRL); + spin_unlock_irqrestore(&i2c->lock, flags); + + ret = wait_for_completion_timeout(&i2c->msg_complete, i2c->adapter.timeout); + + spin_lock_irqsave(&i2c->lock, flags); + if (!ret) + i2c->error_code = -ETIMEDOUT; + nvt_i2c_reset(i2c); + ret = i2c->error_code; + i2c->current_msg = NULL; + spin_unlock_irqrestore(&i2c->lock, flags); + + if (ret) + dev_dbg(i2c->dev, "Read failed (err:%d); SA[0x%X]\n", + i2c->error_code, msg->addr); + + return ret; +} + +static int nvt_i2c_xfer(struct i2c_adapter *adap, + struct i2c_msg msgs[], + int num) +{ + struct nvt_i2c_bus *i2c = i2c_get_adapdata(adap); + struct i2c_msg *msg = &msgs[0]; + int ret = 0; + + nvt_i2c_ctrl_init(i2c); + + if (num == 2) { + ret = nvt_i2c_set_subaddr(i2c, msgs, num); + if (ret) + return ret; + msg = &msgs[1]; + } + + if (msg->flags & I2C_M_RD) + ret = nvt_i2c_read(i2c, msg); + else + ret = nvt_i2c_write(i2c, msg); + + if (ret < 0) + return ret; + return num; +} + +static u32 nvt_i2c_func(struct i2c_adapter *adap) +{ + return (I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL) & ~I2C_FUNC_SMBUS_QUICK; +} + +static const struct i2c_algorithm nvt_i2c_algo = { + .master_xfer = nvt_i2c_xfer, + .functionality = nvt_i2c_func, +}; + +static int nvt_i2c_parse_dts(struct nvt_i2c_bus *i2c) +{ + struct device *dev = i2c->dev; + struct device_node *np = dev->of_node; + int ret; + + /* read DTS(novatek,stbc-syscon) for STBC I2C */ + i2c->stbc_i2c = of_property_present(dev->of_node, "novatek,stbc-syscon"); + if (i2c->stbc_i2c) { + i2c->stbc_regmap = syscon_regmap_lookup_by_phandle(dev->of_node, + "novatek,stbc-syscon"); + if (IS_ERR(i2c->stbc_regmap)) + return dev_err_probe(dev, PTR_ERR(i2c->stbc_regmap), + "Failed to get STBC syscon\n"); + } + + /* read DTS(clock-frequency) */ + ret = of_property_read_u32(np, "clock-frequency", &i2c->bus_clk_rate); + if (ret || !i2c->bus_clk_rate) { + dev_info(dev, "Not set dtb clock-frequency, set default 100kHz\n"); + i2c->bus_clk_rate = I2C_MAX_STANDARD_MODE_FREQ; + } + + return 0; +} + +static const struct of_device_id nvt_i2c_of_match[] = { + { .compatible = "novatek,nt72676-i2c" }, + { } +}; +MODULE_DEVICE_TABLE(of, nvt_i2c_of_match); + +static const struct i2c_adapter_quirks nvt_i2c_quirks = { + .flags = I2C_AQ_COMB_WRITE_THEN_READ | I2C_AQ_NO_ZERO_LEN, + .max_num_msgs = 2, + .max_write_len = 4096, + .max_read_len = 4096, + .max_comb_1st_msg_len = SUBADDR_MAX_LEN, + .max_comb_2nd_msg_len = 4096, +}; + +static int nvt_i2c_probe(struct platform_device *pdev) +{ + struct nvt_i2c_bus *i2c; + int ret; + + i2c = devm_kzalloc(&pdev->dev, sizeof(*i2c), GFP_KERNEL); + if (!i2c) + return -ENOMEM; + + i2c->base = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(i2c->base)) + return PTR_ERR(i2c->base); + + spin_lock_init(&i2c->lock); + init_completion(&i2c->msg_complete); + i2c->dev = &pdev->dev; + + ret = nvt_i2c_parse_dts(i2c); + if (ret) + return ret; + + i2c->clk = devm_clk_get_enabled(&pdev->dev, NULL); + if (IS_ERR(i2c->clk)) + return dev_err_probe(&pdev->dev, PTR_ERR(i2c->clk), + "devm_clk_get_enabled fail\n"); + + i2c->input_clk_rate = clk_get_rate(i2c->clk); + if (!i2c->input_clk_rate) + return dev_err_probe(&pdev->dev, -EINVAL, "Invalid input clock rate\n"); + + if (i2c->input_clk_rate < i2c->bus_clk_rate) + return dev_err_probe(&pdev->dev, -EINVAL, + "Input clock rate %lu is lower than bus frequency %u\n", + i2c->input_clk_rate, i2c->bus_clk_rate); + + i2c->irq = platform_get_irq(pdev, 0); + if (i2c->irq < 0) + return i2c->irq; + + ret = nvt_i2c_init(i2c); + if (ret) + return dev_err_probe(&pdev->dev, ret, "nvt_i2c_init fail\n"); + + ret = devm_request_irq(&pdev->dev, i2c->irq, nvt_i2c_isr, + IRQF_SHARED, dev_name(&pdev->dev), i2c); + if (ret) { + nvt_i2c_release(i2c); + return dev_err_probe(&pdev->dev, ret, "devm_request_irq fail\n"); + } + + /* Setup I2C adapter */ + i2c->adapter.owner = THIS_MODULE; + i2c->adapter.algo = &nvt_i2c_algo; + i2c->adapter.dev.of_node = pdev->dev.of_node; + i2c->adapter.dev.parent = &pdev->dev; + i2c->adapter.quirks = &nvt_i2c_quirks; + i2c->adapter.timeout = 3 * HZ; + strscpy(i2c->adapter.name, dev_name(&pdev->dev), sizeof(i2c->adapter.name)); + i2c_set_adapdata(&i2c->adapter, i2c); + + ret = i2c_add_adapter(&i2c->adapter); + if (ret) { + nvt_i2c_release(i2c); + return dev_err_probe(&pdev->dev, ret, "Failed to add adapter\n"); + } + + platform_set_drvdata(pdev, i2c); + + return 0; +} + +static void nvt_i2c_remove(struct platform_device *pdev) +{ + struct nvt_i2c_bus *i2c = platform_get_drvdata(pdev); + unsigned long flags; + + i2c_del_adapter(&i2c->adapter); + spin_lock_irqsave(&i2c->lock, flags); + nvt_i2c_release(i2c); + spin_unlock_irqrestore(&i2c->lock, flags); +} + +static const struct dev_pm_ops nvt_i2c_pm_ops = { + .resume_early = nvt_i2c_resume, + .suspend_late = nvt_i2c_suspend, +}; + +static struct platform_driver nvt_i2c_driver = { + .probe = nvt_i2c_probe, + .remove = nvt_i2c_remove, + .driver = { + .name = "nt72676_i2c", + .pm = &nvt_i2c_pm_ops, + .of_match_table = of_match_ptr(nvt_i2c_of_match), + }, +}; +module_platform_driver(nvt_i2c_driver); + +MODULE_DESCRIPTION("Novatek NT72676 SoC I2C Bus Driver"); +MODULE_AUTHOR("Ben Huang "); +MODULE_LICENSE("GPL"); From 898f9403066a96e74b97b0554d372fd49149fd05 Mon Sep 17 00:00:00 2001 From: Ben Huang Date: Thu, 10 Sep 2026 11:01:22 +0800 Subject: [PATCH 25/37] MAINTAINERS: Add entry for Novatek NT72676 I2C controller driver Add a maintainer entry for Novatek NT72676 I2C controller driver. Signed-off-by: Ben Huang Signed-off-by: Nina Kuo Signed-off-by: Andi Shyti Link: https://patch.msgid.link/20260910030123.215609-4-nina_kuo@novatek.com.tw --- MAINTAINERS | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/MAINTAINERS b/MAINTAINERS index 3a19da74d00c..5d37eb3ab0a2 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -19442,6 +19442,13 @@ T: git git://git.kernel.org/pub/scm/linux/kernel/git/nolibc/linux-nolibc.git F: tools/include/nolibc/ F: tools/testing/selftests/nolibc/ +NOVATEK NT72676 I2C CONTROLLER DRIVER +M: Ben Huang +L: linux-i2c@vger.kernel.org +S: Maintained +F: Documentation/devicetree/bindings/i2c/novatek,nt72676-i2c.yaml +F: drivers/i2c/busses/i2c-nt72676.c + NOVATEK NVT-TS I2C TOUCHSCREEN DRIVER M: Hans de Goede L: linux-input@vger.kernel.org From 0265dfc5d2a8de303561ced996c46b38f95cb4ef Mon Sep 17 00:00:00 2001 From: Hitesh Patel Date: Wed, 7 Oct 2026 10:31:46 +0530 Subject: [PATCH 26/37] i2c: qcom-cci: always enable SCL clock stretching The CCI timing tables leave SCL clock stretching disabled. A slave that holds SCL low is then not waited for: the master keeps its own clock timing and the transfer fails with a NACK or returns corrupt data. This is hit with a camera reached through a GMSL serializer/deserializer I2C tunnel (MAX9296A/MAX96717 on the RB3 Gen2 vision mezzanine). The deserializer acknowledges the address locally, but forwards the transaction over the coax link and stretches SCL until the remote side has completed it, which takes well over one clock period at 100 kHz. Without stretching the register reads of the sensor behind the link intermittently return garbage and writes are dropped, which shows up as random sensor init failures. Clock stretching is part of the I2C specification for every speed mode and a device that does not stretch is unaffected by enabling it, so set the bit unconditionally in cci_init() and drop the per-table scl_stretch_en field, which is zero in every table. Signed-off-by: Hitesh Patel Reviewed-by: Loic Poulain Tested-by: Hangxiang Ma # S5KJN5 on Kaanapali Signed-off-by: Andi Shyti Link: https://patch.msgid.link/20261007050147.274096-1-hitesh@ebytelogic.com --- drivers/i2c/busses/i2c-qcom-cci.c | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/drivers/i2c/busses/i2c-qcom-cci.c b/drivers/i2c/busses/i2c-qcom-cci.c index d692f72c50a1..056527b7405c 100644 --- a/drivers/i2c/busses/i2c-qcom-cci.c +++ b/drivers/i2c/busses/i2c-qcom-cci.c @@ -28,6 +28,7 @@ #define CCI_I2C_Mm_SDA_CTL_1(m) (0x108 + 0x100 * (m)) #define CCI_I2C_Mm_SDA_CTL_2(m) (0x10c + 0x100 * (m)) #define CCI_I2C_Mm_MISC_CTL(m) (0x110 + 0x100 * (m)) +#define CCI_I2C_MISC_CTL_SCL_STRETCH_EN BIT(8) #define CCI_I2C_Mm_READ_DATA(m) (0x118 + 0x100 * (m)) #define CCI_I2C_Mm_READ_BUF_LEVEL(m) (0x11c + 0x100 * (m)) @@ -105,7 +106,6 @@ struct hw_params { u16 thd_dat; /* data hold time */ u16 thd_sta; /* hold time (repeated) START condition */ u16 tbuf; /* bus free time between a STOP and START condition */ - u8 scl_stretch_en; u16 trdhld; u16 tsp; /* pulse width of spikes suppressed by the input filter */ }; @@ -260,7 +260,6 @@ static const struct hw_params cci_hw_params[NUM_CCI_CLK_RATES][NUM_I2C_MODES] = .thd_dat = 10, .thd_sta = 77, .tbuf = 118, - .scl_stretch_en = 0, .trdhld = 6, .tsp = 1 }, @@ -272,7 +271,6 @@ static const struct hw_params cci_hw_params[NUM_CCI_CLK_RATES][NUM_I2C_MODES] = .thd_dat = 13, .thd_sta = 18, .tbuf = 32, - .scl_stretch_en = 0, .trdhld = 6, .tsp = 3 }, @@ -284,7 +282,6 @@ static const struct hw_params cci_hw_params[NUM_CCI_CLK_RATES][NUM_I2C_MODES] = .thd_dat = 22, .thd_sta = 162, .tbuf = 227, - .scl_stretch_en = 0, .trdhld = 6, .tsp = 3 }, @@ -296,7 +293,6 @@ static const struct hw_params cci_hw_params[NUM_CCI_CLK_RATES][NUM_I2C_MODES] = .thd_dat = 22, .thd_sta = 35, .tbuf = 62, - .scl_stretch_en = 0, .trdhld = 6, .tsp = 3 }, @@ -308,7 +304,6 @@ static const struct hw_params cci_hw_params[NUM_CCI_CLK_RATES][NUM_I2C_MODES] = .thd_dat = 16, .thd_sta = 15, .tbuf = 24, - .scl_stretch_en = 0, .trdhld = 3, .tsp = 3 }, @@ -368,7 +363,7 @@ static int cci_init(struct cci *cci) val = hw->tbuf; writel(val, cci->base + CCI_I2C_Mm_SDA_CTL_2(i)); - val = hw->scl_stretch_en << 8 | hw->trdhld << 4 | hw->tsp; + val = CCI_I2C_MISC_CTL_SCL_STRETCH_EN | hw->trdhld << 4 | hw->tsp; writel(val, cci->base + CCI_I2C_Mm_MISC_CTL(i)); } From 7109df1eafca7163f7714d287df922b4f2c104b2 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Thu, 16 Jul 2026 15:30:29 +0200 Subject: [PATCH 27/37] i2c: core: document preference for i2c_get_adapter_by_fwnode() The i2c subsystem currently blocks during adapter deregistration whenever there are consumers holding a reference. Document that i2c_get_adapter_by_fwnode(), which also takes a module reference, should almost always be preferred over i2c_find_adapter_by_fwnode() so that an attempt to unload the adapter module while in use fails gracefully instead of blocking uninterruptibly. Note that by implication of_get_i2c_adapter_by_node() is also generally preferred over of_find_i2c_adapter_by_node(). Signed-off-by: Johan Hovold Reviewed-by: Wolfram Sang Signed-off-by: Wolfram Sang Signed-off-by: Andi Shyti Link: https://patch.msgid.link/20260716133029.1565853-1-johan@kernel.org --- drivers/i2c/i2c-core-base.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/i2c/i2c-core-base.c b/drivers/i2c/i2c-core-base.c index ddaacf876dad..852d6a08518c 100644 --- a/drivers/i2c/i2c-core-base.c +++ b/drivers/i2c/i2c-core-base.c @@ -1902,6 +1902,9 @@ static int i2c_dev_or_parent_fwnode_match(struct device *dev, const void *data) * If no adapter can be found, or @fwnode is NULL, this returns NULL. * * The user must call put_device(&adapter->dev) once done with the i2c adapter. + * + * Note that i2c_get_adapter_by_fwnode(), which also takes a module reference, + * should almost always be used instead of this one. */ struct i2c_adapter *i2c_find_adapter_by_fwnode(struct fwnode_handle *fwnode) { From ed404d9fd36b826f43e85a7c7cc77b3d966cf83d Mon Sep 17 00:00:00 2001 From: Oleg Keri Date: Fri, 9 Oct 2026 01:07:25 +0200 Subject: [PATCH 28/37] i2c: qcom-cci: skip core clock rates the clock cannot provide cci_get_required_rate() picks the lowest rate that has timings for every active master, but never checks that the CCI clock can run at it. On glymur, sm8750 and kaanapali the CCI clock table has no 19.2 MHz entry, so the request rounds up to 30 MHz or 37.5 MHz, the timing lookup for the running rate fails and the controller does not probe: i2c-qcom-cci ac16000.cci: CCI clock is not at expected 19200000 Hz i2c-qcom-cci ac16000.cci: no timing for mode 1 at CCI clock 30000000 Hz i2c-qcom-cci ac16000.cci: probe with driver i2c-qcom-cci failed with error -95 Only consider rates clk_round_rate() can deliver exactly. Glymur then runs its CCI at 37.5 MHz, which has timings for every mode. Fixes: fa1eb3af12fa ("i2c: qcom-cci: Enforce the required CCI clock rate") Signed-off-by: Oleg Keri Reviewed-by: Loic Poulain Signed-off-by: Andi Shyti Link: https://patch.msgid.link/20261008230725.3228-1-okerixx@gmail.com --- drivers/i2c/busses/i2c-qcom-cci.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/i2c/busses/i2c-qcom-cci.c b/drivers/i2c/busses/i2c-qcom-cci.c index 056527b7405c..5eb1c59c2d14 100644 --- a/drivers/i2c/busses/i2c-qcom-cci.c +++ b/drivers/i2c/busses/i2c-qcom-cci.c @@ -581,6 +581,9 @@ static int cci_get_required_rate(struct cci *cci, unsigned long *rate) for (int ri = 0; ri < NUM_CCI_CLK_RATES; ri++) { bool supported = true; + if (clk_round_rate(cci->cci_clk, cci_clk_rates[ri]) != cci_clk_rates[ri]) + continue; + for (int i = 0; i < cci->data->num_masters; i++) { int mode = cci->master[i].mode; From ef3af9bb58a9f758622980c09974a9bb13f304f6 Mon Sep 17 00:00:00 2001 From: Manuel Ebner Date: Wed, 16 Sep 2026 16:07:52 +0200 Subject: [PATCH 29/37] docs: i2c: writing-clients: Add missing end of command Add missing ');'. Signed-off-by: Manuel Ebner Signed-off-by: Andi Shyti Link: https://patch.msgid.link/20260916140752.329836-2-manuelebnerli@mailbox.org --- Documentation/i2c/writing-clients.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Documentation/i2c/writing-clients.rst b/Documentation/i2c/writing-clients.rst index 121e618e72ec..8bab8b16ca8a 100644 --- a/Documentation/i2c/writing-clients.rst +++ b/Documentation/i2c/writing-clients.rst @@ -258,7 +258,7 @@ Driver Information :: /* Substitute your own name and email address */ - MODULE_AUTHOR("Frodo Looijaard " + MODULE_AUTHOR("Frodo Looijaard "); MODULE_DESCRIPTION("Driver for Barf Inc. Foo I2C devices"); /* a few non-GPL license types are also allowed */ From 4906001042f89cc98f3b310254ab48a16132b17e Mon Sep 17 00:00:00 2001 From: Peng Fan Date: Wed, 16 Sep 2026 23:25:26 +0800 Subject: [PATCH 30/37] i2c: imx-lpi2c: use dmaengine_get_dma_device() for DMA mapping Replace all chan->device->dev references with dmaengine_get_dma_device(chan) so that DMA mapping goes through the correct device when the DMA controller has per-channel IOMMU domains (chan_dma_dev=true). Without this, DMA map/unmap bypasses the IOMMU context configured by the eDMA driver. Signed-off-by: Peng Fan Reviewed-by: Frank Li Signed-off-by: Andi Shyti Link: https://patch.msgid.link/20260916-i2c-dma-v1-1-6dd4f1916910@nxp.com --- drivers/i2c/busses/i2c-imx-lpi2c.c | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/drivers/i2c/busses/i2c-imx-lpi2c.c b/drivers/i2c/busses/i2c-imx-lpi2c.c index 1cfd7a4c8237..addd64985abc 100644 --- a/drivers/i2c/busses/i2c-imx-lpi2c.c +++ b/drivers/i2c/busses/i2c-imx-lpi2c.c @@ -780,7 +780,7 @@ static void lpi2c_dma_unmap(struct lpi2c_imx_dma *dma) struct dma_chan *chan = dma->dma_data_dir == DMA_FROM_DEVICE ? dma->chan_rx : dma->chan_tx; - dma_unmap_single(chan->device->dev, dma->dma_addr, + dma_unmap_single(dmaengine_get_dma_device(chan), dma->dma_addr, dma->dma_len, dma->dma_data_dir); dma->dma_data_dir = DMA_NONE; @@ -789,7 +789,7 @@ static void lpi2c_dma_unmap(struct lpi2c_imx_dma *dma) static void lpi2c_cleanup_rx_cmd_dma(struct lpi2c_imx_dma *dma) { dmaengine_terminate_sync(dma->chan_tx); - dma_unmap_single(dma->chan_tx->device->dev, dma->dma_tx_addr, + dma_unmap_single(dmaengine_get_dma_device(dma->chan_tx), dma->dma_tx_addr, dma->rx_cmd_buf_len, DMA_TO_DEVICE); } @@ -817,10 +817,10 @@ static int lpi2c_dma_rx_cmd_submit(struct lpi2c_imx_struct *lpi2c_imx) struct dma_chan *txchan = dma->chan_tx; dma_cookie_t cookie; - dma->dma_tx_addr = dma_map_single(txchan->device->dev, + dma->dma_tx_addr = dma_map_single(dmaengine_get_dma_device(txchan), dma->rx_cmd_buf, dma->rx_cmd_buf_len, DMA_TO_DEVICE); - if (dma_mapping_error(txchan->device->dev, dma->dma_tx_addr)) { + if (dma_mapping_error(dmaengine_get_dma_device(txchan), dma->dma_tx_addr)) { dev_err(&lpi2c_imx->adapter.dev, "DMA map failed, use pio\n"); return -EINVAL; } @@ -844,12 +844,12 @@ static int lpi2c_dma_rx_cmd_submit(struct lpi2c_imx_struct *lpi2c_imx) return 0; desc_prepare_err_exit: - dma_unmap_single(txchan->device->dev, dma->dma_tx_addr, + dma_unmap_single(dmaengine_get_dma_device(txchan), dma->dma_tx_addr, dma->rx_cmd_buf_len, DMA_TO_DEVICE); return -EINVAL; submit_err_exit: - dma_unmap_single(txchan->device->dev, dma->dma_tx_addr, + dma_unmap_single(dmaengine_get_dma_device(txchan), dma->dma_tx_addr, dma->rx_cmd_buf_len, DMA_TO_DEVICE); dmaengine_desc_free(rx_cmd_desc); return -EINVAL; @@ -872,9 +872,9 @@ static int lpi2c_dma_submit(struct lpi2c_imx_struct *lpi2c_imx) dma->dma_transfer_dir = DMA_MEM_TO_DEV; } - dma->dma_addr = dma_map_single(chan->device->dev, + dma->dma_addr = dma_map_single(dmaengine_get_dma_device(chan), dma->dma_buf, dma->dma_len, dma->dma_data_dir); - if (dma_mapping_error(chan->device->dev, dma->dma_addr)) { + if (dma_mapping_error(dmaengine_get_dma_device(chan), dma->dma_addr)) { dev_err(&lpi2c_imx->adapter.dev, "DMA map failed, use pio\n"); return -EINVAL; } @@ -1085,7 +1085,7 @@ static int lpi2c_imx_dma_xfer(struct lpi2c_imx_struct *lpi2c_imx, } if (dma->dma_msg_flag & I2C_M_RD) - dma_unmap_single(dma->chan_tx->device->dev, dma->dma_tx_addr, + dma_unmap_single(dmaengine_get_dma_device(dma->chan_tx), dma->dma_tx_addr, dma->rx_cmd_buf_len, DMA_TO_DEVICE); lpi2c_dma_unmap(dma); From e7d5d642c089cec5592c295c5fe9c03cb0b62384 Mon Sep 17 00:00:00 2001 From: Navon John Lukose Date: Thu, 8 Oct 2026 03:52:31 +0530 Subject: [PATCH 31/37] i2c: designware: size the RX FIFO threshold to the queued transfer i2c_dw_xfer_msg() leaves DW_IC_RX_TL at the 0 that i2c_dw_configure_mode() writes, so the controller raises RX_FULL once per received byte. Program RX_TL from the reads already queued, capped at half the FIFO until the last message is queued, so there is room for the rest. On an Arrow Lake-H LPSS core with rx_fifo_depth 32, a 22-byte HID input report costs 2.00 interrupts instead of 21.43, and a 452-byte descriptor 0.14 interrupts per byte instead of 1.64. Polling the touchpad with HIDIOCGINPUT at 142 Hz draws 1.58 W less platform power (psys, 32 paired runs). Assisted-by: LLM Signed-off-by: Navon John Lukose Acked-by: Mika Westerberg Signed-off-by: Andi Shyti Link: https://patch.msgid.link/20261007191911.502428-1-navonjohnlukose@gmail.com --- drivers/i2c/busses/i2c-designware-master.c | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/drivers/i2c/busses/i2c-designware-master.c b/drivers/i2c/busses/i2c-designware-master.c index a1bcc3797e4f..f4ad20799045 100644 --- a/drivers/i2c/busses/i2c-designware-master.c +++ b/drivers/i2c/busses/i2c-designware-master.c @@ -378,7 +378,7 @@ i2c_dw_xfer_msg(struct dw_i2c_dev *dev) { struct i2c_msg *msgs = dev->msgs; u32 intr_mask; - int tx_limit, rx_limit; + int tx_limit, rx_limit, rx_tl; u32 buf_len = dev->tx_buf_len; u8 *buf = dev->tx_buf; bool need_restart = false; @@ -484,6 +484,19 @@ i2c_dw_xfer_msg(struct dw_i2c_dev *dev) if (dev->msg_err) intr_mask = 0; + /* + * Size the RX FIFO threshold to the reads already queued, so the + * controller raises one RX_FULL for the whole burst instead of one per + * received byte. While messages remain to be queued, cap it so RX_FULL + * still arrives in time to drain the FIFO and let the next TX_EMPTY + * queue the rest. + */ + rx_tl = dev->rx_outstanding; + if (dev->msg_write_idx < dev->msgs_num) + rx_tl = min_t(int, rx_tl, dev->rx_fifo_depth / 2); + + regmap_write(dev->map, DW_IC_RX_TL, rx_tl ? rx_tl - 1 : 0); + __i2c_dw_write_intr_mask(dev, intr_mask); } From fe87113a9ad03d19a7631139ad00e456a6f050e4 Mon Sep 17 00:00:00 2001 From: Jisheng Zhang Date: Mon, 5 Oct 2026 23:20:36 +0800 Subject: [PATCH 32/37] dt-bindings: i2c: dw: Add Synaptics sl2610 i2c Add the compatible for the Synaptics SL2610 SoC I2C controllers, which are compatible with the Synopsys DesignWare I2C. Signed-off-by: Jisheng Zhang Acked-by: Andi Shyti Acked-by: Conor Dooley Signed-off-by: Andi Shyti Link: https://patch.msgid.link/20261005152036.25276-1-jszhang@kernel.org --- Documentation/devicetree/bindings/i2c/snps,designware-i2c.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/Documentation/devicetree/bindings/i2c/snps,designware-i2c.yaml b/Documentation/devicetree/bindings/i2c/snps,designware-i2c.yaml index 8a7c0a44601b..a73e2a6e0833 100644 --- a/Documentation/devicetree/bindings/i2c/snps,designware-i2c.yaml +++ b/Documentation/devicetree/bindings/i2c/snps,designware-i2c.yaml @@ -57,6 +57,7 @@ properties: - mobileye,eyeq6lplus-i2c - mscc,ocelot-i2c - sophgo,sg2044-i2c + - syna,sl2610-i2c - thead,th1520-i2c - ultrarisc,dp1000-i2c - const: snps,designware-i2c From 09753024bbd330b31ec5510fe22a248fd94d5b53 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Sat, 19 Sep 2026 21:36:41 +0200 Subject: [PATCH 33/37] i2c: acpi: Use I2C_M_DMA_SAFE in acpi_gsb_i2c_[read|write]_bytes() The main read / write "buffer" is kzalloc()ed and as such is safe to directly use for DMA (unlike stack mem). Add the I2C_M_DMA_SAFE flag to the transfers using these buffers. Signed-off-by: Hans de Goede Acked-by: Mika Westerberg Signed-off-by: Andi Shyti Link: https://patch.msgid.link/20260919193642.43058-1-johannes.goede@oss.qualcomm.com --- drivers/i2c/i2c-core-acpi.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/i2c/i2c-core-acpi.c b/drivers/i2c/i2c-core-acpi.c index 564e6ad66398..f0bf7eba5415 100644 --- a/drivers/i2c/i2c-core-acpi.c +++ b/drivers/i2c/i2c-core-acpi.c @@ -609,7 +609,7 @@ static int acpi_gsb_i2c_read_bytes(struct i2c_client *client, msgs[0].buf = &cmd; msgs[1].addr = client->addr; - msgs[1].flags = client->flags | I2C_M_RD; + msgs[1].flags = client->flags | I2C_M_RD | I2C_M_DMA_SAFE; msgs[1].len = data_len; msgs[1].buf = buffer; @@ -650,7 +650,7 @@ static int acpi_gsb_i2c_write_bytes(struct i2c_client *client, memcpy(buffer + 1, data, data_len); msgs[0].addr = client->addr; - msgs[0].flags = client->flags; + msgs[0].flags = client->flags | I2C_M_DMA_SAFE; msgs[0].len = data_len + 1; msgs[0].buf = buffer; From bf31dd72f0d50d5acc71d7777b5dae6c51b3ba17 Mon Sep 17 00:00:00 2001 From: Yo-Jung Date: Tue, 13 Oct 2026 13:36:03 +0530 Subject: [PATCH 34/37] i2c: i801: Gate SPD instantiation on SPD Write Disable bit When DDR5 memory modules are present, probing the spd5118 hub requires page-selection write transactions. If the BIOS has set the SPD Write Disable (SPD_WD) bit in SMBHSTCFG, the transaction fails and wedges the controller into an unrecoverable "SMBus is busy" state. Pass the PCH SPD Write Disable status into the SMBus core via i2c_register_spd_write_disable(). This safely skips spd5118 instantiation under write-protection while leaving legacy DDR4 (ee1004) functional. Retain the original #ifdef CONFIG_I2C_I801_MUX preprocessor block around mux_pdev to prevent compile-time regressions when the multiplexer driver is disabled. Closes: https://bugzilla.kernel.org/show_bug.cgi?id=213345 Co-developed-by: Saranya Gopal Signed-off-by: Saranya Gopal Signed-off-by: Yo-Jung Tested-by: Mark Pearson Reviewed-by: Andy Shevchenko Signed-off-by: Andi Shyti Link: https://patch.msgid.link/20261013080603.808196-1-saranya.gopal@intel.com --- drivers/i2c/busses/i2c-i801.c | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/drivers/i2c/busses/i2c-i801.c b/drivers/i2c/busses/i2c-i801.c index b29c99ed3883..810f61e39cad 100644 --- a/drivers/i2c/busses/i2c-i801.c +++ b/drivers/i2c/busses/i2c-i801.c @@ -1170,6 +1170,14 @@ static void dmi_check_onboard_devices(const struct dmi_header *dm, void *adap) } } +static void i801_register_spd(struct i801_priv *priv) +{ + if (priv->original_hstcfg & SMBHSTCFG_SPD_WD) + i2c_register_spd_write_disable(&priv->adapter); + else + i2c_register_spd_write_enable(&priv->adapter); +} + /* Register optional targets */ static void i801_probe_optional_targets(struct i801_priv *priv) { @@ -1193,7 +1201,7 @@ static void i801_probe_optional_targets(struct i801_priv *priv) #ifdef CONFIG_I2C_I801_MUX if (!priv->mux_pdev) #endif - i2c_register_spd_write_enable(&priv->adapter); + i801_register_spd(priv); } #else static void __init input_apanel_init(void) {} From 45a97832883ec36e5e3a3b50d66dac46cc94191c Mon Sep 17 00:00:00 2001 From: Pawel Zalewski Date: Fri, 18 Sep 2026 14:01:50 +0100 Subject: [PATCH 35/37] i2c: i2c-core-acpi: use named initializers for acpi_device_id Use a designated initializer for the acpi_device_id fields which makes the code more readable and consistent with how lists are initialized in the rest of the kernel code base. Also drop explicitly setting fields to 0 where it is redundant. Unify the list terminator to have a single space between the brackets and no trailing comma. Signed-off-by: Pawel Zalewski Acked-by: Mika Westerberg Signed-off-by: Andi Shyti Link: https://patch.msgid.link/20260918-acpi-i2c-v1-1-caf4bea4886c@thegoodpenguin.co.uk --- drivers/i2c/i2c-core-acpi.c | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/drivers/i2c/i2c-core-acpi.c b/drivers/i2c/i2c-core-acpi.c index f0bf7eba5415..703588e9990a 100644 --- a/drivers/i2c/i2c-core-acpi.c +++ b/drivers/i2c/i2c-core-acpi.c @@ -352,8 +352,8 @@ static const struct acpi_device_id i2c_acpi_force_400khz_device_ids[] = { * at 400KHz (as can be expected of any recent i2c hw) so we force * the speed of the bus to 400 KHz if a Silead device is present. */ - { "MSSL1680", 0 }, - {} + { .id = "MSSL1680" }, + { } }; static const struct acpi_device_id i2c_acpi_force_100khz_device_ids[] = { @@ -371,13 +371,13 @@ static const struct acpi_device_id i2c_acpi_force_100khz_device_ids[] = { * the device works without issues on Windows at what is expected to be * a 400KHz frequency. The root cause of the issue is not known. */ - { "ASUE140D", 0 }, - { "DELL0A86", 0 }, - { "DLL0945", 0 }, - { "ELAN0678", 0 }, - { "ELAN06FA", 0 }, - { "ELAN1300", 0 }, - {} + { .id = "ASUE140D" }, + { .id = "DELL0A86" }, + { .id = "DLL0945" }, + { .id = "ELAN0678" }, + { .id = "ELAN06FA" }, + { .id = "ELAN1300" }, + { } }; static acpi_status i2c_acpi_lookup_speed(acpi_handle handle, u32 level, From 38f5659310d94a81d512caab85cbc04993b870eb Mon Sep 17 00:00:00 2001 From: Pawel Zalewski Date: Fri, 18 Sep 2026 14:01:51 +0100 Subject: [PATCH 36/37] i2c: busses: use named initializers for acpi_device_id Use a designated initializer for the acpi_device_id fields which makes the code more readable and consistent with how lists are initialized in the rest of the kernel code base. Also drop explicitly setting fields to 0 where it is redundant. Unify the list terminator to have a single space between the brackets and no trailing comma. Unify list item alignment to have a single space padding between the brackets. Signed-off-by: Pawel Zalewski Acked-by: Mika Westerberg Signed-off-by: Andi Shyti Link: https://patch.msgid.link/20260918-acpi-i2c-v1-2-caf4bea4886c@thegoodpenguin.co.uk --- drivers/i2c/busses/i2c-amd-asf-plat.c | 2 +- drivers/i2c/busses/i2c-amd-mp2-plat.c | 2 +- drivers/i2c/busses/i2c-cros-ec-tunnel.c | 2 +- drivers/i2c/busses/i2c-designware-platdrv.c | 42 ++++++++++----------- drivers/i2c/busses/i2c-gpio.c | 2 +- drivers/i2c/busses/i2c-hisi.c | 2 +- drivers/i2c/busses/i2c-imx.c | 2 +- drivers/i2c/busses/i2c-ls2x.c | 2 +- drivers/i2c/busses/i2c-mlxbf.c | 8 ++-- drivers/i2c/busses/i2c-qcom-geni.c | 4 +- drivers/i2c/busses/i2c-qup.c | 2 +- drivers/i2c/busses/i2c-scmi.c | 8 ++-- drivers/i2c/busses/i2c-synquacer.c | 4 +- drivers/i2c/busses/i2c-tegra.c | 8 ++-- drivers/i2c/busses/i2c-usbio.c | 12 +++--- drivers/i2c/busses/i2c-viai2c-zhaoxin.c | 2 +- drivers/i2c/busses/i2c-xgene-slimpro.c | 6 +-- drivers/i2c/busses/i2c-xlp9xx.c | 6 +-- 18 files changed, 58 insertions(+), 58 deletions(-) diff --git a/drivers/i2c/busses/i2c-amd-asf-plat.c b/drivers/i2c/busses/i2c-amd-asf-plat.c index b0b40fe1d79b..2a5711411ee5 100644 --- a/drivers/i2c/busses/i2c-amd-asf-plat.c +++ b/drivers/i2c/busses/i2c-amd-asf-plat.c @@ -354,7 +354,7 @@ static int amd_asf_probe(struct platform_device *pdev) } static const struct acpi_device_id amd_asf_acpi_ids[] = { - { "AMDI001A" }, + { .id = "AMDI001A" }, { } }; MODULE_DEVICE_TABLE(acpi, amd_asf_acpi_ids); diff --git a/drivers/i2c/busses/i2c-amd-mp2-plat.c b/drivers/i2c/busses/i2c-amd-mp2-plat.c index 9fdd6a5fb8b6..cc5961364bcc 100644 --- a/drivers/i2c/busses/i2c-amd-mp2-plat.c +++ b/drivers/i2c/busses/i2c-amd-mp2-plat.c @@ -341,7 +341,7 @@ static void i2c_amd_remove(struct platform_device *pdev) } static const struct acpi_device_id i2c_amd_acpi_match[] = { - { "AMDI0011" }, + { .id = "AMDI0011" }, { } }; MODULE_DEVICE_TABLE(acpi, i2c_amd_acpi_match); diff --git a/drivers/i2c/busses/i2c-cros-ec-tunnel.c b/drivers/i2c/busses/i2c-cros-ec-tunnel.c index 208ce4f9e782..a83e735d2086 100644 --- a/drivers/i2c/busses/i2c-cros-ec-tunnel.c +++ b/drivers/i2c/busses/i2c-cros-ec-tunnel.c @@ -300,7 +300,7 @@ static const struct of_device_id cros_ec_i2c_of_match[] __maybe_unused = { MODULE_DEVICE_TABLE(of, cros_ec_i2c_of_match); static const struct acpi_device_id cros_ec_i2c_tunnel_acpi_id[] __maybe_unused = { - { "GOOG0012", 0 }, + { .id = "GOOG0012" }, { } }; MODULE_DEVICE_TABLE(acpi, cros_ec_i2c_tunnel_acpi_id); diff --git a/drivers/i2c/busses/i2c-designware-platdrv.c b/drivers/i2c/busses/i2c-designware-platdrv.c index 447af5523c2e..1da6b0314b6a 100644 --- a/drivers/i2c/busses/i2c-designware-platdrv.c +++ b/drivers/i2c/busses/i2c-designware-platdrv.c @@ -260,27 +260,27 @@ static const struct of_device_id dw_i2c_of_match[] = { MODULE_DEVICE_TABLE(of, dw_i2c_of_match); static const struct acpi_device_id dw_i2c_acpi_match[] = { - { "80860F41", ACCESS_NO_IRQ_SUSPEND }, - { "808622C1", ACCESS_NO_IRQ_SUSPEND }, - { "AMD0010", ACCESS_INTR_MASK }, - { "AMDI0010", ACCESS_INTR_MASK }, - { "AMDI0019", ACCESS_INTR_MASK | ARBITRATION_SEMAPHORE }, - { "AMDI0510", 0 }, - { "APMC0D0F", 0 }, - { "FUJI200B", 0 }, - { "GOOG5000", 0 }, - { "HISI02A1", 0 }, - { "HISI02A2", 0 }, - { "HISI02A3", 0 }, - { "HJMC3001", ACCESS_INTR_MASK }, - { "HYGO0010", ACCESS_INTR_MASK }, - { "INT33C2", 0 }, - { "INT33C3", 0 }, - { "INT3432", 0 }, - { "INT3433", 0 }, - { "INTC10EF", 0 }, - { "LECA0003", 0 }, - {} + { .id = "80860F41", .driver_data = ACCESS_NO_IRQ_SUSPEND }, + { .id = "808622C1", .driver_data = ACCESS_NO_IRQ_SUSPEND }, + { .id = "AMD0010", .driver_data = ACCESS_INTR_MASK }, + { .id = "AMDI0010", .driver_data = ACCESS_INTR_MASK }, + { .id = "AMDI0019", .driver_data = ACCESS_INTR_MASK | ARBITRATION_SEMAPHORE }, + { .id = "AMDI0510" }, + { .id = "APMC0D0F" }, + { .id = "FUJI200B" }, + { .id = "GOOG5000" }, + { .id = "HISI02A1" }, + { .id = "HISI02A2" }, + { .id = "HISI02A3" }, + { .id = "HJMC3001", .driver_data = ACCESS_INTR_MASK }, + { .id = "HYGO0010", .driver_data = ACCESS_INTR_MASK }, + { .id = "INT33C2" }, + { .id = "INT33C3" }, + { .id = "INT3432" }, + { .id = "INT3433" }, + { .id = "INTC10EF" }, + { .id = "LECA0003" }, + { } }; MODULE_DEVICE_TABLE(acpi, dw_i2c_acpi_match); diff --git a/drivers/i2c/busses/i2c-gpio.c b/drivers/i2c/busses/i2c-gpio.c index b7521b7ece37..9c08d7bce937 100644 --- a/drivers/i2c/busses/i2c-gpio.c +++ b/drivers/i2c/busses/i2c-gpio.c @@ -664,7 +664,7 @@ static const struct of_device_id i2c_gpio_dt_ids[] = { MODULE_DEVICE_TABLE(of, i2c_gpio_dt_ids); static const struct acpi_device_id i2c_gpio_acpi_match[] = { - { "LOON0005" }, /* LoongArch */ + { .id = "LOON0005" }, /* LoongArch */ { } }; MODULE_DEVICE_TABLE(acpi, i2c_gpio_acpi_match); diff --git a/drivers/i2c/busses/i2c-hisi.c b/drivers/i2c/busses/i2c-hisi.c index ba5c9579ae19..fca1ca011312 100644 --- a/drivers/i2c/busses/i2c-hisi.c +++ b/drivers/i2c/busses/i2c-hisi.c @@ -516,7 +516,7 @@ static int hisi_i2c_probe(struct platform_device *pdev) } static const struct acpi_device_id hisi_i2c_acpi_ids[] = { - { "HISI03D1", 0 }, + { .id = "HISI03D1" }, { } }; MODULE_DEVICE_TABLE(acpi, hisi_i2c_acpi_ids); diff --git a/drivers/i2c/busses/i2c-imx.c b/drivers/i2c/busses/i2c-imx.c index 19ec056b00af..e5fb493327b1 100644 --- a/drivers/i2c/busses/i2c-imx.c +++ b/drivers/i2c/busses/i2c-imx.c @@ -349,7 +349,7 @@ static const struct of_device_id i2c_imx_dt_ids[] = { MODULE_DEVICE_TABLE(of, i2c_imx_dt_ids); static const struct acpi_device_id i2c_imx_acpi_ids[] = { - {"NXP0001", .driver_data = (kernel_ulong_t)&vf610_i2c_hwdata}, + { .id = "NXP0001", .driver_data = (kernel_ulong_t)&vf610_i2c_hwdata }, { } }; MODULE_DEVICE_TABLE(acpi, i2c_imx_acpi_ids); diff --git a/drivers/i2c/busses/i2c-ls2x.c b/drivers/i2c/busses/i2c-ls2x.c index af7bf61b0f4d..ef2e2a26981e 100644 --- a/drivers/i2c/busses/i2c-ls2x.c +++ b/drivers/i2c/busses/i2c-ls2x.c @@ -356,7 +356,7 @@ static const struct of_device_id ls2x_i2c_id_table[] = { MODULE_DEVICE_TABLE(of, ls2x_i2c_id_table); static const struct acpi_device_id ls2x_i2c_acpi_match[] = { - { "LOON0004" }, /* Loongson LS7A */ + { .id = "LOON0004" }, /* Loongson LS7A */ { } }; MODULE_DEVICE_TABLE(acpi, ls2x_i2c_acpi_match); diff --git a/drivers/i2c/busses/i2c-mlxbf.c b/drivers/i2c/busses/i2c-mlxbf.c index 24ccc4546ab8..0cc8b9e5f333 100644 --- a/drivers/i2c/busses/i2c-mlxbf.c +++ b/drivers/i2c/busses/i2c-mlxbf.c @@ -2198,10 +2198,10 @@ static struct i2c_adapter_quirks mlxbf_i2c_quirks = { }; static const struct acpi_device_id mlxbf_i2c_acpi_ids[] = { - { "MLNXBF03", (kernel_ulong_t)&mlxbf_i2c_chip[MLXBF_I2C_CHIP_TYPE_1] }, - { "MLNXBF23", (kernel_ulong_t)&mlxbf_i2c_chip[MLXBF_I2C_CHIP_TYPE_2] }, - { "MLNXBF31", (kernel_ulong_t)&mlxbf_i2c_chip[MLXBF_I2C_CHIP_TYPE_3] }, - {}, + { .id = "MLNXBF03", .driver_data = (kernel_ulong_t)&mlxbf_i2c_chip[MLXBF_I2C_CHIP_TYPE_1] }, + { .id = "MLNXBF23", .driver_data = (kernel_ulong_t)&mlxbf_i2c_chip[MLXBF_I2C_CHIP_TYPE_2] }, + { .id = "MLNXBF31", .driver_data = (kernel_ulong_t)&mlxbf_i2c_chip[MLXBF_I2C_CHIP_TYPE_3] }, + { } }; MODULE_DEVICE_TABLE(acpi, mlxbf_i2c_acpi_ids); diff --git a/drivers/i2c/busses/i2c-qcom-geni.c b/drivers/i2c/busses/i2c-qcom-geni.c index 01d1fdab5163..3fa69de30b06 100644 --- a/drivers/i2c/busses/i2c-qcom-geni.c +++ b/drivers/i2c/busses/i2c-qcom-geni.c @@ -1298,8 +1298,8 @@ static const struct geni_i2c_desc sa8255p_geni_i2c = { #ifdef CONFIG_ACPI static const struct acpi_device_id geni_i2c_acpi_match[] = { - { "QCOM0220", (kernel_ulong_t)&geni_i2c}, - { "QCOM0411", (kernel_ulong_t)&geni_i2c}, + { .id = "QCOM0220", .driver_data = (kernel_ulong_t)&geni_i2c }, + { .id = "QCOM0411", .driver_data = (kernel_ulong_t)&geni_i2c }, { } }; MODULE_DEVICE_TABLE(acpi, geni_i2c_acpi_match); diff --git a/drivers/i2c/busses/i2c-qup.c b/drivers/i2c/busses/i2c-qup.c index ee7915ee2ba2..ce30634e1f40 100644 --- a/drivers/i2c/busses/i2c-qup.c +++ b/drivers/i2c/busses/i2c-qup.c @@ -1688,7 +1688,7 @@ static void qup_i2c_disable_clocks(struct qup_i2c_dev *qup) } static const struct acpi_device_id qup_i2c_acpi_match[] = { - { "QCOM8010"}, + { .id = "QCOM8010" }, { } }; MODULE_DEVICE_TABLE(acpi, qup_i2c_acpi_match); diff --git a/drivers/i2c/busses/i2c-scmi.c b/drivers/i2c/busses/i2c-scmi.c index 324a63efa1ab..37442c9ee222 100644 --- a/drivers/i2c/busses/i2c-scmi.c +++ b/drivers/i2c/busses/i2c-scmi.c @@ -42,10 +42,10 @@ static const struct smbus_methods_t ibm_smbus_methods = { }; static const struct acpi_device_id acpi_smbus_cmi_ids[] = { - {"SMBUS01", (kernel_ulong_t)&smbus_methods}, - {ACPI_SMBUS_IBM_HID, (kernel_ulong_t)&ibm_smbus_methods}, - {ACPI_SMBUS_MS_HID, (kernel_ulong_t)&smbus_methods}, - {"", 0} + { .id = "SMBUS01", .driver_data = (kernel_ulong_t)&smbus_methods }, + { .id = ACPI_SMBUS_IBM_HID, .driver_data = (kernel_ulong_t)&ibm_smbus_methods }, + { .id = ACPI_SMBUS_MS_HID, .driver_data = (kernel_ulong_t)&smbus_methods }, + { } }; MODULE_DEVICE_TABLE(acpi, acpi_smbus_cmi_ids); diff --git a/drivers/i2c/busses/i2c-synquacer.c b/drivers/i2c/busses/i2c-synquacer.c index 0216e398f274..968cbe32161e 100644 --- a/drivers/i2c/busses/i2c-synquacer.c +++ b/drivers/i2c/busses/i2c-synquacer.c @@ -622,8 +622,8 @@ MODULE_DEVICE_TABLE(of, synquacer_i2c_dt_ids); #ifdef CONFIG_ACPI static const struct acpi_device_id synquacer_i2c_acpi_ids[] = { - { "SCX0003" }, - { /* sentinel */ } + { .id = "SCX0003" }, + { } }; MODULE_DEVICE_TABLE(acpi, synquacer_i2c_acpi_ids); #endif diff --git a/drivers/i2c/busses/i2c-tegra.c b/drivers/i2c/busses/i2c-tegra.c index 9c281b4905eb..76d21bc51c61 100644 --- a/drivers/i2c/busses/i2c-tegra.c +++ b/drivers/i2c/busses/i2c-tegra.c @@ -2486,10 +2486,10 @@ static const struct dev_pm_ops tegra_i2c_pm = { }; static const struct acpi_device_id tegra_i2c_acpi_match[] = { - {.id = "NVDA0101", .driver_data = (kernel_ulong_t)&tegra210_i2c_hw}, - {.id = "NVDA0201", .driver_data = (kernel_ulong_t)&tegra186_i2c_hw}, - {.id = "NVDA0301", .driver_data = (kernel_ulong_t)&tegra194_i2c_hw}, - {.id = "NVDA2017", .driver_data = (kernel_ulong_t)&tegra410_i2c_hw}, + { .id = "NVDA0101", .driver_data = (kernel_ulong_t)&tegra210_i2c_hw }, + { .id = "NVDA0201", .driver_data = (kernel_ulong_t)&tegra186_i2c_hw }, + { .id = "NVDA0301", .driver_data = (kernel_ulong_t)&tegra194_i2c_hw }, + { .id = "NVDA2017", .driver_data = (kernel_ulong_t)&tegra410_i2c_hw }, { } }; MODULE_DEVICE_TABLE(acpi, tegra_i2c_acpi_match); diff --git a/drivers/i2c/busses/i2c-usbio.c b/drivers/i2c/busses/i2c-usbio.c index 259754e5fd05..66da1e90848a 100644 --- a/drivers/i2c/busses/i2c-usbio.c +++ b/drivers/i2c/busses/i2c-usbio.c @@ -24,12 +24,12 @@ struct usbio_i2c { }; static const struct acpi_device_id usbio_i2c_acpi_hids[] = { - { "INTC1008" }, /* MTL */ - { "INTC10B3" }, /* ARL */ - { "INTC10B6" }, /* LNL */ - { "INTC10D2" }, /* MTL-CVF */ - { "INTC10E3" }, /* PTL */ - { "INTC1118" }, /* NVL */ + { .id = "INTC1008" }, /* MTL */ + { .id = "INTC10B3" }, /* ARL */ + { .id = "INTC10B6" }, /* LNL */ + { .id = "INTC10D2" }, /* MTL-CVF */ + { .id = "INTC10E3" }, /* PTL */ + { .id = "INTC1118" }, /* NVL */ { } }; diff --git a/drivers/i2c/busses/i2c-viai2c-zhaoxin.c b/drivers/i2c/busses/i2c-viai2c-zhaoxin.c index 764a2dbc957a..81f0bfdf919f 100644 --- a/drivers/i2c/busses/i2c-viai2c-zhaoxin.c +++ b/drivers/i2c/busses/i2c-viai2c-zhaoxin.c @@ -345,7 +345,7 @@ static const struct dev_pm_ops zxi2c_pm = { }; static const struct acpi_device_id zxi2c_acpi_match[] = { - {"IIC1D17", 0 }, + { .id = "IIC1D17" }, { } }; MODULE_DEVICE_TABLE(acpi, zxi2c_acpi_match); diff --git a/drivers/i2c/busses/i2c-xgene-slimpro.c b/drivers/i2c/busses/i2c-xgene-slimpro.c index b29dec66b2c3..08a126aa83df 100644 --- a/drivers/i2c/busses/i2c-xgene-slimpro.c +++ b/drivers/i2c/busses/i2c-xgene-slimpro.c @@ -539,9 +539,9 @@ MODULE_DEVICE_TABLE(of, xgene_slimpro_i2c_dt_ids); #ifdef CONFIG_ACPI static const struct acpi_device_id xgene_slimpro_i2c_acpi_ids[] = { - {"APMC0D40", XGENE_SLIMPRO_I2C_V1}, - {"APMC0D8B", XGENE_SLIMPRO_I2C_V2}, - {} + { .id = "APMC0D40", .driver_data = XGENE_SLIMPRO_I2C_V1 }, + { .id = "APMC0D8B", .driver_data = XGENE_SLIMPRO_I2C_V2 }, + { } }; MODULE_DEVICE_TABLE(acpi, xgene_slimpro_i2c_acpi_ids); #endif diff --git a/drivers/i2c/busses/i2c-xlp9xx.c b/drivers/i2c/busses/i2c-xlp9xx.c index ad0e5cd218f0..44cad95ee8ab 100644 --- a/drivers/i2c/busses/i2c-xlp9xx.c +++ b/drivers/i2c/busses/i2c-xlp9xx.c @@ -570,9 +570,9 @@ static void xlp9xx_i2c_remove(struct platform_device *pdev) #ifdef CONFIG_ACPI static const struct acpi_device_id xlp9xx_i2c_acpi_ids[] = { - {"BRCM9007", 0}, - {"CAV9007", 0}, - {} + { .id = "BRCM9007" }, + { .id = "CAV9007" }, + { } }; MODULE_DEVICE_TABLE(acpi, xlp9xx_i2c_acpi_ids); #endif From 0f6be986ad0eb6fe7338d963a14eaf0dee87677d Mon Sep 17 00:00:00 2001 From: Frank Li Date: Tue, 29 Sep 2026 15:06:05 -0400 Subject: [PATCH 37/37] i2c: imx-lpi2c: remove dmaengine_desc_free() dmaengine_desc_free() is designed for DMA_CTRL_REUSE. If DMA_CTRL_REUSE is not set, dmaengine_desc_free() will return -EPERM. if (!dmaengine_desc_test_reuse(desc)) return -EPERM; So calling dmaengine_desc_free() does nothing. dmaengine_terminate_(a)sync() will free all already allocated descriptors. Signed-off-by: Frank Li Reviewed-by: Carlos Song Signed-off-by: Andi Shyti Link: https://patch.msgid.link/20260929190605.3516147-1-Frank.Li@oss.nxp.com --- drivers/i2c/busses/i2c-imx-lpi2c.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/drivers/i2c/busses/i2c-imx-lpi2c.c b/drivers/i2c/busses/i2c-imx-lpi2c.c index addd64985abc..7302464c371a 100644 --- a/drivers/i2c/busses/i2c-imx-lpi2c.c +++ b/drivers/i2c/busses/i2c-imx-lpi2c.c @@ -851,7 +851,6 @@ static int lpi2c_dma_rx_cmd_submit(struct lpi2c_imx_struct *lpi2c_imx) submit_err_exit: dma_unmap_single(dmaengine_get_dma_device(txchan), dma->dma_tx_addr, dma->rx_cmd_buf_len, DMA_TO_DEVICE); - dmaengine_desc_free(rx_cmd_desc); return -EINVAL; } @@ -910,7 +909,6 @@ static int lpi2c_dma_submit(struct lpi2c_imx_struct *lpi2c_imx) submit_err_exit: lpi2c_dma_unmap(dma); - dmaengine_desc_free(desc); return -EINVAL; }