[TOC]
drivers/amba: Linux的AMBA总线与SoC设备驱动核心 drivers/amba
目录是 Linux 内核中用于管理和驱动基于 AMBA (Advanced Microcontroller Bus Architecture) 总线的外设 的核心框架。在现代基于 ARM 的片上系统 (SoC) 中,几乎所有的片上外设(如 UART、GPIO、SPI、I2C、定时器等)都挂载在 AMBA 总线上。因此,这个目录是 ARM SoC 平台驱动程序的基石。
一、 历史与背景 这项技术是为了解决什么特定问题而诞生的?
在嵌入式系统中,特别是 SoC 中,外设不是像 PCI 或 USB 设备那样可以在运行时动态发现的(即非“热插拔”或“可枚举”的)。它们是静态地 集成在芯片上的,其物理地址和中断号在芯片设计时就已经固定。
早期的 Linux 内核通过在 C 代码(board-*.c
文件)中硬编码这些“平台设备”(platform devices)的信息来支持它们。这种方法导致了巨大的问题:
内核与硬件紧密耦合 : 每支持一款新的开发板,就需要编写一个新的 board-*.c
文件并编译到内核中。
可维护性差 : 内核源码树中充满了描述硬件布局的 C 代码,极其臃肿和混乱。
缺乏灵活性 : 无法实现一个单一的内核镜像(binary image)来支持多款硬件配置相似但略有不同的开发板。
drivers/amba
框架,与设备树 (Device Tree) 机制紧密结合 ,正是为了解决上述问题而诞生的。它提供了一种标准化的、数据驱动的方式来注册和匹配这些静态的片上外设及其驱动程序。
二、 核心原理与设计 drivers/amba
的核心设计思想是将硬件的描述与硬件的驱动逻辑彻底分离 。
硬件描述 : 由 设备树 (.dts
文件) 提供。设备树以一种结构化的文本格式,描述了 SoC 上有哪些外设、它们的寄存器物理地址、使用的中断号、时钟源等信息。
驱动逻辑 : 由 C 语言编写的驱动程序(如 pl011.c
)提供。驱动程序包含如何操作硬件寄存器以实现特定功能的代码。
drivers/amba
框架扮演了两者之间的**“红娘”**角色。其工作流程如下:
设备树解析 : 内核启动时,会解析 Bootloader 传递过来的设备树二进制文件 (DTB)。
总线扫描 : 当内核发现设备树中描述了一个 AMBA 总线时,drivers/amba
框架会被激活。
设备创建 : 框架会遍历该 AMBA 总线节点下的所有子节点(每个子节点代表一个外设,如一个 UART)。对于每个子节点,它会在内核中动态地创建一个 struct amba_device
对象。这个对象中包含了从设备树中读取到的所有硬件资源信息(寄存器地址、IRQ 等)。
驱动匹配 : 内核的驱动核心会为这个新创建的 amba_device
寻找一个匹配的 amba_driver
。匹配的唯一依据 是设备树节点中的 compatible
字符串 。
驱动加载 (Probe) : 一旦找到 compatible
字符串相匹配的驱动,内核就会调用该驱动的 probe()
函数,并将前面创建的 amba_device
对象作为参数传递给它。
硬件初始化 : 在 probe()
函数中,驱动程序从 amba_device
参数中获取到寄存器地址和中断号等资源,然后进行 ioremap
映射内存、request_irq
注册中断,最终完成硬件的初始化。
主要优势 :
硬件和软件解耦 : 内核代码不再关心具体的硬件地址。
单一内核镜像 : 同一个内核镜像,只需要配合不同的 .dtb
文件,就可以在不同的硬件板子上启动。
标准化 : 为所有 AMBA 外设提供了一套统一的设备-驱动模型。
三、 关键数据结构与文件 1. amba.c
这是 AMBA 总线框架的核心实现。它定义了 AMBA 总线类型 (amba_bustype
),并实现了设备与驱动的匹配逻辑 (amba_match
) 。
2. pl011.c
(以及类似的外设驱动文件) 这是一个具体的AMBA 设备驱动程序 范例,用于驱动 ARM PrimeCell PL011 UART(一种非常常见的串口控制器)。
struct amba_driver
: 这是驱动程序的核心结构,用于向 AMBA 框架“注册”自己。1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 static struct amba_driver pl011_driver = { .drv = { .name = "uart-pl011" , }, .probe = pl011_probe, .remove = pl011_remove, .id_table = pl011_ids, }; ``` * `.probe`: 驱动的入口函数,在设备和驱动匹配成功后被调用。 * `.remove`: 驱动的卸载函数。 * `.id_table`: 一个 `amba_id` 结构体数组,**极其关键**。它列出了这个驱动程序能够支持的所有设备的 `compatible` 字符串(或硬件 ID)。 * **`struct amba_id `**: 定义了匹配规则。 ```c static const struct amba_id pl011_ids [] = { { .id = 0x00041011 , .mask = 0x000fffff , .data = &vendor_arm, }, { 0 , 0 , 0 } };
在现代基于设备树的系统中,匹配更多是依赖 compatible
字符串,而不是硬件 id
。
3. struct amba_device
这是内核中代表一个物理 AMBA 设备 的实例。它由框架根据设备树节点自动创建。
关键字段 :
struct resource res
: 保存了设备的资源 ,主要是内存映射 I/O (MMIO) 的物理基地址和大小。
int irq[NUM_IRQS]
: 保存了设备使用的中断号。
unsigned int periphid
: 从硬件寄存器中读出的设备 ID。
struct device dev
: 内嵌的通用 device
结构,其中包含了设备树节点指针。
四、 实践:观察与交互 你不能直接“运行”AMBA 框架,但你可以通过设备树和 sysfs
来观察和影响它的行为。
1. 设备树 (.dts
) 中描述一个 AMBA 设备
这是一个典型的 UART 设备节点:
1 2 3 4 5 6 7 8 uart0: serial@4000c000 { compatible = "arm,pl011" , "arm,primecell" ; reg = <0x4000c000 0x1000 > ; interrupts = <0 32 4 > ; clocks = <&clkc 15 > , <&clkc 18 > ; clock-names = "uart_pclk" , "apb_pclk" ; };
compatible = "arm,pl011"
: 这是最重要的属性 。drivers/amba/pl011.c
驱动就是因为能匹配这个字符串才被加载的。
reg
: 定义了该 UART 控制器的寄存器基地址是 0x4000c000
,范围是 0x1000
字节。这个信息会被填充到 amba_device->res
中。
interrupts
: 定义了中断号。这个信息会被填充到 amba_device->irq[0]
中。
2. 在 sysfs
中观察结果
当内核启动后,你可以在 /sys/
文件系统中看到 AMBA 框架工作的成果:
1 2 3 4 5 6 7 8 9 10 11 ls /sys/bus/amba/devices/ls /sys/bus/amba/devices/4000c000.serial/
4000c000.serial
: 设备名通常由其基地址和节点名构成。
driver -> ../../../bus/amba/drivers/uart-pl011
: 这是一个符号链接,清晰地表明这个设备已经被 uart-pl011
驱动所绑定。
of_node
: 指向设备树节点的符号链接。
resource
: 一个可读文件,显示了该设备占用的内存资源,内容就是 reg
属性中定义的值。
五、 总结 drivers/amba
目录提供了一个优雅而强大的框架,用于管理 ARM SoC 上的静态片上外设。它通过将硬件描述(设备树)与驱动逻辑(C代码)分离 ,并利用 compatible
字符串作为匹配的桥梁 ,极大地提高了 Linux 内核在 ARM 平台上的可移植性和可维护性。
对于嵌入式 Linux 开发者而言,理解 drivers/amba
的工作原理,就是理解现代 ARM SoC 如何被驱动的根本。它是编写任何与片上外设交互的驱动程序的基础。
drivers/reset/core.c reset_control_deassert: 将一个外设移出复位状态 此函数是Linux内核复位控制子系统(Reset Control Subsystem)的核心API之一。它的作用是解除 对一个硬件外设的复位信号, 允许该外设开始正常工作。这是设备驱动程序初始化过程中的一个关键步骤, 通常在开启外设时钟之后调用。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 int reset_control_deassert (struct reset_control *rstc) { if (!rstc) return 0 ; if (WARN_ON(IS_ERR(rstc))) return -EINVAL; if (reset_control_is_array(rstc)) return reset_control_array_deassert(rstc_to_array(rstc)); if (rstc->shared) { if (WARN_ON(atomic_read (&rstc->triggered_count) != 0 )) return -EINVAL; if (atomic_inc_return(&rstc->deassert_count) != 1 ) return 0 ; } else { if (!rstc->acquired) { WARN(1 , "reset %s (ID: %u) is not acquired\n" , rcdev_name(rstc->rcdev), rstc->id); return -EPERM; } } if (!rstc->rcdev->ops->deassert) return 0 ; return rstc->rcdev->ops->deassert(rstc->rcdev, rstc->id); } EXPORT_SYMBOL_GPL(reset_control_deassert);
drivers/amba/bus.c AMBA Device Sysfs Attributes: Exposing Hardware Info and Control 此代码片段的作用是为内核中每一个AMBA设备, 在其对应的sysfs
目录(例如 /sys/bus/amba/devices/40011000.usart/
)下创建一组标准的属性文件。这些文件向用户空间暴露了该设备的关键硬件信息 (如外设ID和内存资源), 并提供了一个强大的控制接口 (driver_override
) 来覆盖默认的驱动程序绑定行为。
在单核无MMU的STM32H750平台上的原理与作用
在STM32H750平台上, 所有片上外设都挂载在AMBA总线上。当内核根据设备树初始化这些外设时, 会为每一个外设创建一个AMBA设备实例。此时, 本代码片段所定义的amba_dev_groups
就会被自动应用, 为每一个外设(UART, SPI, I2C等)创建如下的sysfs
文件。这对于嵌入式系统的开发和调试至关重要:
硬件信息的可视化 : 无需连接调试器, 用户可以直接通过shell访问这些文件, 来快速确认:
id
文件: 读取该外设的唯一硬件ID (periphid
)。这可以用来验证设备树中的配置是否与硬件手册一致。
resource
文件: 查看该外设被分配到的寄存器内存区域的起始地址、结束地址和标志位。这对于调试地址冲突或内存映射问题极为有用。
灵活的驱动测试 : driver_override
文件是一个强大的调试工具。它允许开发者在不修改内核代码或设备树、不重新编译的情况下, 强制一个特定的驱动程序与某个AMBA设备进行绑定。这在测试一个新版本的驱动, 或者在有多个驱动都能匹配同一个设备的情况下指定使用某一个时, 非常方便。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 static ssize_t driver_override_show (struct device *_dev, struct device_attribute *attr, char *buf) { struct amba_device *dev = to_amba_device(_dev); ssize_t len; device_lock(_dev); len = sprintf (buf, "%s\n" , dev->driver_override); device_unlock(_dev); return len; } static ssize_t driver_override_store (struct device *_dev, struct device_attribute *attr, const char *buf, size_t count) { struct amba_device *dev = to_amba_device(_dev); int ret; ret = driver_set_override(_dev, &dev->driver_override, buf, count); if (ret) return ret; return count; } static DEVICE_ATTR_RW (driver_override) ;#define amba_attr_func(name,fmt,arg...) \ static ssize_t name##_show(struct device *_dev, \ struct device_attribute *attr, char *buf) \ { \ struct amba_device *dev = to_amba_device(_dev); \ return sprintf (buf, fmt, arg); \ } \ static DEVICE_ATTR_RO (name) amba_attr_func (id, "%08x\n" , dev->periphid) ;amba_attr_func(resource, "\t%016llx\t%016llx\t%016lx\n" , (unsigned long long )dev->res.start, (unsigned long long )dev->res.end, dev->res.flags); static struct attribute *amba_dev_attrs [] = { &dev_attr_id.attr, &dev_attr_resource.attr, &dev_attr_driver_override.attr, NULL , }; ATTRIBUTE_GROUPS(amba_dev);
amba_get_enable_pclk
: 获取并使能AMBA设备的外设时钟此函数是一个专用的辅助函数, 它的职责非常明确: 为给定的AMBA设备获取并使能其外设时钟 (pclk
) 。在像STM32H750这样的现代MCU中, 为了节省功耗, 绝大多数外设的硬件时钟在系统启动时都是默认关闭的。在访问一个外设的寄存器或使其工作之前, 必须先为其提供时钟信号。此函数封装了与内核通用时钟框架 (Common Clock Framework) 交互的标准流程, 以完成这一关键的准备步骤。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 static int amba_get_enable_pclk (struct amba_device *pcdev) { int ret; pcdev->pclk = clk_get(&pcdev->dev, "apb_pclk" ); if (IS_ERR(pcdev->pclk)) return PTR_ERR(pcdev->pclk); ret = clk_prepare_enable(pcdev->pclk); if (ret) clk_put(pcdev->pclk); return ret; }
amba_read_periphid
: 从硬件读取AMBA外设的ID此函数是AMBA总线驱动与硬件交互的第一个关键步骤。它的核心任务是访问一个AMBA设备(外设)的物理寄存器, 读取并解析出能够唯一标识该设备类型和厂商的ID号。这个ID号包括外设ID(periphid
)和单元ID(cid
)。为了能成功地访问寄存器, 此函数必须执行一系列前置的准备工作, 包括打开设备的电源域、使能其时钟, 以及解除其复位状态。这是一个典型的、完整的硬件访问流程。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 static int amba_read_periphid (struct amba_device *dev) { struct reset_control *rstc ; u32 size, pid, cid; void __iomem *tmp; int i, ret; ret = dev_pm_domain_attach(&dev->dev, PD_FLAG_ATTACH_POWER_ON); if (ret) { dev_dbg(&dev->dev, "can't get PM domain: %d\n" , ret); goto err_out; } ret = amba_get_enable_pclk(dev); if (ret) { dev_dbg(&dev->dev, "can't get pclk: %d\n" , ret); goto err_pm; } rstc = of_reset_control_array_get_optional_shared(dev->dev.of_node); if (IS_ERR(rstc)) { ret = PTR_ERR(rstc); if (ret != -EPROBE_DEFER) dev_err(&dev->dev, "can't get reset: %d\n" , ret); goto err_clk; } reset_control_deassert(rstc); reset_control_put(rstc); size = resource_size(&dev->res); tmp = ioremap(dev->res.start, size); if (!tmp) { ret = -ENOMEM; goto err_clk; } for (pid = 0 , i = 0 ; i < 4 ; i++) pid |= (readl(tmp + size - 0x20 + 4 * i) & 255 ) << (i * 8 ); for (cid = 0 , i = 0 ; i < 4 ; i++) cid |= (readl(tmp + size - 0x10 + 4 * i) & 255 ) << (i * 8 ); if (cid == CORESIGHT_CID) { void __iomem *csbase = tmp + size - 4096 ; dev->uci.devarch = readl(csbase + UCI_REG_DEVARCH_OFFSET); dev->uci.devtype = readl(csbase + UCI_REG_DEVTYPE_OFFSET) & 0xff ; } if (cid == AMBA_CID || cid == CORESIGHT_CID) { dev->periphid = pid; dev->cid = cid; } if (!dev->periphid) ret = -ENODEV; iounmap(tmp); err_clk: amba_put_disable_pclk(dev); err_pm: dev_pm_domain_detach(&dev->dev, true ); err_out: return ret; }
amba_match
: 匹配AMBA设备与驱动此函数是AMBA总线驱动模型的核心匹配接口。当一个新的AMBA设备被系统发现(通常通过解析设备树), 或者一个新的AMBA驱动程序被加载时, 内核的驱动核心会调用此函数, 以判断该驱动(drv
)是否声称自己能够支持该设备(dev
)。此函数的返回值决定了是否要继续进行下一步的probe
(探测)过程。它的逻辑包含了惰性硬件ID读取、用户空间强制覆盖以及标准的ID表匹配, 是一个健壮且灵活的匹配机制。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 static int amba_match (struct device *dev, const struct device_driver *drv) { struct amba_device *pcdev = to_amba_device(dev); const struct amba_driver *pcdrv = to_amba_driver(drv); mutex_lock(&pcdev->periphid_lock); if (!pcdev->periphid) { int ret = amba_read_periphid(pcdev); if (ret) { mutex_unlock(&pcdev->periphid_lock); return -EPROBE_DEFER; } dev_set_uevent_suppress(dev, false ); kobject_uevent(&dev->kobj, KOBJ_ADD); } mutex_unlock(&pcdev->periphid_lock); if (pcdev->driver_override) return !strcmp (pcdev->driver_override, drv->name); return amba_lookup(pcdrv->id_table, pcdev) != NULL ; }
amba_uevent: 为AMBA设备生成uevent环境变量 此函数是一个回调函数, 在内核为AMBA (Advanced Microcontroller Bus Architecture) 总线上的设备生成一个 uevent
(内核事件) 时被调用。它的核心作用是向这个uevent中添加两个关键的环境变量: AMBA_ID
和 MODALIAS
。这些变量随后被发送到用户空间, 以便 udev
或 mdev
等守护进程能够识别该设备并为其自动加载正确的驱动程序。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 static int amba_uevent (const struct device *dev, struct kobj_uevent_env *env) { const struct amba_device *pcdev = to_amba_device(dev); int retval = 0 ; retval = add_uevent_var(env, "AMBA_ID=%08x" , pcdev->periphid); if (retval) return retval; retval = add_uevent_var(env, "MODALIAS=amba:d%08X" , pcdev->periphid); return retval; }
amba_probe
: AMBA总线设备探测和初始化此函数是AMBA总线驱动模型的核心, 它扮演着“探测”或“初始化”一个具体AMBA设备的角色。当内核发现一个AMBA设备(例如, STM32H750上的一个USART外设)与一个驱动程序(例如, STM32的USART驱动)成功匹配后, 内核就会调用该amba_probe
函数。它的职责是按照严格的顺序, 为该设备准备好所有必要的系统资源(如中断、时钟、电源), 然后调用具体设备驱动自己的probe
函数来完成最终的初始化。如果其中任何一步失败, 它必须能按相反的顺序“回滚”所有已完成的操作, 以确保系统处于干净的状态。
of_amba_device_decode_irq
: 从设备树解析AMBA设备的中断信息这是一个辅助函数, 专门负责从设备树(Device Tree)中读取一个AMBA设备的中断(IRQ)配置信息, 并将这些信息填充到该设备的内核数据结构中。在STM32H750上, 每个外设的中断连接关系都在设备树文件(.dts
)中定义, 此函数就是将这些定义转化为内核可以理解和使用的Linux IRQ号的关键桥梁。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 static int of_amba_device_decode_irq (struct amba_device *dev) { struct device_node *node = dev->dev.of_node; int i, irq = 0 ; if (IS_ENABLED(CONFIG_OF_IRQ) && node) { for (i = 0 ; i < AMBA_NR_IRQS; i++) { irq = of_irq_get(node, i); if (irq < 0 ) { if (irq == -EPROBE_DEFER) return irq; irq = 0 ; } dev->irq[i] = irq; } } return 0 ; }
函数 amba_probe
详细解析 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 static int amba_probe (struct device *dev) { struct amba_device *pcdev = to_amba_device(dev); struct amba_driver *pcdrv = to_amba_driver(dev->driver); const struct amba_id *id = amba_lookup(pcdrv->id_table, pcdev); int ret; do { ret = of_amba_device_decode_irq(pcdev); if (ret) break ; ret = of_clk_set_defaults(dev->of_node, false ); if (ret < 0 ) break ; ret = dev_pm_domain_attach(dev, PD_FLAG_ATTACH_POWER_ON); if (ret) break ; ret = amba_get_enable_pclk(pcdev); if (ret) { dev_pm_domain_detach(dev, true ); break ; } pm_runtime_get_noresume(dev); pm_runtime_set_active(dev); pm_runtime_enable(dev); ret = pcdrv->probe(pcdev, id); if (ret == 0 ) break ; pm_runtime_disable(dev); pm_runtime_set_suspended(dev); pm_runtime_put_noidle(dev); amba_put_disable_pclk(pcdev); dev_pm_domain_detach(dev, true ); } while (0 ); return ret; }
AMBA总线: 注册ARM内核的片上总线类型 此代码片段的核心作用是在Linux内核中定义并注册 AMBA (Advanced Microcontroller Bus Architecture) 总线类型 。这为所有基于AMBA总线的片上外设 (on-chip peripherals) 提供了一个标准的驱动模型框架。注册成功后, 内核就拥有了识别、匹配、和管理AMBA设备及其驱动程序的能力, 并在sysfs
中创建了相应的目录结构 (/sys/bus/amba/
)。
在单核无MMU的STM32H750平台上的原理与作用
STM32H750微控制器是基于ARM Cortex-M7内核的, 其内部所有片上外设 (如GPIO, UART, SPI, I2C, 定时器, DMA控制器等) 都是通过AMBA总线 (具体为AHB和APB总线) 连接到CPU核心的。因此, 这段代码是在STM32H750上运行Linux并驱动其片上外设的基石 。
软件与硬件的桥梁 : amba_bustype
结构体是物理AMBA总线在内核中的软件抽象 。它本身不是硬件, 而是内核用来理解和管理硬件的一套规则和回调函数。
基于设备树的探测 : 当Linux内核在STM32H750上启动时, 它会解析设备树 (.dts
文件)。设备树中详细描述了AMBA总线上存在哪些外设、它们的寄存器地址、中断号等信息。
内核会为设备树中描述的每个AMBA外设创建一个struct device
实例。
然后, 内核的驱动核心会调用amba_bustype
中的.match
函数, 尝试为这个新发现的设备寻找一个名称或compatible
字符串相匹配的驱动程序。
一旦找到匹配的驱动, 核心就会调用amba_bustype
中的.probe
函数, 而这个函数接着会调用具体设备驱动自己的probe
函数, 从而完成设备和驱动的初始化。
电源和DMA管理 : .pm
和.dma_configure
等回调函数为总线上的所有设备提供了统一的电源管理和DMA配置接口。这使得具体的设备驱动可以以一种标准化的方式来处理休眠、唤醒和DMA传输, 无需关心底层的具体实现。
总之, 这段代码为STM32H750的所有片上外设建立了一个标准的、可扩展的驱动管理框架, 是整个平台驱动能够正常工作的前提。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 const struct bus_type amba_bustype = { .name = "amba" , .dev_groups = amba_dev_groups, .match = amba_match, .uevent = amba_uevent, .probe = amba_probe, .remove = amba_remove, .shutdown = amba_shutdown, .dma_configure = amba_dma_configure, .dma_cleanup = amba_dma_cleanup, .pm = &amba_pm, }; EXPORT_SYMBOL_GPL(amba_bustype); bool dev_is_amba (const struct device *dev) { return dev->bus == &amba_bustype; } EXPORT_SYMBOL_GPL(dev_is_amba); static int __init amba_init (void ) { return bus_register(&amba_bustype); } postcore_initcall(amba_init);