文章目录
- 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. 文件层次
drivers/xhr4412_driver/
该文件夹用来存放 xhr4412 板子的driverxhr4412_driver/led/
该文件夹用来存放 led driver 相关文件led_uclass.c
led driver 抽象层led_gpio.c
通过 gpio 控制 led 的实际 driver
xhr4412_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 和 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
头文件中。
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;
};
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 还需要一些步骤。
发现总结如下:
board_r.c
文件中的initr_dm()
最后 call 到lists_bind_fdt()
仅仅是扫描设备树,然后查找 u-boot 中是否有compatible
的struct driver
,能够匹配到才会动态生成struct udevice
。- 如上所述,probe 操作需要我们添加 code 来完成,可以参照其他已有模块,比如 mmc 等,来添加初始化 code。
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。