I2C 多路複用器和複雜拓撲

構建比具有一個介面卡和一個或多個裝置的簡單 I2C 匯流排更復雜的 I2C 拓撲結構有兩個原因。

一些示例用例包括

  1. 可能需要在總線上使用多路複用器以防止地址衝突。

  2. 匯流排可能可以從一些外部匯流排主控器訪問,並且可能需要仲裁來確定是否可以訪問匯流排。

  3. 裝置(特別是 RF 調諧器)可能希望避免來自 I2C 匯流排的數字噪聲,至少在大多數情況下,並且位於一個門後面,該門必須在訪問裝置之前操作。

I2C 多路複用器、I2C 門和 I2C 仲裁器等幾種型別的硬體元件允許處理此類需求。

這些元件在 Linux 中表示為 I2C 介面卡樹,其中每個介面卡都有一個父介面卡(根介面卡除外)和零個或多個子介面卡。 根介面卡是實際發出 I2C 傳輸的介面卡,並且所有具有父介面卡的介面卡都是“i2c-mux”物件的一部分(帶引號,因為它也可以是仲裁器或門)。

根據特定的多路複用器驅動程式,當在其子介面卡之一上進行 I2C 傳輸時,會發生一些事情。 多路複用器驅動程式顯然可以操作多路複用器,但它也可以與外部匯流排主控器進行仲裁或開啟一個門。 多路複用器驅動程式有兩個操作可以執行此操作:select 和 deselect。 select 在傳輸之前呼叫,可選的 deselect 在傳輸之後呼叫。

鎖定

I2C 多路複用器有兩種鎖定變體,它們可以是多路複用器鎖定或父鎖定多路複用器。

多路複用器鎖定多路複用器

多路複用器鎖定多路複用器不會在完整的 select-transfer-deselect 事務期間鎖定整個父介面卡,只會鎖定父介面卡上的多路複用器。 如果 select 和/或 deselect 操作必須使用 I2C 傳輸才能完成其任務,則多路複用器鎖定多路複用器最有趣。 由於父介面卡在整個事務期間未完全鎖定,因此不相關的 I2C 傳輸可能會交錯事務的不同階段。 這有一個好處,即多路複用器驅動程式可能更容易且更乾淨地實現,但它有一些注意事項。

多路複用器鎖定示例

               .----------.     .--------.
.--------.     |   mux-   |-----| dev D1 |
|  root  |--+--|  locked  |     '--------'
'--------'  |  |  mux M1  |--.  .--------.
            |  '----------'  '--| dev D2 |
            |  .--------.       '--------'
            '--| dev D3 |
               '--------'

當訪問 D1 時,會發生以下情況

  1. 有人向 D1 發出 I2C 傳輸。

  2. M1 鎖定其父級(在本例中為根介面卡)上的多路複用器。

  3. M1 呼叫 ->select 以準備好多路複用器。

  4. M1(大概)進行一些 I2C 傳輸作為其 select 的一部分。 這些傳輸是鎖定父介面卡的正常 I2C 傳輸。

  5. M1 將步驟 1 中的 I2C 傳輸作為鎖定父介面卡的正常 I2C 傳輸饋送到其父介面卡。

  6. M1 呼叫 ->deselect,如果它有一個。

  7. 與步驟 4 中相同的規則,但對於 ->deselect。

  8. M1 解鎖其父級上的多路複用器。

這意味著對 D2 的訪問在整個操作的整個期間都被鎖定。 但是對 D3 的訪問可能在任何時候交錯。

多路複用器鎖定注意事項

使用多路複用器鎖定多路複用器時,請注意以下限制

[ML1]

如果您構建的拓撲結構中,多路複用器鎖定多路複用器是父鎖定多路複用器的父級,則可能會破壞父鎖定多路複用器的期望,即在事務期間根介面卡被鎖定。

[ML2]

當這些非同級多路複用器的子介面卡上的裝置之間存在地址衝突時,構建具有兩個(或更多)非同級多路複用器鎖定的多路複用器的任意拓撲結構是不安全的。

即,以例如裝置地址 0x42 為目標的多路複用器一後面的 select-transfer-deselect 事務可能會與以多路複用器二後面的裝置地址 0x42 為目標的類似操作交錯。 在這種假設示例中,這種拓撲結構的目的是多路複用器一和多路複用器二不應同時被選中,但多路複用器鎖定多路複用器不能保證在所有拓撲結構中都滿足這一點。

[ML3]

多路複用器鎖定多路複用器不能被驅動程式用於自動關閉門/多路複用器,即在給定數量(在大多數情況下為一個)I2C 傳輸後自動關閉的東西。 不相關的 I2C 傳輸可能會爬入並過早關閉。

[ML4]

如果多路複用器驅動程式中的任何非 I2C 操作更改了 I2C 多路複用器狀態,則驅動程式必須在該操作期間鎖定根介面卡。 否則,當不相關的 I2C 傳輸正在進行時,從多路複用器後面的裝置來看,總線上可能會出現垃圾。

父鎖定多路複用器

父鎖定多路複用器在完整的 select-transfer-deselect 事務期間鎖定父介面卡。 這意味著多路複用器驅動程式必須確保在事務期間透過該父介面卡的任何和所有 I2C 傳輸都是未鎖定的 I2C 傳輸(例如使用 __i2c_transfer),否則將發生死鎖。

父鎖定示例

               .----------.     .--------.
.--------.     |  parent- |-----| dev D1 |
|  root  |--+--|  locked  |     '--------'
'--------'  |  |  mux M1  |--.  .--------.
            |  '----------'  '--| dev D2 |
            |  .--------.       '--------'
            '--| dev D3 |
               '--------'

當訪問 D1 時,會發生以下情況

  1. 有人向 D1 發出 I2C 傳輸。

  2. M1 鎖定其父級(在本例中為根介面卡)上的多路複用器。

  3. M1 鎖定其父介面卡。

  4. M1 呼叫 ->select 以準備好多路複用器。

  5. 如果 M1 作為其 select 的一部分進行任何 I2C 傳輸(在此根介面卡上),則這些傳輸必須是未鎖定的 I2C 傳輸,這樣它們就不會使根介面卡死鎖。

  6. M1 將步驟 1 中的 I2C 傳輸作為未鎖定的 I2C 傳輸饋送到根介面卡,這樣它就不會使父介面卡死鎖。

  7. M1 呼叫 ->deselect,如果它有一個。

  8. 與步驟 5 中相同的規則,但對於 ->deselect。

  9. M1 解鎖其父介面卡。

  10. M1 解鎖其父級上的多路複用器。

這意味著對 D2 和 D3 的訪問在整個操作的整個期間都被鎖定。

父鎖定注意事項

使用父鎖定多路複用器時,請注意以下限制

[PL1]

如果您構建的拓撲結構中,父鎖定多路複用器是另一個多路複用器的子級,則可能會破壞子多路複用器的一個可能假設,即根介面卡在其 select 操作和實際傳輸之間未使用(例如,如果子多路複用器是自動關閉的,並且父多路複用器作為其 select 的一部分發出 I2C 傳輸)。 如果父多路複用器被多路複用器鎖定,則尤其如此,但如果父多路複用器被父鎖定,也可能會發生這種情況。

[PL2]

如果 select/deselect 呼叫了其他子系統,例如 gpio、pinctrl、regmap 或 iio,則必須解鎖由這些子系統引起的任何 I2C 傳輸。 完成此操作可能很複雜,如果尋求可接受的乾淨解決方案,甚至可能無法實現。

複雜示例

父鎖定多路複用器作為父鎖定多路複用器的父級

這是一個有用的拓撲結構,但它可能很糟糕

               .----------.     .----------.     .--------.
.--------.     |  parent- |-----|  parent- |-----| dev D1 |
|  root  |--+--|  locked  |     |  locked  |     '--------'
'--------'  |  |  mux M1  |--.  |  mux M2  |--.  .--------.
            |  '----------'  |  '----------'  '--| dev D2 |
            |  .--------.    |  .--------.       '--------'
            '--| dev D4 |    '--| dev D3 |
               '--------'       '--------'

當訪問任何裝置時,所有其他裝置都會在操作的整個期間被鎖定(兩個多路複用器都會鎖定它們的父級,特別是當 M2 請求其父級鎖定時,M1 會將責任傳遞給根介面卡)。

如果 M2 是一個自動關閉的多路複用器,並且 M1->select 在根介面卡上發出任何可能洩漏並透過 M2 介面卡看到的未鎖定 I2C 傳輸,從而過早關閉 M2,則此拓撲結構很糟糕。

多路複用器鎖定多路複用器作為多路複用器鎖定多路複用器的父級

這是一個很好的拓撲結構

               .----------.     .----------.     .--------.
.--------.     |   mux-   |-----|   mux-   |-----| dev D1 |
|  root  |--+--|  locked  |     |  locked  |     '--------'
'--------'  |  |  mux M1  |--.  |  mux M2  |--.  .--------.
            |  '----------'  |  '----------'  '--| dev D2 |
            |  .--------.    |  .--------.       '--------'
            '--| dev D4 |    '--| dev D3 |
               '--------'       '--------'

當訪問裝置 D1 時,對 D2 的訪問在操作的整個期間被鎖定(M1 的頂部子介面卡上的多路複用器被鎖定)。 但是對 D3 和 D4 的訪問可能在任何時候交錯。

對 D3 的訪問會鎖定 D1 和 D2,但對 D4 的訪問仍然可能交錯。

多路複用器鎖定多路複用器作為父鎖定多路複用器的父級

這可能是一個糟糕的拓撲結構

               .----------.     .----------.     .--------.
.--------.     |   mux-   |-----|  parent- |-----| dev D1 |
|  root  |--+--|  locked  |     |  locked  |     '--------'
'--------'  |  |  mux M1  |--.  |  mux M2  |--.  .--------.
            |  '----------'  |  '----------'  '--| dev D2 |
            |  .--------.    |  .--------.       '--------'
            '--| dev D4 |    '--| dev D3 |
               '--------'       '--------'

當訪問裝置 D1 時,對 D2 和 D3 的訪問在操作的整個期間被鎖定(M1 鎖定根介面卡上的子多路複用器)。 但是對 D4 的訪問可能在任何時候交錯。

這種拓撲結構通常不合適,可能應該避免。 原因在於,M2 可能假設在其 ->select 和 ->deselect 呼叫期間不會有 I2C 傳輸,並且如果有,任何此類傳輸都可能在 M2 的從屬端顯示為部分 I2C 傳輸,即垃圾或更糟。 這可能會導致裝置鎖定和/或其他問題。

如果 M2 是一個自動關閉的多路複用器,則拓撲結構尤其麻煩。 在這種情況下,對 D4 的任何交錯訪問都可能過早關閉 M2,M1->select 的任何 I2C 傳輸也可能導致這種情況。

但是,如果 M2 沒有做出上述假設,並且如果 M2 不是自動關閉的,則拓撲結構很好。

父鎖定多路複用器作為多路複用器鎖定多路複用器的父級

這是一個很好的拓撲結構

               .----------.     .----------.     .--------.
.--------.     |  parent- |-----|   mux-   |-----| dev D1 |
|  root  |--+--|  locked  |     |  locked  |     '--------'
'--------'  |  |  mux M1  |--.  |  mux M2  |--.  .--------.
            |  '----------'  |  '----------'  '--| dev D2 |
            |  .--------.    |  .--------.       '--------'
            '--| dev D4 |    '--| dev D3 |
               '--------'       '--------'

當訪問 D1 時,對 D2 的訪問在操作的整個期間被鎖定(M1 的頂部子介面卡上的多路複用器被鎖定)。 對 D3 和 D4 的訪問可能在任何時候交錯,正如多路複用器鎖定多路複用器所期望的那樣。

當訪問 D3 或 D4 時,所有其他裝置都會被鎖定。 對於 D3 訪問,M1 鎖定根介面卡。 對於 D4 訪問,直接鎖定根介面卡。

兩個多路複用器鎖定的同級多路複用器

這是一個很好的拓撲結構

                                .--------.
               .----------.  .--| dev D1 |
               |   mux-   |--'  '--------'
            .--|  locked  |     .--------.
            |  |  mux M1  |-----| dev D2 |
            |  '----------'     '--------'
            |  .----------.     .--------.
.--------.  |  |   mux-   |-----| dev D3 |
|  root  |--+--|  locked  |     '--------'
'--------'  |  |  mux M2  |--.  .--------.
            |  '----------'  '--| dev D4 |
            |  .--------.       '--------'
            '--| dev D5 |
               '--------'

當訪問 D1 時,對 D2、D3 和 D4 的訪問被鎖定。 但是對 D5 的訪問可能在任何時候交錯。

兩個父鎖定同級多路複用器

這是一個很好的拓撲結構

                                .--------.
               .----------.  .--| dev D1 |
               |  parent- |--'  '--------'
            .--|  locked  |     .--------.
            |  |  mux M1  |-----| dev D2 |
            |  '----------'     '--------'
            |  .----------.     .--------.
.--------.  |  |  parent- |-----| dev D3 |
|  root  |--+--|  locked  |     '--------'
'--------'  |  |  mux M2  |--.  .--------.
            |  '----------'  '--| dev D4 |
            |  .--------.       '--------'
            '--| dev D5 |
               '--------'

當訪問任何裝置時,對所有其他裝置的訪問都會被鎖定。

多路複用器鎖定和父鎖定同級多路複用器

這是一個很好的拓撲結構

                                .--------.
               .----------.  .--| 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 時,對所有其他裝置的訪問都會被鎖定。

現有裝置驅動程式的多路複用器型別

裝置是被多路複用器鎖定還是父鎖定取決於其實現。 以下列表在編寫時是正確的

在 drivers/i2c/muxes/ 中

i2c-arb-gpio-challenge

父鎖定

i2c-mux-gpio

通常是父鎖定,如果所有涉及的 gpio 引腳都由它們複用的同一個 I2C 根介面卡控制,則為多路複用器鎖定。

i2c-mux-gpmux

通常是父鎖定,如果在裝置樹中指定,則為多路複用器鎖定。

i2c-mux-ltc4306

多路複用器鎖定

i2c-mux-mlxcpld

父鎖定

i2c-mux-pca9541

父鎖定

i2c-mux-pca954x

父鎖定

i2c-mux-pinctrl

通常是父鎖定,如果所有涉及的 pinctrl 裝置都由它們複用的同一個 I2C 根介面卡控制,則為多路複用器鎖定。

i2c-mux-reg

父鎖定

在 drivers/iio/ 中

gyro/mpu3050

多路複用器鎖定

imu/inv_mpu6050/

多路複用器鎖定

在 drivers/media/ 中

dvb-frontends/lgdt3306a

多路複用器鎖定

dvb-frontends/m88ds3103

父鎖定

dvb-frontends/rtl2830

父鎖定

dvb-frontends/rtl2832

多路複用器鎖定

dvb-frontends/si2168

多路複用器鎖定

usb/cx231xx/

父鎖定