转载地址: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
791 char modalias[SPI_NAME_SIZE];
792 const void *platform_data;
793 void *controller_data;
794 int irq;
795
796
797 u32 max_speed_hz;
798
799
800
806 u16 bus_num;
807 u16 chip_select;
808
809
812 u8 mode;
813
814
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,
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总线的工作模式。
配置好这些参数以后,通过下面代码将这个struct spi_board_info
对象注册到内核中,确切说是到spi子系统中去:
146 spi_register_board_info(mx51_3ds_spi_nor_device,
147 ARRAY_SIZE(mx51_3ds_spi_nor_device));
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 };
在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_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
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>;
};
};
这个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。
先看代码的最后一段:
static struct spi_driver m25p80_driver = {
.driver = {
.name = "m25p80",
.owner = THIS_MODULE,
},
.id_table = m25p_ids,
.probe = m25p_probe,
.remove = m25p_remove,
};
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.probe
,struct spi_driver.remove
和struct spi_driver.id_table
等项目,然后注册这个struct spi_driver
对象至内核。
首先看一下struct spi_driver.id_table
,这个项会在struct spi_device
和struct 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
__attribute__((aligned(sizeof(kernel_ulong_t))));
};
其实struct spi_device_id
记录的就是spi设备的名字和驱动相关的数据。我们的驱动中,struct spi_driver.id_table
赋值为m25p_ids[]
,这个数组的部分代码如下:
static const struct spi_device_id m25p_ids[] = {
{ "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) },
{ "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))
return -ENODEV;
#endif
info = (void *)id->driver_data;
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);
if (data && data->name)
flash->mtd.name = data->name;
else
flash->mtd.name = dev_name(&spi->dev);
flash->mtd.type = MTD_NORFLASH;
flash->mtd.writesize = 1;
flash->mtd.flags = MTD_CAP_NORFLASH;
flash->mtd.size = info->sector_size * info->n_sectors;
flash->mtd._erase = m25p80_erase;
flash->mtd._read = m25p80_read;
if (JEDEC_MFR(info->jedec_id) == CFI_MFR_SST)
flash->mtd._write = sst_write;
else
flash->mtd._write = m25p80_write;
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;
}
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接口中实现注册所需类型的设备的代码,并实现该类设备的访问接口。