我是靠谱客的博主 健壮缘分,最近开发中收集的这篇文章主要介绍Cubietruck开发板SPL阶段的s_init分析,觉得挺不错的,现在分享给大家,希望可以做个参考。

概述

/* 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分析所遇到的程序开发问题。

如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(51)

评论列表共有 0 条评论

立即
投稿
返回
顶部