| ============ |
| I2C topology |
| ============ |
| |
| There are a couple of reasons for building more complex i2c topologies |
| than a straight-forward i2c bus with one adapter and one or more devices. |
| |
| 1. A mux may be needed on the bus to prevent address collisions. |
| |
| 2. The bus may be accessible from some external bus master, and arbitration |
| may be needed to determine if it is ok to access the bus. |
| |
| 3. A device (particularly RF tuners) may want to avoid the digital noise |
| from the i2c bus, at least most of the time, and sits behind a gate |
| that has to be operated before the device can be accessed. |
| |
| Etc |
| === |
| |
| These constructs are represented as i2c adapter trees by Linux, where |
| each adapter has a parent adapter (except the root adapter) and zero or |
| more child adapters. The root adapter is the actual adapter that issues |
| i2c transfers, and all adapters with a parent are part of an "i2c-mux" |
| object (quoted, since it can also be an arbitrator or a gate). |
| |
| Depending of the particular mux driver, something happens when there is |
| an i2c transfer on one of its child adapters. The mux driver can |
| obviously operate a mux, but it can also do arbitration with an external |
| bus master or open a gate. The mux driver has two operations for this, |
| select and deselect. select is called before the transfer and (the |
| optional) deselect is called after the transfer. |
| |
| |
| Locking |
| ======= |
| |
| There are two variants of locking available to i2c muxes, they can be |
| mux-locked or parent-locked muxes. As is evident from below, it can be |
| useful to know if a mux is mux-locked or if it is parent-locked. The |
| following list was correct at the time of writing: |
| |
| In 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 |
| ====================== ============================================= |
| |
| In drivers/iio/: |
| |
| ====================== ============================================= |
| gyro/mpu3050 Mux-locked |
| imu/inv_mpu6050/ Mux-locked |
| ====================== ============================================= |
| |
| In 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 |
| ---------------- |
| |
| Mux-locked muxes does not lock the entire parent adapter during the |
| full select-transfer-deselect transaction, only the muxes on the parent |
| adapter are locked. Mux-locked muxes are mostly interesting if the |
| select and/or deselect operations must use i2c transfers to complete |
| their tasks. Since the parent adapter is not fully locked during the |
| full transaction, unrelated i2c transfers may interleave the different |
| stages of the transaction. This has the benefit that the mux driver |
| may be easier and cleaner to implement, but it has some caveats. |
| |
| ==== ===================================================================== |
| 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 | |
| '--------' |
| |
| When there is an access to D1, this happens: |
| |
| 1. Someone issues an i2c-transfer to D1. |
| 2. M1 locks muxes on its parent (the root adapter in this case). |
| 3. M1 calls ->select to ready the mux. |
| 4. M1 (presumably) does some i2c-transfers as part of its select. |
| These transfers are normal i2c-transfers that locks the parent |
| adapter. |
| 5. M1 feeds the i2c-transfer from step 1 to its parent adapter as a |
| normal i2c-transfer that locks the parent adapter. |
| 6. M1 calls ->deselect, if it has one. |
| 7. Same rules as in step 4, but for ->deselect. |
| 8. M1 unlocks muxes on its parent. |
| |
| This means that accesses to D2 are lockout out for the full duration |
| of the entire operation. But accesses to D3 are possibly interleaved |
| at any point. |
| |
| |
| Parent-locked muxes |
| ------------------- |
| |
| Parent-locked muxes lock the parent adapter during the full select- |
| transfer-deselect transaction. The implication is that the mux driver |
| has to ensure that any and all i2c transfers through that parent |
| adapter during the transaction are unlocked i2c transfers (using e.g. |
| __i2c_transfer), or a deadlock will follow. There are a couple of |
| caveats. |
| |
| ==== ==================================================================== |
| 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 issus 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 | |
| '--------' |
| |
| When there is an access to D1, this happens: |
| |
| 1. Someone issues an i2c-transfer to D1. |
| 2. M1 locks muxes on its parent (the root adapter in this case). |
| 3. M1 locks its parent adapter. |
| 4. M1 calls ->select to ready the mux. |
| 5. If M1 does any i2c-transfers (on this root adapter) as part of |
| its select, those transfers must be unlocked i2c-transfers so |
| that they do not deadlock the root adapter. |
| 6. M1 feeds the i2c-transfer from step 1 to the root adapter as an |
| unlocked i2c-transfer, so that it does not deadlock the parent |
| adapter. |
| 7. M1 calls ->deselect, if it has one. |
| 8. Same rules as in step 5, but for ->deselect. |
| 9. M1 unlocks its parent adapter. |
| 10. M1 unlocks muxes on its parent. |
| |
| |
| This means that accesses to both D2 and D3 are locked out for the full |
| duration of the entire operation. |
| |
| |
| Complex Examples |
| ================ |
| |
| Parent-locked mux as parent of parent-locked mux |
| ------------------------------------------------ |
| |
| This is a useful topology, but it can be bad:: |
| |
| .----------. .----------. .--------. |
| .--------. | parent- |-----| parent- |-----| dev D1 | |
| | root |--+--| locked | | locked | '--------' |
| '--------' | | mux M1 |--. | mux M2 |--. .--------. |
| | '----------' | '----------' '--| dev D2 | |
| | .--------. | .--------. '--------' |
| '--| dev D4 | '--| dev D3 | |
| '--------' '--------' |
| |
| When any device is accessed, all other devices are locked out for |
| the full duration of the operation (both muxes lock their parent, |
| and specifically when M2 requests its parent to lock, M1 passes |
| the buck to the root adapter). |
| |
| This topology is bad if M2 is an auto-closing mux and M1->select |
| issues any unlocked i2c transfers on the root adapter that may leak |
| through and be seen by the M2 adapter, thus closing M2 prematurely. |
| |
| |
| Mux-locked mux as parent of mux-locked mux |
| ------------------------------------------ |
| |
| This is a good topology:: |
| |
| .----------. .----------. .--------. |
| .--------. | mux- |-----| mux- |-----| dev D1 | |
| | root |--+--| locked | | locked | '--------' |
| '--------' | | mux M1 |--. | mux M2 |--. .--------. |
| | '----------' | '----------' '--| dev D2 | |
| | .--------. | .--------. '--------' |
| '--| dev D4 | '--| dev D3 | |
| '--------' '--------' |
| |
| When device D1 is accessed, accesses to D2 are locked out for the |
| full duration of the operation (muxes on the top child adapter of M1 |
| are locked). But accesses to D3 and D4 are possibly interleaved at |
| any point. Accesses to D3 locks out D1 and D2, but accesses to D4 |
| are still possibly interleaved. |
| |
| |
| Mux-locked mux as parent of parent-locked mux |
| --------------------------------------------- |
| |
| This is probably a bad topology:: |
| |
| .----------. .----------. .--------. |
| .--------. | mux- |-----| parent- |-----| dev D1 | |
| | root |--+--| locked | | locked | '--------' |
| '--------' | | mux M1 |--. | mux M2 |--. .--------. |
| | '----------' | '----------' '--| dev D2 | |
| | .--------. | .--------. '--------' |
| '--| dev D4 | '--| dev D3 | |
| '--------' '--------' |
| |
| When device D1 is accessed, accesses to D2 and D3 are locked out |
| for the full duration of the operation (M1 locks child muxes on the |
| root adapter). But accesses to D4 are possibly interleaved at any |
| point. |
| |
| This kind of topology is generally not suitable and should probably |
| be avoided. The reason is that M2 probably assumes that there will |
| be no i2c transfers during its calls to ->select and ->deselect, and |
| if there are, any such transfers might appear on the slave side of M2 |
| as partial i2c transfers, i.e. garbage or worse. This might cause |
| device lockups and/or other problems. |
| |
| The topology is especially troublesome if M2 is an auto-closing |
| mux. In that case, any interleaved accesses to D4 might close M2 |
| prematurely, as might any i2c-transfers part of M1->select. |
| |
| But if M2 is not making the above stated assumption, and if M2 is not |
| auto-closing, the topology is fine. |
| |
| |
| Parent-locked mux as parent of mux-locked mux |
| --------------------------------------------- |
| |
| This is a good topology:: |
| |
| .----------. .----------. .--------. |
| .--------. | parent- |-----| mux- |-----| dev D1 | |
| | root |--+--| locked | | locked | '--------' |
| '--------' | | mux M1 |--. | mux M2 |--. .--------. |
| | '----------' | '----------' '--| dev D2 | |
| | .--------. | .--------. '--------' |
| '--| dev D4 | '--| dev D3 | |
| '--------' '--------' |
| |
| When D1 is accessed, accesses to D2 are locked out for the full |
| duration of the operation (muxes on the top child adapter of M1 |
| are locked). Accesses to D3 and D4 are possibly interleaved at |
| any point, just as is expected for mux-locked muxes. |
| |
| When D3 or D4 are accessed, everything else is locked out. For D3 |
| accesses, M1 locks the root adapter. For D4 accesses, the root |
| adapter is locked directly. |
| |
| |
| Two mux-locked sibling muxes |
| ---------------------------- |
| |
| This is a good topology:: |
| |
| .--------. |
| .----------. .--| dev D1 | |
| | mux- |--' '--------' |
| .--| locked | .--------. |
| | | mux M1 |-----| dev D2 | |
| | '----------' '--------' |
| | .----------. .--------. |
| .--------. | | mux- |-----| dev D3 | |
| | root |--+--| locked | '--------' |
| '--------' | | mux M2 |--. .--------. |
| | '----------' '--| dev D4 | |
| | .--------. '--------' |
| '--| dev D5 | |
| '--------' |
| |
| When D1 is accessed, accesses to D2, D3 and D4 are locked out. But |
| accesses to D5 may be interleaved at any time. |
| |
| |
| Two parent-locked sibling muxes |
| ------------------------------- |
| |
| This is a good topology:: |
| |
| .--------. |
| .----------. .--| dev D1 | |
| | parent- |--' '--------' |
| .--| locked | .--------. |
| | | mux M1 |-----| dev D2 | |
| | '----------' '--------' |
| | .----------. .--------. |
| .--------. | | parent- |-----| dev D3 | |
| | root |--+--| locked | '--------' |
| '--------' | | mux M2 |--. .--------. |
| | '----------' '--| dev D4 | |
| | .--------. '--------' |
| '--| dev D5 | |
| '--------' |
| |
| When any device is accessed, accesses to all other devices are locked |
| out. |
| |
| |
| Mux-locked and parent-locked sibling muxes |
| ------------------------------------------ |
| |
| This is a good topology:: |
| |
| .--------. |
| .----------. .--| dev D1 | |
| | mux- |--' '--------' |
| .--| locked | .--------. |
| | | mux M1 |-----| dev D2 | |
| | '----------' '--------' |
| | .----------. .--------. |
| .--------. | | parent- |-----| dev D3 | |
| | root |--+--| locked | '--------' |
| '--------' | | mux M2 |--. .--------. |
| | '----------' '--| dev D4 | |
| | .--------. '--------' |
| '--| dev D5 | |
| '--------' |
| |
| When D1 or D2 are accessed, accesses to D3 and D4 are locked out while |
| accesses to D5 may interleave. When D3 or D4 are accessed, accesses to |
| all other devices are locked out. |