diff options
author | Peter Korsgaard <jacmet@sunsite.dk> | 2011-07-15 04:25:32 -0400 |
---|---|---|
committer | Grant Likely <grant.likely@secretlab.ca> | 2011-07-15 15:54:18 -0400 |
commit | 752ad5e82dfd83851e44a2b9da8761994cd7e61c (patch) | |
tree | de338b98720ac3a37f18b2db868a85e2b8cfe726 /drivers/gpio | |
parent | d62b98f305a6b0d32fbdc72ac6ba3d4f4768adeb (diff) |
mcp23s08: add i2c support
Add i2c bindings for the mcp230xx devices. This is quite a lot simpler
than the spi one as there's no funky sub addressing done (one struct
i2c_client per struct gpio_chip).
The mcp23s08_platform_data structure is reused for i2c, even though
only a single mcp23s08_chip_info structure is needed.
To use, simply fill out a platform_data structure and pass it in
i2c_board_info, E.G.:
static const struct mcp23s08_platform_data mcp23017_data = {
.chip[0] = {
.pullups = 0x00ff,
},
.base = 240,
};
static struct i2c_board_info __initdata i2c_devs[] = {
{ I2C_BOARD_INFO("mcp23017", 0x20),
.platform_data = &smartview_mcp23017_data, },
...
};
Signed-off-by: Peter Korsgaard <jacmet@sunsite.dk>
Signed-off-by: Grant Likely <grant.likely@secretlab.ca>
Diffstat (limited to 'drivers/gpio')
-rw-r--r-- | drivers/gpio/Kconfig | 7 | ||||
-rw-r--r-- | drivers/gpio/gpio-mcp23s08.c | 192 |
2 files changed, 191 insertions, 8 deletions
diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig index 47eae6ea5dd2..363498697c2c 100644 --- a/drivers/gpio/Kconfig +++ b/drivers/gpio/Kconfig | |||
@@ -412,10 +412,11 @@ config GPIO_MAX7301 | |||
412 | GPIO driver for Maxim MAX7301 SPI-based GPIO expander. | 412 | GPIO driver for Maxim MAX7301 SPI-based GPIO expander. |
413 | 413 | ||
414 | config GPIO_MCP23S08 | 414 | config GPIO_MCP23S08 |
415 | tristate "Microchip MCP23Sxx I/O expander" | 415 | tristate "Microchip MCP23xxx I/O expander" |
416 | depends on SPI_MASTER | 416 | depends on SPI_MASTER || I2C |
417 | help | 417 | help |
418 | SPI driver for Microchip MCP23S08/MPC23S17 I/O expanders. | 418 | SPI/I2C driver for Microchip MCP23S08/MCP23S17/MCP23008/MCP23017 |
419 | I/O expanders. | ||
419 | This provides a GPIO interface supporting inputs and outputs. | 420 | This provides a GPIO interface supporting inputs and outputs. |
420 | 421 | ||
421 | config GPIO_MC33880 | 422 | config GPIO_MC33880 |
diff --git a/drivers/gpio/gpio-mcp23s08.c b/drivers/gpio/gpio-mcp23s08.c index 7b78f940868e..1ef46e6c2a2a 100644 --- a/drivers/gpio/gpio-mcp23s08.c +++ b/drivers/gpio/gpio-mcp23s08.c | |||
@@ -1,11 +1,12 @@ | |||
1 | /* | 1 | /* |
2 | * MCP23S08 SPI gpio expander driver | 2 | * MCP23S08 SPI/GPIO gpio expander driver |
3 | */ | 3 | */ |
4 | 4 | ||
5 | #include <linux/kernel.h> | 5 | #include <linux/kernel.h> |
6 | #include <linux/device.h> | 6 | #include <linux/device.h> |
7 | #include <linux/mutex.h> | 7 | #include <linux/mutex.h> |
8 | #include <linux/gpio.h> | 8 | #include <linux/gpio.h> |
9 | #include <linux/i2c.h> | ||
9 | #include <linux/spi/spi.h> | 10 | #include <linux/spi/spi.h> |
10 | #include <linux/spi/mcp23s08.h> | 11 | #include <linux/spi/mcp23s08.h> |
11 | #include <linux/slab.h> | 12 | #include <linux/slab.h> |
@@ -16,13 +17,13 @@ | |||
16 | */ | 17 | */ |
17 | #define MCP_TYPE_S08 0 | 18 | #define MCP_TYPE_S08 0 |
18 | #define MCP_TYPE_S17 1 | 19 | #define MCP_TYPE_S17 1 |
20 | #define MCP_TYPE_008 2 | ||
21 | #define MCP_TYPE_017 3 | ||
19 | 22 | ||
20 | /* Registers are all 8 bits wide. | 23 | /* Registers are all 8 bits wide. |
21 | * | 24 | * |
22 | * The mcp23s17 has twice as many bits, and can be configured to work | 25 | * The mcp23s17 has twice as many bits, and can be configured to work |
23 | * with either 16 bit registers or with two adjacent 8 bit banks. | 26 | * with either 16 bit registers or with two adjacent 8 bit banks. |
24 | * | ||
25 | * Also, there are I2C versions of both chips. | ||
26 | */ | 27 | */ |
27 | #define MCP_IODIR 0x00 /* init/reset: all ones */ | 28 | #define MCP_IODIR 0x00 /* init/reset: all ones */ |
28 | #define MCP_IPOL 0x01 | 29 | #define MCP_IPOL 0x01 |
@@ -73,6 +74,72 @@ struct mcp23s08_driver_data { | |||
73 | struct mcp23s08 chip[]; | 74 | struct mcp23s08 chip[]; |
74 | }; | 75 | }; |
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 | |||
76 | #ifdef CONFIG_SPI_MASTER | 143 | #ifdef CONFIG_SPI_MASTER |
77 | 144 | ||
78 | static int mcp23s08_read(struct mcp23s08 *mcp, unsigned reg) | 145 | static int mcp23s08_read(struct mcp23s08 *mcp, unsigned reg) |
@@ -331,6 +398,20 @@ static int mcp23s08_probe_one(struct mcp23s08 *mcp, struct device *dev, | |||
331 | break; | 398 | break; |
332 | #endif /* CONFIG_SPI_MASTER */ | 399 | #endif /* CONFIG_SPI_MASTER */ |
333 | 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 | |||
334 | default: | 415 | default: |
335 | dev_err(dev, "invalid device type (%d)\n", type); | 416 | dev_err(dev, "invalid device type (%d)\n", type); |
336 | return -EINVAL; | 417 | return -EINVAL; |
@@ -389,6 +470,91 @@ fail: | |||
389 | return status; | 470 | return status; |
390 | } | 471 | } |
391 | 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 | |||
392 | #ifdef CONFIG_SPI_MASTER | 558 | #ifdef CONFIG_SPI_MASTER |
393 | 559 | ||
394 | static int mcp23s08_probe(struct spi_device *spi) | 560 | static int mcp23s08_probe(struct spi_device *spi) |
@@ -525,9 +691,24 @@ static void mcp23s08_spi_exit(void) { } | |||
525 | 691 | ||
526 | static int __init mcp23s08_init(void) | 692 | static int __init mcp23s08_init(void) |
527 | { | 693 | { |
528 | return mcp23s08_spi_init(); | 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; | ||
529 | } | 710 | } |
530 | /* register after spi postcore initcall and before | 711 | /* register after spi/i2c postcore initcall and before |
531 | * subsys initcalls that may rely on these GPIOs | 712 | * subsys initcalls that may rely on these GPIOs |
532 | */ | 713 | */ |
533 | subsys_initcall(mcp23s08_init); | 714 | subsys_initcall(mcp23s08_init); |
@@ -535,6 +716,7 @@ subsys_initcall(mcp23s08_init); | |||
535 | static void __exit mcp23s08_exit(void) | 716 | static void __exit mcp23s08_exit(void) |
536 | { | 717 | { |
537 | mcp23s08_spi_exit(); | 718 | mcp23s08_spi_exit(); |
719 | mcp23s08_i2c_exit(); | ||
538 | } | 720 | } |
539 | module_exit(mcp23s08_exit); | 721 | module_exit(mcp23s08_exit); |
540 | 722 | ||