xref: /linux/drivers/gpio/gpio-pca9570.c (revision 1fd1dc41724319406b0aff221a352a400b0ddfc5)
1 // SPDX-License-Identifier: GPL-2.0-only
2 /*
3  * Driver for PCA9570 I2C GPO expander
4  *
5  * Copyright (C) 2020 Sungbo Eo <mans0n@gorani.run>
6  *
7  * Based on gpio-tpic2810.c
8  * Copyright (C) 2015 Texas Instruments Incorporated - http://www.ti.com/
9  *	Andrew F. Davis <afd@ti.com>
10  */
11 
12 #include <linux/bits.h>
13 #include <linux/cleanup.h>
14 #include <linux/device/devres.h>
15 #include <linux/errno.h>
16 #include <linux/gpio/driver.h>
17 #include <linux/i2c.h>
18 #include <linux/module.h>
19 #include <linux/mutex.h>
20 #include <linux/property.h>
21 #include <linux/types.h>
22 
23 #define SLG7XL45106_GPO_REG	0xDB
24 
25 /**
26  * struct pca9570_chip_data - GPIO platformdata
27  * @ngpio: no of gpios
28  * @command: Command to be sent
29  */
30 struct pca9570_chip_data {
31 	u16 ngpio;
32 	u32 command;
33 };
34 
35 /**
36  * struct pca9570 - GPIO driver data
37  * @chip: GPIO controller chip
38  * @chip_data: GPIO controller platform data
39  * @lock: Protects write sequences
40  * @out: Buffer for device register
41  */
42 struct pca9570 {
43 	struct gpio_chip chip;
44 	const struct pca9570_chip_data *chip_data;
45 	struct mutex lock;
46 	u8 out;
47 };
48 
49 static int pca9570_read(struct pca9570 *gpio, u8 *value)
50 {
51 	struct i2c_client *client = to_i2c_client(gpio->chip.parent);
52 	int ret;
53 
54 	if (gpio->chip_data->command != 0)
55 		ret = i2c_smbus_read_byte_data(client, gpio->chip_data->command);
56 	else
57 		ret = i2c_smbus_read_byte(client);
58 
59 	if (ret < 0)
60 		return ret;
61 
62 	*value = ret;
63 	return 0;
64 }
65 
66 static int pca9570_write(struct pca9570 *gpio, u8 value)
67 {
68 	struct i2c_client *client = to_i2c_client(gpio->chip.parent);
69 
70 	if (gpio->chip_data->command != 0)
71 		return i2c_smbus_write_byte_data(client, gpio->chip_data->command, value);
72 
73 	return i2c_smbus_write_byte(client, value);
74 }
75 
76 static int pca9570_get_direction(struct gpio_chip *chip,
77 				 unsigned offset)
78 {
79 	/* This device always output */
80 	return GPIO_LINE_DIRECTION_OUT;
81 }
82 
83 static int pca9570_get(struct gpio_chip *chip, unsigned offset)
84 {
85 	struct pca9570 *gpio = gpiochip_get_data(chip);
86 	u8 buffer;
87 	int ret;
88 
89 	ret = pca9570_read(gpio, &buffer);
90 	if (ret)
91 		return ret;
92 
93 	return !!(buffer & BIT(offset));
94 }
95 
96 static int pca9570_set(struct gpio_chip *chip, unsigned int offset, int value)
97 {
98 	struct pca9570 *gpio = gpiochip_get_data(chip);
99 	u8 buffer;
100 	int ret;
101 
102 	guard(mutex)(&gpio->lock);
103 
104 	buffer = gpio->out;
105 	if (value)
106 		buffer |= BIT(offset);
107 	else
108 		buffer &= ~BIT(offset);
109 
110 	ret = pca9570_write(gpio, buffer);
111 	if (ret)
112 		return ret;
113 
114 	gpio->out = buffer;
115 
116 	return 0;
117 }
118 
119 static int pca9570_probe(struct i2c_client *client)
120 {
121 	struct device *dev = &client->dev;
122 	struct pca9570 *gpio;
123 	int ret;
124 
125 	gpio = devm_kzalloc(&client->dev, sizeof(*gpio), GFP_KERNEL);
126 	if (!gpio)
127 		return -ENOMEM;
128 
129 	gpio->chip.label = client->name;
130 	gpio->chip.parent = &client->dev;
131 	gpio->chip.owner = THIS_MODULE;
132 	gpio->chip.get_direction = pca9570_get_direction;
133 	gpio->chip.get = pca9570_get;
134 	gpio->chip.set = pca9570_set;
135 	gpio->chip.base = -1;
136 	gpio->chip_data = device_get_match_data(&client->dev);
137 	gpio->chip.ngpio = gpio->chip_data->ngpio;
138 	gpio->chip.can_sleep = true;
139 
140 	ret = devm_mutex_init(dev, &gpio->lock);
141 	if (ret)
142 		return ret;
143 
144 	/* Read the current output level */
145 	pca9570_read(gpio, &gpio->out);
146 
147 	i2c_set_clientdata(client, gpio);
148 
149 	return devm_gpiochip_add_data(&client->dev, &gpio->chip, gpio);
150 }
151 
152 static const struct pca9570_chip_data pca9570_gpio = {
153 	.ngpio = 4,
154 };
155 
156 static const struct pca9570_chip_data pca9571_gpio = {
157 	.ngpio = 8,
158 };
159 
160 static const struct pca9570_chip_data slg7xl45106_gpio = {
161 	.ngpio = 8,
162 	.command = SLG7XL45106_GPO_REG,
163 };
164 
165 static const struct i2c_device_id pca9570_id_table[] = {
166 	{ "pca9570", (kernel_ulong_t)&pca9570_gpio},
167 	{ "pca9571", (kernel_ulong_t)&pca9571_gpio },
168 	{ "slg7xl45106", (kernel_ulong_t)&slg7xl45106_gpio },
169 	{ /* sentinel */ }
170 };
171 MODULE_DEVICE_TABLE(i2c, pca9570_id_table);
172 
173 static const struct of_device_id pca9570_of_match_table[] = {
174 	{ .compatible = "dlg,slg7xl45106", .data = &slg7xl45106_gpio},
175 	{ .compatible = "nxp,pca9570", .data = &pca9570_gpio },
176 	{ .compatible = "nxp,pca9571", .data = &pca9571_gpio },
177 	{ /* sentinel */ }
178 };
179 MODULE_DEVICE_TABLE(of, pca9570_of_match_table);
180 
181 static struct i2c_driver pca9570_driver = {
182 	.driver = {
183 		.name = "pca9570",
184 		.of_match_table = pca9570_of_match_table,
185 	},
186 	.probe = pca9570_probe,
187 	.id_table = pca9570_id_table,
188 };
189 module_i2c_driver(pca9570_driver);
190 
191 MODULE_AUTHOR("Sungbo Eo <mans0n@gorani.run>");
192 MODULE_DESCRIPTION("GPIO expander driver for PCA9570");
193 MODULE_LICENSE("GPL v2");
194