aw9523 gpio expander扩展芯片

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

1. 驱动代码

驱动可参考https://blog.csdn.net/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中使用,下篇通过我自己的理解写一下

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值