diff options
Diffstat (limited to 'drivers/gpio/gpio-mcp23s08.c')
-rw-r--r-- | drivers/gpio/gpio-mcp23s08.c | 723 |
1 files changed, 723 insertions, 0 deletions
diff --git a/drivers/gpio/gpio-mcp23s08.c b/drivers/gpio/gpio-mcp23s08.c new file mode 100644 index 00000000000..1ef46e6c2a2 --- /dev/null +++ b/drivers/gpio/gpio-mcp23s08.c | |||
@@ -0,0 +1,723 @@ | |||
1 | /* | ||
2 | * MCP23S08 SPI/GPIO gpio expander driver | ||
3 | */ | ||
4 | |||
5 | #include <linux/kernel.h> | ||
6 | #include <linux/device.h> | ||
7 | #include <linux/mutex.h> | ||
8 | #include <linux/gpio.h> | ||
9 | #include <linux/i2c.h> | ||
10 | #include <linux/spi/spi.h> | ||
11 | #include <linux/spi/mcp23s08.h> | ||
12 | #include <linux/slab.h> | ||
13 | #include <asm/byteorder.h> | ||
14 | |||
15 | /** | ||
16 | * MCP types supported by driver | ||
17 | */ | ||
18 | #define MCP_TYPE_S08 0 | ||
19 | #define MCP_TYPE_S17 1 | ||
20 | #define MCP_TYPE_008 2 | ||
21 | #define MCP_TYPE_017 3 | ||
22 | |||
23 | /* Registers are all 8 bits wide. | ||
24 | * | ||
25 | * The mcp23s17 has twice as many bits, and can be configured to work | ||
26 | * with either 16 bit registers or with two adjacent 8 bit banks. | ||
27 | */ | ||
28 | #define MCP_IODIR 0x00 /* init/reset: all ones */ | ||
29 | #define MCP_IPOL 0x01 | ||
30 | #define MCP_GPINTEN 0x02 | ||
31 | #define MCP_DEFVAL 0x03 | ||
32 | #define MCP_INTCON 0x04 | ||
33 | #define MCP_IOCON 0x05 | ||
34 | # define IOCON_SEQOP (1 << 5) | ||
35 | # define IOCON_HAEN (1 << 3) | ||
36 | # define IOCON_ODR (1 << 2) | ||
37 | # define IOCON_INTPOL (1 << 1) | ||
38 | #define MCP_GPPU 0x06 | ||
39 | #define MCP_INTF 0x07 | ||
40 | #define MCP_INTCAP 0x08 | ||
41 | #define MCP_GPIO 0x09 | ||
42 | #define MCP_OLAT 0x0a | ||
43 | |||
44 | struct mcp23s08; | ||
45 | |||
46 | struct mcp23s08_ops { | ||
47 | int (*read)(struct mcp23s08 *mcp, unsigned reg); | ||
48 | int (*write)(struct mcp23s08 *mcp, unsigned reg, unsigned val); | ||
49 | int (*read_regs)(struct mcp23s08 *mcp, unsigned reg, | ||
50 | u16 *vals, unsigned n); | ||
51 | }; | ||
52 | |||
53 | struct mcp23s08 { | ||
54 | u8 addr; | ||
55 | |||
56 | u16 cache[11]; | ||
57 | /* lock protects the cached values */ | ||
58 | struct mutex lock; | ||
59 | |||
60 | struct gpio_chip chip; | ||
61 | |||
62 | const struct mcp23s08_ops *ops; | ||
63 | void *data; /* ops specific data */ | ||
64 | }; | ||
65 | |||
66 | /* A given spi_device can represent up to eight mcp23sxx chips | ||
67 | * sharing the same chipselect but using different addresses | ||
68 | * (e.g. chips #0 and #3 might be populated, but not #1 or $2). | ||
69 | * Driver data holds all the per-chip data. | ||
70 | */ | ||
71 | struct mcp23s08_driver_data { | ||
72 | unsigned ngpio; | ||
73 | struct mcp23s08 *mcp[8]; | ||
74 | struct mcp23s08 chip[]; | ||
75 | }; | ||
76 | |||
77 | /*----------------------------------------------------------------------*/ | ||
78 | |||
79 | #ifdef CONFIG_I2C | ||
80 | |||
81 | static int mcp23008_read(struct mcp23s08 *mcp, unsigned reg) | ||
82 | { | ||
83 | return i2c_smbus_read_byte_data(mcp->data, reg); | ||
84 | } | ||
85 | |||
86 | static int mcp23008_write(struct mcp23s08 *mcp, unsigned reg, unsigned val) | ||
87 | { | ||
88 | return i2c_smbus_write_byte_data(mcp->data, reg, val); | ||
89 | } | ||
90 | |||
91 | static int | ||
92 | mcp23008_read_regs(struct mcp23s08 *mcp, unsigned reg, u16 *vals, unsigned n) | ||
93 | { | ||
94 | while (n--) { | ||
95 | int ret = mcp23008_read(mcp, reg++); | ||
96 | if (ret < 0) | ||
97 | return ret; | ||
98 | *vals++ = ret; | ||
99 | } | ||
100 | |||
101 | return 0; | ||
102 | } | ||
103 | |||
104 | static int mcp23017_read(struct mcp23s08 *mcp, unsigned reg) | ||
105 | { | ||
106 | return i2c_smbus_read_word_data(mcp->data, reg << 1); | ||
107 | } | ||
108 | |||
109 | static int mcp23017_write(struct mcp23s08 *mcp, unsigned reg, unsigned val) | ||
110 | { | ||
111 | return i2c_smbus_write_word_data(mcp->data, reg << 1, val); | ||
112 | } | ||
113 | |||
114 | static int | ||
115 | mcp23017_read_regs(struct mcp23s08 *mcp, unsigned reg, u16 *vals, unsigned n) | ||
116 | { | ||
117 | while (n--) { | ||
118 | int ret = mcp23017_read(mcp, reg++); | ||
119 | if (ret < 0) | ||
120 | return ret; | ||
121 | *vals++ = ret; | ||
122 | } | ||
123 | |||
124 | return 0; | ||
125 | } | ||
126 | |||
127 | static const struct mcp23s08_ops mcp23008_ops = { | ||
128 | .read = mcp23008_read, | ||
129 | .write = mcp23008_write, | ||
130 | .read_regs = mcp23008_read_regs, | ||
131 | }; | ||
132 | |||
133 | static const struct mcp23s08_ops mcp23017_ops = { | ||
134 | .read = mcp23017_read, | ||
135 | .write = mcp23017_write, | ||
136 | .read_regs = mcp23017_read_regs, | ||
137 | }; | ||
138 | |||
139 | #endif /* CONFIG_I2C */ | ||
140 | |||
141 | /*----------------------------------------------------------------------*/ | ||
142 | |||
143 | #ifdef CONFIG_SPI_MASTER | ||
144 | |||
145 | static int mcp23s08_read(struct mcp23s08 *mcp, unsigned reg) | ||
146 | { | ||
147 | u8 tx[2], rx[1]; | ||
148 | int status; | ||
149 | |||
150 | tx[0] = mcp->addr | 0x01; | ||
151 | tx[1] = reg; | ||
152 | status = spi_write_then_read(mcp->data, tx, sizeof tx, rx, sizeof rx); | ||
153 | return (status < 0) ? status : rx[0]; | ||
154 | } | ||
155 | |||
156 | static int mcp23s08_write(struct mcp23s08 *mcp, unsigned reg, unsigned val) | ||
157 | { | ||
158 | u8 tx[3]; | ||
159 | |||
160 | tx[0] = mcp->addr; | ||
161 | tx[1] = reg; | ||
162 | tx[2] = val; | ||
163 | return spi_write_then_read(mcp->data, tx, sizeof tx, NULL, 0); | ||
164 | } | ||
165 | |||
166 | static int | ||
167 | mcp23s08_read_regs(struct mcp23s08 *mcp, unsigned reg, u16 *vals, unsigned n) | ||
168 | { | ||
169 | u8 tx[2], *tmp; | ||
170 | int status; | ||
171 | |||
172 | if ((n + reg) > sizeof mcp->cache) | ||
173 | return -EINVAL; | ||
174 | tx[0] = mcp->addr | 0x01; | ||
175 | tx[1] = reg; | ||
176 | |||
177 | tmp = (u8 *)vals; | ||
178 | status = spi_write_then_read(mcp->data, tx, sizeof tx, tmp, n); | ||
179 | if (status >= 0) { | ||
180 | while (n--) | ||
181 | vals[n] = tmp[n]; /* expand to 16bit */ | ||
182 | } | ||
183 | return status; | ||
184 | } | ||
185 | |||
186 | static int mcp23s17_read(struct mcp23s08 *mcp, unsigned reg) | ||
187 | { | ||
188 | u8 tx[2], rx[2]; | ||
189 | int status; | ||
190 | |||
191 | tx[0] = mcp->addr | 0x01; | ||
192 | tx[1] = reg << 1; | ||
193 | status = spi_write_then_read(mcp->data, tx, sizeof tx, rx, sizeof rx); | ||
194 | return (status < 0) ? status : (rx[0] | (rx[1] << 8)); | ||
195 | } | ||
196 | |||
197 | static int mcp23s17_write(struct mcp23s08 *mcp, unsigned reg, unsigned val) | ||
198 | { | ||
199 | u8 tx[4]; | ||
200 | |||
201 | tx[0] = mcp->addr; | ||
202 | tx[1] = reg << 1; | ||
203 | tx[2] = val; | ||
204 | tx[3] = val >> 8; | ||
205 | return spi_write_then_read(mcp->data, tx, sizeof tx, NULL, 0); | ||
206 | } | ||
207 | |||
208 | static int | ||
209 | mcp23s17_read_regs(struct mcp23s08 *mcp, unsigned reg, u16 *vals, unsigned n) | ||
210 | { | ||
211 | u8 tx[2]; | ||
212 | int status; | ||
213 | |||
214 | if ((n + reg) > sizeof mcp->cache) | ||
215 | return -EINVAL; | ||
216 | tx[0] = mcp->addr | 0x01; | ||
217 | tx[1] = reg << 1; | ||
218 | |||
219 | status = spi_write_then_read(mcp->data, tx, sizeof tx, | ||
220 | (u8 *)vals, n * 2); | ||
221 | if (status >= 0) { | ||
222 | while (n--) | ||
223 | vals[n] = __le16_to_cpu((__le16)vals[n]); | ||
224 | } | ||
225 | |||
226 | return status; | ||
227 | } | ||
228 | |||
229 | static const struct mcp23s08_ops mcp23s08_ops = { | ||
230 | .read = mcp23s08_read, | ||
231 | .write = mcp23s08_write, | ||
232 | .read_regs = mcp23s08_read_regs, | ||
233 | }; | ||
234 | |||
235 | static const struct mcp23s08_ops mcp23s17_ops = { | ||
236 | .read = mcp23s17_read, | ||
237 | .write = mcp23s17_write, | ||
238 | .read_regs = mcp23s17_read_regs, | ||
239 | }; | ||
240 | |||
241 | #endif /* CONFIG_SPI_MASTER */ | ||
242 | |||
243 | /*----------------------------------------------------------------------*/ | ||
244 | |||
245 | static int mcp23s08_direction_input(struct gpio_chip *chip, unsigned offset) | ||
246 | { | ||
247 | struct mcp23s08 *mcp = container_of(chip, struct mcp23s08, chip); | ||
248 | int status; | ||
249 | |||
250 | mutex_lock(&mcp->lock); | ||
251 | mcp->cache[MCP_IODIR] |= (1 << offset); | ||
252 | status = mcp->ops->write(mcp, MCP_IODIR, mcp->cache[MCP_IODIR]); | ||
253 | mutex_unlock(&mcp->lock); | ||
254 | return status; | ||
255 | } | ||
256 | |||
257 | static int mcp23s08_get(struct gpio_chip *chip, unsigned offset) | ||
258 | { | ||
259 | struct mcp23s08 *mcp = container_of(chip, struct mcp23s08, chip); | ||
260 | int status; | ||
261 | |||
262 | mutex_lock(&mcp->lock); | ||
263 | |||
264 | /* REVISIT reading this clears any IRQ ... */ | ||
265 | status = mcp->ops->read(mcp, MCP_GPIO); | ||
266 | if (status < 0) | ||
267 | status = 0; | ||
268 | else { | ||
269 | mcp->cache[MCP_GPIO] = status; | ||
270 | status = !!(status & (1 << offset)); | ||
271 | } | ||
272 | mutex_unlock(&mcp->lock); | ||
273 | return status; | ||
274 | } | ||
275 | |||
276 | static int __mcp23s08_set(struct mcp23s08 *mcp, unsigned mask, int value) | ||
277 | { | ||
278 | unsigned olat = mcp->cache[MCP_OLAT]; | ||
279 | |||
280 | if (value) | ||
281 | olat |= mask; | ||
282 | else | ||
283 | olat &= ~mask; | ||
284 | mcp->cache[MCP_OLAT] = olat; | ||
285 | return mcp->ops->write(mcp, MCP_OLAT, olat); | ||
286 | } | ||
287 | |||
288 | static void mcp23s08_set(struct gpio_chip *chip, unsigned offset, int value) | ||
289 | { | ||
290 | struct mcp23s08 *mcp = container_of(chip, struct mcp23s08, chip); | ||
291 | unsigned mask = 1 << offset; | ||
292 | |||
293 | mutex_lock(&mcp->lock); | ||
294 | __mcp23s08_set(mcp, mask, value); | ||
295 | mutex_unlock(&mcp->lock); | ||
296 | } | ||
297 | |||
298 | static int | ||
299 | mcp23s08_direction_output(struct gpio_chip *chip, unsigned offset, int value) | ||
300 | { | ||
301 | struct mcp23s08 *mcp = container_of(chip, struct mcp23s08, chip); | ||
302 | unsigned mask = 1 << offset; | ||
303 | int status; | ||
304 | |||
305 | mutex_lock(&mcp->lock); | ||
306 | status = __mcp23s08_set(mcp, mask, value); | ||
307 | if (status == 0) { | ||
308 | mcp->cache[MCP_IODIR] &= ~mask; | ||
309 | status = mcp->ops->write(mcp, MCP_IODIR, mcp->cache[MCP_IODIR]); | ||
310 | } | ||
311 | mutex_unlock(&mcp->lock); | ||
312 | return status; | ||
313 | } | ||
314 | |||
315 | /*----------------------------------------------------------------------*/ | ||
316 | |||
317 | #ifdef CONFIG_DEBUG_FS | ||
318 | |||
319 | #include <linux/seq_file.h> | ||
320 | |||
321 | /* | ||
322 | * This shows more info than the generic gpio dump code: | ||
323 | * pullups, deglitching, open drain drive. | ||
324 | */ | ||
325 | static void mcp23s08_dbg_show(struct seq_file *s, struct gpio_chip *chip) | ||
326 | { | ||
327 | struct mcp23s08 *mcp; | ||
328 | char bank; | ||
329 | int t; | ||
330 | unsigned mask; | ||
331 | |||
332 | mcp = container_of(chip, struct mcp23s08, chip); | ||
333 | |||
334 | /* NOTE: we only handle one bank for now ... */ | ||
335 | bank = '0' + ((mcp->addr >> 1) & 0x7); | ||
336 | |||
337 | mutex_lock(&mcp->lock); | ||
338 | t = mcp->ops->read_regs(mcp, 0, mcp->cache, ARRAY_SIZE(mcp->cache)); | ||
339 | if (t < 0) { | ||
340 | seq_printf(s, " I/O ERROR %d\n", t); | ||
341 | goto done; | ||
342 | } | ||
343 | |||
344 | for (t = 0, mask = 1; t < chip->ngpio; t++, mask <<= 1) { | ||
345 | const char *label; | ||
346 | |||
347 | label = gpiochip_is_requested(chip, t); | ||
348 | if (!label) | ||
349 | continue; | ||
350 | |||
351 | seq_printf(s, " gpio-%-3d P%c.%d (%-12s) %s %s %s", | ||
352 | chip->base + t, bank, t, label, | ||
353 | (mcp->cache[MCP_IODIR] & mask) ? "in " : "out", | ||
354 | (mcp->cache[MCP_GPIO] & mask) ? "hi" : "lo", | ||
355 | (mcp->cache[MCP_GPPU] & mask) ? " " : "up"); | ||
356 | /* NOTE: ignoring the irq-related registers */ | ||
357 | seq_printf(s, "\n"); | ||
358 | } | ||
359 | done: | ||
360 | mutex_unlock(&mcp->lock); | ||
361 | } | ||
362 | |||
363 | #else | ||
364 | #define mcp23s08_dbg_show NULL | ||
365 | #endif | ||
366 | |||
367 | /*----------------------------------------------------------------------*/ | ||
368 | |||
369 | static int mcp23s08_probe_one(struct mcp23s08 *mcp, struct device *dev, | ||
370 | void *data, unsigned addr, | ||
371 | unsigned type, unsigned base, unsigned pullups) | ||
372 | { | ||
373 | int status; | ||
374 | |||
375 | mutex_init(&mcp->lock); | ||
376 | |||
377 | mcp->data = data; | ||
378 | mcp->addr = addr; | ||
379 | |||
380 | mcp->chip.direction_input = mcp23s08_direction_input; | ||
381 | mcp->chip.get = mcp23s08_get; | ||
382 | mcp->chip.direction_output = mcp23s08_direction_output; | ||
383 | mcp->chip.set = mcp23s08_set; | ||
384 | mcp->chip.dbg_show = mcp23s08_dbg_show; | ||
385 | |||
386 | switch (type) { | ||
387 | #ifdef CONFIG_SPI_MASTER | ||
388 | case MCP_TYPE_S08: | ||
389 | mcp->ops = &mcp23s08_ops; | ||
390 | mcp->chip.ngpio = 8; | ||
391 | mcp->chip.label = "mcp23s08"; | ||
392 | break; | ||
393 | |||
394 | case MCP_TYPE_S17: | ||
395 | mcp->ops = &mcp23s17_ops; | ||
396 | mcp->chip.ngpio = 16; | ||
397 | mcp->chip.label = "mcp23s17"; | ||
398 | break; | ||
399 | #endif /* CONFIG_SPI_MASTER */ | ||
400 | |||
401 | #ifdef CONFIG_I2C | ||
402 | case MCP_TYPE_008: | ||
403 | mcp->ops = &mcp23008_ops; | ||
404 | mcp->chip.ngpio = 8; | ||
405 | mcp->chip.label = "mcp23008"; | ||
406 | break; | ||
407 | |||
408 | case MCP_TYPE_017: | ||
409 | mcp->ops = &mcp23017_ops; | ||
410 | mcp->chip.ngpio = 16; | ||
411 | mcp->chip.label = "mcp23017"; | ||
412 | break; | ||
413 | #endif /* CONFIG_I2C */ | ||
414 | |||
415 | default: | ||
416 | dev_err(dev, "invalid device type (%d)\n", type); | ||
417 | return -EINVAL; | ||
418 | } | ||
419 | |||
420 | mcp->chip.base = base; | ||
421 | mcp->chip.can_sleep = 1; | ||
422 | mcp->chip.dev = dev; | ||
423 | mcp->chip.owner = THIS_MODULE; | ||
424 | |||
425 | /* verify MCP_IOCON.SEQOP = 0, so sequential reads work, | ||
426 | * and MCP_IOCON.HAEN = 1, so we work with all chips. | ||
427 | */ | ||
428 | status = mcp->ops->read(mcp, MCP_IOCON); | ||
429 | if (status < 0) | ||
430 | goto fail; | ||
431 | if ((status & IOCON_SEQOP) || !(status & IOCON_HAEN)) { | ||
432 | /* mcp23s17 has IOCON twice, make sure they are in sync */ | ||
433 | status &= ~(IOCON_SEQOP | (IOCON_SEQOP << 8)); | ||
434 | status |= IOCON_HAEN | (IOCON_HAEN << 8); | ||
435 | status = mcp->ops->write(mcp, MCP_IOCON, status); | ||
436 | if (status < 0) | ||
437 | goto fail; | ||
438 | } | ||
439 | |||
440 | /* configure ~100K pullups */ | ||
441 | status = mcp->ops->write(mcp, MCP_GPPU, pullups); | ||
442 | if (status < 0) | ||
443 | goto fail; | ||
444 | |||
445 | status = mcp->ops->read_regs(mcp, 0, mcp->cache, ARRAY_SIZE(mcp->cache)); | ||
446 | if (status < 0) | ||
447 | goto fail; | ||
448 | |||
449 | /* disable inverter on input */ | ||
450 | if (mcp->cache[MCP_IPOL] != 0) { | ||
451 | mcp->cache[MCP_IPOL] = 0; | ||
452 | status = mcp->ops->write(mcp, MCP_IPOL, 0); | ||
453 | if (status < 0) | ||
454 | goto fail; | ||
455 | } | ||
456 | |||
457 | /* disable irqs */ | ||
458 | if (mcp->cache[MCP_GPINTEN] != 0) { | ||
459 | mcp->cache[MCP_GPINTEN] = 0; | ||
460 | status = mcp->ops->write(mcp, MCP_GPINTEN, 0); | ||
461 | if (status < 0) | ||
462 | goto fail; | ||
463 | } | ||
464 | |||
465 | status = gpiochip_add(&mcp->chip); | ||
466 | fail: | ||
467 | if (status < 0) | ||
468 | dev_dbg(dev, "can't setup chip %d, --> %d\n", | ||
469 | addr, status); | ||
470 | return status; | ||
471 | } | ||
472 | |||
473 | /*----------------------------------------------------------------------*/ | ||
474 | |||
475 | #ifdef CONFIG_I2C | ||
476 | |||
477 | static int __devinit mcp230xx_probe(struct i2c_client *client, | ||
478 | const struct i2c_device_id *id) | ||
479 | { | ||
480 | struct mcp23s08_platform_data *pdata; | ||
481 | struct mcp23s08 *mcp; | ||
482 | int status; | ||
483 | |||
484 | pdata = client->dev.platform_data; | ||
485 | if (!pdata || !gpio_is_valid(pdata->base)) { | ||
486 | dev_dbg(&client->dev, "invalid or missing platform data\n"); | ||
487 | return -EINVAL; | ||
488 | } | ||
489 | |||
490 | mcp = kzalloc(sizeof *mcp, GFP_KERNEL); | ||
491 | if (!mcp) | ||
492 | return -ENOMEM; | ||
493 | |||
494 | status = mcp23s08_probe_one(mcp, &client->dev, client, client->addr, | ||
495 | id->driver_data, pdata->base, | ||
496 | pdata->chip[0].pullups); | ||
497 | if (status) | ||
498 | goto fail; | ||
499 | |||
500 | i2c_set_clientdata(client, mcp); | ||
501 | |||
502 | return 0; | ||
503 | |||
504 | fail: | ||
505 | kfree(mcp); | ||
506 | |||
507 | return status; | ||
508 | } | ||
509 | |||
510 | static int __devexit mcp230xx_remove(struct i2c_client *client) | ||
511 | { | ||
512 | struct mcp23s08 *mcp = i2c_get_clientdata(client); | ||
513 | int status; | ||
514 | |||
515 | status = gpiochip_remove(&mcp->chip); | ||
516 | if (status == 0) | ||
517 | kfree(mcp); | ||
518 | |||
519 | return status; | ||
520 | } | ||
521 | |||
522 | static const struct i2c_device_id mcp230xx_id[] = { | ||
523 | { "mcp23008", MCP_TYPE_008 }, | ||
524 | { "mcp23017", MCP_TYPE_017 }, | ||
525 | { }, | ||
526 | }; | ||
527 | MODULE_DEVICE_TABLE(i2c, mcp230xx_id); | ||
528 | |||
529 | static struct i2c_driver mcp230xx_driver = { | ||
530 | .driver = { | ||
531 | .name = "mcp230xx", | ||
532 | .owner = THIS_MODULE, | ||
533 | }, | ||
534 | .probe = mcp230xx_probe, | ||
535 | .remove = __devexit_p(mcp230xx_remove), | ||
536 | .id_table = mcp230xx_id, | ||
537 | }; | ||
538 | |||
539 | static int __init mcp23s08_i2c_init(void) | ||
540 | { | ||
541 | return i2c_add_driver(&mcp230xx_driver); | ||
542 | } | ||
543 | |||
544 | static void mcp23s08_i2c_exit(void) | ||
545 | { | ||
546 | i2c_del_driver(&mcp230xx_driver); | ||
547 | } | ||
548 | |||
549 | #else | ||
550 | |||
551 | static int __init mcp23s08_i2c_init(void) { return 0; } | ||
552 | static void mcp23s08_i2c_exit(void) { } | ||
553 | |||
554 | #endif /* CONFIG_I2C */ | ||
555 | |||
556 | /*----------------------------------------------------------------------*/ | ||
557 | |||
558 | #ifdef CONFIG_SPI_MASTER | ||
559 | |||
560 | static int mcp23s08_probe(struct spi_device *spi) | ||
561 | { | ||
562 | struct mcp23s08_platform_data *pdata; | ||
563 | unsigned addr; | ||
564 | unsigned chips = 0; | ||
565 | struct mcp23s08_driver_data *data; | ||
566 | int status, type; | ||
567 | unsigned base; | ||
568 | |||
569 | type = spi_get_device_id(spi)->driver_data; | ||
570 | |||
571 | pdata = spi->dev.platform_data; | ||
572 | if (!pdata || !gpio_is_valid(pdata->base)) { | ||
573 | dev_dbg(&spi->dev, "invalid or missing platform data\n"); | ||
574 | return -EINVAL; | ||
575 | } | ||
576 | |||
577 | for (addr = 0; addr < ARRAY_SIZE(pdata->chip); addr++) { | ||
578 | if (!pdata->chip[addr].is_present) | ||
579 | continue; | ||
580 | chips++; | ||
581 | if ((type == MCP_TYPE_S08) && (addr > 3)) { | ||
582 | dev_err(&spi->dev, | ||
583 | "mcp23s08 only supports address 0..3\n"); | ||
584 | return -EINVAL; | ||
585 | } | ||
586 | } | ||
587 | if (!chips) | ||
588 | return -ENODEV; | ||
589 | |||
590 | data = kzalloc(sizeof *data + chips * sizeof(struct mcp23s08), | ||
591 | GFP_KERNEL); | ||
592 | if (!data) | ||
593 | return -ENOMEM; | ||
594 | spi_set_drvdata(spi, data); | ||
595 | |||
596 | base = pdata->base; | ||
597 | for (addr = 0; addr < ARRAY_SIZE(pdata->chip); addr++) { | ||
598 | if (!pdata->chip[addr].is_present) | ||
599 | continue; | ||
600 | chips--; | ||
601 | data->mcp[addr] = &data->chip[chips]; | ||
602 | status = mcp23s08_probe_one(data->mcp[addr], &spi->dev, spi, | ||
603 | 0x40 | (addr << 1), type, base, | ||
604 | pdata->chip[addr].pullups); | ||
605 | if (status < 0) | ||
606 | goto fail; | ||
607 | |||
608 | base += (type == MCP_TYPE_S17) ? 16 : 8; | ||
609 | } | ||
610 | data->ngpio = base - pdata->base; | ||
611 | |||
612 | /* NOTE: these chips have a relatively sane IRQ framework, with | ||
613 | * per-signal masking and level/edge triggering. It's not yet | ||
614 | * handled here... | ||
615 | */ | ||
616 | |||
617 | return 0; | ||
618 | |||
619 | fail: | ||
620 | for (addr = 0; addr < ARRAY_SIZE(data->mcp); addr++) { | ||
621 | int tmp; | ||
622 | |||
623 | if (!data->mcp[addr]) | ||
624 | continue; | ||
625 | tmp = gpiochip_remove(&data->mcp[addr]->chip); | ||
626 | if (tmp < 0) | ||
627 | dev_err(&spi->dev, "%s --> %d\n", "remove", tmp); | ||
628 | } | ||
629 | kfree(data); | ||
630 | return status; | ||
631 | } | ||
632 | |||
633 | static int mcp23s08_remove(struct spi_device *spi) | ||
634 | { | ||
635 | struct mcp23s08_driver_data *data = spi_get_drvdata(spi); | ||
636 | unsigned addr; | ||
637 | int status = 0; | ||
638 | |||
639 | for (addr = 0; addr < ARRAY_SIZE(data->mcp); addr++) { | ||
640 | int tmp; | ||
641 | |||
642 | if (!data->mcp[addr]) | ||
643 | continue; | ||
644 | |||
645 | tmp = gpiochip_remove(&data->mcp[addr]->chip); | ||
646 | if (tmp < 0) { | ||
647 | dev_err(&spi->dev, "%s --> %d\n", "remove", tmp); | ||
648 | status = tmp; | ||
649 | } | ||
650 | } | ||
651 | if (status == 0) | ||
652 | kfree(data); | ||
653 | return status; | ||
654 | } | ||
655 | |||
656 | static const struct spi_device_id mcp23s08_ids[] = { | ||
657 | { "mcp23s08", MCP_TYPE_S08 }, | ||
658 | { "mcp23s17", MCP_TYPE_S17 }, | ||
659 | { }, | ||
660 | }; | ||
661 | MODULE_DEVICE_TABLE(spi, mcp23s08_ids); | ||
662 | |||
663 | static struct spi_driver mcp23s08_driver = { | ||
664 | .probe = mcp23s08_probe, | ||
665 | .remove = mcp23s08_remove, | ||
666 | .id_table = mcp23s08_ids, | ||
667 | .driver = { | ||
668 | .name = "mcp23s08", | ||
669 | .owner = THIS_MODULE, | ||
670 | }, | ||
671 | }; | ||
672 | |||
673 | static int __init mcp23s08_spi_init(void) | ||
674 | { | ||
675 | return spi_register_driver(&mcp23s08_driver); | ||
676 | } | ||
677 | |||
678 | static void mcp23s08_spi_exit(void) | ||
679 | { | ||
680 | spi_unregister_driver(&mcp23s08_driver); | ||
681 | } | ||
682 | |||
683 | #else | ||
684 | |||
685 | static int __init mcp23s08_spi_init(void) { return 0; } | ||
686 | static void mcp23s08_spi_exit(void) { } | ||
687 | |||
688 | #endif /* CONFIG_SPI_MASTER */ | ||
689 | |||
690 | /*----------------------------------------------------------------------*/ | ||
691 | |||
692 | static int __init mcp23s08_init(void) | ||
693 | { | ||
694 | int ret; | ||
695 | |||
696 | ret = mcp23s08_spi_init(); | ||
697 | if (ret) | ||
698 | goto spi_fail; | ||
699 | |||
700 | ret = mcp23s08_i2c_init(); | ||
701 | if (ret) | ||
702 | goto i2c_fail; | ||
703 | |||
704 | return 0; | ||
705 | |||
706 | i2c_fail: | ||
707 | mcp23s08_spi_exit(); | ||
708 | spi_fail: | ||
709 | return ret; | ||
710 | } | ||
711 | /* register after spi/i2c postcore initcall and before | ||
712 | * subsys initcalls that may rely on these GPIOs | ||
713 | */ | ||
714 | subsys_initcall(mcp23s08_init); | ||
715 | |||
716 | static void __exit mcp23s08_exit(void) | ||
717 | { | ||
718 | mcp23s08_spi_exit(); | ||
719 | mcp23s08_i2c_exit(); | ||
720 | } | ||
721 | module_exit(mcp23s08_exit); | ||
722 | |||
723 | MODULE_LICENSE("GPL"); | ||