当前位置: 首页 > news >正文

全志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.fexboard.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设备)及其属性。内核在启动时会解析这个文件,并根据其中的描述来动态加载和初始化驱动。

两者的分工与协作

  1. sys_config.fex管“硬”的:主要负责芯片上电早期、内核启动前的硬件环境初始化,特别是引脚复用、电源和时钟的初始状态。这部分配置会被一个叫sunxi-fex的工具编译成二进制格式,并打包进最终的固件镜像。
  2. board.dts管“软”的:主要负责向Linux内核描述板上有什么设备、设备地址是什么、中断号是多少、需要哪些驱动参数等。内核驱动通过匹配设备树中的compatible属性来绑定设备。

在实际操作中,一个外设要正常工作,往往需要两者配合。例如一个I2C触摸屏:

  • sys_config.fex中,你需要配置对应的I2C控制器引脚(如twi2_sdatwi2_scl)为I2C功能,并可能使能相关的电源域。
  • board.dts中,你需要在i2c2节点下添加一个子节点,描述这个TP的I2C地址、中断引脚、compatible字符串(用于匹配驱动)以及厂商提供的具体参数(如复位脚、最大坐标等)。

2.2 驱动代码的归宿:内核配置 (Kconfig&Makefile)

设备树告诉了内核“板上有个设备”,但内核里必须有对应的驱动代码才能让它动起来。这部分就是标准的Linux内核模块管理。

  • Kconfig文件:定义了在make menuconfig时出现的配置选项。它决定了这个驱动是编译进内核(y)、编译为模块(m)、还是不编译(n)。全志通常会将自家芯片和外设的驱动配置放在类似drivers/input/touchscreen/Kconfig这样的路径下。
  • Makefile文件:指明了如何编译这个驱动,即哪些源文件(.c)在哪种配置条件下会被编译。

为全志平台新增驱动,大部分情况下不是从零写驱动代码,而是确保正确的驱动代码在正确的配置下被编译,并确保设备树或sys_config.fex提供了正确的硬件信息

2.3 配置生效的完整流程

理清了关键文件,我们来看从修改配置到驱动生效的完整数据流,这能帮你定位大部分“配置了但没生效”的问题:

  1. 修改sys_config.fex-> 使用fex2bin(或SDK中的打包脚本)将其编译成sys_config.bin-> 该文件被放入boot分区(通常是boot.img的一部分),在uboot和内核早期初始化阶段被读取。
  2. 修改board.dts-> 使用设备树编译器dtc将其编译成board.dtb(设备树二进制文件)-> 该文件同样被放入boot分区,由Linux内核在启动阶段解析。
  3. 配置内核(make menuconfig) -> 生成.config文件 -> 根据此文件编译内核zImage和驱动模块。
  4. 系统启动
    • Uboot加载sys_config.bin,完成最基础的引脚、时钟初始化。
    • Uboot加载内核镜像zImageboard.dtb到内存,并跳转到内核。
    • 内核启动,解析board.dtb,根据其中的节点遍历所有总线。
    • 当内核发现一个设备节点(如i2c2下的touchscreen@5d),它会读取其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 第一步:硬件连接与原理图确认

这是所有软件配置的基石,绝对不能错。

  1. 确认I2C总线:查原理图,确认TP的SDASCL线连接到了SoC的哪一组I2C上。假设是TWI2(对应Linux内核中的i2c2)。
  2. 确认中断引脚:查原理图,TP的INT脚接到了哪个GPIO。假设是PG12。记住,在Linux设备树中,中断需要配置为下降沿触发或电平触发,具体看芯片数据手册。GT911通常配置为下降沿触发(IRQ_TYPE_EDGE_FALLING)。
  3. 确认复位引脚:查原理图,TP的RESET脚接到了哪个GPIO。假设是PG10。复位时序(拉低多久再拉高)通常在驱动里或设备树中指定。
  4. 确认I2C地址:Goodix芯片的I2C地址可能是0x5d0x14,具体看原理图上ADDR引脚的上拉下拉情况。假设为0x5d

3.2 第二步:配置sys_config.fex(引脚复用与电源)

找到SDK中的sys_config.fex文件(路径可能类似device/config/chips/a133/configs/{方案名}/sys_config.fex)。

  1. 配置I2C2引脚

    [twi2] twi2_used = 1 twi2_scl = port:PE12<2><default><default><default> twi2_sda = port:PE13<2><default><default><default>
    • twi2_used = 1表示启用TWI2控制器。
    • twi2_scltwi2_sda定义了引脚复用。port:PE12<2>...表示PE12引脚复用为功能2(即I2C功能)。这里的PE12PE13需要根据你的实际原理图修改。<2>就是复用功能号,全志每个引脚的功能号是固定的,需要查《A133用户手册》的Pin Mux章节。
  2. 配置中断和复位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

  1. 找到正确的I2C控制器节点:在设备树文件中找到i2c2节点。它可能已经被定义,也可能在i2c0,i2c1旁边,需要你添加。
    &i2c2 { clock-frequency = <400000>; status = "okay"; gt911@5d { 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),触发类型是2(IRQ_TYPE_EDGE_FALLING)。这里的GPIO编号是全局编号,计算方式是组号*32 + 组内序号。对于PG12,P是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-gpiosirq-gpios:使用GPIO描述符的方式指定复位和中断引脚,更现代和推荐。<&pio 10 1>表示使用pio控制器,第10个引脚(PG10),1可能代表GPIO_ACTIVE_LOW。同样需要参考现有代码。
    • status = "okay";:确保I2C控制器本身是启用的。

3.4 第四步:配置内核,确保驱动被编译

  1. 进入内核目录cd kernel/linux-4.9(具体路径根据你的SDK而定)。
  2. 执行菜单配置make ARCH=arm64 menuconfig(A133是64位,所以是arm64。A50是arm)。
  3. 找到触摸屏驱动配置
    • 使用/键搜索GOODIXGT9XX
    • 或者按路径导航:Device Drivers->Input device support->Touchscreens-><*> Goodix I2C touchscreen
  4. 选择编译方式
    • y将驱动编译进内核:这样驱动会直接包含在zImage里,开机自动加载,适合核心、必须的驱动。
    • m将驱动编译为模块:会生成一个.ko文件,需要手动insmod或配置系统自动加载。适合调试或不常用的驱动。建议:首次调试选m(模块),方便反复加载、卸载、调试,不成功也不影响内核启动。稳定后改为y
  5. 保存退出:选择Save,保存到默认的.config文件。
  6. 重新编译内核和模块
    make ARCH=arm64 -j8 make ARCH=arm64 modules -j8
    如果只编译了模块,也需要执行make modules

3.5 第五步:集成与烧录

  1. 更新设备树:编译内核后,设备树源文件(.dts)会被编译成二进制文件(.dtb)。你需要将这个新的.dtb文件替换掉SDK打包目录(如out/{方案名}/)中对应的文件。
  2. 更新sys_config.bin:使用SDK提供的脚本(如pack脚本)或fex2bin工具,将修改后的sys_config.fex转换成sys_config.bin,并更新到打包目录。
  3. 打包固件:在SDK根目录执行./build.sh pack(具体命令看SDK文档),它会将所有组件(uboot, kernel, dtb, sys_config.bin, rootfs)打包成一个可烧录的镜像(如sunxi.img)。
  4. 烧录与测试:使用PhoenixSuit、AllwinnerTech PhoenixUSBPro或其他烧录工具将镜像烧录到板子。
  5. 上电验证
    • 查看设备节点ls /dev/input/看是否有eventX出现。
    • 查看内核日志dmesg | grep -i goodixdmesg | grep -i touch,查看驱动probe是否成功,有无错误信息。
    • 测试输入:使用cat /dev/input/eventX(用实际的event号) 然后触摸屏幕,看是否有乱码输出。或者使用evtest工具进行更专业的测试。

4. 排错指南:当驱动没有按预期工作时

按照上述步骤操作,大部分情况下驱动都能工作。但如果没工作,别慌,按照以下链路系统性排查,这才是真正体现经验的地方。

4.1 排查链第一步:内核启动日志 (dmesg)

这是最重要的信息源。上电后,立刻在串口终端执行dmesg | grep -E \"(goodix|i2c|input|touch)\"

  • 场景A:完全没有相关日志

    • 可能原因1:设备树节点未生效。检查board.dtsi2c2节点的status是否为"okay";检查TP子节点的compatible字符串是否拼写错误;检查设备树是否被正确编译和打包。
    • 可能原因2:I2C控制器引脚复用错误。检查sys_config.fextwi2的配置,特别是引脚号和功能号。用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 -110gt911 2-005d: Failed to read config
    • 可能原因1:I2C地址错误。用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 常见坑点与经验之谈

  1. GPIO编号的“坑”:全志平台GPIO编号方式多样,有直接数字(如204),有Px宏(如PIN_PG12),有<bank, nr>对。务必、务必、务必参考你所用SDK中其他设备(如LED、按键)的写法,保持一致。这是新手最容易栽跟头的地方。
  2. 时钟与电源域:有些外设(如某些TP)可能挂在一个需要单独使能的电源域下,或者其父I2C控制器的时钟需要额外配置。如果排查了所有软配置都不行,去查芯片手册,看该外设是否有特殊的电源/时钟要求,并在sys_config.fex[power][clock]段进行配置。
  3. 驱动版本匹配:从GitHub或其他地方找的驱动,可能与你内核的API不兼容。优先使用SDK自带的驱动,或确保你移植的驱动适配你的内核版本(特别是设备树、GPIO、中断相关的API)。
  4. sys_config.fex与设备树的冲突:如果同一个引脚在两个文件里配置了不同的功能,谁最后生效取决于初始化顺序,可能导致不可预知的行为。尽量保持配置一致,或者明确知道初始化流程。
  5. 调试符号与打印:如果问题棘手,可以尝试打开驱动的调试信息。在内核配置中打开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设备(如屏幕、Flash):

    • sys_config.fex: 配置spiX段,指定CLK、MOSI、MISO、CS引脚。
    • board.dts: 在spiX节点下添加子节点,定义compatiblereg(片选号)、spi-max-frequency等。
    • 内核配置:启用对应的SPI控制器驱动和具体设备驱动(如CONFIG_SPI_SPIDEV用于测试,或具体的显示屏驱动)。
  • GPIO-Keys(按键)

    • 主要工作在设备树。在/根节点或gpio-keys节点下定义子节点,指定gpioslinux,code(对应键盘键值)等。
    • 无需在sys_config.fex特殊配置(除非需要上拉/下拉)。
    • 内核配置:Device Drivers->Input device support->Keyboards->GPIO Buttons
  • 背光/PWM

    • sys_config.fex: 可能配置PWM引脚复用。
    • board.dts: 定义backlight节点,关联到PWM控制器。
    • 内核配置:启用CONFIG_PWM_SUNXICONFIG_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.fexboard.dts是纯文本文件,一定要纳入Git等版本控制系统。每次修改前做好备份,修改时写清注释,说明修改原因和日期。这对于团队协作和问题回溯至关重要。

4. 自动化编译脚本不要手动执行每一步编译命令。利用SDK提供的build.sh脚本,或者自己编写一个Makefile,将内核配置、编译、设备树编译、打包等步骤串联起来,实现一键编译,减少人为失误。

调试驱动配置的过程,就像在解一个多维度的谜题,硬件连接、引脚复用、设备树、内核编译环环相扣。最宝贵的经验往往来自于那些最令人头疼的失败案例。每次成功点亮一个新设备,你对整个嵌入式Linux系统的理解就会加深一层。希望这篇基于A133实战的总结,能帮你少走些弯路,更顺畅地驾驭全志平台。

http://www.cnnetsun.cn/news/3842736.html

相关文章:

  • 二维码容错原理全解析:从里德-所罗门码到工程实践
  • 预测性维护实战指南:从传感器到AI算法的工业设备健康管理
  • AI Agent工程师转型指南:从大模型原理到实战部署全解析
  • 假设检验实战指南:从Z检验到t检验,掌握数据决策核心方法
  • Windows Server与普通Windows核心差异解析:从设计基因到企业级应用选型
  • Excel VLOOKUP函数从入门到精通:数据匹配核心技巧与实战应用
  • Erlang OTP Application 核心原理与实践:从概念到完整项目构建
  • PyTorch MNIST手写数字识别:从环境搭建到模型训练的完整实践指南
  • Altium Designer PCB设计效率革命:核心快捷键体系深度解析与实战应用
  • 解决Visual Studio重装时无法更改安装路径的三种方法
  • 中高考数学提分新路径(AI辅助解题实战白皮书):覆盖函数/几何/概率3大模块,准确率92.7%的验证数据首次公开
  • Windows Style Builder路径全解析:从系统主题到项目管理的完整指南
  • OpenCV自动色彩校正实战:灰度世界与完美反射算法详解
  • 深入解析进程挂起状态:从Linux D状态到实战诊断与预防
  • PyQt5 UI自适应与高DPI缩放:从原理到实战的完整指南
  • SQL日期查询实战:精准处理昨天今天明天,优化慢SQL与索引策略
  • VMware vSphere虚拟网络架构深度解析:从核心组件到流量路径与排错实战
  • Unity SphereCast实战指南:从原理到高级应用
  • GEO优化团队建设贵吗?解析人才与算法带来的隐性成本
  • SPSS一致性分析全攻略:从Kappa、ICC到克朗巴哈α的实战指南
  • Vibe Coding与Codex:AI编程助手实战指南与核心心法
  • Token全解析:从JWT认证到AI计费,一文搞懂数字凭证与流量货币
  • STM32 HAL库定时器PWM配置详解:从原理到实战应用
  • 02_ndarray的创建方式之 array()与asarray()
  • 从零部署VMware ESXi 6.5:硬件准备、安装配置与虚拟机管理全指南
  • Windows端口占用排查:netstat与findstr命令组合实战指南
  • Token技术全解析:从JWT到OAuth,构建现代应用安全认证体系
  • 抖音下载器终极指南:从零开始批量下载无水印视频的完整教程
  • 各种头文件解析:原理、类型与实战指南
  • 模拟退火算法:从物理退火到组合优化问题的全局搜索策略