[导读] 前文分析了Linux设备驱动的驱动模型,本文来聊聊Platform_driver/Platform_device这个类。作嵌入式Linux的驱动,这个也是绕不开的,因此来学习分析总结一下。linux
上文阅读:微信
注:代码分析基于linux-5.4.31数据结构
前文谈到的总线驱动模型(注这个图是照着bootlin的文档绘制的):架构
同时,根据代码分析其基础数据结构框架关系以下(UML关系并不严谨,仅为理解方便):框架
可见驱动程序的模型分层有一层总线基础层,那么对于嵌入式开发领域而言,有不少SOC芯片内置了各类外设,并好比LCD,UART、audio、摄像头口等等,并无总线。为了统一驱动架构抽象,因此引入了platform bus这个虚拟的总线模型。作过嵌入式开发的人应该都有体会,这类设备在嵌入式系统中很是多,因此在研究具体某类设备的驱动开发以前,有必要研究platform 设备的驱动模型。在强调一下这个是统一在总线驱动模型这个体系内的。函数
定义在./include/linux/platform_device.h中,来梳理一下这些数据结构间的关系:学习
struct device platform_bus = { .init_name = "platform", };
EXPORT_SYMBOL_GPL(platform_bus); EXPORT_SYMBOL_GPL(__platform_driver_register); EXPORT_SYMBOL_GPL(__platform_driver_probe); EXPORT_SYMBOL_GPL(platform_get_resource_byname); EXPORT_SYMBOL_GPL(platform_get_irq_byname); ....
那么既然这条总线并不存在,每每并不能实现设备枚举、热插拔等功能。code
既然不能利用总线自动枚举,那么底层又是怎么玩的呢?实际上可选的有这样几种方式orm
平台设备是一般在系统中显示为自治实体的设备。 这包括基于旧端口的设备和到外围总线的主机桥接,以及集成到片上系统平台中的大多数控制器。 它们一般的共同点是从CPU总线直接寻址。 不多有platform_device经过某种其余类型的总线的一部分链接的。 但其寄存器仍将直接可寻址。blog
int platform_driver_register(struct platform_driver *drv);
uart0: serial@44e09000 { compatible = "ti,omap3-uart"; ti,hwmods = "uart1"; clock-frequency = <48000000>; reg = <0x44e09000 0x2000>; interrupts = <72>; status = "disabled"; };
以samsung.c 串口驱动程序为例:
/*兼容匹配表*/ static const struct platform_device_id s3c24xx_serial_driver_ids[] = { { .name = "s3c2410-uart", .driver_data = S3C2410_SERIAL_DRV_DATA, }, { .name = "s3c2412-uart", .driver_data = S3C2412_SERIAL_DRV_DATA, }, { .name = "s3c2440-uart", .driver_data = S3C2440_SERIAL_DRV_DATA, }, { .name = "s3c6400-uart", .driver_data = S3C6400_SERIAL_DRV_DATA, }, { .name = "s5pv210-uart", .driver_data = S5PV210_SERIAL_DRV_DATA, }, { .name = "exynos4210-uart", .driver_data = EXYNOS4210_SERIAL_DRV_DATA, }, { .name = "exynos5433-uart", .driver_data = EXYNOS5433_SERIAL_DRV_DATA, }, { }, }; MODULE_DEVICE_TABLE(platform, s3c24xx_serial_driver_ids); #ifdef CONFIG_OF /*设备树对应解析匹配表*/ static const struct of_device_id s3c24xx_uart_dt_match[] = { { .compatible = "samsung,s3c2410-uart", .data = (void *)S3C2410_SERIAL_DRV_DATA }, { .compatible = "samsung,s3c2412-uart", .data = (void *)S3C2412_SERIAL_DRV_DATA }, { .compatible = "samsung,s3c2440-uart", .data = (void *)S3C2440_SERIAL_DRV_DATA }, { .compatible = "samsung,s3c6400-uart", .data = (void *)S3C6400_SERIAL_DRV_DATA }, { .compatible = "samsung,s5pv210-uart", .data = (void *)S5PV210_SERIAL_DRV_DATA }, { .compatible = "samsung,exynos4210-uart", .data = (void *)EXYNOS4210_SERIAL_DRV_DATA }, { .compatible = "samsung,exynos5433-uart", .data = (void *)EXYNOS5433_SERIAL_DRV_DATA }, {}, }; MODULE_DEVICE_TABLE(of, s3c24xx_uart_dt_match); #endif /*串口设备驱动实体*/ static struct platform_driver samsung_serial_driver = { .probe = s3c24xx_serial_probe, .remove = s3c24xx_serial_remove, .id_table = s3c24xx_serial_driver_ids, .driver = { .name = "samsung-uart", .pm = SERIAL_SAMSUNG_PM_OPS, .of_match_table = of_match_ptr(s3c24xx_uart_dt_match), }, };
对于作嵌入式Linux驱动开发,我的体会是先对总线驱动模型有一个相对清晰的概念认识会比较好,而平台设备以及平台设备驱动模型一样是衍生于总线驱动模型,这样从体系结构上就变得相对统一了。平台设备及驱动在嵌入式系统里大量应用,不少SOC内置了大量丰富的各种设备接口,这些接口每每都是经过处理器内部总线进行直接寻址的,这类型的设备几乎都是经过平台设备及驱动模型进行抽象实施的,因此深刻理解平台设备/平台设备驱动模型,无疑对开发此类设备驱动程序大有助益。
文章出自微信公众号:嵌入式客栈,因为时间关系,博客可能没法及时更新,最新内容,请关注本人公众号,严禁商业使用,违法必究