传统的SoC比如S3C2440,6410或者s5pv210,它们的Uboot在lowlevel_init.S文件中做了很多片上外设的初始化工作,比如配置部分GPIO口的电气属性,配置串口,配置DDR控制器等,配置的过程很简单,简单来说就是这样:

ldr r0,=外设寄存器地址
ldr r1,=寄存器的值
str r1,[r0] @r0和r1只是常用,也可以使用别的寄存器来进行这个过程

但是NXP的IMX6ULL这款SoC的Uboot,在lowlevel_init.S这个文件中仅仅只是在片内RAM中设置了堆栈,然后划分出一部分区域用于存储struct global_data的值

ENTRY(lowlevel_init)ldr sp, =CONFIG_SYS_INIT_SP_ADDR @设置sp指针到片内RAM的一块bic sp, sp, #7 @8字节对齐sub sp, sp, #GD_SIZE  @留出struct global_data变量的区域bic sp, sp, #7        mov r9, sp            @把struct global_data变量的起始地方放到r9中push {ip, lr}bl s_init             @对IMX6ULL来说,这个函数是个空函数pop {ip, pc}  ENDPROC(lowlevel_init)

之后进入到_main函数,这个函数在arch/arm/lib/crt0.S中,对IMX6ULL来说这个函数几乎干了Uboot在启动Linux系统之前的所有事情。

1、上来重新设置以下堆栈指针,因为上述的lowlevel_init函数把sp指针指向struct global变量的起始地址去了。

ldr sp, =(CONFIG_SYS_INIT_SP_ADDR)

2、连续三个函数调用。

mov r0, sp  @进行函数调用的参数传递,把参数放到r0中
bl board_init_f_alloc_reserve      @在片内SRAM留出malloc区域和新的global_data区域
mov sp, r0  @获取函数返回值mov r9, r0
bl board_init_f_init_reserve       @把上述留出的两个区域给清零mov r0, #0
bl board_init_f          @调用board_init_f函数,这个函数非常重要

boadr_init_f函数非常重要!它的作用有两个:一是初始化部分片上外设,比如串口,LCD接口,定时器等。二是初始化global_data中的所有成员变量,global_data的成员变量中有一大部分是地址信息,描述了外部DDR该如何划分,哪里是malloc区,哪里是重定位的起始地址,哪里是Uboot的终止地址等等。有了这些信息才能进行后续的Uboot代码重定位过程。

        下面来看看board_inif_f函数,该函数位于/uboot/common/board_f.c,这里只摘录函数中的重点:

void board_init_f(ulong boot_flags)
{gd->flags = boot_flags;   //传入值为0,上述代码中有gd->have_console = 0;     //表示还没控制台if (initcall_run_list(init_sequence_f))hang();......
}

这个initcall_run_list函数会依次执行init_sequence_f这个函数指针数组中的所有函数,这些函数执行成功返回0,失败返回非0值,就会陷入hang()循环。

这个函数指针数组里的重点函数如下:

static init_fnc_t init_sequence_f[] = {setup_mon_len,//计算整个Uboot的长度board_early_init_f,timer_init,get_clocks,env_init,init_baud_rate,serial_init,console_init_f, display_options,display_text_info, print_cpuinfo,show_board_info,dram_init,setup_dram_config,show_dram_config,setup_dest_addr,reserve_uboot,reserve_malloc,reserve_global_data,reloc_fdt,setup_reloc,.....
}

串口初始化是在board_init_f中完成的,通过调用这个函数数组中的函数初始化串口。下面来看看Uboot是如何初始化串口的:

1、board_early_init_f,配引脚。该函数完成了串口所要使用的引脚的复用属性和电气属性的配置,其在board/freescale/mx6ullevk/mx6ullevk.c中定义:

int board_early_init_f(void)
{setup_iomux_uart();//配置串口的引脚复用为uart,具体实现如下return 0;
}//根据引脚配置设置复用属性
static void setup_iomux_uart(void)
{imx_iomux_v3_setup_multiple_pads(uart1_pads, ARRAY_SIZE(uart1_pads));
}//引脚的复用属性宏和电气属性宏
static iomux_v3_cfg_t const uart1_pads[] = {MX6_PAD_UART1_TX_DATA__UART1_DCE_TX | MUX_PAD_CTRL(UART_PAD_CTRL),MX6_PAD_UART1_RX_DATA__UART1_DCE_RX | MUX_PAD_CTRL(UART_PAD_CTRL),
};//以下是电气属性配置的过程的分解步骤#define MUX_PAD_CTRL(x)        ((iomux_v3_cfg_t)(x) << MUX_PAD_CTRL_SHIFT)#define MUX_PAD_CTRL_SHIFT 42//位于board/freescale/mx6ullevk/mx6ullevk.c中,宏的组合来达到电气属性的配置#define UART_PAD_CTRL  (PAD_CTL_PKE | PAD_CTL_PUE |     \
PAD_CTL_PUS_100K_UP | PAD_CTL_SPEED_MED |               \
PAD_CTL_DSE_40ohm   | PAD_CTL_SRE_FAST  | PAD_CTL_HYS)//以PAD_CTL_PUS_100K_UP说明这个宏是如何配置引脚的电气属性//位于arch/arm/include/asm/imx-common/iomux-v3.h
#define PAD_CTL_PUE       (0x1 << 4)//位于arch/arm/include/asm/imx-common/iomux-v3.h
#define PAD_CTL_PUS_100K_UP (2 << 14 | PAD_CTL_PUE)//通过上述两个操作,使得PAD_CTL_PUS_100K_UP这个宏代表了GPIO的一个电气属性的配置值,只需要将这个配置值写入GPIO的控制寄存器即可//引脚复用属性的配置也是一样的道理//位于arch/arm/include/asm/arch/mx6ul_pins.h    MX6_PAD_UART1_TX_DATA__UART1_DCE_TX = IOMUX_PAD(0x0310, 0x0084, 0, 0x0000, 0, 0)//位于arch/arm/include/asm/imx-common/iomux-v3.htypedef u64 iomux_v3_cfg_t#define IOMUX_PAD(pad_ctrl_ofs, mux_ctrl_ofs, mux_mode, sel_input_ofs,  \sel_input, pad_ctrl)                    \(((iomux_v3_cfg_t)(mux_ctrl_ofs) << MUX_CTRL_OFS_SHIFT)     |   \((iomux_v3_cfg_t)(mux_mode)      << MUX_MODE_SHIFT)         |   \((iomux_v3_cfg_t)(pad_ctrl_ofs)  << MUX_PAD_CTRL_OFS_SHIFT) |   \((iomux_v3_cfg_t)(pad_ctrl)      << MUX_PAD_CTRL_SHIFT)     |   \((iomux_v3_cfg_t)(sel_input_ofs) << MUX_SEL_INPUT_OFS_SHIFT)|   \((iomux_v3_cfg_t)(sel_input)     << MUX_SEL_INPUT_SHIFT))//可以看出,最终出来的也是一个要写入寄存器的值。

总结一下串口的引脚配置过程:

首先在board/freescale/mx6ullevk/mx6ullevk.c文件中的uart1_pads[]数组中写入引脚的复用属性和电气属性宏。

然后调用imx_iomux_v3_setup_multiple_pads(uart1_pads, ARRAY_SIZE(uart1_pads))函数把寄存器的值写入到相应的寄存器中。

2、init_baud_rate,获取波特率存入global_data中。要注意的是波特率的修改是在mx6_common.h中,这个头文件被include/configs/mx6ullevk.h所包含。

//位于uboot/common/board_f.c//从环境变量中读取波特率的值写入到global_data中去static int init_baud_rate(void)
{gd->baudrate = getenv_ulong("baudrate", 10, CONFIG_BAUDRATE);return 0;
}//位于include/configs/mx6_common.h#define CONFIG_BAUDRATE         115200

3、serial_init函数,通过多层调用实现IMX6ULL的UART1的初始化。该函数位于drivers/serial目录下

int serial_init(void)
{gd->flags |= GD_FLG_SERIAL_READY;return get_current()->start();
}static struct serial_device *get_current(void)
{struct serial_device *dev;if (!(gd->flags & GD_FLG_RELOC))dev = default_serial_console();//此时还未重定位,所以会执行这一句,函数定义如下。else if (!serial_current)dev = default_serial_console();elsedev = serial_current;/* We must have a console device */if (!dev) {#ifdef CONFIG_SPL_BUILDputs("Cannot find console\n");hang();#elsepanic("Cannot find console\n");#endif}return dev;
}//可以看到这是一个弱定义,别处没有定义的话就用这里的,在serial.c中未定义,所以会用这个函数。__weak struct serial_device *default_serial_console(void)
{return &mxc_serial_drv;
}//下面是返回的这个结构体的内容
static struct serial_device mxc_serial_drv = {.name   = "mxc_serial",.start  = mxc_serial_init,.stop   = NULL,.setbrg = mxc_serial_setbrg,.putc   = mxc_serial_putc,.puts   = default_serial_puts,.getc   = mxc_serial_getc,.tstc   = mxc_serial_tstc,
};
//可以看出该结构体中都是函数指针,这些函数实现了IMX6ULL的串口初始化和读写函数//回到最前面,serial_init其实调用的就是mxc_serial_init函数。
static int mxc_serial_init(void)
{__REG(UART_PHYS + UCR1) = 0x0;__REG(UART_PHYS + UCR2) = 0x0;while (!(__REG(UART_PHYS + UCR2) & UCR2_SRST));__REG(UART_PHYS + UCR3) = 0x0704 | UCR3_ADNIMP;__REG(UART_PHYS + UCR4) = 0x8000;__REG(UART_PHYS + UESC) = 0x002b;__REG(UART_PHYS + UTIM) = 0x0;__REG(UART_PHYS + UTS) = 0x0;serial_setbrg();__REG(UART_PHYS + UCR2) = UCR2_WS | UCR2_IRTS | UCR2_RXEN | UCR2_TXEN | UCR2_SRST;__REG(UART_PHYS + UCR1) = UCR1_UARTEN;return 0;
}//其中的函数展开为
void serial_setbrg(void)
{get_current()->setbrg();
}//真正的配置IMX6ULL的串口波特率
static void mxc_serial_setbrg(void)
{u32 clk = imx_get_uartclk();if (!gd->baudrate)gd->baudrate = CONFIG_BAUDRATE;__REG(UART_PHYS + UFCR) = (RFDIV << UFCR_RFDIV_SHF)| (TXTL << UFCR_TXTL_SHF)| (RXTL << UFCR_RXTL_SHF);__REG(UART_PHYS + UBIR) = 0xf;__REG(UART_PHYS + UBMR) = clk / (2 * gd->baudrate);}//上面的宏定义如下
#define UART_PHYS   CONFIG_MXC_UART_BASE
#define __REG(x)     (*((volatile u32 *)(x)))//这些都是寄存器相对于基地址UART_PHYS的偏移
#define URXD  0x0  /* Receiver Register */
#define UTXD  0x40 /* Transmitter Register */
#define UCR1  0x80 /* Control Register 1 */
#define UCR2  0x84 /* Control Register 2 */
#define UCR3  0x88 /* Control Register 3 */
#define UCR4  0x8c /* Control Register 4 */
#define UFCR  0x90 /* FIFO Control Register */
#define USR1  0x94 /* Status Register 1 */
#define USR2  0x98 /* Status Register 2 */
#define UESC  0x9c /* Escape Character Register */
#define UTIM  0xa0 /* Escape Timer Register */
#define UBIR  0xa4 /* BRM Incremental Register */
#define UBMR  0xa8 /* BRM Modulator Register */
#define UBRC  0xac /* Baud Rate Count Register */
#define UTS   0xb4 /* UART Test Register (mx31) *///位于include/configs/mx6ullevk.h,这里选择UART1作为uboot启动时候的串口,可以换成2。
#define CONFIG_MXC_UART_BASE        UART1_BASE

通过以上三步,Uboot完成了IMX6ULL的串口初始化。

通过对这个过程的了解,我们在移植Uboot过程中,对于串口,最主要的工作就是在板级文件夹下的.c文件中(uboot/board/freescale/mx6ullevk.c)中修改引脚的复用属性和电气属性,在相应的头文件中修改好波特率和要使用的串口的宏定义。

其他的片上外设的初始化过程分析,敬请期待!

捋一捋IMX6ULL的Uboot是如何初始化片上外设的——以串口为例相关推荐

  1. 捋一捋PDF、PMF、CDF是什么

    总第230篇/张俊红 还记得前段时间看过一篇文章,就是调查大家疫情期间都干了什么,有一条是疫情期间终于弄清楚了PDF和CDF的区别.PDF.PMF.CDF这几个概念确实很容易混淆.今天就来捋一捋这几个 ...

  2. 捋一捋js面向对象的继承问题

    说到面向对象这个破玩意,曾经一度我都处于很懵逼的状态,那么面向对象究竟是什么呢?其实说白了,所谓面向对象,就是基于类这个概念,来实现封装.继承和多态的一种编程思想罢了.今天我们就来说一下这其中继承的问 ...

  3. 乐鑫esp8266学习rtos3.0笔记第4篇:带你捋一捋微信公众号 airkiss 配网 esp8266 并绑定设备的过程,移植并成功实现在 esp8266 rtos3.1 sdk。(附带demo)

    本系列博客学习由非官方人员 半颗心脏 潜心所力所写,仅仅做个人技术交流分享,不做任何商业用途.如有不对之处,请留言,本人及时更改. 1. Esp8266之 搭建开发环境,开始一个"hello ...

  4. android+原点扩散动画,捋一捋Android的转场动画

    捋一捋Android的转场动画 由于录制的gif过大,导致CSDN部分gif无法显示,可以传送到GitHub查看本篇博客 背景 随着 Material Design设计概念的提出,使得很多的开发过程中 ...

  5. 小猪的Python学习之旅 —— 6.捋一捋Python线程概念

    小猪的Python学习之旅 -- 6.捋一捋Python线程概念 标签: Python 引言 从刚开始学习Python爬虫的时候,就一直惦记着多线程这个东西, 想想每次下载图片都是单线程,一个下完继续 ...

  6. “工欲善其事必先利其器“,捋一捋程序员们的十八般兵器

    快过年啦~大家放假了吗?很多人是不是已经在回家的路上了? 放假的这些天有什么安排吗?一年中除了国庆假,春节的假期是最长的了,可得好好利用起来呀~ 相信关注我的很多人都是对编程感兴趣的,也想成为程序员. ...

  7. Python赚钱,门路可多了,给大家捋一捋!

    Python赚钱,门路可多了,给大家捋一捋! 1.程序代写 到猪八戒.网淘宝网上搜:Python程序/接单,然后到相应的店里找客服,就说想做程序开发,是否可以 给联系方式,如果商家比较好相处,多接几次 ...

  8. 捋一捋Python中的数学运算math库(上篇)

    正式的Python专栏第18篇,同学站住,别错过这个从0开始的文章! 很多学习编程的都多多少少学习了一些数学知识. 学委之前也简单吐槽了 Python中奇葩的round函数! 这篇我们讲讲那些常用的数 ...

  9. 2023.4.3数仓项目捋一捋

    数仓项目捋一捋 初步认识 1.数仓需具备 数据存储.管理(一些数据混乱).分析计算(分类,聚合,汇总,挖掘更大价值) 2.对于企业意义 往往作为企业BI(BI重度依赖数据,从大量数据去挖掘有用信息,帮 ...

最新文章

  1. Eclipse 之 EasyExplore 插件
  2. 【Cocos2dx开发】精灵
  3. python3 strip lstrip rstrip 删除字符串首尾指定字符
  4. swift -- 数组
  5. TensorFlow打印一个tensor值报错
  6. [转载]Linux批量替换不同文件中的相同字符串
  7. 超外差和超再生模块有何区别?
  8. MySQL多实例配置
  9. java 从一个容器获取对象,Java 如何实现从spring容器中获取注入的bean对象
  10. 机房服务器巡视项目,年底,机房巡检不能少
  11. 关于交换的知识点(一)
  12. Java之美[从菜鸟到高手演变]之Java学习方法
  13. 10.2.2.7 -DHCP 和 DNS 服务
  14. ANSYS中vonnbsp;misesnbsp;stres…
  15. 租服务器一年多少钱,阿里云服务器租赁一年多少钱
  16. java计算机毕业设计宠物医院管理源码+数据库+系统+lw文档+部署
  17. (清华源)ERROR: Could not find a version that satisfies the requirement pycocotools (unavailable)
  18. 手机怎么修改照片大小尺寸?这两种方法轻松解决
  19. 笔记本电脑没有鼠标怎么右键_电脑鼠标右键没有反应怎么办?鼠标右键无法使用的解决方法...
  20. 请问下载的arcgis切片怎么打开?

热门文章

  1. 软件项目交接最怕什么
  2. 基于51单片机汽车胎压温度监测报警系统(程序+仿真+原理图+元件清单)
  3. 希尔伯特曲线 java_分形的乐趣之_Hilbert曲线
  4. bower安装好了angular 1.6.6,但是还是不能使用angular
  5. CSS盒子模型居中方法,完整版开放下载
  6. 应聘硬件工程师或研发类工作资料集锦
  7. 农村生活污水处理技术有哪些特点?
  8. bootstarp怎么使盒子到最右边_海美迪H7Plus电视盒子:配置喜人,还没有广告?爱了!...
  9. 仙人掌之歌——高层人事风波
  10. JAVA Swing 怎么把背景色或者前景色设置为自己想要的颜色呢?