//spi分析
//刘术河
//2017.05.10
1.spi驱动框架
//核心层函数spi.c
drivers\spi\Spi.c
struct bus_type spi_bus_type = {
.name = "spi",
.dev_attrs = spi_dev_attrs,
.match = spi_match_device,
.uevent = spi_uevent,
.pm = &spi_pm,
};
static struct class spi_master_class = {
.name = "spi_master",
.owner = THIS_MODULE,
.dev_release = spi_master_release,
};
spi_init
bus_register(&spi_bus_type);
class_register(&spi_master_class);
1.板级信息
spi_register_board_info
list_add_tail(&bi->list, &board_list); //把spi_board_info,放入board_list链表,这个链表在spi_register_master会去遍历
list_for_each_entry(master, &spi_master_list, list) //遍历spi_master_list,这个链表是注册spi_register_master时放入的
//这就有意思了,注册board去遍历master的spi_master_list链表,应该是为了匹配用哪一个spi控制器
spi_match_master_to_boardinfo(master, &bi->board_info); //开始遍历
spi_new_device(master, bi);
spi_alloc_device(master);
spi_add_device(proxy); /* 根据名字找到spi_driver, 调用它的probe函数 */
spi_setup(spi);
status = spi->master->setup(spi);
device_add(&spi->dev); /* 会绑定到一个spi_driver */
bus_add_device(dev);
bus_probe_device(dev);
device_attach(dev);
device_bind_driver(dev);
bus_for_each_drv(dev->bus, NULL, dev, __device_attach);
driver_probe_device(drv, dev);
really_probe(dev, drv);
drv->probe(dev);
2.数据发送
spi_write
spi_message_init(&m); /* 一个不可打断的SPI传输过程: cs=0,传数据,cs=1 */
/* 一个spi_message由多个spi_transfer组成 */
spi_message_add_tail(&t, &m); /* spi_transfe是SPI上传输的单方向1个或多个字节 */
spi_sync(spi, &m); /* 启动传输并等待完成 */
3.spi_driver如何调用spi_controller
spi_sync
__spi_sync(spi, message, 0);
spi_async_locked(spi, message);
__spi_async(spi, message);
master->transfer(spi, message);
4.spi_master
spi_register_master
device_add(&master->dev);
spi_master_initialize_queue(master);
list_add_tail(&master->list, &spi_master_list); //将master控制器放入spi_master_list链表,这个链表在注册spi_register_board_info回去遍历
list_for_each_entry(bi, &board_list, list) //这里spi_master 遍历board_list,是为了找到匹配的一个板子,这个board_list是spi_register_board_info填入的
spi_match_master_to_boardinfo(master, &bi->board_info); //开始遍历
5.2440的spi控制器驱动
linux-3.4.2_lsh_01\drivers\spi\Spi-s3c24xx.c
static struct platform_driver s3c24xx_spi_driver = {
.probe = s3c24xx_spi_probe,
.remove = __devexit_p(s3c24xx_spi_remove),
.driver = {
.name = "s3c2410-spi",
.owner = THIS_MODULE,
.pm = S3C24XX_SPI_PMOPS,
},
};
//搜索"s3c2410-spi" 找到spi控制器平台驱动对应的平台设备
//linux-3.4.2_lsh_01\arch\arm\plat-samsung\Devs.c
//将这两个设备加到 \linux-3.4.2_lsh_01\arch\arm\mach-s3c24xx\Mach-smdk2440.c 里面
struct platform_device s3c_device_spi0 = {
.name = "s3c2410-spi",
.id = 0,
.num_resources = ARRAY_SIZE(s3c_spi0_resource),
.resource = s3c_spi0_resource,
.dev = {
.dma_mask = &samsung_device_dma_mask,
.coherent_dma_mask = DMA_BIT_MASK(32),
}
};
struct platform_device s3c_device_spi1 = {
.name = "s3c2410-spi",
.id = 1,
.num_resources = ARRAY_SIZE(s3c_spi1_resource),
.resource = s3c_spi1_resource,
.dev = {
.dma_mask = &samsung_device_dma_mask,
.coherent_dma_mask = DMA_BIT_MASK(32),
}
};
//\linux-3.4.2_lsh_01\arch\arm\mach-s3c24xx\Mach-smdk2440.c 里面
//就是加到这个数组里面,开机会加载这个数组里的设备
static struct platform_device *smdk2440_devices[] __initdata = {
&s3c_device_ohci,
&s3c_device_lcd,
&s3c_device_wdt,
&s3c_device_i2c0,
&s3c_device_iis,
&s3c_device_spi0,
&s3c_device_spi1,
};
5.1当Spi-s3c24xx.c,匹配到对应的spi控制器的平台设备后,会调用.probe = s3c24xx_spi_probe
s3c24xx_spi_probe
spi_alloc_master
hw->pdata = pdata = pdev->dev.platform_data; //这里需要读取平台设备的dev.platform_data平台数据,但是内核没有定义,所以要添加dev.platform_data
5.1.1给 platform_device s3c_device_spi1 加上 .platform_data = &spi0, //lsh 2017.05.10
例如
//lsh 2017.05.10
//lsh 2017.05.10
static struct s3c2410_spi_info spi0{
.num_cs = 0xffff, //支持多少个芯片的cs if (spi->chip_select >= spi->master->num_chipselect) {
//这里要确保 spi->chip_select >= spi->master->num_chipselect
.bus_num = 0, //第0个spi控制器,这个是用来和board来匹配用的
//(master->bus_num != bi->bus_num)
.set_cs = s3c_spi_set_cs, //片选引脚的设置函数,不能用内核自带的,自带有bug
};
//lsh 2017.05.10
static struct s3c2410_spi_info spi1{
.num_cs = 0xffff, //支持多少个芯片的cs if (spi->chip_select >= spi->master->num_chipselect) {
.bus_num = 1,
.set_cs = s3c_spi_set_cs,
};
struct platform_device s3c_device_spi0 = {
.name = "s3c2410-spi",
.id = 0,
.num_resources = ARRAY_SIZE(s3c_spi0_resource),
.resource = s3c_spi0_resource,
.dev = {
.dma_mask = &samsung_device_dma_mask,
.coherent_dma_mask = DMA_BIT_MASK(32),
.platform_data = &spi0, //lsh 2017.05.10
}
};
//设置 .set_cs = s3c_spi_set_cs,
//lsh 2017.05.10
static void s3c_spi_set_cs(struct s3c2410_spi_info *spi, int cs, int pol)
{
//写这个函数的目的是,hw->set_cs(hw->pdata, spi->chip_select, cspol^1);
//set_cs里传入的cs是spi_device->chip_select,而不是内核默认函数用s3c2410_spi_info->cs
gpio_set_value(cs, pol);
}
//lsh 2017.05.10
/*
//内核默认函数
//这是有bug,cs不能用s3c2410_spi_info->pin_cs
//因为s3c24xx_spi_chipsel(struct spi_device *spi, int value)
// hw->set_cs(hw->pdata, spi->chip_select, cspol^1);
static void s3c24xx_spi_gpiocs(struct s3c2410_spi_info *spi, int cs, int pol)
{
gpio_set_value(spi->pin_cs, pol);
}
*/
6. 编译安装驱动
6.1 配置内核使用主控驱动 spi-s3c24xx.c
-> General setup
[*] Prompt for development and/or incomplete code/drivers
-> Device Drivers
-> SPI support
<*> Samsung S3C24XX series SPI
make uImage