spi总线概述2(spi-summary)

特定板初始化代码如何声明SPI器件?

------------------------------------------------------

Linux需要多种信息才能正确配置SPI器件。这些信息通常是电路板的特定代码,甚至有些芯片支持自动发现/枚举。

 

声明控制器

 

第一类信息是存在的SPI控制器的清单。由于片上系统(SOC)在主板上,这些通常是平台设备,控制器可能需要一些platform_data正常运行。 “结构platform_device”将包括资源如控制器的第一个寄存器的物理地址和其IRQ。

 

平台经常抽象为“register SPI controller”操作,

也许代码以初始化引脚配置与耦合,使

arch/.../mach-*/board-*.c的多个主板的文件都可以共享基本相同的控制器设置代码。这是因为大多数的SOCs有几个SPI功能的控制器,只在一个实际可用的指定主板上一般设置和注册。

 

例如 arch/.../mach-*/board-*.c (个人理解这个文件应该是arch/.../mach-*/mach-*.c)文件可能包含下面代码:

 

    #include <mach/spi.h>   /* for mysoc_spi_data */

 

    /* if your mach-* infrastructure doesn'tsupport kernels that can

     * runon multiple boards, pdata wouldn't benefit from "__init".

     */

    static struct mysoc_spi_data __initdatapdata = { ... };

 

    static __init board_init(void)

    {

        ...

        /* this board only uses SPI controller#2 */

        mysoc_register_spi(2, &pdata);

        ...

    }

 

SOC-specific单元代码可能会如下:

 

    #include <mach/spi.h>

 

    static struct platform_device spi2 = { ...};

 

    void mysoc_register_spi(unsigned n, structmysoc_spi_data *pdata)

    {

        struct mysoc_spi_data *pdata2;

 

        pdata2 = kmalloc(sizeof *pdata2,GFP_KERNEL);

        *pdata2 = pdata;

        ...

        if (n == 2) {

            spi2->dev.platform_data = pdata2;

            register_platform_device(&spi2);

 

            /* also: set up pin modes so thespi2 signals are

             * visible on the relevant pins ... bootloaderson

             * production boards may already have donethis, but

             * developer boards will often need Linux to doit.

             */

        }

        ...

    }

 

注意即使使用相同的SOC控制器platform_data可能会有所不同, 。例如,在一个板的SPI可能会使用外部时钟,另一个设备SPI时钟来自当前设置的主时钟。

 

声明从设备

 

第二类信息是在目标板上存在的SPI从器件列表,往往需要一些特定板的数据驱动程序才能正常工作。

 

通常情况下,您的arch/.../mach-*/board-*.c(个人理解这个文件应该是arch/.../mach-*/mach-*.c)文件将提供一个小列表,列出每块板的SPI设备。 (这通常是只有一个一小段),可能看起来像如下。

 

    static structads7846_platform_data ads_info = {

        .vref_delay_usecs   = 100,

        .x_plate_ohms       = 580,

        .y_plate_ohms       = 410,

    };

 

    static struct spi_board_infospi_board_info[] __initdata = {

    {

        .modalias   = "ads7846",

        .platform_data  = &ads_info,

        .mode       =SPI_MODE_0,

        .irq        =GPIO_IRQ(31),

        .max_speed_hz   = 120000 /* max sample rate at 3V */ * 16,

        .bus_num    = 1,

        .chip_select    = 0,

    },

    };

 

再次,注意特定板提供的信息,每个芯片可能需要几种类型。这个例子显示了泛型约束,如最快的SPI时钟允许(在这种情况下,一个板上的电压功能)或IRQ管脚是导线,加上芯片的具体限制,例如在一个引脚的电容变化产生的一个重要的延迟。

 

(还有的“controller_data”,这可能是控制器驱动程序的有用信息。一个例子是特定外设的DMA调整数据或片选回调。这是存储在spi_device后。)

 

board_info应提供足够的信息,让系统在没有芯片的驱动程序被加载时工作。最麻烦的方面比如在spi_device.mode域的SPI_CS_HIGH位,因为设备共享一个总线,直到底层结构直到如何取消选择它前不可能中断片选"backwards"。

 

那么你的板初始化代码将使用SPI底层结构登记表,所以它是,SPI主控制器驱动被注册后晚些时候推出时:

 

spi_register_board_info(spi_board_info,ARRAY_SIZE(spi_board_info));

 

类似其他静态特定板的设置,你不需注销。

 

广泛使用的“card”的风格电脑封装内存,CPU,在卡上并没有别的,也许只有30平方厘米。在这样的系统上,

arch/.../mach-.../board-*.c文件将主要提供卡插入主板上的设备信息,

当然包括通过卡连接器挂上的SPI设备!

 

非静态配置

 

开发板经常扮演不同的角色,比如一个例子是潜在需要热插拔的SPI设备和/或控制器。

 

对于这些情况下,你可能需要使用spi_busnum_to_master()来查找了SPI总线主机,还可能需要spi_new_device()提供热插拔的主板信息。当然,在主板被删除时需要调用spi_unregister_device()。

 

当Linux通过SPI包含支持MMC / SD / SDIO /的DataFlash卡配置也将是动态的。幸运的是,所有这些设备的支持基于设备识别探测,所以他们通常可以热插拔。

 

我怎样写“SPI协议驱动程序”?

----------------------------------------

目前大多数SPI驱动程序是当前内核驱动程序,但也有支持用户空间的驱动程序。在这里,我们只讲内核驱动程序。

 

SPI协议驱动程序有点像平台的设备驱动程序:

 

    static struct spi_driverCHIP_driver = {

        .driver = {

            .name       = "CHIP",

            .owner      = THIS_MODULE,

        },

 

        .probe      =CHIP_probe,

        .remove     =__devexit_p(CHIP_remove),

        .suspend    =CHIP_suspend,

        .resume     =CHIP_resume,

    };

 

驱动核心尝试自动将此驱动程序绑定到任何支持SPI的设备board_info分配一个代号“CHIP”。您的probe()代码可能看起来是这样,除非你创建一个设备用于管理总线(出现在/sys/class/spi_master)。

 

    static int __devinit CHIP_probe(structspi_device *spi)

    {

        struct CHIP         *chip;

        struct CHIP_platform_data   *pdata;

 

        /* assuming the driver requiresboard-specific data: */

        pdata = &spi->dev.platform_data;

        if (!pdata)

            return -ENODEV;

 

        /* get memory for driver's per-chipstate */

        chip = kzalloc(sizeof *chip,GFP_KERNEL);

        if (!chip)

            return -ENOMEM;

        spi_set_drvdata(spi, chip);

 

        ... etc

        return 0;

    }

 

只要它进入probe(),驱动程序可能会发出I / O请求到SPI设备使用“结构spi_message”。当remove()返回,或probe()失败后,驱动保证它不会提交任何此类消息。

 

   - 一个spi_message是协议操作的序列,执行作为一个原子序列。 SPI驱动控制包括:

 

     +当双向读取和写入启动... spi_transfer请求的队列如何安排;

 

+ 哪些I / O缓冲区被占用... 每个spi_transfer封装一个传输方向的缓冲区,支持全双工(两个指针,也许在这两种情况下相同)及半双工(一个指针为NULL)传输;

 

     +传输后可选定义短暂延迟... 使用spi_transfer.delay_usecs设置;

 

     +传输和任何延误后片选是否变得无效... 使用spi_transfer.cs_change标志;

 

     +提示是否接下来的消息是可能去此相同设备...

在最后传输的原子操作使用spi_transfer.cs_change标志,为芯片取消选择和选择操作潜在的节约成本。

 

- 按照标准内核规则,并在你的消息中提供DMA安全缓冲区。这种方式使用DMA控制器驱动程序不会被强迫作出额外的副本除非硬件需要(如围绕硬件勘误表工作,强制使用反弹缓冲)。

 

如果的标准dma_map_single()处理这些缓冲区不适当,你可以使用spi_message.is_dma_mapped,告诉控制器驱动程序你已经提供了相关的DMA地址。

 

   - 基本的I / O原始会是spi_async()。异步请求可能

在任何情况下产生(IRQ处理程序,任务等),完成报告使用消息回调产生。任何检测到的错误后,该芯片被取消和处理spi_message被中止。

 

- 也有同步的封装例如spi_sync(),和封装spi_read中,spi_write(),spi_write_then_read()。这些可发出只有在上下文中可能睡眠,他们都简洁(小,和“可选”)层超过spi_async()。

 

-spi_write_then_read()调用,方便封装其中,应该只用少量的数据额外副本的消耗可能会被忽略。它的设计支持常见的RPC风格的要求,比如写一个8位命令读一个十六位的响应 - spi_w8r16()是其中封装之一,执行这些操作。

 

Somedrivers may need to modify spi_device characteristics like the transfer mode,wordsize, or clock rate. This is done with spi_setup(),which would normally becalled from probe() before the first I/O is done to the device. However, thatcan also be called at any time that no message is pending for that device.

 

虽然“spi_device”将是驱动的底部边界,上边界可能包括sysfs(尤其是传感器读数)输入层,ALSA,网络、MTD字符设备框架,或其它Linux子系统。

 

注意,有两种类型的内存,是你的驱动必须管理的一部分与SPI器件相互作用。

 

- I / O缓冲器使用通常的Linux规则,而且必须是DMA-safe。你通常从堆或空白页面池分配。不要使用堆栈,或任何被声明为“静态”。

 

- spi_message和spi_transfer元数据用于结合I/O缓冲成一组协议交换的I / O缓冲区。这些可以被很方便分配到任何地方,包括部分其他分配一次的驱动器数据结构,初始化为零。

 

如果你喜欢,spi_message_alloc()和spi_message_free()可用来分配和零初始化spi_message。


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值