移植 u-boot-2020.07 到 iTOP-4412(六)DM

文章目录

  • 1. 文件层次
  • 2. 配置文件
  • 3. dts
  • 6. data structure
  • 5. led_uclass.c
  • 6. led_gpio.c
  • 7. probe
  • 8. Test
  • 9. postscript

   本来经过上一篇文章后打算进行 EMMC 的研究,最终达到 EMMC boot 的目的,不过折腾了一天,还是没怎么搞明白,因为涉及到设备树和 DM,对这两个也不是特别懂,所以还是先写一些这方面例程,加深理解。

顺便把自己参考过的文章都记录一下:

  • [uboot] (番外篇)uboot 驱动模型
  • 设备树dts详解

1. 文件层次

  1. drivers/xhr4412_driver/ 该文件夹用来存放 xhr4412 板子的driver
  2. xhr4412_driver/led/ 该文件夹用来存放 led driver 相关文件
    1. led_uclass.c led driver 抽象层
    2. led_gpio.c 通过 gpio 控制 led 的实际 driver
  3. xhr4412_driver/button/ 该文件夹用来存放 button driver 相关文件
    1. btn_uclass.c button driver 抽象层
    2. btn_gpio.c 通过 gpio 获取 button 动作
  4. xhr4412_driver/Kconfig 配置文件
  5. xhr4412_driver/Makefile
  6. include/xhr4412/led.h led driver 提供的 dada structure 和 API

2. 配置文件

   Kconfig 文件依葫芦画瓢即可,这里默认配置 led driver。配置好后就可以 make menuconfig。

移植 u-boot-2020.07 到 iTOP-4412(六)DM_第1张图片

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

3. dts

   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
	};

6. data structure

定义在 led.h 头文件中。

  1. led_ops_t 实际操作函数指针
  2. 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;
};

5. led_uclass.c

   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),
};

6. led_gpio.c

   该文件相当于 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),
};

7. probe

   完成了以上程序编译通过后,我以为就可以成功通过 DM 架构点灯了,事实上,并不可以。。。

   probe 函数并没有被执行,经过研究 u-boot code 发现想运行到 probe callback 还需要一些步骤。

发现总结如下:

  1. board_r.c 文件中的 initr_dm() 最后 call 到 lists_bind_fdt() 仅仅是扫描设备树,然后查找 u-boot 中是否有 compatiblestruct driver,能够匹配到才会动态生成 struct udevice
  2. 如上所述,probe 操作需要我们添加 code 来完成,可以参照其他已有模块,比如 mmc 等,来添加初始化 code。
  3. 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 进行调用。

8. Test

修改好后可以成功 probe 到 led device,并且板子上两个灯也已经点亮。

移植 u-boot-2020.07 到 iTOP-4412(六)DM_第2张图片

9. postscript

   后面最重要的可能就是 emmc 的分区、烧写、boot 了,也不知道自己能不能移植成功,毕竟不是很懂 emmc、文件系统、adb、fastboot 等东东。

   走一步看一步吧,希望能调出来,最后 boot 最新的 linux kernel。

你可能感兴趣的:(移植 u-boot-2020.07 到 iTOP-4412(六)DM)