接上一篇aw9523驱动,由于板卡上的lcm的上电需要使用到aw9523扩展出来的gpio,而开机logo需要在uboot就开始显示,则需要在uboot中实现aw9523的DM模型的驱动
关于DM模型可以参考 超详细【Uboot驱动开发】(三)Uboot驱动模型 和 uboot 驱动模型
这边文章仅描述aw9523 在uboot中的过程
/*
* (C) Copyright 2020 Rockchip Electronics Co., Ltd
*
* SPDX-License-Identifier: GPL-2.0+
*/
#include <common.h>
#include <dm.h>
#include <i2c.h>
#include <asm/gpio.h>
struct dm_aw9523_ops {
int (*get_value)(unsigned int offset);
void (*set_value)(unsigned int offset, int value);
};
struct aw9523_gpio {
struct udevice *dev;
struct gpio_desc reset_gpio;
};
static struct aw9523_gpio *aw9523_client = NULL;
/* aw9523 register */
#define AW9523_REG_CHIPID 0x10
#define AW9523_LED_MODE_P0 0x12
#define AW9523_LED_MODE_P1 0x13
#define AW9523_LED_MODE 0x00
#define AW9523_CHIPID 0x23
#define AW9523_REG_MAX 0x7F
#define AW9523_REG_BRIGHTNESS_BASE 0x20
int aw9523_i2c_write(struct aw9523_gpio *aw9523_gpio, u8 reg, u8 val)
{
int ret;
u8 buf[2];
struct i2c_msg msg;
struct dm_i2c_chip *chip = dev_get_parent_platdata(aw9523_gpio->dev);
buf[0] = reg;
buf[1] = val;
msg.addr = chip->chip_addr;
msg.flags = 0;
msg.len = 2;
msg.buf = buf;
ret = dm_i2c_xfer(aw9523_gpio->dev, &msg, 1);
if (ret) {
printf("aw9523 i2c write failed: %d\n", ret);
return ret;
}
return 0;
}
int aw9523_i2c_read(struct aw9523_gpio *aw9523_gpio, u8 reg, u8 *val)
{
int ret;
u8 data;
struct dm_i2c_chip *chip = dev_get_parent_platdata(aw9523_gpio->dev);
struct i2c_msg msg[] = {
{
.addr = chip->chip_addr,
.flags = 0,
.buf = (u8 *)®,
.len = 1,
}, {
.addr = chip->chip_addr,
.flags = I2C_M_RD,
.buf = (u8 *)&data,
.len = 1,
}
};
ret = dm_i2c_xfer(aw9523_gpio->dev, msg, 2);
if (ret) {
printf("aw9523 i2c read failed: %d\n", ret);
return ret;
}
*val = data;
return 0;
}
static int aw9523_get_value(struct udevice *dev, int port)
{
int ret;
u8 data;
int value;
struct aw9523_gpio *gpios = dev_get_priv(dev);
if (port < 0 || port > 15) {
printf("<%s> invalid port: %d\n", __func__, port);
return -EINVAL;
}
ret = (u8)aw9523_i2c_read(gpios, 0x02 + port / 8, &data);
if(ret)
{
return -1;
}
value = (data >> port) & 1;
printf("====>reg %x value %x\n", 0x02 + port / 8, value);
return value;
}
static int aw9523_set_value(struct udevice *dev, int port,
int value)
{
int ret;
u8 val;
struct aw9523_gpio *gpios = dev_get_priv(dev);
if (port < 0 || port > 15) {
printf("<%s> invalid port: %d\n", __func__, port);
return -1;
}
if (value != 0 && value != 1) {
printf("<%s> invalid value\n", __func__);
return -1;
}
ret = (u8)aw9523_i2c_read(gpios, 0x02 + port / 8, &val);
if (value == 0)
val &= ~(1 << (port % 8));
else
val |= 1 << (port % 8);
ret = aw9523_i2c_write(gpios, port, value);
if(ret)
{
printf("aw9523 i2c write failed: %d\n", ret);
return ret;
}
return 0;
}
int aw9523_port_get(unsigned int offset)
{
int value;
if(aw9523_client)
{
value = aw9523_get_value(aw9523_client->dev, offset);
return value;
}
return 0;
}
void aw9523_port_set(unsigned int offset, int value)
{
if(aw9523_client)
{
aw9523_set_value(aw9523_client->dev, offset, value);
}
}
static const struct dm_aw9523_ops aw9523_gpio_ops = {
.get_value = aw9523_port_get,
.set_value = aw9523_port_set,
};
static int aw9523_probe(struct udevice *dev)
{
int ret;
struct aw9523_gpio *aw9523_gpio = dev_get_priv(dev);
aw9523_client = aw9523_gpio;
aw9523_gpio->dev = dev;
ret = gpio_request_by_name(dev, "aw9523_reset-gpios", 0,
&aw9523_gpio->reset_gpio, GPIOD_IS_OUT);
if (ret) {
printf("Cannot get wakeup_pin GPIO: %d\n", ret);
return ret;
}
if (dm_gpio_is_valid(&aw9523_gpio->reset_gpio))
{
dm_gpio_set_value(&aw9523_gpio->reset_gpio, 1);
mdelay(20);
dm_gpio_set_value(&aw9523_gpio->reset_gpio, 0);
mdelay(30);
dm_gpio_set_value(&aw9523_gpio->reset_gpio, 1);
mdelay(20);
}
// set P0 port as push-pull mode
aw9523_i2c_write(aw9523_gpio, 0x11, 0x10);
aw9523_i2c_write(aw9523_gpio, 0x12, 0xff);
aw9523_i2c_write(aw9523_gpio, 0x13, 0xff);
// P0 and P1 port output mode
aw9523_i2c_write(aw9523_gpio, 0x04, 0x00);
aw9523_i2c_write(aw9523_gpio, 0x05, 0x00);
// disable interrupt function of P0 and P1 port
aw9523_i2c_write(aw9523_gpio, 0x06, 0xff);
aw9523_i2c_write(aw9523_gpio, 0x07, 0xff);
// P0 and P1 port output low level
aw9523_i2c_write(aw9523_gpio, 0x02, 0x00);
aw9523_i2c_write(aw9523_gpio, 0x03, 0x00);
/*
ret = gpio_request_by_name(dev, "aw9523_int-gpios", 0,
&aw9523_gpio->pwr_up_gpio, GPIOD_IS_OUT);
if (ret) {
printf("Cannot get pwr_up_gpio GPIO: %d\n", ret);
return ret;
}
*/
printf("====>aw9523 gpio probe successful!!");
return 0;
}
static const struct udevice_id aw9523_of_match[] = {
{ .compatible = "awinic,aw9523_gpio" },
{}
};
U_BOOT_DRIVER(aw9523_gpio) = {
.name = "aw9523_gpio",
.id = UCLASS_I2C_GENERIC,
.of_match = aw9523_of_match,
.ops = &aw9523_gpio_ops,
.probe = aw9523_probe,
.bind = dm_scan_fdt_dev,
.priv_auto_alloc_size = sizeof(struct aw9523_gpio),
};
aw9523由i2c控制,所以这里id为UCLASS_I2C_GENERIC,of_match 中的关键字应与dts中的compatible一致,bind函数固定为dm_scan_fdt_dev,在kconfig和Makefile中添加编译项,在defconfig中添加编译模块需要的宏
如需debug需要在defconfig中修改CONFIG_BOOTDELAY时间,进入uboot 命令模式,打开CONFIG_CMD_I2C=y,不然在ubot命令模式下没有i2c测试命令,关于i2c命令可以参考《uboot中 使用i2c》
使用dm tree 可以看到注册上的 uclass,udevice
这里好像有点问题,第三列显示aw9523@59没有probed,但现在lcm显示了,所以电源肯定是上电完成了,之前只有一个aw9523时显示的probed,由于在rockchip rk3588平台uboot中这部分注册没有log,我也不知道怎么打开,所以暂时没管
i2c 命令测试,相关命令代码在uboot/cmd/i2c.c中
运行了上面的命令后,aw9523@59显示probed,有点不清楚这是怎么个机制,有知道的同学望告知一下
没有probe的原因搞清楚了
uboot-dm probe 需要主动去调用下。
参考 https://cloud.tencent.com/developer/article/1571947
#define aw9523_get_ops(dev) ((struct dm_aw9523_ops *)(dev)->driver->ops)
struct udevice *aw9523_dev;
const struct dm_aw9523_ops *ops = NULL;
ret = uclass_get_device_by_name(UCLASS_I2C_GENERIC,"aw9523@59", &aw9523_dev);
if(ret < 0)
printf(">>>>>get device fail\n");
if(aw9523_dev)
ops = aw9523_get_ops(aw9523_dev);
.......
if(ops->set_value)
{
//enable aw9523 gpio expander port0~2 for LCM power
ops->set_value(0, 1);
mdelay(20);
ops->set_value(1, 1);
mdelay(20);
ops->set_value(2, 1);
mdelay(20);
}