蜗窝科技的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驱动类。

posted @ 2024-03-26 15:45  skyycj  阅读(5)  评论(0编辑  收藏  举报