[導讀] 前文分析了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內置了大量豐富的各種設備接口,這些接口每每都是經過處理器內部總線進行直接尋址的,這類型的設備幾乎都是經過平臺設備及驅動模型進行抽象實施的,因此深刻理解平臺設備/平臺設備驅動模型,無疑對開發此類設備驅動程序大有助益。
文章出自微信公衆號:嵌入式客棧,因爲時間關係,博客可能沒法及時更新,最新內容,請關注本人公衆號,嚴禁商業使用,違法必究