All of lore.kernel.org
 help / color / mirror / Atom feed
From: Marcus Folkesson <marcus.folkesson@gmail.com>
To: Peter Rosin <peda@axentia.se>
Cc: Wolfram Sang <wsa+renesas@sang-engineering.com>,
	Michael Hennerich <michael.hennerich@analog.com>,
	Bartosz Golaszewski <brgl@bgdev.pl>,
	Andi Shyti <andi.shyti@kernel.org>,
	Bartosz Golaszewski <brgl@kernel.org>,
	linux-i2c@vger.kernel.org, linux-kernel@vger.kernel.org,
	linux-arm-kernel@lists.infradead.org
Subject: Re: [PATCH v4 2/5] i2c: mux: add support for per channel bus frequency
Date: Fri, 13 Feb 2026 16:53:26 +0100	[thread overview]
Message-ID: <aY9I9r8Q82HrxEZM@gmail.com> (raw)
In-Reply-To: <f38230f2-f74b-cf89-8d15-194d85f341df@axentia.se>

[-- Attachment #1: Type: text/plain, Size: 5998 bytes --]

Hi Peter!

On Fri, Feb 13, 2026 at 12:37:29PM +0100, Peter Rosin wrote:
> Hi!
> 
> 2026-02-12 at 22:47, Marcus Folkesson wrote:
> >>> +static int i2c_mux_select_chan(struct i2c_adapter *adap, u32 chan_id)
> > 

[...]

> > Consider the following chain:
> > Root - P1 - M1 - M2 - P2 - D1
> > 
> > P - Parent locked
> > M - Mux locked
> > D - Device
> > 
> > In this case we need to lock both M1 and M2, not just M2 ?
> > I'm not completely sure though, I need to refresh myself on the code
> > base.
> 
> No, that should not be needed. The reason is that when you initiate a
> xfer for D1 the following happens (xfer is a locked transfer, __xfer
> is unlocked):
> 
> - xfer using P2
>  - lock(P2, I2C_LOCK_SEGMENT)
>   + take mux_lock for P2->parent == M2
>   + P2 is parent-locked -> recurse to P2->parent == M2
>    + lock(M2, I2C_LOCK_SEGMENT)
>     + take mux_lock for M2->parent == M1
>     + M2 is mux-locked -> no recursion
>  - ***** (see below)
>  - P2->select (commonly __xfer using M2, elided here)
>  - __xfer using M2 (unlocked since P2 is parent-locked)
>   - §§§§§ (see below)
>   - M2->select (commonly xfer using M1, elided here)
>   - locked xfer using M1 (locked since M2 is mux-locked)
>    - lock(M1, I2C_LOCK_SEGMENT)
>     + take mux_lock for M1->parent == P1
>     + M1 is mux-locked -> no recursion
>    - M1->select (commonly xfer using P1, elided here)
>    - xfer using P1 (locked since M1 is mux-locked)
>     - lock(P1, I2C_LOCK_SEGMENT)
>      + take mux_lock for P1->parent == Root
>      + P1 is parent-locked -> recurse to P1->parent == Root
>       + lock(Root, I2C_LOCK_SEGMENT)
>        + take bus_lock for Root
>        + Root is Root -> no recursion
>     - P1->select (commonly __xfer using Root, elided here)
> 
>     - __xfer using Root (unlocked since P1 is parent-locked)
> 
>     - P1->deselect (commonly __xfer using Root, elided here)
>     - unlock(P1, I2C_LOCK_SEGMENT)
>      + P1 is parent-locked -> recurse to P1->parent == Root
>       + unlock(Root, I2C_LOCK_SEGMENT)
>        + Root is Root -> no recursion
>        + release bus_lock for Root
>      + release mux_lock for P1->parent == Root
>    - M1->deselect (commonly xfer using P1, elided here)
>    - unlock(M1, I2C_LOCK_SEGMENT)
>     + M1 is mux-locked -> no recursion
>     + release mux_unlock for M1->parent == P1
>   - M2->deselect (commonly xfer using M1, elided here)
>  - P2->deselect (commonly __xfer using M2, elided here)
>  - unlock(P2, I2C_LOCK_SEGMENT)
>   + P2 is parent-locked -> recurse to P2->parent == M2
>    + unlock(M2, I2C_LOCK_SEGMENT)
>     + M2 is mux-locked -> no recursion
>     + release mux_lock for M2->parent == P2
>   + release mux_lock for P2->parent == M2
> 
> (Phhew, I wonder how many typos are in there...)
> 
> So, between the steps lock(P2,...) and P2->select (at the ***** mark,
> which is where you add set_clk_freq), what you need to lock is M1,
> i.e. the parent of the first ancestor that is not mux-locked. When
> you lock with I2C_LOCK_ROOT_ADAPTER, this happens:
> 
>  - lock(M1, I2C_LOCK_ROOT_ADAPTER)
>   + take mux_lock for M1->parent == P1
>   + recures to M1->parent == P1
>    + lock(P1, I2C_LOCK_ROOT_ADAPTER)
>     + take mux_lock for P1->parent == Root
>     + recurse to P1->parent == Root
>      + lock(Root, I2C_LOCK_ROOT_ADAPTER)
>       + take bus_lock for Root
>       + Root is Root -> no recursion
>  - Root->set_clk_freq                  <<<< the new thing
>  - unlock(M1, I2C_LOCK_ROOT_ADAPTER)
>   + recurse to M1->parent == P1
>    + unlock(P1, I2C_LOCK_ROOT_ADAPTER)
>     + recurse to P1->parent == Root
>      + unlock(Root, I2C_LOCK_ROOT_ADAPTER)
>       + Root is Root, no recursion
>       + release bus_lock for Root
>     + release mux_lock for P1->parent == Root
>   + release mux_lock for M1->parent == P1

Thanks for the explaination, that makes it much clearer!
> 
> However, spelling that out makes it clearer that Root->set_clk_freq
> will be inserted in more places in the "call tree". It will be added
> before every ->select call, e.g. at §§§§§. Since any of these
> additional set_clk_freq calls happen after the one at *****, they will
> take precedence and ruin the whole thing if any of them should request
> an intermediate frequency (1MHz at Root, 400kHz for any intermediate
> mux and 100kHz for D1, for example).
> 
> I don't immediately see how to reverse that such that the set_clk_freq
> for the top-most level happens closest to the xfer on the root
> adapter.

I've now experimented a bit and think I've landed on this solution:

- i2c_mux_select_chan() will only lower the root bus frequency, this to
  ensure that no intermediate mux will be able to change the frequency
  (all muxes in the middle must have a higher frequency).
- i2c_mux select_chan() store the original bus frequency
- i2c_mux_deselect_chan() will restore the original bus frequency


Something like this:

static int i2c_mux_select_chan(struct i2c_adapter *adap, u32 chan_id, u32 *oldclock)
{
	if (priv->adap.clock_hz && priv->adap.clock_hz < parent->clock_hz) {
		*oldclock = root->clock_hz;
		i2c_adapter_set_clk_freq(root, priv->adap.clock_hz);
    }
}

static void i2c_mux_deselect_chan(struct i2c_adapter *adap, u32 chan_id, u32 oldclock)
{
	if (oldclock && oldclock != priv->adap.clock_hz) {
		i2c_adapter_set_clk_freq(root, oldclock);
    }
}

static int __i2c_mux_master_xfer(struct i2c_adapter *adap,
				 struct i2c_msg msgs[], int num)
{
	u32 oldclock = 0;

	ret = i2c_mux_select_chan(adap, priv->chan_id, &oldclock);
    __i2c_transfer(parent, msgs, num);
	i2c_mux_deselect_chan(adap, priv->chan_id, oldclock);
}

I will do more testing during the weekend.
I now have a virtual i2c bus with virtual mux drivers and virtual
devices so that I can setup different combinations to test with. 

> 
> Cheers,
> Peter

Best regards,
Marcus Folkesson

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]

  reply	other threads:[~2026-02-13 15:53 UTC|newest]

Thread overview: 12+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-01-28  9:54 [PATCH v4 0/5] I2C Mux per channel bus speed Marcus Folkesson
2026-01-28  9:54 ` [PATCH v4 1/5] i2c: core: add callback to change bus frequency Marcus Folkesson
2026-01-28  9:54 ` [PATCH v4 2/5] i2c: mux: add support for per channel " Marcus Folkesson
2026-02-12 15:02   ` Peter Rosin
2026-02-12 21:47     ` Marcus Folkesson
2026-02-13 10:59       ` Marcus Folkesson
2026-02-13 11:37       ` Peter Rosin
2026-02-13 15:53         ` Marcus Folkesson [this message]
2026-01-28  9:54 ` [PATCH v4 3/5] i2c: davinci: calculate bus freq from Hz instead of kHz Marcus Folkesson
2026-01-28  9:54 ` [PATCH v4 4/5] i2c: davinci: add support for setting bus frequency Marcus Folkesson
2026-01-28  9:54 ` [PATCH v4 5/5] docs: i2c: i2c-topology: add section about bus speed Marcus Folkesson
2026-02-12 13:49 ` [PATCH v4 0/5] I2C Mux per channel " Marcus Folkesson

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=aY9I9r8Q82HrxEZM@gmail.com \
    --to=marcus.folkesson@gmail.com \
    --cc=andi.shyti@kernel.org \
    --cc=brgl@bgdev.pl \
    --cc=brgl@kernel.org \
    --cc=linux-arm-kernel@lists.infradead.org \
    --cc=linux-i2c@vger.kernel.org \
    --cc=linux-kernel@vger.kernel.org \
    --cc=michael.hennerich@analog.com \
    --cc=peda@axentia.se \
    --cc=wsa+renesas@sang-engineering.com \
    /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.