【uboot】uboot 2020.04 DM驱动模式 -- Demo体验

1. 前言

本篇文章是基于IMX6ULL平台uboot 2020.04源码分析DM驱动模式是如何使用的。IMX6ULL平台uboot源码下载可以参考这篇文章《【uboot】imx6ull uboot 2020.04源码下载和编译环境配置》

2. uboot的驱动模型简介

uboot引入了驱动模型(driver model)简称为DM,这种驱动模型为驱动的定义和访问接口提供了统一的方法。提高了驱动之间的兼容性以及访问的标准型。

uboot官网有对DM的使用介绍,地址如下:
https://u-boot.readthedocs.io/en/latest/develop/driver-model/design.html

在uboot源码里面有一个DM驱动模型的Demo,我们可以使用它来了解分析DM驱动模型是如何工作的。下图是Demo DM驱动简单的框图,如下:
在这里插入图片描述
Demo DM驱动模型涉及到的文件如下:

成员路径描述
U_BOOT_CMD(do_demo)./cmd/demo.c定义了一个demo的命令,可以在uboot命令行进行操作。
UCLASS_DRIVER(demo)./drivers/demo/demo-uclass.c给UCLASS_DEMO类型的设备提供统一的接口。
U_BOOT_DRIVER(demo_imx6ull_drv)./drivers/demo/demo-imx6ull.c根据设备的定义操作对应的硬件。
uclass_id./include/dm/uclass-id.h定义UCLASS类的ID

使能Demo DM驱动模型需要打开相关配置:

CONFIG_DM=y
CONFIG_CMD_DEMO=y
CONFIG_DM_DEMO=y
CONFIG_DM_DEMO_IMX6ULL=y

3. U_BOOT_CMD(do_demo)分析

通过U_BOOT_CMD定义了一个demo命令,该命令提供了4个功能:

  • hello:打印设备的字符串
  • light:操作硬件LED的开关
  • status:查看设备的状态
  • list:遍历UCLASS_DEMO绑定的所有设备

具体实现的代码如下:

// 定义命令参数对应的处理函数
static cmd_tbl_t demo_commands[] = {
	U_BOOT_CMD_MKENT(list, 0, 1, do_demo_list, "", ""),
	U_BOOT_CMD_MKENT(hello, 2, 1, do_demo_hello, "", ""),
	U_BOOT_CMD_MKENT(light, 2, 1, do_demo_light, "", ""),
	U_BOOT_CMD_MKENT(status, 1, 1, do_demo_status, "", ""),
};

static int do_demo(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
{
    // 通过命令行字符串获取对应的cmd的处理函数
	demo_cmd = find_cmd_tbl(argv[1], demo_commands, ARRAY_SIZE(demo_commands));
		
	devnum = simple_strtoul(argv[0], NULL, 10);
	ret = uclass_get_device(UCLASS_DEMO, devnum, &demo_dev); // 通过设备号获取对应的设备实体struct udevice
		
	ret = demo_cmd->cmd(demo_cmd, flag, argc, argv); //执行对应的处理函数
	
	return cmd_process_error(demo_cmd, ret);
}

U_BOOT_CMD(
	demo,   4,      1,      do_demo,
	"Driver model (dm) demo operations",
	"list                     List available demo devices\n"
	"demo hello <num> [<char>]     Say hello\n"
	"demo light [<num>]            Set or get the lights\n"
	"demo status <num>             Get demo device status\n"
	"demo list                     List available demo devices"
);

4. 执行命令demo list

执行命令demo list命令时,会遍历所有的UCLASS_DEMO类型的设备。具体的代码如下:

int do_demo_list(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
{
	struct udevice *dev;
	int i, ret;

	puts("Demo uclass entries:\n");

	for (i = 0, ret = uclass_first_device(UCLASS_DEMO, &dev);
	     dev;
	     ret = uclass_next_device(&dev)) {
		printf("entry %d - instance %08x, ops %08x, platdata %08x\n",
		       i++, (uint)map_to_sysmem(dev),
		       (uint)map_to_sysmem(dev->driver->ops),
		       (uint)map_to_sysmem(dev_get_platdata(dev)));
	}

	return cmd_process_error(cmdtp, ret);
}

运行结果如下:
在这里插入图片描述
通过以上的打印信息,我们可以了解到:

  • UCLASS_DEMO有4个设备绑定,
  • 4个不同instance地址对应4个不同的struct udevice设备实体地址。
  • 4个设备对应不同的platdata(平台数据)。
  • 4个设备使用同一个驱动,因为ops地址是同一地址。

这里的4个设备就是在device tree被定义的test_demo

/ {
	test_demo@0 {
		compatible = "fsl,imx6ull-demo";
		colour = "red";
		sides = <10>;
		character = <82>;
		led-gpios = <&gpio5 1 0>;
	};

	test_demo@1 {
		compatible = "fsl,imx6ull-demo";
		colour = "green";
		sides = <11>;
		character = <83>;
		led-gpios = <&gpio5 2 0>;
	};

	test_demo@2 {
		compatible = "fsl,imx6ull-demo";
		colour = "blue";
		sides = <12>;
		character = <84>;
		led-gpios = <&gpio5 3 0>;
	};

	test_demo@3 {
		compatible = "fsl,imx6ull-demo";
		colour = "yellow";
		sides = <13>;
		character = <85>;
		led-gpios = <&gpio5 4 0>;
	};
};

5. 执行命令demo hello

执行命令demo hello时,会打印当前设备信息。具体代码如下:

static int imx6ull_hello(struct udevice *dev, int ch)
{
	const struct dm_demo_pdata *pdata = dev_get_platdata(dev);
	struct imx6ull_data *data = dev_get_priv(dev);

	printf("Hello from %08x: %s %d\n", (uint)map_to_sysmem(dev), pdata->colour,
	       pdata->sides);

	data->num_chars = ch;

	return 0;
}

执行结果如下:
在这里插入图片描述
demo hello命令的参数0/1/2/3代表设备编号,通过打印信息可以确定UCLASS_DEMO绑定的4个设备就是device tree里面的4个设备。

6. 执行命令demo light

执行命令demo light时,会设置对应的GPIO的电压来控制LED开关。通过查看硬件LED连接到GPIO5_3,如下:
在这里插入图片描述
通过device tree定义的test_demo@2配置,使其与GPIO5_3关联。然后,发送下面的命令开关LED:

=> demo light 2 0   // 设置GPIO5_3为低电平,打开LED
=> demo light 2 1   // 设置GPIO5_3为高电平,关闭LED

具体代码如下:

static int imx6ull_set_light(struct udevice *dev, int light)
{
	struct imx6ull_data *priv = dev_get_priv(dev);
	struct gpio_desc *desc;
	int ret;
	int i;

	desc = priv->gpio_desc;
	for (i = 0; i < priv->gpio_count; i++, desc++) {
		uint mask = 1 << i;

		ret = dm_gpio_set_value(desc, light & mask);
		if (ret < 0)
			return ret;
	}

	return 0;
}

运行效果如下:
在这里插入图片描述

7. U_BOOT_DRIVER和UCLASS_DRIVER分析

U_BOOT_DRIVER(demo_imx6ull_drv)的定义解析如下:在这里插入图片描述
UCLASS_DRIVER(demo)的定义解析如下:
在这里插入图片描述
U_BOOT_DRIVER(demo_imx6ull_drv)UCLASS_DRIVER(demo)都是被定义到固定的代码段,在DM模型初始化扫描时会进行遍历绑定。DM模型初始化流程:
在这里插入图片描述
在这里插入图片描述
lists_bind_fdt()函数中,主要设备树里面的compatible属性和driver里面属性匹配成功后,就将其绑定:
在这里插入图片描述
在这里插入图片描述
到此udevice、driver和uclass绑定完成,app可以通过uclass_driver提供的方法来操作硬件。

8. 参考资料

涉及的相关代码如下:

  • imx6ull-14x14-evk-emmc-zc.dts是原始代码imx6ull-14x14-evk-emmc.dts基础上修改
  • mx6ull_14x14_evk_emmc_zc_defconfig是原始代码mx6ull_14x14_evk_emmc_defconfig基础上修改

在这里插入图片描述
涉及到的代码下载路径如下:
https://download.csdn.net/download/ZHONGCAI0901/19405287

  • 1
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值