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