你的浏览器版本过低,可能导致网站不能正常访问!
为了你能正常使用网站功能,请使用这些浏览器。

嵌入式Linux的驱动——Platform_driver/Platform_device

[复制链接]
gaosmile 发布时间:2020-5-20 14:04
为什么有Platform_driver

前文谈到的总线驱动模型(注这个图是照着bootlin的文档绘制的):

微信图片_20200520140104.png

同时,根据代码分析其基础数据结构框架关系如下(UML关系并不严谨,仅为理解方便):

微信图片_20200520140107.png

可见驱动程序的模型分层有一层总线基础层,那么对于嵌入式开发领域而言,有很多SOC芯片内置了各种外设,并比如LCD,UART、audio、摄像头口等等,并没有总线。为了统一驱动架构抽象,所以引入了platform bus这个虚拟的总线模型。做过嵌入式开发的人应该都有体会,这类设备在嵌入式系统中非常多,所以在研究具体某类设备的驱动开发之前,有必要研究platform 设备的驱动模型。在强调一下这个是统一在总线驱动模型这个体系内的。

驱动模型的实现

定义在./include/linux/platform_device.h中,来梳理一下这些数据结构间的关系:

微信图片_20200520140111.png
  • platform_device 用于抽象平台设备
  • platform_driver 用于抽象匹配平台设备对应的驱动程序
  • 通过继承演化关系分析,platform_device/platform_driver 仍然统一于总线驱动模型,只是虚拟出来了一条platform bus这样一条虚拟总线。
  • platform_bus在哪里实现的呢?该模块的实现位于./driver/base/platform.c中
    7 E: \0 m6 F% P
struct device platform_bus = {/ k. e! Q8 Z' i' c3 g& @# ^
.init_name = "platform",/ e3 H/ g  a! V+ u0 }; {- y  P/ A
};

( y1 [) H% O8 w! p% `
  • platform.c导出了一系列内核全局操作接口集:" v/ v( V) k7 a7 F6 n
EXPORT_SYMBOL_GPL(platform_bus);
0 ]. z/ @, ^3 Z* v" }8 NEXPORT_SYMBOL_GPL(__platform_driver_register);1 k9 {- K/ u; p1 }" L; y
EXPORT_SYMBOL_GPL(__platform_driver_probe);
( Y! M& c! P5 e8 n  ~) OEXPORT_SYMBOL_GPL(platform_get_resource_byname);
$ g& M" A7 c! j' m3 l9 @) P( MEXPORT_SYMBOL_GPL(platform_get_irq_byname);. b2 Y1 l) b7 r  N, j# i
....

" I+ J* ?0 E. w9 M, ^
  • 那么既然这条总线并不存在,往往并不能实现设备枚举、热插拔等功能。

  • 既然不能利用总线自动枚举,那么底层又是怎么玩的呢?实际上可选的有这样几种方式

      h" f: Q) ?% c' j
    • 通过内核代码静态描述实现
    • 通过设备树进行匹配加载
    • BIOS ACPI表(X86/PC体系)/ K" b, U# v) D6 r1 T
  • 平台设备是通常在系统中显示为自治实体的设备。这包括基于旧端口的设备和到外围总线的主机桥接,以及集成到片上系统平台中的大多数控制器。它们通常的共同点是从CPU总线直接寻址。很少有platform_device通过某种其他类型的总线的一部分连接的。但其寄存器仍将直接可寻址。

    & r6 V3 y, ]" H8 K3 s  ^1 }6 V
设备探测
  • probe()通常应该验证指定的设备硬件确实存在;有时平台设置代码不能确定。该函数用于检测可以使用设备资源,包括时钟和设备platform_data。
  • 设备的注册则是通过下面函数实现
    0 W2 w4 Y: e+ u, [! E% X
int platform_driver_register(struct platform_driver *drv);
5 `/ Q9 Z' }2 ?4 K2 @% n9 v7 ?设备命令以及绑定
  • platform_device.dev.bus_id 设备名由两个部分组成. M# T# D% i4 N' j. J
    • platform_device.name 用于驱动匹配
    • platform_device.id 设备实例号,或者用“-1”表示只有一个实例
    • 如"serial/0“ 表示 bus_id "serial.0","serial/3“ 表示 bus_id "serial.3"/ x. n; p! \4 V( C9 c7 f, W$ T
  • 驱动程序绑定由驱动程序核心自动执行,在发现设备和驱动程序之间的匹配之后调用驱动程序probe()。如果probe()成功,驱动程序和设备将像往常一样绑定。有三种不同的方法来找到这样的匹配:/ J1 E, M- x# b- T' b
    • 每当注册一个设备时,就会检查该总线的驱动程序是否匹配。平台设备应该在系统启动时尽早注册.
    • 当使用platform_driver_register()注册一个驱动程序时,将检查总线上所有未绑定的设备是否匹配。驱动程序通常在引导期间稍后注册,或者通过模块加载注册。
    • 使用platform_driver_probe()注册驱动程序与使用platform_driver_register()一样,不同的是,如果以后有其他设备注册,驱动程序不会被探测。
      7 F7 Q) t; A, }) y( F$ o3 o7 R
资源机制
  • 每个由特定驱动程序管理的设备通常使用不同的硬件资源:I/O寄存器地址、DMA通道、IRQ线路等。
  • struct resource就是用于抽象描述驱动程序需要用到的硬件资源,struct resource 被包进platform_device,实现与 struct platform_device关联。
  • 允许驱动程序被实例化为多个功能类似的设备,但具有不同的地址、irq等。
  • 硬件资源如时针、IO口等的分配现在基本基于设备树,对于设备树这里不展开,后面有机会总结分享,这里举个栗子:) _. c* m  s0 b6 ^
uart0: serial@44e09000 {( T- G7 m8 `, j
    compatible = "ti,omap3-uart";
) e) B% I$ F3 ~& N) I    ti,hwmods = "uart1";
+ U2 Z: v1 x1 \: _    clock-frequency = <48000000>;( e* u& N: t& t& G; e
    reg = <0x44e09000 0x2000>;! _. z1 |. N3 A+ H  V! C4 b
    interrupts = <72>;. o8 {# z4 _& y. d
    status = "disabled";
/ y, T8 |8 V9 z" i8 b7 K+ w};

( S7 a$ E- b4 W3 B; R: Jplatform_driver实例

以samsung.c 串口驱动程序为例:

/*兼容匹配表*/0 N" t. M8 }& d4 b$ |
static const struct platform_device_id s3c24xx_serial_driver_ids[] = {" _6 P- C2 c% u0 D, w( W5 Y
{6 _( ^! S7 n' P6 x' b/ D4 [
  .name  = "s3c2410-uart",
5 B* D% j0 R! q" q6 p6 o" t* R2 S  N  .driver_data = S3C2410_SERIAL_DRV_DATA,
7 a) e& K% x) ^- F" e }, {
, T$ K1 [2 N7 p3 D: [  .name  = "s3c2412-uart",: x1 C7 i# |: p# k* X4 b; ~
  .driver_data = S3C2412_SERIAL_DRV_DATA,
, W: O& O) m& T; K }, {
0 r+ b1 _9 ]# o8 {0 S6 y+ J  .name  = "s3c2440-uart",6 c  P) N- @5 p/ l+ [, s% J
  .driver_data = S3C2440_SERIAL_DRV_DATA,4 O8 i) B9 ?5 M% l
}, {( J$ O' @! X' ^. q
  .name  = "s3c6400-uart",( {6 w% K, r* C4 W( [' p+ Z  A
  .driver_data = S3C6400_SERIAL_DRV_DATA,
6 ]( q& r! v# d7 }7 p7 Q }, {
/ ?2 t0 W) W( f$ r- [4 w  .name  = "s5pv210-uart",* D& V9 W" ^4 d, T  K. n) j5 ?
  .driver_data = S5PV210_SERIAL_DRV_DATA,
' J5 u! A8 e6 {' E }, {) n! ?/ y3 y: }4 E
  .name  = "exynos4210-uart",9 B- f. w5 ]) Y- X
  .driver_data = EXYNOS4210_SERIAL_DRV_DATA,
! H4 {! V, C( ? }, {: h! l+ L$ k9 n/ p5 R
  .name  = "exynos5433-uart",3 ^8 N. y& Y# U. m$ B
  .driver_data = EXYNOS5433_SERIAL_DRV_DATA,, x" h- o' r% G0 M1 p/ h; i
},
5 Q4 c% d2 @! a8 p  r# y" a { },: p+ U- e9 O7 I0 W
};
+ ?' M- C- n1 N8 H  L6 ?1 K& E5 TMODULE_DEVICE_TABLE(platform, s3c24xx_serial_driver_ids);
1 J$ I) c) V& i9 S4 }- E2 T8 x/ ~; H+ C& ~
#ifdef CONFIG_OF4 w7 [( T1 R& K9 e; |) c  A3 M
/*设备树对应解析匹配表*/
1 y/ v9 `" }6 gstatic const struct of_device_id s3c24xx_uart_dt_match[] = {; z' D& k* B" \: {
{ .compatible = "samsung,s3c2410-uart",
. P9 V' v/ z% F  .data = (void *)S3C2410_SERIAL_DRV_DATA },* p0 L4 B1 j8 a3 H! V- q6 w
{ .compatible = "samsung,s3c2412-uart",1 Z$ s$ W- y* O; J+ r9 F# J
  .data = (void *)S3C2412_SERIAL_DRV_DATA },
: X- R7 y$ w3 L, |' O { .compatible = "samsung,s3c2440-uart",5 @4 g) K  L" f
  .data = (void *)S3C2440_SERIAL_DRV_DATA },
8 r2 m) F1 c! m { .compatible = "samsung,s3c6400-uart",
( T6 n; ]4 S) o+ {- T! f, {( a  .data = (void *)S3C6400_SERIAL_DRV_DATA },
6 I9 V6 j  u2 y9 U. u) U6 O6 U { .compatible = "samsung,s5pv210-uart",& H) p+ L! ]7 K% ^. o3 \/ g
  .data = (void *)S5PV210_SERIAL_DRV_DATA },1 j* p$ ?1 t5 h4 j9 J2 k
{ .compatible = "samsung,exynos4210-uart",5 s- h  H+ k( T( n+ `
  .data = (void *)EXYNOS4210_SERIAL_DRV_DATA },  D+ z1 n! a# ?
{ .compatible = "samsung,exynos5433-uart",6 C- f& ?8 `9 K# B
  .data = (void *)EXYNOS5433_SERIAL_DRV_DATA },% b0 W7 D- a4 d5 l5 D, W
{},; |3 z3 Q) B6 q4 A- D  V6 H1 d
};
+ i* v, D+ l" y9 ?2 }7 [) a8 rMODULE_DEVICE_TABLE(of, s3c24xx_uart_dt_match);4 X, R" g. |' q+ q# j
#endif+ t; ^3 ]4 Y$ l- f1 H. P; z
/*串口设备驱动实体*/4 u* e; O# l4 n. E0 p0 D, E2 O
static struct platform_driver samsung_serial_driver = {
; u' H5 l1 g* n" L) k7 b- j .probe  = s3c24xx_serial_probe,
& q8 K  g$ Y: [4 Z- u$ U1 U/ { .remove  = s3c24xx_serial_remove,1 D3 f8 ~3 B3 y. ~* b( D
.id_table = s3c24xx_serial_driver_ids,
" z/ `, I% S7 n8 ]/ H9 m .driver  = {/ I4 ?2 J1 t/ h, c$ D# _: b" K) |- |
  .name = "samsung-uart",' j7 h$ R  @% q% F& k
  .pm = SERIAL_SAMSUNG_PM_OPS,
2 u' l+ A6 v& ^) R9 {  C  .of_match_table = of_match_ptr(s3c24xx_uart_dt_match),# \3 W0 i. r. m  E' Q" S7 p) G8 I
},
4 f3 M- U% J5 P4 Q5 K# _};
% @  [  P" r& R8 N4 M6 R" a8 C
总结一下
微信图片_20200520140114.png

对于做嵌入式Linux驱动开发,个人体会是先对总线驱动模型有一个相对清晰的概念认识会比较好,而平台设备以及平台设备驱动模型同样是衍生于总线驱动模型,这样从体系结构上就变得相对统一了。平台设备及驱动在嵌入式系统里大量应用,很多SOC内置了大量丰富的各类设备接口,这些接口往往都是通过处理器内部总线进行直接寻址的,这类型的设备几乎都是通过平台设备及驱动模型进行抽象实施的,所以深入理解平台设备/平台设备驱动模型,无疑对开发此类设备驱动程序大有助益。

/ }- t8 C0 D' o2 N  q* {  e$ N' `" F
赞 收藏 评论0 发布时间:2020-5-20 14:04

举报

0个回答
关于
我们是谁
投资者关系
意法半导体可持续发展举措
创新与技术
意法半导体官网
联系我们
联系ST分支机构
寻找销售人员和分销渠道
社区
媒体中心
活动与培训
隐私策略
隐私策略
Cookies管理
行使您的权利
官方最新发布
人形机器人运动控制、感知与智能配电
半导体创新技术与应用方向
EE架构与软件定义汽车
12V/48V 汽车智能配电(SPD)
区域控制单元(ZCU)与分区架构
关注我们
st-img 微信公众号
st-img 手机版