Linux-ARM-Kernel Archive on lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH v32 1/5] dt-bindings: i2c: Split AST2600 binding into a new YAML
From: Ryan Chen @ 2026-06-11  5:31 UTC (permalink / raw)
  To: jk, andriy.shevchenko, Andi Shyti, Rob Herring,
	Krzysztof Kozlowski, Conor Dooley, Joel Stanley, Andrew Jeffery,
	Benjamin Herrenschmidt, Rayn Chen, Philipp Zabel
  Cc: linux-i2c, devicetree, linux-arm-kernel, linux-aspeed,
	linux-kernel, openbmc, Ryan Chen, Conor Dooley
In-Reply-To: <20260611-upstream_i2c-v32-0-b66eba921d01@aspeedtech.com>

The AST2600 I2C controller introduces a completely new register layout
with separate controller and target register blocks, unlike the mixed
register layout used by AST2400/AST2500.

Move AST2600 I2C binding from aspeed,i2c.yaml to a dedicated
aspeed,ast2600-i2c.yaml schema.

Besides the split, this also adjusts for AST2600-specific requirements.
- describe two reg regions (controller register block + buffer block);
  the second region is optional (minItems: 1) so existing AST2600 DTs
  that only declare the controller register block continue to validate
- use clock-frequency for bus speed description
- interrupts are required on AST2600
- use correct DTS coding style in example

No compatible strings are changed.

Acked-by: Conor Dooley <conor.dooley@microchip.com>
Reviewed-by: Rob Herring (Arm) <robh@kernel.org>
Signed-off-by: Ryan Chen <ryan_chen@aspeedtech.com>
---
Changes in v31:
- Commit message body: clarify that the second reg region is optional
  (minItems: 1) rather than required, matching the schema and the v30
  backward-compatibility fix (Sashiko AI review).

Changes in v30:
- Add minItems: 1 to reg so existing AST2600 DTs with a single reg
  region continue to validate (Sashiko AI review)
- Retain bus-frequency as a deprecated property to avoid breaking
  existing AST2600 DTs under unevaluatedProperties: false
  (Sashiko AI review)
Changes in v26:
- commit message: include details of changes from original binding
- fix example property ordering to follow DTS coding style
- use consistent "AST2600" naming
---
 .../bindings/i2c/aspeed,ast2600-i2c.yaml           | 73 ++++++++++++++++++++++
 .../devicetree/bindings/i2c/aspeed,i2c.yaml        |  3 +-
 MAINTAINERS                                        |  1 +
 3 files changed, 75 insertions(+), 2 deletions(-)

diff --git a/Documentation/devicetree/bindings/i2c/aspeed,ast2600-i2c.yaml b/Documentation/devicetree/bindings/i2c/aspeed,ast2600-i2c.yaml
new file mode 100644
index 000000000000..abc614315dff
--- /dev/null
+++ b/Documentation/devicetree/bindings/i2c/aspeed,ast2600-i2c.yaml
@@ -0,0 +1,73 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/i2c/aspeed,ast2600-i2c.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: ASPEED I2C on the AST2600 SoCs
+
+maintainers:
+  - Ryan Chen <ryan_chen@aspeedtech.com>
+
+allOf:
+  - $ref: /schemas/i2c/i2c-controller.yaml#
+
+properties:
+  compatible:
+    enum:
+      - aspeed,ast2600-i2c-bus
+
+  reg:
+    minItems: 1
+    items:
+      - description: controller registers
+      - description: controller buffer space
+
+  interrupts:
+    maxItems: 1
+
+  clocks:
+    maxItems: 1
+
+  clock-frequency:
+    description: Desired operating frequency of the I2C bus in Hz.
+    minimum: 500
+    maximum: 4000000
+    default: 100000
+
+  bus-frequency:
+    $ref: /schemas/types.yaml#/definitions/uint32
+    deprecated: true
+    description:
+      Legacy name for clock-frequency. Existing AST2600 device trees
+      used this before the binding was split out. New device trees
+      should use the standard clock-frequency property instead.
+    minimum: 500
+    maximum: 4000000
+
+  resets:
+    maxItems: 1
+
+required:
+  - reg
+  - compatible
+  - clocks
+  - resets
+  - interrupts
+
+unevaluatedProperties: false
+
+examples:
+  - |
+    #include <dt-bindings/clock/aspeed-clock.h>
+    #include <dt-bindings/interrupt-controller/arm-gic.h>
+    i2c@80 {
+        compatible = "aspeed,ast2600-i2c-bus";
+        reg = <0x80 0x80>, <0xc00 0x20>;
+        #address-cells = <1>;
+        #size-cells = <0>;
+        clocks = <&syscon ASPEED_CLK_APB>;
+        resets = <&syscon ASPEED_RESET_I2C>;
+        clock-frequency = <100000>;
+        interrupts = <GIC_SPI 110 IRQ_TYPE_LEVEL_HIGH>;
+    };
diff --git a/Documentation/devicetree/bindings/i2c/aspeed,i2c.yaml b/Documentation/devicetree/bindings/i2c/aspeed,i2c.yaml
index 5b9bd2feda3b..d4e4f412feba 100644
--- a/Documentation/devicetree/bindings/i2c/aspeed,i2c.yaml
+++ b/Documentation/devicetree/bindings/i2c/aspeed,i2c.yaml
@@ -4,7 +4,7 @@
 $id: http://devicetree.org/schemas/i2c/aspeed,i2c.yaml#
 $schema: http://devicetree.org/meta-schemas/core.yaml#
 
-title: ASPEED I2C on the AST24XX, AST25XX, and AST26XX SoCs
+title: ASPEED I2C on the AST24XX, AST25XX SoCs
 
 maintainers:
   - Rayn Chen <rayn_chen@aspeedtech.com>
@@ -17,7 +17,6 @@ properties:
     enum:
       - aspeed,ast2400-i2c-bus
       - aspeed,ast2500-i2c-bus
-      - aspeed,ast2600-i2c-bus
 
   reg:
     minItems: 1
diff --git a/MAINTAINERS b/MAINTAINERS
index 882214b0e7db..f9c929e86e64 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -2596,6 +2596,7 @@ R:	Joel Stanley <joel@jms.id.au>
 L:	linux-i2c@vger.kernel.org
 L:	openbmc@lists.ozlabs.org (moderated for non-subscribers)
 S:	Maintained
+F:	Documentation/devicetree/bindings/i2c/aspeed,ast2600-i2c.yaml
 F:	Documentation/devicetree/bindings/i2c/aspeed,i2c.yaml
 F:	Documentation/devicetree/bindings/interrupt-controller/aspeed,ast2400-i2c-ic.yaml
 F:	drivers/i2c/busses/i2c-aspeed.c

-- 
2.34.1



^ permalink raw reply related

* [PATCH v32 2/5] i2c: aspeed: Read clock-frequency via i2c_parse_fw_timings()
From: Ryan Chen @ 2026-06-11  5:31 UTC (permalink / raw)
  To: jk, andriy.shevchenko, Andi Shyti, Rob Herring,
	Krzysztof Kozlowski, Conor Dooley, Joel Stanley, Andrew Jeffery,
	Benjamin Herrenschmidt, Rayn Chen, Philipp Zabel
  Cc: linux-i2c, devicetree, linux-arm-kernel, linux-aspeed,
	linux-kernel, openbmc, Ryan Chen
In-Reply-To: <20260611-upstream_i2c-v32-0-b66eba921d01@aspeedtech.com>

Use i2c_parse_fw_timings() to read the standard "clock-frequency"
property, and fall back to "bus-frequency" only when the standard
property is absent.

This honors device trees written against the updated
aspeed,ast2600-i2c binding without silently falling back to 100 kHz,
while keeping existing in-tree device trees using "bus-frequency"
working.

Signed-off-by: Ryan Chen <ryan_chen@aspeedtech.com>
---
Changes in v31:
- Zero-initialise `struct i2c_timings timings` so the bus-frequency
  fallback runs when clock-frequency is absent (Sashiko AI review).
---
 drivers/i2c/busses/i2c-aspeed.c | 19 +++++++++++++------
 1 file changed, 13 insertions(+), 6 deletions(-)

diff --git a/drivers/i2c/busses/i2c-aspeed.c b/drivers/i2c/busses/i2c-aspeed.c
index a26b74c71206..f00bd779146e 100644
--- a/drivers/i2c/busses/i2c-aspeed.c
+++ b/drivers/i2c/busses/i2c-aspeed.c
@@ -1000,6 +1000,7 @@ static int aspeed_i2c_probe_bus(struct platform_device *pdev)
 	const struct of_device_id *match;
 	struct aspeed_i2c_bus *bus;
 	struct clk *parent_clk;
+	struct i2c_timings timings = {};
 	int irq, ret;
 
 	bus = devm_kzalloc(&pdev->dev, sizeof(*bus), GFP_KERNEL);
@@ -1025,12 +1026,18 @@ static int aspeed_i2c_probe_bus(struct platform_device *pdev)
 	}
 	reset_control_deassert(bus->rst);
 
-	ret = of_property_read_u32(pdev->dev.of_node,
-				   "bus-frequency", &bus->bus_frequency);
-	if (ret < 0) {
-		dev_err(&pdev->dev,
-			"Could not read bus-frequency property\n");
-		bus->bus_frequency = I2C_MAX_STANDARD_MODE_FREQ;
+	i2c_parse_fw_timings(&pdev->dev, &timings, false);
+	if (timings.bus_freq_hz) {
+		bus->bus_frequency = timings.bus_freq_hz;
+	} else {
+		ret = of_property_read_u32(pdev->dev.of_node,
+					   "bus-frequency",
+					   &bus->bus_frequency);
+		if (ret < 0) {
+			dev_err(&pdev->dev,
+				"Could not read clock-frequency or bus-frequency property\n");
+			bus->bus_frequency = I2C_MAX_STANDARD_MODE_FREQ;
+		}
 	}
 
 	match = of_match_node(aspeed_i2c_bus_of_table, pdev->dev.of_node);

-- 
2.34.1



^ permalink raw reply related

* [PATCH v32 3/5] dt-bindings: i2c: ast2600-i2c.yaml: Add global-regs properties
From: Ryan Chen @ 2026-06-11  5:31 UTC (permalink / raw)
  To: jk, andriy.shevchenko, Andi Shyti, Rob Herring,
	Krzysztof Kozlowski, Conor Dooley, Joel Stanley, Andrew Jeffery,
	Benjamin Herrenschmidt, Rayn Chen, Philipp Zabel
  Cc: linux-i2c, devicetree, linux-arm-kernel, linux-aspeed,
	linux-kernel, openbmc, Ryan Chen, Conor Dooley
In-Reply-To: <20260611-upstream_i2c-v32-0-b66eba921d01@aspeedtech.com>

Add the aspeed,global-regs phandle to reference the AST2600 global
registers syscon node, containing the SoC-common I2C register set.

These properties apply only to the AST2600 binding. Legacy DTs remain
unchanged.

Acked-by: Conor Dooley <conor.dooley@microchip.com>
Reviewed-by: Rob Herring (Arm) <robh@kernel.org>
Signed-off-by: Ryan Chen <ryan_chen@aspeedtech.com>
---
Changes in v32:
- Add if/then conditional schema: when aspeed,global-regs is present,
  require reg to have at least two items. The new driver unconditionally
  maps resource index 1 (the buffer SRAM region); a DT with one reg
  entry and aspeed,global-regs passes schema validation but fails probe.
  The constraint makes the schema consistent with driver behaviour.
- Fix binding example to use the correct AST2600 clock header
  (ast2600-clock.h) and ASPEED_CLK_APB2 instead of the legacy
  aspeed-clock.h, where index 26 (ASPEED_CLK_APB) maps to the
  UART5 gate clock on AST2600 rather than the APB2 bus clock
  used by the I2C controller.

Changes in v29:
- remove aspeed,enable-dma properties.

Changes in v28:
- update commit message correspond with aspeed,enable-dma.
- remove aspeed,transfer-mode and add aspeed,enable-dma property and
  description.
- Fix aspeed,enable-dma description to reflect hardware capability rather
  than software behavior

Changes in v27:
- change aspeed,transfer-mode to aspeed,enable-dma.
---
 .../devicetree/bindings/i2c/aspeed,ast2600-i2c.yaml   | 19 +++++++++++++++++--
 1 file changed, 17 insertions(+), 2 deletions(-)

diff --git a/Documentation/devicetree/bindings/i2c/aspeed,ast2600-i2c.yaml b/Documentation/devicetree/bindings/i2c/aspeed,ast2600-i2c.yaml
index abc614315dff..eb6e316c112f 100644
--- a/Documentation/devicetree/bindings/i2c/aspeed,ast2600-i2c.yaml
+++ b/Documentation/devicetree/bindings/i2c/aspeed,ast2600-i2c.yaml
@@ -48,6 +48,12 @@ properties:
   resets:
     maxItems: 1
 
+  aspeed,global-regs:
+    $ref: /schemas/types.yaml#/definitions/phandle
+    description:
+      Phandle reference to the i2c global syscon node, containing the
+      SoC-common i2c register set.
+
 required:
   - reg
   - compatible
@@ -55,19 +61,28 @@ required:
   - resets
   - interrupts
 
+if:
+  required:
+    - aspeed,global-regs
+then:
+  properties:
+    reg:
+      minItems: 2
+
 unevaluatedProperties: false
 
 examples:
   - |
-    #include <dt-bindings/clock/aspeed-clock.h>
+    #include <dt-bindings/clock/ast2600-clock.h>
     #include <dt-bindings/interrupt-controller/arm-gic.h>
     i2c@80 {
         compatible = "aspeed,ast2600-i2c-bus";
         reg = <0x80 0x80>, <0xc00 0x20>;
         #address-cells = <1>;
         #size-cells = <0>;
-        clocks = <&syscon ASPEED_CLK_APB>;
+        clocks = <&syscon ASPEED_CLK_APB2>;
         resets = <&syscon ASPEED_RESET_I2C>;
         clock-frequency = <100000>;
         interrupts = <GIC_SPI 110 IRQ_TYPE_LEVEL_HIGH>;
+        aspeed,global-regs = <&i2c_global>;
     };

-- 
2.34.1



^ permalink raw reply related

* [PATCH v32 5/5] i2c: ast2600: Add target mode support
From: Ryan Chen @ 2026-06-11  5:31 UTC (permalink / raw)
  To: jk, andriy.shevchenko, Andi Shyti, Rob Herring,
	Krzysztof Kozlowski, Conor Dooley, Joel Stanley, Andrew Jeffery,
	Benjamin Herrenschmidt, Rayn Chen, Philipp Zabel
  Cc: linux-i2c, devicetree, linux-arm-kernel, linux-aspeed,
	linux-kernel, openbmc, Ryan Chen
In-Reply-To: <20260611-upstream_i2c-v32-0-b66eba921d01@aspeedtech.com>

Add target mode support to the AST2600 I2C driver.

Target mode features implemented include:
- Add target interrupt handling
- Address match and response logic

This complements the existing controller-mode support, enabling
dual-role capability.

Signed-off-by: Ryan Chen <ryan_chen@aspeedtech.com>
---
Changes in v32:
- Fix target RX data loss in the master-abort fast path: remove the
  BUFF_CTRL zeroing that preceded the controller IER clear. The shared
  BUFF_CTRL register holds the pending target RX length in bits [29:24];
  zeroing it before the switch statement causes target_rx_len to read
  back as 0, silently discarding all bytes already received by the
  target.
- Fix use-after-free in master-abort fast path: null msgs and re-enable
  the controller IER before calling complete(), not after. Calling
  complete() first allows process context to immediately start a new
  i2c_transfer() and install a new msgs pointer; a stale controller IRQ
  firing during the subsequent IER restore would then dereference the
  new transfer's msgs buffer.
- Fix shared-buffer corruption on coalesced STOP+SLAVE_MATCH IRQ:
  restore the SLAVE_PENDING guard on the target_active = false
  transition. SLAVE_PENDING (bit 29) is set precisely when a new
  address-match is already queued while the previous DMA receive has
  not yet been processed. Clearing target_active unconditionally on
  STOP in that case lets the controller side overwrite the shared Tx/Rx
  buffer before the new target transaction has been re-armed.
- Use READ_ONCE() for all process-context reads of target_active.
  The IRQ path writes target_active with WRITE_ONCE(); plain loads in
  process context allow the compiler to cache the value across the
  IER-disable window, potentially seeing a stale false and starting a
  controller transfer that races with an active target transaction.

Changes in v31:
- Address Sashiko AI code review feedback on the target patch:
  - Clear target_active on any STOP. The previous condition
    `(sts & STOP) && !(sts & SLAVE_PENDING)` failed under coalesced
    STOP+SLAVE_PENDING IRQs and left target_active stuck true,
    deadlocking controller_xfer() with -EBUSY forever.
  - Enable the target IER inside reg_target() instead of
    unconditionally in probe(). unreg_target() disables it; without
    matching re-enable in reg_target() a subsequent re-registration
    would never receive IRQs. Also avoids spurious IRQ activity when
    no slave is registered.
  - Add the missing CMD_STS write in the
    SLAVE_PENDING|RX_DONE|WAIT_TX_DMA|STOP target ISR case so the
    HW state machine is re-armed (TRIGGER | TX_BUFF_EN); otherwise
    the controller stretches SCL until INACTIVE_TO recovers.
  - Default target ISR case now writes TARGET_TRIGGER_CMD to re-arm
    the HW state machine instead of silently breaking, which
    previously left the bus hung on unhandled sts combinations.
  - W1C-clear the ADDR1/2/3_NAK bits in HW (not just locally) in
    ast2600_i2c_target_irq(); stale ADDR_NAK bits made
    controller_xfer() see I2CS_ISR != 0 and bounce every transfer
    with -EBUSY.

- unreg_target(): replace masked ADDR_CTRL write with writel(0, ...);
  AST2600_I2CS_ADDR1_MASK covers only bits[6:0] (the 7-bit address),
  leaving ADDR1_ENABLE (BIT(7)) set after unregister. Although SLAVE_EN
  is cleared first, writing 0 is the correct and complete teardown.

Changes in v30:
- Address Sashiko AI code review feedback:
  - Force-stop path (target IRQ aborting an in-flight controller
    transfer): disable the controller IER and W1C-clear pending ISR
    before calling complete(), then restore the IER after the wake-up.
    Without the disable/clear sequence the controller IRQ handler can
    race with the target abort path and double-complete or touch
    freed msgs.
  - unreg_target() teardown ordering: disable the target IER first,
    then disable SLAVE_EN / clear ADDR_CTRL, synchronize_irq(), W1C
    pending ISR, and only then NULL i2c_bus->target and clear
    target_active. The old order left IER enabled while target was
    being cleared, allowing an in-flight handler to dereference a
    target pointer the caller had already freed.
  - reg_target() bring-up ordering: assign i2c_bus->target before
    enabling SLAVE_EN. Otherwise an IRQ that fires after SLAVE_EN is
    set but before the pointer is stored finds target == NULL, exits
    without clearing the ISR, and the unmasked event re-fires as an
    IRQ storm.
  - Use writel() instead of writeb() when staging a TX byte into the
    target buffer. The AST2600 buffer SRAM only supports 32-bit
    accesses; byte writes are silently dropped (or, on some
    revisions, raise a bus fault), so a SLAVE_READ_REQUESTED reply
    never reaches the master.
  - reg_target() rejects 10-bit client addresses with -EAFNOSUPPORT.
    AST2600_I2CS_ADDR1 is only a 7-bit field; without the check, the
    high bits of a 10-bit address overflow into the adjacent ADDR2
    field and silently corrupt a second target slot.
  - Initialise the local `u8 value` to 0 in the target packet IRQ
    handler. Its address is passed to i2c_slave_event() for events
    such as I2C_SLAVE_STOP / I2C_SLAVE_READ_REQUESTED; a slave
    backend that reads the byte before writing would otherwise leak
    uninitialised kernel stack.

Changes in v29:
- fix race between unreg_target and IRQ handler.
- move i2cs ier enable from ast2600_i2c_init to probe after master ier enable.
- remove dma/byte transfer, use buffer mode only.

Changes in v28:
- fix typo condication -> condition
- fix compile error, when disable CONFIG_I2C_SLAVE

Changes in v26:
- change int to bool target_operate
- rename target_operate to target_active
- use i2c_bus->target replace require IO
- use WRITE_ONCE replace target_operate write.
---
 drivers/i2c/busses/i2c-ast2600.c | 359 +++++++++++++++++++++++++++++++++++++++
 1 file changed, 359 insertions(+)

diff --git a/drivers/i2c/busses/i2c-ast2600.c b/drivers/i2c/busses/i2c-ast2600.c
index 70f37dc77468..aace0c7eeb72 100644
--- a/drivers/i2c/busses/i2c-ast2600.c
+++ b/drivers/i2c/busses/i2c-ast2600.c
@@ -255,6 +255,11 @@ struct ast2600_i2c_bus {
 	bool			multi_master;
 	bool			stop_pending;
 	void __iomem		*buf_base;
+#if IS_ENABLED(CONFIG_I2C_SLAVE)
+	/* target structure */
+	bool			target_active;
+	struct i2c_client	*target;
+#endif
 };
 
 static void ast2600_i2c_ac_timing_config(struct ast2600_i2c_bus *i2c_bus)
@@ -348,6 +353,253 @@ static int ast2600_i2c_recover_bus(struct ast2600_i2c_bus *i2c_bus)
 	return ret;
 }
 
+#if IS_ENABLED(CONFIG_I2C_SLAVE)
+static void ast2600_i2c_target_packet_buff_irq(struct ast2600_i2c_bus *i2c_bus, u32 sts)
+{
+	u8 value = 0;
+	int target_rx_len = 0;
+	u32 cmd = 0;
+	int i;
+
+	/* due to controller target is common buffer, need force the master stop not issue */
+	if (readl(i2c_bus->reg_base + AST2600_I2CM_CMD_STS) & GENMASK(15, 0)) {
+		writel(0, i2c_bus->reg_base + AST2600_I2CM_CMD_STS);
+		writel(0, i2c_bus->reg_base + AST2600_I2CM_IER);
+		writel(readl(i2c_bus->reg_base + AST2600_I2CM_ISR),
+		       i2c_bus->reg_base + AST2600_I2CM_ISR);
+		i2c_bus->cmd_err = -EBUSY;
+		WRITE_ONCE(i2c_bus->msgs, NULL);
+		writel(AST2600_I2CM_PKT_DONE | AST2600_I2CM_BUS_RECOVER,
+		       i2c_bus->reg_base + AST2600_I2CM_IER);
+		complete(&i2c_bus->cmd_complete);
+	}
+
+	/* Handle i2c target timeout condition */
+	if (sts & AST2600_I2CS_INACTIVE_TO) {
+		/* Reset timeout counter */
+		u32 ac_timing = readl(i2c_bus->reg_base + AST2600_I2CC_AC_TIMING) &
+				AST2600_I2CC_AC_TIMING_MASK;
+
+		writel(ac_timing, i2c_bus->reg_base + AST2600_I2CC_AC_TIMING);
+		ac_timing |= AST2600_I2CC_TTIMEOUT(i2c_bus->timeout);
+		writel(ac_timing, i2c_bus->reg_base + AST2600_I2CC_AC_TIMING);
+		writel(TARGET_TRIGGER_CMD, i2c_bus->reg_base + AST2600_I2CS_CMD_STS);
+		writel(AST2600_I2CS_PKT_DONE, i2c_bus->reg_base + AST2600_I2CS_ISR);
+		i2c_slave_event(i2c_bus->target, I2C_SLAVE_STOP, &value);
+		WRITE_ONCE(i2c_bus->target_active, false);
+		return;
+	}
+
+	sts &= ~(AST2600_I2CS_PKT_DONE | AST2600_I2CS_PKT_ERROR);
+
+	if (sts & AST2600_I2CS_SLAVE_MATCH)
+		WRITE_ONCE(i2c_bus->target_active, true);
+
+	switch (sts) {
+	case AST2600_I2CS_SLAVE_PENDING | AST2600_I2CS_WAIT_RX_DMA |
+		 AST2600_I2CS_SLAVE_MATCH | AST2600_I2CS_RX_DONE | AST2600_I2CS_STOP:
+	case AST2600_I2CS_SLAVE_PENDING |
+		 AST2600_I2CS_SLAVE_MATCH | AST2600_I2CS_RX_DONE | AST2600_I2CS_STOP:
+	case AST2600_I2CS_SLAVE_PENDING |
+		 AST2600_I2CS_SLAVE_MATCH | AST2600_I2CS_STOP:
+		i2c_slave_event(i2c_bus->target, I2C_SLAVE_STOP, &value);
+		fallthrough;
+	case AST2600_I2CS_SLAVE_PENDING |
+		 AST2600_I2CS_WAIT_RX_DMA | AST2600_I2CS_SLAVE_MATCH | AST2600_I2CS_RX_DONE:
+	case AST2600_I2CS_WAIT_RX_DMA | AST2600_I2CS_SLAVE_MATCH | AST2600_I2CS_RX_DONE:
+	case AST2600_I2CS_WAIT_RX_DMA | AST2600_I2CS_SLAVE_MATCH:
+		i2c_slave_event(i2c_bus->target, I2C_SLAVE_WRITE_REQUESTED, &value);
+		cmd = TARGET_TRIGGER_CMD;
+		if (sts & AST2600_I2CS_RX_DONE) {
+			target_rx_len = AST2600_I2CC_GET_RX_BUF_LEN(readl(i2c_bus->reg_base +
+						       AST2600_I2CC_BUFF_CTRL));
+			for (i = 0; i < target_rx_len; i++) {
+				value = readb(i2c_bus->buf_base + i2c_bus->buf_size + i);
+				i2c_slave_event(i2c_bus->target, I2C_SLAVE_WRITE_RECEIVED, &value);
+			}
+		}
+		if (readl(i2c_bus->reg_base + AST2600_I2CS_CMD_STS) & AST2600_I2CS_RX_BUFF_EN)
+			cmd = 0;
+		else
+			cmd = TARGET_TRIGGER_CMD | AST2600_I2CS_RX_BUFF_EN;
+
+		writel(AST2600_I2CC_SET_RX_BUF_LEN(i2c_bus->buf_size),
+		       i2c_bus->reg_base + AST2600_I2CC_BUFF_CTRL);
+		break;
+	case AST2600_I2CS_WAIT_RX_DMA | AST2600_I2CS_RX_DONE:
+		cmd = TARGET_TRIGGER_CMD;
+		target_rx_len = AST2600_I2CC_GET_RX_BUF_LEN(readl(i2c_bus->reg_base +
+						       AST2600_I2CC_BUFF_CTRL));
+		for (i = 0; i < target_rx_len; i++) {
+			value = readb(i2c_bus->buf_base + i2c_bus->buf_size + i);
+			i2c_slave_event(i2c_bus->target, I2C_SLAVE_WRITE_RECEIVED, &value);
+		}
+		cmd |= AST2600_I2CS_RX_BUFF_EN;
+		writel(AST2600_I2CC_SET_RX_BUF_LEN(i2c_bus->buf_size),
+		       i2c_bus->reg_base + AST2600_I2CC_BUFF_CTRL);
+		break;
+	case AST2600_I2CS_SLAVE_PENDING | AST2600_I2CS_WAIT_RX_DMA |
+				AST2600_I2CS_RX_DONE | AST2600_I2CS_STOP:
+		cmd = TARGET_TRIGGER_CMD;
+		target_rx_len = AST2600_I2CC_GET_RX_BUF_LEN(readl(i2c_bus->reg_base +
+						       AST2600_I2CC_BUFF_CTRL));
+		for (i = 0; i < target_rx_len; i++) {
+			value = readb(i2c_bus->buf_base + i2c_bus->buf_size + i);
+			i2c_slave_event(i2c_bus->target, I2C_SLAVE_WRITE_RECEIVED, &value);
+		}
+		i2c_slave_event(i2c_bus->target, I2C_SLAVE_STOP, &value);
+		cmd |= AST2600_I2CS_RX_BUFF_EN;
+		writel(AST2600_I2CC_SET_RX_BUF_LEN(i2c_bus->buf_size),
+		       i2c_bus->reg_base + AST2600_I2CC_BUFF_CTRL);
+		break;
+	case AST2600_I2CS_SLAVE_PENDING | AST2600_I2CS_RX_DONE | AST2600_I2CS_STOP:
+		cmd = TARGET_TRIGGER_CMD;
+		target_rx_len = AST2600_I2CC_GET_RX_BUF_LEN(readl(i2c_bus->reg_base +
+						       AST2600_I2CC_BUFF_CTRL));
+		for (i = 0; i < target_rx_len; i++) {
+			value = readb(i2c_bus->buf_base + i2c_bus->buf_size + i);
+			i2c_slave_event(i2c_bus->target, I2C_SLAVE_WRITE_RECEIVED, &value);
+		}
+		/* workaround for avoid next start with len != 0 */
+		writel(BIT(0), i2c_bus->reg_base + AST2600_I2CC_BUFF_CTRL);
+		i2c_slave_event(i2c_bus->target, I2C_SLAVE_STOP, &value);
+		break;
+	case AST2600_I2CS_RX_DONE | AST2600_I2CS_STOP:
+		cmd = TARGET_TRIGGER_CMD;
+		target_rx_len = AST2600_I2CC_GET_RX_BUF_LEN(readl(i2c_bus->reg_base +
+						       AST2600_I2CC_BUFF_CTRL));
+		for (i = 0; i < target_rx_len; i++) {
+			value = readb(i2c_bus->buf_base + i2c_bus->buf_size + i);
+			i2c_slave_event(i2c_bus->target, I2C_SLAVE_WRITE_RECEIVED, &value);
+		}
+		/* workaround for avoid next start with len != 0 */
+		writel(BIT(0), i2c_bus->reg_base + AST2600_I2CC_BUFF_CTRL);
+		i2c_slave_event(i2c_bus->target, I2C_SLAVE_STOP, &value);
+		break;
+	case AST2600_I2CS_SLAVE_PENDING | AST2600_I2CS_RX_DONE |
+	     AST2600_I2CS_WAIT_TX_DMA | AST2600_I2CS_STOP:
+		target_rx_len = AST2600_I2CC_GET_RX_BUF_LEN(readl(i2c_bus->reg_base +
+						       AST2600_I2CC_BUFF_CTRL));
+		for (i = 0; i < target_rx_len; i++) {
+			value = readb(i2c_bus->buf_base + i2c_bus->buf_size + i);
+			i2c_slave_event(i2c_bus->target, I2C_SLAVE_WRITE_RECEIVED, &value);
+		}
+		i2c_slave_event(i2c_bus->target, I2C_SLAVE_STOP, &value);
+		i2c_slave_event(i2c_bus->target, I2C_SLAVE_READ_REQUESTED, &value);
+		writel(value, i2c_bus->buf_base);
+		writel(AST2600_I2CC_SET_TX_BUF_LEN(1),
+		       i2c_bus->reg_base + AST2600_I2CC_BUFF_CTRL);
+		cmd = TARGET_TRIGGER_CMD | AST2600_I2CS_TX_BUFF_EN;
+		break;
+	case AST2600_I2CS_WAIT_TX_DMA | AST2600_I2CS_SLAVE_MATCH:
+		i2c_slave_event(i2c_bus->target, I2C_SLAVE_READ_REQUESTED, &value);
+		writel(value, i2c_bus->buf_base);
+		writel(AST2600_I2CC_SET_TX_BUF_LEN(1),
+		       i2c_bus->reg_base + AST2600_I2CC_BUFF_CTRL);
+		cmd = TARGET_TRIGGER_CMD | AST2600_I2CS_TX_BUFF_EN;
+		break;
+	case AST2600_I2CS_SLAVE_PENDING | AST2600_I2CS_STOP |
+	     AST2600_I2CS_TX_NAK | AST2600_I2CS_SLAVE_MATCH | AST2600_I2CS_RX_DONE:
+	case AST2600_I2CS_SLAVE_PENDING | AST2600_I2CS_WAIT_RX_DMA | AST2600_I2CS_STOP |
+	     AST2600_I2CS_TX_NAK | AST2600_I2CS_SLAVE_MATCH | AST2600_I2CS_RX_DONE:
+		i2c_slave_event(i2c_bus->target, I2C_SLAVE_STOP, &value);
+		i2c_slave_event(i2c_bus->target, I2C_SLAVE_WRITE_REQUESTED, &value);
+		target_rx_len = AST2600_I2CC_GET_RX_BUF_LEN(readl(i2c_bus->reg_base +
+						       AST2600_I2CC_BUFF_CTRL));
+		for (i = 0; i < target_rx_len; i++) {
+			value = readb(i2c_bus->buf_base + i2c_bus->buf_size + i);
+			i2c_slave_event(i2c_bus->target, I2C_SLAVE_WRITE_RECEIVED, &value);
+		}
+		writel(AST2600_I2CC_SET_RX_BUF_LEN(i2c_bus->buf_size),
+		       i2c_bus->reg_base + AST2600_I2CC_BUFF_CTRL);
+		cmd = TARGET_TRIGGER_CMD | AST2600_I2CS_RX_BUFF_EN;
+		break;
+	case AST2600_I2CS_SLAVE_MATCH | AST2600_I2CS_WAIT_TX_DMA | AST2600_I2CS_RX_DONE:
+	case AST2600_I2CS_WAIT_TX_DMA | AST2600_I2CS_RX_DONE:
+	case AST2600_I2CS_WAIT_TX_DMA:
+		if (sts & AST2600_I2CS_SLAVE_MATCH)
+			i2c_slave_event(i2c_bus->target, I2C_SLAVE_WRITE_REQUESTED, &value);
+
+		if (sts & AST2600_I2CS_RX_DONE) {
+			target_rx_len = AST2600_I2CC_GET_RX_BUF_LEN(readl(i2c_bus->reg_base +
+						       AST2600_I2CC_BUFF_CTRL));
+			for (i = 0; i < target_rx_len; i++) {
+				value = readb(i2c_bus->buf_base + i2c_bus->buf_size + i);
+				i2c_slave_event(i2c_bus->target, I2C_SLAVE_WRITE_RECEIVED, &value);
+			}
+			i2c_slave_event(i2c_bus->target, I2C_SLAVE_READ_REQUESTED, &value);
+		} else {
+			i2c_slave_event(i2c_bus->target, I2C_SLAVE_READ_PROCESSED, &value);
+		}
+		writel(value, i2c_bus->buf_base);
+		writel(AST2600_I2CC_SET_TX_BUF_LEN(1),
+		       i2c_bus->reg_base + AST2600_I2CC_BUFF_CTRL);
+		cmd = TARGET_TRIGGER_CMD | AST2600_I2CS_TX_BUFF_EN;
+		break;
+	/* workaround : trigger the cmd twice to fix next state keep 1000000 */
+	case AST2600_I2CS_SLAVE_MATCH | AST2600_I2CS_RX_DONE:
+		i2c_slave_event(i2c_bus->target, I2C_SLAVE_WRITE_REQUESTED, &value);
+		cmd = TARGET_TRIGGER_CMD | AST2600_I2CS_RX_BUFF_EN;
+		writel(cmd, i2c_bus->reg_base + AST2600_I2CS_CMD_STS);
+		break;
+	case AST2600_I2CS_TX_NAK | AST2600_I2CS_STOP:
+	case AST2600_I2CS_STOP:
+		cmd = TARGET_TRIGGER_CMD;
+		i2c_slave_event(i2c_bus->target, I2C_SLAVE_STOP, &value);
+		break;
+	default:
+		dev_dbg(i2c_bus->dev, "unhandled target isr case %x, sts %x\n", sts,
+			readl(i2c_bus->reg_base + AST2600_I2CC_STS_AND_BUFF));
+		cmd = TARGET_TRIGGER_CMD;
+		break;
+	}
+
+	if (cmd)
+		writel(cmd, i2c_bus->reg_base + AST2600_I2CS_CMD_STS);
+
+	writel(AST2600_I2CS_PKT_DONE, i2c_bus->reg_base + AST2600_I2CS_ISR);
+	readl(i2c_bus->reg_base + AST2600_I2CS_ISR);
+
+	if ((sts & AST2600_I2CS_STOP) && !(sts & AST2600_I2CS_SLAVE_PENDING))
+		WRITE_ONCE(i2c_bus->target_active, false);
+}
+
+static int ast2600_i2c_target_irq(struct ast2600_i2c_bus *i2c_bus)
+{
+	u32 ier = readl(i2c_bus->reg_base + AST2600_I2CS_IER);
+	u32 isr = readl(i2c_bus->reg_base + AST2600_I2CS_ISR);
+
+	if (!(isr & ier))
+		return 0;
+
+	/*
+	 * Target interrupt coming after controller packet done
+	 * So need handle controller first.
+	 */
+	if (readl(i2c_bus->reg_base + AST2600_I2CM_ISR) & AST2600_I2CM_PKT_DONE)
+		return 0;
+
+	isr &= ~(AST2600_I2CS_ADDR_INDICATE_MASK);
+
+	if (isr & (AST2600_I2CS_ADDR1_NAK | AST2600_I2CS_ADDR2_NAK |
+		   AST2600_I2CS_ADDR3_NAK)) {
+		writel(isr & (AST2600_I2CS_ADDR1_NAK | AST2600_I2CS_ADDR2_NAK |
+			      AST2600_I2CS_ADDR3_NAK),
+		       i2c_bus->reg_base + AST2600_I2CS_ISR);
+		isr &= ~(AST2600_I2CS_ADDR1_NAK | AST2600_I2CS_ADDR2_NAK |
+			 AST2600_I2CS_ADDR3_NAK);
+	}
+
+	if (AST2600_I2CS_ADDR_MASK & isr)
+		isr &= ~AST2600_I2CS_ADDR_MASK;
+
+	if (AST2600_I2CS_PKT_DONE & isr)
+		ast2600_i2c_target_packet_buff_irq(i2c_bus, isr);
+
+	return 1;
+}
+#endif
+
 static int ast2600_i2c_setup_buff_tx(u32 cmd, struct ast2600_i2c_bus *i2c_bus)
 {
 	struct i2c_msg *msg = &i2c_bus->msgs[i2c_bus->msgs_index];
@@ -559,6 +811,20 @@ static void ast2600_i2c_controller_packet_irq(struct ast2600_i2c_bus *i2c_bus, u
 		}
 		break;
 	case AST2600_I2CM_RX_DONE:
+#if IS_ENABLED(CONFIG_I2C_SLAVE)
+		/*
+		 * Workaround for controller/target packet mode enable rx done stuck issue
+		 * When controller go for first read (RX_DONE), target mode will also effect
+		 * Then controller will send nack, not operate anymore.
+		 */
+		if (readl(i2c_bus->reg_base + AST2600_I2CS_CMD_STS) & AST2600_I2CS_PKT_MODE_EN) {
+			u32 target_cmd = readl(i2c_bus->reg_base + AST2600_I2CS_CMD_STS);
+
+			writel(0, i2c_bus->reg_base + AST2600_I2CS_CMD_STS);
+			writel(target_cmd, i2c_bus->reg_base + AST2600_I2CS_CMD_STS);
+		}
+		fallthrough;
+#endif
 	case AST2600_I2CM_RX_DONE | AST2600_I2CM_NORMAL_STOP:
 		xfer_len = AST2600_I2CC_GET_RX_BUF_LEN(readl(i2c_bus->reg_base +
 							     AST2600_I2CC_BUFF_CTRL));
@@ -666,6 +932,12 @@ static irqreturn_t ast2600_i2c_bus_irq(int irq, void *dev_id)
 {
 	struct ast2600_i2c_bus *i2c_bus = dev_id;
 
+#if IS_ENABLED(CONFIG_I2C_SLAVE)
+	if (i2c_bus->target) {
+		if (ast2600_i2c_target_irq(i2c_bus))
+			return IRQ_HANDLED;
+	}
+#endif
 	return IRQ_RETVAL(ast2600_i2c_controller_irq(i2c_bus));
 }
 
@@ -682,6 +954,21 @@ static int ast2600_i2c_controller_xfer(struct i2c_adapter *adap, struct i2c_msg
 			return ret;
 	}
 
+#if IS_ENABLED(CONFIG_I2C_SLAVE)
+	if (READ_ONCE(i2c_bus->target_active))
+		return -EBUSY;
+	/*
+	 * Controller and target share the same buffer register. A target
+	 * transaction can update buffer state asynchronously via IRQ, so block
+	 * controller transfers while target is active to avoid buffer corruption.
+	 */
+	writel(0, i2c_bus->reg_base + AST2600_I2CS_IER);
+	if (readl(i2c_bus->reg_base + AST2600_I2CS_ISR) || READ_ONCE(i2c_bus->target_active)) {
+		writel(AST2600_I2CS_PKT_DONE, i2c_bus->reg_base + AST2600_I2CS_IER);
+		return -EBUSY;
+	}
+#endif
+
 	i2c_bus->cmd_err = 0;
 	i2c_bus->msgs = msgs;
 	i2c_bus->msgs_index = 0;
@@ -689,6 +976,10 @@ static int ast2600_i2c_controller_xfer(struct i2c_adapter *adap, struct i2c_msg
 	WRITE_ONCE(i2c_bus->stop_pending, false);
 	reinit_completion(&i2c_bus->cmd_complete);
 	ret = ast2600_i2c_do_start(i2c_bus);
+#if IS_ENABLED(CONFIG_I2C_SLAVE)
+	/* avoid race condition target is wait and controller wait 1st target operate */
+	writel(AST2600_I2CS_PKT_DONE, i2c_bus->reg_base + AST2600_I2CS_IER);
+#endif
 	if (ret)
 		goto controller_out;
 	timeout = wait_for_completion_timeout(&i2c_bus->cmd_complete, i2c_bus->adap.timeout);
@@ -717,6 +1008,9 @@ static int ast2600_i2c_controller_xfer(struct i2c_adapter *adap, struct i2c_msg
 		 * if the bus is still busy.
 		 */
 		if (i2c_bus->multi_master &&
+#if IS_ENABLED(CONFIG_I2C_SLAVE)
+		    !READ_ONCE(i2c_bus->target_active) &&
+#endif
 		    (readl(i2c_bus->reg_base + AST2600_I2CC_STS_AND_BUFF) &
 		    AST2600_I2CC_BUS_BUSY_STS))
 			ast2600_i2c_recover_bus(i2c_bus);
@@ -765,8 +1059,66 @@ static int ast2600_i2c_init(struct ast2600_i2c_bus *i2c_bus)
 	/* Clear Interrupt */
 	writel(GENMASK(27, 0), i2c_bus->reg_base + AST2600_I2CM_ISR);
 
+#if IS_ENABLED(CONFIG_I2C_SLAVE)
+	writel(GENMASK(27, 0), i2c_bus->reg_base + AST2600_I2CS_ISR);
+#endif
+
+	return 0;
+}
+
+#if IS_ENABLED(CONFIG_I2C_SLAVE)
+static int ast2600_i2c_reg_target(struct i2c_client *client)
+{
+	struct ast2600_i2c_bus *i2c_bus = i2c_get_adapdata(client->adapter);
+	u32 cmd = TARGET_TRIGGER_CMD;
+
+	if (i2c_bus->target)
+		return -EINVAL;
+
+	if (client->flags & I2C_CLIENT_TEN)
+		return -EAFNOSUPPORT;
+
+	dev_dbg(i2c_bus->dev, "target addr %x\n", client->addr);
+
+	writel(0, i2c_bus->reg_base + AST2600_I2CS_ADDR_CTRL);
+
+	i2c_bus->target = client;
+
+	writel(AST2600_I2CC_SLAVE_EN | readl(i2c_bus->reg_base + AST2600_I2CC_FUN_CTRL),
+	       i2c_bus->reg_base + AST2600_I2CC_FUN_CTRL);
+
+	writel(cmd, i2c_bus->reg_base + AST2600_I2CS_CMD_STS);
+	/* Set target addr. */
+	writel(client->addr | AST2600_I2CS_ADDR1_ENABLE,
+	       i2c_bus->reg_base + AST2600_I2CS_ADDR_CTRL);
+
+	writel(AST2600_I2CS_PKT_DONE, i2c_bus->reg_base + AST2600_I2CS_IER);
+
+	return 0;
+}
+
+static int ast2600_i2c_unreg_target(struct i2c_client *client)
+{
+	struct ast2600_i2c_bus *i2c_bus = i2c_get_adapdata(client->adapter);
+	u32 val;
+
+	writel(0, i2c_bus->reg_base + AST2600_I2CS_IER);
+
+	val = readl(i2c_bus->reg_base + AST2600_I2CC_FUN_CTRL);
+	writel(val & ~AST2600_I2CC_SLAVE_EN, i2c_bus->reg_base + AST2600_I2CC_FUN_CTRL);
+	writel(0, i2c_bus->reg_base + AST2600_I2CS_ADDR_CTRL);
+
+	synchronize_irq(i2c_bus->irq);
+
+	writel(readl(i2c_bus->reg_base + AST2600_I2CS_ISR),
+	       i2c_bus->reg_base + AST2600_I2CS_ISR);
+
+	i2c_bus->target = NULL;
+	WRITE_ONCE(i2c_bus->target_active, false);
+
 	return 0;
 }
+#endif
 
 static u32 ast2600_i2c_functionality(struct i2c_adapter *adap)
 {
@@ -776,6 +1128,10 @@ static u32 ast2600_i2c_functionality(struct i2c_adapter *adap)
 static const struct i2c_algorithm i2c_ast2600_algorithm = {
 	.xfer = ast2600_i2c_controller_xfer,
 	.functionality = ast2600_i2c_functionality,
+#if IS_ENABLED(CONFIG_I2C_SLAVE)
+	.reg_target = ast2600_i2c_reg_target,
+	.unreg_target = ast2600_i2c_unreg_target,
+#endif
 };
 
 static const struct i2c_adapter_quirks ast2600_i2c_quirks = {
@@ -819,6 +1175,9 @@ static int ast2600_i2c_probe(struct platform_device *pdev)
 		regmap_write(i2c_bus->global_regs, AST2600_I2CG_CLK_DIV_CTRL, I2CCG_DIV_CTRL);
 	}
 
+#if IS_ENABLED(CONFIG_I2C_SLAVE)
+	WRITE_ONCE(i2c_bus->target_active, false);
+#endif
 	i2c_bus->dev = dev;
 	i2c_bus->multi_master = device_property_read_bool(dev, "multi-master");
 

-- 
2.34.1



^ permalink raw reply related

* [PATCH v32 4/5] i2c: ast2600: Add controller driver for AST2600 new register set
From: Ryan Chen @ 2026-06-11  5:31 UTC (permalink / raw)
  To: jk, andriy.shevchenko, Andi Shyti, Rob Herring,
	Krzysztof Kozlowski, Conor Dooley, Joel Stanley, Andrew Jeffery,
	Benjamin Herrenschmidt, Rayn Chen, Philipp Zabel
  Cc: linux-i2c, devicetree, linux-arm-kernel, linux-aspeed,
	linux-kernel, openbmc, Ryan Chen
In-Reply-To: <20260611-upstream_i2c-v32-0-b66eba921d01@aspeedtech.com>

The AST2600 introduces a new I2C controller register layout, selectable
at runtime via global control registers. Compared to the legacy layout
used on AST2400/AST2500, the new layout separates controller (master)
and target (slave) registers and adds support for packet-based transfers

The new register set extends the hardware capabilities with:

- Enhanced clock divider configuration for improved timing precision
- tCKHighMin timing control for SCL high pulse width
- Dual pool buffer mode (separate Tx/Rx buffers)
- Hardware-assisted bus recovery and timeout mechanisms

This patch adds an AST2600-specific I2C controller driver implementing
the new register layout, including support for packet-based transfers.

The legacy and new register layouts represent the same AST2600 I2C
controller IP and therefore share the existing compatible string:

  "aspeed,ast2600-i2c-bus"

To preserve DT ABI compatibility, driver selection is performed at probe
time based on DT contents. In particular, the new binding requires the
`aspeed,global-regs` phandle, which is absent from legacy DTBs:

- The new driver only probes successfully when `aspeed,global-regs` is
  present.

- The existing i2c-aspeed driver returns -ENODEV for AST2600 nodes that
  provide `aspeed,global-regs`, allowing the new driver to bind.

Signed-off-by: Ryan Chen <ryan_chen@aspeedtech.com>
---
Changes in v32:
- Add MAINTAINERS entry for drivers/i2c/busses/i2c-ast2600.c so that
  get_maintainer.pl correctly identifies the maintainer when patches
  touch this file.
- Fix interrupt storm: clear PKT_DONE in the IRQ handler when msgs is
  NULL; per the AST2600 datasheet, clearing PKT_DONE (bit 16)
  automatically clears all associated status bits [6:0], [15:13] and
  [18:17] so no extra writes to the ISR are needed.
- Fix out-of-bounds access: guard msgs_index against msgs_count before
  indexing the msgs array in ast2600_i2c_controller_packet_irq().
- Fix use-after-free: use WRITE_ONCE() to null msgs before calling
  complete() in all IRQ completion paths so trailing IRQs bail out
  immediately instead of dereferencing freed memory.
- Fix race condition in timeout path: null msgs before re-enabling IER
  so that a late IRQ cannot access the caller's freed message buffer.
- Fix 0-length SMBus block read hanging the bus: issue a standalone
  STOP via CONTROLLER_TRIGGER_LAST_STOP, set stop_pending, and poll
  for NORMAL_STOP from process context in ast2600_i2c_wait_stop().
- Initialize clk_div_reg to I2CCG_DIV_CTRL (hardware reset default)
  and global_ctrl to 0 to avoid using uninitialized values if
  regmap_read() fails.
- Guard against clock-frequency = <0> in DT; i2c_parse_fw_timings()
  does not reject an explicit zero, which would cause a divide-by-zero
  in ast2600_i2c_ac_timing_config(); default to 100 kHz in that case.
- Remove AST2600_I2CM_BUS_RECOVER_FAIL from IER writes; per the
  AST2600 datasheet, bit 15 is Reserved in I2CM10 (IER) and only
  exists as a status bit in I2CM14 (ISR).

Changes in v31:
- Reject zero-length RX in ast2600_i2c_setup_buff_rx() with -EINVAL.
  AST2600_I2CC_SET_RX_BUF_LEN() encodes length as (x - 1), so passing
  0 underflows to a 32-byte read and overruns msg->buf. Propagate the
  error from the RX_DONE continuation callsite in the controller
  packet IRQ handler so the transfer aborts cleanly instead of
  hanging until the SW timeout.
- Address Sashiko AI code review feedback:
  - Clear i2c_bus->msgs on every controller_xfer() return path and
    bail out at the head of ast2600_i2c_controller_packet_irq() when
    i2c_bus->msgs is NULL. After a transfer times out, i2c-core
    releases the msgs array, leaving i2c_bus->msgs dangling; a late
    IRQ would otherwise dereference freed memory (UAF).
  - Clamp the HW-reported xfer_len against buf_size and remaining
    msg->buf space via a new ast2600_i2c_clamp_len() helper used in
    both the TX_ACK and RX_DONE branches. A HW glitch reporting a
    larger length than expected would otherwise overrun msg->buf
    (out-of-bounds write).
  - Use regmap_update_bits() for AST2600_I2CG_CTRL during global
    initialisation. The register is shared across all i2c buses, and
    regmap_write() would clobber bootloader/other-driver-set bits
    (e.g. SLAVE_PKT_NAK, M_S_SEPARATE_INTR) and lose its TOCTOU
    protection across parallel probes.
  - SMBus block read: when the slave reports recv_len == 0, set
    controller_xfer_cnt = msg->len so the "msg done" check succeeds
    instead of issuing an extra 1-byte RX that would overwrite the
    legitimate length-byte in msg->buf[0].
  - ast2600_i2c_recover_bus() timeout path: mirror the controller
    xfer timeout sequence (disable IER, synchronize_irq(), W1C ISR,
    reset master, restore IER) so a late BUS_RECOVER IRQ cannot
    spuriously complete an unrelated subsequent transfer.

- Remove unused #include <linux/of_device.h>; all APIs used by this
  driver are provided by property.h, mfd/syscon.h, and regmap.h.
- Remove dead adap.algo_data assignment in probe(); since kernel 3.3,
  i2c_get_adapdata() reads via dev_get_drvdata() set by
  i2c_set_adapdata(), not from algo_data directly.

Changes in v30:
- Address Sashiko AI code review feedback:
  - Use manual i2c_add_adapter() / i2c_del_adapter() instead of
    devm_i2c_add_adapter() so the adapter is torn down before the
    hardware is disabled in remove(); otherwise client .remove()
    callbacks can fail or hang after FUN_CTRL/IER have been cleared.
  - synchronize_irq() and clear pending IRQ status on the controller
    timeout path to avoid the ISR racing with the next transfer and
    touching freed msgs.
  - Use clamp_t() for AC TIMING divisor / scl_low / scl_high so
    extreme clock-frequency values cannot underflow into the unsigned
    domain and corrupt the AC TIMING register.
  - Derive the RX buffer offset from buf_size instead of hardcoding
    0x10, since the dual-pool split is configurable.
  - Clamp i2c-scl-clk-low-timeout-us to the TTIMEOUT field's 5-bit
    range (max 31 * 1024us) and emit a dev_warn() instead of letting
    AST2600_I2CC_TTIMEOUT()'s mask silently truncate larger values.
- Return -EBUSY (not -ENOMEM) for every ast2600_i2c_do_start() failure
  path in the controller packet IRQ handler (NORMAL_STOP, TX_ACK, and
  RX_DONE branches).
- Advertise I2C_AQ_NO_ZERO_LEN_READ via i2c_adapter_quirks so the
  i2c-core rejects zero-byte reads before they reach the driver. The
  AST2600 packet engine cannot encode a zero-length RX command and
  would otherwise stall waiting for an RX_DONE that never arrives.

Changes in v29:
- update commit message remove transfer mode selection.
- remove dma/byte transfer, use buffer mode only.
- remove sysfs file.
- remove define I2C_TARGET_MSG_BUF_SIZE and AST2600_I2C_DMA_SIZE.
- remove buf_index in struct ast2600_i2c_bus.

Changes in v28:
- Separate xfer_mode_store into distinct parse and availability-check
  steps by introducing ast2600_i2c_xfer_mode_check()
- fix tx dma memcpy source point address.
- Use a temporary variable for devm_platform_get_and_ioremap_resource()
  to avoid storing an ERR_PTR in i2c_bus->buf_base; drop the redundant
  NULL assignment in the error path since i2c_bus is kzalloc()ed
- Add ABI documentation file
  Documentation/ABI/testing/sysfs-driver-ast2600-i2c

Changes in v27:
- remove aspeed,transfer-mode selection instead aspeed,dma-mode.
- add sysfs for xfer mode.

Changes in v25:
- Rename AST2600_I2CM_SMBUS_ALT to AST2600_I2CM_SMBUS_ALERT.
- Refactor transfer mode handling using setup_tx/setup_rx helpers.
- Rework DMA handling to use pre-allocated buffers and reduce
  mapping overhead in interrupt context.
- Fix IRQ status checks to use consistent (sts & value) style.
- Move device_property_read_bool() to probe().
- Improve probe error handling.
- Handle timeout condition in target_byte_irq().
- Rename "package" to "packet".
- Remove target reset when master wait_for_completion_timeout().
---
 MAINTAINERS                      |   1 +
 drivers/i2c/busses/Makefile      |   2 +-
 drivers/i2c/busses/i2c-aspeed.c  |   5 +
 drivers/i2c/busses/i2c-ast2600.c | 931 +++++++++++++++++++++++++++++++++++++++
 4 files changed, 938 insertions(+), 1 deletion(-)

diff --git a/MAINTAINERS b/MAINTAINERS
index f9c929e86e64..2a6748cee946 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -2600,6 +2600,7 @@ F:	Documentation/devicetree/bindings/i2c/aspeed,ast2600-i2c.yaml
 F:	Documentation/devicetree/bindings/i2c/aspeed,i2c.yaml
 F:	Documentation/devicetree/bindings/interrupt-controller/aspeed,ast2400-i2c-ic.yaml
 F:	drivers/i2c/busses/i2c-aspeed.c
+F:	drivers/i2c/busses/i2c-ast2600.c
 F:	drivers/irqchip/irq-aspeed-i2c-ic.c
 
 ARM/ASPEED MACHINE SUPPORT
diff --git a/drivers/i2c/busses/Makefile b/drivers/i2c/busses/Makefile
index 547123ab351f..ece201a67d41 100644
--- a/drivers/i2c/busses/Makefile
+++ b/drivers/i2c/busses/Makefile
@@ -37,7 +37,7 @@ obj-$(CONFIG_I2C_POWERMAC)	+= i2c-powermac.o
 obj-$(CONFIG_I2C_ALTERA)	+= i2c-altera.o
 obj-$(CONFIG_I2C_AMD_MP2)	+= i2c-amd-mp2-pci.o i2c-amd-mp2-plat.o
 obj-$(CONFIG_I2C_AMD_ASF)	+= i2c-amd-asf-plat.o
-obj-$(CONFIG_I2C_ASPEED)	+= i2c-aspeed.o
+obj-$(CONFIG_I2C_ASPEED)	+= i2c-aspeed.o i2c-ast2600.o
 obj-$(CONFIG_I2C_AT91)		+= i2c-at91.o
 i2c-at91-y			:= i2c-at91-core.o i2c-at91-master.o
 i2c-at91-$(CONFIG_I2C_AT91_SLAVE_EXPERIMENTAL)	+= i2c-at91-slave.o
diff --git a/drivers/i2c/busses/i2c-aspeed.c b/drivers/i2c/busses/i2c-aspeed.c
index f00bd779146e..c96d30b97d16 100644
--- a/drivers/i2c/busses/i2c-aspeed.c
+++ b/drivers/i2c/busses/i2c-aspeed.c
@@ -22,6 +22,7 @@
 #include <linux/of_irq.h>
 #include <linux/of_platform.h>
 #include <linux/platform_device.h>
+#include <linux/property.h>
 #include <linux/reset.h>
 #include <linux/slab.h>
 
@@ -1003,6 +1004,10 @@ static int aspeed_i2c_probe_bus(struct platform_device *pdev)
 	struct i2c_timings timings = {};
 	int irq, ret;
 
+	if (device_is_compatible(&pdev->dev, "aspeed,ast2600-i2c-bus") &&
+	    device_property_present(&pdev->dev, "aspeed,global-regs"))
+		return -ENODEV;
+
 	bus = devm_kzalloc(&pdev->dev, sizeof(*bus), GFP_KERNEL);
 	if (!bus)
 		return -ENOMEM;
diff --git a/drivers/i2c/busses/i2c-ast2600.c b/drivers/i2c/busses/i2c-ast2600.c
new file mode 100644
index 000000000000..70f37dc77468
--- /dev/null
+++ b/drivers/i2c/busses/i2c-ast2600.c
@@ -0,0 +1,931 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * ASPEED AST2600 new register set I2C controller driver
+ *
+ * Copyright (C) 2026 ASPEED Technology Inc.
+ */
+#include <linux/array_size.h>
+#include <linux/bits.h>
+#include <linux/clk.h>
+#include <linux/completion.h>
+#include <linux/delay.h>
+#include <linux/err.h>
+#include <linux/i2c.h>
+#include <linux/i2c-smbus.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/iopoll.h>
+#include <linux/minmax.h>
+#include <linux/mfd/syscon.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/property.h>
+#include <linux/regmap.h>
+#include <linux/reset.h>
+#include <linux/slab.h>
+#include <linux/unaligned.h>
+
+#define AST2600_I2CG_ISR			0x00
+#define AST2600_I2CG_SLAVE_ISR		0x04
+#define AST2600_I2CG_OWNER		0x08
+#define AST2600_I2CG_CTRL		0x0C
+#define AST2600_I2CG_CLK_DIV_CTRL	0x10
+
+#define AST2600_I2CG_SLAVE_PKT_NAK	BIT(4)
+#define AST2600_I2CG_M_S_SEPARATE_INTR	BIT(3)
+#define AST2600_I2CG_CTRL_NEW_REG	BIT(2)
+#define AST2600_I2CG_CTRL_NEW_CLK_DIV	BIT(1)
+#define AST2600_GLOBAL_INIT	\
+	(AST2600_I2CG_CTRL_NEW_REG | AST2600_I2CG_CTRL_NEW_CLK_DIV)
+/*
+ * APB clk : 100Mhz
+ * div	: scl		: baseclk [APB/((div/2) + 1)] : tBuf [1/bclk * 16]
+ * I2CG10[31:24] base clk4 for i2c auto recovery timeout counter (0xC6)
+ * I2CG10[23:16] base clk3 for Standard-mode (100Khz) min tBuf 4.7us
+ * 0x3c : 100.8Khz	: 3.225Mhz					  : 4.96us
+ * 0x3d : 99.2Khz	: 3.174Mhz					  : 5.04us
+ * 0x3e : 97.65Khz	: 3.125Mhz					  : 5.12us
+ * 0x40 : 97.75Khz	: 3.03Mhz					  : 5.28us
+ * 0x41 : 99.5Khz	: 2.98Mhz					  : 5.36us (default)
+ * I2CG10[15:8] base clk2 for Fast-mode (400Khz) min tBuf 1.3us
+ * 0x12 : 400Khz	: 10Mhz						  : 1.6us
+ * I2CG10[7:0] base clk1 for Fast-mode Plus (1Mhz) min tBuf 0.5us
+ * 0x08 : 1Mhz		: 20Mhz						  : 0.8us
+ */
+#define I2CCG_DIV_CTRL 0xC6411208
+
+/* 0x00 : I2CC Controller/Target Function Control Register  */
+#define AST2600_I2CC_FUN_CTRL		0x00
+#define AST2600_I2CC_SLAVE_ADDR_RX_EN		BIT(20)
+#define AST2600_I2CC_MASTER_RETRY_MASK		GENMASK(19, 18)
+#define AST2600_I2CC_MASTER_RETRY(x)		(((x) & GENMASK(1, 0)) << 18)
+#define AST2600_I2CC_BUS_AUTO_RELEASE		BIT(17)
+#define AST2600_I2CC_M_SDA_LOCK_EN			BIT(16)
+#define AST2600_I2CC_MULTI_MASTER_DIS		BIT(15)
+#define AST2600_I2CC_M_SCL_DRIVE_EN			BIT(14)
+#define AST2600_I2CC_MSB_STS				BIT(9)
+#define AST2600_I2CC_SDA_DRIVE_1T_EN		BIT(8)
+#define AST2600_I2CC_M_SDA_DRIVE_1T_EN		BIT(7)
+#define AST2600_I2CC_M_HIGH_SPEED_EN		BIT(6)
+/* reserver 5 : 2 */
+#define AST2600_I2CC_SLAVE_EN			BIT(1)
+#define AST2600_I2CC_MASTER_EN			BIT(0)
+
+/* 0x04 : I2CC Controller/Target Clock and AC Timing Control Register #1 */
+#define AST2600_I2CC_AC_TIMING		0x04
+#define AST2600_I2CC_TTIMEOUT(x)			(((x) & GENMASK(4, 0)) << 24)
+#define AST2600_I2CC_TCKHIGHMIN(x)			(((x) & GENMASK(3, 0)) << 20)
+#define AST2600_I2CC_TCKHIGH(x)			(((x) & GENMASK(3, 0)) << 16)
+#define AST2600_I2CC_TCKLOW(x)			(((x) & GENMASK(3, 0)) << 12)
+#define AST2600_I2CC_THDDAT(x)			(((x) & GENMASK(1, 0)) << 10)
+#define AST2600_I2CC_TOUTBASECLK(x)			(((x) & GENMASK(1, 0)) << 8)
+#define AST2600_I2CC_TBASECLK(x)			((x) & GENMASK(3, 0))
+#define AST2600_I2CC_AC_TIMING_MASK		GENMASK(23, 0)
+
+/* 0x08 : I2CC Controller/Target Transmit/Receive Byte Buffer Register */
+#define AST2600_I2CC_STS_AND_BUFF		0x08
+#define AST2600_I2CC_TX_DIR_MASK			GENMASK(31, 29)
+#define AST2600_I2CC_SDA_OE				BIT(28)
+#define AST2600_I2CC_SDA_O				BIT(27)
+#define AST2600_I2CC_SCL_OE				BIT(26)
+#define AST2600_I2CC_SCL_O				BIT(25)
+
+#define AST2600_I2CC_SCL_LINE_STS			BIT(18)
+#define AST2600_I2CC_SDA_LINE_STS			BIT(17)
+#define AST2600_I2CC_BUS_BUSY_STS			BIT(16)
+
+#define AST2600_I2CC_GET_RX_BUFF(x)			(((x) >> 8) & GENMASK(7, 0))
+
+/* 0x0C : I2CC Controller/Target Pool Buffer Control Register  */
+#define AST2600_I2CC_BUFF_CTRL		0x0C
+#define AST2600_I2CC_GET_RX_BUF_LEN(x)      (((x) & GENMASK(29, 24)) >> 24)
+#define AST2600_I2CC_SET_RX_BUF_LEN(x)		(((((x) - 1) & GENMASK(4, 0)) << 16) | BIT(0))
+#define AST2600_I2CC_SET_TX_BUF_LEN(x)		(((((x) - 1) & GENMASK(4, 0)) << 8) | BIT(0))
+#define AST2600_I2CC_GET_TX_BUF_LEN(x)      ((((x) & GENMASK(12, 8)) >> 8) + 1)
+
+/* 0x10 : I2CM Controller Interrupt Control Register */
+#define AST2600_I2CM_IER			0x10
+/* 0x14 : I2CM Controller Interrupt Status Register   : WC */
+#define AST2600_I2CM_ISR			0x14
+
+#define AST2600_I2CM_PKT_TIMEOUT			BIT(18)
+#define AST2600_I2CM_PKT_ERROR			BIT(17)
+#define AST2600_I2CM_PKT_DONE			BIT(16)
+
+#define AST2600_I2CM_BUS_RECOVER_FAIL		BIT(15)
+#define AST2600_I2CM_SDA_DL_TO			BIT(14)
+#define AST2600_I2CM_BUS_RECOVER			BIT(13)
+#define AST2600_I2CM_SMBUS_ALERT			BIT(12)
+
+#define AST2600_I2CM_SCL_LOW_TO			BIT(6)
+#define AST2600_I2CM_ABNORMAL			BIT(5)
+#define AST2600_I2CM_NORMAL_STOP			BIT(4)
+#define AST2600_I2CM_ARBIT_LOSS			BIT(3)
+#define AST2600_I2CM_RX_DONE			BIT(2)
+#define AST2600_I2CM_TX_NAK				BIT(1)
+#define AST2600_I2CM_TX_ACK				BIT(0)
+
+/* 0x18 : I2CM Controller Command/Status Register   */
+#define AST2600_I2CM_CMD_STS		0x18
+#define AST2600_I2CM_PKT_ADDR(x)			(((x) & GENMASK(6, 0)) << 24)
+#define AST2600_I2CM_PKT_EN				BIT(16)
+#define AST2600_I2CM_SDA_OE_OUT_DIR			BIT(15)
+#define AST2600_I2CM_SDA_O_OUT_DIR			BIT(14)
+#define AST2600_I2CM_SCL_OE_OUT_DIR			BIT(13)
+#define AST2600_I2CM_SCL_O_OUT_DIR			BIT(12)
+#define AST2600_I2CM_RECOVER_CMD_EN			BIT(11)
+
+#define AST2600_I2CM_RX_DMA_EN			BIT(9)
+#define AST2600_I2CM_TX_DMA_EN			BIT(8)
+/* Command Bit */
+#define AST2600_I2CM_RX_BUFF_EN			BIT(7)
+#define AST2600_I2CM_TX_BUFF_EN			BIT(6)
+#define AST2600_I2CM_STOP_CMD			BIT(5)
+#define AST2600_I2CM_RX_CMD_LAST			BIT(4)
+#define AST2600_I2CM_RX_CMD				BIT(3)
+
+#define AST2600_I2CM_TX_CMD				BIT(1)
+#define AST2600_I2CM_START_CMD			BIT(0)
+
+/* 0x1C : I2CM Controller DMA Transfer Length Register	 */
+#define AST2600_I2CM_DMA_LEN		0x1C
+/* Tx Rx support length 1 ~ 4096 */
+#define AST2600_I2CM_SET_RX_DMA_LEN(x)	((((x) & GENMASK(11, 0)) << 16) | BIT(31))
+#define AST2600_I2CM_SET_TX_DMA_LEN(x)	(((x) & GENMASK(11, 0)) | BIT(15))
+
+/* 0x20 : I2CS Target Interrupt Control Register   */
+#define AST2600_I2CS_IER			0x20
+/* 0x24 : I2CS Target Interrupt Status Register	 */
+#define AST2600_I2CS_ISR			0x24
+
+#define AST2600_I2CS_ADDR_INDICATE_MASK	GENMASK(31, 30)
+#define AST2600_I2CS_SLAVE_PENDING			BIT(29)
+
+#define AST2600_I2CS_WAIT_TX_DMA			BIT(25)
+#define AST2600_I2CS_WAIT_RX_DMA			BIT(24)
+
+#define AST2600_I2CS_ADDR3_NAK			BIT(22)
+#define AST2600_I2CS_ADDR2_NAK			BIT(21)
+#define AST2600_I2CS_ADDR1_NAK			BIT(20)
+
+#define AST2600_I2CS_ADDR_MASK			GENMASK(19, 18)
+#define AST2600_I2CS_PKT_ERROR			BIT(17)
+#define AST2600_I2CS_PKT_DONE			BIT(16)
+#define AST2600_I2CS_INACTIVE_TO			BIT(15)
+
+#define AST2600_I2CS_SLAVE_MATCH			BIT(7)
+#define AST2600_I2CS_ABNOR_STOP			BIT(5)
+#define AST2600_I2CS_STOP				BIT(4)
+#define AST2600_I2CS_RX_DONE_NAK			BIT(3)
+#define AST2600_I2CS_RX_DONE			BIT(2)
+#define AST2600_I2CS_TX_NAK				BIT(1)
+#define AST2600_I2CS_TX_ACK				BIT(0)
+
+/* 0x28 : I2CS Target CMD/Status Register   */
+#define AST2600_I2CS_CMD_STS		0x28
+#define AST2600_I2CS_ACTIVE_ALL			GENMASK(18, 17)
+#define AST2600_I2CS_PKT_MODE_EN			BIT(16)
+#define AST2600_I2CS_AUTO_NAK_NOADDR		BIT(15)
+#define AST2600_I2CS_AUTO_NAK_EN			BIT(14)
+
+#define AST2600_I2CS_ALT_EN				BIT(10)
+#define AST2600_I2CS_RX_DMA_EN			BIT(9)
+#define AST2600_I2CS_TX_DMA_EN			BIT(8)
+#define AST2600_I2CS_RX_BUFF_EN			BIT(7)
+#define AST2600_I2CS_TX_BUFF_EN			BIT(6)
+#define AST2600_I2CS_RX_CMD_LAST			BIT(4)
+
+#define AST2600_I2CS_TX_CMD				BIT(2)
+
+#define AST2600_I2CS_DMA_LEN		0x2C
+#define AST2600_I2CS_SET_RX_DMA_LEN(x)	(((((x) - 1) & GENMASK(11, 0)) << 16) | BIT(31))
+#define AST2600_I2CS_SET_TX_DMA_LEN(x)	((((x) - 1) & GENMASK(11, 0)) | BIT(15))
+
+/* I2CM Controller DMA Tx Buffer Register   */
+#define AST2600_I2CM_TX_DMA			0x30
+/* I2CM Controller DMA Rx Buffer Register	*/
+#define AST2600_I2CM_RX_DMA			0x34
+/* I2CS Target DMA Tx Buffer Register   */
+#define AST2600_I2CS_TX_DMA			0x38
+/* I2CS Target DMA Rx Buffer Register   */
+#define AST2600_I2CS_RX_DMA			0x3C
+
+#define AST2600_I2CS_ADDR_CTRL		0x40
+
+#define	AST2600_I2CS_ADDR3_MASK		GENMASK(22, 16)
+#define	AST2600_I2CS_ADDR2_MASK		GENMASK(14, 8)
+#define	AST2600_I2CS_ADDR1_MASK		GENMASK(6, 0)
+
+#define AST2600_I2CM_DMA_LEN_STS		0x48
+#define AST2600_I2CS_DMA_LEN_STS		0x4C
+
+#define AST2600_I2C_GET_TX_DMA_LEN(x)		((x) & GENMASK(12, 0))
+#define AST2600_I2C_GET_RX_DMA_LEN(x)        (((x) & GENMASK(28, 16)) >> 16)
+
+/* 0x40 : Target Device Address Register */
+#define AST2600_I2CS_ADDR3_ENABLE			BIT(23)
+#define AST2600_I2CS_ADDR3(x)			((x) << 16)
+#define AST2600_I2CS_ADDR2_ENABLE			BIT(15)
+#define AST2600_I2CS_ADDR2(x)			((x) << 8)
+#define AST2600_I2CS_ADDR1_ENABLE			BIT(7)
+#define AST2600_I2CS_ADDR1(x)			(x)
+
+#define CONTROLLER_TRIGGER_LAST_STOP	(AST2600_I2CM_RX_CMD_LAST | AST2600_I2CM_STOP_CMD)
+#define TARGET_TRIGGER_CMD	(AST2600_I2CS_ACTIVE_ALL | AST2600_I2CS_PKT_MODE_EN)
+
+#define AST_I2C_TIMEOUT_CLK		0x1
+
+struct ast2600_i2c_bus {
+	struct i2c_adapter	adap;
+	struct device		*dev;
+	void __iomem		*reg_base;
+	struct regmap		*global_regs;
+	struct clk		*clk;
+	struct i2c_timings	timing_info;
+	struct completion	cmd_complete;
+	struct i2c_msg		*msgs;
+	u32			apb_clk;
+	u32			timeout;
+	int			irq;
+	int			cmd_err;
+	int			msgs_index;
+	int			msgs_count;
+	int			controller_xfer_cnt;
+	size_t			buf_size;
+	bool			multi_master;
+	bool			stop_pending;
+	void __iomem		*buf_base;
+};
+
+static void ast2600_i2c_ac_timing_config(struct ast2600_i2c_bus *i2c_bus)
+{
+	unsigned long base_clk[16];
+	int baseclk_idx = 0;
+	int divisor = 0;
+	u32 clk_div_reg = I2CCG_DIV_CTRL;
+	u32 scl_low;
+	u32 scl_high;
+	u32 data;
+
+	regmap_read(i2c_bus->global_regs, AST2600_I2CG_CLK_DIV_CTRL, &clk_div_reg);
+
+	for (int i = 0; i < ARRAY_SIZE(base_clk); i++) {
+		if (i == 0)
+			base_clk[i] = i2c_bus->apb_clk;
+		else if (i < 5)
+			base_clk[i] = (i2c_bus->apb_clk * 2) /
+			   (((clk_div_reg >> ((i - 1) * 8)) & GENMASK(7, 0)) + 2);
+		else
+			base_clk[i] = base_clk[4] >> (i - 4);
+
+		if ((base_clk[i] / i2c_bus->timing_info.bus_freq_hz) <= 32) {
+			baseclk_idx = i;
+			divisor = DIV_ROUND_UP(base_clk[i], i2c_bus->timing_info.bus_freq_hz);
+			break;
+		}
+	}
+	baseclk_idx = clamp_t(int, baseclk_idx, 0, 15);
+	divisor = clamp_t(int, divisor, 2, 32);
+	scl_low = clamp_t(int, divisor * 9 / 16 - 1, 0, 15);
+	scl_high = clamp_t(int, divisor - scl_low - 2, 1, 15);
+	data = (scl_high - 1) << 20 | scl_high << 16 | scl_low << 12 | baseclk_idx;
+	if (i2c_bus->timeout) {
+		data |= AST2600_I2CC_TOUTBASECLK(AST_I2C_TIMEOUT_CLK);
+		data |= AST2600_I2CC_TTIMEOUT(i2c_bus->timeout);
+	}
+
+	writel(data, i2c_bus->reg_base + AST2600_I2CC_AC_TIMING);
+}
+
+static int ast2600_i2c_recover_bus(struct ast2600_i2c_bus *i2c_bus)
+{
+	u32 state = readl(i2c_bus->reg_base + AST2600_I2CC_STS_AND_BUFF);
+	int ret = 0;
+	u32 ctrl;
+	int r;
+
+	dev_dbg(i2c_bus->dev, "%d-bus recovery bus [%x]\n", i2c_bus->adap.nr, state);
+
+	/* reset controller */
+	ctrl = readl(i2c_bus->reg_base + AST2600_I2CC_FUN_CTRL);
+	writel(ctrl & ~AST2600_I2CC_MASTER_EN, i2c_bus->reg_base + AST2600_I2CC_FUN_CTRL);
+	writel(ctrl, i2c_bus->reg_base + AST2600_I2CC_FUN_CTRL);
+
+	reinit_completion(&i2c_bus->cmd_complete);
+	i2c_bus->cmd_err = 0;
+
+	/* Check SDA/SCL status in the status register. */
+	state = readl(i2c_bus->reg_base + AST2600_I2CC_STS_AND_BUFF);
+	if (!(state & AST2600_I2CC_SDA_LINE_STS) && (state & AST2600_I2CC_SCL_LINE_STS)) {
+		writel(AST2600_I2CM_RECOVER_CMD_EN, i2c_bus->reg_base + AST2600_I2CM_CMD_STS);
+		r = wait_for_completion_timeout(&i2c_bus->cmd_complete, i2c_bus->adap.timeout);
+		if (r == 0) {
+			dev_dbg(i2c_bus->dev, "recovery timed out\n");
+			writel(0, i2c_bus->reg_base + AST2600_I2CM_IER);
+			synchronize_irq(i2c_bus->irq);
+			writel(readl(i2c_bus->reg_base + AST2600_I2CM_ISR),
+			       i2c_bus->reg_base + AST2600_I2CM_ISR);
+			ctrl = readl(i2c_bus->reg_base + AST2600_I2CC_FUN_CTRL);
+			writel(ctrl & ~AST2600_I2CC_MASTER_EN,
+			       i2c_bus->reg_base + AST2600_I2CC_FUN_CTRL);
+			writel(ctrl, i2c_bus->reg_base + AST2600_I2CC_FUN_CTRL);
+			writel(AST2600_I2CM_PKT_DONE | AST2600_I2CM_BUS_RECOVER,
+			       i2c_bus->reg_base + AST2600_I2CM_IER);
+			return -ETIMEDOUT;
+		} else if (i2c_bus->cmd_err) {
+			dev_dbg(i2c_bus->dev, "recovery error\n");
+			ret = -EPROTO;
+		}
+	}
+
+	/* Recovery done */
+	state = readl(i2c_bus->reg_base + AST2600_I2CC_STS_AND_BUFF);
+	if (state & AST2600_I2CC_BUS_BUSY_STS) {
+		dev_dbg(i2c_bus->dev, "Can't recover bus [%x]\n", state);
+		ret = -EPROTO;
+	}
+
+	return ret;
+}
+
+static int ast2600_i2c_setup_buff_tx(u32 cmd, struct ast2600_i2c_bus *i2c_bus)
+{
+	struct i2c_msg *msg = &i2c_bus->msgs[i2c_bus->msgs_index];
+	int xfer_len = msg->len - i2c_bus->controller_xfer_cnt;
+	u32 wbuf_dword;
+	int i;
+
+	cmd |= AST2600_I2CM_PKT_EN;
+
+	if (xfer_len > i2c_bus->buf_size)
+		xfer_len = i2c_bus->buf_size;
+	else if (i2c_bus->msgs_index + 1 == i2c_bus->msgs_count)
+		cmd |= AST2600_I2CM_STOP_CMD;
+
+	if (cmd & AST2600_I2CM_START_CMD)
+		cmd |= AST2600_I2CM_PKT_ADDR(msg->addr);
+
+	if (xfer_len) {
+		cmd |= AST2600_I2CM_TX_BUFF_EN | AST2600_I2CM_TX_CMD;
+		/*
+		 * The controller's buffer register supports dword writes only.
+		 * Therefore, write dwords to the buffer register in a 4-byte aligned,
+		 * and write the remaining unaligned data at the end.
+		 */
+		for (i = 0; i < xfer_len; i += 4) {
+			int xfer_cnt = i2c_bus->controller_xfer_cnt + i;
+
+			switch (min(xfer_len - i, 4) % 4) {
+			case 1:
+				wbuf_dword = msg->buf[xfer_cnt];
+				break;
+			case 2:
+				wbuf_dword = get_unaligned_le16(&msg->buf[xfer_cnt]);
+				break;
+			case 3:
+				wbuf_dword = get_unaligned_le24(&msg->buf[xfer_cnt]);
+				break;
+			default:
+				wbuf_dword = get_unaligned_le32(&msg->buf[xfer_cnt]);
+				break;
+			}
+			writel(wbuf_dword, i2c_bus->buf_base + i);
+		}
+		writel(AST2600_I2CC_SET_TX_BUF_LEN(xfer_len),
+		       i2c_bus->reg_base + AST2600_I2CC_BUFF_CTRL);
+	}
+
+	writel(cmd, i2c_bus->reg_base + AST2600_I2CM_CMD_STS);
+
+	return 0;
+}
+
+static int ast2600_i2c_setup_buff_rx(u32 cmd, struct ast2600_i2c_bus *i2c_bus)
+{
+	struct i2c_msg *msg = &i2c_bus->msgs[i2c_bus->msgs_index];
+	int xfer_len = msg->len - i2c_bus->controller_xfer_cnt;
+
+	cmd |= AST2600_I2CM_PKT_EN | AST2600_I2CM_RX_BUFF_EN | AST2600_I2CM_RX_CMD;
+
+	if (cmd & AST2600_I2CM_START_CMD)
+		cmd |= AST2600_I2CM_PKT_ADDR(msg->addr);
+
+	if (msg->flags & I2C_M_RECV_LEN) {
+		dev_dbg(i2c_bus->dev, "smbus read\n");
+		xfer_len = 1;
+	} else if (xfer_len > i2c_bus->buf_size) {
+		xfer_len = i2c_bus->buf_size;
+	} else if (i2c_bus->msgs_index + 1 == i2c_bus->msgs_count) {
+		cmd |= CONTROLLER_TRIGGER_LAST_STOP;
+	}
+
+	if (xfer_len <= 0)
+		return -EINVAL;
+
+	writel(AST2600_I2CC_SET_RX_BUF_LEN(xfer_len), i2c_bus->reg_base + AST2600_I2CC_BUFF_CTRL);
+
+	writel(cmd, i2c_bus->reg_base + AST2600_I2CM_CMD_STS);
+
+	return 0;
+}
+
+static int ast2600_i2c_do_start(struct ast2600_i2c_bus *i2c_bus)
+{
+	struct i2c_msg *msg = &i2c_bus->msgs[i2c_bus->msgs_index];
+
+	/* send start */
+	dev_dbg(i2c_bus->dev, "[%d] %s %d byte%s %s 0x%02x\n",
+		i2c_bus->msgs_index, str_read_write(msg->flags & I2C_M_RD),
+		msg->len, str_plural(msg->len),
+		msg->flags & I2C_M_RD ? "from" : "to", msg->addr);
+
+	i2c_bus->controller_xfer_cnt = 0;
+
+	if (msg->flags & I2C_M_RD)
+		return ast2600_i2c_setup_buff_rx(AST2600_I2CM_START_CMD, i2c_bus);
+
+	return ast2600_i2c_setup_buff_tx(AST2600_I2CM_START_CMD, i2c_bus);
+}
+
+static int ast2600_i2c_irq_err_to_errno(u32 irq_status)
+{
+	if (irq_status & AST2600_I2CM_ARBIT_LOSS)
+		return -EAGAIN;
+	if (irq_status & (AST2600_I2CM_SDA_DL_TO | AST2600_I2CM_SCL_LOW_TO))
+		return -ETIMEDOUT;
+	if (irq_status & (AST2600_I2CM_ABNORMAL))
+		return -EPROTO;
+
+	return 0;
+}
+
+static int ast2600_i2c_clamp_len(struct ast2600_i2c_bus *i2c_bus,
+				 struct i2c_msg *msg, int len)
+{
+	int remaining = msg->len - i2c_bus->controller_xfer_cnt;
+
+	if (len > i2c_bus->buf_size)
+		len = i2c_bus->buf_size;
+	if (remaining < 0)
+		remaining = 0;
+	if (len > remaining)
+		len = remaining;
+	return len;
+}
+
+static int ast2600_i2c_wait_stop(struct ast2600_i2c_bus *i2c_bus)
+{
+	u32 sts;
+	int ret;
+
+	ret = readl_poll_timeout(i2c_bus->reg_base + AST2600_I2CM_ISR, sts,
+				 sts & AST2600_I2CM_NORMAL_STOP, 1000, 10000);
+	if (ret)
+		return ret;
+
+	writel(AST2600_I2CM_NORMAL_STOP, i2c_bus->reg_base + AST2600_I2CM_ISR);
+
+	return 0;
+}
+
+static void ast2600_i2c_controller_packet_irq(struct ast2600_i2c_bus *i2c_bus, u32 sts)
+{
+	struct i2c_msg *msg;
+	int xfer_len;
+	int i;
+
+	if (!i2c_bus->msgs) {
+		writel(AST2600_I2CM_PKT_DONE, i2c_bus->reg_base + AST2600_I2CM_ISR);
+		return;
+	}
+	if (i2c_bus->msgs_index >= i2c_bus->msgs_count) {
+		writel(AST2600_I2CM_PKT_DONE, i2c_bus->reg_base + AST2600_I2CM_ISR);
+		return;
+	}
+	msg = &i2c_bus->msgs[i2c_bus->msgs_index];
+
+	sts &= ~AST2600_I2CM_PKT_DONE;
+	writel(AST2600_I2CM_PKT_DONE, i2c_bus->reg_base + AST2600_I2CM_ISR);
+	switch (sts) {
+	case AST2600_I2CM_PKT_ERROR:
+		WRITE_ONCE(i2c_bus->msgs, NULL);
+		i2c_bus->cmd_err = -EAGAIN;
+		complete(&i2c_bus->cmd_complete);
+		break;
+	case AST2600_I2CM_PKT_ERROR | AST2600_I2CM_TX_NAK: /* a0 fix for issue */
+		fallthrough;
+	case AST2600_I2CM_PKT_ERROR | AST2600_I2CM_TX_NAK | AST2600_I2CM_NORMAL_STOP:
+		WRITE_ONCE(i2c_bus->msgs, NULL);
+		i2c_bus->cmd_err = -ENXIO;
+		complete(&i2c_bus->cmd_complete);
+		break;
+	case AST2600_I2CM_NORMAL_STOP:
+		/* write 0 byte only have stop isr */
+		i2c_bus->msgs_index++;
+		if (i2c_bus->msgs_index < i2c_bus->msgs_count) {
+			if (ast2600_i2c_do_start(i2c_bus)) {
+				WRITE_ONCE(i2c_bus->msgs, NULL);
+				i2c_bus->cmd_err = -EBUSY;
+				complete(&i2c_bus->cmd_complete);
+			}
+		} else {
+			WRITE_ONCE(i2c_bus->msgs, NULL);
+			i2c_bus->cmd_err = i2c_bus->msgs_index;
+			complete(&i2c_bus->cmd_complete);
+		}
+		break;
+	case AST2600_I2CM_TX_ACK:
+	case AST2600_I2CM_TX_ACK | AST2600_I2CM_NORMAL_STOP:
+		xfer_len = AST2600_I2CC_GET_TX_BUF_LEN(readl(i2c_bus->reg_base +
+						       AST2600_I2CC_BUFF_CTRL));
+		xfer_len = ast2600_i2c_clamp_len(i2c_bus, msg, xfer_len);
+		i2c_bus->controller_xfer_cnt += xfer_len;
+
+		if (i2c_bus->controller_xfer_cnt == msg->len) {
+			i2c_bus->msgs_index++;
+			if (i2c_bus->msgs_index == i2c_bus->msgs_count) {
+				WRITE_ONCE(i2c_bus->msgs, NULL);
+				i2c_bus->cmd_err = i2c_bus->msgs_index;
+				complete(&i2c_bus->cmd_complete);
+			} else {
+				if (ast2600_i2c_do_start(i2c_bus)) {
+					WRITE_ONCE(i2c_bus->msgs, NULL);
+					i2c_bus->cmd_err = -EBUSY;
+					complete(&i2c_bus->cmd_complete);
+				}
+			}
+		} else {
+			ast2600_i2c_setup_buff_tx(0, i2c_bus);
+		}
+		break;
+	case AST2600_I2CM_RX_DONE:
+	case AST2600_I2CM_RX_DONE | AST2600_I2CM_NORMAL_STOP:
+		xfer_len = AST2600_I2CC_GET_RX_BUF_LEN(readl(i2c_bus->reg_base +
+							     AST2600_I2CC_BUFF_CTRL));
+		xfer_len = ast2600_i2c_clamp_len(i2c_bus, msg, xfer_len);
+		for (i = 0; i < xfer_len; i++)
+			msg->buf[i2c_bus->controller_xfer_cnt + i] =
+				readb(i2c_bus->buf_base + i2c_bus->buf_size + i);
+
+		if (msg->flags & I2C_M_RECV_LEN) {
+			u8 recv_len = AST2600_I2CC_GET_RX_BUFF(readl(i2c_bus->reg_base
+					       + AST2600_I2CC_STS_AND_BUFF));
+
+			msg->len = min_t(unsigned int, recv_len, I2C_SMBUS_BLOCK_MAX);
+			msg->len += ((msg->flags & I2C_CLIENT_PEC) ? 2 : 1);
+			msg->flags &= ~I2C_M_RECV_LEN;
+			if (!recv_len) {
+				/*
+				 * Workaround: a standalone STOP triggers NORMAL_STOP in
+				 * ISR without generating another IRQ, so poll for it from
+				 * process context.
+				 */
+				i2c_bus->controller_xfer_cnt = msg->len;
+				WRITE_ONCE(i2c_bus->stop_pending, true);
+				writel(CONTROLLER_TRIGGER_LAST_STOP,
+				       i2c_bus->reg_base + AST2600_I2CM_CMD_STS);
+
+				WRITE_ONCE(i2c_bus->msgs, NULL);
+				i2c_bus->cmd_err = -EPROTO;
+				complete(&i2c_bus->cmd_complete);
+				break;
+			}
+			i2c_bus->controller_xfer_cnt = 1;
+		} else {
+			i2c_bus->controller_xfer_cnt += xfer_len;
+		}
+
+		if (i2c_bus->controller_xfer_cnt == msg->len) {
+			i2c_bus->msgs_index++;
+			if (i2c_bus->msgs_index == i2c_bus->msgs_count) {
+				WRITE_ONCE(i2c_bus->msgs, NULL);
+				i2c_bus->cmd_err = i2c_bus->msgs_index;
+				complete(&i2c_bus->cmd_complete);
+			} else {
+				if (ast2600_i2c_do_start(i2c_bus)) {
+					WRITE_ONCE(i2c_bus->msgs, NULL);
+					i2c_bus->cmd_err = -EBUSY;
+					complete(&i2c_bus->cmd_complete);
+				}
+			}
+		} else if (ast2600_i2c_setup_buff_rx(0, i2c_bus)) {
+			WRITE_ONCE(i2c_bus->msgs, NULL);
+			i2c_bus->cmd_err = -EINVAL;
+			complete(&i2c_bus->cmd_complete);
+		}
+		break;
+	default:
+		dev_dbg(i2c_bus->dev, "unhandled sts %x\n", sts);
+		break;
+	}
+}
+
+static int ast2600_i2c_controller_irq(struct ast2600_i2c_bus *i2c_bus)
+{
+	u32 sts = readl(i2c_bus->reg_base + AST2600_I2CM_ISR);
+	u32 ctrl;
+
+	sts &= ~AST2600_I2CM_SMBUS_ALERT;
+
+	if (sts & AST2600_I2CM_BUS_RECOVER_FAIL) {
+		writel(AST2600_I2CM_BUS_RECOVER_FAIL, i2c_bus->reg_base + AST2600_I2CM_ISR);
+		ctrl = readl(i2c_bus->reg_base + AST2600_I2CC_FUN_CTRL);
+		writel(0, i2c_bus->reg_base + AST2600_I2CC_FUN_CTRL);
+		writel(ctrl, i2c_bus->reg_base + AST2600_I2CC_FUN_CTRL);
+		WRITE_ONCE(i2c_bus->msgs, NULL);
+		i2c_bus->cmd_err = -EPROTO;
+		complete(&i2c_bus->cmd_complete);
+		return 1;
+	}
+
+	if (sts & AST2600_I2CM_BUS_RECOVER) {
+		writel(AST2600_I2CM_BUS_RECOVER, i2c_bus->reg_base + AST2600_I2CM_ISR);
+		WRITE_ONCE(i2c_bus->msgs, NULL);
+		i2c_bus->cmd_err = 0;
+		complete(&i2c_bus->cmd_complete);
+		return 1;
+	}
+
+	i2c_bus->cmd_err = ast2600_i2c_irq_err_to_errno(sts);
+	if (i2c_bus->cmd_err) {
+		WRITE_ONCE(i2c_bus->msgs, NULL);
+		writel(AST2600_I2CM_PKT_DONE, i2c_bus->reg_base + AST2600_I2CM_ISR);
+		complete(&i2c_bus->cmd_complete);
+		return 1;
+	}
+
+	if (sts & AST2600_I2CM_PKT_DONE) {
+		ast2600_i2c_controller_packet_irq(i2c_bus, sts);
+		return 1;
+	}
+
+	return 0;
+}
+
+static irqreturn_t ast2600_i2c_bus_irq(int irq, void *dev_id)
+{
+	struct ast2600_i2c_bus *i2c_bus = dev_id;
+
+	return IRQ_RETVAL(ast2600_i2c_controller_irq(i2c_bus));
+}
+
+static int ast2600_i2c_controller_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num)
+{
+	struct ast2600_i2c_bus *i2c_bus = i2c_get_adapdata(adap);
+	unsigned long timeout;
+	int ret;
+
+	if (!i2c_bus->multi_master &&
+	    (readl(i2c_bus->reg_base + AST2600_I2CC_STS_AND_BUFF) & AST2600_I2CC_BUS_BUSY_STS)) {
+		ret = ast2600_i2c_recover_bus(i2c_bus);
+		if (ret)
+			return ret;
+	}
+
+	i2c_bus->cmd_err = 0;
+	i2c_bus->msgs = msgs;
+	i2c_bus->msgs_index = 0;
+	i2c_bus->msgs_count = num;
+	WRITE_ONCE(i2c_bus->stop_pending, false);
+	reinit_completion(&i2c_bus->cmd_complete);
+	ret = ast2600_i2c_do_start(i2c_bus);
+	if (ret)
+		goto controller_out;
+	timeout = wait_for_completion_timeout(&i2c_bus->cmd_complete, i2c_bus->adap.timeout);
+	if (timeout == 0) {
+		u32 ctrl = readl(i2c_bus->reg_base + AST2600_I2CC_FUN_CTRL);
+
+		dev_dbg(i2c_bus->dev, "timeout isr[%x], sts[%x]\n",
+			readl(i2c_bus->reg_base + AST2600_I2CM_ISR),
+			readl(i2c_bus->reg_base + AST2600_I2CC_STS_AND_BUFF));
+
+		writel(0, i2c_bus->reg_base + AST2600_I2CM_IER);
+		synchronize_irq(i2c_bus->irq);
+		writel(readl(i2c_bus->reg_base + AST2600_I2CM_ISR),
+		       i2c_bus->reg_base + AST2600_I2CM_ISR);
+
+		writel(ctrl & ~AST2600_I2CC_MASTER_EN, i2c_bus->reg_base + AST2600_I2CC_FUN_CTRL);
+		writel(ctrl, i2c_bus->reg_base + AST2600_I2CC_FUN_CTRL);
+
+		WRITE_ONCE(i2c_bus->msgs, NULL);
+		writel(AST2600_I2CM_PKT_DONE | AST2600_I2CM_BUS_RECOVER,
+		       i2c_bus->reg_base + AST2600_I2CM_IER);
+
+		/*
+		 * A slave holding SCL low can stall the transfer and trigger
+		 * a master timeout. In multi-master mode, attempt bus recovery
+		 * if the bus is still busy.
+		 */
+		if (i2c_bus->multi_master &&
+		    (readl(i2c_bus->reg_base + AST2600_I2CC_STS_AND_BUFF) &
+		    AST2600_I2CC_BUS_BUSY_STS))
+			ast2600_i2c_recover_bus(i2c_bus);
+		ret = -ETIMEDOUT;
+	} else {
+		ret = i2c_bus->cmd_err;
+	}
+
+	if (READ_ONCE(i2c_bus->stop_pending)) {
+		int stop_ret;
+
+		stop_ret = ast2600_i2c_wait_stop(i2c_bus);
+		WRITE_ONCE(i2c_bus->stop_pending, false);
+		WRITE_ONCE(i2c_bus->msgs, NULL);
+		if (stop_ret) {
+			i2c_bus->cmd_err = stop_ret;
+			ret = stop_ret;
+		}
+	}
+
+	dev_dbg(i2c_bus->dev, "bus%d-m: %d end\n", i2c_bus->adap.nr, i2c_bus->cmd_err);
+
+controller_out:
+	i2c_bus->msgs = NULL;
+	return ret;
+}
+
+static int ast2600_i2c_init(struct ast2600_i2c_bus *i2c_bus)
+{
+	u32 fun_ctrl = AST2600_I2CC_BUS_AUTO_RELEASE | AST2600_I2CC_MASTER_EN;
+
+	/* I2C Reset */
+	writel(0, i2c_bus->reg_base + AST2600_I2CC_FUN_CTRL);
+
+	if (!i2c_bus->multi_master)
+		fun_ctrl |= AST2600_I2CC_MULTI_MASTER_DIS;
+
+	/* Enable Controller Mode */
+	writel(fun_ctrl, i2c_bus->reg_base + AST2600_I2CC_FUN_CTRL);
+	/* disable target address */
+	writel(0, i2c_bus->reg_base + AST2600_I2CS_ADDR_CTRL);
+
+	/* Set AC Timing */
+	ast2600_i2c_ac_timing_config(i2c_bus);
+
+	/* Clear Interrupt */
+	writel(GENMASK(27, 0), i2c_bus->reg_base + AST2600_I2CM_ISR);
+
+	return 0;
+}
+
+static u32 ast2600_i2c_functionality(struct i2c_adapter *adap)
+{
+	return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL | I2C_FUNC_SMBUS_BLOCK_DATA;
+}
+
+static const struct i2c_algorithm i2c_ast2600_algorithm = {
+	.xfer = ast2600_i2c_controller_xfer,
+	.functionality = ast2600_i2c_functionality,
+};
+
+static const struct i2c_adapter_quirks ast2600_i2c_quirks = {
+	.flags = I2C_AQ_NO_ZERO_LEN_READ,
+};
+
+static int ast2600_i2c_probe(struct platform_device *pdev)
+{
+	struct device *dev = &pdev->dev;
+	struct ast2600_i2c_bus *i2c_bus;
+	void __iomem *buf_base;
+	struct reset_control *rst;
+	struct resource *res;
+	u32 global_ctrl = 0;
+	int ret;
+
+	if (!device_property_present(dev, "aspeed,global-regs"))
+		return -ENODEV;
+
+	i2c_bus = devm_kzalloc(dev, sizeof(*i2c_bus), GFP_KERNEL);
+	if (!i2c_bus)
+		return -ENOMEM;
+
+	i2c_bus->reg_base = devm_platform_ioremap_resource(pdev, 0);
+	if (IS_ERR(i2c_bus->reg_base))
+		return PTR_ERR(i2c_bus->reg_base);
+
+	rst = devm_reset_control_get_shared_deasserted(dev, NULL);
+	if (IS_ERR(rst))
+		return dev_err_probe(dev, PTR_ERR(rst), "Missing reset ctrl\n");
+
+	i2c_bus->global_regs =
+		syscon_regmap_lookup_by_phandle(dev_of_node(dev), "aspeed,global-regs");
+	if (IS_ERR(i2c_bus->global_regs))
+		return PTR_ERR(i2c_bus->global_regs);
+
+	regmap_read(i2c_bus->global_regs, AST2600_I2CG_CTRL, &global_ctrl);
+	if ((global_ctrl & AST2600_GLOBAL_INIT) != AST2600_GLOBAL_INIT) {
+		regmap_update_bits(i2c_bus->global_regs, AST2600_I2CG_CTRL,
+				   AST2600_GLOBAL_INIT, AST2600_GLOBAL_INIT);
+		regmap_write(i2c_bus->global_regs, AST2600_I2CG_CLK_DIV_CTRL, I2CCG_DIV_CTRL);
+	}
+
+	i2c_bus->dev = dev;
+	i2c_bus->multi_master = device_property_read_bool(dev, "multi-master");
+
+	buf_base = devm_platform_get_and_ioremap_resource(pdev, 1, &res);
+	if (IS_ERR(buf_base))
+		return dev_err_probe(dev, PTR_ERR(buf_base), "Missing buffer resource\n");
+	i2c_bus->buf_base = buf_base;
+	i2c_bus->buf_size = resource_size(res) / 2;
+
+	/*
+	 * i2c timeout counter: use base clk4 1Mhz,
+	 * per unit: 1/(1000/1024) = 1024us
+	 */
+	ret = device_property_read_u32(dev, "i2c-scl-clk-low-timeout-us", &i2c_bus->timeout);
+	if (!ret) {
+		i2c_bus->timeout = DIV_ROUND_UP(i2c_bus->timeout, 1024);
+		if (i2c_bus->timeout > GENMASK(4, 0)) {
+			dev_warn(dev,
+				 "i2c-scl-clk-low-timeout-us exceeds HW max (31 * 1024us), clamped\n");
+			i2c_bus->timeout = GENMASK(4, 0);
+		}
+	}
+
+	init_completion(&i2c_bus->cmd_complete);
+
+	i2c_bus->irq = platform_get_irq(pdev, 0);
+	if (i2c_bus->irq < 0)
+		return i2c_bus->irq;
+
+	platform_set_drvdata(pdev, i2c_bus);
+
+	i2c_bus->clk = devm_clk_get(i2c_bus->dev, NULL);
+	if (IS_ERR(i2c_bus->clk))
+		return dev_err_probe(i2c_bus->dev, PTR_ERR(i2c_bus->clk), "Can't get clock\n");
+
+	i2c_bus->apb_clk = clk_get_rate(i2c_bus->clk);
+
+	i2c_parse_fw_timings(i2c_bus->dev, &i2c_bus->timing_info, true);
+	if (!i2c_bus->timing_info.bus_freq_hz) {
+		dev_warn(dev, "invalid clock-frequency 0, using default 100kHz\n");
+		i2c_bus->timing_info.bus_freq_hz = I2C_MAX_STANDARD_MODE_FREQ;
+	}
+
+	/* Initialize the I2C adapter */
+	i2c_bus->adap.owner = THIS_MODULE;
+	i2c_bus->adap.algo = &i2c_ast2600_algorithm;
+	i2c_bus->adap.quirks = &ast2600_i2c_quirks;
+	i2c_bus->adap.retries = 0;
+	i2c_bus->adap.dev.parent = i2c_bus->dev;
+	device_set_node(&i2c_bus->adap.dev, dev_fwnode(dev));
+	strscpy(i2c_bus->adap.name, pdev->name);
+	i2c_set_adapdata(&i2c_bus->adap, i2c_bus);
+
+	ret = ast2600_i2c_init(i2c_bus);
+	if (ret < 0)
+		return dev_err_probe(dev, ret, "Unable to initialize i2c %d\n", ret);
+
+	ret = devm_request_irq(dev, i2c_bus->irq, ast2600_i2c_bus_irq, 0,
+			       dev_name(dev), i2c_bus);
+	if (ret < 0) {
+		ret = dev_err_probe(dev, ret, "Unable to request irq %d\n",
+				    i2c_bus->irq);
+		goto err;
+	}
+
+	writel(AST2600_I2CM_PKT_DONE | AST2600_I2CM_BUS_RECOVER,
+	       i2c_bus->reg_base + AST2600_I2CM_IER);
+
+	ret = i2c_add_adapter(&i2c_bus->adap);
+	if (ret)
+		goto err;
+
+	return 0;
+
+err:
+	writel(0, i2c_bus->reg_base + AST2600_I2CC_FUN_CTRL);
+	writel(0, i2c_bus->reg_base + AST2600_I2CM_IER);
+	return ret;
+}
+
+static void ast2600_i2c_remove(struct platform_device *pdev)
+{
+	struct ast2600_i2c_bus *i2c_bus = platform_get_drvdata(pdev);
+
+	i2c_del_adapter(&i2c_bus->adap);
+
+	/* Disable everything. */
+	writel(0, i2c_bus->reg_base + AST2600_I2CC_FUN_CTRL);
+	writel(0, i2c_bus->reg_base + AST2600_I2CM_IER);
+}
+
+static const struct of_device_id ast2600_i2c_of_match[] = {
+	{ .compatible = "aspeed,ast2600-i2c-bus" },
+	{ }
+};
+MODULE_DEVICE_TABLE(of, ast2600_i2c_of_match);
+
+static struct platform_driver ast2600_i2c_driver = {
+	.probe		= ast2600_i2c_probe,
+	.remove		= ast2600_i2c_remove,
+	.driver		= {
+		.name		= "ast2600-i2c",
+		.of_match_table	= ast2600_i2c_of_match,
+	},
+};
+module_platform_driver(ast2600_i2c_driver);
+
+MODULE_AUTHOR("Ryan Chen <ryan_chen@aspeedtech.com>");
+MODULE_DESCRIPTION("ASPEED AST2600 I2C Controller Driver");
+MODULE_LICENSE("GPL");

-- 
2.34.1



^ permalink raw reply related

* Re: [PATCH v5 2/2] ARM: dts: aspeed: ventura2: Add Meta ventura2 BMC
From: Kyle Hsieh @ 2026-06-11  5:41 UTC (permalink / raw)
  To: Andrew Jeffery
  Cc: Rob Herring, Krzysztof Kozlowski, Conor Dooley, Joel Stanley,
	devicetree, linux-arm-kernel, linux-aspeed, linux-kernel
In-Reply-To: <3d56889c004fc2d11b76ace6033c7ccfb8a37d03.camel@codeconstruct.com.au>

On Wed, Jun 10, 2026 at 7:15 PM Andrew Jeffery
<andrew@codeconstruct.com.au> wrote:
>
> Hi Kyle,
>
> On Mon, 2026-06-08 at 10:42 +0800, Kyle Hsieh wrote:
> > Add linux device tree entry related to the Meta(Facebook) rmc-node.
>
> This is redundant as it is evident from the patch itself. Can you
> please remove it?
>
> > The system use an AT2600 BMC.
> > This node is named "ventura2".
>
> Can you provide some more detail about the platform in the commit
> message? What's it's purpose? Can you describe some interesting
> features or details about its design?
>
Hi Andrew,

To briefly answer your question: Ventura2 is Rack Management
Controller. It is a modular device primarily designed to manage liquid
cooling systems and monitor rack-level hardware states. Its key
hardware features include an extensive I2C/GPIO topology for
tray-level and rack-level liquid leakage detection, as well as MCTP
over I2C support for asynchronous device communications.

I will remove the redundant sentence and include this detailed
description of the platform's purpose and architecture in the commit
message for v7.
> >
> > Signed-off-by: Kyle Hsieh <kylehsieh1995@gmail.com>
> > ---
> >  arch/arm/boot/dts/aspeed/Makefile                  |    1 +
> >  .../dts/aspeed/aspeed-bmc-facebook-ventura2.dts    | 2888 ++++++++++++++++++++
> >  2 files changed, 2889 insertions(+)
> >
> > diff --git a/arch/arm/boot/dts/aspeed/Makefile b/arch/arm/boot/dts/aspeed/Makefile
> > index 9adf9278dc94..6b96997629d4 100644
> > --- a/arch/arm/boot/dts/aspeed/Makefile
> > +++ b/arch/arm/boot/dts/aspeed/Makefile
> > @@ -32,6 +32,7 @@ dtb-$(CONFIG_ARCH_ASPEED) += \
> >       aspeed-bmc-facebook-minipack.dtb \
> >       aspeed-bmc-facebook-santabarbara.dtb \
> >       aspeed-bmc-facebook-tiogapass.dtb \
> > +     aspeed-bmc-facebook-ventura2.dtb \
> >       aspeed-bmc-facebook-wedge40.dtb \
> >       aspeed-bmc-facebook-wedge100.dtb \
> >       aspeed-bmc-facebook-wedge400-data64.dtb \
> > diff --git a/arch/arm/boot/dts/aspeed/aspeed-bmc-facebook-ventura2.dts b/arch/arm/boot/dts/aspeed/aspeed-bmc-facebook-ventura2.dts
> > new file mode 100644
> > index 000000000000..9bf7d6e52e40
> > --- /dev/null
> > +++ b/arch/arm/boot/dts/aspeed/aspeed-bmc-facebook-ventura2.dts
> > @@ -0,0 +1,2888 @@
> > +// SPDX-License-Identifier: GPL-2.0
> > +// Copyright (c) 2023 Facebook Inc.
> > +/dts-v1/;
> > +
> > +#include "aspeed-g6.dtsi"
> > +#include <dt-bindings/i2c/i2c.h>
> > +#include <dt-bindings/gpio/aspeed-gpio.h>
> > +
> > +/ {
> > +     model = "Facebook Ventura2 RMC";
> > +     compatible = "facebook,ventura2-rmc", "aspeed,ast2600";
> >
>
> ...
>
> > +};
> > +
>
> ...
>
> > +&gpio1 {
> > +     gpio-line-names =
> > +     /*18A0-18A7*/   "","","","","","","","",
> > +     /*18B0-18B7*/   "","","","",
> > +                     "FM_BOARD_BMC_REV_ID0","FM_BOARD_BMC_REV_ID1",
> > +                     "FM_BOARD_BMC_REV_ID2","",
> > +     /*18C0-18C7*/   "SPI_BMC_BIOS_ROM_IRQ0_R_N","","","","","","","",
> > +     /*18D0-18D7*/   "","","","","","","","",
> > +     /*18E0-18E3*/   "FM_BMC_PROT_LS_EN","AC_PWR_BMC_BTN_R_N","","";
> > +};
> > +
> > +&i2c0 {
> > +     status = "okay";
> > +
> > +     i2c-mux@77 {
> > +             compatible = "nxp,pca9548";
> > +             reg = <0x77>;
> > +             #address-cells = <1>;
> > +             #size-cells = <0>;
> > +             i2c-mux-idle-disconnect;
> > +
> > +             i2c0mux0ch0: i2c@0 {
> > +                     #address-cells = <1>;
> > +                     #size-cells = <0>;
> > +                     reg = <0>;
> > +             };
> > +
> > +             i2c0mux0ch1: i2c@1 {
> > +                     #address-cells = <1>;
> > +                     #size-cells = <0>;
> > +                     reg = <1>;
> > +             };
> > +
> > +             i2c0mux0ch2: i2c@2 {
> > +                     #address-cells = <1>;
> > +                     #size-cells = <0>;
> > +                     reg = <2>;
> > +             };
> > +
> > +             i2c0mux0ch3: i2c@3 {
> > +                     #address-cells = <1>;
> > +                     #size-cells = <0>;
> > +                     reg = <3>;
> > +                     status = "okay";
> > +             };
> > +
> > +             i2c0mux0ch4: i2c@4 {
> > +                     #address-cells = <1>;
> > +                     #size-cells = <0>;
> > +                     reg = <4>;
> > +                     status = "okay";
> > +             };
> > +
> > +             i2c0mux0ch5: i2c@5 {
> > +                     #address-cells = <1>;
> > +                     #size-cells = <0>;
> > +                     reg = <5>;
> > +                     status = "okay";
> > +
> > +                     eeprom@56 {
> > +                             compatible = "atmel,24c128";
> > +                             reg = <0x56>;
> > +                     };
> > +             };
> > +
> > +             i2c0mux0ch6: i2c@6 {
> > +                     #address-cells = <1>;
> > +                     #size-cells = <0>;
> > +                     reg = <6>;
> > +
> > +                     eeprom@56 {
> > +                             compatible = "atmel,24c128";
> > +                             reg = <0x56>;
> > +                     };
> > +
> > +                     fan_io_expander0: gpio@20 {
> > +                             compatible = "nxp,pca9555";
> > +                             reg = <0x20>;
> > +                             gpio-controller;
> > +                             #gpio-cells = <2>;
> > +                     };
> > +
> > +                     fan_io_expander1: gpio@21 {
> > +                             compatible = "nxp,pca9555";
> > +                             reg = <0x21>;
> > +                             gpio-controller;
> > +                             #gpio-cells = <2>;
> > +                     };
> > +
> > +                     adc@1d {
> > +                             compatible = "ti,adc128d818";
> > +                             reg = <0x1d>;
> > +                             ti,mode = /bits/ 8 <1>;
> > +                     };
> > +
> > +                     adc@35 {
> > +                             compatible = "maxim,max11617";
> > +                             reg = <0x35>;
> > +                     };
> > +             };
> > +
> > +             i2c0mux0ch7: i2c@7 {
> > +                     #address-cells = <1>;
> > +                     #size-cells = <0>;
> > +                     reg = <7>;
> > +
> > +                     fanctl0: fan-controller@20 {
> > +                             compatible = "maxim,max31790";
> > +                             reg = <0x20>;
> > +                             #address-cells = <1>;
> > +                             #size-cells = <0>;
> > +                             channel@2 {
>
> Can you make sure that you consistently use a blank line to separate
> child nodes from each other and from properties in their parent?
>
> Please fix throughout.

I will recheck the whole file to satisfy the rule in the next patch v7, thanks.
>
> > +                                     reg = <2>;
> > +                                     sensor-type = "TACH";
> > +                             };
> > +                             channel@5 {
> > +                                     reg = <5>;
> > +                                     sensor-type = "TACH";
> > +                             };
> > +                     };
> > +
> > +                     fanctl1: fan-controller@23 {
> > +                             compatible = "nuvoton,nct7363";
> > +                             reg = <0x23>;
> > +                             #pwm-cells = <2>;
> > +
> > +                             fan-9 {
> > +                                     pwms = <&fanctl1 0 20000>;
> > +                                     tach-ch = /bits/ 8 <0x09>;
> > +                             };
> > +
> > +                             fan-11 {
> > +                                     pwms = <&fanctl1 0 20000>;
> > +                                     tach-ch = /bits/ 8 <0x0B>;
> > +                             };
> > +
> > +                             fan-10 {
> > +                                     pwms = <&fanctl1 4 20000>;
> > +                                     tach-ch = /bits/ 8 <0x0A>;
> > +                             };
> > +
> > +                             fan-13 {
> > +                                     pwms = <&fanctl1 4 20000>;
> > +                                     tach-ch = /bits/ 8 <0x0D>;
> > +                             };
> > +
> > +                             fan-15 {
> > +                                     pwms = <&fanctl1 6 20000>;
> > +                                     tach-ch = /bits/ 8 <0x0F>;
> > +                             };
> > +
> > +                             fan-1 {
>
> Can you please sort the fan nodes in ascending order?
I will reorder the fan node in next patch v7.
>
> > +                                     pwms = <&fanctl1 6 20000>;
> > +                                     tach-ch = /bits/ 8 <0x01>;
> > +                             };
> > +
> > +                             fan-0 {
> > +                                     pwms = <&fanctl1 10 20000>;
> > +                                     tach-ch = /bits/ 8 <0x00>;
> > +                             };
> > +
> > +                             fan-3 {
> > +                                     pwms = <&fanctl1 10 20000>;
> > +                                     tach-ch = /bits/ 8 <0x03>;
> > +                             };
> > +                     };
> > +             };
> > +     };
> > +};
> >
>
> ...
>
> > +
> > +     // Marvell 88E6393X EEPROM
>
> Please try to be consistent with the comment style (prefer /* */).
I will fix this in the next patch v7, thanks for your suggestion.
>
> > +     eeprom@50 {
> > +             compatible = "atmel,24c64";
> > +             reg = <0x50>;
> > +     };
> > +
> > +     rtc@51 {
> > +             compatible = "nxp,pcf8563";
> > +             reg = <0x51>;
> > +     };
> > +};
> > +


^ permalink raw reply

* Re: (subset) [PATCH v4 0/3] Reserve eDMA channels 0-1 for V2X
From: Vinod Koul @ 2026-06-11  5:51 UTC (permalink / raw)
  To: Rob Herring, Krzysztof Kozlowski, Conor Dooley, Shawn Guo,
	Sascha Hauer, Pengutronix Kernel Team, Fabio Estevam, Frank Li,
	Peng Fan, Ye Li, Joy Zou
  Cc: devicetree, imx, linux-arm-kernel, linux-kernel,
	Laurentiu Mihalcea
In-Reply-To: <20260211-b4-imx95-v2x-v4-0-10852754b267@nxp.com>


On Wed, 11 Feb 2026 17:28:23 +0800, Joy Zou wrote:
> 
> 

Applied, thanks!

[1/3] dt-bindings: dma: fsl-edma: add dma-channel-mask property description
      commit: 75b28d74f90c79e788e0e86caf0173fc0b2e92aa

Best regards,
-- 
~Vinod




^ permalink raw reply

* Re: [PATCH] dmaengine: qcom: hidma: use sysfs_emit() in sysfs show callbacks
From: Vinod Koul @ 2026-06-11  5:51 UTC (permalink / raw)
  To: okaya, Hungyu Lin
  Cc: Frank.Li, linux-arm-kernel, linux-arm-msm, dmaengine,
	linux-kernel
In-Reply-To: <20260607163119.78717-1-dennylin0707@gmail.com>


On Sun, 07 Jun 2026 16:31:19 +0000, Hungyu Lin wrote:
> Replace sprintf() and strlen() patterns in sysfs show callbacks
> with sysfs_emit().
> 
> sysfs_emit() is the preferred helper for formatting sysfs output
> and simplifies the implementation.
> 
> 
> [...]

Applied, thanks!

[1/1] dmaengine: qcom: hidma: use sysfs_emit() in sysfs show callbacks
      commit: 26f926b44dbfc035d5ba0ccfc4387a40aa9947c1

Best regards,
-- 
~Vinod




^ permalink raw reply

* Re: [PATCH v6 00/20] dma-mapping: Use DMA_ATTR_CC_SHARED through direct, pool and swiotlb paths
From: Aneesh Kumar K.V @ 2026-06-11  5:52 UTC (permalink / raw)
  To: Catalin Marinas
  Cc: iommu, linux-arm-kernel, linux-kernel, linux-coco, Robin Murphy,
	Marek Szyprowski, Will Deacon, Marc Zyngier, Steven Price,
	Suzuki K Poulose, Jiri Pirko, Jason Gunthorpe, Mostafa Saleh,
	Petr Tesarik, Alexey Kardashevskiy, Dan Williams, Xu Yilun,
	linuxppc-dev, linux-s390, Madhavan Srinivasan, Michael Ellerman,
	Nicholas Piggin, Christophe Leroy (CS GROUP), Alexander Gordeev,
	Gerald Schaefer, Heiko Carstens, Vasily Gorbik,
	Christian Borntraeger, Sven Schnelle, x86
In-Reply-To: <aigYbK12D8uKQvJF@arm.com>

Catalin Marinas <catalin.marinas@arm.com> writes:

> On Thu, Jun 04, 2026 at 02:09:39PM +0530, Aneesh Kumar K.V (Arm) wrote:
>> This series propagates DMA_ATTR_CC_SHARED through the dma-direct,
>> dma-pool, and swiotlb paths so that encrypted and decrypted DMA buffers
>> are handled consistently.
>> 
>> Today, the direct DMA path mostly relies on force_dma_unencrypted() for
>> shared/decrypted buffer handling. This series consolidates the
>> force_dma_unencrypted() checks in the top-level functions and ensures
>> that the remaining DMA interfaces use DMA attributes to make the correct
>> decisions.
>
> Please check Sashiko's reports, it has some good points:
>
> https://sashiko.dev/#/patchset/20260604083959.1265923-1-aneesh.kumar@kernel.org
>
> I think the main one is the swiotlb_tbl_map_single() changes which break
> AMD SME host support. There cc_platform_has(CC_ATTR_MEM_ENCRYPT) is true
> but force_dma_unencrypted() is false. Normally you'd not end up on this
> path but you can have swiotlb=force.
>

I would consider the above similar to a trusted device requiring swiotlb
bouncing. At some point, based on real-world use cases, we may need to
add protected io_tlb_mem pools. We have not done that yet because no
such use case has come so far.

-aneesh


^ permalink raw reply

* [PATCH] media: bcm2835-unicam: Fix querycap multiple caps
From: Eugen Hristev @ 2026-06-11  6:09 UTC (permalink / raw)
  To: Raspberry Pi Kernel Maintenance, Mauro Carvalho Chehab,
	Florian Fainelli, Ray Jui, Scott Branden,
	Broadcom internal kernel review list, Sakari Ailus,
	Dave Stevenson, Laurent Pinchart, Jean-Michel Hautbois,
	Naushir Patuck
  Cc: Hans Verkuil, linux-media, linux-rpi-kernel, linux-arm-kernel,
	linux-kernel, Eugen Hristev

The unicam exposes two video nodes, one for image, another for metadata.
Querycap should return the right caps for the respective node, not both.

video0:

Capabilities     : 0xa4200001
        Video Capture
        I/O MC
        Streaming
        Extended Pix Format
        Device Capabilities
Device Caps      : 0x24200001
        Video Capture
        I/O MC
        Streaming
        Extended Pix Format

video1:

Capabilities     : 0xa4a00000
        Metadata Capture
        I/O MC
        Streaming
        Extended Pix Format
        Device Capabilities
Device Caps      : 0x24a00000
        Metadata Capture
        I/O MC
        Streaming
        Extended Pix Format

Fixes: 392cd78d495f ("media: bcm2835-unicam: Add support for CCP2/CSI2 camera interface")
Signed-off-by: Eugen Hristev <ehristev@kernel.org>
---
 drivers/media/platform/broadcom/bcm2835-unicam.c | 5 ++++-
 1 file changed, 4 insertions(+), 1 deletion(-)

diff --git a/drivers/media/platform/broadcom/bcm2835-unicam.c b/drivers/media/platform/broadcom/bcm2835-unicam.c
index 8d28ba0b59a3..4bf36ce80047 100644
--- a/drivers/media/platform/broadcom/bcm2835-unicam.c
+++ b/drivers/media/platform/broadcom/bcm2835-unicam.c
@@ -1833,7 +1833,10 @@ static int unicam_querycap(struct file *file, void *priv,
 	strscpy(cap->driver, UNICAM_MODULE_NAME, sizeof(cap->driver));
 	strscpy(cap->card, UNICAM_MODULE_NAME, sizeof(cap->card));
 
-	cap->capabilities |= V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_META_CAPTURE;
+	if (is_image_node(node))
+		cap->capabilities |= V4L2_CAP_VIDEO_CAPTURE;
+	else
+		cap->capabilities |= V4L2_CAP_META_CAPTURE;
 
 	return 0;
 }

---
base-commit: a87737435cfa134f9cdcc696ba3080759d04cf72
change-id: 20260611-bcmpiqcap-f893a9ea2da9

Best regards,
--  
Eugen Hristev <ehristev@kernel.org>



^ permalink raw reply related

* [PATCH] dmaengine: sun6i-dma: Fix use-after-free in error handling paths
From: Hongling Zeng @ 2026-06-11  6:36 UTC (permalink / raw)
  To: vkoul, Frank.Li, wens, jernej.skrabec, samuel, mripard, arnd
  Cc: dmaengine, linux-arm-kernel, linux-sunxi, linux-kernel,
	zhongling0719, Hongling Zeng

In error handling paths, the for loop frees v_lli in the loop body,
then accesses v_lli->v_lli_next and v_lli->p_lli_next in the
increment expression, which is use-after-free.

Fix by saving both the next virtual and physical pointers before
freeing the current node.

Fixes: 555859308723 ("dmaengine: Add driver for Allwinner sun6i DMA")
Signed-off-by: Hongling Zeng <zenghongling@kylinos.cn>
---
 drivers/dma/sun6i-dma.c | 20 ++++++++++++++++----
 1 file changed, 16 insertions(+), 4 deletions(-)

diff --git a/drivers/dma/sun6i-dma.c b/drivers/dma/sun6i-dma.c
index a9a254dbf8cb..eb9c4ae87ac8 100644
--- a/drivers/dma/sun6i-dma.c
+++ b/drivers/dma/sun6i-dma.c
@@ -788,9 +788,15 @@ static struct dma_async_tx_descriptor *sun6i_dma_prep_slave_sg(
 	return vchan_tx_prep(&vchan->vc, &txd->vd, flags);
 
 err_lli_free:
-	for (p_lli = txd->p_lli, v_lli = txd->v_lli; v_lli;
-	     p_lli = v_lli->p_lli_next, v_lli = v_lli->v_lli_next)
+	p_lli = txd->p_lli;
+	v_lli = txd->v_lli;
+	while (v_lli) {
+		struct sun6i_dma_lli *next_v_lli = v_lli->v_lli_next;
+		dma_addr_t next_p_lli = v_lli->p_lli_next;
 		dma_pool_free(sdev->pool, v_lli, p_lli);
+		v_lli = next_v_lli;
+		p_lli = next_p_lli;
+	}
 	kfree(txd);
 	return NULL;
 }
@@ -869,9 +875,15 @@ static struct dma_async_tx_descriptor *sun6i_dma_prep_dma_cyclic(
 	return vchan_tx_prep(&vchan->vc, &txd->vd, flags);
 
 err_lli_free:
-	for (p_lli = txd->p_lli, v_lli = txd->v_lli; v_lli;
-	     p_lli = v_lli->p_lli_next, v_lli = v_lli->v_lli_next)
+	p_lli = txd->p_lli;
+	v_lli = txd->v_lli;
+	while (v_lli) {
+		struct sun6i_dma_lli *next_v_lli = v_lli->v_lli_next;
+		dma_addr_t next_p_lli = v_lli->p_lli_next;
 		dma_pool_free(sdev->pool, v_lli, p_lli);
+		v_lli = next_v_lli;
+		p_lli = next_p_lli;
+	}
 	kfree(txd);
 	return NULL;
 }
-- 
2.25.1



^ permalink raw reply related

* Re: spi: uniphier: KASAN wild-memory-access in complete() on early IRQ
From: Kunihiko Hayashi @ 2026-06-11  5:32 UTC (permalink / raw)
  To: Jaeyoung Chung, Masami Hiramatsu
  Cc: Mark Brown, linux-spi, linux-arm-kernel, linux-kernel,
	Sangyun Kim, Kyungwook Boo
In-Reply-To: <20260610225705.37b09a7f876e5ba07757377a@kernel.org>

Hi, Jaeyoung Masami-san

On 2026/06/10 22:57, Masami Hiramatsu wrote:
> On Wed, 10 Jun 2026 20:56:21 +0900
> Jaeyoung Chung <jjy600901@snu.ac.kr> wrote:
> 
>> Hi,
>>
>> uniphier_spi_probe() in drivers/spi/spi-uniphier.c registers the
>> interrupt handler uniphier_spi_handler() with devm_request_irq() before
>> it initializes priv->xfer_done with init_completion(). If an interrupt
>> arrives after devm_request_irq() and before init_completion(), the
>> handler calls complete() on an uninitialized completion, causing a
>> kernel panic.
>>
>> The probe path, in uniphier_spi_probe():
>>
>>      host = spi_alloc_host(&pdev->dev, sizeof(*priv)); /* priv
> kzalloc-zeroed */
>>      ...
>>      ret = devm_request_irq(&pdev->dev, irq, uniphier_spi_handler,
>>                             0, "uniphier-spi", priv);  /* register
> handler */
>>      ...
>>      init_completion(&priv->xfer_done);                /* initialize
> completion */
>>
>> The interrupt handler uniphier_spi_handler() calls complete() on its
>> done path:
>>
>>      done:
>>          complete(&priv->xfer_done);
>>
>> If the device raises an interrupt before init_completion() runs,
>> complete() acquires the uninitialized wait.lock and walks the zeroed
>> task_list in swake_up_locked(). The zeroed task_list makes list_empty()
>> return false, so swake_up_locked() dereferences a NULL list entry,
>> triggering a KASAN wild-memory-access.
>>
>> Suggested fix: move init_completion(&priv->xfer_done) above
>> devm_request_irq(), so the completion is valid before the handler can
> run.
>>
>> Reported-by: Sangyun Kim <sangyun.kim@snu.ac.kr>
>> Reported-by: Kyungwook Boo <bookyungwook@gmail.com>

Thank you for your report.

Indeed, if an interrupt occurs before init_completion, an exception might
occur depending on the status.

And I also confirmed the detection of KASAN using pseudo interrupt call.

   BUG: KASAN: out-of-bounds in complete+0x74/0xf0
   Read of size 8 at addr fffffffffffffff8 by task swapper/0/1
   Pointer tag: [ff], memory tag: [fe]
   ...
   Memory state around the buggy address:
    fffffffffffffd00: fe fe fe fe fe fe fe fe fe fe fe fe fe fe fe fe
    fffffffffffffe00: fe fe fe fe fe fe fe fe fe fe fe fe fe fe fe fe
   >ffffffffffffff00: fe fe fe fe fe fe fe fe fe fe fe fe fe fe fe fe
   Unable to handle kernel paging request at virtual address efff800000000000
   KASAN: null-ptr-deref in range [0x0000000000000000-0x000000000000000f]

> Good catch! All resources used by a callback, should be initialized before
> registering it.
> Kinihiko, can you fix it by reordering the initialization?

Yes, I'll prepare to fix it according to the report.

Thank you,

> 
> Thank you,
> 
>> Thanks,
>> Jaeyoung Chung

-- 
---
Best Regards
Kunihiko Hayashi


^ permalink raw reply

* Re: [RFC PATCH v2 1/3] mm/huge_memory: make persistent huge zero folio read-only
From: Mike Rapoport @ 2026-06-11  6:49 UTC (permalink / raw)
  To: Lance Yang
  Cc: dave.hansen, xueyuan.chen21, akpm, linux-mm, linux-kernel,
	linux-arm-kernel, x86, catalin.marinas, will, tglx, mingo, bp,
	dave.hansen, luto, peterz, hpa, david, ljs, liam, vbabka, surenb,
	mhocko, ziy, baolin.wang, npache, ryan.roberts, dev.jain, baohua,
	yang, jannh
In-Reply-To: <20260610032022.23361-1-lance.yang@linux.dev>

Hi,

On Wed, Jun 10, 2026 at 11:20:22AM +0800, Lance Yang wrote:
> Hi Dave,
> 
> Thanks for taking the time to review.
> 
> On Tue, Jun 09, 2026 at 12:33:36PM -0700, Dave Hansen wrote:
> >On 6/9/26 07:37, Xueyuan Chen wrote:
> >> +bool __weak arch_make_pages_readonly(struct page *page, int nr_pages)
> >> +{
> >> +	return false;
> >> +}
> >
> >This is a rather wonky function. It's going to cause all kinds of fun if
> >it is used like this:
> >
> >	arch_make_pages_readonly(syscall_table, 1);
> 
> Ouch, yeah, it is ...

We already have set_direct_map* APIs, why don't you add a new one there?
set_direct_map_ro() for example.
 
> >It's also kinda weird to have it return a bool, and not check that bool
> >at the single call site. Some things come to mind:
> >
> >1. This function needs commenting. It needs to say what it does, when
> >   architectures should override it and what their implementations
> >   should look like. It needs to be clear that this can't be used for
> >   anything really important. What should architectures do with alias
> >   mappings? Are they allowed to touch non-direct map aliases? Are they
> >   required to?
> 
> Agreed. Needs a real comment ...
> 
> Just meant as a best-effort direct/linear-map permission chang, nothing
> stronger than that. I should spell out what happens, or does not happen,
> to non-direct-map aliases, if anything, and make clear callers cannot
> treat this as a hard guarantee :D

It's not only about highmem, anything that changes the direct map might
fail to allocate memory when splitting larger mappings.
 
> >2. The return type needs to be reconsidered. Is 'bool' even acceptable?
> >   Should it just be 'void' if callers can't do anything when it fails?
> 
> Maybe ignoring it is OK now, but someone may need the return value later?
> 
> >3. What should the naming be? "readonly" vs "ro". Should it have a
> >   "maybe" since it's kinda optional?
> 
> Fair point. "make" may be overstating it a bit ...
> 
> With a return value, arch_try_make_pages_readonly() sounds about right
> to me. If we end up with void and pure best-effort semantics, maybe
> arch_maybe_make_pages_readonly() fits better :)

Realistically, I wouldn't expect 32-bit configs to enable
PERSISTENT_HUGE_ZERO_FOLIO or even THP, so naming this function to reflect
32-bit behaviour seems odd going forward.
 
> >4. Should this new API be folio or page-based in the first place?
> 
> For page vs folio, I was mostly following David's RFC v1 suggestion.
> 
> Current caller is a folio, sure, but the page-range helper leaves room
> for non-folio users later. Happy to add a simple folio wrapper if that
> reads better ;)
> 
> >5. Is mm/huge_memory.c the right place to define a generic mm function,
> >   even a stub?
> 
> Ah, you're right! My bad, wrong place for a generic stub. Will move it
> out for RFC v3.

We have include/linux/set_memory.h for such function declarations and their
stubs.
 
> Thanks, Lance
> 

-- 
Sincerely yours,
Mike.


^ permalink raw reply

* [PATCH] arm64: dts: aspeed: Fix duplicate pinctrl labels and address scheme
From: Ryan Chen @ 2026-06-11  6:50 UTC (permalink / raw)
  To: Rob Herring, Krzysztof Kozlowski, Conor Dooley, Joel Stanley,
	Andrew Jeffery, Arnd Bergmann
  Cc: devicetree, linux-arm-kernel, linux-aspeed, linux-kernel,
	Ryan Chen

Fix duplicate pinctrl_tach{0-15} and pinctrl_n{cts,dcd,dsr,ri}5 labels
in aspeed-g7-soc1-pinctrl.dtsi.

Drop the cpu-index from secondary/tertiary container nodes: reduce the
"#address-cells" from 2 to 1 and update ssp_nvic/tsp_nvic unit-address
and reg accordingly. Also remove URL comments from the DTS.

Suggested-by: Andrew Jeffery <andrew@codeconstruct.com.au>
Fixes: e77bb5dc5759 ("arm64: dts: aspeed: Add initial AST27xx SoC device tree")
Signed-off-by: Ryan Chen <ryan_chen@aspeedtech.com>
---
This series contains follow-up fixes for the AST27xx DTS support that
was merged into linux-next (e77bb5dc5759).

Two issues were identified after merge by Andrew Jeffery during review
of the pending v11 series:

1. Duplicate pinctrl state labels in aspeed-g7-soc1-pinctrl.dtsi caused
   dtc to abort with fatal label-redefinition errors.

2. The synthetic container nodes (secondary, tertiary) for sub-processor
   interrupt controllers used a 2-cell address scheme to encode a
   <cpu-index reg-base> tuple.  Since the cpu-index adds no value for
   nodes that are purely phandle anchors, Andrew requested we drop it
   and use the bare register address instead.
---
 arch/arm64/boot/dts/aspeed/aspeed-g7-a35.dtsi      |  14 ++-
 .../boot/dts/aspeed/aspeed-g7-soc1-pinctrl.dtsi    | 102 ---------------------
 2 files changed, 6 insertions(+), 110 deletions(-)

diff --git a/arch/arm64/boot/dts/aspeed/aspeed-g7-a35.dtsi b/arch/arm64/boot/dts/aspeed/aspeed-g7-a35.dtsi
index ef283d95649a..58193c3c3696 100644
--- a/arch/arm64/boot/dts/aspeed/aspeed-g7-a35.dtsi
+++ b/arch/arm64/boot/dts/aspeed/aspeed-g7-a35.dtsi
@@ -84,32 +84,30 @@ l2: l2-cache0 {
 	};
 
 	secondary {
-		#address-cells = <2>;
-		/* https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/drivers/of/address.c?h=v6.16#n491 */
+		#address-cells = <1>;
 		#size-cells = <0>;
-		/* https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/drivers/of/address.c?h=v6.16#n430 */
 
-		ssp_nvic: interrupt-controller@1,e000e100 {
+		ssp_nvic: interrupt-controller@e000e100 {
 			compatible = "arm,v7m-nvic";
 			#interrupt-cells = <2>;
 			#address-cells = <0>;
 			interrupt-controller;
-			reg = <1 0xe000e100>;
+			reg = <0xe000e100>;
 			arm,num-irq-priority-bits = <3>;
 			status = "disabled";
 		};
 	};
 
 	tertiary {
-		#address-cells = <2>;
+		#address-cells = <1>;
 		#size-cells = <0>;
 
-		tsp_nvic: interrupt-controller@2,e000e100 {
+		tsp_nvic: interrupt-controller@e000e100 {
 			compatible = "arm,v7m-nvic";
 			#interrupt-cells = <2>;
 			#address-cells = <0>;
 			interrupt-controller;
-			reg = <2 0xe000e100>;
+			reg = <0xe000e100>;
 			arm,num-irq-priority-bits = <3>;
 			status = "disabled";
 		};
diff --git a/arch/arm64/boot/dts/aspeed/aspeed-g7-soc1-pinctrl.dtsi b/arch/arm64/boot/dts/aspeed/aspeed-g7-soc1-pinctrl.dtsi
index 72d93323593d..6edf14617b09 100644
--- a/arch/arm64/boot/dts/aspeed/aspeed-g7-soc1-pinctrl.dtsi
+++ b/arch/arm64/boot/dts/aspeed/aspeed-g7-soc1-pinctrl.dtsi
@@ -496,87 +496,6 @@ pinctrl_hvi3c15_default: hvi3c15-default-state {
 		function = "I3C15";
 		groups = "HVI3C15";
 	};
-
-	pinctrl_tach0_default: tach0-default-state {
-		function = "TACH0";
-		groups = "TACH0";
-	};
-
-	pinctrl_tach1_default: tach1-default-state {
-		function = "TACH1";
-		groups = "TACH1";
-	};
-
-	pinctrl_tach2_default: tach2-default-state {
-		function = "TACH2";
-		groups = "TACH2";
-	};
-
-	pinctrl_tach3_default: tach3-default-state {
-		function = "TACH3";
-		groups = "TACH3";
-	};
-
-	pinctrl_tach4_default: tach4-default-state {
-		function = "TACH4";
-		groups = "TACH4";
-	};
-
-	pinctrl_tach5_default: tach5-default-state {
-		function = "TACH5";
-		groups = "TACH5";
-	};
-
-	pinctrl_tach6_default: tach6-default-state {
-		function = "TACH6";
-		groups = "TACH6";
-	};
-
-	pinctrl_tach7_default: tach7-default-state {
-		function = "TACH7";
-		groups = "TACH7";
-	};
-
-	pinctrl_tach8_default: tach8-default-state {
-		function = "TACH8";
-		groups = "TACH8";
-	};
-
-	pinctrl_tach9_default: tach9-default-state {
-		function = "TACH9";
-		groups = "TACH9";
-	};
-
-	pinctrl_tach10_default: tach10-default-state {
-		function = "TACH10";
-		groups = "TACH10";
-	};
-
-	pinctrl_tach11_default: tach11-default-state {
-		function = "TACH11";
-		groups = "TACH11";
-	};
-
-	pinctrl_tach12_default: tach12-default-state {
-		function = "TACH12";
-		groups = "TACH12";
-	};
-
-	pinctrl_tach13_default: tach13-default-state {
-		function = "TACH13";
-		groups = "TACH13";
-	};
-
-	pinctrl_tach14_default: tach14-default-state {
-		function = "TACH14";
-		groups = "TACH14";
-	};
-
-	pinctrl_tach15_default: tach15-default-state {
-		function = "TACH15";
-		groups = "TACH15";
-	};
-
 	pinctrl_thru0_default: thru0-default-state {
 		function = "THRU0";
 		groups = "THRU0";
@@ -940,27 +859,6 @@ pinctrl_uart3_default: uart3-default-state {
 		function = "UART3";
 		groups = "UART3";
 	};
-
-	pinctrl_ncts5_default: ncts5-default-state {
-		function = "NCTS5";
-		groups = "NCTS5";
-	};
-
-	pinctrl_ndcd5_default: ndcd5-default-state {
-		function = "NDCD5";
-		groups = "NDCD5";
-	};
-
-	pinctrl_ndsr5_default: ndsr5-default-state {
-		function = "NDSR5";
-		groups = "NDSR5";
-	};
-
-	pinctrl_nri5_default: nri5-default-state {
-		function = "NRI5";
-		groups = "NRI5";
-	};
-
 	pinctrl_ndtr5_default: ndtr5-default-state {
 		function = "NDTR5";
 		groups = "NDTR5";

---
base-commit: abe651837cb394f76d738a7a747322fca3bf17ba
change-id: 20260611-dtsi_fix-099b11a321b5

Best regards,
-- 
Ryan Chen <ryan_chen@aspeedtech.com>



^ permalink raw reply related

* Re: [PATCH] media: bcm2835-unicam: Fix querycap multiple caps
From: Jean-Michel Hautbois @ 2026-06-11  6:51 UTC (permalink / raw)
  To: Eugen Hristev, Raspberry Pi Kernel Maintenance,
	Mauro Carvalho Chehab, Florian Fainelli, Ray Jui, Scott Branden,
	Broadcom internal kernel review list, Sakari Ailus,
	Dave Stevenson, Laurent Pinchart, Naushir Patuck
  Cc: Hans Verkuil, linux-media, linux-rpi-kernel, linux-arm-kernel,
	linux-kernel
In-Reply-To: <20260611-bcmpiqcap-v1-1-10cf7fb438df@kernel.org>

Hi Eugen,

Thank you for the patch.

Two issues with this one, I'm afraid.

Le 11/06/2026 à 08:09, Eugen Hristev a écrit :
> The unicam exposes two video nodes, one for image, another for metadata.
> Querycap should return the right caps for the respective node, not both.
> 
> video0:
> 
> Capabilities     : 0xa4200001
>          Video Capture
>          I/O MC
>          Streaming
>          Extended Pix Format
>          Device Capabilities
> Device Caps      : 0x24200001
>          Video Capture
>          I/O MC
>          Streaming
>          Extended Pix Format
> 
> video1:
> 
> Capabilities     : 0xa4a00000
>          Metadata Capture
>          I/O MC
>          Streaming
>          Extended Pix Format
>          Device Capabilities
> Device Caps      : 0x24a00000
>          Metadata Capture
>          I/O MC
>          Streaming
>          Extended Pix Format
> 
> Fixes: 392cd78d495f ("media: bcm2835-unicam: Add support for CCP2/CSI2 camera interface")
> Signed-off-by: Eugen Hristev <ehristev@kernel.org>
> ---
>   drivers/media/platform/broadcom/bcm2835-unicam.c | 5 ++++-
>   1 file changed, 4 insertions(+), 1 deletion(-)
> 
> diff --git a/drivers/media/platform/broadcom/bcm2835-unicam.c b/drivers/media/platform/broadcom/bcm2835-unicam.c
> index 8d28ba0b59a3..4bf36ce80047 100644
> --- a/drivers/media/platform/broadcom/bcm2835-unicam.c
> +++ b/drivers/media/platform/broadcom/bcm2835-unicam.c
> @@ -1833,7 +1833,10 @@ static int unicam_querycap(struct file *file, void *priv,
>   	strscpy(cap->driver, UNICAM_MODULE_NAME, sizeof(cap->driver));
>   	strscpy(cap->card, UNICAM_MODULE_NAME, sizeof(cap->card));
>   
> -	cap->capabilities |= V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_META_CAPTURE;
> +	if (is_image_node(node))

First, it does not compile, as node is not declared here.
'struct unicam_node *node = video_drvdata(file);' would be needed.

> +		cap->capabilities |= V4L2_CAP_VIDEO_CAPTURE;
> +	else
> +		cap->capabilities |= V4L2_CAP_META_CAPTURE;
>   
>   	return 0;
>   }
> 

Second, and more important,  I don't think the current behaviour is a bug.
Documentation/userspace-api/media/v4l/vidioc-querycap.rst states about 
the 'capabilities' field:

"The capabilities field should contain a union of all capabilities 
available around the several V4L2 devices exported to userspace.
For all those devices the capabilities field returns the same set of 
capabilities."

Per-node differentiation is the job of 'device_caps', which unicam
already sets correctly when registering each video device (your
v4l2-ctl output shows the Device Caps are already right).

So this looks like working as intended to me, and the patch should be
dropped.

Thanks,
JM

> ---
> base-commit: a87737435cfa134f9cdcc696ba3080759d04cf72
> change-id: 20260611-bcmpiqcap-f893a9ea2da9
> 
> Best regards,
> --
> Eugen Hristev <ehristev@kernel.org>
> 



^ permalink raw reply

* Re: [PATCH 3/3] soc: samsung: exynos-pmu: fix error paths in cpuhotplug/idle states setup
From: Peter Griffin @ 2026-06-11  7:07 UTC (permalink / raw)
  To: Alexey Klimov
  Cc: Krzysztof Kozlowski, Alim Akhtar, Sam Protsenko,
	linux-samsung-soc, linux-arm-kernel, linux-kernel, stable,
	Sashiko
In-Reply-To: <DJ5GP6VQJDHL.2V30K56ME95DO@linaro.org>

Hi Alexey,

On Wed, 10 Jun 2026 at 16:07, Alexey Klimov <alexey.klimov@linaro.org> wrote:
>
> On Wed Jun 10, 2026 at 2:34 PM BST, Peter Griffin wrote:
> > Hi Alexey,
>
> Hi Peter,
>
> > Thanks for your patch!
> >
> > On Fri, 5 Jun 2026 at 21:19, Alexey Klimov <alexey.klimov@linaro.org> wrote:
> >>
> >> The setup_cpuhp_and_cpuidle() initialisation sequence currently ignores
> >> the return values of cpuhp_setup_state(), cpu_pm_register_notifier(), and
> >> register_reboot_notifier(). If any of these registrations fail during
> >> probe() routine, the driver returns 0, leaving the driver partially
> >> configured.
> >
> > I originally made the failure non-fatal because the system still boots
> > without the notifiers registered (and all other Arm64 Exynos SoCs
> > upstream don't register notifiers and AFAICT have broken cpu hotplug
> > and cpu idle).
> >
> > In hindsight, that seems like a mistake. I think your patch to fully
> > unwind everything in case of failure makes more sense.  See small
> > comment below about destroy_cpuhp_and_cpuidle()
>
> Wait, setup_cpuhp_and_cpuidle() should be non-fatal and shouldn't
> return any errors?

I suggest you re-read my above comment above ^^

Peter.


^ permalink raw reply

* [PATCH] pinctrl: meson: amlogic-a4: use nolock get range
From: Xianwei Zhao via B4 Relay @ 2026-06-11  7:10 UTC (permalink / raw)
  To: Linus Walleij, Neil Armstrong, Kevin Hilman, Jerome Brunet,
	Martin Blumenstingl
  Cc: linux-amlogic, linux-gpio, linux-arm-kernel, linux-kernel,
	Xianwei Zhao

From: Xianwei Zhao <xianwei.zhao@amlogic.com>

Use pinctrl_find_gpio_range_from_pin_nolock() instead of
pinctrl_find_gpio_range_from_pin() when configuring a pin or
setting a GPIO value.

This avoids taking the lock and allows the code to be safely
called from interrupt context.

Signed-off-by: Xianwei Zhao <xianwei.zhao@amlogic.com>
---
Use pinctrl_find_gpio_range_from_pin_nolock 
when configuring a pin or setting a GPIO value.
---
 drivers/pinctrl/meson/pinctrl-amlogic-a4.c | 10 +++++-----
 1 file changed, 5 insertions(+), 5 deletions(-)

diff --git a/drivers/pinctrl/meson/pinctrl-amlogic-a4.c b/drivers/pinctrl/meson/pinctrl-amlogic-a4.c
index 5ae0c19d007d..420f7915c010 100644
--- a/drivers/pinctrl/meson/pinctrl-amlogic-a4.c
+++ b/drivers/pinctrl/meson/pinctrl-amlogic-a4.c
@@ -300,7 +300,7 @@ static int aml_pmx_set_mux(struct pinctrl_dev *pctldev, unsigned int fselector,
 	int i;
 
 	for (i = 0; i < group->npins; i++) {
-		range =  pinctrl_find_gpio_range_from_pin(pctldev, group->pins[i]);
+		range =  pinctrl_find_gpio_range_from_pin_nolock(pctldev, group->pins[i]);
 		aml_pctl_set_function(info, range, group->pins[i], group->func[i]);
 	}
 
@@ -499,7 +499,7 @@ static int aml_pinconf_disable_bias(struct aml_pinctrl *info,
 				    unsigned int pin)
 {
 	struct pinctrl_gpio_range *range =
-			 pinctrl_find_gpio_range_from_pin(info->pctl, pin);
+			 pinctrl_find_gpio_range_from_pin_nolock(info->pctl, pin);
 	struct aml_gpio_bank *bank = gpio_chip_to_bank(range->gc);
 	unsigned int reg, bit = 0;
 
@@ -512,7 +512,7 @@ static int aml_pinconf_enable_bias(struct aml_pinctrl *info, unsigned int pin,
 				   bool pull_up)
 {
 	struct pinctrl_gpio_range *range =
-			 pinctrl_find_gpio_range_from_pin(info->pctl, pin);
+			 pinctrl_find_gpio_range_from_pin_nolock(info->pctl, pin);
 	struct aml_gpio_bank *bank = gpio_chip_to_bank(range->gc);
 	unsigned int reg, bit, val = 0;
 	int ret;
@@ -534,7 +534,7 @@ static int aml_pinconf_set_drive_strength(struct aml_pinctrl *info,
 					  u16 drive_strength_ua)
 {
 	struct pinctrl_gpio_range *range =
-			 pinctrl_find_gpio_range_from_pin(info->pctl, pin);
+			 pinctrl_find_gpio_range_from_pin_nolock(info->pctl, pin);
 	struct aml_gpio_bank *bank = gpio_chip_to_bank(range->gc);
 	unsigned int reg, bit, ds_val;
 
@@ -569,7 +569,7 @@ static int aml_pinconf_set_gpio_bit(struct aml_pinctrl *info,
 				    bool arg)
 {
 	struct pinctrl_gpio_range *range =
-			 pinctrl_find_gpio_range_from_pin(info->pctl, pin);
+			 pinctrl_find_gpio_range_from_pin_nolock(info->pctl, pin);
 	struct aml_gpio_bank *bank = gpio_chip_to_bank(range->gc);
 	unsigned int reg, bit;
 

---
base-commit: aca7f93a3eba55962d3448ed005f84daabe37c5f
change-id: 20260611-pinctrl-nolock-2f93ff2d61eb

Best regards,
-- 
Xianwei Zhao <xianwei.zhao@amlogic.com>




^ permalink raw reply related

* Re: [PATCH v5 06/10] m68k: stmark2: use ioport.h macros for resources
From: Andy Shevchenko @ 2026-06-11  7:18 UTC (permalink / raw)
  To: Angelo Dureghello
  Cc: Greg Ungerer, Geert Uytterhoeven, Steven King, Arnd Bergmann,
	Maxime Coquelin, Alexandre Torgue, Jonathan Cameron,
	David Lechner, Nuno Sá, Andy Shevchenko, Greg Ungerer,
	linux-m68k, linux-kernel, linux-stm32, linux-arm-kernel,
	linux-iio
In-Reply-To: <20260610-wip-stmark2-dac-v5-6-b76b83366d5c@baylibre.com>

On Wed, Jun 10, 2026 at 10:35:11PM +0200, Angelo Dureghello wrote:

> Change resource declaration using DEFINE_RES_*() macros.
> DEFINE_DMA_RES() is for a single dma channel, not a range, so used twice.
> 
> Also, some drivers assume IRQ resources are from index 1, so just to stay
> uniform, moved IRQ resource at index 1.

Reviewed-by: Andy Shevchenko <andriy.shevchenko@intel.com>

-- 
With Best Regards,
Andy Shevchenko




^ permalink raw reply

* Re: [PATCHv2] PCI: mvebu: Use fixed-width interrupt masks
From: Manivannan Sadhasivam @ 2026-06-11  7:20 UTC (permalink / raw)
  To: linux-pci, Rosen Penev
  Cc: Thomas Petazzoni, Pali Rohár, Lorenzo Pieralisi, linusw,
	Krzysztof Wilczyński, Rob Herring, Bjorn Helgaas,
	linux-arm-kernel, linux-kernel
In-Reply-To: <20260526044016.1025613-1-rosenp@gmail.com>


On Mon, 25 May 2026 21:40:16 -0700, Rosen Penev wrote:
> Use u32-typed BIT and GENMASK helpers for PCIe interrupt register
> masks.  This keeps inverted masks in the same width as the registers
> and avoids truncation warnings on 64-bit compile-test builds.
> 
> Fixes this and similar warnings:
> 
> drivers/pci/controller/pci-mvebu.c:316:21: error: implicit conversion from
> 'unsigned long' to 'u32' (aka 'unsigned int') changes value from
> 18446744069414584320 to 0 [-Werror,-Wconstant-conversion]
>   316 |         mvebu_writel(port, ~PCIE_INT_ALL_MASK, PCIE_INT_UNMASK_OFF);
> 
> [...]

Applied, thanks!

[1/1] PCI: mvebu: Use fixed-width interrupt masks
      commit: c525508c1854e72b080b49e6ff7074913dc1905d

Best regards,
-- 
Manivannan Sadhasivam <mani@kernel.org>



^ permalink raw reply

* Re: [PATCH v3 1/5] dt-bindings: soc: cix,sky1-system-control: add audss system control
From: Krzysztof Kozlowski @ 2026-06-11  7:40 UTC (permalink / raw)
  To: joakim.zhang
  Cc: mturquette, sboyd, bmasney, robh, krzk+dt, conor+dt, p.zabel,
	gary.yang, cix-kernel-upstream, linux-clk, devicetree,
	linux-kernel, linux-arm-kernel
In-Reply-To: <20260610075645.3581145-2-joakim.zhang@cixtech.com>

On Wed, Jun 10, 2026 at 03:56:41PM +0800, joakim.zhang@cixtech.com wrote:
> From: Joakim Zhang <joakim.zhang@cixtech.com>
> 
> The Cix Sky1 Audio Subsystem (AUDSS) groups audio-related clock, reset
> and control registers in a dedicated CRU block. Software reset lines are
> exposed on the syscon parent via #reset-cells, following the same model
> as the existing Sky1 FCH and S5 system control bindings.
> 
> Add the cix,sky1-audss-system-control compatible to
> cix,sky1-system-control.yaml for the MFD/syscon parent node, and define
> AUDSS software reset indices in
> include/dt-bindings/reset/cix,sky1-audss-system-control.h for I2S, HDA,
> DMAC, mailbox, watchdog and timer blocks.

All this is pretty pointless - you explained the binding, which answers
nothing why you did it that way. Instead you must explain the hardware
design.

> 
> Signed-off-by: Joakim Zhang <joakim.zhang@cixtech.com>
> ---
>  .../soc/cix/cix,sky1-system-control.yaml      | 52 +++++++++++++++++--
>  .../reset/cix,sky1-audss-system-control.h     | 25 +++++++++
>  2 files changed, 72 insertions(+), 5 deletions(-)
>  create mode 100644 include/dt-bindings/reset/cix,sky1-audss-system-control.h
> 
> diff --git a/Documentation/devicetree/bindings/soc/cix/cix,sky1-system-control.yaml b/Documentation/devicetree/bindings/soc/cix/cix,sky1-system-control.yaml
> index a01a515222c6..61d26a69fd44 100644
> --- a/Documentation/devicetree/bindings/soc/cix/cix,sky1-system-control.yaml
> +++ b/Documentation/devicetree/bindings/soc/cix/cix,sky1-system-control.yaml
> @@ -15,11 +15,16 @@ description:
>  
>  properties:
>    compatible:
> -    items:
> -      - enum:
> -          - cix,sky1-system-control
> -          - cix,sky1-s5-system-control
> -      - const: syscon
> +    oneOf:
> +      - items:
> +          - enum:
> +              - cix,sky1-system-control
> +              - cix,sky1-s5-system-control
> +          - const: syscon
> +      - items:
> +          - const: cix,sky1-audss-system-control
> +          - const: simple-mfd

Just so you are aware - this means children do not depend on the parent
for operation. You will not be able to fix it later, if it turns out
that children do depend...

> +          - const: syscon
>  
>    reg:
>      maxItems: 1
> @@ -27,6 +32,28 @@ properties:
>    '#reset-cells':
>      const: 1
>  
> +  clock-controller:
> +    type: object
> +    properties:
> +      compatible:
> +        const: cix,sky1-audss-clock
> +    required:
> +      - compatible
> +    additionalProperties: true
> +
> +allOf:
> +  - if:
> +      properties:
> +        compatible:
> +          contains:
> +            const: cix,sky1-audss-system-control
> +    then:
> +      required:
> +        - clock-controller
> +    else:
> +      properties:
> +        clock-controller: false
> +
>  required:
>    - compatible
>    - reg
> @@ -40,3 +67,18 @@ examples:
>        reg = <0x4160000 0x100>;
>        #reset-cells = <1>;
>      };
> +  - |
> +    audss_syscon: system-controller@7110000 {
> +        compatible = "cix,sky1-audss-system-control", "simple-mfd", "syscon";
> +        reg = <0x7110000 0x10000>;
> +        #reset-cells = <1>;
> +
> +        clock-controller {
> +            compatible = "cix,sky1-audss-clock";
> +            power-domains = <&smc_devpd 0>;

My questions from v2 from the other patch are still valid - why audss
system clock controller is outside of the power domain? Why the audss
reset is outside, but audss clock not?

This does not feel like correct hardware representation.

Best regards,
Krzysztof



^ permalink raw reply

* RE: [PATCH V2 1/2] PCI: host-generic: Simplify return value handling in pci_host_common_parse_port(s)
From: Hongxing Zhu @ 2026-06-11  7:40 UTC (permalink / raw)
  To: Sherry Sun (OSS), l.stach@pengutronix.de, Frank Li,
	bhelgaas@google.com, lpieralisi@kernel.org,
	kwilczynski@kernel.org, mani@kernel.org, robh@kernel.org,
	s.hauer@pengutronix.de, kernel@pengutronix.de, festevam@gmail.com,
	will@kernel.org
  Cc: imx@lists.linux.dev, linux-pci@vger.kernel.org,
	linux-arm-kernel@lists.infradead.org,
	linux-kernel@vger.kernel.org, Sherry Sun
In-Reply-To: <20260525065443.2338629-2-sherry.sun@oss.nxp.com>

> -----Original Message-----
> From: Sherry Sun (OSS) <sherry.sun@oss.nxp.com>
> Sent: Monday, May 25, 2026 2:55 PM
> To: Hongxing Zhu <hongxing.zhu@nxp.com>; l.stach@pengutronix.de; Frank Li
> <frank.li@nxp.com>; bhelgaas@google.com; lpieralisi@kernel.org;
> kwilczynski@kernel.org; mani@kernel.org; robh@kernel.org;
> s.hauer@pengutronix.de; kernel@pengutronix.de; festevam@gmail.com;
> will@kernel.org
> Cc: imx@lists.linux.dev; linux-pci@vger.kernel.org; linux-arm-
> kernel@lists.infradead.org; linux-kernel@vger.kernel.org; Sherry Sun
> <sherry.sun@nxp.com>
> Subject: [PATCH V2 1/2] PCI: host-generic: Simplify return value handling in
> pci_host_common_parse_port(s)
> 
> From: Sherry Sun <sherry.sun@nxp.com>
> 
> The pci_host_common_parse_port() shouldn't check the RC-level binding.
> That's a policy decision that belongs to the caller, not this common helper.
> 
> Simplify pci_host_common_parse_port() to only parse properties from the Root
> Port (and its children) without checking the RC node. Also change
> pci_host_common_parse_ports() to return 0 when no ports are found, since it is
> not an error.
> 
> So now both functions won't return failure for "property not found" or "port not
> found", they purely return 0 on success and a negative error code on real failures.
> 
> Signed-off-by: Sherry Sun <sherry.sun@nxp.com>
Reviewed-by: Richard Zhu <hongxing.zhu@nxp.com>

Best Regards
Richard Zhu

> ---
>  drivers/pci/controller/pci-host-common.c | 29 ++++--------------------
>  1 file changed, 5 insertions(+), 24 deletions(-)
> 
> diff --git a/drivers/pci/controller/pci-host-common.c b/drivers/pci/controller/pci-
> host-common.c
> index 2ce6f4b66133..c93de5a10758 100644
> --- a/drivers/pci/controller/pci-host-common.c
> +++ b/drivers/pci/controller/pci-host-common.c
> @@ -108,8 +108,7 @@ static int pci_host_common_parse_perst(struct device
> *dev,
>   * dependencies and the driver may fail to operate if required resources
>   * are missing.
>   *
> - * Return: 0 on success, -ENODEV if PERST# found in RC node (legacy binding
> - * should be used), Other negative error codes on failure.
> + * Return: 0 on success, negative error codes on failure.
>   */
>  static int pci_host_common_parse_port(struct device *dev,
>  				      struct pci_host_bridge *bridge, @@ -128,22
> +127,6 @@ static int pci_host_common_parse_port(struct device *dev,
>  	if (ret)
>  		return ret;
> 
> -	/*
> -	 * 1. PERST# found in RP or its child nodes - list is not empty,
> -	 *    continue
> -	 *
> -	 * 2. PERST# not found in RP/children, but found in RC node -
> -	 *    return -ENODEV to fallback legacy binding
> -	 *
> -	 * 3. PERST# not found anywhere - list is empty, continue (optional
> -	 *    PERST#)
> -	 */
> -	if (list_empty(&port->perst)) {
> -		if (of_property_present(dev->of_node, "reset-gpios") ||
> -		    of_property_present(dev->of_node, "reset-gpio"))
> -			return -ENODEV;
> -	}
> -
>  	INIT_LIST_HEAD(&port->list);
>  	list_add_tail(&port->list, &bridge->ports);
> 
> @@ -158,13 +141,11 @@ static int pci_host_common_parse_port(struct device
> *dev,
>   * Iterate through child nodes of the host bridge and parse Root Port
>   * properties (currently only reset GPIOs).
>   *
> - * Return: 0 on success, -ENODEV if no ports found or PERST# found in RC
> - * node (legacy binding should be used), Other negative error codes on
> - * failure.
> + * Return: 0 on success, negative error codes on failure.
>   */
>  int pci_host_common_parse_ports(struct device *dev, struct pci_host_bridge
> *bridge)  {
> -	int ret = -ENODEV;
> +	int ret = 0;
> 
>  	for_each_available_child_of_node_scoped(dev->of_node, of_port) {
>  		if (!of_node_is_type(of_port, "pci")) @@ -174,8 +155,8 @@ int
> pci_host_common_parse_ports(struct device *dev, struct pci_host_bridge *brid
>  			goto err_cleanup;
>  	}
> 
> -	if (ret)
> -		return ret;
> +	if (list_empty(&bridge->ports))
> +		return 0;
> 
>  	return devm_add_action_or_reset(dev, pci_host_common_delete_ports,
>  					&bridge->ports);
> --
> 2.37.1



^ permalink raw reply

* Re: [PATCH v2 3/5] dt-bindings: clock: cix,sky1-audss-clock: add audss clock controller
From: Krzysztof Kozlowski @ 2026-06-11  7:41 UTC (permalink / raw)
  To: Joakim Zhang, mturquette@baylibre.com, sboyd@kernel.org,
	bmasney@redhat.com, robh@kernel.org, krzk+dt@kernel.org,
	conor+dt@kernel.org, p.zabel@pengutronix.de, Gary Yang
  Cc: cix-kernel-upstream, linux-clk@vger.kernel.org,
	devicetree@vger.kernel.org, linux-kernel@vger.kernel.org,
	linux-arm-kernel@lists.infradead.org
In-Reply-To: <SEYPR06MB622688915CBD1AA9B65FFB33821D2@SEYPR06MB6226.apcprd06.prod.outlook.com>

On 09/06/2026 08:27, Joakim Zhang wrote:
> 
> Hi Krzysztof,
> 
>> -----Original Message-----
>> From: Krzysztof Kozlowski <krzk@kernel.org>
>> Sent: Friday, June 5, 2026 5:24 PM
>> To: Joakim Zhang <joakim.zhang@cixtech.com>; mturquette@baylibre.com;
>> sboyd@kernel.org; bmasney@redhat.com; robh@kernel.org;
>> krzk+dt@kernel.org; conor+dt@kernel.org; p.zabel@pengutronix.de; Gary Yang
>> <gary.yang@cixtech.com>
>> Cc: cix-kernel-upstream <cix-kernel-upstream@cixtech.com>; linux-
>> clk@vger.kernel.org; devicetree@vger.kernel.org; linux-kernel@vger.kernel.org;
>> linux-arm-kernel@lists.infradead.org
>> Subject: Re: [PATCH v2 3/5] dt-bindings: clock: cix,sky1-audss-clock: add audss
>> clock controller
>>
>> EXTERNAL EMAIL
>>
>> On 05/06/2026 05:22, joakim.zhang@cixtech.com wrote:
>>> +description: |
>>> +  Clock provider for the Cix Sky1 audio subsystem (AUDSS).
>>> +
>>> +  This node is a child of a cix,sky1-audss-system-control MFD/syscon
>>> + node  (see cix,sky1-system-control.yaml). It does not have a reg
>>> + property; clock  mux, divider and gate fields are accessed through the parent
>> register block.
>>> +
>>> +  Software reset lines for AUDSS blocks are exposed on the parent
>>> + syscon via  #reset-cells. Reset indices are defined in
>>> + include/dt-bindings/reset/cix,sky1-audss-system-control.h.
>>> +
>>> +  Six SoC-level reference clocks listed in clocks/clock-names feed
>>> + the AUDSS  clock tree. The provider exposes the internal AUDSS
>>> + clocks to other devices  via #clock-cells; indices are defined in cix,sky1-
>> audss.h.
>>> +
>>> +properties:
>>> +  compatible:
>>> +    const: cix,sky1-audss-clock
>>> +
>>> +  '#clock-cells':
>>> +    const: 1
>>> +    description:
>>> +      Clock indices are defined in include/dt-bindings/clock/cix,sky1-audss.h.
>>> +
>>> +  clocks:
>>> +    minItems: 6
>>
>> Drop
> OK
> 
>>> +    maxItems: 6
>>> +    description:
>>> +      Six SoC-level audio reference clocks that feed the audio subsystem,
>>> +      in the same order as clock-names.
>>> +
>>> +  clock-names:
>>> +    items:
>>> +      - const: audio_clk0
>>> +      - const: audio_clk1
>>> +      - const: audio_clk2
>>> +      - const: audio_clk3
>>> +      - const: audio_clk4
>>> +      - const: audio_clk5
>>
>> Pretty pointless names. Names matching indexes have no benefits, drop all of
>> them and instead list items in "clocks" with description.
> Yes, you are right, I will describe these more meaningful.
> 
>>> +
>>> +  resets:
>>> +    maxItems: 1
>>> +    description: Audio subsystem NoC (or bus) reset line.
>>> +
>>> +  power-domains:
>>> +    maxItems: 1
>>> +    description: Audio subsystem power domain.
>>
>> So the clock part has power domain but reset part does not? This is odd.
>> Especially that parent is audss (right?) and here you describe that this is audss
>> poer domain.
>>
>> Same question about resets.
> 
> The reset and power domain takes effect on the entire subsystem, i.e., audss can be accessed only after powered on and reset released, including the CRU registers which contains clock/reset/control bits for all device within the audss.
> 
> Because the reset controller probe does not access the hardware, while the clock controller does, so at that time, the power domain and reset were placed in the clock driver. At present, it does not seem very reasonable either. 
> 
> Linking the "reset" and "power domain" to the parent node requires us to ensure the order of the probes. We need to perform deferred probes within the child nodes until the parent node has been probed.
> 

Please wrap your replies.

You refer here to probe, so driver design, but I did not ask about that.
I asked about hardware design.

Best regards,
Krzysztof


^ permalink raw reply

* Re: [PATCH v3 3/5] dt-bindings: clock: cix,sky1-audss-clock: add audss clock controller
From: Krzysztof Kozlowski @ 2026-06-11  7:42 UTC (permalink / raw)
  To: joakim.zhang
  Cc: mturquette, sboyd, bmasney, robh, krzk+dt, conor+dt, p.zabel,
	gary.yang, cix-kernel-upstream, linux-clk, devicetree,
	linux-kernel, linux-arm-kernel
In-Reply-To: <20260610075645.3581145-4-joakim.zhang@cixtech.com>

On Wed, Jun 10, 2026 at 03:56:43PM +0800, joakim.zhang@cixtech.com wrote:
> +  '#clock-cells':
> +    const: 1
> +    description:
> +      Clock indices are defined in include/dt-bindings/clock/cix,sky1-audss.h.
> +
> +  clocks:
> +    items:
> +      - description: I2S parent clock for sampling rates multiple of 8kHz.
> +      - description: I2S parent clock for sampling rates multiple of 11.025kHz.
> +      - description: clock feeding most devices in audss (NOC, DSP, SRAM, HDA, DMAC, I2S, and Mailbox).
> +      - description: clock feeding for HDA, Timer and Watchdog, which is a delicated 48MHz clock.
> +
> +  clock-names:
> +    items:
> +      - const: x8k
> +      - const: x11k
> +      - const: sys
> +      - const: 48m
> +
> +  resets:
> +    maxItems: 1
> +    description: Audio subsystem NoC (or bus) reset line.
> +
> +  power-domains:
> +    maxItems: 1
> +    description: Audio subsystem power domain.

Same comments as last time, but let's keep discussion in previous patch.

> +
> +required:
> +  - compatible
> +  - '#clock-cells'
> +  - clocks
> +  - clock-names
> +  - resets
> +  - power-domains
> +
> +additionalProperties: false
> +
> +examples:
> +  - |
> +    #include <dt-bindings/clock/cix,sky1.h>
> +
> +    clock-controller {
> +        compatible = "cix,sky1-audss-clock";
> +        power-domains = <&smc_devpd 0>;
> +        #clock-cells = <1>;
> +        clocks = <&scmi_clk CLK_TREE_AUDIO_CLK0>, <&scmi_clk CLK_TREE_AUDIO_CLK2>,
> +                 <&scmi_clk CLK_TREE_AUDIO_CLK4>, <&scmi_clk CLK_TREE_AUDIO_CLK5>;
> +        clock-names = "x8k", "x11k", "sys", "48m";
> +        resets = <&s5_syscon 31>;
> +    };
> diff --git a/include/dt-bindings/clock/cix,sky1-audss.h b/include/dt-bindings/clock/cix,sky1-audss.h
> new file mode 100644
> index 000000000000..033046407dee
> --- /dev/null
> +++ b/include/dt-bindings/clock/cix,sky1-audss.h

Filename must match the compatible.

Best regards,
Krzysztof



^ permalink raw reply

* [PATCH v3] arm64: dts: imx94: Add Root Port node and PERST property
From: hongxing.zhu @ 2026-06-11  7:50 UTC (permalink / raw)
  To: sherry.sun, robh, krzk+dt, conor+dt, frank.li, s.hauer, festevam
  Cc: kernel, devicetree, imx, linux-arm-kernel, linux-kernel,
	Richard Zhu

From: Richard Zhu <hongxing.zhu@nxp.com>

Since describing the PCIe PERST# property under Host Bridge node is now
deprecated, it is recommended to add it to the Root Port node, so
creating the Root Port node and add the reset-gpios property in Root
Port.

Signed-off-by: Richard Zhu <hongxing.zhu@nxp.com>
Reviewed-by: Sherry Sun <sherry.sun@nxp.com>
---
 arch/arm64/boot/dts/freescale/imx94.dtsi     | 11 +++++++++++
 arch/arm64/boot/dts/freescale/imx943-evk.dts | 14 ++++++++++----
 arch/arm64/boot/dts/freescale/imx943.dtsi    | 11 +++++++++++
 3 files changed, 32 insertions(+), 4 deletions(-)
---
Changes in v3:
- Move the regulator to Root Port node as well, since [2] had been
  settled.
- Collect Reviewed-by tag issued by Sherry.

Changes in v2:
- Delete reset-gpio properties in PCIe bridge node.
- Correct the "reset-gpio" property to "reset-gpios".

Since the patch-set [1] issued by Sherry had been landed. Add according changes on i.MX943 board too.
[1] https://lkml.org/lkml/2026/6/1/1461
[2] https://lore.kernel.org/imx/20260520084904.2424253-1-sherry.sun@oss.nxp.com/


diff --git a/arch/arm64/boot/dts/freescale/imx94.dtsi b/arch/arm64/boot/dts/freescale/imx94.dtsi
index 1f9035e6cf159..dfbb73603cb24 100644
--- a/arch/arm64/boot/dts/freescale/imx94.dtsi
+++ b/arch/arm64/boot/dts/freescale/imx94.dtsi
@@ -1411,6 +1411,17 @@ pcie0: pcie@4c300000 {
 			power-domains = <&scmi_devpd IMX94_PD_HSIO_TOP>;
 			fsl,max-link-speed = <3>;
 			status = "disabled";
+
+			pcie0_port0: pcie@0 {
+				compatible = "pciclass,0604";
+				device_type = "pci";
+				reg = <0x0 0x0 0x0 0x0 0x0>;
+				bus-range = <0x01 0xff>;
+
+				#address-cells = <3>;
+				#size-cells = <2>;
+				ranges;
+			};
 		};
 
 		pcie0_ep: pcie-ep@4c300000 {
diff --git a/arch/arm64/boot/dts/freescale/imx943-evk.dts b/arch/arm64/boot/dts/freescale/imx943-evk.dts
index 7cfd424689507..674410e541cba 100644
--- a/arch/arm64/boot/dts/freescale/imx943-evk.dts
+++ b/arch/arm64/boot/dts/freescale/imx943-evk.dts
@@ -1034,12 +1034,15 @@ &pcie0 {
 		 <&pcie_ref_clk>;
 	clock-names = "pcie", "pcie_bus", "pcie_phy", "pcie_aux",
 		      "ref", "extref";
-	reset-gpio = <&pcal6416_i2c3_u46 3 GPIO_ACTIVE_LOW>;
-	vpcie3v3aux-supply = <&reg_m2_wlan>;
 	supports-clkreq;
 	status = "okay";
 };
 
+&pcie0_port0 {
+	reset-gpios = <&pcal6416_i2c3_u46 3 GPIO_ACTIVE_LOW>;
+	vpcie3v3aux-supply = <&reg_m2_wlan>;
+};
+
 &pcie0_ep {
 	pinctrl-0 = <&pinctrl_pcie0>;
 	pinctrl-names = "default";
@@ -1058,12 +1061,15 @@ &pcie1 {
 		 <&pcie_ref_clk>;
 	clock-names = "pcie", "pcie_bus", "pcie_phy", "pcie_aux",
 		      "ref", "extref";
-	reset-gpio = <&pcal6416_i2c3_u46 1 GPIO_ACTIVE_LOW>;
-	vpcie3v3aux-supply = <&reg_slot_pwr>;
 	supports-clkreq;
 	status = "okay";
 };
 
+&pcie1_port0 {
+	reset-gpios = <&pcal6416_i2c3_u46 1 GPIO_ACTIVE_LOW>;
+	vpcie3v3aux-supply = <&reg_slot_pwr>;
+};
+
 &pcie1_ep {
 	pinctrl-0 = <&pinctrl_pcie1>;
 	pinctrl-names = "default";
diff --git a/arch/arm64/boot/dts/freescale/imx943.dtsi b/arch/arm64/boot/dts/freescale/imx943.dtsi
index cf5b3dbb47ff7..01152fd0efa5e 100644
--- a/arch/arm64/boot/dts/freescale/imx943.dtsi
+++ b/arch/arm64/boot/dts/freescale/imx943.dtsi
@@ -255,6 +255,17 @@ pcie1: pcie@4c380000 {
 			power-domains = <&scmi_devpd IMX94_PD_HSIO_TOP>;
 			fsl,max-link-speed = <3>;
 			status = "disabled";
+
+			pcie1_port0: pcie@0 {
+				compatible = "pciclass,0604";
+				device_type = "pci";
+				reg = <0x0 0x0 0x0 0x0 0x0>;
+				bus-range = <0x01 0xff>;
+
+				#address-cells = <3>;
+				#size-cells = <2>;
+				ranges;
+			};
 		};
 
 		pcie1_ep: pcie-ep@4c380000 {
-- 
2.34.1



^ permalink raw reply related

* [PATCH RFC 0/2] pinctrl: Add support gpiod_to_irq
From: Xianwei Zhao via B4 Relay @ 2026-06-11  7:54 UTC (permalink / raw)
  To: Linus Walleij, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
	Neil Armstrong, Kevin Hilman, Jerome Brunet, Martin Blumenstingl
  Cc: linux-amlogic, linux-gpio, devicetree, linux-kernel,
	linux-arm-kernel, Xianwei Zhao

Some users need to obtain an IRQ directly from a GPIO descriptor through gpiod_to_irq().
Add the required DT binding and implementation to support this use case.
Since this introduces a new DT property, the property is kept optional to
maintain compatibility with existing SoCs and DTS files.

Signed-off-by: Xianwei Zhao <xianwei.zhao@amlogic.com>
---
Xianwei Zhao (2):
      dt-bindings: pinctl: amlogic,pinctrl-a4: Add gpio irq property
      pinctrl: meson: amlogic-a4: support gpiod_to_irq

 .../bindings/pinctrl/amlogic,pinctrl-a4.yaml       |  5 ++
 drivers/pinctrl/meson/pinctrl-amlogic-a4.c         | 54 ++++++++++++++++++++++
 2 files changed, 59 insertions(+)
---
base-commit: 4ca496f6285e16d91751e5c84c6010e03285528c
change-id: 20260520-gpio-to-irq-be4797d2a23f

Best regards,
-- 
Xianwei Zhao <xianwei.zhao@amlogic.com>




^ permalink raw reply


This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox