Skip to content

Commit 16d44b6

Browse files
mans0nlinusw
authored andcommitted
gpio: pca9570: add GPO driver for PCA9570
NXP PCA9570 is a 4-bit I2C GPO expander without interrupt functionality. Its ports are controlled only by a data byte without register address. Signed-off-by: Sungbo Eo <[email protected]> Reviewed-by: Andy Shevchenko <[email protected]> Datasheet: https://www.nxp.com/docs/en/data-sheet/PCA9570.pdf Link: https://lore.kernel.org/r/[email protected] Signed-off-by: Linus Walleij <[email protected]>
1 parent e6827bc commit 16d44b6

File tree

3 files changed

+155
-0
lines changed

3 files changed

+155
-0
lines changed

drivers/gpio/Kconfig

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -962,6 +962,14 @@ config GPIO_PCA953X_IRQ
962962
Say yes here to enable the pca953x to be used as an interrupt
963963
controller. It requires the driver to be built in the kernel.
964964

965+
config GPIO_PCA9570
966+
tristate "PCA9570 4-Bit I2C GPO expander"
967+
help
968+
Say yes here to enable the GPO driver for the NXP PCA9570 chip.
969+
970+
To compile this driver as a module, choose M here: the module will
971+
be called gpio-pca9570.
972+
965973
config GPIO_PCF857X
966974
tristate "PCF857x, PCA{85,96}7x, and MAX732[89] I2C GPIO expanders"
967975
select GPIOLIB_IRQCHIP

drivers/gpio/Makefile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,7 @@ obj-$(CONFIG_GPIO_OCTEON) += gpio-octeon.o
111111
obj-$(CONFIG_GPIO_OMAP) += gpio-omap.o
112112
obj-$(CONFIG_GPIO_PALMAS) += gpio-palmas.o
113113
obj-$(CONFIG_GPIO_PCA953X) += gpio-pca953x.o
114+
obj-$(CONFIG_GPIO_PCA9570) += gpio-pca9570.o
114115
obj-$(CONFIG_GPIO_PCF857X) += gpio-pcf857x.o
115116
obj-$(CONFIG_GPIO_PCH) += gpio-pch.o
116117
obj-$(CONFIG_GPIO_PCIE_IDIO_24) += gpio-pcie-idio-24.o

drivers/gpio/gpio-pca9570.c

Lines changed: 146 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,146 @@
1+
// SPDX-License-Identifier: GPL-2.0-only
2+
/*
3+
* Driver for PCA9570 I2C GPO expander
4+
*
5+
* Copyright (C) 2020 Sungbo Eo <[email protected]>
6+
*
7+
* Based on gpio-tpic2810.c
8+
* Copyright (C) 2015 Texas Instruments Incorporated - http://www.ti.com/
9+
* Andrew F. Davis <[email protected]>
10+
*/
11+
12+
#include <linux/gpio/driver.h>
13+
#include <linux/i2c.h>
14+
#include <linux/module.h>
15+
#include <linux/mutex.h>
16+
#include <linux/property.h>
17+
18+
/**
19+
* struct pca9570 - GPIO driver data
20+
* @chip: GPIO controller chip
21+
* @lock: Protects write sequences
22+
* @out: Buffer for device register
23+
*/
24+
struct pca9570 {
25+
struct gpio_chip chip;
26+
struct mutex lock;
27+
u8 out;
28+
};
29+
30+
static int pca9570_read(struct pca9570 *gpio, u8 *value)
31+
{
32+
struct i2c_client *client = to_i2c_client(gpio->chip.parent);
33+
int ret;
34+
35+
ret = i2c_smbus_read_byte(client);
36+
if (ret < 0)
37+
return ret;
38+
39+
*value = ret;
40+
return 0;
41+
}
42+
43+
static int pca9570_write(struct pca9570 *gpio, u8 value)
44+
{
45+
struct i2c_client *client = to_i2c_client(gpio->chip.parent);
46+
47+
return i2c_smbus_write_byte(client, value);
48+
}
49+
50+
static int pca9570_get_direction(struct gpio_chip *chip,
51+
unsigned offset)
52+
{
53+
/* This device always output */
54+
return GPIO_LINE_DIRECTION_OUT;
55+
}
56+
57+
static int pca9570_get(struct gpio_chip *chip, unsigned offset)
58+
{
59+
struct pca9570 *gpio = gpiochip_get_data(chip);
60+
u8 buffer;
61+
int ret;
62+
63+
ret = pca9570_read(gpio, &buffer);
64+
if (ret)
65+
return ret;
66+
67+
return !!(buffer & BIT(offset));
68+
}
69+
70+
static void pca9570_set(struct gpio_chip *chip, unsigned offset, int value)
71+
{
72+
struct pca9570 *gpio = gpiochip_get_data(chip);
73+
u8 buffer;
74+
int ret;
75+
76+
mutex_lock(&gpio->lock);
77+
78+
buffer = gpio->out;
79+
if (value)
80+
buffer |= BIT(offset);
81+
else
82+
buffer &= ~BIT(offset);
83+
84+
ret = pca9570_write(gpio, buffer);
85+
if (ret)
86+
goto out;
87+
88+
gpio->out = buffer;
89+
90+
out:
91+
mutex_unlock(&gpio->lock);
92+
}
93+
94+
static int pca9570_probe(struct i2c_client *client)
95+
{
96+
struct pca9570 *gpio;
97+
98+
gpio = devm_kzalloc(&client->dev, sizeof(*gpio), GFP_KERNEL);
99+
if (!gpio)
100+
return -ENOMEM;
101+
102+
gpio->chip.label = client->name;
103+
gpio->chip.parent = &client->dev;
104+
gpio->chip.owner = THIS_MODULE;
105+
gpio->chip.get_direction = pca9570_get_direction;
106+
gpio->chip.get = pca9570_get;
107+
gpio->chip.set = pca9570_set;
108+
gpio->chip.base = -1;
109+
gpio->chip.ngpio = (uintptr_t)device_get_match_data(&client->dev);
110+
gpio->chip.can_sleep = true;
111+
112+
mutex_init(&gpio->lock);
113+
114+
/* Read the current output level */
115+
pca9570_read(gpio, &gpio->out);
116+
117+
i2c_set_clientdata(client, gpio);
118+
119+
return devm_gpiochip_add_data(&client->dev, &gpio->chip, gpio);
120+
}
121+
122+
static const struct i2c_device_id pca9570_id_table[] = {
123+
{ "pca9570", 4 },
124+
{ /* sentinel */ }
125+
};
126+
MODULE_DEVICE_TABLE(i2c, pca9570_id_table);
127+
128+
static const struct of_device_id pca9570_of_match_table[] = {
129+
{ .compatible = "nxp,pca9570", .data = (void *)4 },
130+
{ /* sentinel */ }
131+
};
132+
MODULE_DEVICE_TABLE(of, pca9570_of_match_table);
133+
134+
static struct i2c_driver pca9570_driver = {
135+
.driver = {
136+
.name = "pca9570",
137+
.of_match_table = pca9570_of_match_table,
138+
},
139+
.probe_new = pca9570_probe,
140+
.id_table = pca9570_id_table,
141+
};
142+
module_i2c_driver(pca9570_driver);
143+
144+
MODULE_AUTHOR("Sungbo Eo <[email protected]>");
145+
MODULE_DESCRIPTION("GPIO expander driver for PCA9570");
146+
MODULE_LICENSE("GPL v2");

0 commit comments

Comments
 (0)