设备树节点 之 plateform_device 2

📅 2026/8/22 2:00:06
设备树节点 之 plateform_device 2
一、无设备树代码// 板级文件 board-demo.c static struct resource demo_res[] { DEFINE_RES_MEM(0x12340000, 0x1000), }; static struct platform_device demo_pdev { .name old-demo-dev, .resource demo_res, .num_resources ARRAY_SIZE(demo_res), };// 驱动内部私有数据 struct my_priv { void __iomem *io_base; resource_size_t phys; resource_size_t size; }; static int old_probe(struct platform_device *pdev) { struct my_priv *priv; struct resource *res; priv devm_kzalloc(...); res platform_get_resource(pdev, IORESOURCE_MEM, 0); priv-io_base devm_ioremap_resource(pdev-dev, res); platform_set_drvdata(pdev, priv); // 私有数据绑定设备 }二、有设备树驱动通常大一个.c 文件 中 如my_platform_driver_sample.cstruct my_priv { void __iomem *io_base0; void __iomem *io_base1; }; static int dt_probe(struct platform_device *pdev) { struct my_priv *priv; struct resource res; priv devm_kzalloc(...); // OF API 解析dts reg资源 of_address_to_resource(pdev-of_node, 0, res); priv-io_base0 devm_ioremap_resource(pdev-dev, res); platform_set_drvdata(pdev, priv); return 0; }三、结论点而是自定义私有结构体通过 platform_set_drvdata() 绑定到 struct device pdev-dev上方便整个 platform driver 各个函数取。私有数据是自己定义的结构体通过 drvdata 挂载到 struct device platform_device内嵌标准流程你日常写驱动的范式1. 自己定义 xxx_priv 私有结构体存放 ioremap 虚拟地址、irq、配置、锁等2. probe分配 priv解析dts资源、ioremap3. platform_set_drvdata(pdev, priv); → 把 priv 挂到设备上4. 在中断服务、work、定时器、remove 函数中struct my_priv { void __iomem *io_base0; void __iomem *io_base1; }; struct my_priv *priv platform_get_drvdata(pdev); /**** // 绑定私有数据到 device void platform_set_drvdata(struct platform_device *pdev, void *data); // 获取私有数据 void *platform_get_drvdata(struct platform_device *pdev); #等价底层 dev_set_drvdata(pdev-dev, data); dev_get_drvdata(pdev-dev); ***/四、resource 操作函数输入输出用途platform_get_resource(pdev, IORESOURCE_MEM, 0)设备资源名物理地址范围 (resource)描述硬件位置devm_ioremap_resource()resource虚拟地址 (void __iomem *)供 CPU 访问寄存器platform_get_resource(pdev, IORESOURCE_IRQ, 0)得到中断资源platform_get_irqpdev 0得到中断资源ret devm_request_irq(pdev-dev, priv-irq, my_irq_handler,IRQF_SHARED, my-device, priv);注册中断#include linux/module.h #include linux/platform_device.h #include linux/io.h struct my_priv { void __iomem *io_base0; /* ← 虚拟地址 */ phys_addr_t phys_base; /* ← 物理地址仅用于打印/记录 */ int irq; /* 中断号 */ }; /* 中断服务程序 (ISR) */ static irqreturn_t my_irq_handler(int irq, void *dev_id) { struct my_priv *priv dev_id; u32 status; /* 通过虚拟地址读取中断状态寄存器 */ status readl(priv-io_base0 0x08); if (status 0x01) { /* 处理中断 */ pr_info(IRQ triggered! status0x%x\n, status); /* 写 1 清除中断标志 */ writel(status, priv-io_base0 0x08); return IRQ_HANDLED; } return IRQ_NONE; } static int my_remove(struct platform_device *pdev) { struct my_priv *priv platform_get_drvdata(pdev); /* 先关闭硬件中断再卸载驱动 */ writel(0x00, priv-io_base0 0x0C); /* devm_request_irq 和 devm_ioremap_resource 会自动清理 * 不需要手动 free_irq / iounmap */ return 0; } static int my_probe(struct platform_device *pdev) { struct my_priv *priv; struct resource *res; priv devm_kzalloc(pdev-dev, sizeof(*priv), GFP_KERNEL); if (!priv) return -ENOMEM; /* 1. 从设备树获取物理地址资源 */ res platform_get_resource(pdev, IORESOURCE_MEM, 0); if (!res) { dev_err(pdev-dev, Failed to get memory resource\n); return -ENODEV; } /* res-start 是物理地址 */ priv-phys_base res-start; dev_info(pdev-dev, Physical address: 0x%pa, size: 0x%llx\n, priv-phys_base, (unsigned long long)resource_size(res)); /* 2. 映射为虚拟地址 */ priv-io_base0 devm_ioremap_resource(pdev-dev, res); if (IS_ERR(priv-io_base0)) { dev_err(pdev-dev, Failed to remap resource\n); return PTR_ERR(priv-io_base0); } /* io_base0 是虚拟地址可以直接用 readl/writel 访问 */ dev_info(pdev-dev, Virtual address: %p\n, priv-io_base0); /* 2. 获取中断号 */ /* 方式 Aplatform_get_irq() 推荐 */ priv-irq platform_get_irq(pdev, 0); if (priv-irq 0) { dev_err(pdev-dev, No IRQ resource\n); return priv-irq; } /* 方式 B老代码platform_get_resource(pdev, IORESOURCE_IRQ, 0) dev_info(pdev-dev, phys0x%pa, virt%p, irq%d\n, res-start, priv-io_base0, priv-irq); */ /* 3. 通过虚拟地址读写寄存器 */ u32 val readl(priv-io_base0 0x00); /* 读偏移 0x00 的寄存器 */ writel(0x1, priv-io_base0 0x04); /* 向偏移 0x04 写 0x1 */ /* 注意绝对不能直接用物理地址访问 */ // readl(priv-phys_base); /* ❌ 错误会崩溃或读不到正确数据 */ /* 3. 注册中断处理函数 */ ret devm_request_irq(pdev-dev, priv-irq, my_irq_handler, IRQF_SHARED, my-device, priv); if (ret) { dev_err(pdev-dev, Failed to request IRQ %d\n, priv-irq); return ret; } platform_set_drvdata(pdev, priv); return 0; } static const struct of_device_id my_of_match[] { { .compatible vendor,my-device }, { } }; MODULE_DEVICE_TABLE(of, my_of_match); static struct platform_driver my_driver { .probe my_probe, .remove my_remove, .driver { .name my-device, .of_match_table my_of_match, }, }; module_platform_driver(my_driver); MODULE_LICENSE(GPL);