aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/gpio/mcp23s08.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/gpio/mcp23s08.c')
-rw-r--r--drivers/gpio/mcp23s08.c191
1 files changed, 143 insertions, 48 deletions
diff --git a/drivers/gpio/mcp23s08.c b/drivers/gpio/mcp23s08.c
index 69f6f1955a3..40e076083ec 100644
--- a/drivers/gpio/mcp23s08.c
+++ b/drivers/gpio/mcp23s08.c
@@ -10,7 +10,13 @@
10#include <linux/spi/spi.h> 10#include <linux/spi/spi.h>
11#include <linux/spi/mcp23s08.h> 11#include <linux/spi/mcp23s08.h>
12#include <linux/slab.h> 12#include <linux/slab.h>
13#include <asm/byteorder.h>
13 14
15/**
16 * MCP types supported by driver
17 */
18#define MCP_TYPE_S08 0
19#define MCP_TYPE_S17 1
14 20
15/* Registers are all 8 bits wide. 21/* Registers are all 8 bits wide.
16 * 22 *
@@ -35,27 +41,38 @@
35#define MCP_GPIO 0x09 41#define MCP_GPIO 0x09
36#define MCP_OLAT 0x0a 42#define MCP_OLAT 0x0a
37 43
44struct mcp23s08;
45
46struct 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
38struct mcp23s08 { 53struct mcp23s08 {
39 struct spi_device *spi; 54 struct spi_device *spi;
40 u8 addr; 55 u8 addr;
41 56
42 u8 cache[11]; 57 u16 cache[11];
43 /* lock protects the cached values */ 58 /* lock protects the cached values */
44 struct mutex lock; 59 struct mutex lock;
45 60
46 struct gpio_chip chip; 61 struct gpio_chip chip;
47 62
48 struct work_struct work; 63 struct work_struct work;
64
65 const struct mcp23s08_ops *ops;
49}; 66};
50 67
51/* A given spi_device can represent up to four mcp23s08 chips 68/* A given spi_device can represent up to eight mcp23sxx chips
52 * sharing the same chipselect but using different addresses 69 * sharing the same chipselect but using different addresses
53 * (e.g. chips #0 and #3 might be populated, but not #1 or $2). 70 * (e.g. chips #0 and #3 might be populated, but not #1 or $2).
54 * Driver data holds all the per-chip data. 71 * Driver data holds all the per-chip data.
55 */ 72 */
56struct mcp23s08_driver_data { 73struct mcp23s08_driver_data {
57 unsigned ngpio; 74 unsigned ngpio;
58 struct mcp23s08 *mcp[4]; 75 struct mcp23s08 *mcp[8];
59 struct mcp23s08 chip[]; 76 struct mcp23s08 chip[];
60}; 77};
61 78
@@ -70,7 +87,7 @@ static int mcp23s08_read(struct mcp23s08 *mcp, unsigned reg)
70 return (status < 0) ? status : rx[0]; 87 return (status < 0) ? status : rx[0];
71} 88}
72 89
73static int mcp23s08_write(struct mcp23s08 *mcp, unsigned reg, u8 val) 90static int mcp23s08_write(struct mcp23s08 *mcp, unsigned reg, unsigned val)
74{ 91{
75 u8 tx[3]; 92 u8 tx[3];
76 93
@@ -81,17 +98,81 @@ static int mcp23s08_write(struct mcp23s08 *mcp, unsigned reg, u8 val)
81} 98}
82 99
83static int 100static int
84mcp23s08_read_regs(struct mcp23s08 *mcp, unsigned reg, u8 *vals, unsigned n) 101mcp23s08_read_regs(struct mcp23s08 *mcp, unsigned reg, u16 *vals, unsigned n)
85{ 102{
86 u8 tx[2]; 103 u8 tx[2], *tmp;
104 int status;
87 105
88 if ((n + reg) > sizeof mcp->cache) 106 if ((n + reg) > sizeof mcp->cache)
89 return -EINVAL; 107 return -EINVAL;
90 tx[0] = mcp->addr | 0x01; 108 tx[0] = mcp->addr | 0x01;
91 tx[1] = reg; 109 tx[1] = reg;
92 return spi_write_then_read(mcp->spi, tx, sizeof tx, vals, n); 110
111 tmp = (u8 *)vals;
112 status = spi_write_then_read(mcp->spi, tx, sizeof tx, tmp, n);
113 if (status >= 0) {
114 while (n--)
115 vals[n] = tmp[n]; /* expand to 16bit */
116 }
117 return status;
118}
119
120static int mcp23s17_read(struct mcp23s08 *mcp, unsigned reg)
121{
122 u8 tx[2], rx[2];
123 int status;
124
125 tx[0] = mcp->addr | 0x01;
126 tx[1] = reg << 1;
127 status = spi_write_then_read(mcp->spi, tx, sizeof tx, rx, sizeof rx);
128 return (status < 0) ? status : (rx[0] | (rx[1] << 8));
129}
130
131static int mcp23s17_write(struct mcp23s08 *mcp, unsigned reg, unsigned val)
132{
133 u8 tx[4];
134
135 tx[0] = mcp->addr;
136 tx[1] = reg << 1;
137 tx[2] = val;
138 tx[3] = val >> 8;
139 return spi_write_then_read(mcp->spi, tx, sizeof tx, NULL, 0);
140}
141
142static int
143mcp23s17_read_regs(struct mcp23s08 *mcp, unsigned reg, u16 *vals, unsigned n)
144{
145 u8 tx[2];
146 int status;
147
148 if ((n + reg) > sizeof mcp->cache)
149 return -EINVAL;
150 tx[0] = mcp->addr | 0x01;
151 tx[1] = reg << 1;
152
153 status = spi_write_then_read(mcp->spi, tx, sizeof tx,
154 (u8 *)vals, n * 2);
155 if (status >= 0) {
156 while (n--)
157 vals[n] = __le16_to_cpu((__le16)vals[n]);
158 }
159
160 return status;
93} 161}
94 162
163static const struct mcp23s08_ops mcp23s08_ops = {
164 .read = mcp23s08_read,
165 .write = mcp23s08_write,
166 .read_regs = mcp23s08_read_regs,
167};
168
169static const struct mcp23s08_ops mcp23s17_ops = {
170 .read = mcp23s17_read,
171 .write = mcp23s17_write,
172 .read_regs = mcp23s17_read_regs,
173};
174
175
95/*----------------------------------------------------------------------*/ 176/*----------------------------------------------------------------------*/
96 177
97static int mcp23s08_direction_input(struct gpio_chip *chip, unsigned offset) 178static int mcp23s08_direction_input(struct gpio_chip *chip, unsigned offset)
@@ -101,7 +182,7 @@ static int mcp23s08_direction_input(struct gpio_chip *chip, unsigned offset)
101 182
102 mutex_lock(&mcp->lock); 183 mutex_lock(&mcp->lock);
103 mcp->cache[MCP_IODIR] |= (1 << offset); 184 mcp->cache[MCP_IODIR] |= (1 << offset);
104 status = mcp23s08_write(mcp, MCP_IODIR, mcp->cache[MCP_IODIR]); 185 status = mcp->ops->write(mcp, MCP_IODIR, mcp->cache[MCP_IODIR]);
105 mutex_unlock(&mcp->lock); 186 mutex_unlock(&mcp->lock);
106 return status; 187 return status;
107} 188}
@@ -114,7 +195,7 @@ static int mcp23s08_get(struct gpio_chip *chip, unsigned offset)
114 mutex_lock(&mcp->lock); 195 mutex_lock(&mcp->lock);
115 196
116 /* REVISIT reading this clears any IRQ ... */ 197 /* REVISIT reading this clears any IRQ ... */
117 status = mcp23s08_read(mcp, MCP_GPIO); 198 status = mcp->ops->read(mcp, MCP_GPIO);
118 if (status < 0) 199 if (status < 0)
119 status = 0; 200 status = 0;
120 else { 201 else {
@@ -127,20 +208,20 @@ static int mcp23s08_get(struct gpio_chip *chip, unsigned offset)
127 208
128static int __mcp23s08_set(struct mcp23s08 *mcp, unsigned mask, int value) 209static int __mcp23s08_set(struct mcp23s08 *mcp, unsigned mask, int value)
129{ 210{
130 u8 olat = mcp->cache[MCP_OLAT]; 211 unsigned olat = mcp->cache[MCP_OLAT];
131 212
132 if (value) 213 if (value)
133 olat |= mask; 214 olat |= mask;
134 else 215 else
135 olat &= ~mask; 216 olat &= ~mask;
136 mcp->cache[MCP_OLAT] = olat; 217 mcp->cache[MCP_OLAT] = olat;
137 return mcp23s08_write(mcp, MCP_OLAT, olat); 218 return mcp->ops->write(mcp, MCP_OLAT, olat);
138} 219}
139 220
140static void mcp23s08_set(struct gpio_chip *chip, unsigned offset, int value) 221static void mcp23s08_set(struct gpio_chip *chip, unsigned offset, int value)
141{ 222{
142 struct mcp23s08 *mcp = container_of(chip, struct mcp23s08, chip); 223 struct mcp23s08 *mcp = container_of(chip, struct mcp23s08, chip);
143 u8 mask = 1 << offset; 224 unsigned mask = 1 << offset;
144 225
145 mutex_lock(&mcp->lock); 226 mutex_lock(&mcp->lock);
146 __mcp23s08_set(mcp, mask, value); 227 __mcp23s08_set(mcp, mask, value);
@@ -151,14 +232,14 @@ static int
151mcp23s08_direction_output(struct gpio_chip *chip, unsigned offset, int value) 232mcp23s08_direction_output(struct gpio_chip *chip, unsigned offset, int value)
152{ 233{
153 struct mcp23s08 *mcp = container_of(chip, struct mcp23s08, chip); 234 struct mcp23s08 *mcp = container_of(chip, struct mcp23s08, chip);
154 u8 mask = 1 << offset; 235 unsigned mask = 1 << offset;
155 int status; 236 int status;
156 237
157 mutex_lock(&mcp->lock); 238 mutex_lock(&mcp->lock);
158 status = __mcp23s08_set(mcp, mask, value); 239 status = __mcp23s08_set(mcp, mask, value);
159 if (status == 0) { 240 if (status == 0) {
160 mcp->cache[MCP_IODIR] &= ~mask; 241 mcp->cache[MCP_IODIR] &= ~mask;
161 status = mcp23s08_write(mcp, MCP_IODIR, mcp->cache[MCP_IODIR]); 242 status = mcp->ops->write(mcp, MCP_IODIR, mcp->cache[MCP_IODIR]);
162 } 243 }
163 mutex_unlock(&mcp->lock); 244 mutex_unlock(&mcp->lock);
164 return status; 245 return status;
@@ -184,16 +265,16 @@ static void mcp23s08_dbg_show(struct seq_file *s, struct gpio_chip *chip)
184 mcp = container_of(chip, struct mcp23s08, chip); 265 mcp = container_of(chip, struct mcp23s08, chip);
185 266
186 /* NOTE: we only handle one bank for now ... */ 267 /* NOTE: we only handle one bank for now ... */
187 bank = '0' + ((mcp->addr >> 1) & 0x3); 268 bank = '0' + ((mcp->addr >> 1) & 0x7);
188 269
189 mutex_lock(&mcp->lock); 270 mutex_lock(&mcp->lock);
190 t = mcp23s08_read_regs(mcp, 0, mcp->cache, sizeof mcp->cache); 271 t = mcp->ops->read_regs(mcp, 0, mcp->cache, ARRAY_SIZE(mcp->cache));
191 if (t < 0) { 272 if (t < 0) {
192 seq_printf(s, " I/O ERROR %d\n", t); 273 seq_printf(s, " I/O ERROR %d\n", t);
193 goto done; 274 goto done;
194 } 275 }
195 276
196 for (t = 0, mask = 1; t < 8; t++, mask <<= 1) { 277 for (t = 0, mask = 1; t < chip->ngpio; t++, mask <<= 1) {
197 const char *label; 278 const char *label;
198 279
199 label = gpiochip_is_requested(chip, t); 280 label = gpiochip_is_requested(chip, t);
@@ -219,28 +300,33 @@ done:
219/*----------------------------------------------------------------------*/ 300/*----------------------------------------------------------------------*/
220 301
221static int mcp23s08_probe_one(struct spi_device *spi, unsigned addr, 302static int mcp23s08_probe_one(struct spi_device *spi, unsigned addr,
222 unsigned base, unsigned pullups) 303 unsigned type, unsigned base, unsigned pullups)
223{ 304{
224 struct mcp23s08_driver_data *data = spi_get_drvdata(spi); 305 struct mcp23s08_driver_data *data = spi_get_drvdata(spi);
225 struct mcp23s08 *mcp = data->mcp[addr]; 306 struct mcp23s08 *mcp = data->mcp[addr];
226 int status; 307 int status;
227 int do_update = 0;
228 308
229 mutex_init(&mcp->lock); 309 mutex_init(&mcp->lock);
230 310
231 mcp->spi = spi; 311 mcp->spi = spi;
232 mcp->addr = 0x40 | (addr << 1); 312 mcp->addr = 0x40 | (addr << 1);
233 313
234 mcp->chip.label = "mcp23s08",
235
236 mcp->chip.direction_input = mcp23s08_direction_input; 314 mcp->chip.direction_input = mcp23s08_direction_input;
237 mcp->chip.get = mcp23s08_get; 315 mcp->chip.get = mcp23s08_get;
238 mcp->chip.direction_output = mcp23s08_direction_output; 316 mcp->chip.direction_output = mcp23s08_direction_output;
239 mcp->chip.set = mcp23s08_set; 317 mcp->chip.set = mcp23s08_set;
240 mcp->chip.dbg_show = mcp23s08_dbg_show; 318 mcp->chip.dbg_show = mcp23s08_dbg_show;
241 319
320 if (type == MCP_TYPE_S17) {
321 mcp->ops = &mcp23s17_ops;
322 mcp->chip.ngpio = 16;
323 mcp->chip.label = "mcp23s17";
324 } else {
325 mcp->ops = &mcp23s08_ops;
326 mcp->chip.ngpio = 8;
327 mcp->chip.label = "mcp23s08";
328 }
242 mcp->chip.base = base; 329 mcp->chip.base = base;
243 mcp->chip.ngpio = 8;
244 mcp->chip.can_sleep = 1; 330 mcp->chip.can_sleep = 1;
245 mcp->chip.dev = &spi->dev; 331 mcp->chip.dev = &spi->dev;
246 mcp->chip.owner = THIS_MODULE; 332 mcp->chip.owner = THIS_MODULE;
@@ -248,45 +334,39 @@ static int mcp23s08_probe_one(struct spi_device *spi, unsigned addr,
248 /* verify MCP_IOCON.SEQOP = 0, so sequential reads work, 334 /* verify MCP_IOCON.SEQOP = 0, so sequential reads work,
249 * and MCP_IOCON.HAEN = 1, so we work with all chips. 335 * and MCP_IOCON.HAEN = 1, so we work with all chips.
250 */ 336 */
251 status = mcp23s08_read(mcp, MCP_IOCON); 337 status = mcp->ops->read(mcp, MCP_IOCON);
252 if (status < 0) 338 if (status < 0)
253 goto fail; 339 goto fail;
254 if ((status & IOCON_SEQOP) || !(status & IOCON_HAEN)) { 340 if ((status & IOCON_SEQOP) || !(status & IOCON_HAEN)) {
255 status &= ~IOCON_SEQOP; 341 /* mcp23s17 has IOCON twice, make sure they are in sync */
256 status |= IOCON_HAEN; 342 status &= ~(IOCON_SEQOP | (IOCON_SEQOP << 8));
257 status = mcp23s08_write(mcp, MCP_IOCON, (u8) status); 343 status |= IOCON_HAEN | (IOCON_HAEN << 8);
344 status = mcp->ops->write(mcp, MCP_IOCON, status);
258 if (status < 0) 345 if (status < 0)
259 goto fail; 346 goto fail;
260 } 347 }
261 348
262 /* configure ~100K pullups */ 349 /* configure ~100K pullups */
263 status = mcp23s08_write(mcp, MCP_GPPU, pullups); 350 status = mcp->ops->write(mcp, MCP_GPPU, pullups);
264 if (status < 0) 351 if (status < 0)
265 goto fail; 352 goto fail;
266 353
267 status = mcp23s08_read_regs(mcp, 0, mcp->cache, sizeof mcp->cache); 354 status = mcp->ops->read_regs(mcp, 0, mcp->cache, ARRAY_SIZE(mcp->cache));
268 if (status < 0) 355 if (status < 0)
269 goto fail; 356 goto fail;
270 357
271 /* disable inverter on input */ 358 /* disable inverter on input */
272 if (mcp->cache[MCP_IPOL] != 0) { 359 if (mcp->cache[MCP_IPOL] != 0) {
273 mcp->cache[MCP_IPOL] = 0; 360 mcp->cache[MCP_IPOL] = 0;
274 do_update = 1; 361 status = mcp->ops->write(mcp, MCP_IPOL, 0);
362 if (status < 0)
363 goto fail;
275 } 364 }
276 365
277 /* disable irqs */ 366 /* disable irqs */
278 if (mcp->cache[MCP_GPINTEN] != 0) { 367 if (mcp->cache[MCP_GPINTEN] != 0) {
279 mcp->cache[MCP_GPINTEN] = 0; 368 mcp->cache[MCP_GPINTEN] = 0;
280 do_update = 1; 369 status = mcp->ops->write(mcp, MCP_GPINTEN, 0);
281 }
282
283 if (do_update) {
284 u8 tx[4];
285
286 tx[0] = mcp->addr;
287 tx[1] = MCP_IPOL;
288 memcpy(&tx[2], &mcp->cache[MCP_IPOL], sizeof(tx) - 2);
289 status = spi_write_then_read(mcp->spi, tx, sizeof tx, NULL, 0);
290 if (status < 0) 370 if (status < 0)
291 goto fail; 371 goto fail;
292 } 372 }
@@ -305,19 +385,26 @@ static int mcp23s08_probe(struct spi_device *spi)
305 unsigned addr; 385 unsigned addr;
306 unsigned chips = 0; 386 unsigned chips = 0;
307 struct mcp23s08_driver_data *data; 387 struct mcp23s08_driver_data *data;
308 int status; 388 int status, type;
309 unsigned base; 389 unsigned base;
310 390
391 type = spi_get_device_id(spi)->driver_data;
392
311 pdata = spi->dev.platform_data; 393 pdata = spi->dev.platform_data;
312 if (!pdata || !gpio_is_valid(pdata->base)) { 394 if (!pdata || !gpio_is_valid(pdata->base)) {
313 dev_dbg(&spi->dev, "invalid or missing platform data\n"); 395 dev_dbg(&spi->dev, "invalid or missing platform data\n");
314 return -EINVAL; 396 return -EINVAL;
315 } 397 }
316 398
317 for (addr = 0; addr < 4; addr++) { 399 for (addr = 0; addr < ARRAY_SIZE(pdata->chip); addr++) {
318 if (!pdata->chip[addr].is_present) 400 if (!pdata->chip[addr].is_present)
319 continue; 401 continue;
320 chips++; 402 chips++;
403 if ((type == MCP_TYPE_S08) && (addr > 3)) {
404 dev_err(&spi->dev,
405 "mcp23s08 only supports address 0..3\n");
406 return -EINVAL;
407 }
321 } 408 }
322 if (!chips) 409 if (!chips)
323 return -ENODEV; 410 return -ENODEV;
@@ -329,16 +416,17 @@ static int mcp23s08_probe(struct spi_device *spi)
329 spi_set_drvdata(spi, data); 416 spi_set_drvdata(spi, data);
330 417
331 base = pdata->base; 418 base = pdata->base;
332 for (addr = 0; addr < 4; addr++) { 419 for (addr = 0; addr < ARRAY_SIZE(pdata->chip); addr++) {
333 if (!pdata->chip[addr].is_present) 420 if (!pdata->chip[addr].is_present)
334 continue; 421 continue;
335 chips--; 422 chips--;
336 data->mcp[addr] = &data->chip[chips]; 423 data->mcp[addr] = &data->chip[chips];
337 status = mcp23s08_probe_one(spi, addr, base, 424 status = mcp23s08_probe_one(spi, addr, type, base,
338 pdata->chip[addr].pullups); 425 pdata->chip[addr].pullups);
339 if (status < 0) 426 if (status < 0)
340 goto fail; 427 goto fail;
341 base += 8; 428
429 base += (type == MCP_TYPE_S17) ? 16 : 8;
342 } 430 }
343 data->ngpio = base - pdata->base; 431 data->ngpio = base - pdata->base;
344 432
@@ -358,7 +446,7 @@ static int mcp23s08_probe(struct spi_device *spi)
358 return 0; 446 return 0;
359 447
360fail: 448fail:
361 for (addr = 0; addr < 4; addr++) { 449 for (addr = 0; addr < ARRAY_SIZE(data->mcp); addr++) {
362 int tmp; 450 int tmp;
363 451
364 if (!data->mcp[addr]) 452 if (!data->mcp[addr])
@@ -388,7 +476,7 @@ static int mcp23s08_remove(struct spi_device *spi)
388 } 476 }
389 } 477 }
390 478
391 for (addr = 0; addr < 4; addr++) { 479 for (addr = 0; addr < ARRAY_SIZE(data->mcp); addr++) {
392 int tmp; 480 int tmp;
393 481
394 if (!data->mcp[addr]) 482 if (!data->mcp[addr])
@@ -405,9 +493,17 @@ static int mcp23s08_remove(struct spi_device *spi)
405 return status; 493 return status;
406} 494}
407 495
496static const struct spi_device_id mcp23s08_ids[] = {
497 { "mcp23s08", MCP_TYPE_S08 },
498 { "mcp23s17", MCP_TYPE_S17 },
499 { },
500};
501MODULE_DEVICE_TABLE(spi, mcp23s08_ids);
502
408static struct spi_driver mcp23s08_driver = { 503static struct spi_driver mcp23s08_driver = {
409 .probe = mcp23s08_probe, 504 .probe = mcp23s08_probe,
410 .remove = mcp23s08_remove, 505 .remove = mcp23s08_remove,
506 .id_table = mcp23s08_ids,
411 .driver = { 507 .driver = {
412 .name = "mcp23s08", 508 .name = "mcp23s08",
413 .owner = THIS_MODULE, 509 .owner = THIS_MODULE,
@@ -432,4 +528,3 @@ static void __exit mcp23s08_exit(void)
432module_exit(mcp23s08_exit); 528module_exit(mcp23s08_exit);
433 529
434MODULE_LICENSE("GPL"); 530MODULE_LICENSE("GPL");
435MODULE_ALIAS("spi:mcp23s08");