Linux SPIバスとデバイス駆動アーキテクチャの3:SPIコントローラ駆動


最初の記事では,SPI駆動アーキテクチャ全体がプロトコル駆動,汎用インタフェース層,コントローラ駆動の3つの大部分に分けられることを知った.ここで、コントローラ駆動は最下層のデータ送受信を担当し、データの送受信を完了するために、コントローラ駆動は以下の機能を完了する必要がある.
1.割り込み、DMAチャネル、DMAメモリバッファなどの必要なハードウェアリソースを申請する.
2.SPIコントローラの動作モードとパラメータを配置し、対応する設備と正確なデータ交換作業を行うことができる.
3.汎用インタフェース層にインタフェースを提供し、上位層のプロトコル駆動が汎用インタフェース層を通じてコントローラ駆動にアクセスできるようにする.
4.汎用インタフェース層と協力して、メッセージキューが空になるまでデータメッセージキューのキューと処理を完了する.
/声明:本博の内容はすべてhttp://blog.csdn.net/droidphoneオリジナル、転載は出典を明記して下さい、ありがとうございます!
コントローラデバイスの定義
SPIコントローラはlinuxのデバイスモデルフレームワークに従うので、1つのSPIコントローラはコードの中で1つのdevice構造に対応し、組み込みシステムの場合、私たちは通常SPIコントローラをプラットフォームデバイスとして扱うので、私たちにとって、ボードレベルのコードの中でSPIコントローラのためにplatform_を定義すればデバイス構造でいいです.以下、SamsungのSOCチップ:S 3 C 6410を例に、このplatformを定義する方法を見てみましょう.device.以下のコードは/arch/arm/plat-samsung/devs.c中:
static struct resource s3c64xx_spi0_resource[] = {
	[0] = DEFINE_RES_MEM(S3C_PA_SPI0, SZ_256),
	[1] = DEFINE_RES_DMA(DMACH_SPI0_TX),
	[2] = DEFINE_RES_DMA(DMACH_SPI0_RX),
	[3] = DEFINE_RES_IRQ(IRQ_SPI0),
};

struct platform_device s3c64xx_device_spi0 = {
	.name		= "s3c6410-spi",
	.id		= 0,
	.num_resources	= ARRAY_SIZE(s3c64xx_spi0_resource),
	.resource	= s3c64xx_spi0_resource,
	.dev = {
		.dma_mask		= &samsung_device_dma_mask,
		.coherent_dma_mask	= DMA_BIT_MASK(32),
	},
};
でこのplatform_デバイスでは、コントローラに必要なレジスタアドレス、DMAチャネルリソース、およびIRQ番号を定義し、デバイスの名前は、後続および対応するコントローラ駆動のマッチングに使用されるs 3 c 64 xx−spiと定義する.machineの初期化コードでは、このSPIコントローラを表すプラットフォームデバイスを登録する必要があります.また、s 3 c 64 xx_spi0_set_platdata関数は、後続のコントローラ駆動のためにプラットフォームに関するパラメータを設定します.
static struct platform_device *crag6410_devices[] __initdata = {
        ......
        &s3c64xx_device_spi0,
        ......
};

static void __init xxxx_machine_init(void)
{

        s3c64xx_spi0_set_platdata(NULL, 0, 2);
        //      
        platform_add_devices(crag6410_devices, ARRAY_SIZE(crag6410_devices));
}
s3c64xx_spi0_set_platdata関数の定義は次のとおりです.
void __init s3c64xx_spi0_set_platdata(int (*cfg_gpio)(void), int src_clk_nr,
						int num_cs)
{
	struct s3c64xx_spi_info pd;
	......
	pd.num_cs = num_cs;
	pd.src_clk_nr = src_clk_nr;
	pd.cfg_gpio = (cfg_gpio) ? cfg_gpio : s3c64xx_spi0_cfg_gpio;
        ......
	s3c_set_platdata(&pd, sizeof(pd), &s3c64xx_device_spi0);
}
上記の関数は、主にコントローラが使用するgpio構成、スライスピン個数、クロック構成などの情報を指定している.これらの情報は、後述するコントローラドライバで使用します.
SPIコントローラのplatform_を登録するdriver
前節ではSPIコントローラをplatformとして登録しましたデバイス、それに応じて、対応する駆動はプラットフォーム駆動であるべきである:platform_driverはplatform busによって互いにマッチングします.以下のコードは、/drivers/spi/spi-s 3 c 64 xxから来る.c
static struct platform_driver s3c64xx_spi_driver = {
        .driver = {
                .name   = "s3c64xx-spi",
                .owner = THIS_MODULE,
                .pm = &s3c64xx_spi_pm,
                .of_match_table = of_match_ptr(s3c64xx_spi_dt_match),
        },
        .remove = s3c64xx_spi_remove,
        .id_table = s3c64xx_spi_driver_ids,
};
MODULE_ALIAS("platform:s3c64xx-spi");

static int __init s3c64xx_spi_init(void)
{
        return platform_driver_probe(&s3c64xx_spi_driver, s3c64xx_spi_probe);
}
subsys_initcall(s3c64xx_spi_init);
明らかに、システム初期化フェーズ(subsys_initcallフェーズ)、s 3 c 64 x_spi_Init()は、s 3 c 64 xx-spiというプラットフォームドライバを登録しています.もちろん、プラットフォームバスは前節で定義したplatform_と同じ名前です.デバイスが一致し、probeコールバックが呼び出される(s 3 c 64 xx_spi_probe関数である).もちろん、ここでのマッチングはid_tableフィールドが完了しました:
static struct platform_device_id s3c64xx_spi_driver_ids[] = {
        {
                .name           = "s3c2443-spi",
                .driver_data    = (kernel_ulong_t)&s3c2443_spi_port_config,
        }, {
                .name           = "s3c6410-spi",
                .driver_data    = (kernel_ulong_t)&s3c6410_spi_port_config,
        }, 
        ......
        { },
};

登録spi_master
linuxデバイスモデルでは、SPIコントローラを表すのは第1節で定義したplatform_です.デバイス構造ですが、SPI汎用インタフェース層ではコントローラを表すspi_master構造、spi_についてマスター構造の説明は、Linux SPIバスとデバイス駆動アーキテクチャの2つ目:SPI汎用インタフェースレイヤを参照してください.デバイスとドライバが一致すると、ドライバのprobeコールバック関数が呼び出され、probeコールバック関数はドライバとデバイスを初期化する適切なタイミングであることが知られています.この例では、対応するprobeコールバックはs 3 c 64 x_です.spi_probe:
static int s3c64xx_spi_probe(struct platform_device *pdev)
{
        ......

        /*     spi_master   */
        master = spi_alloc_master(&pdev->dev,
                                sizeof(struct s3c64xx_spi_driver_data));
        ......

        platform_set_drvdata(pdev, master);
        ......
        master->dev.of_node = pdev->dev.of_node;
        master->bus_num = sdd->port_id;
        master->setup = s3c64xx_spi_setup;
        master->cleanup = s3c64xx_spi_cleanup;
        master->prepare_transfer_hardware = s3c64xx_spi_prepare_transfer;
        master->transfer_one_message = s3c64xx_spi_transfer_one_message;
        master->unprepare_transfer_hardware = s3c64xx_spi_unprepare_transfer;
        master->num_chipselect = sci->num_cs;
        master->dma_alignment = 8;
        master->bits_per_word_mask = SPI_BPW_MASK(32) | SPI_BPW_MASK(16) |
                                        SPI_BPW_MASK(8);
        /* the spi->mode bits understood by this driver: */
        master->mode_bits = SPI_CPOL | SPI_CPHA | SPI_CS_HIGH;
        master->auto_runtime_pm = true;

        ......
        /*         spi_master   */
        if (spi_register_master(master)) {
                dev_err(&pdev->dev, "cannot register SPI master
"); ret = -EBUSY; goto err3; } ...... }
上記の関数は、必要なハードウェアリソースの初期化作業を完了する以外に、最も重要な作業はspi_を通過することである.alloc_master関数はspi_を割り当てましたmaster構造、この構造を初期化し、最終的にspi_register_master関数はコントローラの登録を完了しました.コードからもspi_master構造におけるいくつかの重要なコールバック関数は、コントローラとデバイス間のデータ交換を完了するために、汎用インタフェース層によって適切なタイミングで呼び出されるいくつかの重要なコールバック関数に割り当てられている.
実装spi_master構造のコールバック関数
実際、SPIコントローラドライバの主な仕事はspi_を実現することです.master構造のいくつかのコールバック関数、その他の動作ロジックは、汎用インタフェース層によって完成され、汎用インタフェース層は適切なタイミングでこれらのコールバック関数を呼び出します.ここでは、各コールバック関数の役割を紹介します.具体的な実装例は、コードツリーの各プラットフォームの例(コードは:/drivers/spi/)を自分で読んでください.
int (*setup)(struct spi_device *spi)
プロトコルドライバがコントローラの動作モードまたはパラメータを変更する場合、汎用インタフェースレイヤが提供するAPI:spi_を呼び出します.setup()は、最後にsetupコールバック関数を呼び出して設定を完了します.
int (*transfer)(struct spi_device *spi, struct spi_message *mesg)
現在、私たち自身がこのコールバック関数を実現する必要はなく、初期化時に直接NULLに設定すればよい.現在の汎用インタフェース層はメッセージキュー化を実現し、spi_を登録している.マスターの場合、汎用インタフェースレイヤは、実装された汎用関数を提供します.現在、古いドライバだけがコールバック方式を使用しています.新しいドライバはコールバック関数の使用を停止するのではなく、キュー化されたtransfer_を使用する必要があります.one_Messageコールバック.注意すべきは、transferを設定する方法の1つしか選択できません.one_Messageコールバックではtransferコールバックを設定できません.逆も同様です.
void (*cleanup)(struct spi_device *spi)
デバイス(spi_device構造)からSPIが解放されると、コールバック関数が呼び出され、スレーブデバイスが占有するハードウェアリソースが解放される.
int (*prepare_transfer_hardware)(struct spi_master *master)
int (*unprepare_transfer_hardware)(struct spi_master *master)
この2つのコールバック関数は、データ転送プロセスを開始する前後に、コントローラに機会を駆動し、DMAリソースやメモリリソースなどの必要なハードウェアリソースを申請または解放するために使用されます.
int (*prepare_message)(struct spi_master *master, struct spi_message *message)
int (*unprepare_message)(struct spi_master *master, struct spi_message *message)
この2つのコールバック関数は、1つのデータ転送プロセスを開始する前と後に、コントローラにチャンスを与え、メッセージに対して必要な前処理または後処理を行い、例えばメッセージに基づいてデータを交換するスレーブデバイスに基づいて、コントローラの正しい動作クロック、ワード長、動作モードなどを設定するためにも使用される.
int (*transfer_one_message)(struct spi_master *master, struct spi_message *mesg)
汎用インタフェースレイヤがmasterのキューにメッセージを転送する必要があることを発見すると、コールバック関数が呼び出されるので、この関数は本当にメッセージ転送を完了する作業関数であり、転送作業が完了するとspi_を呼び出すべきである.finalize_current_Message関数は、汎用インタフェースレイヤに通知し、キュー内の次のメッセージの転送を開始します.