Devicetree
 help / color / mirror / Atom feed
From: sashiko-bot@kernel.org
To: "Marc Dietrich" <marvin24@gmx.de>
Cc: devicetree@vger.kernel.org, robh@kernel.org, conor+dt@kernel.org
Subject: Re: [PATCH 2/2] i2c: tegra20: Add driver for Tegra20 I2C target controller instances
Date: Sun, 08 Nov 2026 21:48:42 +0000	[thread overview]
Message-ID: <sashiko-outbox-204092@kernel.org> (raw)
In-Reply-To: <20261108212458.275980-3-marvin24@gmx.de>

Thank you for your contribution! Sashiko AI review found 3 potential
issues to consider:
- [Medium] i2c: tegra-target: stale nack state in
  tegra_i2c_target_init() and ISR
- [Medium] i2c: tegra-target: unclocked MMIO and unbalanced IRQ/clocks
  after resume error
- [Low] i2c: tegra-target: broken DT binding file path in MAINTAINERS
--

commit f38185c59a54e5417359880cac9a408a1a024e72
Author: Marc Dietrich <marvin24@gmx.de>

i2c: tegra20: Add driver for Tegra20 I2C target controller instances

This commit adds a driver for the NVIDIA Tegra20 I2C controller operating
exclusively in target (slave) mode.

> diff --git a/MAINTAINERS b/MAINTAINERS
> index 3ee846e37066d..e543fde8bdae4 100644
> --- a/MAINTAINERS
> +++ b/MAINTAINERS
> @@ -27534,6 +27534,13 @@ R:	Dmitry Osipenko <digetx@gmail.com>
>  S:	Supported
>  F:	drivers/i2c/busses/i2c-tegra.c
>  
> +TEGRA I2C TARGET DRIVER
> +M:	Marc Dietrich <marvin24@gmx.de>
> +L:	linux-tegra@vger.kernel.org
> +S:	Maintained
> +F:	Documentation/devicetree/bindings/i2c/nvidia,tegra20-i2c-target.yaml
> +F:	drivers/i2c/busses/i2c-tegra-target.c

[Severity: Low]
Does Documentation/devicetree/bindings/i2c/nvidia,tegra20-i2c-target.yaml
exist?  The nvidia,tegra20-i2c-target compatible string is documented in
Documentation/devicetree/bindings/i2c/nvidia,tegra20-i2c.yaml, so this F:
entry points to a non-existent path and triggers a broken file pattern
warning in get_maintainer.pl.

> diff --git a/drivers/i2c/busses/i2c-tegra-target.c b/drivers/i2c/busses/i2c-tegra-target.c
> new file mode 100644
> index 0000000000000..6a8642983ea24
> --- /dev/null
> +++ b/drivers/i2c/busses/i2c-tegra-target.c

[ ... ]

> +static int tegra_i2c_target_init(struct tegra_i2c_target_dev *i2c_dev)
> +{
> +	u32 val;
> +	int err;
> +
> +	err = device_reset(i2c_dev->dev);
> +	if (err)
> +		return err;
> +
> +	val = I2C_CNFG_NEW_MASTER_FSM | I2C_CNFG_PACKET_MODE_EN |
> +	      FIELD_PREP(I2C_CNFG_DEBOUNCE_CNT, 2);
> +	i2c_writel(i2c_dev, val, I2C_CNFG);
> +
> +	err = clk_set_rate(i2c_dev->div_clk,
> +			   i2c_dev->timings.bus_freq_hz * I2C_CLK_MULTIPLIER);
> +	if (err) {
> +		dev_err(i2c_dev->dev, "failed to set div-clk rate: %d\n", err);
> +		return err;
> +	}
> +
> +	i2c_writel(i2c_dev, I2C_SL_CNFG_NEWSL, I2C_SL_CNFG);
> +
> +	/* only 7-bit target address is supported for now */
> +	val = FIELD_PREP(I2C_SL_ADDR1_7BIT_ADDR, i2c_dev->target->addr);
> +	i2c_writel(i2c_dev, val, I2C_SL_ADDR1);
> +	i2c_writel(i2c_dev, 0, I2C_SL_ADDR2);
> +
> +	return 0;
> +}

[ ... ]

> +static irqreturn_t tegra_i2c_target_isr(int irq, void *dev_id)
> +{
> +	struct tegra_i2c_target_dev *i2c_dev = dev_id;
> +	struct i2c_client *target = i2c_dev->target;
> +	bool read, new_xfer;
> +	u32 status;
> +	u8 val = 0xff;
> +
> +	status = i2c_readl(i2c_dev, I2C_SL_STATUS);
> +	if (!(status & I2C_SL_STATUS_SL_IRQ))
> +		return IRQ_NONE;
> +
> +	/* RNW is set if the controller reads from the target */
> +	read = status & I2C_SL_STATUS_RNW;
> +
> +	/* RCVD is set if the interrupt is for the address byte */
> +	new_xfer = status & I2C_SL_STATUS_RCVD;
> +
> +	if (!read) {
> +		val = i2c_readl(i2c_dev, I2C_SL_RCVD);
> +		if (new_xfer)
> +			i2c_writel(i2c_dev, 0, I2C_SL_RCVD);
> +	}
> +
> +	if (status & I2C_SL_STATUS_END_TRANS) {
> +		i2c_slave_event(target, I2C_SLAVE_STOP, &val);
> +
> +		if (i2c_dev->nack)
> +			tegra_i2c_target_set_nack(i2c_dev, false);
> +
> +		/* a new transfer may already have started */
> +		if (!new_xfer)
> +			return IRQ_HANDLED;
> +	}
> +
> +	if (read) {
> +		if (new_xfer) {
> +			udelay(I2C_READ_ACK_DELAY_US);
> +			i2c_slave_event(target, I2C_SLAVE_READ_REQUESTED, &val);
> +		} else {
> +			i2c_slave_event(target, I2C_SLAVE_READ_PROCESSED, &val);
> +		}
> +
> +		/* send the byte and release the bus */
> +		i2c_writel(i2c_dev, val, I2C_SL_RCVD);
> +	} else if (new_xfer) {
> +		/* the address phase is always acknowledged */
> +		if (i2c_slave_event(target, I2C_SLAVE_WRITE_REQUESTED, &val))
> +			tegra_i2c_target_set_nack(i2c_dev, true);
> +	} else if (!i2c_dev->nack) {
> +		if (i2c_slave_event(target, I2C_SLAVE_WRITE_RECEIVED, &val))
> +			tegra_i2c_target_set_nack(i2c_dev, true);
> +	}
> +
> +	return IRQ_HANDLED;
> +}

[Severity: Medium]
Can i2c_dev->nack remain stale across a Repeated START or across system
suspend and resume?

In tegra_i2c_target_isr(), tegra_i2c_target_set_nack(i2c_dev, false) is
only called when I2C_SL_STATUS_END_TRANS is set.  If a controller follows
a NACKed write with a Repeated START (where I2C_SL_STATUS_RCVD is set
without I2C_SL_STATUS_END_TRANS), i2c_dev->nack is never cleared, so a
new write transfer remains NACKed and skips I2C_SLAVE_WRITE_RECEIVED even
when I2C_SLAVE_WRITE_REQUESTED succeeds.

Similarly, tegra_i2c_target_init() writes I2C_SL_CNFG_NEWSL to
I2C_SL_CNFG, clearing I2C_SL_CNFG_NACK in hardware without resetting
i2c_dev->nack = false.  If the system suspends while i2c_dev->nack is
true, tegra_i2c_target_resume() -> tegra_i2c_target_init() leaves
hardware ACKing bytes on the bus while tegra_i2c_target_isr() silently
drops them because i2c_dev->nack is still true.

[ ... ]

> +static int tegra_i2c_target_suspend(struct device *dev)
> +{
> +	struct tegra_i2c_target_dev *i2c_dev = dev_get_drvdata(dev);
> +
> +	if (!i2c_dev->target)
> +		return 0;
> +
> +	disable_irq(i2c_dev->irq);
> +	tegra_i2c_target_deinit(i2c_dev);
> +	clk_bulk_disable_unprepare(ARRAY_SIZE(i2c_dev->clocks), i2c_dev->clocks);
> +
> +	return 0;
> +}
> +
> +static int tegra_i2c_target_resume(struct device *dev)
> +{
> +	struct tegra_i2c_target_dev *i2c_dev = dev_get_drvdata(dev);
> +	int err;
> +
> +	if (!i2c_dev->target)
> +		return 0;
> +
> +	err = clk_bulk_prepare_enable(ARRAY_SIZE(i2c_dev->clocks),
> +				      i2c_dev->clocks);
> +	if (err) {
> +		dev_err(dev, "failed to enable clocks: %d\n", err);
> +		return err;
> +	}
> +
> +	err = tegra_i2c_target_init(i2c_dev);
> +	if (err) {
> +		clk_bulk_disable_unprepare(ARRAY_SIZE(i2c_dev->clocks),
> +					   i2c_dev->clocks);
> +		return err;
> +	}
> +
> +	enable_irq(i2c_dev->irq);
> +
> +	return 0;
> +}

[Severity: Medium]
What happens in tegra_i2c_target_unreg_target() or a later
tegra_i2c_target_suspend() if tegra_i2c_target_resume() fails?

When clk_bulk_prepare_enable() or tegra_i2c_target_init() fails in
tegra_i2c_target_resume(), the clocks are left disabled and i2c_dev->irq
remains disabled, while i2c_dev->target is still non-NULL.  A subsequent
call to tegra_i2c_target_unreg_target() or tegra_i2c_target_suspend()
will then access I2C_SL_CNFG in tegra_i2c_target_deinit() with the
controller clocks disabled (which hangs the APB bus on Tegra20), call
disable_irq() a second time, and call clk_bulk_disable_unprepare() on
already disabled clocks.

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20261108212458.275980-1-marvin24@gmx.de?part=2

  reply	other threads:[~2026-11-08 21:48 UTC|newest]

Thread overview: 6+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-11-08 21:24 [PATCH 0/2] i2c: tegra20: add target controller support Marc Dietrich
2026-11-08 21:24 ` [PATCH 1/2] dt-bindings: i2c: Add Tegra20 I2C target controller Marc Dietrich
2026-11-08 21:32   ` sashiko-bot
2026-11-08 21:24 ` [PATCH 2/2] i2c: tegra20: Add driver for Tegra20 I2C target controller instances Marc Dietrich
2026-11-08 21:48   ` sashiko-bot [this message]
2026-11-09  7:27   ` Uwe Kleine-König

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=sashiko-outbox-204092@kernel.org \
    --to=sashiko-bot@kernel.org \
    --cc=conor+dt@kernel.org \
    --cc=devicetree@vger.kernel.org \
    --cc=marvin24@gmx.de \
    --cc=robh@kernel.org \
    --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 a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox