ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

coreboot 对 Qualcomm SC7180 的支持:SOC 代码架构、外设初始化与硬件笔记详解

coreboot 对 Qualcomm SC7180 的支持:SOC 代码架构、外设初始化与硬件笔记详解
  • 固件
  • 嵌入式
  • 操作系统
  • 驱动开发

【免费下载链接】coreboot

Read-only mirror of https://review.coreboot.org/coreboot.git. Synced every hour. We don't handle Pull Requests.

项目地址:https://gitcode.com/gh_mirrors/co/coreboot
点击查看免费下载

导读

本文围绕 coreboot 仓库中 Documentation/soc/qualcomm/sc7180/index.md 这一份 SOC 级文档展开,系统梳理 Qualcomm SC7180(Snapdragon 7c 系列)在 coreboot 中的落地方式。你将了解 SOC 代码如何组织 MMU、时钟、GPIO、QUPv3 固件桥、UART、SPI-NOR、AOP 固件与 USB 等八大模块,并结合src/soc/qualcomm/sc7180/与src/soc/qualcomm/common/下的源码,弄清 bootblock 到 ramstage 各阶段的初始化调用链,以及构建时所需的 Kconfig 选项与二进制固件(blob)清单。读完本文,你可以看懂 SC7180 参考主板(Google Trogdor 系列)的启动配置,也能为基于该 SoC 的 coreboot 移植提供直接依据。

SOC 代码的功能清单与目录结构

原文档明确指出,SOC folder(即 coreboot 源码树中的 SoC 支持代码)包含以下功能的实现:

  • MMU(内存管理单元)
  • CLOCK(时钟)
  • GPIO(通用输入输出)
  • QUPv3 FW(为串行接口提供桥接)
  • UART
  • SPI-NOR
  • AOP FW(Always-On Processor 固件)
  • USB

在仓库中,这些功能分散在两个目录:SoC 特有代码位于 src/soc/qualcomm/sc7180/,而多个 Qualcomm SoC 共享的通用代码位于 src/soc/qualcomm/common/。SC7180 目录下的文件与模块对应关系如下:

功能模块SC7180 特有源码共享通用源码
MMUmmu.c、decompressor.ccommon/mmu.c
CLOCKclock.c、include/soc/clock.hcommon/clock.c
GPIOinclude/soc/gpio.h、include/soc/addressmap.hcommon/gpio.c
QUPv3 FWqcom_qup_se.ccommon/qupv3_config.c、common/qup_se_handler.c
UARTinclude/soc/uart.hcommon/qupv3_uart.c
SPI-NORinclude/soc/addressmap.h(QSPI 引脚与基址)common/qspi.c、common/spi.c
AOP FWclock.c(clock_reset_aop)common/aop_load_reset.c
USB—common/usb/usb.c

MMU:为各启动阶段建立页表与内存属性

SC7180 的 MMU 初始化函数sc7180_mmu_init()定义在 src/soc/qualcomm/sc7180/mmu.c,它在 decompressor 阶段由decompressor_soc_init()(decompressor.c)调用,遵循mmu_init() → mmu_config_range() → mmu_enable()的标准流程:

void sc7180_mmu_init(void) { mmu_init(); mmu_config_range((void *)(4 * KiB), ((4UL * GiB) - (4 * KiB)), DEV_MEM); mmu_config_range((void *)_ssram, REGION_SIZE(ssram), CACHED_RAM); mmu_config_range((void *)_bsram, REGION_SIZE(bsram), CACHED_RAM); mmu_config_range((void *)_dma_coherent, REGION_SIZE(dma_coherent), UNCACHED_RAM); mmu_enable(); }

可见 SC7180 的内存映射策略:

  • 从 4 KiB 起到 4 GiB 的绝大部分地址空间映射为DEV_MEM(设备内存,不可缓存),这覆盖了所有外设寄存器区域;
  • SoC 内置的SSRAM(系统 SRAM,基址 0x14680000)与BSRAM(启动 SRAM,基址 0x14800000)映射为CACHED_RAM;
  • DMA 一致性缓冲区(dma_coherent,位于 BSRAM 内 0x1484E000)映射为UNCACHED_RAM,供 QSPI DMA 等场景使用。

这些内存区域的布局细节由 src/soc/qualcomm/sc7180/memlayout.ld 定义:BSRAM 中依次排布了 PBL 时间戳、看门狗墓碑、bootblock(52K)、TPM 日志、预 RAM CBFS 缓存、CBMEM 控制台、时间戳、页表(TTB)、栈、vboot 工作区与 DMA 一致性区域,还预留了ddr_training、qclib_serial_log、ddr_information、dcb、pmic、qclib等与内存初始化相关的区域;DRAM 段(0x80000000 起)则划分出dram_aop、dram_soc、BL31、modem 专用区(dram_modem_wifi_only/dram_modem_extra)、POSTRAM CBFS 缓存与 ramstage 区段。cbmem_top_chipset()返回4 * GiB(cbmem.c),将 CBMEM 放在 4 GiB 边界之下。

CLOCK:GPLL0 初始化、外设时钟配置与 CPU 频率提升

SC7180 的时钟子系统分为两块:SoC 特有部分(clock.c)与共享的通用时钟框架(common/clock.c)。

系统时钟初始化(clock_init)

clock_init()(clock.c)在 bootblock 阶段被调用,完成三件事:

  1. 配置并启用 GPLL0:clock_configure_gpll0()通过写 GCC 的 GPLL0 用户控制寄存器(user_ctl/user_ctl_u)设置偶数分频与多路输出(PLLOUT_EVEN / MAIN / ODD),使 GPLL0 输出 300 MHz(GPLL0_EVEN_HZ);
  2. 为 QUPV3 的两组 Wrap 使能时钟投票:分别对qup_wrap0与qup_wrap1的 core_2x、core、M_AHB、S_AHB 时钟在apcs_clk_br_en1投票寄存器上使能(偏移定义见 include/soc/clock.h);
  3. 提升启动 CPU 频率:speed_up_boot_cpu()(clock.c)通过pll_init_and_set()配置银核(Silver)PLL 到 1516.8 MHz、L3 PLL 到 1209.6 MHz,对应L_VAL_1516P8MHz = 0x4F与L_VAL_1209P6MHz = 0x3F,并打印调试日志。

外设时钟频率表

clock.c中还维护了两组静态频率配置表:

  • QSPI 核心时钟表qspi_core_cfg:提供 19.2 kHz(XO)、100 MHz、150 MHz、300 MHz 四档频率,分别由 XO 19.2 MHz 源或 GPLL0_EVEN 300 MHz 源经分频得到;
  • QUPv3 Wrap 时钟表qupv3_wrap_cfg:提供 19.2 kHz、32 MHz、48 MHz、64 MHz、96 MHz、100 MHz 等档位,其中 32/48/64/96 MHz 使用 M/N/D_2 分数分频(如 32 MHz 对应 m=8、n=75、d_2=75),由clock_configure_dfsr()写入 DFSR(动态频率切换)表供串行引擎选择。

此外,mdss_clock_configure()/mdss_clock_enable()(clock.c)负责显示子系统 MDSS 的 ESC0、PCLK0、BYTE0、BYTE0_INTF 时钟,注意寄存器中的分频值以 "2n-1" 形式存储,调用方需传入 2n 值。

GPIO:TLMM 分片与引脚复用定义

SC7180 的 GPIO 支持基于 TLMM(Top Level Mode Multiplexer),地址映射中定义了四个分片的基址(addressmap.h):

  • TLMM_NORTH_TILE_BASE= 0x03900000
  • TLMM_SOUTH_TILE_BASE= 0x03D00000
  • TLMM_WEST_TILE_BASE= 0x03500000

include/soc/gpio.h 用PIN(index, tlmm, func1, ..., func7)宏为每个引脚同时生成:

  • 寄存器地址GPIO<idx>_ADDR = TLMM_<tile>_TILE_BASE + (idx * TLMM_GPIO_OFF_DELTA)
  • 引脚功能选择枚举GPIO<idx>_FUNC_<func> = 1..7

例如PIN(0, SOUTH, QUP0_L0, ...)生成GPIO0_ADDR(位于南片 TLMM)与GPIO0_FUNC_QUP0_L0 = 1。SC7180 的 QSPI 引脚也在此定义(addressmap.h):QSPI_CLK = GPIO(63)、QSPI_DATA_0 = GPIO(64)、QSPI_DATA_1 = GPIO(65)、QSPI_CS = GPIO(68),对应的功能选择为GPIO63_FUNC_QSPI_CLK等。

在 qcom_qup_se.c 中,12 个 QUP 串行引擎(QUPV3_0_SE0 至 QUPV3_1_SE5)各自关联了寄存器基地址(QUP_SERIAL0_BASE0x00880000 起,见 addressmap.h)与最多 6 个引脚的功能映射,例如qup[8](QUPV3_1_SE2,即默认控制台 UART)使用 GPIO(42)~GPIO(45) 的QUP1_L0..L3功能。

QUPv3 FW:串行接口的固件桥与 GSI 初始化

原文档特别指出 QUPv3 FW 的作用是"为串行接口提供桥接"(provides a bridge to serial interfaces)。这是因为 SC7180 的 QUP 串行引擎(UART/SPI/I2C)硬件本身是一块可编程微序列器,需要加载专用固件后才能工作。

固件文件与 CBFS 打包

构建时(Makefile.mk)会从3rdparty/qc_blobs/sc7180/qup_fw/下取出三个固件并打入 CBFS:

CBFS 名称源文件类型
fallback/uart_fwqup_fw/uart_fw.binraw
fallback/spi_fwqup_fw/spi_fw.binraw
fallback/i2c_fwqup_fw/i2c_fw.binraw

固件加载与引擎初始化

qupv3_se_fw_load_and_init(bus, protocol, mode)(common/qupv3_config.c)按高通 HPG(Hardware Programming Guide)的步骤完成一次完整的串行引擎初始化:

  1. 从 CBFS 映射对应协议固件(cbfs_map("fallback/spi_fw", ...)等),并校验头部魔数SEFW(SEFW_MAGIC_HEADER);
  2. 解锁引擎、关闭 GENI 固件与相关时钟门控;
  3. 对非 UART 协议启用 DFS 模式并配置 DFSR 时钟表;
  4. 根据mode(GSI/FIFO/CPU_DMA/MIXED)设置geni_dma_mode_en、中断使能与事件使能寄存器;
  5. 把固件头部携带的配置索引/值对写入geni_cfg_reg0起始的配置寄存器,并把固件本体(fw_val_arr)拷贝进se_geni_cfg_ramn程序 RAM;
  6. 最后写版本寄存器、重新锁定引擎。

bootblock 阶段的qupv3_fw_init()(common/qupv3_config.c)则为全部 12 个 QUP 使能时钟,并对 QUP_WRAP0/1 的公共配置寄存器(AHB 主机时钟门控、快速切换禁用)进行初始化。

参考主板 Trogdor 在 ramstage 的load_qup_fw()(src/mainboard/google/trogdor/mainboard.c)展示了实际分配:SE1 用于 eSIM SPI、SE3 用于蓝牙 UART、SE4/SE5 用于笔触与 SAR 的 I2C、QUPV3_1_SE1 用于触控板 I2C、QUPV3_1_SE3/SE5 用于音频功放与 Codec I2C、QUPV3_1_SE4 用于指纹 SPI,并在禁用串口控制台时仍为 OS 加载控制台 UART 的固件。

UART:基于 QUPv3 的控制台串口实现

SC7180 的 UART 控制台走 QUPv3 通用引擎,实现在 common/qupv3_uart.c。uart_init(idx)(L47-L115)的关键逻辑包括:

  • 若 RX 次序列器已激活(GENI_STATUS_S_GENI_CMD_ACTIVE_MASK),说明前一阶段已初始化过,直接跳过避免重复配置;
  • 调用qupv3_se_fw_load_and_init(idx, SE_PROTOCOL_UART, FIFO)加载 UART 固件并校验协议版本;
  • 波特率按"16 个时钟脉冲采样 1 位"计算:uart_freq = baud_rate * 16,分频div = DIV_ROUND_CLOSEST(SRC_XO_HZ, uart_freq)(XO 为 19.2 MHz),写入geni_ser_m_clk_cfg/geni_ser_s_clk_cfg;
  • 对发送(TX)与接收(RX)引脚执行gpio_configure()复用配置并设置上拉;
  • 配置 TX/RX 水印、8 位字长、禁用奇偶校验、设置 FIFO 打包向量(每字 8 bit),最后写入geni_s_cmd0启动 RX。

默认控制台实例由UART_FOR_CONSOLE决定,默认值为 8(即QUPV3_1_SE2,见 Kconfig)。SC7180 特有的UART_TX_PIN = GPIO(44)定义在 include/soc/uart.h,与 qup[8] 的 TX 引脚(GPIO(44))一致。构建时CONFIG_DRIVERS_UART决定将common/qupv3_uart.c编入 verstage/romstage/ramstage(Makefile.mk),而 bootblock 阶段使用比特bang 方式(common/uart_bitbang.c)输出早期日志。

SPI-NOR:QSPI 控制器与 DMA 描述符链

SPI-NOR 由 SC7180 的 QSPI(Qualcomm SPI-NOR)控制器驱动,实现在 common/qspi.c。它支持 SDR/DDR 与 1/2/4-bit 多种模式(enum qspi_mode:SDR_1BIT到DDR_4BIT),核心机制是基于 DMA 的描述符链:

  • 三个预分配的cmd_desc描述符(存放在_dma_coherent一致性内存中)通过next_descriptor串联,描述符内的位域包含方向、多 I/O 模式、fragment、长度与反弹(bounce)缓冲区地址;
  • dma_transfer_chain()把链首地址写入next_dma_desc_addr后轮询mstr_int_sts等待DMA_CHAIN_DONE;
  • flush_chain()在传输完成后处理反弹缓冲与缓存一致性:对读方向要么dcache_invalidate_by_mva()使缓存失效,要么把 bounce 数据memcpy回目标缓冲区。

QSPI 控制器基址为QSPI_BASE = 0x088DC000,其核心时钟由clock_configure_qspi(hz)(clock.c)配置并使能qspi_cnoc_ahb_cbcr/qspi_core_cbcr时钟。bootblock 的bootblock_soc_init()(bootblock.c)即以 37.5 MHz 调用quadspi_init(37500 * KHz)初始化 QSPI,用于读取 CBFS 中的启动固件。配置项SC7180_QSPI(默认在启用COMMON_CBFS_SPI_WRAPPER时打开)控制是否用 SPI-NOR 作为启动介质,BOOT_DEVICE_SPI_FLASH_BUS默认值为 16(见 Kconfig)。

AOP FW:Always-On Processor 固件的加载与复位

AOP(Always-On Processor)是 SC7180 上负责低功耗管理的小核,其固件由 ramstage 的soc_init()(soc.c)通过aop_fw_load_reset()(common/aop_load_reset.c)加载:

  1. 先把 AOP 代码 RAM 区域(0x0B000000,256 KiB,见 memlayout.ld)临时映射为CACHED_RAM,保证 4 字节对齐的写入路径;
  2. 用selfload()从 CBFS 加载名为aop的 payload(源文件3rdparty/qc_blobs/sc7180/aop/aop.mbn,见 Makefile.mk);
  3. 对缓存区域执行dcache_clean_by_mva()刷回,再重新映射为DEV_MEM;
  4. 最后调用clock_reset_aop()(include/soc/clock.h)——通过 AOSS_CC 的aoss_cc_apcs_misc寄存器对 AOP 子系统执行复位,使其从新固件启动。

USB:DWC3 控制器与 PHY 初始化

USB 支持基于 Qualcomm 通用 USB 代码(common/usb/usb.c),驱动 DWC3 控制器并配合两套 PHY:

  • QUSB2 PHY:高速 USB2 PHY,基址QUSB_PRIM_PHY_BASE = 0x088e3000(含 DIG 部分 0x088e3200);
  • QMP PHY:USB3 组合 PHY,包含 QSERDES COM/TX/RX 与 PCS 寄存器块(0x088e9000 起)。

usb_port0结构把 DWC3 控制器(USB_HOST_DWC3_BASE = 0x0a60c100)与 GCC 中的usb30_prim_bcr、qusb2phy_prim_bcr、usb3_dp_phy_prim_bcr、usb3_phy_prim_bcr复位寄存器关联起来。romstage 中主板会先执行reset_usb0()(见 src/mainboard/google/trogdor/romstage.c),"趁早踢出复位以便至少 1 ms 稳定"。ramstage 的setup_usb_host0(&usb0_board_data)则通过usb_board_data结构(如pll_bias_control_2、imp_ctrl1、port_tune1)下发 PHY 调谐参数。此外,include/soc/efuse.h 定义了 QFPROM 中 USB 发射端微调(qusb_hstx_trim_lsb@ 0x4258)的读取入口。

硬件笔记:定时器来自 ARMv8 架构代码

原文档的"Notes about the hardware"明确指出:定时器使用 ARMv8 架构特有的代码(The timer is used from the ARMv8 architecture specific code)。这对应 common/timer.c 的实现:

void init_timer(void) { raw_write_cntfrq_el0(19200*KHz); }

即把 ARM 通用计时器频率寄存器CNTFRQ_EL0设置为 19.2 MHz(与 XO 时钟同频)。相应地,Kconfig 中SOC_QUALCOMM_SC7180显式选择了ARM64_USE_ARCH_TIMER(使用 ARM 架构计时器)、HAVE_MONOTONIC_TIMER与GENERIC_UDELAY,整个 SoC 采用 ARMv8 64 位架构(ARCH_BOOTBLOCK/VERSTAGE/ROMSTAGE/RAMSTAGE_ARMV8_64),并依赖ARM64_USE_ARM_TRUSTED_FIRMWARE集成 BL31(构建参数BL31_MAKEARGS += PLAT=sc7180,见 Makefile.mk)。

启动流程与各阶段的 SOC 代码分工

把上述模块串起来,SC7180 的启动流程与各阶段分工如下:

阶段主要 SOC 代码职责
decompressordecompressor.c解压 bootblock 前先执行sc7180_mmu_init()建立页表
bootblockbootblock.cclock_init()初始化时钟、quadspi_init(37500*KHz)使 QSPI 可访问 CBFS、qupv3_fw_init()使能 QUP 时钟
verstagecommon/qupv3_uart.cvboot 验证阶段的串口控制台输出
romstageqclib.c、carve_out.c、common/qclib.c加载并运行 QCLib 完成 DDR 初始化/训练;按调制解调器 ID 预留内存
ramstagesoc.c、common/aop_load_reset.c注册内存资源、加载 AOP 固件、USB 与显示初始化

其中值得注意的细节:

  • QCLib(DDR 初始化):qclib_soc_override()(qclib.c)在运行前从 CBFS 加载dcb(DDR 配置块)并加入 QCLib 接口表;qclib_override_soc_file()会根据 efuse 位(LONG_SYS_DCB_REG0x7801C0 的 bit 15)判断是否改用dcb_longsys1p8(Longsys 1.8V 内存)配置。Trogdor 主板还通过qclib_mainboard_override()(romstage.c)为 Lazor 板设置 DDR 频率上限(QCLIB_GA_DDR_FMAX_LIMIT_HYNIX8GB)以规避内存稳定性问题;
  • 内存预留:soc_read_resources()(soc.c)上报 DDR 主区域与dram_aop、dram_soc预留区,soc_modem_carve_out()(carve_out.c)按_modem_id中的调制解调器 ID(LTE/WiFi)动态切出 modem 专用内存;
  • bootblock 打包:构建时用util/qualcomm/createxbl.py将 bootblock 与qc_sec.mbn安全镜像合并,再用util/qualcomm/qgpt.py生成最终bootblock.bin(Makefile.mk)。

构建配置与固件依赖清单

启用 SC7180 SoC 的 Kconfig 入口为SOC_QUALCOMM_SC7180(默认关闭),关键配置项汇总如下(均见 Kconfig):

配置项类型/默认值说明
SOC_QUALCOMM_SC7180bool,默认 n总开关,自动选择 ARMv8-64 各阶段架构、ATF、GPIO 库、架构计时器等
QC_SDI_ENABLEbool,default y if QC_RAMDUMP启用 QcSdi 调试镜像(构建libqtisec_dbg.a)并打包fallback/qcsdi
MEMLAYOUT_LD_FILEstring,src/soc/qualcomm/sc7180/memlayout.ld内存布局链接脚本
SC7180_QSPIbool,default y if COMMON_CBFS_SPI_WRAPPER使用 SPI-NOR 作为启动介质
BOOT_DEVICE_SPI_FLASH_BUSint,默认 16启动设备 SPI 总线号
UART_FOR_CONSOLEint,默认 8用于控制台输出的 QUP 实例(QUPV3_1_SE2)
VBOOT—选择VBOOT_SEPARATE_VERSTAGE、VBOOT_RETURN_FROM_VERSTAGE、VBOOT_MUST_REQUEST_DISPLAY、VBOOT_STARTS_IN_BOOTBLOCK

在CONFIG_USE_QC_BLOBS=y时,构建还需从3rdparty/qc_blobs/sc7180/提供的二进制固件(完整清单见 Makefile.mk):boot/QcLib.elf(QCLib,DDR 初始化)、boot/Pmic.bin(PMIC 配置)、boot/dcb.bin与boot/dcb_slow.bin(DDR 配置块)、aop/aop.mbn(AOP 固件)、qup_fw/uart_fw.bin/spi_fw.bin/i2c_fw.bin(QUP 串行引擎固件)、qc_sec/qc_sec.mbn(安全启动镜像)、qtiseclib/libqtisec.a(ATF 安全库)。

在主板侧,参考实现是 Google Trogdor 系列(src/mainboard/google/trogdor/),覆盖 Bubs、Coachz、Gelarshie、Homestar、Kingoftown、Lazor、Marzipan、Mrbland、Pazquel、Pompom、Quackingstick、Trogdor、Wormdingler 等衍生板卡,通过select SOC_QUALCOMM_SC7180接入该 SoC,并在 bootblock 初始化 TPM 与 EC 的 QUP SPI 总线(bootblock.c)。coreboot 对 Qualcomm SC7180 的支持自 4.11 版本起加入(见 Documentation/releases/coreboot-4.11-relnotes.md)。更多 Qualcomm SoC 文档可参见 Documentation/soc/qualcomm/index.md。

  • 固件
  • 嵌入式
  • 操作系统
  • 驱动开发

【免费下载链接】coreboot

Read-only mirror of https://review.coreboot.org/coreboot.git. Synced every hour. We don't handle Pull Requests.

项目地址:https://gitcode.com/gh_mirrors/co/coreboot
点击查看免费下载

相关推荐

上一篇:终极机械键盘连击克星:3步彻底告别重复输入烦恼
下一篇:Windows热键冲突终极解决方案:Hotkey Detective完整使用指南

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进