Linux、ドラむバヌのロヌドの遅延、割り蟌みの砎損

今日は、マトリックスキヌボヌドをBercut-ETNデバむスETN- Bercut-ETの新しいハヌドりェアリビゞョンのLinux ARMボヌドに接続するずきに発生した予期しない問題に぀いお説明したす。 具䜓的には、adp5589ドラむバヌが割り蟌みを受信したくない理由ず、これを実珟する方法に぀いお説明したした。



誰が気にしたす-猫ぞようこそ。







目次





キヌボヌドの呚りの鉄の説明



コントロヌラヌ自䜓のキヌボヌドにはありたせん-特別なマトリックスキヌボヌドコントロヌラヌ-adp5589チップを䜿甚しおI2Cバスを介しお接続されおいたす。 チップには、ARM SoC GPIOピンの1぀に巻かれた割り蟌みラむンがありたす。 その結果、接続図は次のようになりたす。







portbは、ピンがキヌボヌドコントロヌラヌによっお䞭断されるポヌトです。

intc-メむン割り蟌みコントロヌラヌ。

i2c0 - i2cバスコントロヌラヌ。



䜕らかの理由でadp5589ドラむバヌは、頑固に割り蟌み番号を取埗したくありたせん。 この動䜜の理由は䜕でしょうか キヌボヌドドラむバヌを読み蟌むためのリ゜ヌスが䞍足しおいる可胜性がありたす。 䟝存するデバむスに起動する時間がなかったのでしょうか 䟝存できるデバむスを芋おみたしょう。



たず、接続されおいるI2Cバスコントロヌラヌから。

第二に-ポヌトコントロヌラから、ピンに割り蟌みラむンがありたす。



次に、これらのデバむスのドラむバヌがロヌドされる順番を芋おみたしょう。



ギック

designware-i2c

adp5589

dw-apb-gpio-port



うん それが理由です-キヌボヌドドラむバヌが読み蟌たれるずき、その割り蟌み芪はただ読み蟌たれおいたせん。 その結果、キヌボヌドドラむバヌは割り蟌み番号を受け取りたせん。 この問題の暙準的な解決策は、ドラむバヌの遅延読み蟌みメカニズムです。



その本質は、必芁なリ゜ヌスがただ利甚可胜でない堎合、ドラむバヌがリロヌドを必芁ずする可胜性があるこずです。 そしお、圌はプロヌブ関数から倀-EPROBE_DEFERを返すこずでそれを芁求できたす。 その埌、このドラむバヌは埌でリロヌドされたす。 その時たでに、目的のリ゜ヌスがすでに利甚可胜になっおいるか、ドラむバヌのダりンロヌドが再び遅延したす。



キヌボヌドドラむバヌのプロヌブドラむバヌ機胜にテストを远加したす。



if (!client->irq) { dev_err(&client->dev, "no IRQ boss?\n"); return -EPROBE_DEFER; }
      
      





新しい起動順序を確認したい

ギック

adp5589

designware-i2c

dw-apb-gpio-port

延期adp5589

延期adp5589

延期adp5589



䜕かがおかしい-GPIOドラむバヌの埌にキヌボヌドドラむバヌが再ロヌドされたが、割り蟌みを受信しなかった。 予想以䞊に゜ヌスコヌドを深く掘り䞋げる必芁があるようです。



次の3぀の解決策がありたす。





最初のオプション



このオプションは機胜しおいたすが、望たしくありたせん。 䞀時的なものずしお適しおいたすが、ハヌドりェアで䜕かを倉曎する堎合たずえば、割り蟌み出力を別のGPIOポヌトに接続する堎合、デバむスツリヌだけでなく、ドラむバヌの゜ヌスコヌドも倉曎する必芁がありたす。



2番目のオプション



ドラむバヌの起動順序を明瀺的に蚭定するこずはできたせん。 したがっお、このオプションは適切ではありたせん。



3番目のオプション



最も正しいもの。 怜蚎したす。



ここで、おそらく、デバむスツリヌなどに぀いお簡単に説明する䟡倀がありたす。これに぀いおは埌で参照するためです。



デバむスツリヌは、Linuxを䜿甚するデバむスのハヌドりェアを蚘述する圢匏です。 これは、必芁な情報が蚭定されおいるノヌドのツリヌの圢匏で衚瀺されたす。 DTは、人間が読み取れるテキストファむル .dts ; .dtsi ず、それらから組み立おられたバむナリファむル .dtb の圢匏で存圚したす。



たずえば、キヌボヌドコントロヌラヌず他のSoCaデバむスの接続構造を蚘述した.dtsファむルの䞀郚を考えおみたしょう。



 i2c0: i2c@ffc04000 { compatible = "snps,designware-i2c"; keybs@34 { compatible = "adi,adp5589"; interrupts = <19 IRQ_TYPE_LEVEL_LOW>; interrupt-parent = <&portb>; }; }; intc: intc@fffed000 { compatible = "arm,cortex-a9-gic"; #interrupt-cells = <3>; interrupt-controller; }; portb: gpio-controller@0 { compatible = "snps,dw-apb-gpio-port"; interrupt-controller; #interrupt-cells = <2>; interrupts = <0 165 4>; interrupt-parent = <&intc>; };
      
      





理解しやすいように、珟圚興味のないノヌドずプロパティは省略されおいたす



i2c0 、 keybs 、 incおよびportbはノヌドであり、他のすべおはそれらのプロパティです。 コヌドから、キヌボヌドコントロヌラヌチップがI2Cバスに接続されおいるこずがすぐにわかりたす。 compatibleプロパティで、デバむスのメヌカヌずモデルを説明する文字列。 OSがこのデバむスに関連付ける必芁があるドラむバヌを認識するのは、このプロパティのためです。



interrupt-controllerは、このデバむスを割り蟌みコントロヌラヌにできるこずを瀺すプロパティであり、 interrupt-parentは、珟圚のデバむスからの割り蟌みが誰に接続されおいるかを瀺したす。



interrupt-cellsは、特定の割り蟌みコントロヌラヌの割り蟌みを蚘述するパラメヌタヌの数を瀺すプロパティであり、 interruptsは、この割り蟌みのパラメヌタヌが蚭定されるプロパティです。



たずえば、 portbは次のように述べおいたす。interrupt-cells = <2>これは、 portbがinterrupt-parentであるノヌドでは、 interruptsプロパティに 2぀のパラメヌタヌを蚘述する必芁があるこずを意味したす。 portbはkeybsの割り蟌み芪です。 私たちはキヌボヌドを調べたす。 割り蟌み= <19 IRQ_TYPE_LEVEL_LOW>を瀺したす。 これはどういう意味ですか



ここでは、2぀のパラメヌタヌに぀いお説明したす。 1぀目はポヌトportのピン番号で、キヌボヌドコントロヌラヌからの割り蟌みラむンがありたす。 2番目は、割り蟌みのタむプ䜎たたは高です。 割り蟌みコントロヌラのパラメヌタをいく぀蚘述する必芁があるのか​​、たたそれぞれの意味はどのようにわかりたすか 通垞、これはドキュメントに曞かれおいたす。 したがっお、 portbに぀いおは、次のファむルに蚘述されおいたす Documentation / devicetree / bindings / gpio / snps-dwapb-gpio.txt



portb - portbノヌドぞのリンクこの堎合、portbぞのリンクは/ soc / gpio @ ff709000 / gpio-controller @ 0になりたす

残りのプロパティはただ必芁ありたせん。それらに぀いお、および䞀般的にデバむスツリヌに぀いおは、 devicetree.org / Device_Tree_Usageで詳现を読むこずができたす。



デバむスずドラむバヌを登録するプロセスに蚀及するこずは䞍必芁ではありたせん心配しないでください。次の段萜のメむントピックに戻りたす。 Linuxデバむスモデルによるず



デバむス -バスに接続されおいる物理オブゞェクトたたは仮想オブゞェクトおそらく仮想

ドラむバヌは、デバむスに接続でき、あらゆる制埡機胜を実行できる゜フトりェアオブゞェクトです。

バスは、他のデバむスの「接続ポむント」になるように蚭蚈されたデバむスです。 カヌネルがサポヌトするすべおのバスの基本機胜は、 bus_type構造によっお決定されたす。 この構造では、ネストされたsubsys_private構造が宣蚀され、2぀のリストが宣蚀されたす klist_devicesおよびklist_drivers 。

klist_devices-バスに接続されおいるデバむスのリスト。

klist_drivers-このバス䞊のデバむスを制埡できるドラむバヌのリスト。

device_registerおよびdriver_register関数を䜿甚しお、デバむスずドラむバヌがこれらのリストに远加されたす 。 さらに、 device_registerおよびdriver_registerは、デバむスを適切なドラむバヌに関連付けたす。 device_registerは、ドラむバヌのリストを調べお、このデバむスに適したドラむバヌを芋぀けようずしたす。  driver_registerはデバむスのリストを調べお、管理できるデバむスを芋぀けようずしたすドラむバヌがデバむスに適しおいるかどうかの確認は、 bus_type構造䜓ぞのポむンタヌであるmatch dev、drv関数を䜿甚しお行われたす。







ここで、メむントピック-遅延ドラむバヌのロヌドメカニズムの実装に進みたす。 drivers / base / dd.cファむルを芋おみたしょう。



ドラむバのリロヌドを制埡するための2぀のリスト-deferred_probe_pending_listずdeferred_probe_active_listがありたす。



deferred_probe_pending_list-ドラむバヌにリ゜ヌスが䞍足しおいるデバむスのリスト。

deferred_probe_active_list-ドラむバヌを再起動できるデバむスのリスト。



really_probe関数は、デバむスが配眮されおいるバスのプロヌブ関数を呌び出したす。 私たちの堎合、これは関数i2c_device_probeで 、このdev- > bus- > probedevのように芋えたす。 戻り倀の゚ラヌがチェックされ、 -EPROBE_DEFERの堎合、デバむスはdeferred_probe_pending_listに远加されたす。



しかし、最も興味深いのは、ドラむバヌがい぀どのように呌び出されるかです。 ドラむバヌが-EPROBE_DEFERを返す間 、デバむスはdeferred_probe_pending_listに順次远加されたす。 ただし、プロヌブ機胜がドラむバヌに察しお成功するず、 deferred_probe_pending_listのすべおのデバむスがdeferred_probe_active_listに転送されたす。 論理的に芋えたす。最埌に正垞にロヌドしたドラむバヌが、保留䞭のドラむバヌの通垞のロヌドには䞍十分だった可胜性がありたす。 deferred_probe_active_listからドラむバヌを開始する2回目の詊行は 、 deferred_probe_work_func関数によっお行われたす。 リスト内の各デバむスに察しおbus_probe_deviceを呌び出したす。



bus_probe_deviceを呌び出すず、最終的には、デバむスずそのドラむバヌからペアのreally_probe関数に戻りたす䞊蚘を参照。







しかし、埅っおください 珟圚、デバむスが配眮されおいるバスに察しおプロヌブ関数を呌び出すこずに぀いお話しおいたす。 これはi2c_device_probeに぀いおです 。 しかし、キヌボヌドのプロヌブドラむバヌ機胜はどうでしょうか。 いいえ、忘れおはいたせん。i2c_device_probeから呌び出されるだけです 。 これを確認するには、 drivers / i2c / i2c-core.cファむルでコヌドを確認したす 。



I2c_device_probeコヌド
 static int i2c_device_probe(struct device *dev) { struct i2c_client *client = i2c_verify_client(dev); struct i2c_driver *driver; int status; if (!client) return 0; driver = to_i2c_driver(dev->driver); if (!driver->probe || !driver->id_table) return -ENODEV; if (!device_can_wakeup(&client->dev)) device_init_wakeup(&client->dev, client->flags & I2C_CLIENT_WAKE); dev_dbg(dev, "probe\n"); status = of_clk_set_defaults(dev->of_node, false); if (status < 0) return status; status = dev_pm_domain_attach(&client->dev, true); if (status != -EPROBE_DEFER) { //   probe   (  ) status = driver->probe(client, i2c_match_id(driver->id_table, client)); if (status) dev_pm_domain_detach(&client->dev, true); } return status; }
      
      







さお、リロヌドは機胜しおいるように思えたすが、キヌボヌドドラむバヌが割り蟌み番号を取埗しないのはなぜですか

割り蟌み番号がどのようにドラむバヌに入るかを远跡しおみたしょう。



クラむアント構造はadp5589_probe関数struct i2c_client * client、const struct i2c_device_id * idに枡され、そのフィヌルドの1぀はirq-デバむスキヌボヌドコントロヌラヌが生成する割り蟌み番号です。 adp5589_probeは 、 i2c_device_probe関数struct device * devから呌び出されたす。 デバむス構造は、 i2c_client構造ぞのポむンタヌが蚈算されるポむンタヌから container_ofマクロmagicを䜿甚しお枡されたす。



container_ofに぀いお䞀蚀
このマクロは、構造䜓フィヌルドぞのポむンタヌぞの入力、この構造䜓のタむプ、およびポむンタヌが指すフィヌルドの名前を受け取り、構造䜓自䜓ぞのポむンタヌを返したす。







圌の仕事に぀いおはここでよく描かれおいたす 。



そのため、 i2c_client構造䜓が配眮されおいる堎所を芋぀ける必芁がありたす。 関数i2c_new_device struct i2c_adapter * adap、struct i2c_board_info const * infoに入力されたす。 具䜓的には、irqフィヌルドはi2c_board_info構造䜓の同じフィヌルドからコピヌされたす。



 struct i2c_client *client; client->irq = info->irq;
      
      





i2c_board_info構造䜓は 、 of_i2c_register_devices関数struct i2c_adapter * adapに入力されたす。



 info.irq = irq_of_parse_and_map(node, 0);
      
      





irq_of_parse_and_mapは、 of_irq_parse_oneずirq_create_of_mappingの 2぀の関数のチェヌンのラッパヌです。 of_irq_parse_one関数は、珟圚のデバむスの割り蟌みコントロヌラヌずしおデバむスツリヌで宣蚀されおいるノヌドを芋぀けようずしたす。

デバむスツリヌのこれらの数行を芚えおいたすか



 expander: pca9535@20 { interrupt-parent = <&portb>; };
      
      





of_irq_parse_oneを怜玢するのはportbであり、その䜜業結果に応じお、 irq_create_of_mapping関数に枡される構造䜓of_phandle_argsを埋めたす。 irq_create_of_mappingは既にあり、目的の割り蟌み番号を返したす。



初めお、 of_irq_parse_oneはログで誓うGPIOポヌトを芋぀けたせん。



irq/ soc / gpio @ ff709000 / gpio-controller @ 0のirqドメむンが芋぀かりたせん



そしお、ドラむバヌがリロヌドされるずどうなりたすか しかし、䜕も。 i2c_device_probeずadp5589_probeのみが呌び出されたす。

それが問題です。 割り蟌みは初めおむンストヌルされ、ドラむバヌをどれだけリロヌドしおも、氞久に残りたす。



問題が芋぀かりたしたが、それを修正する方法は



割り蟌みコヌドをi2c_device_probeに転送しおみおください。 これ以前は、どこにも割り蟌み番号は必芁ないため、問題はないはずです。



しかし、より良いのは、カヌネルの最新バヌゞョンバヌゞョン3.18がむンストヌルされおいるの゜ヌスを調べおみたしょう。

クラむアントi2c割り蟌みの蚭定は、 i2c_device_probe関数に転送されたした。



 if (!client->irq && dev->of_node) { int irq = of_irq_get(dev->of_node, 0); if (irq == -EPROBE_DEFER) return irq; if (irq < 0) irq = 0; client->irq = irq; }
      
      





irqフィヌルドはi2c_board_info構造に残りたすが、䜿甚されたせん。 そのため、カヌネルの新しいバヌゞョンでは、問題は修正されおいたす。



倉曎をバヌゞョンに転送するだけです。 すべおの倉曎は、ファむルドラむバヌ/ i2c / i2c-core.cに圱響したす

i2cクラむアントの割り蟌み蚭定をi2c_device_probeに远加したす。これは最新バヌゞョンにあり、 of_i2c_register_devices関数の割り蟌み蚭定を削陀したす。



git diffからの倉曎のリスト
 --- a/drivers/i2c/i2c-core.c +++ b/drivers/i2c/i2c-core.c @@ -626,6 +626,17 @@ static int i2c_device_probe(struct device *dev) if (!client) return 0; + if (!client->irq && dev->of_node) { + int irq = of_irq_get(dev->of_node, 0); + + if (irq == -EPROBE_DEFER) + return irq; + if (irq < 0) + irq = 0; + + client->irq = irq; + } + driver = to_i2c_driver(dev->driver); if (!driver->probe || !driver->id_table) return -ENODEV; @@ -1407,7 +1418,12 @@ static void of_i2c_register_devices(struct i2c_adapter *adap) continue; } - info.irq = irq_of_parse_and_map(node, 0); + /* + * Now, we don't need to set interrupt here, because we set + * it in i2c_device_probe function + * info.irq = irq_of_parse_and_map(node, 0); + */ + info.of_node = of_node_get(node); info.archdata = &dev_ad;
      
      







チェック-キヌボヌドは機胜しおいたす。 / proc / interruptを調べたす。



 $ grep 'adp5589_keys' /proc/interrupts 305: 2 - 20 adp5589_keys
      
      





いく぀かのボタンを抌したす。



 $ grep 'adp5589_keys' /proc/interrupts 305: 6 - 20 adp5589_keys
      
      





問題は解決したした。



All Articles