[linux 驱动]platform总线设备驱动详解与实战
目录
1 描述
2 结构体
2.1 bus_type
2.2 platform_bus_type
2.2.1 platform_match
2.2.2 platform_uevent
2.2.3 platform_dma_configure
2.2.4 platform_dev_pm_ops
2.3 platform_driver
2.4 platform_device
3 platform注册
3.1 platform_driver_register
3.1.1 __platform_driver_register分析
3.1.1.1 platform_drv_probe
3.1.1.2 platform_drv_remove
3.1.1.3 platform_drv_shutdown
3.1.1.4 driver_register
3.2 platform_driver_unregister
3.3 platform_device_register
3.4 platform_device_unregister
4 jw_io_core.c 驱动分析
4.1设备树
4.1.1 设备树文件
4.2 驱动分析
4.2.1 驱动框架
4.2.2 platform驱动文件系统
4.2.3 probe参数传递
4.2.4 驱动设备匹配分析
1 描述
下图是Linux总线、驱动和设备模式,向系统注册一个驱动的时候,总线就会在右侧的设备中查找,看看有没有与之匹配的设备,如果有的话就将两者联系起来。同样的,当向系统中注册一个设备的时候,总线就会在左侧的驱动中查找看有没有与之匹配的设备,有的话也联系起来。
总线(bus)、驱动(driver)和设备(device)模型,比如 I2C、SPI、USB 等总线。在 SOC 中有些外设是没有总线这个概念的,但是又要使用总线、驱动和设备模型该怎么办呢?为了解决此问题,Linux 提出了 platform 这个虚拟总线,相应的就有 platform_driver 和 platform_device。
platform总线在drivers/base/platform.c文件中描述。
/sys/bus/目录下包含了有关系统总线的各类信息。
rk3399_Android11:/sys/bus # ls
amba clockevents container event_source gpio i2c mdio_bus mipi-dsi mmc_rpmb pci platform scsi spi usb-serial
cec clocksource cpu genpd hid iio media mmc nvmem pci_express scmi_protocol sdio usb workqueue
rk3399_Android11:/sys/bus #
/sys/bus/platform/目录下保存着当前板子 platform 总线下的设备和驱动,其中devices 子目录为 platform 设备,drivers 子目录为 plartofm 驱动。
rk3399_Android11:/sys/bus/platform # ls
devices drivers drivers_autoprobe drivers_probe uevent
rk3399_Android11:/sys/bus/platform # ls devices/
110000.ramoops fe330000.sdhci ff420030.pwm ff788000.gpio3 ffa5c000.qos ffa8f400.nocp-vio0-msch1 firmware:optee serial8250
Fixed\ MDIO\ bus.0 fe380000.usb ff630000.dfi ff790000.gpio4 ffa60080.qos ffa8f800.nocp-vio1-msch1 gpio-keys snd-soc-dummy
adc_keys fe3a0000.usb ff650000.vepu ff7c0000.phy ffa60100.qos ffa90000.qos hdmi-audio-codec.1.auto timer
alarmtimer fe3c0000.usb ff650400.vdpu ff800000.phy ffa60180.qos ffa98000.qos hdmi-audio-codec.5.auto usb@fe800000
amba fe3e0000.usb ff650800.iommu ff848000.watchdog ffa70000.qos ffaa0000.qos ion.0.auto usb@fe900000
armv7sec.0 fe800000.dwc3 ff660000.rkvdec ff850000.rktimer ffa70080.qos ffaa0080.qos jw_io_init vcc-phy-regulator
backlight fe900000.dwc3 ff660480.iommu ff870000.spdif ffa74000.qos ffaa8000.qos mpp-srv vcc-pwr-1v8-regulator
charge-animation fec00000.dp ff670000.iep ff880000.i2s ffa76000.qos ffaa8080.qos pinctrl vcc-sys
cpufreq-dt ff100000.saradc ff670800.iommu ff8a0000.i2s ffa86000.nocp-cci-msch0 ffab0000.qos pmu_a53 vcc1v8-s0
cpuinfo ff110000.i2c ff680000.rga ff8b8000.rng ffa86400.nocp-gpu-msch0 ffab0080.qos pmu_a72 vcc3v3-sys
ddr_timing ff180000.serial ff690000.efuse ff8d0000.sram ffa86800.nocp-hp-msch0 ffab8000.qos psci vcc5v0-host-regulator
display-subsystem ff260000.tsadc ff720000.gpio0 ff8f0000.vop ffa86c00.nocp-lp-msch0 ffac0000.qos reg-dummy vdd-log
dmc ff310000.power-management ff730000.gpio1 ff8f3f00.iommu ffa87000.nocp-video-msch0 ffac0080.qos regulatory.0 wireless-bluetooth
dw-hdmi-cec.3.auto ff310000.power-management:power-controller ff770000.syscon ff900000.vop ffa87400.nocp-vio0-msch0 ffac8000.qos rk-headset wireless-wlan
dw-hdmi-hdcp.4.auto ff320000.syscon ff770000.syscon:io-domains ff903f00.iommu ffa87800.nocp-vio1-msch0 ffac8080.qos rk808-clkout xhci-hcd.6.auto
dw-hdmi-i2s-audio.2.auto ff320000.syscon:io-domains ff770000.syscon:phy@f780 ff914000.iommu ffa8e000.nocp-cci-msch1 ffad0000.qos rk808-regulator
es8323-sound ff320000.syscon:reboot-mode ff770000.syscon:pvtm ff924000.iommu ffa8e400.nocp-gpu-msch1 ffad8080.qos rk808-rtc
fe300000.ethernet ff3c0000.i2c ff770000.syscon:usb2-phy@e450 ff940000.hdmi ffa8e800.nocp-hp-msch1 ffae0000.qos rockchip-suspend
fe310000.dwmmc ff3d0000.i2c ff770000.syscon:usb2-phy@e460 ff9a0000.gpu ffa8ec00.nocp-lp-msch1 fiq-debugger rockchip-system-monitor
fe320000.dwmmc ff420020.pwm ff780000.gpio2 ffa58000.qos ffa8f000.nocp-video-msch1 fiq_debugger.0 sdio-pwrseq
rk3399_Android11:/sys/bus/platform # ls devices/jw_io_init/
driver driver_override led_display mic_switch modalias of_node power subsystem uevent
rk3399_Android11:/sys/bus/platform # ls drivers
4g-modem-platdata dw-mipi-dsi inno-mipi-dphy mpp_vdpu2 remotectl-pwm rk618-hdmi rk628-rgb rkisp_hw rockchip-i2s rockchip-rng serial8250
RKNPU dw_mmc inno-video-combo-phy mpp_vepu1 rfkill_bt rk618-lvds rk630-tve rknand rockchip-i2s-tdm rockchip-saradc snd-soc-dummy
adc_keys dw_wdt innohdmi-rockchip mpp_vepu2 rga2 rk618-rgb rk805-pinctrl rockchip,bus rockchip-inno-dwc3 rockchip-snps-pcie3-phy soc-audio
ahci dwc2 jw_io_control naneng-combphy rga3_core0 rk618-scaler rk805-pwrkey rockchip,dmcdbg rockchip-iodomain rockchip-spdif spdif-dir
alarmtimer dwc3 leds-gpio of_fixed_clk rga3_core1 rk618-vif rk808-clkout rockchip-cdndp-sound rockchip-lvds rockchip-spi spdif-dit
arm-scmi dwc3-of-simple mali of_fixed_factor_clk rgb13h-flash rk628-combrxphy rk808-regulator rockchip-clcok-pvtm rockchip-mipi-csi2 rockchip-system-monitor spirit\ module
armv7sec dwhdmi-rockchip mali-utgard ohci-platform rk-crypto rk628-combtxphy rk817-battery rockchip-cpuinfo rockchip-mipi-dphy-rx rockchip-thermal spirit-io
armv8-pmu dwmmc_rockchip mbox-scpi panel-simple rk-fiq-debugger rk628-cru rk817-charger rockchip-csi2-dphy-hw rockchip-nocp rockchip-tve sram
asoc-simple-card ehci-platform mh248 poweroff-gpio rk-hdmi-dp-sound rk628-csi rk817-codec rockchip-dfi rockchip-pcie rockchip-typec-phy syscon
bt-sco fiq_debugger midgard pwm-backlight rk-hdmi-sound rk628-dsi rk818-battery rockchip-dmc rockchip-pcie-phy rockchip-u3phy syscon-reboot-mode
cdn-dp gpio-clk mpp-iep2 pwm-clock rk-multicodecs rk628-efuse rk818-charger rockchip-dp rockchip-pdm rockchip-usb-phy wlan-platdata
cpufreq-dt gpio-keys mpp_jpgdec pwm-regulator rk3328-codec rk628-gvi rk_codec_digital rockchip-drm rockchip-pinctrl rockchip-usb2phy xhci-hcd
dummy_codec gpio-regulator mpp_rkvdec pwrseq_emmc rk3368-mailbox rk628-hdmi rk_gmac-dwmac rockchip-edp-phy-grf rockchip-pm rockchip-vop
dw-apb-uart hdmi-audio-codec mpp_rkvdec2 pwrseq_simple rk3368-thermal rk628-hdmirx rk_iommu rockchip-edpphy-naneng rockchip-pm-domain rockchip-vop2
dw-hdmi-cec i2c-gpio mpp_rkvenc ramoops rk3x-i2c rk628-lvds rkcifhw rockchip-efuse rockchip-pvtm rockchip_headset
dw-hdmi-hdcp iep mpp_service reg-dummy rk618-cru rk628-pinctrl rkisp rockchip-emmc-phy rockchip-pwm sdhci-arasan
dw-hdmi-i2s-audio inno-hdmi-phy mpp_vdpu1 reg-fixed-voltage rk618-dsi rk628-post-process rkisp1 rockchip-gpio rockchip-rgb sdhci-dwcmshc
rk3399_Android11:/sys/bus/platform # ls drivers/jw_io_control/
bind jw_io_init uevent unbind
rk3399_Android11:/sys/bus/platform # ls drivers/jw_io_control/jw_io_init/
driver driver_override led_display mic_switch modalias of_node power subsystem uevent
rk3399_Android11:/sys/bus/platform #
2 结构体
2.1 bus_type
struct bus_type 在 Linux 内核中是一个非常重要的结构体,它用于表示和管理不同类型的总线。在 Linux 的设备模型中,总线(bus)是一个关键的抽象概念,它用于将设备和驱动程序连接起来。每种类型的总线(如 platporm、PCI、USB、I2C 等)都由一个 bus_type 结构体来表示,这个结构体包含了该类型总线所需的所有信息和操作函数。
122 struct bus_type {
123 const char *name;
124 const char *dev_name;
125 struct device *dev_root;
126 const struct attribute_group **bus_groups;
127 const struct attribute_group **dev_groups;
128 const struct attribute_group **drv_groups;
129
130 int (*match)(struct device *dev, struct device_driver *drv);
131 int (*uevent)(struct device *dev, struct kobj_uevent_env *env);
132 int (*probe)(struct device *dev);
133 void (*sync_state)(struct device *dev);
134 int (*remove)(struct device *dev);
135 void (*shutdown)(struct device *dev);
136
137 int (*online)(struct device *dev);
138 int (*offline)(struct device *dev);
139
140 int (*suspend)(struct device *dev, pm_message_t state);
141 int (*resume)(struct device *dev);
142
143 int (*num_vf)(struct device *dev);
144
145 int (*dma_configure)(struct device *dev);
146
147 const struct dev_pm_ops *pm;
148
149 const struct iommu_ops *iommu_ops;
150
151 struct subsys_private *p;
152 struct lock_class_key lock_key;
153
154 bool need_parent_lock;
155
156 ANDROID_KABI_RESERVE(1);
157 ANDROID_KABI_RESERVE(2);
158 ANDROID_KABI_RESERVE(3);
159 ANDROID_KABI_RESERVE(4);
160 };
bus_type结构体的成员解释。
name 是总线的名称,如 "pci"、"usb" 等,用于在系统中唯一标识总线类型。
dev_name 通常用于构造设备在系统中的名称,但在这个结构体定义中,它可能不是所有情况下都使用或必需。其确切用途可能取决于内核版本和具体实现。
dev_root:指向该总线类型下所有设备的根设备。在一些总线类型中,设备可能会以树状结构组织,其中 dev_root 表示这棵树的根节点。
bus_groups, dev_groups, drv_groups:这些字段分别指向属性组的数组,这些属性组可以通过 sysfs 接口被用户空间访问。bus_groups 包含总线级别的属性,dev_groups 包含设备级别的属性,而 drv_groups 包含驱动程序级别的属性。
match、uevent、probe、sync_state、remove、shutdown、online、offline、suspend、resume 等函数指针分别指向处理设备添加、移除、状态同步、电源管理等操作的函数。这些函数定义了当设备或驱动程序与总线交互时应执行的行为。
num_vf 可能用于某些支持虚拟功能(如 SR-IOV)的总线,用于返回设备的虚拟功能数量。
dma_configure 用于配置设备的 DMA(直接内存访问)特性。
pm:指向 dev_pm_ops 结构体的指针,该结构体包含了一组用于电源管理的函数指针,如设备挂起、恢复等操作。
iommu_ops:指向 iommu_ops 结构体的指针,该结构体定义了一组用于管理输入/输出内存管理单元(IOMMU)的函数。IOMMU 用于在物理内存和设备地址空间之间建立映射。
p:指向 subsys_private 结构体的指针,这通常是一个私有数据结构,用于存储与总线子系统相关的私有信息。
lock_key:用于锁类别的键,这有助于调试和性能分析,确保锁的正确使用和避免死锁。
need_parent_lock:一个布尔值,指示在访问总线上的设备时是否需要锁定其父设备。这有助于处理总线层次结构中的并发访问。
ANDROID_KABI_RESERVE:这些是 Android 特有的保留字段,用于确保在 Android 和 Linux 内核之间的接口保持稳定。这些字段当前未使用,但保留以供将来扩展。
2.2 platform_bus_type
platform_bus_type 结构体是 struct bus_type 的一个实例,专门用于表示和管理平台总线(platform bus)。在 Linux 内核中,平台总线是一种特殊的总线,用于连接那些不直接挂接在标准总线(如 PCI、USB)上的设备。这些设备通常是通过设备树在内核启动时被识别和初始化的。
1179 struct bus_type platform_bus_type = {
1180 .name = "platform",
1181 .dev_groups = platform_dev_groups,
1182 .match = platform_match,
1183 .uevent = platform_uevent,
1184 .dma_configure = platform_dma_configure,
1185 .pm = &platform_dev_pm_ops,
1186 };
platform_bus_type结构体的成员解释。
.name = "platform": 指定了总线类型的名称为 "platform"。这是该总线在内核中的唯一标识符。
.dev_groups = platform_dev_groups;: 指向一个包含属性组的数组,这些属性组与平台总线上的设备相关。用户空间程序可以通过 sysfs 接口访问这些属性组,以获取或设置设备的配置信息。
.match = platform_match;: 指向一个函数,该函数用于在设备添加到平台总线时,检查设备与已注册的驱动程序是否匹配。如果匹配,内核将调用该驱动程序的 probe 函数来初始化设备。
.uevent = platform_uevent;: 指向一个函数,该函数用于生成并发送 uevent 消息。当设备被添加到平台总线或从中移除时,会触发这些消息。用户空间程序可以监听这些消息,并据此执行相应的操作。
.dma_configure = platform_dma_configure;: 指向一个函数,该函数用于配置平台设备上的 DMA(直接内存访问)特性。不过,请注意,并非所有平台设备都需要 DMA 支持,因此这个字段可能在一些情况下不会被使用。
.pm = &platform_dev_pm_ops;: 指向一个 dev_pm_ops 结构体,该结构体包含了一组用于电源管理的函数指针。这些函数定义了平台设备在电源管理(如挂起、恢复)时的行为。
2.2.1 platform_match
函数 platform_match 是 Linux 内核中用于匹配平台设备(platform_device)和平台驱动程序(platform_driver)的函数。它是 struct bus_type 结构体中 match 字段的一个实现,专门用于平台总线(platform_bus_type)。该函数的目的是确定给定的设备(dev)和驱动程序(drv)是否匹配,以便内核可以将设备绑定到正确的驱动程序上。
988 static int platform_match(struct device *dev, struct device_driver *drv)
989 {
990 struct platform_device *pdev = to_platform_device(dev);
991 struct platform_driver *pdrv = to_platform_driver(drv);
992
993 /* When driver_override is set, only bind to the matching driver */
994 if (pdev->driver_override)
995 return !strcmp(pdev->driver_override, drv->name);
996
997 /* Attempt an OF style match first */
998 if (of_driver_match_device(dev, drv))
999 return 1;
1000
1001 /* Then try ACPI style match */
1002 if (acpi_driver_match_device(dev, drv))
1003 return 1;
1004
1005 /* Then try to match against the id table */
1006 if (pdrv->id_table)
1007 return platform_match_id(pdrv->id_table, pdev) != NULL;
1008
1009 /* fall-back to driver name match */
1010 return (strcmp(pdev->name, drv->name) == 0);
1011 }
使用 of_driver_match_device(dev, drv) 尝试进行基于设备树的匹配。如果设备树中存在与驱动程序相匹配的条目,则返回 1 表示成功匹配
2.2.2 platform_uevent
函数 platform_uevent 是 Linux 内核中用于为平台设备(platform_device)生成并发送 uevent 消息的函数。uevent 是一种内核向用户空间发送事件的机制,允许用户空间程序响应设备的添加、移除或其他变化。在这个特定的函数中,它主要用于为平台设备构建并添加一个 MODALIAS 环境变量,该变量可以帮助用户空间的 udev 或其他守护进程找到并加载适当的驱动程序
942 static int platform_uevent(struct device *dev, struct kobj_uevent_env *env)
943 {
944 struct platform_device *pdev = to_platform_device(dev);
945 int rc;
946
947 /* Some devices have extra OF data and an OF-style MODALIAS */
948 rc = of_device_uevent_modalias(dev, env);
949 if (rc != -ENODEV)
950 return rc;
951
952 rc = acpi_device_uevent_modalias(dev, env);
953 if (rc != -ENODEV)
954 return rc;
955
956 add_uevent_var(env, "MODALIAS=%s%s", PLATFORM_MODULE_PREFIX,
957 pdev->name);
958 return 0;
959 }
2.2.3 platform_dma_configure
函数 platform_dma_configure 是用于为平台设备(platform_device)配置直接内存访问(DMA)设置的。它接受一个指向 struct device 结构的指针作为参数,这个结构代表了需要配置DMA的设备。函数的主要目的是根据设备的设备树(Device Tree)或ACPI信息(如果可用)来配置DMA属性
1157 int platform_dma_configure(struct device *dev)
1158 {
1159 enum dev_dma_attr attr;
1160 int ret = 0;
1161
1162 if (dev->of_node) {
1163 ret = of_dma_configure(dev, dev->of_node, true);
1164 } else if (has_acpi_companion(dev)) {
1165 attr = acpi_get_dma_attr(to_acpi_device_node(dev->fwnode));
1166 if (attr != DEV_DMA_NOT_SUPPORTED)
1167 ret = acpi_dma_configure(dev, attr);
1168 }
1169
1170 return ret;
1171 }
2.2.4 platform_dev_pm_ops
struct dev_pm_ops 是 Linux 内核中用于定义设备电源管理操作的结构体,它包含了一系列指向电源管理函数的指针,这些函数用于控制设备的电源状态,如挂起、恢复、休眠等
platform_dev_pm_ops 结构体,为平台设备提供了一套基本的电源管理操作,并通过使用USE_PLATFORM_PM_SLEEP_OPS 宏来简化其他电源管理操作的指定
1173 static const struct dev_pm_ops platform_dev_pm_ops = {
1174 .runtime_suspend = pm_generic_runtime_suspend,
1175 .runtime_resume = pm_generic_runtime_resume,
1176 USE_PLATFORM_PM_SLEEP_OPS
1177 };
2.3 platform_driver
struct platform_driver 是 Linux 内核中用于定义平台设备驱动程序的结构体。这个结构体包含了指向各种函数的指针,这些函数是驱动程序与内核交互的关键部分,以及一些用于描述驱动程序本身的信息。
183 struct platform_driver {
184 int (*probe)(struct platform_device *);
185 int (*remove)(struct platform_device *);
186 void (*shutdown)(struct platform_device *);
187 int (*suspend)(struct platform_device *, pm_message_t state);
188 int (*resume)(struct platform_device *);
189 struct device_driver driver;
190 const struct platform_device_id *id_table;
191 bool prevent_deferred_probe;
192 };
platform_driver结构体的成员解释。
int (*probe)(struct platform_device *):这是一个指向函数的指针,该函数是驱动程序的初始化函数。当内核发现与驱动程序匹配的平台设备时,会调用此函数。驱动程序的 probe 函数负责设置设备所需的任何资源(如内存、中断等),并注册设备到内核中,以便其他部分的内核或用户空间程序可以使用它。
int (*remove)(struct platform_device *):这是一个指向函数的指针,该函数在设备被移除时调用。它负责释放 probe 函数中分配的所有资源,并清理驱动程序与设备之间的任何关联。
void (*shutdown)(struct platform_device *):这是一个指向函数的指针,该函数在系统关闭时调用。它允许驱动程序执行必要的清理工作,以确保在系统关闭过程中设备处于安全状态。
int (*suspend)(struct platform_device *, pm_message_t state):这是一个指向函数的指针,该函数在系统进入低功耗状态时调用,如系统挂起或休眠。它允许驱动程序保存设备的状态,并关闭设备以节省电源。state 参数指示了系统即将进入的电源状态。
int (*resume)(struct platform_device *):这是一个指向函数的指针,该函数在系统从低功耗状态恢复时调用。它允许驱动程序恢复设备的状态,并重新启用设备。
struct device_driver driver:这是一个 device_driver 结构体,它包含了描述驱动程序本身的信息,如名称、总线类型等。这个结构体是通用的,用于内核中的设备驱动模型。
const struct platform_device_id *id_table:这是一个指向 platform_device_id 结构体数组的指针,该数组用于匹配设备和驱动程序。每个 platform_device_id 结构体包含一个或多个用于匹配设备名称的字符串,以及一个可选的驱动程序数据指针。如果 id_table 为 NULL,则驱动程序将匹配所有平台设备。
bool prevent_deferred_probe:这是一个布尔值,用于控制是否延迟探测设备。如果设置为 true,则驱动程序将不会参与内核的延迟探测机制,这可能会影响设备的初始化和启动顺序。
struct platform_device_id 结构体是用于匹配平台设备和平台驱动程序的一个关键组成部分。
546 struct platform_device_id {
547 char name[PLATFORM_NAME_SIZE];
548 kernel_ulong_t driver_data;
549 };
struct device_driver 是 Linux 内核中用于描述设备驱动程序的一个结构体。它包含了驱动程序的名称、所属的总线类型、模块所有者、以及一些回调函数等关键信息。
298 struct device_driver {
299 const char *name;
300 struct bus_type *bus;
301
302 struct module *owner;
303 const char *mod_name; /* used for built-in modules */
304
305 bool suppress_bind_attrs; /* disables bind/unbind via sysfs */
306 enum probe_type probe_type;
307
308 const struct of_device_id *of_match_table;
309 const struct acpi_device_id *acpi_match_table;
310
311 int (*probe) (struct device *dev);
312 void (*sync_state)(struct device *dev);
313 int (*remove) (struct device *dev);
314 void (*shutdown) (struct device *dev);
315 int (*suspend) (struct device *dev, pm_message_t state);
316 int (*resume) (struct device *dev);
317 const struct attribute_group **groups;
318
319 const struct dev_pm_ops *pm;
320 void (*coredump) (struct device *dev);
321
322 struct driver_private *p;
323
324 ANDROID_KABI_RESERVE(1);
325 ANDROID_KABI_RESERVE(2);
326 ANDROID_KABI_RESERVE(3);
327 ANDROID_KABI_RESERVE(4);
328 };
device_driver结构体的成员解释。
const char *name:驱动程序的名称,用于在内核中唯一标识该驱动程序。
struct bus_type *bus:指向该驱动程序所属的总线类型的指针。总线类型定义了设备如何与系统进行通信,以及设备之间如何相互通信。
struct module *owner:指向拥有该驱动程序的模块的指针。如果驱动程序是静态编译进内核的(非模块化),则此指针可能为 NULL。
const char *mod_name:用于内置模块的名称,主要用于调试和日志记录。对于非模块化驱动程序,此字段可能不被使用。
bool suppress_bind_attrs:一个标志,用于禁用通过 sysfs 接口进行的绑定(bind)和解绑(unbind)操作。这有助于保护系统稳定性,防止用户空间程序意外更改设备绑定。
enum probe_type probe_type:指定驱动程序的探测类型。这可能影响驱动程序如何以及何时探测其设备。
const struct of_device_id *of_match_table; 和 const struct acpi_device_id *acpi_match_table;:分别用于匹配 Open Firmware (OF) 和 ACPI 描述的设备。这些表允许驱动程序通过设备的硬件描述(如设备树或 ACPI 表)来识别设备。
回调函数:包括 probe、sync_state、remove、shutdown、suspend、resume 等,这些函数是驱动程序与内核交互的关键部分。它们分别在设备发现、状态同步、设备移除、系统关闭、系统休眠和唤醒等情况下被调用。
const struct attribute_group **groups;:指向属性组的指针数组,这些属性组用于通过 sysfs 导出驱动程序的配置选项或状态信息。
const struct dev_pm_ops *pm:指向电源管理操作表的指针,该表定义了驱动程序在电源管理事件(如系统休眠和唤醒)中的行为。
void (*coredump) (struct device *dev):一个可选的回调函数,用于在系统崩溃时转储设备的核心信息。
struct driver_private *p:指向驱动程序私有数据的指针,这通常用于存储驱动程序内部使用的数据结构或状态信息。
ANDROID_KABI_RESERVE 宏:这些宏在 Android 内核中用于保留结构体的未来扩展空间。它们不直接影响驱动程序的正常功能,但为将来的内核更新或新功能的添加提供了灵活性。通过在结构体末尾添加这些保留字段,Android 可以确保在不影响现有驱动程序兼容性的情况下,向 struct device_driver 结构体添加新成员。这些保留字段通常不会被普通驱动程序代码直接访问。
of_match_table 就是采用设备树的时候驱动使用的匹配表,是数组,每个匹配项都为 of_device_id 结构体类型
241 struct of_device_id {
242 char name[32];
243 char type[32];
244 char compatible[128];
245 const void *data;
246 };
compatible 非常重要,因为对于设备树而言,就是通过设备节点的 compatible 属性值和 of_match_table 中每个项目的 compatible 成员变量进行比较,如果有相等的就表示设备和此驱动匹配成功
2.4 platform_device
struct platform_device 是 Linux 内核中用于表示平台设备的一个结构体。平台设备是一种不依赖于传统 PCI/USB/I2C 等总线的设备,它们通常通过静态配置或设备树(Device Tree)来声明。
在以前不支持设备树的Linux版本中,用户需要编写platform_device变量来描述设备信息,然后使用 platform_device_register 函数将设备信息注册到 Linux 内核中
23 struct platform_device {
24 const char *name;
25 int id;
26 bool id_auto;
27 struct device dev;
28 u32 num_resources;
29 struct resource *resource;
30
31 const struct platform_device_id *id_entry;
32 char *driver_override; /* Driver name to force a match */
33
34 /* MFD cell pointer */
35 struct mfd_cell *mfd_cell;
36
37 /* arch specific additions */
38 struct pdev_archdata archdata;
39 };
platform_device结构体的成员解释。
const char *name:设备的名称。这个名称是唯一的,用于在内核中标识设备。它也被用于在设备树中查找相应的设备描述。
int id:设备的实例ID。在某些情况下,可能需要在同一个设备类型下区分不同的设备实例,此时可以通过这个ID来区分。
bool id_auto:一个标志,用于指示是否自动分配设备ID。如果设置为true,内核可能会根据设备的某些属性(如物理地址)自动生成一个唯一的ID。
struct device dev:内嵌的struct device结构体,表示一个通用的设备。这个结构体包含了设备的许多基本属性,如设备类型、父设备、设备驱动等。通过dev成员,platform_device能够继承和使用device结构体中定义的所有功能。
u32 num_resources:表示设备所使用的资源数量。资源可以是I/O端口、中断号、DMA通道等。
struct resource *resource:指向资源数组的指针。每个资源都通过struct resource结构体来描述,包括资源类型、起始地址、长度等信息。
const struct platform_device_id *id_entry:指向platform_device_id结构体的指针,该结构体通常用于匹配设备与其驱动程序。当设备被枚举时,内核会检查id_entry中定义的设备名称是否与驱动程序中定义的设备ID相匹配。
char *driver_override:一个可选的字符串,用于强制指定驱动程序名称。在某些情况下,如果内核无法自动匹配到合适的驱动程序,可以通过设置这个成员来强制指定一个驱动程序。
struct mfd_cell *mfd_cell:用于多功能设备(Multi-Function Device, MFD)的特定成员。MFD是指在一个物理设备中包含多个逻辑子设备的设备。mfd_cell用于描述这些子设备的信息。
struct pdev_archdata archdata:架构特定的附加数据。这个成员允许不同的硬件架构为平台设备添加额外的信息或数据。它的具体内容和用途取决于具体的硬件架构。
struct device结构体如下所示。
1031 struct device {
1032 struct device *parent;
1033
1034 struct device_private *p;
1035
1036 struct kobject kobj;
1037 const char *init_name; /* initial name of the device */
1038 const struct device_type *type;
1039
1040 struct mutex mutex; /* mutex to synchronize calls to
1041 * its driver.
1042 */
1043
1044 struct bus_type *bus; /* type of bus device is on */
1045 struct device_driver *driver; /* which driver has allocated this
1046 device */
1047 void *platform_data; /* Platform specific data, device
1048 core doesn't touch it */
1049 void *driver_data; /* Driver data, set and get with
1050 dev_set/get_drvdata */
1051 struct dev_links_info links;
1052 struct dev_pm_info power;
1053 struct dev_pm_domain *pm_domain;
1054
1055 #ifdef CONFIG_GENERIC_MSI_IRQ_DOMAIN
1056 struct irq_domain *msi_domain;
1057 #endif
1058 #ifdef CONFIG_PINCTRL
1059 struct dev_pin_info *pins;
1060 #endif
1061 #ifdef CONFIG_GENERIC_MSI_IRQ
1062 struct list_head msi_list;
1063 #endif
1064
1065 #ifdef CONFIG_NUMA
1066 int numa_node; /* NUMA node this device is close to */
1067 #endif
1068 const struct dma_map_ops *dma_ops;
1069 u64 *dma_mask; /* dma mask (if dma'able device) */
1070 u64 coherent_dma_mask;/* Like dma_mask, but for
1071 alloc_coherent mappings as
1072 not all hardware supports
1073 64 bit addresses for consistent
1074 allocations such descriptors. */
1075 u64 bus_dma_mask; /* upstream dma_mask constraint */
1076 unsigned long dma_pfn_offset;
1077
1078 struct device_dma_parameters *dma_parms;
1079
1080 struct list_head dma_pools; /* dma pools (if dma'ble) */
1081
1082 struct dma_coherent_mem *dma_mem; /* internal for coherent mem
1083 override */
1084 #ifdef CONFIG_DMA_CMA
1085 struct cma *cma_area; /* contiguous memory area for dma
1086 allocations */
1087 #endif
1088 struct removed_region *removed_mem;
1089 /* arch specific additions */
1090 struct dev_archdata archdata;
1091
1092 struct device_node *of_node; /* associated device tree node */
1093 struct fwnode_handle *fwnode; /* firmware device node */
1094
1095 dev_t devt; /* dev_t, creates the sysfs "dev" */
1096 u32 id; /* device instance */
1097
1098 spinlock_t devres_lock;
1099 struct list_head devres_head;
1100
1101 struct klist_node knode_class;
1102 struct class *class;
1103 const struct attribute_group **groups; /* optional groups */
1104
1105 void (*release)(struct device *dev);
1106 struct iommu_group *iommu_group;
1107 struct iommu_fwspec *iommu_fwspec;
1108
1109 bool offline_disabled:1;
1110 bool offline:1;
1111 bool of_node_reused:1;
1112 bool state_synced:1;
1113
1114 ANDROID_KABI_RESERVE(1);
1115 ANDROID_KABI_RESERVE(2);
1116 ANDROID_KABI_RESERVE(3);
1117 ANDROID_KABI_RESERVE(4);
1118 ANDROID_KABI_RESERVE(5);
1119 ANDROID_KABI_RESERVE(6);
1120 ANDROID_KABI_RESERVE(7);
1121 ANDROID_KABI_RESERVE(8);
1122 };
struct device_node 是 Linux 内核中用于表示设备树(Device Tree)中一个节点(Node)的数据结构。设备树是一种数据结构,用于描述硬件设备的信息,如它们的地址、中断、时钟等,这些信息对于操作系统在启动过程中配置和初始化硬件设备至关重要。
51 struct device_node {
52 const char *name;
53 const char *type;
54 phandle phandle;
55 const char *full_name;
56 struct fwnode_handle fwnode;
57
58 struct property *properties;
59 struct property *deadprops; /* removed properties */
60 struct device_node *parent;
61 struct device_node *child;
62 struct device_node *sibling;
63 #if defined(CONFIG_OF_KOBJ)
64 struct kobject kobj;
65 #endif
66 unsigned long _flags;
67 void *data;
68 #if defined(CONFIG_SPARC)
69 const char *path_component_name;
70 unsigned int unique_id;
71 struct of_irq_controller *irq_trans;
72 #endif
73 };
device_node结构体的成员解释。
const char *name;:节点的名称。在设备树中,每个节点都有一个唯一的名称,用于标识该节点。
const char *type;:节点的类型。类型通常用于描述节点的功能或类别,例如 "cpu", "serial", "memory" 等。
phandle phandle;:节点的句柄(Phandle)。在设备树中,每个节点都可以有一个唯一的句柄,用于在设备树的其他部分引用该节点。
const char *full_name;:节点的完整名称。这通常是节点路径的字符串表示,从根节点开始到该节点的完整路径。
struct fwnode_handle fwnode;:这是一个通用句柄,用于将设备树节点与其他类型的固件节点(如 ACPI 节点)统一处理。它允许内核以一种通用的方式访问和操作不同类型的固件节点。
struct property *properties;:指向节点属性列表的指针。属性是节点的一部分,用于存储有关节点的额外信息,如寄存器的地址、中断号等。
struct property *deadprops;:指向已删除属性列表的指针。在设备树处理过程中,某些属性可能会被删除或标记为无效,这些属性会被移动到 deadprops 列表中。
struct device_node *parent;、struct device_node *child;、struct device_node *sibling;:分别指向父节点、第一个子节点和兄弟节点的指针。这些指针构成了设备树中节点之间的链接,使得可以遍历整个设备树。
struct kobject kobj;(条件编译):如果定义了 CONFIG_OF_KOBJ 配置选项,则包含此成员。它用于将设备树节点与内核的对象模型(kobject)集成,从而允许使用内核的通用对象管理功能。
unsigned long _flags;:用于存储节点标志的位字段。这些标志可能用于控制节点的某些行为或属性。
void *data;:一个指向任意数据的指针,通常用于存储与节点相关的私有数据。
const char *path_component_name;、unsigned int unique_id;、struct of_irq_controller *irq_trans;(条件编译,针对 SPARC 架构):这些成员是 SPARC 架构特有的,可能用于处理与 SPARC 相关的特定硬件特性,如中断控制器和唯一标识符。
struct resource 是 Linux 内核中用于表示和管理系统资源(如内存、I/O 端口、中断等)的一个结构体。
20 struct resource {
21 resource_size_t start;
22 resource_size_t end;
23 const char *name;
24 unsigned long flags;
25 unsigned long desc;
26 struct resource *parent, *sibling, *child;
27
28 ANDROID_KABI_RESERVE(1);
29 ANDROID_KABI_RESERVE(2);
30 ANDROID_KABI_RESERVE(3);
31 ANDROID_KABI_RESERVE(4);
32 };
flags 表示资源类型。
3 platform注册
3.1 platform_driver_register
函数原型 | #define platform_driver_register(drv) __platform_driver_register(drv, THIS_MODULE) | |
参数 | struct platform_driver *drv | 指向 platform_driver 结构的指针。platform_driver 结构定义了平台驱动的相关信息和操作 |
返回值 | ||
功能 | 用于注册平台设备驱动 |
648 int __platform_driver_register(struct platform_driver *drv,
649 struct module *owner)
650 {
651 drv->driver.owner = owner;
652 drv->driver.bus = &platform_bus_type;
653 drv->driver.probe = platform_drv_probe;
654 drv->driver.remove = platform_drv_remove;
655 drv->driver.shutdown = platform_drv_shutdown;
656
657 return driver_register(&drv->driver);
658 }
3.1.1 __platform_driver_register分析
3.1.1.1 platform_drv_probe
该函数处理设备的探测过程,包括设置时钟默认值、附加到电源管理域、调用struct platform_driver的探测函数,并根据需要处理探测失败的情况。
587 static int platform_drv_probe(struct device *_dev)
588 {
589 struct platform_driver *drv = to_platform_driver(_dev->driver);
590 struct platform_device *dev = to_platform_device(_dev);
591 int ret;
592
593 ret = of_clk_set_defaults(_dev->of_node, false);
594 if (ret < 0)
595 return ret;
596
597 ret = dev_pm_domain_attach(_dev, true);
598 if (ret)
599 goto out;
600
601 if (drv->probe) {
602 ret = drv->probe(dev);
603 if (ret)
604 dev_pm_domain_detach(_dev, true);
605 }
606
607 out:
608 if (drv->prevent_deferred_probe && ret == -EPROBE_DEFER) {
609 dev_warn(_dev, "probe deferral not supported\n");
610 ret = -ENXIO;
611 }
612
613 return ret;
614 }
将设备附加到电源管理域。
104 int dev_pm_domain_attach(struct device *dev, bool power_on)
105 {
106 int ret;
107
108 if (dev->pm_domain)
109 return 0;
110
111 ret = acpi_dev_pm_attach(dev, power_on);
112 if (!ret)
113 ret = genpd_dev_pm_attach(dev);
114
115 return ret < 0 ? ret : 0;
116 }
3.1.1.2 platform_drv_remove
执行struct platform_driver中的remove函数。
621 static int platform_drv_remove(struct device *_dev)
622 {
623 struct platform_driver *drv = to_platform_driver(_dev->driver);
624 struct platform_device *dev = to_platform_device(_dev);
625 int ret = 0;
626
627 if (drv->remove)
628 ret = drv->remove(dev);
629 dev_pm_domain_detach(_dev, true);
630
631 return ret;
632 }
3.1.1.3 platform_drv_shutdown
执行struct platform_driver中的shutdown函数。
634 static void platform_drv_shutdown(struct device *_dev)
635 {
636 struct platform_driver *drv = to_platform_driver(_dev->driver);
637 struct platform_device *dev = to_platform_device(_dev);
638
639 if (drv->shutdown)
640 drv->shutdown(dev);
641 }
3.1.1.4 driver_register
146 int driver_register(struct device_driver *drv)
147 {
148 int ret;
149 struct device_driver *other;
150
151 if (!drv->bus->p) {
152 pr_err("Driver '%s' was unable to register with bus_type '%s' because the bus was not initialized.\n",
153 drv->name, drv->bus->name);
154 return -EINVAL;
155 }
156
157 if ((drv->bus->probe && drv->probe) ||
158 (drv->bus->remove && drv->remove) ||
159 (drv->bus->shutdown && drv->shutdown))
160 printk(KERN_WARNING "Driver '%s' needs updating - please use "
161 "bus_type methods\n", drv->name);
162
163 other = driver_find(drv->name, drv->bus);
164 if (other) {
165 printk(KERN_ERR "Error: Driver '%s' is already registered, "
166 "aborting...\n", drv->name);
167 return -EBUSY;
168 }
169
170 ret = bus_add_driver(drv);
171 if (ret)
172 return ret;
173 ret = driver_add_groups(drv, drv->groups);
174 if (ret) {
175 bus_remove_driver(drv);
176 return ret;
177 }
178 kobject_uevent(&drv->p->kobj, KOBJ_ADD);
179
180 return ret;
181 }
3.2 platform_driver_unregister
函数原型 | void platform_driver_unregister(struct platform_driver *drv) | |
参数 | struct platform_driver *drv | 指向 platform_driver 结构的指针。platform_driver 结构定义了平台驱动的相关信息和操作 |
返回值 | ||
功能 | 用于注销平台设备驱动 |
665 void platform_driver_unregister(struct platform_driver *drv)
666 {
667 driver_unregister(&drv->driver);
668 }
190 void driver_unregister(struct device_driver *drv)
191 {
192 if (!drv || !drv->p) {
193 WARN(1, "Unexpected driver unregister!\n");
194 return;
195 }
196 driver_remove_groups(drv, drv->groups);
197 bus_remove_driver(drv);
198 }
3.3 platform_device_register
在以前不支持设备树的Linux版本中,用户需要编写platform_device变量来描述设备信息,然后使用 platform_device_register 函数将设备信息注册到 Linux 内核中。
函数原型 | int platform_device_register(struct platform_device *pdev) | |
参数 | struct platform_device *pdev | 指向 platform_device 结构体的指针,这个结构体包含了设备的相关信息 |
返回值 | 成功:0 失败:错误码 | |
功能 | 注册平台设备 |
493 int platform_device_register(struct platform_device *pdev)
494 {
495 device_initialize(&pdev->dev);
496 arch_setup_pdev_archdata(pdev);
497 return platform_device_add(pdev);
498 }
3.4 platform_device_unregister
函数原型 | int platform_device_unregister(struct platform_device *pdev) | |
参数 | struct platform_device *pdev | 指向 platform_device 结构体的指针,这个结构体包含了设备的相关信息 |
返回值 | 成功:0 失败:错误码 | |
功能 | 注销注册平台设备 |
509 void platform_device_unregister(struct platform_device *pdev)
510 {
511 platform_device_del(pdev);
512 platform_device_put(pdev);
513 }
4 jw_io_core.c 驱动分析
4.1设备树
4.1.1 设备树文件
Bootloader会将设备树传递给内核,然后内核识别设备树,并根据它展开出Linux内核中的platform_device、i2c_client、spi_device等设备,而这些设备用到的内存、IRQ等资源,也被传递给了内核,内核会将这些资源绑定给展开的相应的设备。
9 / {
10 compatible = "rockchip,rk3399-tve1030g-avb", "rockchip,rk3399";
11 jw_io_init {
12 compatible = "jw_io_control";
13 hdmi_5v_gpio = <&gpio4 RK_PD5 GPIO_ACTIVE_HIGH>;
14 //vbus_5v_gpio = <&gpio1 RK_PD0 GPIO_ACTIVE_HIGH>;
15 led_ctl = <&gpio4 RK_PD4 GPIO_ACTIVE_HIGH>;
16 hub_rst_gpio = <&gpio4 RK_PD6 GPIO_ACTIVE_HIGH>;
17 power_hold_gpio = <&gpio0 RK_PB0 GPIO_ACTIVE_HIGH>;
18 mic_switch_gpio = <&gpio4 RK_PA6 GPIO_ACTIVE_HIGH>;
19 audio_switch_gpio = <&gpio4 RK_PA7 GPIO_ACTIVE_HIGH>;
20 pwm_gpio = <&gpio4 RK_PC6 GPIO_ACTIVE_HIGH>;
21 SPDIF_gpio = <&gpio3 RK_PC0 GPIO_ACTIVE_HIGH>;
22 pwms = <&pwm1 0 1000000 0>;
23 pinctrl-names = "default";
24 pinctrl-0 = <&jw_io_gpio>;
25
26 ddr_check_1_gpio = <&gpio2 RK_PA0 GPIO_ACTIVE_HIGH>;
27 ddr_check_2_gpio = <&gpio2 RK_PA7 GPIO_ACTIVE_HIGH>;
28 ddr_check_3_gpio = <&gpio2 RK_PB3 GPIO_ACTIVE_HIGH>;
29 ddr_check_4_gpio = <&gpio2 RK_PB4 GPIO_ACTIVE_HIGH>;
30 };
31 };
761 &pinctrl {
884 jw_io_init{
885 jw_io_gpio: jw_io_gpio_col{
886 rockchip,pins =
887 <2 0 RK_FUNC_GPIO &pcfg_pull_up>,
888 <2 7 RK_FUNC_GPIO &pcfg_pull_up>,
889 <2 11 RK_FUNC_GPIO &pcfg_pull_up>,
890 <2 12 RK_FUNC_GPIO &pcfg_pull_up>;
891 };
892 };
900 };
生成的文件节点在/proc/device-tree/jw_io_init下。
rk3399_Android11:/proc/device-tree # ls
#address-cells dummy_cpll i2c@ff140000 mipi-dphy-tx1rx1@ff968000 pcie@f8000000 qos@ffa76000 rkisp1@ff920000 spi@ff1f0000 vcc3v3-sys
#size-cells dummy_vpll i2c@ff150000 mpp-srv phy@ff7c0000 qos@ffa90000 rktimer@ff850000 spi@ff200000 vcc5v0-host-regulator
__symbols__ dwmmc@fe310000 i2c@ff160000 name phy@ff800000 qos@ffa98000 rkvdec@ff660000 spi@ff350000 vdd-log
adc_keys dwmmc@fe320000 i2c@ff3c0000 nocp-cci-msch0@ffa86000 pinctrl qos@ffaa0000 rng@ff8b8000 sram@ff8d0000 vdpu@ff650400
aliases edp@ff970000 i2c@ff3d0000 nocp-cci-msch1@ffa8e000 pmu-clock-controller@ff750000 qos@ffaa0080 rockchip-suspend syscon@ff320000 vepu@ff650000
amba efuse@ff690000 i2c@ff3e0000 nocp-gpu-msch0@ffa86400 pmu_a53 qos@ffaa8000 rockchip-system-monitor syscon@ff770000 vop@ff8f0000
backlight energy-costs i2s@ff880000 nocp-gpu-msch1@ffa8e400 pmu_a72 qos@ffaa8080 saradc@ff100000 test-power vop@ff900000
charge-animation es8323-sound i2s@ff890000 nocp-hp-msch0@ffa86800 power-management@ff310000 qos@ffab0000 sdhci@fe330000 thermal-zones voppwm@ff8f01a0
chosen ethernet@fe300000 i2s@ff8a0000 nocp-hp-msch1@ffa8e800 psci qos@ffab0080 sdio-pwrseq timer voppwm@ff9001a0
clock-controller@ff760000 external-gmac-clock iep@ff670000 nocp-lp-msch0@ffa86c00 pwm@ff420000 qos@ffab8000 serial-number tsadc@ff260000 watchdog@ff848000
compatible fiq-debugger interrupt-controller@fee00000 nocp-lp-msch1@ffa8ec00 pwm@ff420010 qos@ffac0000 serial@ff180000 usb@fe340000 wireless-bluetooth
cpuinfo firmware interrupt-parent nocp-video-msch0@ffa87000 pwm@ff420020 qos@ffac0080 serial@ff190000 usb@fe380000 wireless-wlan
cpus gpio-keys iommu@ff650800 nocp-video-msch1@ffa8f000 pwm@ff420030 qos@ffac8000 serial@ff1a0000 usb@fe3a0000 xin24m
ddr_timing gpu@ff9a0000 iommu@ff660480 nocp-vio0-msch0@ffa87400 qos@ffa58000 qos@ffac8080 serial@ff1b0000 usb@fe3c0000 xin32k
dfi@ff630000 hdmi-dp-sound iommu@ff670800 nocp-vio0-msch1@ffa8f400 qos@ffa5c000 qos@ffad0000 serial@ff370000 usb@fe3e0000
display-subsystem hdmi-hdcp2@ff988000 iommu@ff8f3f00 nocp-vio1-msch0@ffa87800 qos@ffa60080 qos@ffad8080 spdif-out usb@fe800000
dmc hdmi-sound iommu@ff903f00 nocp-vio1-msch1@ffa8f800 qos@ffa60100 qos@ffae0000 spdif-sound usb@fe900000
dp-sound hdmi@ff940000 iommu@ff914000 opp-table0 qos@ffa60180 reserved-memory spdif@ff870000 vcc-phy-regulator
dp@fec00000 i2c@ff110000 iommu@ff924000 opp-table1 qos@ffa70000 rga@ff680000 spi@ff1c0000 vcc-pwr-1v8-regulator
dsi@ff960000 i2c@ff120000 jw_io_init opp-table2 qos@ffa70080 rk-headset spi@ff1d0000 vcc-sys
dsi@ff968000 i2c@ff130000 memory opp-table3 qos@ffa74000 rkisp1@ff910000 spi@ff1e0000 vcc1v8-s0
rk3399_Android11:/proc/device-tree # ls jw_io_init/
audio_switch_gpio compatible ddr_check_1_gpio ddr_check_2_gpio ddr_check_3_gpio ddr_check_4_gpio hdmi_5v_gpio hub_rst_gpio led_ctl mic_switch_gpio name pinctrl-0 pinctrl-names power_hold_gpio pwm_gpio pwms
rk3399_Android11:/proc/device-tree #
/sys/bus/platform/devices/文件下生成了节点jw_io_init。
rk3399_Android11:/sys/bus/platform # ls devices/jw_io_init/
driver driver_override led_display mic_switch modalias of_node power subsystem uevent
4.2 驱动分析
4.2.1 驱动框架
14 #include <linux/module.h>
15 #include <linux/moduleparam.h>
16 #include <linux/init.h>
17 #include <linux/delay.h>
18 #include <linux/pm.h>
19 #include <linux/i2c.h>
20 #include <linux/spi/spi.h>
21 #include <linux/platform_device.h>
22 #include <linux/errno.h>
23 #include <linux/err.h>
24 #include <linux/debugfs.h>
25 #include <linux/of_gpio.h>
26 #include <linux/gpio.h>
27 #include <linux/iio/consumer.h>
28 #include "jwio.h"
199 static int jw_io_control_probe(struct platform_device *pdev)
200 {
201 struct device_node *node = pdev->dev.of_node;
202 struct jw_io_pdata *pdata;
203 int ret;
204 enum of_gpio_flags flags;
205 printk(" #######jw_io_control_probe####### \n");
206 enable = 0 ;
207
208 pdata = kzalloc(sizeof(struct jw_io_pdata), GFP_KERNEL);
209 if (pdata == NULL) {
210 printk("%s failed to allocate driver data\n",__FUNCTION__);
211 return -ENOMEM;
212 }
213 memset(pdata,0,sizeof(struct jw_io_pdata));
214
216 ret = of_get_named_gpio_flags(node, "power_hold_gpio", 0, &flags);
217 if (ret < 0) {
218 printk("%s() Can not read property power_hold_gpio\n", __FUNCTION__);
219 goto err;
220 } else {
221 pdata->power_hold_gpio = ret;
222 ret = devm_gpio_request(&pdev->dev, pdata->power_hold_gpio, "power_hold_gpio");
223 if(ret < 0){
224 printk("%s() devm_gpio_request power_hold_gpio request ERROR\n", __FUNCTION__);
225 goto err;
226 }
227 ret = gpio_direction_output(pdata->power_hold_gpio,1);
228 if(ret < 0){
229 printk("%s() gpio_direction_input power_hold_gpio set ERROR\n", __FUNCTION__);
230 goto err;
231 }
232 }
397 }
398
399 static int jw_io_control_remove(struct platform_device *pdev)
400 {
401 if(JWpdata_info)
402 kfree(JWpdata_info);
403 return 0;
404 }
405
406 static int jw_io_control_suspend(struct platform_device *pdev, pm_message_t state)
407 {
408 printk("LED_early_suspend LED_early_suspend LED_early_suspend !!!!\n");
409
410
411 enable = 0;
412 LED_SET(0);
413 return 0;
414 }
415
416 static int jw_io_control_resume(struct platform_device *pdev)
417 {
418 printk("LED_early_resume LED_early_resume LED_early_resume !!!!\n");
419 gpio_direction_output(JWpdata_info->hub_rst_gpio,0);
420 msleep(800);
421 gpio_direction_output(JWpdata_info->hub_rst_gpio,1);
422
423 enable = 1;
424 LED_SET(11);
425 return 0;
426 }
428 static const struct of_device_id jw_io_control_of_match[] = {
429 { .compatible = "jw_io_control", },
430 {},
431 };
432 MODULE_DEVICE_TABLE(of, jw_io_control_of_match);
433
434 static struct platform_driver jw_io_control_driver = {
435 .probe = jw_io_control_probe,
436 .remove = jw_io_control_remove,
437 .resume = jw_io_control_resume,
438 .suspend = jw_io_control_suspend,
439 .driver = {
440 .name = "jw_io_control",
441 .owner = THIS_MODULE,
442 .of_match_table = of_match_ptr(jw_io_control_of_match),
443 },
444 };
445
446 static int __init jw_io_control_init(void)
447 {
448 platform_driver_register(&jw_io_control_driver);
449 return 0;
450 }
451
452 static void __exit jw_io_control_exit(void)
453 {
454 platform_driver_unregister(&jw_io_control_driver);
455 }
456
457 subsys_initcall(jw_io_control_init);
458
460 MODULE_DESCRIPTION("jw io Core Driver");
461 MODULE_LICENSE("GPL");
4.2.2 platform驱动文件系统
当一个平台设备驱动被注册时,内核会创建相应的目录和文件,并将这些信息暴露到 /sys/bus/platform/drivers/ 目录中
rk3399_Android11:/sys/bus/platform/drivers/jw_io_control # ls
bind jw_io_init uevent unbind
rk3399_Android11:/sys/bus/platform/drivers/jw_io_control/jw_io_init # ls
driver driver_override led_display mic_switch modalias of_node power subsystem uevent
/sys/bus/platform/drivers/文件下生成文件节点jw_io_control,struct platform_driver实例化jw_io_control_driver驱动时将驱动的名称设置为jw_io_control
bind: 用于将平台设备绑定到 jw_io_control驱动程序。可以通过写入设备的 ID 来将设备绑定到驱动程序。
unbind: 用于将设备从 jw_io_control驱动程序中解绑。
jw_io_control: 显示驱动程序的名称
434 static struct platform_driver jw_io_control_driver = {
435 .probe = jw_io_control_probe,
436 .remove = jw_io_control_remove,
437 .resume = jw_io_control_resume,
438 .suspend = jw_io_control_suspend,
439 .driver = {
440 .name = "jw_io_control",
441 .owner = THIS_MODULE,
442 .of_match_table = of_match_ptr(jw_io_control_of_match),
443 },
444 };
jw_io_control文件创建定位:platform_driver_register->__platform_driver_register->driver_register->bus_add_driver->driver_create_file
4.2.3 probe参数传递
static int jw_io_control_probe(struct platform_device *pdev)函数中的pdev参数是如何传递进来的?
设备注册:当一个平台设备被注册到系统中时,它会被内核添加到平台总线上。设备的注册过程通常涉及 platform_device_register() 函数。或者内核识别设备树,生成平台设备。
驱动程序绑定:驱动程序会通过 platform_driver 结构体中的 probe 函数指针与设备进行绑定。这个结构体定义了驱动程序的行为和处理函数。platform_driver 结构体通常包含 probe、remove、suspend 和 resume 等回调函数。这些函数会在设备注册、解绑、挂起和恢复时被调用。
Probe 函数调用:当设备和驱动程序绑定时,内核会调用 probe 函数,pdev 是由内核在设备和驱动程序绑定时自动传递给 probe 函数的,包含了当前设备的相关信息和资源。此时,pdev 参数就是 platform_device 结构体的指针,它代表了当前正在被初始化的设备。platform_device 结构体包含了设备的相关信息,如设备的 ID、资源(内存、IO 地址等)、设备名称和设备状态等。
4.2.4 驱动设备匹配分析
platform 总线是 bus_type 的一个具体实例,platform_bus_type 就是 platform 平台总线,其中 platform_match 就是匹配函数。
988 static int platform_match(struct device *dev, struct device_driver *drv)
989 {
990 struct platform_device *pdev = to_platform_device(dev);
991 struct platform_driver *pdrv = to_platform_driver(drv);
992
993 /* When driver_override is set, only bind to the matching driver */
994 if (pdev->driver_override)
995 return !strcmp(pdev->driver_override, drv->name);
996
997 /* Attempt an OF style match first */
998 if (of_driver_match_device(dev, drv))
999 return 1;
1000
1001 /* Then try ACPI style match */
1002 if (acpi_driver_match_device(dev, drv))
1003 return 1;
1004
1005 /* Then try to match against the id table */
1006 if (pdrv->id_table)
1007 return platform_match_id(pdrv->id_table, pdev) != NULL;
1008
1009 /* fall-back to driver name match */
1010 return (strcmp(pdev->name, drv->name) == 0);
1011 }
of_driver_match_device 函数定义在文件 include/linux/of_device.h 中。device_driver 结构体(表示 设备驱动)中有个名为of_match_table的成员变量,此成员变量保存着驱动的compatible匹配表, 设备树中的每个设备节点的 compatible 属性会和 of_match_table 表中的所有成员比较,查看是 否有相同的条目,如果有的话就表示设备和此驱动匹配,设备和驱动匹配成功以后 probe 函数 就会执行
追踪:of_driver_match_device->of_match_device->of_match_node->__of_match_node->__of_device_is_compatible
1063 const struct of_device_id *__of_match_node(const struct of_device_id *matches,
1064 const struct device_node *node)
1065 {
1066 const struct of_device_id *best_match = NULL;
1067 int score, best_score = 0;
1068
1069 if (!matches)
1070 return NULL;
1071
1072 for (; matches->name[0] || matches->type[0] || matches->compatible[0]; matches++) {
1073 score = __of_device_is_compatible(node, matches->compatible,
1074 matches->type, matches->name);
1075 if (score > best_score) {
1076 best_match = matches;
1077 best_score = score;
1078 }
1079 }
1080
1081 return best_match;
1082 }
struct device_node 是一个在 Linux 内核中用于表示设备树节点的结构体
500 static int __of_device_is_compatible(const struct device_node *device,
501 const char *compat, const char *type, const char *name)
502 {
503 struct property *prop;
504 const char *cp;
505 int index = 0, score = 0;
506
507 /* Compatible match has highest priority */
508 if (compat && compat[0]) {
509 prop = __of_find_property(device, "compatible", NULL);
510 for (cp = of_prop_next_string(prop, NULL); cp;
511 cp = of_prop_next_string(prop, cp), index++) {
512 if (of_compat_cmp(cp, compat, strlen(compat)) == 0) {
513 score = INT_MAX/2 - (index << 2);
514 break;
515 }
516 }
517 if (!score)
518 return 0;
519 }
520
521 /* Matching type is better than matching name */
522 if (type && type[0]) {
523 if (!device->type || of_node_cmp(type, device->type))
524 return 0;
525 score += 2;
526 }
527
528 /* Matching name is a bit better than not */
529 if (name && name[0]) {
530 if (!device->name || of_node_cmp(name, device->name))
531 return 0;
532 score++;
533 }
534
535 return score;
536 }