diff options
author | eric miao <eric.miao@marvell.com> | 2008-02-05 01:28:26 -0500 |
---|---|---|
committer | Linus Torvalds <torvalds@woody.linux-foundation.org> | 2008-02-05 12:44:13 -0500 |
commit | 9e60fdcf0c2905d7a8fc4cb2b3711ea5c5acaae1 (patch) | |
tree | c90417ab0e2079d0355ffb553985d9a0f2348bab /drivers/gpio/pca9539.c | |
parent | e58b9e2762a6ef99e20dba47aba21b911658541d (diff) |
gpiolib: pca9539 i2c gpio expander support
This adds a new-style I2C driver with basic support for the sixteen bit
PCA9539 GPIO expanders. These chips have multiple registers, push-pull output
drivers, and (not supported in this patch) pin change interrupts.
Board-specific code must provide "pca9539_platform_data" with each chip's
"i2c_board_info". That provides the GPIO numbers to be used by that chip, and
callbacks for board-specific setup/teardown logic.
Derived from drivers/i2c/chips/pca9539.c (which has no current known users).
This is faster and simpler; it uses 16-bit register access, and cache the
OUTPUT and DIRECTION registers for fast access
Signed-off-by: eric miao <eric.miao@marvell.com>
Signed-off-by: David Brownell <dbrownell@users.sourceforge.net>
Acked-by: Jean Delvare <khali@linux-fr.org>
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/pca9539.c')
-rw-r--r-- | drivers/gpio/pca9539.c | 271 |
1 files changed, 271 insertions, 0 deletions
diff --git a/drivers/gpio/pca9539.c b/drivers/gpio/pca9539.c new file mode 100644 index 000000000000..3e85c92a7d59 --- /dev/null +++ b/drivers/gpio/pca9539.c | |||
@@ -0,0 +1,271 @@ | |||
1 | /* | ||
2 | * pca9539.c - 16-bit I/O port with interrupt and reset | ||
3 | * | ||
4 | * Copyright (C) 2005 Ben Gardner <bgardner@wabtec.com> | ||
5 | * Copyright (C) 2007 Marvell International Ltd. | ||
6 | * | ||
7 | * Derived from drivers/i2c/chips/pca9539.c | ||
8 | * | ||
9 | * This program is free software; you can redistribute it and/or modify | ||
10 | * it under the terms of the GNU General Public License as published by | ||
11 | * the Free Software Foundation; version 2 of the License. | ||
12 | */ | ||
13 | |||
14 | #include <linux/module.h> | ||
15 | #include <linux/init.h> | ||
16 | #include <linux/i2c.h> | ||
17 | #include <linux/i2c/pca9539.h> | ||
18 | |||
19 | #include <asm/gpio.h> | ||
20 | |||
21 | |||
22 | #define NR_PCA9539_GPIOS 16 | ||
23 | |||
24 | #define PCA9539_INPUT 0 | ||
25 | #define PCA9539_OUTPUT 2 | ||
26 | #define PCA9539_INVERT 4 | ||
27 | #define PCA9539_DIRECTION 6 | ||
28 | |||
29 | struct pca9539_chip { | ||
30 | unsigned gpio_start; | ||
31 | uint16_t reg_output; | ||
32 | uint16_t reg_direction; | ||
33 | |||
34 | struct i2c_client *client; | ||
35 | struct gpio_chip gpio_chip; | ||
36 | }; | ||
37 | |||
38 | /* NOTE: we can't currently rely on fault codes to come from SMBus | ||
39 | * calls, so we map all errors to EIO here and return zero otherwise. | ||
40 | */ | ||
41 | static int pca9539_write_reg(struct pca9539_chip *chip, int reg, uint16_t val) | ||
42 | { | ||
43 | if (i2c_smbus_write_word_data(chip->client, reg, val) < 0) | ||
44 | return -EIO; | ||
45 | else | ||
46 | return 0; | ||
47 | } | ||
48 | |||
49 | static int pca9539_read_reg(struct pca9539_chip *chip, int reg, uint16_t *val) | ||
50 | { | ||
51 | int ret; | ||
52 | |||
53 | ret = i2c_smbus_read_word_data(chip->client, reg); | ||
54 | if (ret < 0) { | ||
55 | dev_err(&chip->client->dev, "failed reading register\n"); | ||
56 | return -EIO; | ||
57 | } | ||
58 | |||
59 | *val = (uint16_t)ret; | ||
60 | return 0; | ||
61 | } | ||
62 | |||
63 | static int pca9539_gpio_direction_input(struct gpio_chip *gc, unsigned off) | ||
64 | { | ||
65 | struct pca9539_chip *chip; | ||
66 | uint16_t reg_val; | ||
67 | int ret; | ||
68 | |||
69 | chip = container_of(gc, struct pca9539_chip, gpio_chip); | ||
70 | |||
71 | reg_val = chip->reg_direction | (1u << off); | ||
72 | ret = pca9539_write_reg(chip, PCA9539_DIRECTION, reg_val); | ||
73 | if (ret) | ||
74 | return ret; | ||
75 | |||
76 | chip->reg_direction = reg_val; | ||
77 | return 0; | ||
78 | } | ||
79 | |||
80 | static int pca9539_gpio_direction_output(struct gpio_chip *gc, | ||
81 | unsigned off, int val) | ||
82 | { | ||
83 | struct pca9539_chip *chip; | ||
84 | uint16_t reg_val; | ||
85 | int ret; | ||
86 | |||
87 | chip = container_of(gc, struct pca9539_chip, gpio_chip); | ||
88 | |||
89 | /* set output level */ | ||
90 | if (val) | ||
91 | reg_val = chip->reg_output | (1u << off); | ||
92 | else | ||
93 | reg_val = chip->reg_output & ~(1u << off); | ||
94 | |||
95 | ret = pca9539_write_reg(chip, PCA9539_OUTPUT, reg_val); | ||
96 | if (ret) | ||
97 | return ret; | ||
98 | |||
99 | chip->reg_output = reg_val; | ||
100 | |||
101 | /* then direction */ | ||
102 | reg_val = chip->reg_direction & ~(1u << off); | ||
103 | ret = pca9539_write_reg(chip, PCA9539_DIRECTION, reg_val); | ||
104 | if (ret) | ||
105 | return ret; | ||
106 | |||
107 | chip->reg_direction = reg_val; | ||
108 | return 0; | ||
109 | } | ||
110 | |||
111 | static int pca9539_gpio_get_value(struct gpio_chip *gc, unsigned off) | ||
112 | { | ||
113 | struct pca9539_chip *chip; | ||
114 | uint16_t reg_val; | ||
115 | int ret; | ||
116 | |||
117 | chip = container_of(gc, struct pca9539_chip, gpio_chip); | ||
118 | |||
119 | ret = pca9539_read_reg(chip, PCA9539_INPUT, ®_val); | ||
120 | if (ret < 0) { | ||
121 | /* NOTE: diagnostic already emitted; that's all we should | ||
122 | * do unless gpio_*_value_cansleep() calls become different | ||
123 | * from their nonsleeping siblings (and report faults). | ||
124 | */ | ||
125 | return 0; | ||
126 | } | ||
127 | |||
128 | return (reg_val & (1u << off)) ? 1 : 0; | ||
129 | } | ||
130 | |||
131 | static void pca9539_gpio_set_value(struct gpio_chip *gc, unsigned off, int val) | ||
132 | { | ||
133 | struct pca9539_chip *chip; | ||
134 | uint16_t reg_val; | ||
135 | int ret; | ||
136 | |||
137 | chip = container_of(gc, struct pca9539_chip, gpio_chip); | ||
138 | |||
139 | if (val) | ||
140 | reg_val = chip->reg_output | (1u << off); | ||
141 | else | ||
142 | reg_val = chip->reg_output & ~(1u << off); | ||
143 | |||
144 | ret = pca9539_write_reg(chip, PCA9539_OUTPUT, reg_val); | ||
145 | if (ret) | ||
146 | return; | ||
147 | |||
148 | chip->reg_output = reg_val; | ||
149 | } | ||
150 | |||
151 | static int pca9539_init_gpio(struct pca9539_chip *chip) | ||
152 | { | ||
153 | struct gpio_chip *gc; | ||
154 | |||
155 | gc = &chip->gpio_chip; | ||
156 | |||
157 | gc->direction_input = pca9539_gpio_direction_input; | ||
158 | gc->direction_output = pca9539_gpio_direction_output; | ||
159 | gc->get = pca9539_gpio_get_value; | ||
160 | gc->set = pca9539_gpio_set_value; | ||
161 | |||
162 | gc->base = chip->gpio_start; | ||
163 | gc->ngpio = NR_PCA9539_GPIOS; | ||
164 | gc->label = "pca9539"; | ||
165 | |||
166 | return gpiochip_add(gc); | ||
167 | } | ||
168 | |||
169 | static int __devinit pca9539_probe(struct i2c_client *client) | ||
170 | { | ||
171 | struct pca9539_platform_data *pdata; | ||
172 | struct pca9539_chip *chip; | ||
173 | int ret; | ||
174 | |||
175 | pdata = client->dev.platform_data; | ||
176 | if (pdata == NULL) | ||
177 | return -ENODEV; | ||
178 | |||
179 | chip = kzalloc(sizeof(struct pca9539_chip), GFP_KERNEL); | ||
180 | if (chip == NULL) | ||
181 | return -ENOMEM; | ||
182 | |||
183 | chip->client = client; | ||
184 | |||
185 | chip->gpio_start = pdata->gpio_base; | ||
186 | |||
187 | /* initialize cached registers from their original values. | ||
188 | * we can't share this chip with another i2c master. | ||
189 | */ | ||
190 | ret = pca9539_read_reg(chip, PCA9539_OUTPUT, &chip->reg_output); | ||
191 | if (ret) | ||
192 | goto out_failed; | ||
193 | |||
194 | ret = pca9539_read_reg(chip, PCA9539_DIRECTION, &chip->reg_direction); | ||
195 | if (ret) | ||
196 | goto out_failed; | ||
197 | |||
198 | /* set platform specific polarity inversion */ | ||
199 | ret = pca9539_write_reg(chip, PCA9539_INVERT, pdata->invert); | ||
200 | if (ret) | ||
201 | goto out_failed; | ||
202 | |||
203 | ret = pca9539_init_gpio(chip); | ||
204 | if (ret) | ||
205 | goto out_failed; | ||
206 | |||
207 | if (pdata->setup) { | ||
208 | ret = pdata->setup(client, chip->gpio_chip.base, | ||
209 | chip->gpio_chip.ngpio, pdata->context); | ||
210 | if (ret < 0) | ||
211 | dev_warn(&client->dev, "setup failed, %d\n", ret); | ||
212 | } | ||
213 | |||
214 | i2c_set_clientdata(client, chip); | ||
215 | return 0; | ||
216 | |||
217 | out_failed: | ||
218 | kfree(chip); | ||
219 | return ret; | ||
220 | } | ||
221 | |||
222 | static int pca9539_remove(struct i2c_client *client) | ||
223 | { | ||
224 | struct pca9539_platform_data *pdata = client->dev.platform_data; | ||
225 | struct pca9539_chip *chip = i2c_get_clientdata(client); | ||
226 | int ret = 0; | ||
227 | |||
228 | if (pdata->teardown) { | ||
229 | ret = pdata->teardown(client, chip->gpio_chip.base, | ||
230 | chip->gpio_chip.ngpio, pdata->context); | ||
231 | if (ret < 0) { | ||
232 | dev_err(&client->dev, "%s failed, %d\n", | ||
233 | "teardown", ret); | ||
234 | return ret; | ||
235 | } | ||
236 | } | ||
237 | |||
238 | ret = gpiochip_remove(&chip->gpio_chip); | ||
239 | if (ret) { | ||
240 | dev_err(&client->dev, "%s failed, %d\n", | ||
241 | "gpiochip_remove()", ret); | ||
242 | return ret; | ||
243 | } | ||
244 | |||
245 | kfree(chip); | ||
246 | return 0; | ||
247 | } | ||
248 | |||
249 | static struct i2c_driver pca9539_driver = { | ||
250 | .driver = { | ||
251 | .name = "pca9539", | ||
252 | }, | ||
253 | .probe = pca9539_probe, | ||
254 | .remove = pca9539_remove, | ||
255 | }; | ||
256 | |||
257 | static int __init pca9539_init(void) | ||
258 | { | ||
259 | return i2c_add_driver(&pca9539_driver); | ||
260 | } | ||
261 | module_init(pca9539_init); | ||
262 | |||
263 | static void __exit pca9539_exit(void) | ||
264 | { | ||
265 | i2c_del_driver(&pca9539_driver); | ||
266 | } | ||
267 | module_exit(pca9539_exit); | ||
268 | |||
269 | MODULE_AUTHOR("eric miao <eric.miao@marvell.com>"); | ||
270 | MODULE_DESCRIPTION("GPIO expander driver for PCA9539"); | ||
271 | MODULE_LICENSE("GPL"); | ||