SPI内核驱动模型-dm8127

利用TI提供的已有SPI内核驱动模型,该方案需要我们了解内核的SPI驱动模型是如何实现的。

这篇文章是从网上转载过来的,加上自己的调试经验!

主要是添加dm8127的内核spi驱动,添加是的红色字体部分。我主要是添加spi3驱动设备


3.1. 
    SPI内核驱动模型
在2.6的linux内核中,SPI的驱动架构可以分为如下三个层次:SPI 核心层、SPI控制器驱动层和SPI设备驱动层。Linux中SPI驱动代码位于drivers/spi目录。

3.1.1.      SPI核心层
SPI核心层是Linux的SPI核心部分,提供了核心数据结构的定义、SPI控制器驱动和设备驱动的注册、注销管理等API。其为硬件平台无关层,向下屏蔽了物理总线控制器的差异,定义了统一的访问策略和接口;其向上提供了统一的接口,以便SPI设备驱动通过总线控制器进行数据收发。Linux中,SPI核心层的代码位于driver/spi/spi.c。

3.1.2.      SPI控制器驱动层
SPI控制器驱动层,每种处理器平台都有自己的控制器驱动,属于平台移植相关层。它的职责是为系统中每条SPI总线实现相应的读写方法。在物理上,每个SPI控制器可以连接若干个SPI从设备。
在系统开机时,SPI控制器驱动被首先装载。一个控制器驱动用于支持一条特定的SPI总线的读写。一个控制器驱动可以用数据结构structspi_master来描述,定义在include/liunx/spi/spi.h文件中。
针对DM8127,控制器驱动程序为omap2_mcspi.c,SPI 控制器驱动的注册采用Platformdevice和Platform driver机制。

3.1.2.1.      SPI控制器的Platform device
Platform device的定义和注册代码位于arch/arm/mach-omap2/devices.c中。
static struct omap2_mcspi_platform_config omap2_mcspi1_config = {
    .num_cs        = 4,// 4个片选:
};

static struct resource omap2_mcspi1_resources[] = {//spi0设备
    {// io资源:
        .start        = OMAP2_MCSPI1_BASE,
        .end        = OMAP2_MCSPI1_BASE + 0xff,
        .flags        = IORESOURCE_MEM,
    },
    {
        //SPI0XEVT0: SPI0 Transmit 0   
        .start        = 16,//这个16和17为什么这么选择呢?具体要看dm8127的芯片资料,可以搜索SPI0XEVT0,start和stop主要发送和接收选取哪一个dma通道的问题
        .end        = 16,
        .flags        = IORESOURCE_DMA,
    },
    {
        //SPI0REVT0: SPI0 Receive 0
        .start        = 17,
        .end        = 17,
        .flags        = IORESOURCE_DMA,
    },
};
static struct resource omap2_mcspi4_resources[] = {//内核中这部分代码是没有的,就是意思是没有申请spi3设备驱动的资源
    {
        .start        = OMAP2_MCSPI4_BASE,
        .end        = OMAP2_MCSPI4_BASE + 0xff,
        .flags        = IORESOURCE_MEM,
    },
    {
        //SPI3XEVT0: SPI0 Transmit 0
        .start        = 20,
        .end        = 20,
        .flags        = IORESOURCE_DMA,
    },
    {
        //SPI3REVT0: SPI0 Receive 0
        .start        = 21,
        .end        = 21,
        .flags        = IORESOURCE_DMA,
    },
};

static struct platform_device omap2_mcspi1 = {
      .name            = "omap2_mcspi",
      .id            = 1,
      .num_resources      = ARRAY_SIZE(omap2_mcspi1_resources),
      .resource      = omap2_mcspi1_resources,
      .dev            = {
            .platform_data =&omap2_mcspi1_config,
      },
};

static struct platform_device omap2_mcspi2 = {
      .name            = "omap2_mcspi",
      .id            = 2,
      .num_resources      = ARRAY_SIZE(omap2_mcspi2_resources),
      .resource      = omap2_mcspi2_resources,
      .dev            = {
            .platform_data =&omap2_mcspi2_config,
      },
};

static struct platform_device omap2_mcspi3 = {
      .name            = "omap2_mcspi",
      .id            = 3,
      .num_resources      = ARRAY_SIZE(omap2_mcspi3_resources),
      .resource      = omap2_mcspi3_resources,
      .dev            = {
            .platform_data =&omap2_mcspi3_config,
      },
};

static struct platform_device omap2_mcspi4 = {
      .name            = "omap2_mcspi",
      .id            = 4,
      .num_resources      = ARRAY_SIZE(omap2_mcspi4_resources),
      .resource      = omap2_mcspi4_resources,
      .dev            = {
            .platform_data =&omap2_mcspi4_config,
      },
};
可以看到,这边定义了4个SPI控制器的Platform device,id分别为“1,2,3,4”,name都为“omap2_mcspi”,变量resource中定义了适配器的寄存器基地址。

3.1.2.2.      Platform device的注册如下(arch/arm/mach-omap2/devices.c) 在omap_init_mcspi(void)函数里面
static void omap_init_mcspi(void)
{

    omap_writel(0x20, 0x48140B74);//jsyjzz add spi3  SPI[3]_SCS[1]  这里注意了,假如用spidev_test测试spi驱动的时候,读出的数据全为0的时候,很有可能你管脚没有复用成功!这里增加管脚的复用   其中omap_mux_init_signal("vout0_r_cr1",    OMAP_MUX_MODE1);这种类型其实也就是写寄存器跟 omap_writel的功能是一样的
    omap_writel(0x20, 0x48140B78);//jsyjzz add spi3  SPI[3]_SCLK
    omap_writel(0x20, 0x48140B7C);//jsyjzz add spi3  SPI[3]_D[1]
    omap_writel(0x20, 0x48140B80);//jsyjzz add spi3   SPI[3]_D[0]

     //设置spi寄存器开始地址和结束地址
    if (cpu_is_omap44xx())
        omap4_mcspi_fixup();

    if (cpu_is_ti81xx())
        ti81xx_mcspi_fixup();

    platform_device_register(&omap2_mcspi1); //注册spi master设备
    if (!cpu_is_ti816x())
        omap2_mcspi2_init();

    if (cpu_is_omap2430() || cpu_is_omap343x() || cpu_is_omap44xx() ||
        cpu_is_ti814x())
        omap2_mcspi3_init();

    if (cpu_is_omap343x() || cpu_is_omap44xx() || cpu_is_ti814x())
        omap2_mcspi4_init();
}

platform_device_register(&omap2_mcspi1);
platform_device_register(&omap2_mcspi2);
platform_device_register(&omap2_mcspi3);
platform_device_register(&omap2_mcspi4);
以上四个函数在内核启动时完成。将Platformdevice注册到Platform总线上,完成注册后,寄存器的基地址等信息会在设备树中描述,此后只需利用platform_get_resource等标准接口自动获取即可,实现了驱动和资源的分离。

3.1.2.3.      SPI控制器的Platform driver
Platformdriver的注册代码位于内核的drivers/spi/omap2_mcspi.c中,该驱动的注册目的是初始化DM8127的SPI控制器,提供SPI总线数据传输的具体实现,并且向PSI核心层注册SPI 控制器。

3.1.2.4.      Platform driver的定义如下
static struct platform_driver omap2_mcspi_driver = {
      .driver ={
            .name =            "omap2_mcspi",
            .owner =      THIS_MODULE,
            .pm =            &omap2_mcspi_pm_ops
      },
      .remove=      __exit_p(omap2_mcspi_remove),
};
内核启动时会调用函数platform_driver_probe()来注册Platformdriver,该函数是带有probe函数的平台驱动注册函数,注册时,会扫描platformbus上的所有设备,由于匹配因子name为" omap2_mcspi ",与之前注册的Platformdevice匹配成功,于是函数omap2_mcspi_probe()将被调用,控制器device和driver将被绑定起来。
在文件drivers/spi/omap2_mcspi.c中会涉及到一个数据结构omap2_mcspi,这个结构专门针对DM8127的SPI控制器,代码如下:
struct omap2_mcspi {
      structwork_struct      work;
     
      spinlock_t            lock;
      structlist_head      msg_queue;
      structspi_master      *master;
      structclk            *ick;
      structclk            *fck;
     
      void__iomem            *base;
      unsignedlong            phys;
     
      structomap2_mcspi_dma      *dma_channels;
};
msg_queue对应消息队列。
master对应通用的spi控制器结构。
ick和fck分别对应接口时钟和功能时钟。
base对应SPI控制器寄存器的虚拟地址。
phys对应SPI控制器寄存器的物理地址。
dma_channels对应SPI控制器的DMA通道。  
函数omap2_mcspi_probe()的执行流程如下图:
 
3.1.3.      SPI设备驱动层
SPI设备驱动层为用户接口层,其为用户提供了通过SPI总线访问具体设备的接口。
SPI设备驱动层可以用两个模块来描述,struct spi_driver和struct spi_device。
针对DM8127上的设备,我们利用内核提供的SPI用户驱动程序spidev.c

3.1.2.1.      DM8127上的设备注册
设备的创建和注册分为两步。
第一步:将设备信息加入到设备链表
在板级初始化时将SPI设备的名称,地址和相关的信息加入到链表board_list中,该链表定义在driver/spi/spi.c中,记录了具体开发板上的SPI设备信息。
在\kernel\arch\arm\mach-omap2\board-ti8148ipnc\board-ti8148ipnc.c中,设备信息定义如下:(这里是我们主要修改的代码,添加spi设备所对应的信息,包括spi总信号,也就是具体对应dm8127的哪个spi控制器;spi时钟平率;片选,也就是对应dm8127的哪个片选信号;工作模式即极性和相位)
struct spi_board_info __initdata ti8148_spi_slave_info[] = {
      {
            .modalias      = "dm8127_spidev",
            .irq            = -1,
            .max_speed_hz      = 100000,
            .bus_num      = 1,//AD9970挂在SPI0上,片选为cs0
            .chip_select      = 0,
            .mode = SPI_MODE_3,
      },
      {
            .modalias      = "dm8127_spidev",
            .irq            = -1,
            .max_speed_hz      = 100000,
            .bus_num      = 1, //AD9970挂在SPI0上,片选为cs1
            .chip_select      = 1,
            .mode = SPI_MODE_3,
      },
      {
            .modalias      = "dm8127_spidev",
            .irq            = -1,
            .max_speed_hz      = 100000,
            .bus_num      = 1, //eeprom挂在SPI0上,片选为cs2
            .chip_select      = 2,
            .mode = SPI_MODE_0,
      },
      {
            .modalias      = "dm8127_spidev",
            .irq            = -1,
            .max_speed_hz      = 100000,
            .bus_num      = 2, //fpga挂在SPI1上,片选为cs0
            .chip_select      = 0,
            .mode = SPI_MODE_0,
      },
    
   {
 
          .modalias      = "spidev",//为什么改成spidev呢?因为spidev.c里面注册设备驱动的name如下面蓝色字体的(1),蓝色字体部分只是注释,代码不做添加
 
          .irq            = -1,//无特殊要求一般不要写
 
          .max_speed_hz      = 100000,//时钟
 
          .bus_num      = 4, //SPI3    这里为什么是4呢?原因是总线是从1开始计算的,所以spi3在总线上是4
 
          .chip_select      = 1,//片选,主要是看芯片资料,看硬件接的哪个管脚?其中spi3的cs有四个片选,我选择是cs1
 
          .mode = SPI_MODE_0,//无非是高低和上升沿、下降沿触发的发送数据的问题  在spidev_test的时候,可以设置
 
    },
(1)  static struct spi_driver spidev_spi_driver = {
    .driver = {
        .name =        "spidev",
        .owner =    THIS_MODULE,
    },
    .probe =    spidev_probe,
    .remove =    __devexit_p(spidev_remove),

    /* NOTE:  suspend/resume methods are not necessary here.
     * We don't do anything except pass the requests to/from
     * the underlying controller.  The refrigerator handles
     * most issues; the controller driver handles the rest.
     */
};

}
设备信息通过函数spi_register_board_info()加入到链表board_list中,代码如下:
void __init ti8148_spi_init(void)
{
      spi_register_board_info(ti8148_spi_slave_info,
                        ARRAY_SIZE(ti8148_spi_slave_info));
}
设备加入到设备链表board_list的流程图如下:
 
第二步:设备spi_device的创建并添加
spi_device的创建并添加是在SPI控制器驱动注册过程中完成的,spi_register_master()函数在注册SPI控制器驱动的过程会扫描SPI设备链表board_list,如果该总线上有对应的SPI设备,则创建相应的spi_device,并将其添加到SPI的设备链表中。流程图如下所示:
 
3.1.2.2.      DM8127上的设备驱动注册
在drivers/spi/spidev.c中,定义了设备驱动,代码如下:
static struct spi_driver spidev_spi_driver = {
      .driver ={
            .name =            "dm8127_spidev",
            .owner =      THIS_MODULE,
      },
      .probe=      spidev_probe,
      .remove=      __devexit_p(spidev_remove),
};

static int __init spidev_init(void)
{
status =spi_register_driver(&spidev_spi_driver);     
}
注册的简要示意图如下:
 
在模块加载的时候首先调用spidev_init(),然后spidev_init()调用函数spi_register_driver()注册spider_spi_driver结构体。
函数spi_register_driver()初始化该驱动的总线为spi_bus_type,然后使用函数driver_register(&sdrv->driver)注册该驱动,因此内核会在SPI总线上遍历所有SPI设备,由于该设备驱动的匹配因子name变量为“dm8127_spidev”,因此正好和之前创建的chip->modalias为“dm8127_spidev”的SPI设备匹配。因此SPI设备驱动的probe函数spidev_probe()将会被调用,完成一些具体设备相关的初始化等操作,同时我们要针对不同spi设备去配置一下spi传输的bits_per_word,代码如下:
if(spi->master->bus_num ==2)//spi1
{
      spi->bits_per_word = 32;
}
else if(spi->master->bus_num ==1)//spi0 这里设置不设置都没什么关系!因为你调用spidev_test的时候,可以设置word的长度的。
{
      spi->bits_per_word = 8;
}
spi_setup(spi);


drivers\spi\spi.c里面的spi_add_device的函数里面:if (spi->chip_select > spi->master->num_chipselect) 将>= 改成>

drivers\spi\omap2_mcspi.c里面的omap2_mcspi_probe函数里面   
case 4:
        rxdma_id = spi4_rxdma_id;
        txdma_id = spi4_txdma_id;
        num_chipselect = 1;修改为=4

还有最后一步:
内核配置选项Device Drivers  --->
                                      [*] SPI support  --->
                                              -*-   Utilities for Bitbanging SPI masters
                                            <*>   GPIO-based bitbanging SPI Master
                                             <*>   McSPI driver for OMAP/TI81XX
                                              <*>   User mode SPI device driver support 
 这些都添加上,编译内核就没问题了。
经过上述的步骤就成功把spi驱动添加到内核,板子跑起来,在dev下面发现了spi4.1设备节点
内核已经自带了spi的测试代码kernel的Documentation/spi下
改下设备名字,编译运行,打印如下  注意: 必须把spi的发送和接收的脚短接,这个测试程序是针对自发自收的,而且管脚决不能在接任何东西了,否则读出的数据跟发送会不一样的
        0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
        0x40, 0x00, 0x00, 0x00, 0x00, 0x95,
        0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
        0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
        0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
        0xDE, 0xAD, 0xBE, 0xEF, 0xBA, 0xAD,
        0xF0, 0x0D,
这样spi的驱动就可以使用了!
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值