PMON 对龙芯 2K300 处理器的硬件初始化流程。
PMON 对龙芯 2K300 硬件初始化分析
一、初始化流程总览
┌─────────────────────────────────────────────────────────────────────────────┐
│ start.S - 最早启动代码 │
│ (CPU 上电后第一条指令入口) │
└─────────────────────────────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────────────────────┐
│ 第一阶段:CPU 核心初始化 │
│ - 配置内存控制器窗口 │
│ - 解除 DDR/USB 软复位 │
│ - 设置缓存一致性 │
│ - 启用性能计数器 │
└─────────────────────────────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────────────────────┐
│ 第二阶段:PLL 时钟配置 │
│ (clksetting_2k300.S - SOFT_CLKSEL) │
│ - Node PLL (CPU/总线时钟) │
│ - DDR PLL (内存/外设时钟) │
│ - PIX PLL (显示时钟) │
└─────────────────────────────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────────────────────┐
│ 第三阶段:缓存与内存映射 │
│ - 锁定 Scache (256KB) 并拷贝代码 │
│ - 初始化串口 (initserial) │
│ - 探测内存大小 │
└─────────────────────────────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────────────────────┐
│ 第四阶段:C 语言初始化 │
│ (init_loongarch() → tgt_devconfig()) │
│ - 配置 GPIO 引脚复用 │
│ - 初始化 PCIe 设备 │
│ - 初始化显示控制器 (DC) │
│ - 初始化键盘、RTC 等外设 │
└─────────────────────────────────────────────────────────────────────────────┘
二、启动汇编代码分析 (start.S)
2.1 早期 CPU 配置
_start:
start:
/* 配置内存控制器窗口 */
li.w t0, 0x16000100
ld.w t1, t0, 0x0
li.w t2, (1 << 5 | 0xf << 20) /* bit5=1 默认配置, bit20-23=0xf */
or t1, t1, t2
st.w t1, t0, 0x0
/* 关闭 USB & DDR 软复位 */
li.w t1, 0xffffff7e
st.w t1, t0, 0x1c
关键寄存器地址 (0x16000100 附近):
0x16000100:芯片配置寄存器 0
0x1600011c:复位控制寄存器
2.2 缓存一致性配置
/* 配置缓存一致性掩码 */
li.w t0, 0x1600010c
ld.w t1, t0, 0x0
li.w t2, ((0x1f << 1) | (0x1f << 9)) /* 为 USB/GMAC0/GMAC1/DC 启用缓存一致性 */
or t1, t1, t2
st.w t1, t0, 0x0
缓存一致性掩码含义:
| 位域 | 设备 | 说明 |
|------|------|------|
| bit1-5 | USB | USB 控制器缓存一致性 |
| bit9-13 | GMAC0/GMAC1/DC | 网络和显示控制器 |
2.3 CPU 特性配置
/* 设置 CPUCFG 0 的 bit24 */
li.d t0, (1 << (24 + 32))
csrxchg t0, t0, 0xc0
/* 设置 CPUCFG 0x13 的 bit16-23 */
li.d t0, (0xa << (16 + 32))
li.d t1, (0xff << (16 + 32))
csrxchg t0, t1, 0xc9
/* 启用性能计数器作为 cp0 计数器 */
li.w t0, (0x1 << 16)
csrxchg t0, t0, 0x200
2.4 异常向量配置
/* 清除 Vint 配置 */
li.d t0, (0x7 << 16)
csrxchg zero, t0, 0x4
/* 设置 EBase 地址 (异常向量基址) */
li.d t0, PHYS_TO_CACHED(0x1c001000)
csrwr t0, 0xc
/* 设置 TLB 异常地址 */
li.d t0, 0x000000001c001000
csrwr t0, 0x88
/* 禁用中断 */
li.d t0, (1 << 2)
csrxchg zero, t0, 0x0
2.5 地址模式切换
/* 跳转到缓存地址空间 */
li.d t1, CACHED_MEMORY_ADDR
pcaddi t0, 3
or t0, t1, t0
jirl zero, t0, 0
/* 从直接地址模式切换到页模式 */
li.w t0, 0xb0
csrwr t0, 0x0
三、PLL 时钟配置 (clksetting_2k300.S)
PMON 支持软件配置 PLL,通过 SOFT_CLKSEL 宏控制:
#ifdef SOFT_CLKSEL
/* NODE PLL 配置 (CPU 核心时钟) */
li.d t0, PHYS_TO_UNCACHED(0x16000400) /* PLL 控制寄存器基址 */
li.w t1, (0x1 << 5) /* 先下电 PLL */
st.w t1, t0, 0
li.w t4, (NODE_DIV << 24) | (NODE_LOOPC << 15) | (NODE_REFC << 8)
st.w t4, t0, 0
/* 配置 GMAC 和 I2S 分频 */
ld.w t1, t0, 4
li.w t2, ~((0x7f << 8) | 0x7f)
and t1, t1, t2
li.w t2, (I2S_DIV << 8) | (GMAC_DIV)
or t2, t2, t1
st.w t2, t0, 0x4
/* 使能 PLL 并等待锁定 */
ori t4, t4, PLL_L1_ENA
st.w t4, t0, 0
wait_locked_sys:
ld.w a0, t0, 0
and a0, a0, PLL_L1_LOCKED
beqz a0, wait_locked_sys
PLL 计算公式:
// Node PLL (CPU 核心)
NODE_LOOPC = (CORE_FREQ * NODE_REFC * NODE_DIV) / 100
CORE_FREQ = 125 * 8 = 1000MHz (GMAC_FREQ=125, NODE_DIV=2, NODE_REFC=6)
// DDR PLL (内存)
DDR_LOOPC = (DDR_FREQ * DDR_REFC * DDR_DIV) / 100
// PIX PLL (显示)
PIX_LOOPC = 80 // 100MHz 输出
PIX_DIV = 20
PIX_REFC = 4
四、缓存初始化与代码重定位
4.1 Scache (L2 缓存) 锁定
/* 锁定 Scache (256KB) */
li.d a0, LOCK_CACHE_BASE
li.d a1, LOCK_CACHE_BASE + LOCK_CACHE_SIZE
/* 配置 Scache 锁定窗口 */
li.d t0, PHYS_TO_UNCACHED(0x16000200)
li.d t1, ~(LOCK_CACHE_SIZE - 1) /* 地址掩码 */
st.d t1, t0, 0x40 /* 设置窗口大小 */
li.d t1, (LOCK_CACHE_BASE & 0xffffffffffff) | (1 << 63) /* 地址 + 有效位 */
st.d t1, t0, 0x0 /* 设置窗口基址 */
4.2 代码拷贝到锁定缓存
/* 将 Flash 代码拷贝到 Scache */
li.d a1, PHYS_TO_CACHED(0x1c000000) /* Flash 起始地址 */
la a0, start /* 目标地址 (锁定缓存) */
la a2, edata /* 数据结束地址 */
copy_loop:
ld.d a3, a1, 0
st.d a3, a0, 0
addi.d a0, a0, 8
addi.d a1, a1, 8
bne a2, a0, copy_loop
五、串口初始化 (serial.S)
LEAF(initserial)
li.d a0, COM1_BASE_ADDR /* UART 基址 */
/* 设置 DLAB=1,访问波特率寄存器 */
li.w a1, 0x80
st.b a1, a0, 3
/* 设置波特率 (115200) */
li.w a1, UART_DIV_HI
st.b a1, a0, 1
li.w a1, UART_DIV_LO
st.b a1, a0, 0
/* 设置数据格式: 8 位数据 */
li.w a1, 3
st.b a1, a0, 3
/* 使能 FIFO */
li.w a1, 71
st.b a1, a0, 2
jirl zero, ra, 0
END(initserial)
波特率计算:
#define UART_REF_CLK 100000000
#define UART_DIV_HI (((UART_REF_CLK + (115200*8)) / (115200*16)) >> 8)
#define UART_DIV_LO (((UART_REF_CLK + (115200*8)) / (115200*16)) & 0xff)
六、GPIO 引脚复用配置 (pincfgs.c)
2K300 的引脚复用配置非常复杂,通过 set_pin_mode() 宏设置:
#define set_pin_mode(pin_num, mode) \
do { \
volatile unsigned int *addr = (unsigned int *) \
(LS2K300_GPIO_MULTI_CFG + (pin_num) / 16 * 4); \
int bit = (pin_num) % 16 * 2; \
*addr &= ~(0x3 << bit); \
*addr |= ((mode) << bit); \
} while(0)
引脚复用模式含义:
| mode | 说明 |
|------|------|
| 0 | GPIO 模式 |
| 1 | 第一复用功能 |
| 2 | 第二复用功能 |
| 3 | 主功能 |
主要引脚配置示例:
pin_cfgs_t default_pin_cfgs[106] = {
{0, 3}, // lcd_clk (主功能)
{1, 3}, // lcd_vsync (主功能)
{28, 3}, // gmac0_rx_ctl (GMAC0 接收控制)
{40, 3}, // uart0_rx (UART0 接收)
{41, 3}, // uart0_tx (UART0 发送)
{56, 3}, // spi0_clk (SPI0 时钟)
{90, 3}, // sdio0_clk (SDIO0 时钟)
// ...
};
七、C 语言初始化 (tgt_machdep.c)
7.1 主初始化函数 init_loongarch()
void init_loongarch()
{
unsigned long long tgt_memsz = readq(SMBIOS_INFO_IN_SDRAM_OFFS);
/* 解锁 Scache */
unlock_scache(LOCK_CACHE_BASE, LOCK_CACHE_SIZE);
/* 获取内存大小 */
get_memorysize(tgt_memsz);
/* 配置所有引脚复用 */
cfg_all_pin_multi(default_pin_cfgs);
/* 启用浮点单元 */
tgt_fpuenable();
/* 2K300 特定初始化 (前) */
ls2k300_init_before();
/* 探测 CPU 频率 */
tgt_cpufreq();
/* 拷贝异常向量 */
bcopy(LoongArchException, (char *)GEN_EXC_VEC,
LoongArchExceptionEnd - LoongArchException);
cpu_set_ebase();
cpu_set_tlb_ebase();
/* 调试器初始化 */
dbginit(NULL);
/* 2K300 特定初始化 (后) */
ls2k300_init_after();
/* 进入 main() */
main();
}
7.2 设备配置 tgt_devconfig()
void tgt_devconfig()
{
config_init();
configure();
#ifdef DC
/* 初始化显示控制器 */
fbaddress = dc_init();
fb_init(fbaddress, ioaddress);
vga_available = 1;
#endif
}
7.3 RTC 初始化
static void init_legacy_rtc(void)
{
/* 配置 RTC 控制寄存器 */
val = (1 << 13) | (1 << 11) | (1 << 8);
outl(LS2K300_RTC_CTRL_REG, val);
/* 读取并验证时间有效性 */
year = inl(LS2K300_TOY_READ1_REG);
month = (val >> 26) & 0x3f;
date = (val >> 21) & 0x1f;
// ...
if (时间无效) {
/* 重置为 2000-01-01 00:00:00 */
outl(LS2K300_TOY_WRITE1_REG, 0x64);
outl(LS2K300_TOY_WRITE0_REG, (1 << 26) | (1 << 21));
}
}
八、时钟频率探测 (_probe_frequencies())
static void _probe_frequencies()
{
/* 使用 HPET 高精度定时器测量频率 */
outl(LS2K300_HPET0_PERIOD, 0x989680); // 10ns 周期
outl(LS2K300_HPET0_CONF, 0x1); // 使能主时钟
sec = inl(LS2K300_HPET0_MAIN); // 获取当前时间
cnt = CPU_GetCOUNT(); // 获取 CPU 计数器
cur = (inl(LS2K300_HPET0_PERIOD) / 1000000);
sec = sec + (100000000 / cur); // 等待 100ms
do {
cur = inl(LS2K300_HPET0_MAIN);
} while (cur < sec);
cnt = CPU_GetCOUNT() - cnt;
md_pipefreq = cnt / 1000; // 计算频率
md_pipefreq *= 20000;
}
九、2K300 特定外设初始化 (ls2k300_init.c)
void ls2k300_init_before(void)
{
unsigned int val;
#ifdef LCD_EN
/* LCD 引脚复用配置 */
readl(PHYS_TO_UNCACHED(0x16000490)) |= 0xffffffff;
readl(PHYS_TO_UNCACHED(0x16000494)) |= 0xffffff;
/* PWM0 复用配置 (用于背光) */
readl(PHYS_TO_UNCACHED(0x160004a4)) &= ~(0x3 << 12);
readl(PHYS_TO_UNCACHED(0x160004a4)) |= (0x2 << 12);
/* PWM 配置 */
readl(PHYS_TO_UNCACHED(0x1611b00c)) &= ~0x1;
writel(150, PHYS_TO_UNCACHED(0x1611b004)); // 周期
writel(1500, PHYS_TO_UNCACHED(0x1611b008)); // 占空比
readl(PHYS_TO_UNCACHED(0x1611b00c)) |= 0x1;
#endif
/* SPI PAD 默认值 */
readl(PHYS_TO_UNCACHED(0x16000110)) |= (0x3 << 18);
}
十、关键硬件地址映射总结
| 外设 | 物理地址 | 功能 |
|------|----------|------|
| UART0 | 0x16100000 | 调试串口 |
| SPI0 | 0x16010000 | SPI Flash |
| SDIO0 | 0x16140000 | SD 卡接口 |
| DC | 0x16090000 | 显示控制器 |
| GMAC0 | - | 以太网控制器 |
| EHCI | 0x16080000 | USB 2.0 主机 |
| HPET | 0x16120000 | 高精度定时器 |
| RTC | 0x16128000 | 实时时钟 |
| GPIO | 0x16104000 | 通用输入输出 |
| PLL | 0x16000400 | 时钟 PLL 控制 |
| Scache | 0x16000200 | L2 缓存锁定控制 |
十一、总结
PMON 对 2K300 的硬件初始化具有以下特点:
- 分层初始化:从汇编到 C 语言,逐步建立运行环境
- PLL 软件配置:支持运行时动态配置时钟频率
- 缓存优化:将关键代码锁定到 Scache 中运行,提升启动速度
- 引脚复用管理:通过
pincfgs.h 集中管理 106 个引脚的功能配置
- 频率探测:使用 HPET 精确测量 CPU 和总线频率
- 内存探测:从 SMBIOS 信息区读取内存配置
- 显示支持:初始化内置显示控制器 (DC)
- 调试支持:早期串口输出和异常向量设置