android13/u-boot/drivers/gpio/nca9539_gpio.c

302 lines
7.0 KiB
C

// SPDX-License-Identifier: GPL-2.0-only
/*
* NCA9539 I2C Port Expander I/O
*
* Copyright (C) 2023 Cody Xie <cody.xie@rock-chips.com>
*
*/
#include <common.h>
#include <errno.h>
#include <dm.h>
#include <fdtdec.h>
#include <i2c.h>
#include <malloc.h>
#include <asm/gpio.h>
#include <asm/io.h>
#include <dt-bindings/gpio/gpio.h>
#include <linux/bitops.h>
#define NCA9539_REG_INPUT_PORT_BASE 0x00
#define NCA9539_REG_INPUT_PORT0 (NCA9539_REG_INPUT_PORT_BASE + 0x0)
#define NCA9539_REG_INPUT_PORT1 (NCA9539_REG_INPUT_PORT_BASE + 0x1)
#define NCA9539_REG_OUTPUT_PORT_BASE 0x02
#define NCA9539_REG_OUTPUT_PORT0 (NCA9539_REG_OUTPUT_PORT_BASE + 0x0)
#define NCA9539_REG_OUTPUT_PORT1 (NCA9539_REG_OUTPUT_PORT_BASE + 0x1)
#define NCA9539_REG_POLARITY_BASE 0x04
#define NCA9539_REG_POLARITY_PORT0 (NCA9539_REG_POLARITY_BASE + 0x0)
#define NCA9539_REG_POLARITY_PORT1 (NCA9539_REG_POLARITY_BASE + 0x1)
#define NCA9539_REG_CONFIG_BASE 0x06
#define NCA9539_REG_CONFIG_PORT0 (NCA9539_REG_CONFIG_BASE + 0x0)
#define NCA9539_REG_CONFIG_PORT1 (NCA9539_REG_CONFIG_BASE + 0x1)
#define NCA9539_BANK_SZ 8
#define NCA9539_MAX_BANK 2
#define NCA9539_CHIP_ADDR 0x74
#ifndef BIT
#define BIT(nr) (1UL << (nr))
#endif
struct nca9539_info {
struct udevice *dev;
int addr;
unsigned int ngpio;
};
static int nca9539_write_reg(struct udevice *dev, int reg, u8 val)
{
int ret = 0;
ret = dm_i2c_write(dev, reg, &val, 1);
if (ret) {
dev_err(dev, "%s error\n", __func__);
return ret;
}
return 0;
}
static int nca9539_read_reg(struct udevice *dev, int reg, u8 *val)
{
int ret;
u8 byte;
ret = dm_i2c_read(dev, reg, &byte, 1);
if (ret) {
dev_err(dev, "%s error\n", __func__);
return ret;
}
*val = byte;
return 0;
}
static int nca9539_gpio_get_direction(struct udevice *dev, unsigned int offset)
{
unsigned int port = offset / NCA9539_BANK_SZ;
unsigned int pin = offset % NCA9539_BANK_SZ;
u8 value;
int ret;
dev_dbg(dev, "%s offset(%d)\n", __func__, offset);
ret = nca9539_read_reg(dev, NCA9539_REG_CONFIG_BASE + port, &value);
if (ret < 0) {
dev_err(dev, "%s offset(%d) read config failed\n", __func__,
offset);
return ret;
}
if (value & BIT(pin))
return GPIOF_INPUT;
return GPIOF_OUTPUT;
}
static int nca9539_gpio_direction_input(struct udevice *dev,
unsigned int offset)
{
unsigned int port = offset / NCA9539_BANK_SZ;
unsigned int pin = offset % NCA9539_BANK_SZ;
u8 val;
int ret = 0;
dev_dbg(dev, "%s offset(%d)\n", __func__, offset);
ret = nca9539_read_reg(dev, NCA9539_REG_CONFIG_BASE + port, &val);
if (!ret) {
val &= ~BIT(pin);
val |= BIT(pin);
ret = nca9539_write_reg(dev, NCA9539_REG_CONFIG_BASE + port,
val);
}
if (ret < 0) {
dev_err(dev, "%s offset(%d) read config failed\n", __func__,
offset);
}
return ret;
}
static int nca9539_gpio_direction_output(struct udevice *dev,
unsigned int offset, int val)
{
unsigned int port = offset / NCA9539_BANK_SZ;
unsigned int pin = offset % NCA9539_BANK_SZ;
u8 value;
int ret;
dev_dbg(dev, "%s offset(%d) val(%d)\n", __func__, offset, val);
ret = nca9539_read_reg(dev, NCA9539_REG_CONFIG_BASE + port, &value);
if (!ret) {
value &= ~BIT(pin);
ret = nca9539_write_reg(dev, NCA9539_REG_CONFIG_BASE + port,
value);
}
if (ret < 0) {
dev_warn(dev, "%s offset(%d) read config failed\n", __func__,
offset);
}
ret = nca9539_read_reg(dev, NCA9539_REG_OUTPUT_PORT_BASE + port,
&value);
if (!ret) {
value &= ~BIT(pin);
value |= val ? BIT(pin) : 0;
ret = nca9539_write_reg(
dev, NCA9539_REG_OUTPUT_PORT_BASE + port, value);
}
if (ret < 0) {
dev_err(dev, "%s offset(%d) val(%d) update output failed\n",
__func__, offset, val);
}
return ret;
}
static int nca9539_gpio_get(struct udevice *dev, unsigned int offset)
{
unsigned int port = offset / NCA9539_BANK_SZ;
unsigned int pin = offset % NCA9539_BANK_SZ;
int reg;
u8 value;
int ret;
dev_dbg(dev, "%s offset(%d)\n", __func__, offset);
ret = nca9539_read_reg(dev, NCA9539_REG_CONFIG_BASE + port, &value);
if (ret < 0) {
dev_err(dev, "%s offset(%d) check config failed\n", __func__,
offset);
return ret;
}
if (!(BIT(pin) & value))
reg = NCA9539_REG_OUTPUT_PORT_BASE + port;
else
reg = NCA9539_REG_INPUT_PORT_BASE + port;
ret = nca9539_read_reg(dev, reg, &value);
if (ret < 0) {
dev_err(dev, "%s offset(%d) read value failed\n", __func__,
offset);
return -EIO;
}
return !!(BIT(pin) & value);
}
static int nca9539_gpio_set(struct udevice *dev, unsigned int offset, int val)
{
unsigned int port = offset / NCA9539_BANK_SZ;
unsigned int pin = offset % NCA9539_BANK_SZ;
u8 value;
int ret;
dev_dbg(dev, "%s offset(%d) val(%d)\n", __func__, offset, val);
ret = nca9539_read_reg(dev, NCA9539_REG_CONFIG_BASE + port, &value);
if (ret < 0 || !!(BIT(pin) & value)) {
dev_warn(dev, "%s offset(%d) val(%d) check config failed\n",
__func__, offset, val);
}
ret = nca9539_read_reg(dev, NCA9539_REG_OUTPUT_PORT_BASE + port,
&value);
if (!ret) {
value &= ~BIT(pin);
value |= val ? BIT(pin) : 0;
ret = nca9539_write_reg(
dev, NCA9539_REG_OUTPUT_PORT_BASE + port, value);
}
if (ret < 0) {
dev_err(dev, "%s offset(%d) val(%d) read input failed\n",
__func__, offset, val);
}
return ret;
}
static int nca9539_get_function(struct udevice *dev, unsigned offset)
{
return nca9539_gpio_get_direction(dev, offset);
}
static int nca9539_xlate(struct udevice *dev, struct gpio_desc *desc,
struct ofnode_phandle_args *args)
{
desc->offset = args->args[0];
desc->flags = args->args[1] & GPIO_ACTIVE_LOW ? GPIOD_ACTIVE_LOW : 0;
return 0;
}
static const struct dm_gpio_ops nca9539_ops = {
.direction_input = nca9539_gpio_direction_input,
.direction_output = nca9539_gpio_direction_output,
.get_value = nca9539_gpio_get,
.set_value = nca9539_gpio_set,
.get_function = nca9539_get_function,
.xlate = nca9539_xlate,
};
static int nca9539_probe(struct udevice *dev)
{
struct nca9539_info *info = dev_get_platdata(dev);
struct gpio_dev_priv *uc_priv = dev_get_uclass_priv(dev);
struct dm_i2c_chip *chip = dev_get_parent_platdata(dev);
char name[32], *str;
ulong driver_data;
if (!info) {
dev_err(dev, "platdata not ready\n");
return -ENOMEM;
}
if (!chip) {
dev_err(dev, "i2c not ready\n");
return -ENODEV;
}
#if CONFIG_IS_ENABLED(OF_CONTROL)
info->addr = chip->chip_addr;
#else
info->addr = NCA9539_CHIP_ADDR;
#endif
driver_data = dev_get_driver_data(dev);
info->ngpio = driver_data;
if (info->ngpio > NCA9539_MAX_BANK * NCA9539_BANK_SZ) {
dev_err(dev, "Max support %d pins now\n",
NCA9539_MAX_BANK * NCA9539_BANK_SZ);
return -EINVAL;
}
snprintf(name, sizeof(name), "gpio@%x_", info->addr);
str = strdup(name);
if (!str)
return -ENOMEM;
uc_priv->bank_name = str;
uc_priv->gpio_count = info->ngpio;
dev_dbg(dev, "%s is ready\n", str);
return 0;
}
static const struct udevice_id nca9539_ids[] = {
{
.compatible = "novo,nca9539-gpio",
.data = (ulong)16,
},
{ /* sentinel */ },
};
U_BOOT_DRIVER(nca9539) = {
.name = "nca9539",
.id = UCLASS_GPIO,
.ops = &nca9539_ops,
.probe = nca9539_probe,
.platdata_auto_alloc_size = sizeof(struct nca9539_info),
.of_match = nca9539_ids,
};