最近在调试新板卡,由于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 = ®,
}, {
.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中使用,下篇通过我自己的理解写一下