aw9523 gpio expander扩展芯片

本文介绍AW9523 GPIO扩展芯片的驱动编写及使用方法,包括驱动代码实现、配置文件示例及如何在其他模块中引用。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

最近在调试新板卡,由于gpio不够用,使用了aw9523 gpio扩展芯片,调试记录如下

1. 驱动代码

驱动可参考https://blog.youkuaiyun.com/zuoanyouzhuan_/article/details/126286937

此处我也贴出我修改过的 ,代码还没有完善,比如aw9523_gpio_direction_out函数

// SPDX-License-Identifier: GPL-2.0+
/*
 * gpiolib support for Wolfson WM835x PMICs
 *
 * Copyright 2009 Wolfson Microelectronics PLC.
 *
 * Author: Mark Brown <broonie@opensource.wolfsonmicro.com>
 *
 */

#include <linux/kernel.h>
#include <linux/slab.h>
#include <linux/module.h>
#include <linux/gpio/driver.h>
#include <linux/delay.h>
#include <linux/i2c.h>
#include <linux/init.h>
#include <linux/leds.h>
#include <linux/mutex.h>
#include <linux/slab.h>
#include <linux/gpio/consumer.h>
#include <linux/of_gpio.h>
#include <linux/gpio.h>
#include <linux/regulator/consumer.h>

/* 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

struct aw9523_gpio {
	int aw9523_init_finished;
	struct gpio_desc *aw9523_rst_gpio;
	struct i2c_client *aw9523_client;
	struct gpio_chip aw9523_gpio_chip;
	struct device dev;
	spinlock_t lock;
};

struct gpio_chip *aw9523_chip = NULL;

static u8 reg_read(struct aw9523_gpio *aw9523_gpio_dev, u8 reg)
{
        u8 buf;
        int ret;
        struct i2c_msg msgs[] = {
                {
                        .addr  = aw9523_gpio_dev->aw9523_client->addr,
                        .flags = 0,
                        .len   = 1,
                        .buf   = &reg,
                }, {
                        .addr  = aw9523_gpio_dev->aw9523_client->addr,
                        .flags = I2C_M_RD,
                        .len   = 1,
                        .buf   = &buf,
                },
        };

        ret = i2c_transfer(aw9523_gpio_dev->aw9523_client->adapter, msgs, ARRAY_SIZE(msgs));
        if (ret < 0) {
                dev_err(&aw9523_gpio_dev->aw9523_client->dev, "aw9523 Reading register %x from %x failed\n",
                         reg, aw9523_gpio_dev->aw9523_client->addr);
                return ret;
        }

        return buf;
}

static int aw9523_read_reg(struct aw9523_gpio *aw9523_gpio_dev, u8 reg)
{
	int rc;

	rc = reg_read(aw9523_gpio_dev,reg);
	
	dev_err(&aw9523_gpio_dev->aw9523_client->dev,"aw9523 read reg = %02x, value = %02x****\n",reg,rc);
	rc = i2c_smbus_read_byte_data(aw9523_gpio_dev->aw9523_client, reg);
	if (rc < 0) {
		dev_err(&aw9523_gpio_dev->aw9523_client->dev,
			"<%s> can't read from %02x, i2c addr= %02x\n", __func__, reg, aw9523_gpio_dev->aw9523_client->addr);
		return -rc;
	}
	return rc;
}

static int aw9523_write_reg(struct aw9523_gpio *aw9523_gpio_dev, u8 reg, u8 val)
{
	int rc;

	rc = i2c_smbus_write_byte_data(aw9523_gpio_dev->aw9523_client, reg,
				       val);
	if (rc < 0) {
		dev_err(&aw9523_gpio_dev->aw9523_client->dev,
			"<%s> can't write %02x to %02x\n", __func__, val, reg);
		return -rc;
	}
	return 0;
}

static int aw9523_check_chipid(struct aw9523_gpio *aw9523_gpio_dev)
{
	u8 val;
	u8 cnt;

	for (cnt = 5; cnt > 0; cnt--) {
		val = aw9523_read_reg(aw9523_gpio_dev, AW9523_REG_CHIPID);
		dev_err(&aw9523_gpio_dev->aw9523_client->dev,
			"<%s> aw9523 chip id %0x\n", __func__, val);
		if (val == AW9523_CHIPID)
			return 0;
	}
	return -EINVAL;
}
static int aw9523_gpio_get_value(struct aw9523_gpio *aw9523_gpio_dev, unsigned port)
{
	u8 val;
	int value;

	if (aw9523_gpio_dev->aw9523_init_finished == 0) {
		dev_err(&aw9523_gpio_dev->aw9523_client->dev,
			"<%s> aw9523 is not inited\n", __func__);
		return -EPERM;
	}

	if (port < 0 || port > 15) {
		dev_err(&aw9523_gpio_dev->aw9523_client->dev,
			"<%s> invalid port: %d\n", __func__, port);
		return -EINVAL;
	}

	val = (u8)aw9523_read_reg(aw9523_gpio_dev, 0x02 + port / 8);
	value = (val >> port) & 1;
	return value;
}

static int aw9523_gpio_set_value(struct aw9523_gpio *aw9523_gpio_dev, unsigned port,
			  int value)
{
	u8 val;

	if (aw9523_gpio_dev->aw9523_init_finished == 0) {
		dev_err(&aw9523_gpio_dev->aw9523_client->dev,
			"<%s> aw9523 is not inited\n", __func__);
		return -EPERM;
	}

	if (port < 0 || port > 15) {
		dev_err(&aw9523_gpio_dev->aw9523_client->dev,
			"<%s> invalid port: %d\n", __func__, port);
		return -EINVAL;
	}

	if (value != 0 && value != 1) {
		dev_err(&aw9523_gpio_dev->aw9523_client->dev,
			"<%s> invalid value\n", __func__);
		return -EINVAL;
	}

	val = (u8)aw9523_read_reg(aw9523_gpio_dev, 0x02 + port / 8);

	if (value == 0)
		val &= ~(1 << (port % 8));
	else
		val |= 1 << (port % 8);

	dev_dbg(&aw9523_gpio_dev->aw9523_client->dev,
			"<%s> begin to write reg\n", __func__);

	return aw9523_write_reg(aw9523_gpio_dev, 0x02 + port / 8, val);
}

int aw9523_gpio_get(struct gpio_chip *chip, unsigned offset)
{
	int value;
	unsigned long flags;
	struct aw9523_gpio *aw9523_gpio_dev =
		container_of(chip, struct aw9523_gpio, aw9523_gpio_chip);

	value = aw9523_gpio_get_value(aw9523_gpio_dev, offset);

	return value;
}

void aw9523_gpio_set(struct gpio_chip *chip, unsigned offset, int value)
{
	unsigned long flags;
	struct aw9523_gpio *aw9523_gpio_dev =
		container_of(chip, struct aw9523_gpio, aw9523_gpio_chip);
	aw9523_gpio_set_value(aw9523_gpio_dev, offset, value);
}

static int aw9523_gpio_direction_out(struct gpio_chip *chip, unsigned offset,
				     int value)
{
	unsigned long flags;

	struct aw9523_gpio *aw9523_gpio_dev =
		container_of(chip, struct aw9523_gpio, aw9523_gpio_chip);

	//spin_lock_irqsave(&aw9523_gpio_dev->lock, flags);
	dev_err(&aw9523_gpio_dev->aw9523_client->dev,
		"%s:offset = %d,value = %d\n", __func__, offset, value);

	aw9523_gpio_set_value(aw9523_gpio_dev, offset, value);
	//spin_unlock_irqrestore(&aw9523_gpio_dev->lock, flags);
	return 0;
}

#if 0
int aw9523_port_get(unsigned int offset)
{
	int value;
	unsigned long flags;
	struct aw9523_gpio *aw9523_gpio_dev =
		container_of(aw9523_chip, struct aw9523_gpio, aw9523_gpio_chip);

	value = aw9523_gpio_get_value(aw9523_gpio_dev, offset);

	return value;
}
EXPORT_SYMBOL_GPL(aw9523_port_get);

void aw9523_port_set(unsigned int offset, int value)
{
	unsigned long flags;
	struct aw9523_gpio *aw9523_gpio_dev =
		container_of(aw9523_chip, struct aw9523_gpio, aw9523_gpio_chip);
	aw9523_gpio_set_value(aw9523_gpio_dev, offset, value);
}
EXPORT_SYMBOL_GPL(aw9523_port_set);
#endif

static ssize_t aw9523_gpio_show(struct device* dev, struct device_attribute* attr, char* buf)
{
	int rc = -1;
	struct i2c_client *client =
                container_of(dev, struct i2c_client, dev);
    struct aw9523_gpio *aw9523_gpio_dev =
                i2c_get_clientdata(client);

	rc = aw9523_read_reg(aw9523_gpio_dev, 0x02);
	if(rc < 0)
	{
		dev_err(&aw9523_gpio_dev->aw9523_client->dev,
			"<%s> can't read from %02x, i2c addr= %02x\n", __func__, 0x02, aw9523_gpio_dev->aw9523_client->addr);
	}
	else 
	{
		printk("<%s> read from %02x, value= %02x\n", __func__, 0x02, rc);
	}

	rc = aw9523_read_reg(aw9523_gpio_dev, 0x03);
	if(rc < 0)
	{
		dev_err(&aw9523_gpio_dev->aw9523_client->dev,
			"<%s> can't read from %02x, i2c addr= %02x\n", __func__, 0x03, aw9523_gpio_dev->aw9523_client->addr);
	}
	else 
	{
		printk("<%s> read from %02x, value= %02x\n", __func__, 0x03, rc);
	}

	return 0;
}

static ssize_t aw9523_gpio_store(struct device *dev,
               struct device_attribute *attr, const char *buf, size_t count)
{
		int rc;
		int port;
		int value;
		char port_buf[5];
		char value_buf[5];

		struct i2c_client *client =
					container_of(dev, struct i2c_client, dev);
		struct aw9523_gpio *aw9523_gpio_dev =
					i2c_get_clientdata(client);

		if (count < 2) {
				pr_err("<%s> invalid write string: %s\n", __func__, buf);
				return -EINVAL;
		}
		sscanf(buf,"%s %s",port_buf,value_buf);
		rc = kstrtouint(port_buf, 10, &port);
		rc = kstrtouint(value_buf, 10, &value);

		dev_err(dev, "%s:bing port = %d value = %d addr = %02x\n",__func__,port,value,aw9523_gpio_dev->aw9523_client->addr);
		rc = aw9523_gpio_set_value(aw9523_gpio_dev, port, value);
		return (rc == 0) ? count : -EINVAL;
}

static DEVICE_ATTR(aw9523_gpio, 0664, aw9523_gpio_show, aw9523_gpio_store);

static ssize_t aw9523_reg_show(struct device* dev, struct device_attribute* attr, char* buf)
{
	int rc = -1;
	int reg = 0;
	struct i2c_client *client =
                container_of(dev, struct i2c_client, dev);
    struct aw9523_gpio *aw9523_gpio_dev =
                i2c_get_clientdata(client);

	while (reg < 12)
	{
		rc = aw9523_read_reg(aw9523_gpio_dev, reg);
		if(rc < 0)
		{
			dev_err(&aw9523_gpio_dev->aw9523_client->dev,
				"<%s> can't read from %02x, i2c addr= %02x\n", __func__, reg, aw9523_gpio_dev->aw9523_client->addr);
		}
		else 
		{
			printk("<%s> read from %02x, value= %02x\n", __func__, reg, rc);
		}
		reg++;
	}

	return 0;
}

static DEVICE_ATTR(aw9523_reg, S_IRUSR|S_IRGRP|S_IROTH, aw9523_reg_show, NULL);

static int aw9523_gpio_probe(struct i2c_client *client,
			     const struct i2c_device_id *id)
{
	int ret;
	enum of_gpio_flags flags;
	int i;
	struct aw9523_gpio *aw9523_gpio_dev;
	
	aw9523_gpio_dev = devm_kzalloc(
		&client->dev, (sizeof(struct aw9523_gpio)), GFP_KERNEL);
	if (!aw9523_gpio_dev)
		return -ENOMEM;

	aw9523_gpio_dev->aw9523_client = client;
	aw9523_gpio_dev->aw9523_rst_gpio =
		devm_gpiod_get_optional(&client->dev, "aw9523_reset", 0);
	if (!aw9523_gpio_dev->aw9523_rst_gpio) {
		dev_err(&client->dev, "invalid  reset gpio\n");
		return -1;
	} else {
		gpiod_direction_output(aw9523_gpio_dev->aw9523_rst_gpio, 1);
		msleep(20);
		gpiod_direction_output(aw9523_gpio_dev->aw9523_rst_gpio, 0);
		msleep(30);
		gpiod_direction_output(aw9523_gpio_dev->aw9523_rst_gpio, 1);
		msleep(20);
	}

	aw9523_gpio_dev->aw9523_gpio_chip.label = "aw_gpio0";
	aw9523_gpio_dev->aw9523_gpio_chip.owner = THIS_MODULE;
	aw9523_gpio_dev->aw9523_gpio_chip.direction_output =
		aw9523_gpio_direction_out;
	aw9523_gpio_dev->aw9523_gpio_chip.set = aw9523_gpio_set;
	aw9523_gpio_dev->aw9523_gpio_chip.get = aw9523_gpio_get;
	aw9523_gpio_dev->aw9523_gpio_chip.ngpio = 16;
	aw9523_gpio_dev->aw9523_gpio_chip.parent = &client->dev;
	aw9523_gpio_dev->aw9523_gpio_chip.base = -1;
	aw9523_chip = &(aw9523_gpio_dev->aw9523_gpio_chip);

	ret = devm_gpiochip_add_data(&client->dev,
				     &(aw9523_gpio_dev->aw9523_gpio_chip),
				     &(aw9523_gpio_dev->aw9523_gpio_chip));
	if (ret) {
		dev_err(&client->dev, "devm_gpiochip_add_data error\n");
		return -EINVAL;
	}

	ret = aw9523_check_chipid(aw9523_gpio_dev);
	if (ret) {
		dev_err(&client->dev, "Check chip id error\n");
		return -EINVAL;
	}
	// set P0 port as push-pull mode
	aw9523_write_reg(aw9523_gpio_dev, 0x11, 0x10);
	aw9523_write_reg(aw9523_gpio_dev, 0x12, 0xff);
	aw9523_write_reg(aw9523_gpio_dev, 0x13, 0xff);
	// P0 and P1 port output mode
	aw9523_write_reg(aw9523_gpio_dev, 0x04, 0x00);
	aw9523_write_reg(aw9523_gpio_dev, 0x05, 0x00);
	// disable interrupt function of P0 and P1 port
	aw9523_write_reg(aw9523_gpio_dev, 0x06, 0xff);
	aw9523_write_reg(aw9523_gpio_dev, 0x07, 0xff);
	// P0 and P1 port output low level
	aw9523_write_reg(aw9523_gpio_dev, 0x02, 0x00);
	aw9523_write_reg(aw9523_gpio_dev, 0x03, 0x00);

	
	spin_lock_init(&aw9523_gpio_dev->lock);
	aw9523_gpio_dev->aw9523_init_finished = 1;
	i2c_set_clientdata(client, aw9523_gpio_dev);
	
	ret = device_create_file(&client->dev, &dev_attr_aw9523_gpio);
	if (ret) {
			pr_err("<%s> create file aw9523_gpio failed\n", __func__);
			return -EPERM;
	}

	ret = device_create_file(&client->dev, &dev_attr_aw9523_reg);
	if (ret) {
			pr_err("<%s> create file aw9523_reg failed\n", __func__);
			return -EPERM;
	}
	dev_err(&client->dev, "aw9523_probe succeeded\n");

	return 0;
}

static int aw9523_gpio_remove(struct i2c_client *client)
{
	return 0;
}

static const struct of_device_id aw9523_match_table[] = {
	{
		.compatible = "awinic,aw9523_gpio",
	},
	{},
};
MODULE_DEVICE_TABLE(of, aw9523_match_table);

static const struct i2c_device_id aw9523_id[] = {
	{ "awinic,aw9523_gpio", 0 },
	{},
};
MODULE_DEVICE_TABLE(i2c, aw9523_id);

static struct i2c_driver aw9523_driver = {
	.driver = {
		.name = "aw9523-gpio",
		.owner		= THIS_MODULE,
		.of_match_table = aw9523_match_table,
	},
	.probe    = aw9523_gpio_probe,
	.remove   = aw9523_gpio_remove,
	.id_table = aw9523_id,
};

module_i2c_driver(aw9523_driver);
MODULE_LICENSE("GPL V2");

2. 配置gpio

dts设置如下


&i2c6 {
	status = "okay";

	aw_gpio0:aw9523@59 {
		label = "aw_gpio0";
		compatible = "awinic,aw9523_gpio";
		reg = <0x59>;
		pinctrl-names = "default";
		pinctrl-0 = <&aw_gpio0_reset>;
		aw9523_reset-gpios = <&gpio3 RK_PB1 GPIO_ACTIVE_HIGH>;
		aw9523_int-gpios = <&gpio3 RK_PA4 GPIO_ACTIVE_HIGH>;
		gpio-controller;
		#gpio-cells = <2>;
		#address-cells = <1>;
		#size-cells = <1>;
		
/*		interrupt-parent = <&gpio3>;
		interrupt-controller;
		#interrupt-cells = <2>;
		interrupts = <4 8>;
*/
	};

由于现在仅仅使用AW9523B的gpio功能,不使用LED或中断功能,所以dts里和代码里没有设置为终端控制器

3. 使用gpio

使用时可以使用EXPORT_SYMBOL_GPL导出函数,在其他模块使用或者在dts中直接引用,因为已经在驱动中devm_gpiochip_add_data注册


&dsi0_panel {
	vbat-gpios = <&aw_gpio0 0 GPIO_ACTIVE_HIGH>; //p00对应的gpio,aw9523有两个port P0,P1,每个port 有8个gpio
	vci-gpios = <&aw_gpio0 1 GPIO_ACTIVE_HIGH>;//p01对应的gpio
	vio-gpios = <&aw_gpio0 2 GPIO_ACTIVE_HIGH>;
	reset-gpios = <&gpio2 RK_PB4 GPIO_ACTIVE_HIGH>;
	enable-gpios = <&gpio3 RK_PC2 GPIO_ACTIVE_HIGH>;
	pinctrl-names = "default";
	pinctrl-0 = <&lcd_rst_gpio>;
};

驱动中实现的的aw9523_gpio_set和aw9523_gpio_get以及aw9523_gpio_direction_out实际上就对应操作gpio的通用函数,如下可以看到调用流程

设置gpio时会调用gpio扩展芯片的中gpio_chip.set/get函数


rk3x_i2c_xfer_common+0x3cc/0x630
[    9.711843][  T167]  rk3x_i2c_xfer+0x18/0x28
[    9.711850][  T167]  __i2c_transfer+0x254/0x79c
[    9.711857][  T167]  i2c_transfer+0xa4/0x100
[    9.711866][  T167]  aw9523_read_reg+0x78/0x13c
[    9.711871][  T167]  aw9523_gpio_set_value+0x98/0x158
[    9.711878][  T167]  aw9523_gpio_set+0x48/0x70
[    9.711886][  T167]  gpiod_set_raw_value_commit+0x64/0x138
[    9.711892][  T167]  gpiod_set_value+0x74/0x118

相关文件以及源码可以通过链接下载,里面包含可aw9523 和 tca6424 扩展gpio

另外有些gpio扩展芯片驱动可能需要在uboot中使用,下篇通过我自己的理解写一下

### AW9523B 驱动代码示例 以下是基于 U-Boot DM 框架实现的 AW9523B GPIO 扩展器驱动代码示例。该代码展示了如何初始化设备并设置 GPIO 的值。 #### 初始化部分 ```c #include <common.h> #include <dm.h> #include <asm-generic/gpio.h> #define AW9523_REG_OUTPUT_PORT0 0x0A #define AW9523_REG_OUTPUT_PORT1 0x0B static int aw9523_probe(struct udevice *dev) { struct dm_aw9523_platdata *plat = dev_get_platdata(dev); struct i2c_bus_priv *bus = dev_get_parent_priv(dev); /* 设置 I2C 地址 */ plat->chip.i2c_addr = CONFIG_DM_I2C_CHIP_ADDR; /* 探测设备是否存在 */ if (!i2c_reg_read(bus, plat->chip.i2c_addr)) { printf("AW9523 device not found at address %x\n", plat->chip.i2c_addr); return -ENODEV; } /* 初始化寄存器 */ i2c_reg_write(bus, plat->chip.i2c_addr, AW9523_REG_OUTPUT_PORT0, 0xFF); // 默认关闭 Port0 输出 i2c_reg_write(bus, plat->chip.i2c_addr, AW9523_REG_OUTPUT_PORT1, 0xFF); // 默认关闭 Port1 输出 return 0; } ``` #### 设定 GPIO 值函数 ```c const struct dm_aw9523_ops *ops = NULL; int set_gpio_values(struct udevice *dev, uint8_t port, uint8_t value) { struct dm_aw9523_platdata *plat = dev_get_platdata(dev); struct i2c_bus_priv *bus = dev_get_parent_priv(dev); switch (port) { case 0: i2c_reg_write(bus, plat->chip.i2c_addr, AW9523_REG_OUTPUT_PORT0, value); break; case 1: i2c_reg_write(bus, plat->chip.i2c_addr, AW9523_REG_OUTPUT_PORT1, value); break; default: return -EINVAL; } return 0; } if (ops && ops->set_value) { ops->set_value(0, 1); // 启用 Port0 第一位 mdelay(20); ops->set_value(1, 1); // 启用 Port1 第一位 mdelay(20); }[^2] ``` #### 平台数据结构定义 ```c struct dm_aw9523_platdata { struct gpio_chip chip; }; ``` --- ### 关键点说明 1. **U-Boot DM 框架支持** 上述代码利用了 U-Boot Device Model (DM) 框架来管理硬件资源,`uclass_get_device_by_name()` 函数用于获取指定名称的设备实例[^2]。 2. **I2C 寄存器操作** `i2c_reg_read()` 和 `i2c_reg_write()` 是标准的 I2C 注册表访问方法,分别用于读取和写入目标芯片上的寄存器。 3. **GPIO 控制逻辑** 使用 `set_gpio_values()` 函数可以灵活控制 AW9523B 的两组端口(Port0 和 Port1),并通过延时 (`mdelay()`) 来确保信号稳定。 4. **错误处理机制** 如果无法找到设备或发生其他异常情况,则返回相应的错误码以提示开发者排查问题。 --- ###
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

autho

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值