spi的platform_device

本文详细介绍了SPI控制器的配置过程,包括资源分配、中断设置及DMA掩码定义等内容。通过具体的代码示例,展示了如何为不同的SPI接口设定从设备参数,并注册SPI板载设备信息。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

在dev-spi.c中

/* SPI (0) */
static struct resource s3c_spi0_resource[] = {
[0] = {
.start = S3C_PA_SPI0,
.end   = S3C_PA_SPI0 + S3C_SZ_SPI0 - 1,
.flags = IORESOURCE_MEM,
},
[1] = {
.start = IRQ_SPI0,
.end   = IRQ_SPI0,
.flags = IORESOURCE_IRQ,
}
};
static struct s3c_spi_mstr_info sspi0_mstr_info = {
.pdev = NULL,
.clk = NULL,
.prnt_clk = NULL,
.num_slaves = 0,
.spiclck_get = smi_getclcks,
.spiclck_put = smi_putclcks,
.spiclck_en = smi_enclcks,
.spiclck_dis = smi_disclcks,
.spiclck_setrate = smi_setrate,
.spiclck_getrate = smi_getrate,
};
static u64 s3c_device_spi0_dmamask = 0xffffffffUL;
struct platform_device s3c_device_spi0 = {
.name = "s3c-spi",
.id = 0,
.num_resources = ARRAY_SIZE(s3c_spi0_resource),
.resource = s3c_spi0_resource,
.dev = {
.dma_mask = &s3c_device_spi0_dmamask,
.coherent_dma_mask = 0xffffffffUL,
.platform_data = &sspi0_mstr_info,
}
};
EXPORT_SYMBOL(s3c_device_spi0);

/* SPI (1) */
static struct resource s3c_spi1_resource[] = {
[0] = {
.start = S3C_PA_SPI1,
.end   = S3C_PA_SPI1 + S3C_SZ_SPI1 - 1,
.flags = IORESOURCE_MEM,
},
[1] = {
.start = IRQ_SPI1,
.end   = IRQ_SPI1,
.flags = IORESOURCE_IRQ,
}
};
static struct s3c_spi_mstr_info sspi1_mstr_info = {
.pdev = NULL,
.clk = NULL,
.prnt_clk = NULL,
.num_slaves = 0,
.spiclck_get = smi_getclcks,
.spiclck_put = smi_putclcks,
.spiclck_en = smi_enclcks,
.spiclck_dis = smi_disclcks,
.spiclck_setrate = smi_setrate,
.spiclck_getrate = smi_getrate,
};
static u64 s3c_device_spi1_dmamask = 0xffffffffUL;
struct platform_device s3c_device_spi1 = {
.name = "s3c-spi",
.id = 1,
.num_resources = ARRAY_SIZE(s3c_spi1_resource),
.resource = s3c_spi1_resource,
.dev = {
.dma_mask = &s3c_device_spi1_dmamask,
.coherent_dma_mask = 0xffffffffUL,
.platform_data = &sspi1_mstr_info,
}
};
EXPORT_SYMBOL(s3c_device_spi1);

/* SPI (2) */
static struct resource s3c_spi2_resource[] = {
[0] = {
.start = S3C_PA_SPI2,
.end   = S3C_PA_SPI2 + S3C_SZ_SPI2 - 1,
.flags = IORESOURCE_MEM,
},
[1] = {
.start = IRQ_SPI2,
.end   = IRQ_SPI2,
.flags = IORESOURCE_IRQ,
}
};
static struct s3c_spi_mstr_info sspi2_mstr_info = {
.pdev = NULL,
.clk = NULL,
.prnt_clk = NULL,
.num_slaves = 0,
.spiclck_get = smi_getclcks,
.spiclck_put = smi_putclcks,
.spiclck_en = smi_enclcks,
.spiclck_dis = smi_disclcks,
.spiclck_setrate = smi_setrate,
.spiclck_getrate = smi_getrate,
};
static u64 s3c_device_spi2_dmamask = 0xffffffffUL;
struct platform_device s3c_device_spi2 = {
.name = "s3c-spi",
.id = 2,
.num_resources = ARRAY_SIZE(s3c_spi2_resource),
.resource = s3c_spi2_resource,
.dev = {
.dma_mask = &s3c_device_spi2_dmamask,
.coherent_dma_mask = 0xffffffffUL,
.platform_data = &sspi2_mstr_info,
}
};
EXPORT_SYMBOL(s3c_device_spi2);

void __init s3cspi_set_slaves(unsigned id, int n, struct s3c_spi_pdata const *dat)
{
struct s3c_spi_mstr_info *pinfo;

if(id == 0)
  pinfo = (struct s3c_spi_mstr_info *)s3c_device_spi0.dev.platform_data;
else if(id == 1)
  pinfo = (struct s3c_spi_mstr_info *)s3c_device_spi1.dev.platform_data;
else if(id == 2)
  pinfo = (struct s3c_spi_mstr_info *)s3c_device_spi2.dev.platform_data;
else
  return;

pinfo->spd = kmalloc(n * sizeof (*dat), GFP_KERNEL);
if(!pinfo->spd)
  return;
memcpy(pinfo->spd, dat, n * sizeof(*dat));

pinfo->num_slaves = n;
}



在板初始化中调用:/mach-smdkv210.c

static void __init smdkv210_machine_init(void)
{
.......
#if defined(CONFIG_SPI_CNTRLR_0)
        s3cspi_set_slaves(BUSNUM(0), ARRAY_SIZE(s3c_slv_pdata_0), s3c_slv_pdata_0);
#endif
#if defined(CONFIG_SPI_CNTRLR_1)
        s3cspi_set_slaves(BUSNUM(1), ARRAY_SIZE(s3c_slv_pdata_1), s3c_slv_pdata_1);
#endif
        spi_register_board_info(s3c_spi_devs, ARRAY_SIZE(s3c_spi_devs));
..........
}

#if defined(CONFIG_SPI_CNTRLR_0)
static struct s3c_spi_pdata s3c_slv_pdata_0[] __initdata = {
        [0] = { /* Slave-0 */
                .cs_level     = CS_FLOAT,
                .cs_pin       = S5PV210_GPB(1),
                .cs_mode   = S5PV210_GPB_OUTPUT(1),
                .cs_set       = s3c_cs_set,
                .cs_config  = s3c_cs_config,
                .cs_suspend   = s3c_cs_suspend,
                .cs_resume    = s3c_cs_resume,
        },
};
#endif

#if defined(CONFIG_SPI_CNTRLR_1)
static struct s3c_spi_pdata s3c_slv_pdata_1[] __initdata = {
        [0] = { /* Slave-0 */
                .cs_level     = CS_FLOAT,
                .cs_pin       = S5PV210_GPB(5),
                .cs_mode      = S5PV210_GPB_OUTPUT(5),
                .cs_set       = s3c_cs_set,
                .cs_config    = s3c_cs_config,
                .cs_suspend   = s3c_cs_suspend,
                .cs_resume    = s3c_cs_resume,
        },
};
#endif

static struct spi_board_info s3c_spi_devs[] __initdata = {
#if defined(CONFIG_SPI_CNTRLR_0)
        [0] = {
                .modalias         = "spidev", /* Test Interface */
                .mode             = SPI_MODE_0,  /* CPOL=0, CPHA=0 */
                .max_speed_hz = 100000,
                /* Connected to SPI-0 as 1st Slave */
                .bus_num         = 0,
                .irq             = IRQ_SPI0,
                .chip_select     = 0,
        },
#endif
#if defined(CONFIG_SPI_CNTRLR_1)
        [1] = {
                .modalias         = "spidev", /* Test Interface */
                .mode             = SPI_MODE_0,  /* CPOL=0, CPHA=0 */
                .max_speed_hz  = 100000,
                /* Connected to SPI-1 as 1st Slave */
                .bus_num         = 1,
                .irq             = IRQ_SPI1,
                .chip_select     = 0,
        },
#endif
};

<think>我们正在讨论Linux设备模型中的三个关键结构体:platform_deviceplatform_driver和device_driver。 它们之间的关系是: 1. platform_device 代表一个具体的平台设备,通常描述SoC上的外设(如I2C控制器、SPI控制器等),它包含设备的资源(如内存地址、中断号等)。 2. device_driver 是一个通用的设备驱动结构体,包含驱动的基本操作(如probe、remove)和设备模型需要的字段。 3. platform_driver 是平台设备驱动,它内嵌了一个device_driver结构体,并扩展了平台设备特有的操作(如平台设备匹配和电源管理)。 具体关系如下: - platform_driver 结构体中包含一个 device_driver 结构体成员(通常命名为driver)。 - platform_driver 通过其内部的 device_driver 成员与内核设备模型关联。 - 当注册一个 platform_driver 时,实际上是通过其内部的 device_driver 注册到总线上(platform_bus_type)。 - platform_deviceplatform_driver 通过名称进行匹配(即platform_device的name字段和platform_driver的id_table或者driver.name匹配)。 - 匹配成功后,会调用 platform_driver 中的 probe 函数。 结构体定义(简化): ```c struct platform_device { const char *name; // 设备名称,用于匹配驱动 int id; struct device dev; // 内嵌的通用设备结构体 // ... 资源信息等 }; struct device_driver { const char *name; // 驱动名称 struct bus_type *bus; // 所属总线 int (*probe)(struct device *dev); // 探测设备 int (*remove)(struct device *dev); // 移除设备 // ... 其他操作 }; struct platform_driver { int (*probe)(struct platform_device *); // 平台设备的探测函数 int (*remove)(struct platform_device *); // 移除函数 struct device_driver driver; // 内嵌的通用设备驱动 const struct platform_device_id *id_table; // 设备匹配表 }; ``` 工作流程: 1. 系统启动时,平台设备platform_device)通常通过板级文件或设备树被注册到内核。 2. 驱动程序通过调用`platform_driver_register`注册一个platform_driver。 3. 内核的platform总线(platform_bus_type)会尝试将已注册的设备与驱动进行匹配。 4. 匹配依据:platform_driver的driver.name与platform_device的name匹配,或者platform_driver的id_table中的任一条目与platform_device的name匹配。 5. 匹配成功则调用platform_driver的probe函数(注意:实际调用的是platform_driver->driver.probe,但通常这个函数指针被设置为一个内部函数,该内部函数会调用platform_driver->probe,并将device转换为platform_device)。 注意:在platform_driver的初始化中,通常会将device_driver的probe函数设置为一个通用的平台驱动probe函数(例如platform_drv_probe),这个函数会调用platform_driver自己的probe函数。 总结: - platform_device 描述硬件设备。 - platform_driver 包含驱动该硬件设备的函数,并内嵌一个device_driver。 - device_driver 是设备驱动的通用部分,由platform_driver中的driver成员实现。 关系图: platform_driver -> 包含 device_driver platform_device -> 包含 device 在总线上,device_driver和device进行匹配,而实际上匹配的是platform_deviceplatform_driver。 §§相关问题§§ 1. 在内核中,platform_deviceplatform_driver是如何通过名称进行匹配的? 2. 当platform_driver注册时,其内嵌的device_driver的probe函数是如何被设置并最终调用到platform_driver的probe函数的? 3. 设备树(Device Tree)机制下,platform_device是如何被创建和注册的? 4. platform_device结构体中的struct device dev成员与device_driver结构体有何关联? 5. 在platform驱动模型中,为什么需要将device_driver内嵌在platform_driver中,而不是直接使用device_driver?</think>### Linux设备驱动模型核心结构体关系解析 #### 1. **基础层级关系** ```c struct device_driver { // 通用驱动抽象 const char *name; // 驱动名称 struct bus_type *bus; // 所属总线 int (*probe)(struct device *dev); // 探测设备 ... }; struct platform_driver { // 平台设备驱动 int (*probe)(struct platform_device *); struct device_driver driver; // 内嵌通用驱动结构 const struct platform_device_id *id_table; ... }; struct platform_device { // 平台设备实例 const char *name; // 设备名称 struct device dev; // 内嵌设备基类 struct resource *resource; // 设备资源 ... }; ``` #### 2. **关键交互机制** 1. **注册绑定流程** ```mermaid graph LR A[platform_device_register] --> B[注册到platform_bus] C[platform_driver_register] --> D[注册driver到platform_bus] B & D --> E[总线匹配driver.name与device.name] E --> F[调用platform_driver.probe] ``` 2. **结构体内嵌关系** - `platform_driver` 包含 `device_driver` 成员 - `platform_device` 包含 `device` 成员 - 通过`container_of`宏实现父子结构转换: ```c struct platform_device *pdev = container_of(dev, struct platform_device, dev); struct platform_driver *pdrv = container_of(drv, struct platform_driver, driver); ``` #### 3. **功能对比表** | 结构体 | 作用域 | 关键成员 | 生命周期管理 | |------------------|-------------|----------------------------|-------------------| | `device_driver` | 通用驱动模型 | name, bus, probe | 所有设备类型通用 | | `platform_driver`| 平台设备驱动 | driver, probe, id_table | 封装platform特有操作 | | `platform_device`| 平台设备实例 | name, dev, resource | 描述SoC固定资源 | #### 4. **典型驱动开发流程** ```c // 定义平台驱动 static struct platform_driver my_driver = { .probe = my_probe, .driver = { .name = "my_device", // 匹配设备名称 .owner = THIS_MODULE, }, }; // 设备探测函数 static int my_probe(struct platform_device *pdev) { // 1. 获取设备资源 struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0); // 2. 映射寄存器 void __iomem *base = devm_ioremap_resource(&pdev->dev, res); // 3. 注册字符设备等操作 ... } // 注册驱动 module_platform_driver(my_driver); ``` #### 5. **设备树(Device Tree)整合** 现代Linux使用设备树替代硬编码`platform_device`: ```dts my_device: my_device@0x12340000 { compatible = "vendor,my-device"; // 匹配驱动 reg = <0x12340000 0x1000>; // 寄存器空间 interrupts = <0 45 4>; // 中断号 }; ``` 驱动通过`of_match_table`匹配: ```c static const struct of_device_id my_of_ids[] = { { .compatible = "vendor,my-device" }, {} }; struct platform_driver my_driver = { .driver = { .of_match_table = my_of_ids, }, }; ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值