All of lore.kernel.org
 help / color / mirror / Atom feed
From: sashiko-bot@kernel.org
To: "Ryan Chen" <ryan_chen@aspeedtech.com>
Cc: robh@kernel.org, conor+dt@kernel.org, devicetree@vger.kernel.org
Subject: Re: [PATCH v32 5/5] i2c: ast2600: Add target mode support
Date: Thu, 11 Jun 2026 05:46:44 +0000	[thread overview]
Message-ID: <20260611054645.3FEEF1F00893@smtp.kernel.org> (raw)
In-Reply-To: <20260611-upstream_i2c-v32-5-b66eba921d01@aspeedtech.com>

Thank you for your contribution! Sashiko AI review found 4 potential issue(s) to consider:
- [High] Incorrect chronological ordering of I2C slave events and spurious event signaling in the target interrupt handler breaks backend state machines.
- [High] Controller mode can be permanently locked out because `target_active` is not reliably cleared upon target transaction STOP.
- [High] Race condition in the target mode master-abort fast path leaves stale controller interrupts pending, leading to use-after-free and data corruption of subsequent I2C transfers.
- [High] Failure to W1C-clear target ISR error bits causes a permanent deadlock where all subsequent controller transfers fail with -EBUSY.
--

commit e5417daca64552c41b25f31b14fb3eb562d3e134
Author: Ryan Chen <ryan_chen@aspeedtech.com>

i2c: ast2600: Add target mode support

Add target mode support to the AST2600 I2C driver, enabling dual-role 
capability. This includes adding target interrupt handling and address match 
response logic.

> diff --git a/drivers/i2c/busses/i2c-ast2600.c b/drivers/i2c/busses/i2c-ast2600.c
> index 70f37dc77468a..aace0c7eeb721 100644
> --- a/drivers/i2c/busses/i2c-ast2600.c
> +++ b/drivers/i2c/busses/i2c-ast2600.c
> @@ -348,6 +353,253 @@ static int ast2600_i2c_recover_bus(struct ast2600_i2c_bus *i2c_bus)
[ ... ]
> +#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);
> +	}

[Severity: High]
Could this fast path abort leave stale controller interrupts pending?

If the hardware asynchronously asserts PKT_DONE or PKT_ERROR shortly after the
W1C ISR read here, the complete() call wakes the waiting thread, which might
immediately start a new controller transfer.

Would the stale interrupt then fire and incorrectly apply to the new transfer,
potentially leading to a use-after-free on the new msgs buffer?

> +
> +	/* 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;
> +	}

[Severity: High]
Does this early return fail to clear the AST2600_I2CS_INACTIVE_TO bit from
the hardware register?

Since it is never W1C-cleared, the ISR register might remain non-zero
permanently.

> +
> +	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;

[Severity: High]
Is the event ordering inverted here?

This unconditionally emits I2C_SLAVE_STOP first, and then falls through to
emit I2C_SLAVE_WRITE_REQUESTED and I2C_SLAVE_WRITE_RECEIVED.

Emitting the STOP condition prior to receiving the write data leaves backend
state machines (like i2c-slave-eeprom) without a terminating event for the
data, which typically causes writes to be dropped.

[ ... ]
> +	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);

[Severity: High]
Can this spuriously emit I2C_SLAVE_WRITE_REQUESTED during a repeated start
transitioning to a Master Read (WAIT_TX_DMA)?

If so, it would reset the backend's internal write pointers, corrupting
previously received write data before I2C_SLAVE_READ_REQUESTED is processed.

[ ... ]
> +	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);
> +}

[Severity: High]
Is this condition too strict?

For standard target write transactions, the hardware explicitly combines
AST2600_I2CS_STOP and AST2600_I2CS_SLAVE_PENDING in the same interrupt event.

If this evaluates to false in those normal cases, target_active would
remain permanently true, locking out all subsequent controller mode
transfers with -EBUSY.

> @@ -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

[Severity: High]
If AST2600_I2CS_INACTIVE_TO is left uncleared in the target interrupt handler,
will this check permanently return -EBUSY for all subsequent controller
transfers?

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20260611-upstream_i2c-v32-0-b66eba921d01@aspeedtech.com?part=5

  reply	other threads:[~2026-06-11  5:46 UTC|newest]

Thread overview: 10+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-06-11  5:31 [PATCH v32 0/5] Add ASPEED AST2600 I2C controller driver Ryan Chen
2026-06-11  5:31 ` [PATCH v32 1/5] dt-bindings: i2c: Split AST2600 binding into a new YAML Ryan Chen
2026-06-11  5:31 ` [PATCH v32 2/5] i2c: aspeed: Read clock-frequency via i2c_parse_fw_timings() Ryan Chen
2026-06-11  5:40   ` sashiko-bot
2026-06-11  5:31 ` [PATCH v32 3/5] dt-bindings: i2c: ast2600-i2c.yaml: Add global-regs properties Ryan Chen
2026-06-11  5:31 ` [PATCH v32 4/5] i2c: ast2600: Add controller driver for AST2600 new register set Ryan Chen
2026-06-11  5:46   ` sashiko-bot
2026-06-11  5:31 ` [PATCH v32 5/5] i2c: ast2600: Add target mode support Ryan Chen
2026-06-11  5:46   ` sashiko-bot [this message]
2026-06-11  8:42 ` [PATCH v32 0/5] Add ASPEED AST2600 I2C controller driver Ryan Chen

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20260611054645.3FEEF1F00893@smtp.kernel.org \
    --to=sashiko-bot@kernel.org \
    --cc=conor+dt@kernel.org \
    --cc=devicetree@vger.kernel.org \
    --cc=robh@kernel.org \
    --cc=ryan_chen@aspeedtech.com \
    --cc=sashiko-reviews@lists.linux.dev \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.