Linux设备驱动模型SPI之二

本文深入剖析了TI Davinci DM368的SPI接口,涵盖平台设备、外部设备、平台驱动及外部驱动的注册流程。重点解释了spi_master、spi_driver和spi_device数据结构,揭示了设备与驱动如何通过总线连接,并讨论了源码追踪的方法。
摘要由CSDN通过智能技术生成

本文详细分析TI Davinci DM368 SPI接口的平台设备注册、外部设备注册、平台驱动注册、外部驱动程序注册。应该注意以下四点:

  1. 平台设备描述DM368芯片集成的SPI接口硬件描述
  2. 平台设备驱动描述如何控制DM368芯片的SPI接口,从而能够收发数据
  3. 外部设备描述的是挂在SPI接口上的设备(以下称为spi设备
  4. 外部驱动程序描述的是如何控制SPI接口上的外部设备(以下称为spi驱动

如下图所示平台设备与平台驱动通过平台总线连接在一起,类似的spi设备与spi驱动通过spi总线连接在一起
这里写图片描述

描述spi有三个数据结构:

  1. spi_master 描述SPI主机控制器,其实就是platform driver
  2. spi_driver 描述spi驱动
  3. spi_device 描述spi设备

spi_master嵌入在spi_device之中,也就是说spi_device中已经包含了spi控制器,更进一步讲,可以认为spi_device就是连接在spi接口上的spi设备,这样就将平台设备和平台设备驱动相关的知识给隐藏了。从而只专注于spi_driver、spi_device,他们两个也通过总线连接在一起。这样就和平台设备与平台设备驱动通过平台总线连接具有相似性,好处就是,可以实现内核代码公用。

这里写图片描述

本文对代码的追踪是按照调用顺序进行的

平台设备注册、平台设备驱动注册与连接

在…\arch\arm\mach-davinci\dm365.c之下

static struct platform_device dm365_spi0_device = {
    .name = "spi_davinci",
    .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,
};//定义了spi平台设备,name为“spi_davinci”,因此必然存在一个spi平台驱动,其名字为“spi_davinci”,注册在平台总线的设备通过这个名字进行匹配
void __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);//注册spi平台设备,因此在平台设备总线上挂着了一个spi平台设备
}
//先初始化设备,然后向platform bus上添加设备
int platform_device_register(struct platform_device *pdev)
{
    device_initialize(&pdev->dev);//初始化
    return platform_device_add(pdev);//添加设备
}
int platform_device_add(struct platform_device *pdev)
{
    int i, ret = 0;

    if (!pdev)
        return -EINVAL;

    if (!pdev->dev.parent)
        pdev->dev.parent = &platform_bus;//可以看到是在platform_bus上注册了一个设备,此处的platform_bus是一个全局变量

    pdev->dev.bus = &platform_bus_type;

    if (pdev->id != -1)
        dev_set_name(&pdev->dev, "%s.%d", pdev->name,  pdev->id);
    else
        dev_set_name(&pdev->dev, "%s", pdev->name);

    for (i = 0; i < pdev->num_resources; i++) {
        struct resource *p, *r = &pdev->resource[i];

        if (r->name == NULL)
            r->name = dev_name(&pdev->dev);

        p = r->parent;
        if (!p) {
            if (resource_type(r) == IORESOURCE_MEM)
                p = &iomem_resource;
            else if (resource_type(r) == IORESOURCE_IO)
                p = &ioport_resource;
        }

        if (p && insert_resource(p, r)) {
            printk(KERN_ERR
                   "%s: failed to claim resource %d\n",
                   dev_name(&pdev->dev), i);
            ret = -EBUSY;
            goto failed;
        }
    }

    pr_debug("Registering platform device '%s'. Parent at %s\n",
         dev_name(&pdev->dev), dev_name(pdev->dev.parent));

    ret = device_add(&pdev->dev);//添加设备
    if (ret == 0)
        return ret;

 failed:
    while (--i >= 0) {
   
        struct resource *r = &pdev->resource[i];
        unsigned long type = resource_type(r);

        if (type == IORESOURCE_MEM || type == IORESOURCE_IO)
            release_resource(r);
    }

    return ret;
}
//只选取了与我们分析最为密切的一些函数
int device_add(struct device *dev)
{
    //...
    error = bus_add_device(dev);//在总线上添加平台设备
    //...
    bus_probe_device(dev);//在总线上寻找与该平台设备相关的平台驱动
    //...
}
void bus_probe_device(struct device *dev)
{
    struct bus_type *bus = dev->bus;
    int ret;

    if (bus && bus->p->drivers_autoprobe) {
        ret = device_attach(dev);
        WARN_ON(ret < 0);
    }
}
//如果找到了平台驱动就将两者连接,没有则返回
//假如有,因为后面平台驱动的注册会有类似的匹配过程:平台驱动定义、平台驱动注册到平台总线上、在平台总线上寻找name匹配的平台驱动,下面我们忽略了平台设备驱动的代码分析过程,读者可以自行分析
int device_attach(struct device *dev)
{
    int ret = 0;

    down(&dev
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值