本来经过上一篇文章后打算进行 EMMC 的研究,最终达到 EMMC boot 的目的,不过折腾了一天,还是没怎么搞明白,因为涉及到设备树和 DM,对这两个也不是特别懂,所以还是先写一些这方面例程,加深理解。
顺便把自己参考过的文章都记录一下:
drivers/xhr4412_driver/
该文件夹用来存放 xhr4412 板子的driverxhr4412_driver/led/
该文件夹用来存放 led driver 相关文件
led_uclass.c
led driver 抽象层led_gpio.c
通过 gpio 控制 led 的实际 driverxhr4412_driver/button/
该文件夹用来存放 button driver 相关文件
btn_uclass.c
button driver 抽象层btn_gpio.c
通过 gpio 获取 button 动作xhr4412_driver/Kconfig
配置文件xhr4412_driver/Makefile
include/xhr4412/led.h
led driver 提供的 dada structure 和 APIKconfig 文件依葫芦画瓢即可,这里默认配置 led driver。配置好后就可以 make menuconfig。
menu "xhr4412 Drivers Support"
config XHR_DRV_LED
bool "Enable xhr4412 led_uclass.c"
default y if TARGET_XHR4412
depends on TARGET_XHR4412
help
xhr4412 board's led driver
config XHR_DRV_LED_GPIO
bool "- led_gpio.c"
default y
depends on XHR_DRV_LED
config XHR_DRV_BTN
bool "Enable xhr4412 led_uclass.c"
depends on TARGET_XHR4412
help
xhr4412 board's button driver
config XHR_DRV_BTN_GPIO
bool "- btn_gpio.c"
default y
depends on XHR_DRV_BTN
endmenu
dts 添加两个 led 节点,这里是自己添加的,没有使用通用的 GPIO 的模式。
简单的将 led 需要的变量放在 dts 中,在 driver 中通过 API 来获取这些值,包括寄存器地址,第几个 GPIO。
xhr_led_1 {
compatible = "xhr-led-gpio";
led_name = "led_1";
reg_addr = <0x11000100>;
position = <0>; // GPL2_0
};
xhr_led_2 {
compatible = "xhr-led-gpio";
led_name = "led_2";
reg_addr = <0x11000060>;
position = <1>; // GPK1_1
};
定义在 led.h
头文件中。
led_ops_t
实际操作函数指针led_plat_data_t
uclass driver 所需数据struct led_ops_t {
int (*set_val)(struct udevice *dev, led_state_t val);
led_state_t (*get_val)(struct udevice *dev);
};
struct led_plat_data_t {
const char * name;
};
uclass 层提供所有类型的 led device driver 的抽象,相当于所有 led driver 的父类,各个子类再去实现真正的操作函数。
我们为了简单,实现 led 的 get 和 set 两个方法就可以了。
#define led_get_ops(dev) ((struct led_ops_t *)(dev)->driver->ops)
int led_set_val(struct udevice *dev, led_state_t val)
{
const struct led_ops_t * ops = led_get_ops(dev);
if(ops && ops->set_val)
return ops->set_val(dev, val);
return LED_FAULT;
}
led_state_t led_get_val(struct udevice *dev)
{
const struct led_ops_t * ops = led_get_ops(dev);
if(ops && ops->get_val)
return ops->get_val(dev);
return LED_FAULT;
}
UCLASS_DRIVER(xhr_led) = {
.id = UCLASS_XHR_LED,
.name = "xhr_led",
.post_bind = led_uclass_post_bind,
.per_device_platdata_auto_alloc_size = sizeof(struct led_plat_data_t),
};
该文件相当于 led 的一种子类,实现该子类特定的操作函数,该子类通过操作 GPIO 来实现 led 的功能。如果还有其他 led ,可以参考该子类架构添加子类。
主要实现子类的特定操作函数,probe、remove 等,对于我们的 led 实现特定的 ops 函数,get 和 set。
实现 id_table,将用来匹配 dts 中的 compatible
属性,只有匹配到了的 device 才会动态的生成 udevice 结构体进行 probe 操作。
struct led_gpio_priv_t
:解析 dts 后,将参数存入该结构中。仅给出 probe 函数实体。
struct led_gpio_priv_t {
struct e4412_gpio_regs * regs;
unsigned long pos;
const char * name;
};
static int led_gpio_set_val(struct udevice *dev, led_state_t val);
static led_state_t led_gpio_get_val(struct udevice *dev)
static const struct led_ops_t led_gpio_ops = {
.set_val = led_gpio_set_val,
.get_val = led_gpio_get_val,
};
static int led_gpio_probe(struct udevice *dev)
{
struct led_plat_data_t *plat = dev_get_uclass_platdata(dev);
struct led_gpio_priv_t *priv = dev_get_priv(dev);
int ret; u32 tmp;
priv->name = ofnode_read_string(dev->node, "led_name");
ret = ofnode_read_u32(dev->node, "reg_addr", &tmp);
if(ret) return ret;
priv->regs = (void*)tmp;
ret = ofnode_read_u32(dev->node, "position", &tmp);
if(ret) return ret;
priv->pos = 1 << tmp;
dprint("uclass=%s led=%s reg= %p pos= %d\n",
plat->name, priv->name, priv->regs, tmp);
priv->regs->con &= ~((0xF) << (tmp << 2));
priv->regs->con |= 1 << (tmp << 2); // 配置 gpio 为输出模式
priv->regs->pud &= ~((0x3) << (tmp << 1)); // disable pull-up/down
led_gpio_set_val(dev, LED_STD_ON); // gpio 输出高电平
return 0;
}
static int led_gpio_remove(struct udevice *dev);
static const struct udevice_id led_gpio_ids[] = {
{ .compatible = "xhr-led-gpio" }, { }
};
U_BOOT_DRIVER(led_gpio) = {
.name = "led_gpio_xhr",
.id = UCLASS_XHR_LED,
.of_match = led_gpio_ids,
.ops = &led_gpio_ops,
.probe = led_gpio_probe,
.remove = led_gpio_remove,
.priv_auto_alloc_size = sizeof(struct led_gpio_priv_t),
};
完成了以上程序编译通过后,我以为就可以成功通过 DM 架构点灯了,事实上,并不可以。。。
probe 函数并没有被执行,经过研究 u-boot code 发现想运行到 probe callback 还需要一些步骤。
发现总结如下:
board_r.c
文件中的 initr_dm()
最后 call 到 lists_bind_fdt()
仅仅是扫描设备树,然后查找 u-boot 中是否有 compatible
的 struct driver
,能够匹配到才会动态生成 struct udevice
。CONFIG_OF_LIVE
这个配置宏没有太搞明白,不太明白什么用,我打开后无限重启,百度发现也有类似现象:crash with CONFIG_OF_LIVE需要添加初始化 code 进行 probe:
int xhr_led_uclass_init(void)
{
int ret, i;
struct uclass *uc;
struct udevice *dev;
ret = uclass_get(UCLASS_XHR_LED, &uc);
if (ret)
return ret;
uclass_foreach_dev(dev, uc) {
ret = device_probe(dev);
if (ret)
pr_err("%s - probe failed: %d\n", dev->name, ret);
}
return 0;
}
主要是使用 device_probe()
函数对 led driver probe callback 进行调用。
修改好后可以成功 probe 到 led device,并且板子上两个灯也已经点亮。
后面最重要的可能就是 emmc 的分区、烧写、boot 了,也不知道自己能不能移植成功,毕竟不是很懂 emmc、文件系统、adb、fastboot 等东东。
走一步看一步吧,希望能调出来,最后 boot 最新的 linux kernel。