蜗窝科技的X项目学习笔记
转载:http://www.wowotech.net/x_project/bubblegum_uboot_porting.html
U-boot移植说明
前言
U-boot相关代码
board(开发板)->machine(zynq)->arch(arm)->cpu(armv8)
移植目录结构和Kconfig(选配)的确定
board
board/action/*/Kconfig&Makefile
arch/arm
arch/arm/Kconfig
链接脚本
u-boot串口驱动移植
硬件信息
板子上有哪些串口资源,对外的连接方式如何?
电平 管脚
每个串口都有哪些信号,这些信号连接到CPU的哪些管脚上了,管脚的复用情况如何,使用哪些寄存器控制?
串口控制器的power、clock、reset等资源是否可以单独控制,相应的子系统是否已经提供标准的控制接口了,如果没有,由哪些寄存器控制?
串口控制器的寄存器说明,主要包括数据位、校验位、停止位、流控等信息的控制,数据的收发等。
串口驱动的软件框架(u-boot)
u-boot-serial-architecture
对下,串口驱动依赖driver model、device tree、clock driver、pinctrl driver等基础模块。
对上,串口驱动向u-boot的console模块提供接口,进而为lib中的printf等提供接口。
另外,为了简化串口驱动的编写,u-boot将串口有关的共性实现,抽象出来并封装在serial uclass中,我们编写驱动的时候,只需要按照serial uclass的规则,填充执行的serial ops即可。具体可以参考后续的说明。
device driver
点击查看代码
/*
* (C) Copyright 2016 wowotech
*
* Refers to "https://github.com/96boards-bubblegum/u-boot/blob/
* bubblegum96-2015.07/drivers/serial/serial_owl.c"
*
* wowo<wowo@wowotech.net>
*
* SPDX-License-Identifier: GPL-2.0+
*/
#include <common.h>
#include <dm.h>
#include <errno.h>
#include <fdtdec.h>
#include <serial.h>
struct owl_serial_priv {
fdt_addr_t base; /* register address */
};
DECLARE_GLOBAL_DATA_PTR;
/* TODO, we need pinmux, device tree, clock framework, etc. */
//#define USING_UART2
#define HOSC_FREQ (24000000)
#define CMU_DEVCLKEN1 (0xE01600A4)
#define CMU_DEVRST1 (0xE01600AC)
#define CMU_UART2CLK (0xE0160064)
#define CMU_UART5CLK (0xE01600B8)
#define MFP_CTL1 (0xE01B0044)
#define UART2_BASE (0xE0124000)
#define UART5_BASE (0xE012a000)
/* UART Register offset */
#define UART_CTL (0x0)
#define UART_RXDAT (0x4)
#define UART_TXDAT (0x8)
#define UART_STAT (0xc)
/* UART_CTL */
#define UART_CTL_EN (0x1 << 15) /* UART enable */
#define UART_CTL_AFC (0x1 << 12) /* Auto flow control */
#define UART_CTL_PARITY (0x7 << 4) /* Parity */
#define UART_CTL_STOP (0x1 << 2) /* stop bit */
#define UART_CTL_DATA_WIDTH (0x3 << 0) /* Data width */
#define UART_PARITY_NONE (0)
#define UART_PARITY_ODD (4)
#define UART_PARITY_LOGIC1 (5)
#define UART_PARITY_EVEN (6)
#define UART_PARITY_LOGIC0 (7)
#define UART_DATA_WIDTH_5 (0)
#define UART_DATA_WIDTH_6 (1)
#define UART_DATA_WIDTH_7 (2)
#define UART_DATA_WIDTH_8 (3)
/* UART_STAT */
#define UART_STAT_TFES (0x1 << 10) /* TX FIFO Empty Status */
#define UART_STAT_RFFS (0x1 << 9) /* RX FIFO full Status */
#define UART_STAT_TFFU (0x1 << 6) /* TX FIFO full Status */
#define UART_STAT_RFEM (0x1 << 5) /* RX FIFO Empty Status */
int owl_serial_setbrg(struct udevice *dev, int baudrate)
{
int divider;
struct owl_serial_priv *priv = dev_get_priv(dev);
divider = (115200 * 8);
divider = (HOSC_FREQ + divider / 2) / divider;
if (divider > 0)
divider--;
#ifdef USING_UART2
clrsetbits_le32(CMU_UART2CLK, 0x1f, divider);
#else
clrsetbits_le32(CMU_UART5CLK, 0x1f, divider);
#endif
/*
* 8N1
*/
clrsetbits_le32(priv->base + UART_CTL, UART_CTL_DATA_WIDTH,
UART_DATA_WIDTH_8);
clrsetbits_le32(priv->base + UART_CTL, UART_CTL_PARITY,
UART_PARITY_NONE);
clrbits_le32(priv->base + UART_CTL, UART_CTL_STOP);
return 0;
}
static int owl_serial_getc(struct udevice *dev)
{
struct owl_serial_priv *priv = dev_get_priv(dev);
if (readl(priv->base + UART_STAT) & UART_STAT_RFEM)
return -EAGAIN;
return (int)(readl(priv->base + UART_RXDAT));
}
static int owl_serial_putc(struct udevice *dev, const char ch)
{
struct owl_serial_priv *priv = dev_get_priv(dev);
if (readl(priv->base + UART_STAT) & UART_STAT_TFFU)
return -EAGAIN;
writel(ch, priv->base + UART_TXDAT);
return 0;
}
static int owl_serial_pending(struct udevice *dev, bool input)
{
struct owl_serial_priv *priv = dev_get_priv(dev);
unsigned int stat = readl(priv->base + UART_STAT);
if (input)
return !(stat & UART_STAT_RFEM);
else
return !(stat & UART_STAT_TFES);
}
extern void bubblegum_early_debug(int debug_code);
static int owl_serial_probe(struct udevice *dev)
{
struct owl_serial_priv *priv = dev_get_priv(dev);
priv->base = fdtdec_get_addr(gd->fdt_blob, dev->of_offset, "reg");
if (priv->base == FDT_ADDR_T_NONE)
return -1;
debug("%s: base is 0x%llx\n", __func__, priv->base);
/* device clock enable */
#ifdef USING_UART2
setbits_le32(CMU_DEVCLKEN1, 1 << 8); /* uart2 */
#else
setbits_le32(CMU_DEVCLKEN1, 1 << 21); /* uart5 */
#endif
/* reset de-assert */
#ifdef USING_UART2
setbits_le32(CMU_DEVRST1, 1 << 7);
#else
setbits_le32(CMU_DEVRST1, 1 << 17);
#endif
/* set default baudrate and enable UART */
owl_serial_setbrg(dev, 115200);
/* enable uart */
setbits_le32(priv->base + UART_CTL, UART_CTL_EN);
bubblegum_early_debug(8);
return 0;
}
static const struct dm_serial_ops owl_serial_ops = {
.putc = owl_serial_putc,
.pending = owl_serial_pending,
.getc = owl_serial_getc,
.setbrg = owl_serial_setbrg,
};
static const struct udevice_id owl_serial_ids[] = {
{ .compatible = "actions,s900-serial", },
{ }
};
U_BOOT_DRIVER(serial_owl) = {
.name = "serial_owl",
.id = UCLASS_SERIAL,
.of_match = owl_serial_ids,
.probe = owl_serial_probe,
.ops = &owl_serial_ops,
.flags = DM_FLAG_PRE_RELOC,
.priv_auto_alloc_size = sizeof(struct owl_serial_priv),
};
u-boot device-tree移植
从本质上,device tree要做的事情很简单
就是把源代码中(不管是linux kernel或者u-boot)那些静态定义的struct device变量,替换为类似于自然语言的、人类可懂的、脚本性质的dts描述。
当然,为了适配设备模型,这些dts描述最终还是会转换为struct device变量。不过,大可放心,这个转换过程是由软件自动完成的,不需要驱动工程师操心。
最后,为了效率,软件不会直接解析dts描述(只有人才比较喜欢这种方式),而是解析由dts描述所生产的一个二进制文件(dtb)。dts转换为dtb,类似于C语言的编译过程,因此也不需要工程师操心。
u-boot DDR的初始化
1)软件从外部存储介质加载并执行的时候
a)第一个被执行的image,必须小于2KB(我们暂时称它为SPL,Secondary Program Loader)。
b)SPL在有限的size中,必须完成两个事情:初始化DDR;将后续的启动代码(如u-boot)从存储介质中copy到DDR中执行。
c)u-boot在DDR中运行(不再受限于系统资源),进行必要的初始化之后,将linux kernel copy到DDR中并执行。
d)linux kernel执行,并加载rootfs。
2)固件更新的时候(这里提供一种方案,将借助Android的fastboot,不唯一)
a)第一个被执行的image(SPL),必须小于SRAM的size(根据经验,Bubblegum-96平台,要小于70KB)。
b)通过ROM code的DFU程序,将SPL上传到SRAM并执行。
c)SPL初始化DDR,并将控制权重新交给ROM code的DFU程序。
d)通过ROM code的DFU程序,将u-boot(size不再受限)上传到DDR并执行。
e)u-boot中启动fastboot服务,通过fastboot协议,更新固件。
u-boot 的cmdline实现
- menuconifg:
CONFIG_CMDLINE:命令行模式开关
CONFIG_SYS_PROMPT:命令行模式提示符
CONFIG_HUSH_PARSER:使用hush shell 来对命令进行解析
BOOTDELAY:设置启动延时
run_main_loop(common/board.c)
|-> main_loop(common/main.c)
|-> setenv
|-> cli_init
|-> run_preboot_enviroment_commond
|-> bootdelay_process
|->s = env_get(bootdelay)
|-> store_bootdealy
|-> autoboot_commond
-> aboartboot()
|-> cli_loop()
kernel移植
u-boot在boot kernel的时候,要么使用传统的atags的方式,要么使用device tree的方式,向kernel传递参数。而kernel的ARM64代码不支持atags了,device tree就成了唯一选项了。
那么能不能暂时不给呢?不能,因为u-boot会罢工。
early consloe
autoboot
arm gic driver的移植
u-boot驱动模型
what
类似kernel,兼容不同设备
why
how
- menuconifg Device Drivers -> Generic Driver Options -> Enable Driver Model
data struct
global_data,管理着整个Uboot的全局变量,其中dm_root,dm_root_f,uclass_root用来管理整个DM模型。这几个变量代表什么意思呢?
dm_root:DM模型的根设备
dm_root_f:重定向前的根设备
uclass_root:uclass链表的头
这几个变量,最终要的作用就是:管理整个模型中的udevice设备信息和uclass驱动类。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· 别再用vector<bool>了!Google高级工程师:这可能是STL最大的设计失误
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 单元测试从入门到精通
· 上周热点回顾(3.3-3.9)