概述
/* do some early init */
void s_init(void)
{
#if !defined CONFIG_SPL_BUILD && defined CONFIG_SUN7I
/* Enable SMP mode for CPU0, by setting bit 6 of Auxiliary Ctl reg */
asm volatile(
"mrc p15, 0, r0, c1, c0, 1n"
"orr r0, r0, #1 << 6n"
"mcr p15, 0, r0, c1, c0, 1n");
#endif
watchdog_init();
clock_init();
timer_init();
gpio_init();
#ifdef CONFIG_SPL_BUILD
gd = &gdata;
preloader_console_init();
#ifdef CONFIG_SPL_I2C_SUPPORT
/* Needed early by sunxi_board_init if PMU is enabled */
i2c_init(CONFIG_SYS_I2C_SPEED, CONFIG_SYS_I2C_SLAVE);
#endif
sunxi_board_init();
#endif
}
1.
#if !defined CONFIG_SPL_BUILD && defined CONFIG_SUN7I
/* Enable SMP mode for CPU0, by setting bit 6 of Auxiliary Ctl reg */asm volatile(
"mrc p15, 0, r0, c1, c0, 1n"
"orr r0, r0, #1 << 6n"
"mcr p15, 0, r0, c1, c0, 1n");
#endif
这段并不会编译,因为!defined CONFIG_SPL_BUILD为假
2.
watchdog_init();
在watchdog.c中
#define WDT_CTRL_RESTART (0x1 << 0)
#define WDT_CTRL_KEY
(0x0a57 << 1)
void watchdog_reset(void)
{
static const struct sunxi_wdog *wdog =
&((struct sunxi_timer_reg *)SUNXI_TIMER_BASE)->wdog;
writel(WDT_CTRL_KEY | WDT_CTRL_RESTART, &wdog->ctl);
}
void watchdog_set(int timeout)
{
static struct sunxi_wdog *const wdog =
&((struct sunxi_timer_reg *)SUNXI_TIMER_BASE)->wdog;
/* Set timeout, reset & enable */
if (timeout >= 0) {
writel(WDT_MODE_TIMEOUT(timeout) |
WDT_MODE_RESET_EN | WDT_MODE_EN,
&wdog->mode);
} else {
writel(0, &wdog->mode);
}
watchdog_reset();
}
void watchdog_init(void)
{
#ifdef CONFIG_WATCHDOG
watchdog_set(WDT_MAX_TIMEOUT);
#else
watchdog_set(WDT_OFF); /* no timeout */
#endif
}
也就是把WTG关掉,具体的操作是writel(0, &wdog->mode);writel(WDT_CTRL_KEY | WDT_CTRL_RESTART, &wdog->ctl);
分析到这里#define WDT_CTRL_KEY (0x0a57 << 1),什么意思?魔数吗??查看手册
31:1没什么意义啊,总之是看不懂这个#define WDT_CTRL_KEY (0x0a57 << 1)
3.
clock_init();
在clock.c中
int clock_init(void)
{
struct sunxi_ccm_reg *const ccm =
(struct sunxi_ccm_reg *)SUNXI_CCM_BASE;
#ifdef CONFIG_SPL_BUILD
clock_init_safe();
#endif
/* uart clock source is apb1 */
sr32(&ccm->apb1_clk_div_cfg, 24, 2, APB1_CLK_SRC_OSC24M);
sr32(&ccm->apb1_clk_div_cfg, 16, 2, APB1_FACTOR_N);
sr32(&ccm->apb1_clk_div_cfg, 0, 5, APB1_FACTOR_M);
/* open the clock for uart */
sr32(&ccm->apb1_gate, 16 + CONFIG_CONS_INDEX - 1, 1, CLK_GATE_OPEN);
#ifdef CONFIG_NAND_SUNXI
/* nand clock source is osc24m */
sr32(&ccm->nand_sclk_cfg, 24, 2, NAND_CLK_SRC_OSC24);
sr32(&ccm->nand_sclk_cfg, 16, 2, NAND_CLK_DIV_N);
sr32(&ccm->nand_sclk_cfg, 0, 4, NAND_CLK_DIV_M);
sr32(&ccm->nand_sclk_cfg, 31, 1, CLK_GATE_OPEN);
/* open clock for nand */
sr32(&ccm->ahb_gate0, AHB_GATE_OFFSET_NAND, 1, CLK_GATE_OPEN);
#endif
return 0;
}
首先这里的#ifdef都为真,整个过程就是clock_init_safe()、open the clock for uart、open clock for nand
这里的clock_init_safe()没看太懂,貌似是对电源管理的时钟配置还有时钟源的时钟配置,暂时不关心这个
因为关心的是open the clock for uart、open clock for nand,不用说也明白,具体的操作过程通过查看
芯片手册可知,但要注意在使能外设时钟时要配置其时钟源(想到stm32)
4.
timer_init();
在timer.c中
#define TIMER_MODE
(0x0 << 7) /* continuous mode */
#define TIMER_DIV
(0x0 << 4) /* pre scale 1 */
#define TIMER_SRC
(0x1 << 2) /* osc24m */
#define TIMER_RELOAD (0x1 << 1) /* reload internal value */
#define TIMER_EN
(0x1 << 0) /* enable timer */
#define TIMER_LOAD_VAL
0xffffffff
#define TIMER_NUM
0 /* we use timer 0 */
static struct sunxi_timer *timer_base =
&((struct sunxi_timer_reg *)SUNXI_TIMER_BASE)->timer[TIMER_NUM];
/* macro to read the 32 bit timer: since it decrements, we invert read value */
#define READ_TIMER() (~readl(&timer_base->val))
/* init timer register */
int timer_init(void)
{
writel(TIMER_LOAD_VAL, &timer_base->inter);
writel(TIMER_MODE | TIMER_DIV | TIMER_SRC | TIMER_RELOAD | TIMER_EN,
&timer_base->ctl);
return 0;
}
写的很明白,对timer0进行了设置
5.
gpio_init();在s_init同文件中
int gpio_init(void)
{
#if CONFIG_CONS_INDEX == 1 && defined(CONFIG_UART0_PORT_F)
#if defined(CONFIG_SUN4I) || defined(CONFIG_SUN7I)
/* disable GPB22,23 as uart0 tx,rx to avoid conflict */
sunxi_gpio_set_cfgpin(SUNXI_GPB(22), SUNXI_GPIO_INPUT);
sunxi_gpio_set_cfgpin(SUNXI_GPB(23), SUNXI_GPIO_INPUT);
#endif
sunxi_gpio_set_cfgpin(SUNXI_GPF(2), SUNXI_GPF2_UART0_TX);
sunxi_gpio_set_cfgpin(SUNXI_GPF(4), SUNXI_GPF4_UART0_RX);
sunxi_gpio_set_pull(SUNXI_GPF(4), 1);
#elif CONFIG_CONS_INDEX == 1 && (defined(CONFIG_SUN4I) || defined(CONFIG_SUN7I))
sunxi_gpio_set_cfgpin(SUNXI_GPB(22), SUN4I_GPB22_UART0_TX);
sunxi_gpio_set_cfgpin(SUNXI_GPB(23), SUN4I_GPB23_UART0_RX);
sunxi_gpio_set_pull(SUNXI_GPB(23), 1);
#elif CONFIG_CONS_INDEX == 1 && defined(CONFIG_SUN5I)
sunxi_gpio_set_cfgpin(SUNXI_GPB(19), SUN5I_GPB19_UART0_TX);
sunxi_gpio_set_cfgpin(SUNXI_GPB(20), SUN5I_GPB20_UART0_RX);
sunxi_gpio_set_pull(SUNXI_GPB(20), 1);
#elif CONFIG_CONS_INDEX == 2 && defined(CONFIG_SUN5I)
sunxi_gpio_set_cfgpin(SUNXI_GPG(3), SUN5I_GPG3_UART1_TX);
sunxi_gpio_set_cfgpin(SUNXI_GPG(4), SUN5I_GPG4_UART1_RX);
sunxi_gpio_set_pull(SUNXI_GPG(4), 1);
#else
#error Unsupported console port number. Please fix pin mux settings in board.c
#endif
return 0;
}
这里主要是分析预编译的处理,可知
#elif CONFIG_CONS_INDEX == 1 && (defined(CONFIG_SUN4I) || defined(CONFIG_SUN7I))
sunxi_gpio_set_cfgpin(SUNXI_GPB(22), SUN4I_GPB22_UART0_TX);
sunxi_gpio_set_cfgpin(SUNXI_GPB(23), SUN4I_GPB23_UART0_RX);
sunxi_gpio_set_pull(SUNXI_GPB(23), 1);
代码写的也很明白,就是初始化了uart用到的管脚,
6.
#ifdef CONFIG_SPL_BUILD
gd = &gdata;
preloader_console_init();
#ifdef CONFIG_SPL_I2C_SUPPORT
/* Needed early by sunxi_board_init if PMU is enabled */
i2c_init(CONFIG_SYS_I2C_SPEED, CONFIG_SYS_I2C_SLAVE);
#endif
sunxi_board_init();
#endif
preloader_console_init();i2c_init(CONFIG_SYS_I2C_SPEED, CONFIG_SYS_I2C_SLAVE);就不用说了,
但是gd = &gdata;什么意思呢?其中/* Pointer to as well as the global data structure for SPL */
DECLARE_GLOBAL_DATA_PTR;
gd_t gdata __attribute__ ((section(".data")));这个定义在<arch/arm/lib/spl.c>中,
DECLARE_GLOBAL_DATA_PTR;定义在<arch/arm/include/asm/global_data.h>中
#ifdef CONFIG_ARM64
#define DECLARE_GLOBAL_DATA_PTR
register volatile gd_t *gd asm ("x18")
#else
#define DECLARE_GLOBAL_DATA_PTR
register volatile gd_t *gd asm ("r9")
#endif
意思是定义一个gd_t类型的的指针gd放在r9中,在使用到gd时,直接使用宏就定义了,注意并没有给指针赋值,
gd_t 定义在<include/arm-generic/global_data.h>中
#ifndef __ASM_GENERIC_GBL_DATA_H
#define __ASM_GENERIC_GBL_DATA_H
/*
* The following data structure is placed in some memory which is
* available very early after boot (like DPRAM on MPC8xx/MPC82xx, or
* some locked parts of the data cache) to allow for a minimum set of
* global variables during system initialization (until we have set
* up the memory controller so that we can use RAM).
*
* Keep it *SMALL* and remember to set GENERATED_GBL_DATA_SIZE > sizeof(gd_t)
*
* Each architecture has its own private fields. For now all are private
*/
#ifndef __ASSEMBLY__
#include <linux/list.h>
typedef struct global_data {
bd_t *bd;
unsigned long flags;
unsigned int baudrate;
unsigned long cpu_clk; /* CPU clock in Hz!
*/
unsigned long bus_clk;
/* We cannot bracket this with CONFIG_PCI due to mpc5xxx */
unsigned long pci_clk;
unsigned long mem_clk;
#if defined(CONFIG_LCD) || defined(CONFIG_VIDEO)
unsigned long fb_base; /* Base address of framebuffer mem */
#endif
#if defined(CONFIG_POST) || defined(CONFIG_LOGBUFFER)
unsigned long post_log_word;
/* Record POST activities */
unsigned long post_log_res; /* success of POST test */
unsigned long post_init_f_time;
/* When post_init_f started */
#endif
#ifdef CONFIG_BOARD_TYPES
unsigned long board_type;
#endif
unsigned long have_console; /* serial_init() was called */
#ifdef CONFIG_PRE_CONSOLE_BUFFER
unsigned long precon_buf_idx; /* Pre-Console buffer index */
#endif
#ifdef CONFIG_MODEM_SUPPORT
unsigned long do_mdm_init;
unsigned long be_quiet;
#endif
unsigned long env_addr; /* Address
of Environment struct */
unsigned long env_valid; /* Checksum of Environment valid? */
unsigned long ram_top; /* Top address of RAM used by U-Boot */
unsigned long relocaddr; /* Start address of U-Boot in RAM */
phys_size_t ram_size; /* RAM size */
unsigned long mon_len; /* monitor len */
unsigned long irq_sp;
/* irq stack pointer */
unsigned long start_addr_sp; /* start_addr_stackpointer */
unsigned long reloc_off;
struct global_data *new_gd; /* relocated global data */
#ifdef CONFIG_DM
struct device *dm_root; /* Root instance for Driver Model */
struct list_head uclass_root; /* Head of core tree */
#endif
const void *fdt_blob; /* Our device tree, NULL if none */
void *new_fdt;
/* Relocated FDT */
unsigned long fdt_size; /* Space reserved for relocated FDT */
void **jt;
/* jump table */
char env_buf[32]; /* buffer for getenv() before reloc. */
#ifdef CONFIG_TRACE
void
*trace_buff; /* The trace buffer */
#endif
#if defined(CONFIG_SYS_I2C)
int
cur_i2c_bus; /* current used i2c bus */
#endif
unsigned long timebase_h;
unsigned long timebase_l;
struct arch_global_data arch; /* architecture-specific data */
} gd_t;
#endif
/*
* Global Data Flags
*/
#define GD_FLG_RELOC
0x00001 /* Code was relocated to RAM
*/
#define GD_FLG_DEVINIT
0x00002 /* Devices have been initialized
*/
#define GD_FLG_SILENT
0x00004 /* Silent mode
*/
#define GD_FLG_POSTFAIL
0x00008 /* Critical POST test failed
*/
#define GD_FLG_POSTSTOP
0x00010 /* POST seqeunce aborted
*/
#define GD_FLG_LOGINIT
0x00020 /* Log Buffer has been initialized */
#define GD_FLG_DISABLE_CONSOLE 0x00040 /* Disable console (in & out)
*/
#define GD_FLG_ENV_READY 0x00080 /* Env. imported into hash table
*/
#endif /* __ASM_GENERIC_GBL_DATA_H */
意思是在没有初始化内存要用到的一个全局结构,其中多是关于板子初始化用到的有关板子配置、
内存环境的变量等等。
总结一下gd:
1.gd_t gdata __attribute__ ((section(".data")));这个定义在<arch/arm/lib/spl.c>中,声明gdata。
2.DECLARE_GLOBAL_DATA_PTR;声明指针gd,貌似多处定义不会引起重复定义?因为是register吗?
3.gd = &gdata;使指针gd指向gdata
或者ldr r9, =gdata <arch/arm/armv7/cpu/lowlevel_init.S>中
但是此处的gd = &gdata;貌似还是没用到,但是查看preloader_console_init();的定义
/*
* This requires UART clocks to be enabled.
In order for this to work the
* caller must ensure that the gd pointer is valid.
*/
void preloader_console_init(void)
{
gd->bd = &bdata;
gd->baudrate = CONFIG_BAUDRATE;
serial_init();
/* serial communications setup */
gd->have_console = 1;
puts("nU-Boot SPL " PLAIN_VERSION " (" U_BOOT_DATE " - "
U_BOOT_TIME ")n");
#ifdef CONFIG_SPL_DISPLAY_PRINT
spl_display_print();
#endif
}
可知,虽然preloader_console_init所在文件DECLARE_GLOBAL_DATA_PTR,但是没有对gd赋值,
/*
* This requires UART clocks to be enabled.
In order for this to work the
* caller must ensure that the gd pointer is valid.
*/
就是说调用之前要对gd赋值,所以才有了此处的gd = &gdata;调用preloader_console_init
并不会破坏r9,也就是说preloader_console_init中gd是有意义的。
下面sunxi_board_init();中用到了gd,同样没有赋值,整个项目中对gd赋值的就3处:
1.lowlevel_init.S中25: ldr r9, =gdata
2.board.c中gd = &gdata;
3.void __weak board_init_f(ulong dummy)中gd = &gdata;
而用到的地方不止3处
回过来想想
1.
为什么不在preloader_console_init中赋值,而不用在调用preloader_console_init之前赋值?
只能解释为:为了节约宝贵的占空间吗?要知道现在SDRAM还没有初始化完成
2.
还有gdata到底是在什么时候初始化的?还是在链接的时候处理的呢?
貌似gd_t gdata __attribute__ ((section(".data")));在data段占有空间,并没有直接给出全部成员的初始化
而是像
void preloader_console_init(void)
{
gd->bd = &bdata;
gd->baudrate = CONFIG_BAUDRATE;
serial_init();
/* serial communications setup */
gd->have_console = 1;
puts("nU-Boot SPL " PLAIN_VERSION " (" U_BOOT_DATE " - "
U_BOOT_TIME ")n");
#ifdef CONFIG_SPL_DISPLAY_PRINT
spl_display_print();
#endif
}
这样使用的
然后是sunxi_board_init();在<board/sunxi/board.c>中
void sunxi_board_init(void)
{
int power_failed = 0;
unsigned long ramsize;
printf("DRAM:");
ramsize = sunxi_dram_init();
printf(" %lu MiBn", ramsize >> 20);
if (!ramsize)
hang();
#ifdef CONFIG_AXP152_POWER
power_failed = axp152_init();
power_failed |= axp152_set_dcdc2(1400);
power_failed |= axp152_set_dcdc3(1500);
power_failed |= axp152_set_dcdc4(1250);
power_failed |= axp152_set_ldo2(3000);
#endif
#ifdef CONFIG_AXP209_POWER
power_failed |= axp209_init();
power_failed |= axp209_set_dcdc2(1400);
#ifdef CONFIG_FAST_MBUS
power_failed |= axp209_set_dcdc3(1300);
#else
power_failed |= axp209_set_dcdc3(1250);
#endif
power_failed |= axp209_set_ldo2(3000);
power_failed |= axp209_set_ldo3(2800);
power_failed |= axp209_set_ldo4(2800);
#endif
/*
* Only clock up the CPU to full speed if we are reasonably
* assured it's being powered with suitable core voltage
*/
if (!power_failed)
#ifdef CONFIG_SUN7I
clock_set_pll1(912000000);
#else
clock_set_pll1(1008000000);
#endif
else
printf("Failed to set core voltage! Can't set CPU frequencyn");
}
总体过程:初始化内存、设置PMU、设置CPU频率,这里需要注意一下,如果要设置PMU
需要前面的I2C的初始化(I2C初始化的注释:/* Needed early by sunxi_board_init if PMU is enabled */)
返回到start.S中bl _main
最后
以上就是健壮缘分为你收集整理的Cubietruck开发板SPL阶段的s_init分析的全部内容,希望文章能够帮你解决Cubietruck开发板SPL阶段的s_init分析所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复