//spi分析//刘术河//2017.05.10
1.spi驱动框架//核心层函数spi.cdrivers\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_controllerspi_sync __spi_sync(spi, message, 0); spi_async_locked(spi, message); __spi_async(spi, message); master->transfer(spi, message);
4.spi_masterspi_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_probes3c24xx_spi_probe spi_alloc_master hw->pdata = pdata = pdev->dev.platform_data; //这里需要读取平台设备的dev.platform_data平台数据,但是内核没有定义,所以要添加dev.platform_data5.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
转载于:https://www.cnblogs.com/liushuhe1990/p/9608974.html
相关资源:linux驱动 SPI源码框架分析