当前位置: 代码迷 >> 综合 >> 移植 u-boot-2020.07 到 iTOP-4412(六)DM
  详细解决方案

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

热度:0   发布时间:2024-02-12 17:05:54.0

文章目录

  • 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。

在这里插入图片描述

menu "xhr4412 Drivers Support"config XHR_DRV_LEDbool "Enable xhr4412 led_uclass.c"default y if TARGET_XHR4412depends on TARGET_XHR4412helpxhr4412 board's led driverconfig XHR_DRV_LED_GPIObool "-  led_gpio.c"default ydepends on XHR_DRV_LEDconfig XHR_DRV_BTNbool "Enable xhr4412 led_uclass.c"depends on TARGET_XHR4412helpxhr4412 board's button driverconfig XHR_DRV_BTN_GPIObool "-  btn_gpio.c"default ydepends on XHR_DRV_BTNendmenu

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/downled_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;/** Try to add them in sequence order. Really with driver model we* should allow holes, but the current MMC list does not allow that.* So if we request 0, 1, 3 we will get 0, 1, 2.*/for (i = 0; ; i++) {ret = uclass_get_device_by_seq(UCLASS_XHR_LED, i, &dev);if (ret == -ENODEV)break;}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,并且板子上两个灯也已经点亮。

在这里插入图片描述

9. postscript

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

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

  相关解决方案