行业资讯
📅 2026/8/5 6:09:39
全志A133平台Linux驱动配置实战:从设备树到内核编译完整指南
1. 项目缘起一次由“缺驱动”引发的板卡启动失败最近在调试一块基于全志A133平台的新板子遇到了一个典型的嵌入式开发“拦路虎”系统启动后某个关键外设比如一个I2C接口的TP芯片死活不工作。用ls /dev/一看对应的设备节点压根没生成用dmesg翻看内核启动日志也只能看到一句“probe failed”或者干脆没有相关驱动加载的痕迹。问题直指内核配置——驱动没配进去。这场景对于玩全志平台的朋友来说太熟悉了。无论是A50、A133还是T527、H616这些芯片功能强大、性价比高但官方SDK的驱动配置体系对于刚上手的新人甚至是有经验的开发者调整新外设时都像是一座需要耐心翻越的小山。网上的资料要么是零散的代码片段要么是过于笼统的“勾选某个选项”真正把“为什么这么配”以及“配错了会怎样”讲清楚的内容并不多。今天我就结合这次给A133新增TP驱动以Goodix GT9XX系列为例的实际经历把在全志Linux内核以Linux 4.9/5.4等长期支持版本为例中新增驱动的完整流程、背后的配置逻辑以及那些容易踩坑的细节系统地梳理一遍。目标很明确让你不仅能跟着步骤“配出来”更能理解每一步在做什么下次遇到其他外设也能举一反三。2. 全志平台驱动配置体系深度解析在动手修改任何一个文件之前我们必须先理解全志SDK中驱动配置的“游戏规则”。这不同于标准的、纯内核的make menuconfig它是一套结合了全志硬件特性和产品快速开发需求的“增强型”配置体系。2.1 核心配置文件sys_config.fex与board.dts这是全志平台驱动配置的“总开关”和“硬件描述书”理解它们的关系至关重要。sys_config.fex文件这是全志沿用自Allwinner方案的经典配置脚本。它是一个文本文件使用类似INI的格式定义了芯片各个引脚的功能复用Pin Mux、电源管理、时钟以及大量外设的板级参数。例如你要配置一个UART不仅需要打开内核驱动还需要在sys_config.fex里指定使用哪两个引脚作为TX和RX并配置好波特率。注意在新版本的SDK尤其是Linux 5.x内核以后中全志正在推动向标准设备树Device Tree的迁移。但大量现有项目和BSP包仍然严重依赖sys_config.fex而且很多底层硬件初始化特别是boot阶段仍由其控制。因此它目前是不可或缺的。board.dts文件这是Linux内核标准的设备树源文件。它以一种结构化的数据格式描述了硬件的拓扑结构包括CPU、内存、总线以及挂载在总线上的各种设备如I2C设备、SPI设备及其属性。内核在启动时会解析这个文件并根据其中的描述来动态加载和初始化驱动。两者的分工与协作sys_config.fex管“硬”的主要负责芯片上电早期、内核启动前的硬件环境初始化特别是引脚复用、电源和时钟的初始状态。这部分配置会被一个叫sunxi-fex的工具编译成二进制格式并打包进最终的固件镜像。board.dts管“软”的主要负责向Linux内核描述板上有什么设备、设备地址是什么、中断号是多少、需要哪些驱动参数等。内核驱动通过匹配设备树中的compatible属性来绑定设备。在实际操作中一个外设要正常工作往往需要两者配合。例如一个I2C触摸屏在sys_config.fex中你需要配置对应的I2C控制器引脚如twi2_sdatwi2_scl为I2C功能并可能使能相关的电源域。在board.dts中你需要在i2c2节点下添加一个子节点描述这个TP的I2C地址、中断引脚、compatible字符串用于匹配驱动以及厂商提供的具体参数如复位脚、最大坐标等。2.2 驱动代码的归宿内核配置 (KconfigMakefile)设备树告诉了内核“板上有个设备”但内核里必须有对应的驱动代码才能让它动起来。这部分就是标准的Linux内核模块管理。Kconfig文件定义了在make menuconfig时出现的配置选项。它决定了这个驱动是编译进内核(y)、编译为模块(m)、还是不编译(n)。全志通常会将自家芯片和外设的驱动配置放在类似drivers/input/touchscreen/Kconfig这样的路径下。Makefile文件指明了如何编译这个驱动即哪些源文件(.c)在哪种配置条件下会被编译。为全志平台新增驱动大部分情况下不是从零写驱动代码而是确保正确的驱动代码在正确的配置下被编译并确保设备树或sys_config.fex提供了正确的硬件信息。2.3 配置生效的完整流程理清了关键文件我们来看从修改配置到驱动生效的完整数据流这能帮你定位大部分“配置了但没生效”的问题修改sys_config.fex- 使用fex2bin或SDK中的打包脚本将其编译成sys_config.bin- 该文件被放入boot分区通常是boot.img的一部分在uboot和内核早期初始化阶段被读取。修改board.dts- 使用设备树编译器dtc将其编译成board.dtb设备树二进制文件- 该文件同样被放入boot分区由Linux内核在启动阶段解析。配置内核(make menuconfig) - 生成.config文件 - 根据此文件编译内核zImage和驱动模块。系统启动Uboot加载sys_config.bin完成最基础的引脚、时钟初始化。Uboot加载内核镜像zImage和board.dtb到内存并跳转到内核。内核启动解析board.dtb根据其中的节点遍历所有总线。当内核发现一个设备节点如i2c2下的touchscreen5d它会读取其compatible属性。内核在所有已编译的驱动中寻找of_device_id表里compatible字段与之匹配的驱动。如果找到匹配的驱动且驱动被编译进了内核y或可作为模块加载内核就会调用驱动的probe函数来初始化该设备。probe函数会从设备树节点中读取中断号、寄存器地址等参数最终创建设备文件如/dev/input/event0。一个常见的误区只在menuconfig里勾选了驱动但没在设备树或sys_config.fex里描述硬件驱动probe时找不到硬件信息就会失败。反之亦然。3. 实战为A133新增Goodix GT9XX触摸驱动下面我们进入实战环节。假设我们的A133板子I2C2接口上挂了一个Goodix GT911触摸芯片中断接在PG12复位脚接在PG10。3.1 第一步硬件连接与原理图确认这是所有软件配置的基石绝对不能错。确认I2C总线查原理图确认TP的SDA和SCL线连接到了SoC的哪一组I2C上。假设是TWI2对应Linux内核中的i2c2。确认中断引脚查原理图TP的INT脚接到了哪个GPIO。假设是PG12。记住在Linux设备树中中断需要配置为下降沿触发或电平触发具体看芯片数据手册。GT911通常配置为下降沿触发(IRQ_TYPE_EDGE_FALLING)。确认复位引脚查原理图TP的RESET脚接到了哪个GPIO。假设是PG10。复位时序拉低多久再拉高通常在驱动里或设备树中指定。确认I2C地址Goodix芯片的I2C地址可能是0x5d或0x14具体看原理图上ADDR引脚的上拉下拉情况。假设为0x5d。3.2 第二步配置sys_config.fex引脚复用与电源找到SDK中的sys_config.fex文件路径可能类似device/config/chips/a133/configs/{方案名}/sys_config.fex。配置I2C2引脚[twi2] twi2_used 1 twi2_scl port:PE122defaultdefaultdefault twi2_sda port:PE132defaultdefaultdefaulttwi2_used 1表示启用TWI2控制器。twi2_scl和twi2_sda定义了引脚复用。port:PE122...表示PE12引脚复用为功能2即I2C功能。这里的PE12和PE13需要根据你的实际原理图修改。2就是复用功能号全志每个引脚的功能号是固定的需要查《A133用户手册》的Pin Mux章节。配置中断和复位GPIO可选但推荐 虽然设备树中也会配置但在sys_config.fex中预先配置这些GPIO的初始状态如上拉、下拉、驱动能力可以避免启动阶段的毛刺。通常放在[gpio_para]或其他GPIO配置段但更常见的做法是只在设备树中配置因为设备树描述的是内核接管后的状态。对于复位脚为了确保开机稳定有时会在sys_config.fex的[power]段或特定脚本里先拉高。3.3 第三步配置设备树board.dts这是最关键的一步告诉Linux内核这个设备的存在。文件路径通常为kernel/linux-4.9/arch/arm64/boot/dts/sunxi/或类似位置下的{板型}.dts。找到正确的I2C控制器节点在设备树文件中找到i2c2节点。它可能已经被定义也可能在i2c0,i2c1旁边需要你添加。i2c2 { clock-frequency 400000; status okay; gt9115d { compatible goodix,gt911; reg 0x5d; interrupt-parent pio; interrupts 12 2; // PG12, 2 代表下降沿触发 reset-gpios pio 10 1 GPIO_ACTIVE_LOW; // PG10, 低电平有效 irq-gpios pio 12 0; // PG12 touchscreen-size-x 800; touchscreen-size-y 480; // 其他厂商特定参数如 // goodix,cfg-data [ ... ]; // 配置寄存器数组 }; };compatible goodix,gt911;这是驱动匹配的“钥匙”。内核中Goodix驱动的of_device_id表里必须有一项是goodix,gt911。reg 0x5d;I2C设备地址。interrupts 12 2;这是中断说明。12 2的含义是中断号是GPIO编号12即PG12触发类型是2IRQ_TYPE_EDGE_FALLING。这里的GPIO编号是全局编号计算方式是组号*32 组内序号。对于PG12P是G组组内序号12所以编号是6*32 12 204等等这里容易出错实际上全志的设备树中常用pio PIN IRQ_TYPE格式PIN就是PG12中的12。而interrupts单元格的具体含义依赖于interrupt-parent。上面例子是一种简化写法。更准确的做法是查阅SDK中类似板子的写法。一种常见的正确写法是interrupts PIN_GPIO(6, 12) IRQ_TYPE_EDGE_FALLING; // 假设PIN_GPIO是宏或者直接使用数字interrupts 204 IRQ_TYPE_EDGE_FALLING;(如果PG12的全局中断号是204)。这里是最易错点必须参考原SDK中其他中断的写法reset-gpios和irq-gpios使用GPIO描述符的方式指定复位和中断引脚更现代和推荐。pio 10 1表示使用pio控制器第10个引脚PG101可能代表GPIO_ACTIVE_LOW。同样需要参考现有代码。status okay;确保I2C控制器本身是启用的。3.4 第四步配置内核确保驱动被编译进入内核目录cd kernel/linux-4.9(具体路径根据你的SDK而定)。执行菜单配置make ARCHarm64 menuconfig(A133是64位所以是arm64。A50是arm)。找到触摸屏驱动配置使用/键搜索GOODIX或GT9XX。或者按路径导航Device Drivers-Input device support-Touchscreens-* Goodix I2C touchscreen。选择编译方式按y将驱动编译进内核这样驱动会直接包含在zImage里开机自动加载适合核心、必须的驱动。按m将驱动编译为模块会生成一个.ko文件需要手动insmod或配置系统自动加载。适合调试或不常用的驱动。建议首次调试选m模块方便反复加载、卸载、调试不成功也不影响内核启动。稳定后改为y。保存退出选择Save保存到默认的.config文件。重新编译内核和模块make ARCHarm64 -j8 make ARCHarm64 modules -j8如果只编译了模块也需要执行make modules。3.5 第五步集成与烧录更新设备树编译内核后设备树源文件(.dts)会被编译成二进制文件(.dtb)。你需要将这个新的.dtb文件替换掉SDK打包目录如out/{方案名}/中对应的文件。更新sys_config.bin使用SDK提供的脚本如pack脚本或fex2bin工具将修改后的sys_config.fex转换成sys_config.bin并更新到打包目录。打包固件在SDK根目录执行./build.sh pack具体命令看SDK文档它会将所有组件uboot, kernel, dtb, sys_config.bin, rootfs打包成一个可烧录的镜像如sunxi.img。烧录与测试使用PhoenixSuit、AllwinnerTech PhoenixUSBPro或其他烧录工具将镜像烧录到板子。上电验证查看设备节点ls /dev/input/看是否有eventX出现。查看内核日志dmesg | grep -i goodix或dmesg | grep -i touch查看驱动probe是否成功有无错误信息。测试输入使用cat /dev/input/eventX(用实际的event号) 然后触摸屏幕看是否有乱码输出。或者使用evtest工具进行更专业的测试。4. 排错指南当驱动没有按预期工作时按照上述步骤操作大部分情况下驱动都能工作。但如果没工作别慌按照以下链路系统性排查这才是真正体现经验的地方。4.1 排查链第一步内核启动日志 (dmesg)这是最重要的信息源。上电后立刻在串口终端执行dmesg | grep -E \(goodix|i2c|input|touch)\。场景A完全没有相关日志可能原因1设备树节点未生效。检查board.dts中i2c2节点的status是否为okay检查TP子节点的compatible字符串是否拼写错误检查设备树是否被正确编译和打包。可能原因2I2C控制器引脚复用错误。检查sys_config.fex中twi2的配置特别是引脚号和功能号。用cat /sys/kernel/debug/pinctrl/pio/路径可能不同查看引脚复用状态。可能原因3驱动根本未编译。检查.config文件确认CONFIG_TOUCHSCREEN_GOODIX是y或m。如果是m检查/lib/modules/$(uname -r)/下是否有goodix.ko文件。场景B有I2C通信错误日志类似i2c i2c-2: sendbytes: error -110或gt911 2-005d: Failed to read config。可能原因1I2C地址错误。用i2cdetect -y 2假设是i2c-2总线扫描看0x5d地址是否出现(UU表示被驱动占用5d表示设备存在但无驱动)。可能原因2电源或复位时序问题。测量TP芯片的供电电压是否正常。检查复位脚波形驱动可能在probe时进行了复位操作但复位时间不足或过长。可以在设备树中调整复位延时参数或检查硬件复位电路。可能原因3中断问题。如果驱动需要中断但中断配置错误可能导致通信超时。检查设备树中的中断配置用cat /proc/interrupts查看gt911中断是否被注册以及触发次数。场景C驱动probe成功但无输入事件日志显示input: goodix_ts as /devices/platform/soc/.../input/inputX但/dev/input/下没有事件或事件无效。可能原因1坐标轴反转或缩放。检查设备树中的touchscreen-size-x/y是否正确或者尝试在驱动中或用户空间通过evdev交换X/Y坐标。可能原因2触摸屏上报的数据格式不对。有些TP需要加载特定的固件或配置表(cfg-data)。检查驱动文档看是否需要通过设备树属性goodix,cfg-data传入配置数组。4.2 排查链第二步深入系统状态检查检查设备树是否被加载cat /proc/device-tree/可以浏览内核解析后的设备树。找到i2c2节点看其下是否有gt911子节点属性是否正确。检查I2C总线i2cdetect -l列出所有I2C总线。i2cdetect -y 2扫描总线2上的设备。检查GPIO状态cat /sys/kernel/debug/gpio查看GPIO使用情况确认你的中断和复位GPIO没有被其他驱动占用。检查模块是否加载lsmod | grep goodix。如果是模块确保已加载insmod goodix.ko。4.3 常见坑点与经验之谈GPIO编号的“坑”全志平台GPIO编号方式多样有直接数字如204有Px宏如PIN_PG12有bank, nr对。务必、务必、务必参考你所用SDK中其他设备如LED、按键的写法保持一致。这是新手最容易栽跟头的地方。时钟与电源域有些外设如某些TP可能挂在一个需要单独使能的电源域下或者其父I2C控制器的时钟需要额外配置。如果排查了所有软配置都不行去查芯片手册看该外设是否有特殊的电源/时钟要求并在sys_config.fex的[power]或[clock]段进行配置。驱动版本匹配从GitHub或其他地方找的驱动可能与你内核的API不兼容。优先使用SDK自带的驱动或确保你移植的驱动适配你的内核版本特别是设备树、GPIO、中断相关的API。sys_config.fex与设备树的冲突如果同一个引脚在两个文件里配置了不同的功能谁最后生效取决于初始化顺序可能导致不可预知的行为。尽量保持配置一致或者明确知道初始化流程。调试符号与打印如果问题棘手可以尝试打开驱动的调试信息。在内核配置中打开CONFIG_DYNAMIC_DEBUG然后在驱动加载后执行echo -n module goodix p /sys/kernel/debug/dynamic_debug/control这样驱动内部的dev_dbg打印就会输出到dmesg获得更详细的运行信息。5. 举一反三其他类型驱动的配置思路掌握了I2C触摸屏的配置其他类型的驱动大同小异核心都是“内核配置 硬件描述”两板斧。UART串口sys_config.fex: 配置uartX段指定TX、RX引脚及功能号。board.dts: 通常串口控制器节点已使能主要检查status okay和波特率等参数。如果使用串口作为控制台还需修改uboot和内核的启动参数(bootargs)。内核配置Device Drivers-Character devices-Serial drivers-Allwinner SoC serial support。SPI设备如屏幕、Flashsys_config.fex: 配置spiX段指定CLK、MOSI、MISO、CS引脚。board.dts: 在spiX节点下添加子节点定义compatible、reg片选号、spi-max-frequency等。内核配置启用对应的SPI控制器驱动和具体设备驱动如CONFIG_SPI_SPIDEV用于测试或具体的显示屏驱动。GPIO-Keys按键主要工作在设备树。在/根节点或gpio-keys节点下定义子节点指定gpios、linux,code对应键盘键值等。无需在sys_config.fex特殊配置除非需要上拉/下拉。内核配置Device Drivers-Input device support-Keyboards-GPIO Buttons。背光/PWMsys_config.fex: 可能配置PWM引脚复用。board.dts: 定义backlight节点关联到PWM控制器。内核配置启用CONFIG_PWM_SUNXI和CONFIG_BACKLIGHT_PWM。核心思想先确定外设的通信总线类型I2C/SPI/UART等或控制类型GPIO/PWM等然后去配置对应的控制器最后在控制器下添加设备节点。内核配置则确保这条路径上的所有驱动总线控制器驱动、设备驱动都被编译。6. 进阶驱动配置的优化与固化当驱动调试稳定后我们还需要考虑如何让配置更优化、更易于维护。1. 将驱动编译进内核 vs. 编译为模块进内核(y)启动速度快驱动总是可用。适合系统必需的基础驱动如eMMC、USB Host控制器。模块(m)减小内核镜像大小增加灵活性可以动态加载/卸载。适合调试阶段、或非必需/可插拔的设备驱动如某些传感器、特定型号的Wi-Fi模块。建议基础平台驱动用y外设驱动根据情况用m。在产品发布时为了启动速度和可靠性通常会把所有必需驱动编译进内核。2. 设备树的重用与覆盖如果你的项目有多个衍生板卡它们大部分配置相同只有少数外设不同比如一个板子有TP另一个没有可以使用设备树覆盖(dt-overlay)或条件编译。全志SDK常用方法在board.dts中使用#include包含一个公共的.dtsi文件然后在板级.dts文件中只进行差异化的修改如启用或禁用某个节点。在打包脚本中选择对应的.dts文件进行编译。3. 配置的版本管理sys_config.fex和board.dts是纯文本文件一定要纳入Git等版本控制系统。每次修改前做好备份修改时写清注释说明修改原因和日期。这对于团队协作和问题回溯至关重要。4. 自动化编译脚本不要手动执行每一步编译命令。利用SDK提供的build.sh脚本或者自己编写一个Makefile将内核配置、编译、设备树编译、打包等步骤串联起来实现一键编译减少人为失误。调试驱动配置的过程就像在解一个多维度的谜题硬件连接、引脚复用、设备树、内核编译环环相扣。最宝贵的经验往往来自于那些最令人头疼的失败案例。每次成功点亮一个新设备你对整个嵌入式Linux系统的理解就会加深一层。希望这篇基于A133实战的总结能帮你少走些弯路更顺畅地驾驭全志平台。