Linux spi驱动 (三)

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设备到内核中的。
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 };

下面就结合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 };

一个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总线的工作模式。

注册spi_dev

46         spi_register_board_info(mx51_3ds_spi_nor_device,
147                      ARRAY_SIZE(mx51_3ds_spi_nor_device));

在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 }

遍历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_num和spi_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 }

通过代码我们可以看到,其实就是spi_master.bus_num和spi_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);

使用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>;
    };
};         

这个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,然后注册至内核中。

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");
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);
}

匹配成功 spi_driver侧拿到spi_device中的spi_master,然后使用控制器的spi_transfer进行设备的读写

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值