Linux设备驱动模型 SPI之一

来源:互联网 发布:电脑怎么连接宽带网络 编辑:程序博客网 时间:2024/06/05 23:40

在前一篇博客当中,我谈了谈自己对于Linux设备驱动模型的一些理解,在这篇博客里,我准备以TI Davinci DM368 的SPI接口为例来谈一谈设备驱动相关的一些知识

首先回顾一下上篇博客总结的数据收发流程

协议 (符合特定外设需求的格式化数据,协议由驱动之上的驱动产生)—〉platform driver (平台设备驱动)—〉platform device(平台设备)—〉外设接口所连接的设备

这里面有四个要素:协议、platform driver、platform device、外设接口所连接的设备。那么这四个要素具体的代码在哪里呢?又是如何实现的呢?

假设SPI接口外部连接的是EEPROM,那么我们需要分析的要素变为三个:协议、platform driver、platform device。

  1. 定义SPI platform device并注册
  2. 定义SPIplatform driver并注册
  3. 定义协议(也就是能够驱动EEPROM的驱动程序)

在这篇博客中,我们先找到与这三个要素相关的代码,在下篇博客中详细分析这些代码是如何组成一个有机的整体,并形成一个在用户空间能够应用的一个设备文件的。

platform driver、platform device、协议代码分析

在Linux内核的架构下…\arch\arm\mach-davinci\dm365.c中有关于SPI platform device的定义和初始化函数(其中涵盖了注册SPI platform device)

static u64 dm365_spi0_dma_mask = DMA_BIT_MASK(32);//dma 掩码static struct davinci_spi_platform_data dm365_spi0_pdata = {    .version    = SPI_VERSION_1,    .num_chipselect = 2,    .dma_event_q    = EVENTQ_3,    .prescaler_limit = 1,};// spi 私有化数据static struct resource dm365_spi0_resources[] = {    {        .start = 0x01c66000,        .end   = 0x01c667ff,        .flags = IORESOURCE_MEM,    },    {        .start = IRQ_DM365_SPIINT0_0,        .flags = IORESOURCE_IRQ,    },    {        .start = 17,        .flags = IORESOURCE_DMA,    },    {        .start = 16,        .flags = IORESOURCE_DMA,    },};//dm365 spi0所占有的资源static struct platform_device dm365_spi0_device = {    .name = "spi_davinci",//与platform driver的相同,用于与platform_driver的匹配    .id = 0,    .dev = {        .dma_mask = &dm365_spi0_dma_mask,        .coherent_dma_mask = DMA_BIT_MASK(32),        .platform_data = &dm365_spi0_pdata,    },    .num_resources = ARRAY_SIZE(dm365_spi0_resources),    .resource = dm365_spi0_resources,};// dm365 spi platform device资源的整合,也就是SPI platform devicevoid __init dm365_init_spi0(unsigned chipselect_mask,        const struct spi_board_info *info, unsigned len){    davinci_cfg_reg(DM365_SPI0_SCLK);    davinci_cfg_reg(DM365_SPI0_SDI);    davinci_cfg_reg(DM365_SPI0_SDO);    /* not all slaves will be wired up */    if (chipselect_mask & BIT(0))        davinci_cfg_reg(DM365_SPI0_SDENA0);    if (chipselect_mask & BIT(1))        davinci_cfg_reg(DM365_SPI0_SDENA1);    spi_register_board_info(info, len);    platform_device_register(&dm365_spi0_device);//注册dm365 spi0也就是注册SPI platform device}

在…\arch\arm\mach-davinci\board-dm365-evm.c中定义了EEPROM的驱动(也就是上文中说的协议)

static struct spi_eeprom at25640 = {    .byte_len   = SZ_64K / 8,    .name       = "at25640",    .page_size  = 32,    .flags      = EE_ADDR2,};static struct spi_board_info dm365_evm_spi_info[] __initconst = {    {        .modalias   = "at25",//驱动的名字(也就是产生协议相关的格式化数据)我们可以在linux内核driver文件下找到相关的驱动文件        .platform_data  = &at25640,        .max_speed_hz   = 10 * 1000 * 1000,        .bus_num    = 0,        .chip_select    = 0,        .mode       = SPI_MODE_0,    },};//spi 接口所连接的设备信息,其中modalias用于指定该外部设备所用的协议(也就是驱动程序)的名字
static __init void dm365_evm_init(void){    int ret;    ret = dm365_gpio_register();    if (ret)        pr_warn("%s: GPIO init failed: %d\n", __func__, ret);    evm_init_i2c();    davinci_serial_init(dm365_serial_device);    dm365evm_emac_configure();    dm365evm_mmc_configure();    davinci_setup_mmc(0, &dm365evm_mmc_config);    dm365_init_video(&vpfe_cfg, &dm365evm_display_cfg);    /* maybe setup mmc1/etc ... _after_ mmc0 */    evm_init_cpld();#ifdef CONFIG_SND_DM365_AIC3X_CODEC    dm365_init_asp(&dm365_evm_snd_data);#elif defined(CONFIG_SND_DM365_VOICE_CODEC)    dm365_init_vc(&dm365_evm_snd_data);#endif    dm365_init_rtc();    dm365_init_ks(&dm365evm_ks_data);    dm365_init_spi0(BIT(0), dm365_evm_spi_info,            ARRAY_SIZE(dm365_evm_spi_info));}

在…\drivers\spi\spi-davinci.c可以找到platform driver的定义

static struct platform_driver davinci_spi_driver = {    .driver = {        .name = "spi_davinci",//与platform device相同,用于与platform device的匹配        .of_match_table = of_match_ptr(davinci_spi_of_match),    },    .probe = davinci_spi_probe,    .remove = davinci_spi_remove,};

在…\drivers\base\platform.c有platform driver的注册函数

int platform_driver_register(struct platform_driver *drv){    drv->driver.bus = &platform_bus_type;    if (drv->probe)        drv->driver.probe = platform_drv_probe;    if (drv->remove)        drv->driver.remove = platform_drv_remove;    if (drv->shutdown)        drv->driver.shutdown = platform_drv_shutdown;    return driver_register(&drv->driver);}

总结:platform driver与platform device都注册在 platform bus上,然后根据他们的name域进行匹配,如果匹配成功,则驱动与设备绑定在一起。外设接口所连接的外部设备与协议(外部设备的驱动程序)也经历相似的匹配过程。

之所以一直强调协议(也就是外部设备驱动),主要是为了区分platform driver。

PS:在下篇博客中将详细分析这四个要素之间是如何建立联系的。

0 0
原创粉丝点击