Linux spi 设备驱动

转载地址:http://blog.csdn.net/liangzhenliang/article/details/46629155

1、概览

spi子系统中分为spi控制器驱动和spi设备驱动。前面已经讲述了spi控制器驱动。下面讲述一下spi设备驱动。所谓的spi设备驱动,就是挂在spi总线上形形色色的芯片驱动。它可能是一个Flash芯片,一个声音编解码芯片,也可能是一个网卡芯片。这些芯片如果要正常工作,那就必须为之编写对应的驱动,这个驱动就是spi设备驱动。下面将讲解如何实现一个spi设备驱动。

2、向内核注册spi设备


如果希望一个spi设备可以在linux系统下很好的工作,除了写驱动,还要向内核申明和注册这个spi设备。目前有两种方法向内核注册一个spi设备。在稍微老点版本的内核(2.6.xx)中通过向内核注册struct spi_board_info对象,来申明一个spi设备。在比较新的内核中(3.xx)使用device tree的方式向内核申明并注册一个spi设备的。无论使用哪种方式,其实最终的目的就是为了建立一个struct spi_deivce对象,并注册到spi子系统中。下面就结合代码来看看是如何注册一个spi设备到内核中的。

2.1)spi_board_info的方式

这种方式新版本的内核中已经很少用了,随着device tree的普及,这种方式也不建议使用了。不过目前版本的内核中仍然支持。struct spi_board_info的注册通常在平台的板级代码中。先来看看struct spi_board_info这个结构体的结构:

783 struct spi_board_info {
784         /* the device name and module name are coupled, like platform_bus;
785          * "modalias" is normally the driver name.
786          *
787          * platform_data goes to spi_device.dev.platform_data,
788          * controller_data goes to spi_device.controller_data,
789          * irq is copied too
790          */
791         char            modalias[SPI_NAME_SIZE];
792         const void      *platform_data;
793         void            *controller_data;
794         int             irq;
795 
796         /* slower signaling on noisy or low voltage boards */
797         u32             max_speed_hz;
798 
799 
800         /* bus_num is board specific and matches the bus_num of some
801          * spi_master that will probably be registered later.
802          *
803          * chip_select reflects how this chip is wired to that master;
804          * it's less than num_chipselect.
805          */
806         u16             bus_num;
807         u16             chip_select;
808 
809         /* mode becomes spi_device.mode, and is essential for chips
810          * where the default of SPI_CS_HIGH = 0 is wrong.
811          */
812         u8              mode;
813 
814         /* ... may need additional spi_device chip config data here.
815          * avoid stuff protocol drivers can set; but include stuff
816          * needed to behave without being bound to a driver:
817          *  - quirks like clock rate mattering when not selected
818          */
819 };
 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37

下面就结合imx51的一个板级代码讲解struct spi_board_info各个字段的含义,以及如何在板级代码中注册struct spi_board_info。在代码kernel/arch/arm/mach-imx51-3ds.c的121行左右:

121 static struct spi_board_info mx51_3ds_spi_nor_device[] = {
122         {
123          .modalias = "m25p80",
124          .max_speed_hz = 25000000,      /* max spi clock (SCK) speed in HZ */
125          .bus_num = 1,
126          .chip_select = 1,
127          .mode = SPI_MODE_0,
128          .platform_data = NULL,},
129 };
 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

一个struct spi_board_info对象对应一个spi设备。上面代码中:

  • .modalias = "m25p80", 
    spi设备名字,设备驱动探测时会用到该项;

  • .max_speed_hz = 25000000, 
    记录了这个spi设备支持的最大速度;

  • .bus_num = 1, 
    记录了该spi设备是连接在哪个spi总线上的,所在总线的编号;

  • .chip_select = 1, 
    该spi设备的片选编号;

  • .mode = SPI_MODE_0, 
    和这个spi设备支持spi总线的工作模式。

配置好这些参数以后,通过下面代码将这个struct spi_board_info对象注册到内核中,确切说是到spi子系统中去:

146         spi_register_board_info(mx51_3ds_spi_nor_device,
147                      ARRAY_SIZE(mx51_3ds_spi_nor_device));
 
 
  • 1
  • 2

spi_register_board_info()这个函数调用做了两件事:

  • struct spi_board_info对象转化为struct boardinfo对象,并将之连接到名为board_list的全局链表中。 
    board_list链表记录系统所有的spi设备对应的struct spi_board_info对象。 
    struct boardinfo就是对struct spi_board_info的一个简单封装,定义如下:
287 struct boardinfo {
288         struct list_head        list;
289         struct spi_board_info   board_info;
290 };
 
 
  • 1
  • 2
  • 3
  • 4

在kernel/drivers/spi/spi.c中的515行左右,就是将struct boardinfo对象添加到board_list

501 int spi_register_board_info(struct spi_board_info const *info, unsigned n)
502 {
503         struct boardinfo *bi;
504         int i;
505 
506         bi = kzalloc(n * sizeof(*bi), GFP_KERNEL);
507         if (!bi)
508                 return -ENOMEM;
509 
510         for (i = 0; i < n; i++, bi++, info++) {
511                 struct spi_master *master;
512 
513                 memcpy(&bi->board_info, info, sizeof(*info));
514                 mutex_lock(&board_lock);
515                 list_add_tail(&bi->list, &board_list);
516                 list_for_each_entry(master, &spi_master_list, list)
517                         spi_match_master_to_boardinfo(master, &bi->board_info);
518                 mutex_unlock(&board_lock);
519         }
520 
521         return 0;
522 }
 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 遍历spi_master_list上的所有struct spi_master对象,并将上面一步spi_board_info.bus_num和spi_master.bus_num做比较,相等则创建struct spi_device对象并注册。 
    spi_master_list记录系统中所有的spi控制器。
    spi_master.bus_numspi_board_info.bus_num相等,则说明该spi_board_info所对应的spi设备连接在该spi控制器上。我们的最终目的是创建并注册struct spi_device。在前面的文章说到,struct spi_device`在内核中才真正代表一个spi设备。 
    下面看看spi_master和spi_board_info是如何匹配的,在kernel/drivers/spi/spi.c:
468 static void spi_match_master_to_boardinfo(struct spi_master *master,
469                                 struct spi_board_info *bi)
470 {
471         struct spi_device *dev;
472 
473         if (master->bus_num != bi->bus_num)
474                 return;
475 
476         dev = spi_new_device(master, bi);
477         if (!dev)
478                 dev_err(master->dev.parent, "can't create new device for %s\n",
479                         bi->modalias);
480 }
 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

通过代码我们可以看到,其实就是spi_master.bus_numspi_board_info.bus_num的简单比较。匹配的话就调用spi_new_device()创建struct spi_device对象并注册,在kernel/drivers/spi/spi.c中:

430 struct spi_device *spi_new_device(struct spi_master *master,
431                                   struct spi_board_info *chip)
432 {
433         struct spi_device       *proxy;
434         int                     status;
435 
436         /* NOTE:  caller did any chip->bus_num checks necessary.
437          *
438          * Also, unless we change the return value convention to use
439          * error-or-pointer (not NULL-or-pointer), troubleshootability
440          * suggests syslogged diagnostics are best here (ugh).
441          */
442 
443         proxy = spi_alloc_device(master);
444         if (!proxy)
445                 return NULL;
446 
447         WARN_ON(strlen(chip->modalias) >= sizeof(proxy->modalias));
448 
449         proxy->chip_select = chip->chip_select;
450         proxy->max_speed_hz = chip->max_speed_hz;
451         proxy->mode = chip->mode;
452         proxy->irq = chip->irq;
453         strlcpy(proxy->modalias, chip->modalias, sizeof(proxy->modalias));
454         proxy->dev.platform_data = (void *) chip->platform_data;
455         proxy->controller_data = chip->controller_data;
456         proxy->controller_state = NULL;
457 
458         status = spi_add_device(proxy);
459         if (status < 0) {
460                 spi_dev_put(proxy);
461                 return NULL;
462         }
463 
464         return proxy;
465 }
466 EXPORT_SYMBOL_GPL(spi_new_device);
 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37

至此,我们就向系统中注册了一个spi设备。具体到我们这个例子就是想系统中注册了一个名为”m25p80”的spi设备。

2.2)device tree的方式

下面以am33xx的一个平台,讲解如何使用device tree来向内核中添加一个spi设备,首先看下面的device tree代码片段kernel/arch/arm/boot/dts/am335x-evm.dts:

&spi0 {
    status = "okay";
    spi-flash@0 {
        compatible = "spansion,s25fl064k", "m25p80";
        spi-max-frequency = <24000000>;
        reg = <0>;
    };
};                                                  
 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

这个device tree最外面的节点spi0描述的就是一个spi控制器,这个我们已经在上篇博文中说过了,这里面就是上篇博文提到的spi0。这里我们主要关注子节点。 
在spi控制器的每一个子节点,都会被内核解析成一个spi设备,最后生成一个struct spi_device,并注册到内核中,我们这个节点也不例外。这里的spi-flash节点就会被解析成一个struct spi_device对象。那么在什么时候这些子节点被解析成呢?这些子节点是在它的父节点对应的控制器被注册时解析的,也就是说是在调用spi_register_master()向内核注册一个spi控制器时,在这个函数中被解析。这个函数的的最后调用了of_register_spi_devices(master),这个函数的主要目的就是遍历struct spi_master对象所对应的device tree节点的所有子节点,并使用子节点中的属性信息创建对应的struct spi_device,然后注册至内核中。

3、spi设备驱动


上面的两种方法注册spi设备,讲的都是m25p80这个spi flash设备,spi设备的驱动,我们也讲解m25p80的驱动。代码在kernel/drivers/mtd/devices/m25p80.c。

先看代码的最后一段:

/* 定义个一个struct spi_driver*/
static struct spi_driver m25p80_driver = {
    .driver = {
        .name   = "m25p80",
        .owner  = THIS_MODULE,
    },
    .id_table   = m25p_ids,
    .probe  = m25p_probe,
    .remove = m25p_remove,

    /* REVISIT: many of these chips have deep power-down modes, which
     * should clearly be entered on suspend() to minimize power use.
     * And also when they're otherwise idle...
     */
};
/* 注册struct spi_driver对象至内核 */
module_spi_driver(m25p80_driver);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("Mike Lavender");
MODULE_DESCRIPTION("MTD SPI driver for ST M25Pxx flash chips");
 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21

这里其实是比较固定的格式,就是定义一个struct spi_driver对象,填写struct spi_driver.probestruct spi_driver.removestruct spi_driver.id_table等项目,然后注册这个struct spi_driver对象至内核。

首先看一下struct spi_driver.id_table,这个项会在struct spi_devicestruct spi_driver对象进行匹配时使用到。id_table就是struct spi_device_id数组。我们先看struct spi_device_id结构的原型:

struct spi_device_id {   
    char name[SPI_NAME_SIZE]; 
    kernel_ulong_t driver_data  /* Data private to the driver */
            __attribute__((aligned(sizeof(kernel_ulong_t))));
};
 
 
  • 1
  • 2
  • 3
  • 4
  • 5

其实struct spi_device_id记录的就是spi设备的名字和驱动相关的数据。我们的驱动中,struct spi_driver.id_table赋值为m25p_ids[],这个数组的部分代码如下:

static const struct spi_device_id m25p_ids[] = {
    /* Atmel -- some are (confusingly) marketed as "DataFlash" */
    { "at25fs010",  INFO(0x1f6601, 0, 32 * 1024,   4, SECT_4K) },
    { "at25fs040",  INFO(0x1f6604, 0, 64 * 1024,   8, SECT_4K) },

    { "at25df041a", INFO(0x1f4401, 0, 64 * 1024,   8, SECT_4K) },
    { "at25df321a", INFO(0x1f4701, 0, 64 * 1024,  64, SECT_4K) },
    { "at25df641",  INFO(0x1f4800, 0, 64 * 1024, 128, SECT_4K) },
    /* {......} */
    /* ST Microelectronics -- newer production may have feature updates */
    { "m25p05",  INFO(0x202010,  0,  32 * 1024,   2, 0) },
    { "m25p10",  INFO(0x202011,  0,  32 * 1024,   4, 0) },
    { "m25p20",  INFO(0x202012,  0,  64 * 1024,   4, 0) },
    { "m25p40",  INFO(0x202013,  0,  64 * 1024,   8, 0) },
    { "m25p80",  INFO(0x202014,  0,  64 * 1024,  16, 0) },
    { "m25p16",  INFO(0x202015,  0,  64 * 1024,  32, 0) },
    { "m25p32",  INFO(0x202016,  0,  64 * 1024,  64, 0) },
    { "m25p64",  INFO(0x202017,  0,  64 * 1024, 128, 0) },
    { "m25p128", INFO(0x202018,  0, 256 * 1024,  64, 0) },
    { "n25q032", INFO(0x20ba16,  0,  64 * 1024,  64, 0) },
    /*  {......}  */
};
 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22

其实这个数组就是列举了一些这个驱动所支持的spi flash设备。

下面简单的说一下struct spi_driver.probe接口,挑一些关键代码,其余省略:

static int m25p_probe(struct spi_device *spi)
{   
    const struct spi_device_id  *id = spi_get_device_id(spi); 
    struct flash_platform_data  *data;
    struct m25p         *flash;
    struct flash_info       *info;
    unsigned            i;    
    struct mtd_part_parser_data ppdata;
    struct device_node __maybe_unused *np = spi->dev.of_node;

#ifdef CONFIG_MTD_OF_PARTS
    if (!of_device_is_available(np))  /* 解析device tree中配置的partition */
        return -ENODEV;  
#endif

    /* {......} */

    info = (void *)id->driver_data; /* 获取spi_device_id中的driver_data*/

    /*  {......}  */

    /* 创建flash设备 */
    flash = kzalloc(sizeof *flash, GFP_KERNEL);
    if (!flash)
        return -ENOMEM;
    flash->command = kmalloc(MAX_CMD_SIZE + (flash->fast_read ? 1 : 0),
                    GFP_KERNEL);
    if (!flash->command) {
        kfree(flash);
        return -ENOMEM;
    }

    flash->spi = spi;
    mutex_init(&flash->lock);
    dev_set_drvdata(&spi->dev, flash);

    /* {......} */
    /* 设置mtd设备的信息和接口*/
    if (data && data->name)
        flash->mtd.name = data->name;
    else
        flash->mtd.name = dev_name(&spi->dev);

    flash->mtd.type = MTD_NORFLASH; /*类型为norflash*/
    flash->mtd.writesize = 1;        /*写的大小*/
    flash->mtd.flags = MTD_CAP_NORFLASH;
    flash->mtd.size = info->sector_size * info->n_sectors; /* mtd总大小*/                                                                                                              
    flash->mtd._erase = m25p80_erase; /* mtd 擦接口*/
    flash->mtd._read = m25p80_read; /* mtd 读接口*/

    /* 下面设置写接口,和一次擦除的大小 */
    /* sst flash chips use AAI word program */
    if (JEDEC_MFR(info->jedec_id) == CFI_MFR_SST)
        flash->mtd._write = sst_write;
    else
        flash->mtd._write = m25p80_write;

    /* prefer "small sector" erase if possible */
    if (info->flags & SECT_4K) {
        flash->erase_opcode = OPCODE_BE_4K;                                                                                                                              
        flash->mtd.erasesize = 4096;
    } else {
        flash->erase_opcode = OPCODE_SE;
        flash->mtd.erasesize = info->sector_size;
    }
    /* {......} */
    /* partitions should match sector boundaries; and it may be good to
     * use readonly partitions for writeprotected sectors (BP2..BP0).
     */
    /* 注册mtd设备至内核 */ 
    return mtd_device_parse_register(&flash->mtd, NULL, &ppdata,
            data ? data->parts : NULL,
            data ? data->nr_parts : 0);
}
 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74

probe接口主要的目的就是为了设置mtd的各个接口,并注册一个mtd设备到内核中。spi设备驱动的主要工作就是实现probe接口,并在该接口中实现所需的设备(字符/块/网络),并实现该类设备的访问接口。

4、总结


下面简单总结一下实现一个spi设备驱动所需要的步骤。

第一步:向内核注册一个struct spi_device对象。可以通过spi_board_info的方式,如果支持device tree,也可以使用device tree的方式来注册。

第二步:实现一个struct spi_driver对象,并实现probe和remove和id_table接口。

第三步:在struct spi_driver的probe接口中实现注册所需类型的设备的代码,并实现该类设备的访问接口。

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值