构建更复杂的I2C拓扑,而不是带有一个适配器和一个或多个设备的直观的I2C总线,有几个原因。
- 在总线上可能需要一个多路交换器来防止地址冲突。
- 总线可能可以从一些外部总线主机访问,并且可能需要仲裁来确定是否可以访问总线。
- 一个设备(特别是射频调谐器)可能想要避免来自I2C总线的数字噪声,至少在大多数时间,并且位于一个门的后面,在设备可以访问之前必须操作这个门。
Etc
Linux将这些结构表示为I2C适配器树,其中每个适配器都有一个父适配器(根适配器除外)和零个或多个子适配器。根适配器是发出I2C传输的实际适配器,具有父级的所有适配器都是“I2C -mux”对象(引用,因为它也可以是仲裁器或门)的一部分。
根据特定的mux驱动程序,当它的子适配器上有I2C传输时,会发生一些事情。mux驱动程序显然可以操作一个mux,但它也可以与外部总线主机进行仲裁或打开门。mux驱动程序有两种操作,select 和 deselect。在传输之前调用select,而(可选的)deselect在传输之后调用。
Locking
I2C mux有两种锁定方式,它们可以是mux-locked 或 parent-locked锁。从下面可以明显看出,知道一个mux是mux-locked锁还是parent-locked锁是很有用的。以下列表在撰写本文时是正确的:
在drivers/i2c/muxes/下:
i2c-arb-gpio-challenge |
Parent-locked |
i2c-mux-gpio |
Normally parent-locked, mux-locked iff all involved gpio pins are controlled by the same I2C root adapter that they mux. |
i2c-mux-gpmux |
Normally parent-locked, mux-locked iff specified in device-tree. |
i2c-mux-ltc4306 |
Mux-locked |
i2c-mux-mlxcpld |
Parent-locked |
i2c-mux-pca9541 |
Parent-locked |
i2c-mux-pca954x |
Parent-locked |
i2c-mux-pinctrl |
Normally parent-locked, mux-locked iff all involved pinctrl devices are controlled by the same I2C root adapter that they mux. |
i2c-mux-reg |
Parent-locked |
在drivers/iio/下:
gyro/mpu3050 |
Mux-locked |
imu/inv_mpu6050/ |
Mux-locked |
在drivers/media/下:
dvb-frontends/lgdt3306a |
Mux-locked |
dvb-frontends/m88ds3103 |
Parent-locked |
dvb-frontends/rtl2830 |
Parent-locked |
dvb-frontends/rtl2832 |
Mux-locked |
dvb-frontends/si2168 |
Mux-locked |
usb/cx231xx/ |
Parent-locked |
Mux-locked muxes
在完全select-transfer-deselect事务期间,mux锁定的mux不会锁定整个父适配器,只有父适配器上的mux被锁定。如果select和/或deselect操作必须使用I2C传输来完成它们的任务, Mux-locked muxes是最有趣的。由于父适配器在整个事务期间没有完全锁定,不相关的I2C传输可能会交叉事务的不同阶段。:这样做的好处是mux驱动程序可能更容易、更清晰地实现,但也有一些注意事项。
ML1. |
If you build a topology with a mux-locked mux being the parent of a parent-locked mux, this might break the expectation from the parent-locked mux that the root adapter is locked during the transaction. |
ML2. |
It is not safe to build arbitrary topologies with two (or more) mux-locked muxes that are not siblings, when there are address collisions between the devices on the child adapters of these non-sibling muxes. I.e. the select-transfer-deselect transaction targeting e.g. device address 0x42 behind mux-one may be interleaved with a similar operation targeting device address 0x42 behind mux-two. The intension with such a topology would in this hypothetical example be that mux-one and mux-two should not be selected simultaneously, but mux-locked muxes do not guarantee that in all topologies. |
ML3. |
A mux-locked mux cannot be used by a driver for auto-closing gates/muxes, i.e. something that closes automatically after a given number (one, in most cases) of I2C transfers. Unrelated I2C transfers may creep in and close prematurely. |
ML4. |
If any non-I2C operation in the mux driver changes the I2C mux state, the driver has to lock the root adapter during that operation. Otherwise garbage may appear on the bus as seen from devices behind the mux, when an unrelated I2C transfer is in flight during the non-I2C mux-changing operation. |
Mux-locked Example
.----------. .--------. .--------. | mux- |-----| dev D1 | | root |--+--| locked | '--------' '--------' | | mux M1 |--. .--------. | '----------' '--| dev D2 | | .--------. '--------' '--| dev D3 | '--------'
当访问D1时,会发生如下情况:
- 有人向D1发出I2C传输
- M1在其父(本例中的根适配器)上锁定mux。
- M1调用 ->select 准备mux。
- M1(大概)做一些I2C传输作为它选择的一部分。这些传输是锁定父适配器的普通I2C传输。
- M1将第1步的I2C传输作为锁定父适配器的普通I2C传输提供给其父适配器
- M1调用 ->deselect ,如果它有一个的话。
- 规则与步骤4相同,但是对于 ->deselect。
- M1解锁其父结点的mux。
这意味着对D2的访问在整个操作期间都被锁定。但是对D3的访问可能在任何点都是交错的。
Parent-locked muxes
parent -locked muxes在整个select- transfer-deselect事务期间锁定父适配器。这意味着mux驱动程序必须确保在事务期间通过父适配器的任何和所有I2C传输都是未锁定的I2C传输(例如__i2c_transfer),否则就会出现死锁。有几点需要注意。
PL1. |
If you build a topology with a parent-locked mux being the child of another mux, this might break a possible assumption from the child mux that the root adapter is unused between its select op and the actual transfer (e.g. if the child mux is auto-closing and the parent mux issues I2C transfers as part of its select). This is especially the case if the parent mux is mux-locked, but it may also happen if the parent mux is parent-locked. |
PL2. |
If select/deselect calls out to other subsystems such as gpio, pinctrl, regmap or iio, it is essential that any I2C transfers caused by these subsystems are unlocked. This can be convoluted to accomplish, maybe even impossible if an acceptably clean solution is sought. |
Parent-locked Example
.----------. .--------. .--------. | parent- |-----| dev D1 | | root |--+--| locked | '--------' '--------' | | mux M1 |--. .--------. | '----------' '--| dev D2 | | .--------. '--------' '--| dev D3 | '--------'
当访问D1时,会发生如下情况:
- 有人向D1发出I2C传输
- M1在其父(本例中的根适配器)上锁定mux。
- M1锁定它的父适配器。
- M1调用 ->select 准备mux。
- 如果M1将任何I2C传输(在这个根适配器上)作为其选择的一部分,那么这些传输必须是未锁定的I2C传输,以便它们不会死锁根适配器。
- M1将第1步到根适配器的I2C传输作为未锁定的I2C传输提供,这样它就不会死锁父适配器。
- M1调用 ->deselect ,如果它有一个的话。
- 规则与第5步相同,但是对于 ->deselect。
- M1解锁它的父适配器。
- M1解锁其父结点的mux。
这意味着在整个操作期间,D2和D3的访问都是被锁定的。
Complex Examples
Parent-locked mux as parent of parent-locked mux
这是一个有用的拓扑,但也可能是糟糕的:
.----------. .----------. .--------. .--------. | parent- |-----| parent- |-----| dev D1 | | root |--+--| locked | | locked | '--------' '--------' | | mux M1 |--. | mux M2 |--. .--------. | '----------' | '----------' '--| dev D2 | | .--------. | .--------. '--------' '--| dev D4 | '--| dev D3 | '--------' '--------'
当任何设备被访问时,所有其他设备在整个操作期间都被锁定(mux锁定它们的父设备,特别是当M2请求其父设备锁定时,M1将责任传递给根适配器)。
如果M2是一个自动关闭的mux,而 M1->select 在根适配器上发出任何可能泄漏并被M2适配器看到的未解锁的I2C传输,从而过早关闭M2,那么这种拓扑就很糟糕。
Mux-locked mux as parent of mux-locked mux
这是一个很好的拓扑:
.----------. .----------. .--------. .--------. | mux- |-----| mux- |-----| dev D1 | | root |--+--| locked | | locked | '--------' '--------' | | mux M1 |--. | mux M2 |--. .--------. | '----------' | '----------' '--| dev D2 | | .--------. | .--------. '--------' '--| dev D4 | '--| dev D3 | '--------' '--------'
当设备D1被访问时,D2的访问在整个操作期间都被锁定(M1的顶部子适配器的mux被锁定)。但是对D3和D4的访问可能在任何一点都是交错的。对D3的访问锁定D1和D2,但对D4的访问仍然可能是交错的。
Mux-locked mux as parent of parent-locked mux
这可能是一个糟糕的拓扑:
.----------. .----------. .--------. .--------. | mux- |-----| parent- |-----| dev D1 | | root |--+--| locked | | locked | '--------' '--------' | | mux M1 |--. | mux M2 |--. .--------. | '----------' | '----------' '--| dev D2 | | .--------. | .--------. '--------' '--| dev D4 | '--| dev D3 | '--------' '--------'
当设备D1被访问时,D2和D3的访问在整个操作期间都被锁定(M1锁定根适配器上的子mux)。但是对D4的访问可能在任何一点都是交错的。
这种拓扑通常是不合适的,应该避免。原因是M2可能假设在它调用 ->select 和 ->deselect 的过程中不会有I2C传输,如果有的话,任何这样的传输可能会出现在M2的从端作为部分I2C传输,即垃圾或更糟。这可能会导致设备锁定和/或其他问题。
如果M2是一个自动关闭的mux,那么这种拓扑结构尤其麻烦。在这种情况下,对D4的任何交错访问都可能过早地关闭M2,就像 M1->select 的任何I2C传输部分一样。
但是如果M2没有做出上述假设,并且M2不是自动关闭的,那么拓扑是好的。
Parent-locked mux as parent of mux-locked mux
这是一个很好的拓扑:
.----------. .----------. .--------. .--------. | parent- |-----| mux- |-----| dev D1 | | root |--+--| locked | | locked | '--------' '--------' | | mux M1 |--. | mux M2 |--. .--------. | '----------' | '----------' '--| dev D2 | | .--------. | .--------. '--------' '--| dev D4 | '--| dev D3 | '--------' '--------'
当D1被访问时,D2的访问在整个操作期间都被锁定(M1的顶层子适配器上的mux被锁定)。D3和D4的访问可能在任何点交错,就像预期的mux-locked mux。
当D3或D4被访问时,其他所有内容都被锁定。对于D3访问,M1锁定根适配器。对于D4访问,根适配器是直接锁定的。
Two mux-locked sibling muxes
这是一个很好的拓扑:
.--------. .----------. .--| dev D1 | | mux- |--' '--------' .--| locked | .--------. | | mux M1 |-----| dev D2 | | '----------' '--------' | .----------. .--------. .--------. | | mux- |-----| dev D3 | | root |--+--| locked | '--------' '--------' | | mux M2 |--. .--------. | '----------' '--| dev D4 | | .--------. '--------' '--| dev D5 | '--------'
当D1被访问时,D2、D3和D4的访问被锁定。但是对D5的访问可以在任何时候交错。
Two parent-locked sibling muxes
这是一个很好的拓扑:
.--------. .----------. .--| dev D1 | | parent- |--' '--------' .--| locked | .--------. | | mux M1 |-----| dev D2 | | '----------' '--------' | .----------. .--------. .--------. | | parent- |-----| dev D3 | | root |--+--| locked | '--------' '--------' | | mux M2 |--. .--------. | '----------' '--| dev D4 | | .--------. '--------' '--| dev D5 | '--------'
当访问任何设备时,对其他设备的访问将被锁定。
Mux-locked and parent-locked sibling muxes
这是一个很好的拓扑:
.--------. .----------. .--| dev D1 | | mux- |--' '--------' .--| locked | .--------. | | mux M1 |-----| dev D2 | | '----------' '--------' | .----------. .--------. .--------. | | parent- |-----| dev D3 | | root |--+--| locked | '--------' '--------' | | mux M2 |--. .--------. | '----------' '--| dev D4 | | .--------. '--------' '--| dev D5 | '--------'
当D1或D2被访问时,对D3和D4的访问被锁定,而对D5的访问可能会交错。当D3或D4被访问时,对所有其他设备的访问被锁定。