diff options
author | David Brownell <dbrownell@users.sourceforge.net> | 2008-02-05 01:28:24 -0500 |
---|---|---|
committer | Linus Torvalds <torvalds@woody.linux-foundation.org> | 2008-02-05 12:44:13 -0500 |
commit | 15fae37d9f5f21571a9618d8353164b6ddfea6f6 (patch) | |
tree | 13b4fb5479cf3a9522c6673cc6ccae20b6658e55 /drivers/gpio/pcf857x.c | |
parent | 1c44f5f16fee880b294f8068354bfb9dddf1349b (diff) |
gpiolib: pcf857x i2c gpio expander support
This is a new-style I2C driver for most common 8 and 16 bit I2C based
"quasi-bidirectional" GPIO expanders: pcf8574 or pcf8575, and several
compatible models (mostly faster, supporting I2C at up to 1 MHz).
The driver exposes the GPIO signals using the platform-neutral GPIO
programming interface, so they are easily accessed by other kernel code. The
lack of such a flexible kernel API has been a big factor in the proliferation
of board-specific drivers for these chips... stuff that rarely makes it
upstream since it's so ugly. This driver will let such boards use standard
calls.
Since it's a new-style driver, these devices must be configured as part of
board-specific init. That eliminates the need for error-prone manual
configuration of module parameters, and makes compatibility with legacy
drivers (pcf8574.c, pc8575.c) for these chips easier (there's a clear
either/or disjunction).
Signed-off-by: David Brownell <dbrownell@users.sourceforge.net>
Acked-by: Jean Delvare <khali@linux-fr.org>
Cc: Eric Miao <eric.miao@marvell.com>
Cc: Sam Ravnborg <sam@ravnborg.org>
Cc: Haavard Skinnemoen <hskinnemoen@atmel.com>
Cc: Philipp Zabel <philipp.zabel@gmail.com>
Cc: Russell King <rmk@arm.linux.org.uk>
Cc: Ben Gardner <bgardner@wabtec.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Diffstat (limited to 'drivers/gpio/pcf857x.c')
-rw-r--r-- | drivers/gpio/pcf857x.c | 330 |
1 files changed, 330 insertions, 0 deletions
diff --git a/drivers/gpio/pcf857x.c b/drivers/gpio/pcf857x.c new file mode 100644 index 000000000000..c6b3b5378384 --- /dev/null +++ b/drivers/gpio/pcf857x.c | |||
@@ -0,0 +1,330 @@ | |||
1 | /* | ||
2 | * pcf857x - driver for pcf857x, pca857x, and pca967x I2C GPIO expanders | ||
3 | * | ||
4 | * Copyright (C) 2007 David Brownell | ||
5 | * | ||
6 | * This program is free software; you can redistribute it and/or modify | ||
7 | * it under the terms of the GNU General Public License as published by | ||
8 | * the Free Software Foundation; either version 2 of the License, or | ||
9 | * (at your option) any later version. | ||
10 | * | ||
11 | * This program is distributed in the hope that it will be useful, | ||
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
14 | * GNU General Public License for more details. | ||
15 | * | ||
16 | * You should have received a copy of the GNU General Public License | ||
17 | * along with this program; if not, write to the Free Software | ||
18 | * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. | ||
19 | */ | ||
20 | |||
21 | #include <linux/kernel.h> | ||
22 | #include <linux/slab.h> | ||
23 | #include <linux/i2c.h> | ||
24 | #include <linux/i2c/pcf857x.h> | ||
25 | |||
26 | #include <asm/gpio.h> | ||
27 | |||
28 | |||
29 | /* | ||
30 | * The pcf857x, pca857x, and pca967x chips only expose one read and one | ||
31 | * write register. Writing a "one" bit (to match the reset state) lets | ||
32 | * that pin be used as an input; it's not an open-drain model, but acts | ||
33 | * a bit like one. This is described as "quasi-bidirectional"; read the | ||
34 | * chip documentation for details. | ||
35 | * | ||
36 | * Many other I2C GPIO expander chips (like the pca953x models) have | ||
37 | * more complex register models and more conventional circuitry using | ||
38 | * push/pull drivers. They often use the same 0x20..0x27 addresses as | ||
39 | * pcf857x parts, making the "legacy" I2C driver model problematic. | ||
40 | */ | ||
41 | struct pcf857x { | ||
42 | struct gpio_chip chip; | ||
43 | struct i2c_client *client; | ||
44 | unsigned out; /* software latch */ | ||
45 | }; | ||
46 | |||
47 | /*-------------------------------------------------------------------------*/ | ||
48 | |||
49 | /* Talk to 8-bit I/O expander */ | ||
50 | |||
51 | static int pcf857x_input8(struct gpio_chip *chip, unsigned offset) | ||
52 | { | ||
53 | struct pcf857x *gpio = container_of(chip, struct pcf857x, chip); | ||
54 | |||
55 | gpio->out |= (1 << offset); | ||
56 | return i2c_smbus_write_byte(gpio->client, gpio->out); | ||
57 | } | ||
58 | |||
59 | static int pcf857x_get8(struct gpio_chip *chip, unsigned offset) | ||
60 | { | ||
61 | struct pcf857x *gpio = container_of(chip, struct pcf857x, chip); | ||
62 | s32 value; | ||
63 | |||
64 | value = i2c_smbus_read_byte(gpio->client); | ||
65 | return (value < 0) ? 0 : (value & (1 << offset)); | ||
66 | } | ||
67 | |||
68 | static int pcf857x_output8(struct gpio_chip *chip, unsigned offset, int value) | ||
69 | { | ||
70 | struct pcf857x *gpio = container_of(chip, struct pcf857x, chip); | ||
71 | unsigned bit = 1 << offset; | ||
72 | |||
73 | if (value) | ||
74 | gpio->out |= bit; | ||
75 | else | ||
76 | gpio->out &= ~bit; | ||
77 | return i2c_smbus_write_byte(gpio->client, gpio->out); | ||
78 | } | ||
79 | |||
80 | static void pcf857x_set8(struct gpio_chip *chip, unsigned offset, int value) | ||
81 | { | ||
82 | pcf857x_output8(chip, offset, value); | ||
83 | } | ||
84 | |||
85 | /*-------------------------------------------------------------------------*/ | ||
86 | |||
87 | /* Talk to 16-bit I/O expander */ | ||
88 | |||
89 | static int i2c_write_le16(struct i2c_client *client, u16 word) | ||
90 | { | ||
91 | u8 buf[2] = { word & 0xff, word >> 8, }; | ||
92 | int status; | ||
93 | |||
94 | status = i2c_master_send(client, buf, 2); | ||
95 | return (status < 0) ? status : 0; | ||
96 | } | ||
97 | |||
98 | static int i2c_read_le16(struct i2c_client *client) | ||
99 | { | ||
100 | u8 buf[2]; | ||
101 | int status; | ||
102 | |||
103 | status = i2c_master_recv(client, buf, 2); | ||
104 | if (status < 0) | ||
105 | return status; | ||
106 | return (buf[1] << 8) | buf[0]; | ||
107 | } | ||
108 | |||
109 | static int pcf857x_input16(struct gpio_chip *chip, unsigned offset) | ||
110 | { | ||
111 | struct pcf857x *gpio = container_of(chip, struct pcf857x, chip); | ||
112 | |||
113 | gpio->out |= (1 << offset); | ||
114 | return i2c_write_le16(gpio->client, gpio->out); | ||
115 | } | ||
116 | |||
117 | static int pcf857x_get16(struct gpio_chip *chip, unsigned offset) | ||
118 | { | ||
119 | struct pcf857x *gpio = container_of(chip, struct pcf857x, chip); | ||
120 | int value; | ||
121 | |||
122 | value = i2c_read_le16(gpio->client); | ||
123 | return (value < 0) ? 0 : (value & (1 << offset)); | ||
124 | } | ||
125 | |||
126 | static int pcf857x_output16(struct gpio_chip *chip, unsigned offset, int value) | ||
127 | { | ||
128 | struct pcf857x *gpio = container_of(chip, struct pcf857x, chip); | ||
129 | unsigned bit = 1 << offset; | ||
130 | |||
131 | if (value) | ||
132 | gpio->out |= bit; | ||
133 | else | ||
134 | gpio->out &= ~bit; | ||
135 | return i2c_write_le16(gpio->client, gpio->out); | ||
136 | } | ||
137 | |||
138 | static void pcf857x_set16(struct gpio_chip *chip, unsigned offset, int value) | ||
139 | { | ||
140 | pcf857x_output16(chip, offset, value); | ||
141 | } | ||
142 | |||
143 | /*-------------------------------------------------------------------------*/ | ||
144 | |||
145 | static int pcf857x_probe(struct i2c_client *client) | ||
146 | { | ||
147 | struct pcf857x_platform_data *pdata; | ||
148 | struct pcf857x *gpio; | ||
149 | int status; | ||
150 | |||
151 | pdata = client->dev.platform_data; | ||
152 | if (!pdata) | ||
153 | return -ENODEV; | ||
154 | |||
155 | /* Allocate, initialize, and register this gpio_chip. */ | ||
156 | gpio = kzalloc(sizeof *gpio, GFP_KERNEL); | ||
157 | if (!gpio) | ||
158 | return -ENOMEM; | ||
159 | |||
160 | gpio->chip.base = pdata->gpio_base; | ||
161 | gpio->chip.can_sleep = 1; | ||
162 | |||
163 | /* NOTE: the OnSemi jlc1562b is also largely compatible with | ||
164 | * these parts, notably for output. It has a low-resolution | ||
165 | * DAC instead of pin change IRQs; and its inputs can be the | ||
166 | * result of comparators. | ||
167 | */ | ||
168 | |||
169 | /* 8574 addresses are 0x20..0x27; 8574a uses 0x38..0x3f; | ||
170 | * 9670, 9672, 9764, and 9764a use quite a variety. | ||
171 | * | ||
172 | * NOTE: we don't distinguish here between *4 and *4a parts. | ||
173 | */ | ||
174 | if (strcmp(client->name, "pcf8574") == 0 | ||
175 | || strcmp(client->name, "pca8574") == 0 | ||
176 | || strcmp(client->name, "pca9670") == 0 | ||
177 | || strcmp(client->name, "pca9672") == 0 | ||
178 | || strcmp(client->name, "pca9674") == 0 | ||
179 | ) { | ||
180 | gpio->chip.ngpio = 8; | ||
181 | gpio->chip.direction_input = pcf857x_input8; | ||
182 | gpio->chip.get = pcf857x_get8; | ||
183 | gpio->chip.direction_output = pcf857x_output8; | ||
184 | gpio->chip.set = pcf857x_set8; | ||
185 | |||
186 | if (!i2c_check_functionality(client->adapter, | ||
187 | I2C_FUNC_SMBUS_BYTE)) | ||
188 | status = -EIO; | ||
189 | |||
190 | /* fail if there's no chip present */ | ||
191 | else | ||
192 | status = i2c_smbus_read_byte(client); | ||
193 | |||
194 | /* '75/'75c addresses are 0x20..0x27, just like the '74; | ||
195 | * the '75c doesn't have a current source pulling high. | ||
196 | * 9671, 9673, and 9765 use quite a variety of addresses. | ||
197 | * | ||
198 | * NOTE: we don't distinguish here between '75 and '75c parts. | ||
199 | */ | ||
200 | } else if (strcmp(client->name, "pcf8575") == 0 | ||
201 | || strcmp(client->name, "pca8575") == 0 | ||
202 | || strcmp(client->name, "pca9671") == 0 | ||
203 | || strcmp(client->name, "pca9673") == 0 | ||
204 | || strcmp(client->name, "pca9675") == 0 | ||
205 | ) { | ||
206 | gpio->chip.ngpio = 16; | ||
207 | gpio->chip.direction_input = pcf857x_input16; | ||
208 | gpio->chip.get = pcf857x_get16; | ||
209 | gpio->chip.direction_output = pcf857x_output16; | ||
210 | gpio->chip.set = pcf857x_set16; | ||
211 | |||
212 | if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) | ||
213 | status = -EIO; | ||
214 | |||
215 | /* fail if there's no chip present */ | ||
216 | else | ||
217 | status = i2c_read_le16(client); | ||
218 | |||
219 | } else | ||
220 | status = -ENODEV; | ||
221 | |||
222 | if (status < 0) | ||
223 | goto fail; | ||
224 | |||
225 | gpio->chip.label = client->name; | ||
226 | |||
227 | gpio->client = client; | ||
228 | i2c_set_clientdata(client, gpio); | ||
229 | |||
230 | /* NOTE: these chips have strange "quasi-bidirectional" I/O pins. | ||
231 | * We can't actually know whether a pin is configured (a) as output | ||
232 | * and driving the signal low, or (b) as input and reporting a low | ||
233 | * value ... without knowing the last value written since the chip | ||
234 | * came out of reset (if any). We can't read the latched output. | ||
235 | * | ||
236 | * In short, the only reliable solution for setting up pin direction | ||
237 | * is to do it explicitly. The setup() method can do that, but it | ||
238 | * may cause transient glitching since it can't know the last value | ||
239 | * written (some pins may need to be driven low). | ||
240 | * | ||
241 | * Using pdata->n_latch avoids that trouble. When left initialized | ||
242 | * to zero, our software copy of the "latch" then matches the chip's | ||
243 | * all-ones reset state. Otherwise it flags pins to be driven low. | ||
244 | */ | ||
245 | gpio->out = ~pdata->n_latch; | ||
246 | |||
247 | status = gpiochip_add(&gpio->chip); | ||
248 | if (status < 0) | ||
249 | goto fail; | ||
250 | |||
251 | /* NOTE: these chips can issue "some pin-changed" IRQs, which we | ||
252 | * don't yet even try to use. Among other issues, the relevant | ||
253 | * genirq state isn't available to modular drivers; and most irq | ||
254 | * methods can't be called from sleeping contexts. | ||
255 | */ | ||
256 | |||
257 | dev_info(&client->dev, "gpios %d..%d on a %s%s\n", | ||
258 | gpio->chip.base, | ||
259 | gpio->chip.base + gpio->chip.ngpio - 1, | ||
260 | client->name, | ||
261 | client->irq ? " (irq ignored)" : ""); | ||
262 | |||
263 | /* Let platform code set up the GPIOs and their users. | ||
264 | * Now is the first time anyone could use them. | ||
265 | */ | ||
266 | if (pdata->setup) { | ||
267 | status = pdata->setup(client, | ||
268 | gpio->chip.base, gpio->chip.ngpio, | ||
269 | pdata->context); | ||
270 | if (status < 0) | ||
271 | dev_warn(&client->dev, "setup --> %d\n", status); | ||
272 | } | ||
273 | |||
274 | return 0; | ||
275 | |||
276 | fail: | ||
277 | dev_dbg(&client->dev, "probe error %d for '%s'\n", | ||
278 | status, client->name); | ||
279 | kfree(gpio); | ||
280 | return status; | ||
281 | } | ||
282 | |||
283 | static int pcf857x_remove(struct i2c_client *client) | ||
284 | { | ||
285 | struct pcf857x_platform_data *pdata = client->dev.platform_data; | ||
286 | struct pcf857x *gpio = i2c_get_clientdata(client); | ||
287 | int status = 0; | ||
288 | |||
289 | if (pdata->teardown) { | ||
290 | status = pdata->teardown(client, | ||
291 | gpio->chip.base, gpio->chip.ngpio, | ||
292 | pdata->context); | ||
293 | if (status < 0) { | ||
294 | dev_err(&client->dev, "%s --> %d\n", | ||
295 | "teardown", status); | ||
296 | return status; | ||
297 | } | ||
298 | } | ||
299 | |||
300 | status = gpiochip_remove(&gpio->chip); | ||
301 | if (status == 0) | ||
302 | kfree(gpio); | ||
303 | else | ||
304 | dev_err(&client->dev, "%s --> %d\n", "remove", status); | ||
305 | return status; | ||
306 | } | ||
307 | |||
308 | static struct i2c_driver pcf857x_driver = { | ||
309 | .driver = { | ||
310 | .name = "pcf857x", | ||
311 | .owner = THIS_MODULE, | ||
312 | }, | ||
313 | .probe = pcf857x_probe, | ||
314 | .remove = pcf857x_remove, | ||
315 | }; | ||
316 | |||
317 | static int __init pcf857x_init(void) | ||
318 | { | ||
319 | return i2c_add_driver(&pcf857x_driver); | ||
320 | } | ||
321 | module_init(pcf857x_init); | ||
322 | |||
323 | static void __exit pcf857x_exit(void) | ||
324 | { | ||
325 | i2c_del_driver(&pcf857x_driver); | ||
326 | } | ||
327 | module_exit(pcf857x_exit); | ||
328 | |||
329 | MODULE_LICENSE("GPL"); | ||
330 | MODULE_AUTHOR("David Brownell"); | ||