uboot读取adc,通过cmdline传给kernel解析数值,不同硬件模块进行处理
- uboot开发,要想读取adc在哪个时间点读取以及如何传给cmdline,需要清楚2点,
- uboot启动得大致流程及相关启动功能,uboot各个硬件模块得接口,
- cmdline如何去设置
uboo开发具体如下:
1.uboot启动流程:
start.s
// 汇编环境
=> IRQ/FIQ/lowlevel/vbar/errata/cp15/gic // ARM架构相关的lowlevel初始化
=> _main
=> stack // 准备好C环境需要的栈
// 【第一阶段】C环境初始化,发起一系列的函数调用
=> board_init_f: init_sequence_f[]
initf_malloc
arch_cpu_init // 【SoC的lowlevel初始化】
serial_init // 串口初始化
dram_init // 【获取ddr容量信息】
reserve_mmu // 从ddr末尾开始往低地址reserve内存
reserve_video
reserve_uboot内存布局
U-Boot 由前级 Loader 加载到 CONFIG_SYS_TEXT_BASE 地址,初始化时会探明当前系统的总内存容
量,32位平台上认为最大4GB可用(但是不影响内核对容量的识别),64位平台上认为所有内存都可
用。然后通过一系列reserve_xxx() 接口从内存末尾往前预留需要的内存,最后把自己relocate到某段
reserve的空间上。内存整体使用布局如下,以ARM64为例(常规情况):
reserve_malloc
reserve_global_data
reserve_fdt
reserve_stacks
dram_init_banksize
sysmem_init
setup_reloc // 确定U-Boot自身要reloc的地址
// 汇编环境
=> relocate_code // 汇编实现U-Boot代码的relocation
// 【第二阶段】C环境初始化,发起一系列的函数调用
=> board_init_r: init_sequence_r[]
initr_caches // 使能MMU和I/Dcache
initr_malloc
bidram_initr
sysmem_initr
initr_of_live // 初始化of_live
initr_dm // 初始化dm框架
board_init // 【平台初始化,最核心部分】
board_debug_uart_init // 串口iomux、clk配置
init_kernel_dtb // 【切到kernel dtb】!
clks_probe // 初始化系统频率
regulators_enable_boot_on // 初始化系统电源
io_domain_init // io-domain初始化
set_armclk_rate // __weak,ARM提频(平台有需求才实现)
dvfs_init // 宽温芯片的调频调压
rk_board_init // __weak,由各个具体平台进行实现
console_init_r
board_late_init // 【平台late初始化】
rockchip_set_ethaddr // 设置mac地址
rockchip_set_serialno // 设置serialno
setup_boot_mode // 解析"reboot xxx"命令、
// 识别按键和loader烧写模式、
recovery
charge_display // U-Boot充电
rockchip_show_logo // 显示开机logo
soc_clk_dump // 打印clk tree
rk_board_late_init // __weak,由各个具体平台进行实现
run_main_loop // 【进入命令行模式,或执行启动命令】
2.通过流程知道,我们大概是要在哪里去加这个adc读取及设置cmdline。起码不能再adc驱动接口之前去处理,我们放在board_init里,追一下代码流程即可
这里我在这里添加如下:
u-boot/common/android_bootloader.c
android_get_dtbo函数(因为这个函数里有处理第一个cmdline字符串),具体debug追代码即可
3.知道在哪里添加接口及设置cmdline,我们需要知道如何获取到adc得值,uboot中有封装了寄存器接口,我们之间调接口即可
当然提前得分析驱动及找到相关接口:
首先得知道uboot2017已经实现了kernel同理设备配合驱动得架构
DM (Driver Model) 是 U-Boot 标准的 device-driver 开发模型,跟 kernel 的 device-driver 模型非常类似。v2017版本也遵循 DM 框架开发各功能模块。建议读者先阅读DM文档,了解DM架构原理和实现。
查看./doc/driver-model/README.txt说明
Terminology
-----------
Uclass - a group of devices which operate in the same way. A uclass provides
a way of accessing individual devices within the group, but always
using the same interface. For example a GPIO uclass provides
operations for get/set value. An I2C uclass may have 10 I2C ports,
4 with one driver, and 6 with another.
Driver - some code which talks to a peripheral and presents a higher-level
interface to it.
Device - an instance of a driver, tied to a particular port or peripheral.
简要概括:
uclass:设备驱动模型
driver: 驱动
device:设备
这个时候我们可以去查看dts,找到saradc的硬件定义,然后去找到相关驱动,这里不细说,驱动路径:
u-boot/drivers/adc
查看相关代码即可分析出,我们要获取adc值得接口。
具体使用,传参,你可以搜索是否有样例,也可以分析源码。
4.cmdline如何更新及生效。百度即可知道使用 env_update接口。使用得顺序就是你上传字符串先后得顺序
二、kernel解析cmdline
关于kernel解析,我踩了坑,这里详细说一下,
刚开始我想通过一些字符串得接口,自己去解析出一长串中,我需要得数据,发现会遇到很多问题。
比如:
storagemedia=emmc androidboot.storagemedia=emmc androidboot.mode=normal androidboot.dtb_idx=0 adc_val=20 androidboot.dtbo_idx=0 androidboot.verifiedbootstate=orange androidboot.serialno=11213335POT447000006 console=ttyFIQ0 firmware_class.path=/vendor/etc/firmware init=/init rootwait ro loop.max_part=7 androidboot.console=ttyFIQ0 androidboot.wificountrycode=CN androidboot.hardware=rk30board androidboot.boot_devices=fe2e0000.mmc androidboot.selinux=permissive buildvariant=userdebug earlycon=uart8250,mmio32,0xfeb50000 irqchip.gicv3_pseudo_nmi=0
我获取到这串字符按,然后解析出adc_val=20中得20,其中20是变有可能是100,有可能是1000,字符长度不定。所以很难做到。
通过查找资料,百度等,换了另一种办法通过__setup(xx,xx)去获取我上传得字符串得变量
开发如下:
1.首先kernel已经做了cmdline得解析,start_kernel中setup_arch中解析tags获取cmdline,拷贝到boot_command_line中。我们接着往下看start_kernel。
start_kernel中setup_arch中解析tags获取cmdline,拷贝到boot_command_line中。我们接着往下看start_kernel。调用setup_command_line,
将cmdline拷贝2份,放在saved_command_line static_command_line。
总结下kernel的参数解析:
(1)kernel编译链接,利用.__param .init.setup段将kernel所需参数(driver及通用)和对应处理函数的映射表(obs_kernel_param kernel_param结构体)存放起来。
(2)Kernel启动,do_early_param处理kernel早期使用的参数(如earlyprintk earlycon)
(3)parse_args对cmdline每个参数都遍历__param .init.setup进行匹配,匹配成功,则调用对应处理函数进行参数值的解析和设置。
总结来说,内核有解析cmdline得源码,它解析出来得数据会存在一个地方,而且cmdline整个字符串,也存在了一个全局指针里。
对于各个内核驱动模块来说,我们可以拿这个全局指针获取到cmdline,然后去解析出你自己需要得字符串数据,又或者是之间使用__setup,
这里我们使用(3)得方法,写一个函数,之间获取到相应字符串得数据,如下:
这样我们就可以再各个内核模块中获取到adc值,进而去判断硬件版本,然后去设置相应得功能
三、在设置不同功能的时候,又会遇到很多坑,涉及到dts得处理。所以最好是能存几份dts,然后根据adc去判断取那份dts,在接下来得文档里说明