i2c登録プロセス

Linuxでのi2cドライバーノート

https://www.cnblogs.com/shmily-linux/archive/2013/10/07/3356306.html

1.いくつかの基本的な概念

1.1。デバイスモデル

バス(bus_type)+デバイス(device)+ドライバー(device_driver)で構成されます。このモデルでは、すべてのデバイスがバスを介して接続されます。一部のデバイスが物理バスに接続されていない場合でも、linuxは内部の仮想プラットフォームを設定します。バス、バス、ドライバー、および機器間の関係を維持するために使用されます。

したがって、Linuxでのデバイスドライバーの実現は、2つの主要なステップに分けることができます。

1.機器の登録;

2.ドライバー登録。

 

もちろん、いくつかの詳細があります:

1.ドリブンプローブ機能

2.ドライバーとデバイスはどのようにバインドされていますか?

1.2.i2cデバイスドライバーのいくつかのデータ構造

 

i2c_adapter:

各i2c_adapterは、i2cバスドライバーのプローブ機能で動的に作成される物理i2cコントローラーに対応します。i2c_add_adapterを介してi2c_coreに登録します。

 

i2c_algorithm:

i2c_algorithmのキー関数master_xfer()は、i2c_msgの単位でi2cアクセスに必要な信号を生成します。異なるプラットフォームに対応するmaster_xfer()は異なります。i2c_algorithmのmaster_xferポインターを埋めるために使用されるプラットフォームのハードウェア特性に応じて、独自のxxx_xfer()メソッドを実装する必要があります。A31では、これはsun6i_i2c_algorithm関数です。

 

i2c_client:

i2cバスにマウントされたi2cスレーブデバイスを表し、デバイスに必要なデータを含みます。

i2cスレーブデバイスが接続されているi2cコントローラーstructi2c_adapter * adapter

i2cスレーブデバイスのドライバーstructi2c_driver * driver

i2cスレーブデバイスのアクセスアドレスアドレス、名前

i2cスレーブデバイスの名前。

2.i2cバスドライバー

2.1。機能分割

ハードウェア機能から、i2cコントローラーとi2cペリフェラル(スレーブデバイス)に分けることができます。複数のi2cペリフェラルを各i2cコントローラーバスに取り付けることができます。Linuxでのi2cコントローラーと周辺機器の個別管理:i2c-sun6i.cファイルはi2cコントローラーデバイスの登録とドライバー登録を完了します; i2c-core.cは特定のi2c周辺機器用の統合デバイス登録インターフェイスとドライバー登録インターフェイスを提供し、実装の詳細を分離しますデバイスドライバとハードウェア制御(i2cレジスタの操作など)。

2.2。i2c-sun6i.c

このファイルは特定のハードウェアプラットフォームに関連しており、A3xシリーズのチップに対応しています。このファイルは、実際にはi2cバスドライバーの実現であり、基本的にi2cバスデバイスをカーネルに登録し、バスドライバーを登録し、バス送信のタイミング制御アルゴリズムを実現します。i2cコントローラーは次のようにプラットフォームデバイスとして登録されます。

コードをコピーする

    if(twi_used_mask&TWI0_USED_MASK)
        platform_device_register(&sun6i_twi0_device); 
 
    if(twi_used_mask&TWI1_USED_MASK)
        platform_device_register(&sun6i_twi1_device); 
 
    if(twi_used_mask&TWI2_USED_MASK)
        platform_device_register(&sun6i_twi2_device); 
 
    if(twi_used_mask&TWI3_USED_MASK)
        platform_device_register(&sun6i_twi3_device); 
 
    if(twi_used_mask)
        return platform_driver_register(&sun6i_i2c_driver);

コードをコピーする

 

デバイスとドライバー間の対応は多対1であることに注意してください。つまり、デバイスタイプが同じ場合、それらは同じドライバーセットを共有するため、上記のコードはドライバーplatform_driver_register(&sun6i_i2c_driver)のみを登録します。一度。 

 

デバイス登録:

i2cコントローラーデバイスをプラットフォームデバイスとして登録し、コントローラーごとにstruct platform_deviceデータ構造を定義し、.nameを「sun6i-i2c」に設定して(ドライバーは後で名前と一致します)、platform_device_register()を呼び出します。プラットフォームバス上のデバイス。

 

デバイスの登録が完了すると、その直感的なパフォーマンスがファイルシステムの下に表示されます:/sys/bus/platform/devices/sun6i-i2c.0

 

platform_device_register()による登録プロセスは、最終的な分析では、struct platform_deviceのデータ構造への変更であり、.dev.parent、.dev.kobj、および.dev.busの割り当てが徐々に完了し、次に.devが追加されます。 .kobjからplatform_bus-> kobjのリンクリスト。

 

ドライバー登録:

手順はデバイス登録の手順と似ていますが、ドライバーのデータ構造も定義します。

struct platform_driver sun6i_i2c_driver;

 

1つのドライバーが複数のデバイスに対応でき、システム内の3つのコントローラーは基本的に同じであるため(違いはレジスターのアドレスが異なることです)、上記で登録された3つのデバイスは同じドライバーのセットを共有します。

 

.probe関数と.remove関数を初期化し、platform_driver_registerを呼び出してドライバーを登録します。主な関数呼び出しプロセス:

platform_driver_register-> driver_register-> bus_add_driver-> driver_attach

 

driver_attach、この関数はバス上のすべてのデバイス(platform_bus_type)をトラバースし、ドライバーに一致するデバイスを見つけ、条件に一致するデバイス構造にドライバーポインターを向けて、ドライバー間の一致を完了することに注意してください。ドライバーとデバイス(__driver_attach関数が完了しました)。

 

デバイスが一致する場合は、platform_bus_typeのプローブ関数を実行する必要があり、最後にデバイスドライバー(sun6i_i2c_probe)のプローブ関数が呼び出されます。

2.2.1 sun6i_i2c_probe

sun6i_i2c_probe関数では、ハードウェアの初期化、割り込み登録、各i2cコントローラーのi2c_adapterの作成など、多くの作業が行われます。

コードをコピーする

1268 pdata = pdev-> dev.platform_data; 
1269 if(pdata == NULL){ 
1270 return -ENODEV; 
1271} 
1272 
1273 res = platform_get_resource(pdev、IORESOURCE_MEM、0); 
1274 irq = platform_get_irq(pdev、0); 
1275 if(res == NULL || irq <0){ 
1276 return -ENODEV; 
1277} 
1278 
1279 if(!request_mem_region(res-> start、resource_size(res)、res-> name)){ 
1280 return -ENOMEM; 
1281}

コードをコピーする

 

  • まず、現在のデバイスのプライベートデータポインタを取得してpdataに保持します。次に、platform_get_resourceを介してデバイスが占有するメモリリソースを取得し、request_mem_regionに申請します。同時に、irqリソースも保持されます。 

コードをコピーする

1288 
1289 strlcpy(i2c-> adap.name、 "sun6i-i2c"、sizeof(i2c-> adap.name)); 
1290 i2c-> adap.owner = THIS_MODULE; 
1291 i2c-> adap.nr = pdata-> bus_num; 
1292 i2c-> adap.retries = 3; 
1293 i2c-> adap.timeout = 5 * HZ; 
1294 i2c-> adap.class = I2C_CLASS_HWMON | I2C_CLASS_SPD; 
1295 i2c-> bus_freq = pdata-> frequency; 
1296 i2c-> irq = irq; 
1297 i2c-> bus_num = pdata-> bus_num; 
1298 i2c-> status = I2C_XFER_IDLE; 
1299 i2c-> suspended = 0; 
1300 spin_lock_init(&i2c-> lock); 
1301 init_waitqueue_head(&i2c-> wait);

コードをコピーする

  • i2c_adapterを初期化し、ワークキューinit_waitqueue_headを初期化します。 
  • ioremapを介してIOリソースを申請します。
  • request_irqを介してirqリソースを申請するための、割り込み処理サービス関数は次のとおりです。sun6i_i2c_handler;
  • sun6i_i2c_hw_init、i2c制御用のハードウェア初期化。
  • i2c-> adap.algo =&sun6i_i2c_algorithm、コントローラーのバス送信アルゴリズムを初期化し、デバイスドライバーを呼び出します。
  • 初期化されたi2c_adapterをi2c_coreに登録します:i2c_add_numbered_adapter。

この時点で、プローブ機能は完了です。

2.2.2 sun6i_i2c_core_process

i2cコントローラの割り込みサービスプログラムsun6i_i2c_handlerはsun6i_i2c_core_processを呼び出し、i2cバスの実際の伝送制御もこの関数で完了します。

主なプロセス:

  1. i2cコントローラーの現在のステータスtwi_query_irq_statusを読み取り、その状態を維持します。
  2. 状態の値に従って分岐およびジャンプして、i2cの動作状態を制御します。
  3. 転送が完了したら、sun6i_i2c_xfer_completeを呼び出して、ワークキューをウェイクアップします。

 

2.2.3 sun6i_i2c_xfer

各i2cコントローラーデバイスは、ドライバーがコントローラーを記述するためにバインドされた後、i2c_adapterを作成します。i2c_adapterの確立と初期化は、プローブが駆動されるときに確立されます。各i2c_adapterには、i2c_algorithm構造体へのポインターが含まれています。i2c_algorithmは、i2cコントローラーを操作するための機能インターフェイス(主にmaster_xfer関数)を提供するために使用されます。これは、実際には次のとおりです。

 

static int sun6i_i2c_xfer(struct i2c_adapter * adapter、struct i2c_msg * msgs、int num)

この関数の機能は、周辺機器とデータを交換する必要があることをi2c_adapterに通知することであり、交換される情報はstruct i2c_msg * msgsを介して渡されます。sun6i_i2c_xferは、実際には送信のためにsun6i_i2c_do_xferを呼び出します。

i2cバスの読み取りおよび書き込み速度が制限されているため、sun6i_i2c_do_xferはi2c送信を開始し、割り込みがウェイクアップまたはタイムアウトするまでwait_event_timeoutを介してスリープに入ります。割り込みウェイクアップはsun6i_i2c_xfer_completeによって完了します。

3.i2cデバイスドライバー

3.1。ドライバー登録

i2cスレーブデバイスのドライバー登録は、i2c-core.cによって提供されるインターフェースを使用します:i2c_register_driver;その呼び出しは次のとおりです。

 

i2c_register_driver-> driver_register-> bus_add_driver;

 

bus_add_driverを分析します:

  • device_driverデータ構造のstructdriver_private * pについて

デバイスドライバーモデルはkobjectを介したデバイスドライバーの階層管理であるため、device_driverにはkobjectメンバーが含まれ、linuxではstruct driver_privateにkobjectが含まれ、device_driverにはstructdriver_privateが含まれます。カーネルが動作することでdriver_privateはdevice_driverのプライベートデータであることがわかります。

struct driver_privateは、ドライバー登録の開始時に動的に適用され、初期化されます。

  • klist_init(&priv-> klist_devices、NULL、NULL);

デバイスのリンクリストを初期化すると、ドライバーに一致するすべてのデバイスがリンクリストに追加されます。

  • priv-> kobj.kset = bus-> p-> drivers_kset;

ドライバが属するksetを指定します。

  • kobject_init_and_add

kobjectを初期化し、kobjectを対応するksetセットに追加します(つまり、bus-> p-> drivers_kset)。

関数は最後にkobject_add_internalを呼び出して、対応するksetにkobjectを追加します。主なことは、kobjectの親がNULLの場合、その親はksetコレクションのkobjectに設定されるということです。

親= kobject_get(&kobj-> kset-> kobj);

 

次に、kobjectのフォルダーcreate_dir(kobj);を作成して、/ sys /ディレクトリーから表示できるようにします。

  • driver_attach、ドライバーとデバイスをバインドします

バス上のデバイスリンクリストをトラバースし、一致するデバイスを見つけてバインドします。

driver_attach-> bus_for_each_dev(drv-> bus、NULL、drv、__ driver_attach);

関数ポインタ__driver_attachをbus_for_each_devに渡し、見つかった各デバイスを駆動して照合します。

 

bus_for_each_dev:

バス上のデバイスはすべてbus-> p-> klist_devicesリンクリスト上のノードであるため、バス上のすべてのデバイスをトラバースします。したがって、この関数は実際にはリンクリストのトラバースです。詳細については、klistを参照してください。

 

__driver_attach(ソースコードの場所drivers / base / dd.c):

デバイスとドライバーを一致させます。一致が成功した場合は、バインドを試みます。

 

1.最初に一致を確認します:driver_match_device(drv、dev);

呼び出し関係:-> drv-> bus-> match-> i2c_device_match 

-> of_driver_match_device

  i2c_match_id

 

一致するクエリを実行するには、最終的に2つの方法があることがわかります。

方法1:of_device_idをof_driver_match_deviceで比較します。

方法2:id_tableをi2c_match_idで比較します。

方法2は実際には比較です

i2c_driver-> id_table-> nameとclient-> nameは同じです。

 

2.一致が確認されたら、ドライバーとデバイスをバインドします。driver_probe_device;

呼び出し関係:driver_probe_device->かせ_probe

 ->開発->バス->プローブ

    driver_bound

 

Reality_probeで、最初にデバイスのドライバーポインターをドライバーにポイントします:dev-> driver = drv。

i2c_bus_typeに対応して、dev-> bus-> probeはi2c_device_probeです。これは最終的にドライバーのプローブ関数を呼び出します。

 

最後はdriver_boundで、これはドライバーとデバイスをバインドします。

実際、klist_add_tailを呼び出すことです。デバイスノードをドライバーのklist_devicesに追加します。

 

  • klist_add_tailを呼び出して、登録されたドライバーをバスのklist_driversに追加します。

klist_add_tail(&priv-> knode_bus、&bus-> p-> klist_drivers);

 

  •  module_add_driver(drv-> owner、drv)

sysfsにドライバーディレクトリを作成します

3.2。デバイス登録

方法1:i2cデバイスの動的な検出と登録

i2c_register_driverの最後:

        INIT_LIST_HEAD(&driver-> clients); 
        / *すでに存在するアダプターをウォークします* / 
        i2c_for_each_dev(driver、__ process_new_driver);

 

i2c_for_each_devを観察します: 

コードをコピーする

int i2c_for_each_dev(void * data、int(* fn)(struct device *、void *))
{ 
        int res; 
 
        mutex_lock(&core_lock); 
        res = bus_for_each_dev(&i2c_bus_type、NULL、data、fn); 
        mutex_unlock(&core_lock); 
 
        解像度を返します。
}

コードをコピーする

 

実際、i2cバス上のklist_devicesリンクリストをトラバースし、取得したデバイスごとに__process_new_driverを実行します。 

跟踪__process_new_driver-> i2c_do_add_adapter-> i2c_detect

i2c_detectは、i2cデバイスの検出を実現します。ドライバーを登録した後、i2c_detectは、適切なデバイスがバスに接続されているかどうかを検出します。i2c_detectの実装は次のとおりです。

  • 各アダプターのドライバーによって指定されたアドレスリスト(address_list)をトラバースします。これは、i2c_detect_address関数によって完了されます。最後に、driver-> detect(デバイスドライバーによって提供されるデバイス検出関数)が呼び出されます。
  • 条件を満たすデバイスが見つかった場合は、i2c_new_deviceを実行してデバイスのi2c_clientを確立し、デバイスをi2c_bus_type-> p-> klist_devicesリンクリスト(device_register)に追加し、bus_add_device関数を使用して完了し、最後にbus_probe_deviceを呼び出して試行します。ドライバーをバインドします。
  • クライアントをドライバーのデバイスリストに追加します:list_add_tail(&client-> ected、&driver-> clients)

 

方法2:i2cデバイスの静的登録

Linux 3.3は、デバイスを登録するための静的定義メソッドであるインターフェースプロトタイプを提供します:linux-3.3 / drivers / i2c / i2c-boardinfo.c

int __init 
i2c_register_board_info(int busnum、
        struct i2c_board_info const * info、unsigned len)

コアコンテンツ: 

  • struct i2c_devinfoを申請して、i2cペリフェラルを記述します。
  • list_add_tail(&devinfo-> list、&__ i2c_board_list)、後続の検索のためにリンクリスト__i2c_board_listにdevinfoを追加します。

__i2c_board_listをスキャンし、クライアントを作成します

i2c_register_board_infoはデバイス記述子を__i2c_board_listに追加しましたが、クライアントを作成しませんでした。アダプターを登録するためにi2c_register_adapterを呼び出すと、__ i2c_board_listをスキャンしてクライアントを作成します。特定の呼び出し:

i2c_register_adapter 

-> i2c_scan_static_board_info 

-> i2c_new_device 

-> device_register

 

i2c_new_deviceでのクライアントの作成とデバイス登録device_registerが完了しました。

 

PS:

上記の登録プロセスによれば、i2c_register_board_infoはi2c_register_adapterの前に完了する必要があります。そうしないと、__ i2c_board_list内のノードがスキャンされません。

 

総括する:

  • 上記の分析から、i2cデバイスドライバーはi2c_register_driverを介して登録され、i2cデバイスはi2c_new_deviceを介して登録されていることがわかります。最終的に、これら2つの関数はドライブとデバイスバインディング(driver_attachとbus_probe_device)を試みます。最初にドライバを登録するか、最初にデバイスを登録し、最後に適切なドライバとデバイスをバインドするかは関係ありません。
  • デバイスを登録するには、次の2つの方法があります。

1. i2c_register_board_infoを介して、i2cデバイスの登録はシステムの起動時に静的に実行されます(axpパワードライバーがこれを実行します)。

2. i2cデバイスドライバーの検出機能を実装し、ドライバーがロードされたときにデバイスの作成を動的に検出します。awプラットフォームのタッチスクリーンドライバーgt82x.koはこの方法を使用します。

  • Linuxは、ドライブデータ構造にkobjectとksetを埋め込むことにより、デバイスドライバーの階層管理を完了します。kobjectとksetを理解することは、デバイスドライバーモデルを理解するために非常に重要です。

4.i2cドライバーのアーキテクチャ図

 

1、i2c_add_adapter

2、i2c_new_device / i2c_register_board_info

3、i2c_add_driver

4.i2cバスに登録されている一致関数を呼び出して一致させます

5.プラットフォームバスに登録されている一致関数を呼び出して一致させます

6、i2cdev_attach_adapter

おすすめ

転載: blog.csdn.net/u014426028/article/details/108833217