Skip to content
Snippets Groups Projects
Commit 949598ee authored by Tobias Kahlki's avatar Tobias Kahlki Committed by Tobias Kahlki
Browse files

driver:gpio: Added driver for PI4IOE5V6408 GPIO Expander

Ported driver for the PI4IOE5V6408 GPIO I2C Expander from
https://coral.googlesource.com/linux-imx/+/refs/heads/master/drivers/gpio/gpio-pi4ioe5v6408.c

BCS 746-001106
parent e6b4d80b
No related branches found
No related tags found
No related merge requests found
...@@ -122,4 +122,6 @@ source "drivers/staging/hikey9xx/Kconfig" ...@@ -122,4 +122,6 @@ source "drivers/staging/hikey9xx/Kconfig"
source "drivers/staging/fsl_ppfe/Kconfig" source "drivers/staging/fsl_ppfe/Kconfig"
source "drivers/staging/gpio/Kconfig"
endif # STAGING endif # STAGING
...@@ -51,3 +51,4 @@ obj-$(CONFIG_QLGE) += qlge/ ...@@ -51,3 +51,4 @@ obj-$(CONFIG_QLGE) += qlge/
obj-$(CONFIG_WFX) += wfx/ obj-$(CONFIG_WFX) += wfx/
obj-y += hikey9xx/ obj-y += hikey9xx/
obj-$(CONFIG_FSL_PPFE) += fsl_ppfe/ obj-$(CONFIG_FSL_PPFE) += fsl_ppfe/
obj-y += gpio/
# SPDX-License-Identifier: GPL-2.0
#
# GPIO infrastructure and drivers
#
menu "I2C GPIO expanders"
depends on I2C
config GPIO_PI4IOE5V6408
tristate "Pericom PI4IOE5V6408 I2C GPIO Expander"
select REGMAP_I2C
help
Say yes here to enable the Pericom PI4IOE5V6408 GPIO Expander
endmenu
# SPDX-License-Identifier: GPL-2.0
#
# Makefile for GPIO infrastructure and drivers
#
obj-$(CONFIG_GPIO_PI4IOE5V6408) += gpio-pi4ioe5v6408.o
// SPDX-License-Identifier: GPL-2.0
/*
* Driver for the Pericom PI4IOE5V6408 GPIO Expander.
*
* Copyright (C) 2018 Google, LLC.
*/
#include <linux/gpio.h>
#include <linux/i2c.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/regmap.h>
#define PI4IO_CHIP_ID 0x1
#define PI4IO_IO_DIRECTION 0x3
#define PI4IO_OUTPUT 0x5
#define PI4IO_OUTPUT_HI_IMPEDANCE 0x7
#define PI4IO_INPUT_STATUS 0xF
#define PI4IO_INTERRUPT_STATUS 0x13
#define PI4IO_CHIP_ID_VAL 0xA0
#define PI4IO_CHIP_ID_MASK 0xFC
#define PI4IO_DIRECTION_TO_GPIOD(x) ((x) ? GPIOF_DIR_OUT : GPIOF_DIR_IN)
#define GPIOD_DIRECTION_TO_PI4IO(x) ((x) == GPIOF_DIR_OUT ? 1 : 0)
#define GPIO_OUT_LOW 0
#define GPIO_OUT_HIGH 1
static bool pi4io_readable_reg(struct device *dev, unsigned int reg)
{
// All readable registers are odd-numbered.
return (reg % 2) == 1;
}
static bool pi4io_writeable_reg(struct device *dev, unsigned int reg)
{
// All odd-numbered registers are writable except for 0xF.
if ((reg % 2) == 1) {
if (reg != PI4IO_INPUT_STATUS) {
return true;
}
}
return false;
}
static bool pi4io_volatile_reg(struct device *dev, unsigned int reg)
{
if (reg == PI4IO_INPUT_STATUS || reg == PI4IO_INTERRUPT_STATUS) {
return true;
}
return false;
}
static const struct regmap_config pi4io_regmap = {
.reg_bits = 8,
.val_bits = 8,
.max_register = 0x13,
.writeable_reg = pi4io_writeable_reg,
.readable_reg = pi4io_readable_reg,
.volatile_reg = pi4io_volatile_reg,
};
struct pi4io_priv {
struct i2c_client *i2c;
struct regmap *regmap;
struct gpio_chip gpio;
};
static int pi4io_gpio_get_direction(struct gpio_chip *chip, unsigned offset)
{
int ret, io_dir;
struct pi4io_priv *pi4io = gpiochip_get_data(chip);
struct device *dev = &pi4io->i2c->dev;
ret = regmap_read(pi4io->regmap, PI4IO_IO_DIRECTION, &io_dir);
if (ret) {
dev_err(dev, "Failed to read I/O direction: %d", ret);
return ret;
}
return PI4IO_DIRECTION_TO_GPIOD((io_dir >> offset) & 1);
}
static int pi4io_gpio_set_direction(
struct gpio_chip *chip, unsigned offset, int direction)
{
int ret;
struct pi4io_priv *pi4io = gpiochip_get_data(chip);
struct device *dev = &pi4io->i2c->dev;
ret = regmap_update_bits(pi4io->regmap, PI4IO_IO_DIRECTION, 1 << offset,
GPIOD_DIRECTION_TO_PI4IO(direction) << offset);
if (ret) {
dev_err(dev, "Failed to set direction: %d", ret);
return ret;
}
// We desire the hi-impedance state to track the output state.
ret = regmap_update_bits(pi4io->regmap, PI4IO_OUTPUT_HI_IMPEDANCE,
1 << offset, direction << offset);
return ret;
}
static int pi4io_gpio_get(struct gpio_chip *chip, unsigned offset)
{
int ret, out;
struct pi4io_priv *pi4io = gpiochip_get_data(chip);
struct device *dev = &pi4io->i2c->dev;
ret = regmap_read(pi4io->regmap, PI4IO_OUTPUT, &out);
if (ret) {
dev_err(dev, "Failed to read output: %d", ret);
return ret;
}
if (out & (1 << offset)) {
return 1;
}
return 0;
}
static void pi4io_gpio_set(struct gpio_chip *chip, unsigned offset, int value)
{
int ret;
struct pi4io_priv *pi4io = gpiochip_get_data(chip);
struct device *dev = &pi4io->i2c->dev;
ret = regmap_update_bits(
pi4io->regmap, PI4IO_OUTPUT, 1 << offset, value << offset);
if (ret) {
dev_err(dev, "Failed to write output: %d", ret);
}
}
static int pi4io_gpio_direction_input(struct gpio_chip *chip, unsigned offset)
{
return pi4io_gpio_set_direction(chip, offset, GPIOF_DIR_IN);
}
static int pi4io_gpio_direction_output(
struct gpio_chip *chip, unsigned offset, int value)
{
int ret;
struct pi4io_priv *pi4io = gpiochip_get_data(chip);
struct device *dev = &pi4io->i2c->dev;
ret = pi4io_gpio_set_direction(chip, offset, GPIOF_DIR_OUT);
if (ret) {
dev_err(dev, "Failed to set direction: %d", ret);
return ret;
}
pi4io_gpio_set(chip, offset, value);
return 0;
}
static int pi4io_gpio_setup(struct pi4io_priv *pi4io)
{
int ret;
struct device *dev = &pi4io->i2c->dev;
struct gpio_chip *gc = &pi4io->gpio;
gc->ngpio = 8;
gc->label = pi4io->i2c->name;
gc->parent = &pi4io->i2c->dev;
gc->owner = THIS_MODULE;
gc->base = -1;
gc->can_sleep = true;
gc->get_direction = pi4io_gpio_get_direction;
gc->direction_input = pi4io_gpio_direction_input;
gc->direction_output = pi4io_gpio_direction_output;
gc->get = pi4io_gpio_get;
gc->set = pi4io_gpio_set;
ret = devm_gpiochip_add_data(dev, gc, pi4io);
if (ret) {
dev_err(dev, "devm_gpiochip_add_data failed: %d", ret);
return ret;
}
return 0;
}
static int pi4io_probe(
struct i2c_client *client, const struct i2c_device_id *id)
{
int ret, chip_id;
struct device *dev = &client->dev;
struct pi4io_priv *pi4io;
pi4io = devm_kzalloc(dev, sizeof(struct pi4io_priv), GFP_KERNEL);
if (!pi4io) {
return -ENOMEM;
}
i2c_set_clientdata(client, pi4io);
pi4io->i2c = client;
pi4io->regmap = devm_regmap_init_i2c(client, &pi4io_regmap);
ret = regmap_read(pi4io->regmap, PI4IO_CHIP_ID, &chip_id);
if (ret < 0) {
dev_err(dev, "Failed to read Chip ID: %d", ret);
return ret;
}
if ((chip_id & PI4IO_CHIP_ID_MASK) != PI4IO_CHIP_ID_VAL) {
dev_err(dev, "Invalid Chip ID!");
return -EINVAL;
}
ret = pi4io_gpio_setup(pi4io);
if (ret < 0) {
dev_err(dev, "Failed to setup GPIOs: %d", ret);
return ret;
}
dev_dbg(dev, "PI4IO probe finished");
return 0;
}
static const struct i2c_device_id pi4io_id_table[] = { { "pi4io", 0 }, {} };
MODULE_DEVICE_TABLE(i2c, pi4io_id_table);
static struct i2c_driver pi4io_driver = {
.driver = {
.name = "pi4io-gpio",
},
.probe = pi4io_probe,
.id_table = pi4io_id_table,
};
module_i2c_driver(pi4io_driver);
MODULE_AUTHOR("Alex Van Damme <atv@google.com>");
MODULE_DESCRIPTION("PI4IOE5V6408");
MODULE_LICENSE("GPL v2");
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment