Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

gpio/mcp23s08: support mcp23s17 variant

mpc23s17 is very similar to the mcp23s08, except that registers are 16bit
wide, so extend the interface to work with both variants.

The s17 variant also has an additional address pin, so adjust platform
data structure to support up to 8 devices per SPI chipselect.

Signed-off-by: Peter Korsgaard <jacmet@sunsite.dk>
Signed-off-by: Grant Likely <grant.likely@secretlab.ca>

authored by

Peter Korsgaard and committed by
Grant Likely
0b7bb77f 9c3c8afc

+155 -57
+3 -3
drivers/gpio/Kconfig
··· 368 368 GPIO driver for Maxim MAX7301 SPI-based GPIO expander. 369 369 370 370 config GPIO_MCP23S08 371 - tristate "Microchip MCP23S08 I/O expander" 371 + tristate "Microchip MCP23Sxx I/O expander" 372 372 depends on SPI_MASTER 373 373 help 374 - SPI driver for Microchip MCP23S08 I/O expander. This provides 375 - a GPIO interface supporting inputs and outputs. 374 + SPI driver for Microchip MCP23S08/MPC23S17 I/O expanders. 375 + This provides a GPIO interface supporting inputs and outputs. 376 376 377 377 config GPIO_MC33880 378 378 tristate "Freescale MC33880 high-side/low-side switch"
+143 -48
drivers/gpio/mcp23s08.c
··· 10 10 #include <linux/spi/spi.h> 11 11 #include <linux/spi/mcp23s08.h> 12 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 21 /* Registers are all 8 bits wide. 16 22 * ··· 41 35 #define MCP_GPIO 0x09 42 36 #define MCP_OLAT 0x0a 43 37 38 + struct mcp23s08; 39 + 40 + struct mcp23s08_ops { 41 + int (*read)(struct mcp23s08 *mcp, unsigned reg); 42 + int (*write)(struct mcp23s08 *mcp, unsigned reg, unsigned val); 43 + int (*read_regs)(struct mcp23s08 *mcp, unsigned reg, 44 + u16 *vals, unsigned n); 45 + }; 46 + 44 47 struct mcp23s08 { 45 48 struct spi_device *spi; 46 49 u8 addr; 47 50 48 - u8 cache[11]; 51 + u16 cache[11]; 49 52 /* lock protects the cached values */ 50 53 struct mutex lock; 51 54 52 55 struct gpio_chip chip; 53 56 54 57 struct work_struct work; 58 + 59 + const struct mcp23s08_ops *ops; 55 60 }; 56 61 57 - /* A given spi_device can represent up to four mcp23s08 chips 62 + /* A given spi_device can represent up to eight mcp23sxx chips 58 63 * sharing the same chipselect but using different addresses 59 64 * (e.g. chips #0 and #3 might be populated, but not #1 or $2). 60 65 * Driver data holds all the per-chip data. 61 66 */ 62 67 struct mcp23s08_driver_data { 63 68 unsigned ngpio; 64 - struct mcp23s08 *mcp[4]; 69 + struct mcp23s08 *mcp[8]; 65 70 struct mcp23s08 chip[]; 66 71 }; 67 72 ··· 87 70 return (status < 0) ? status : rx[0]; 88 71 } 89 72 90 - static int mcp23s08_write(struct mcp23s08 *mcp, unsigned reg, u8 val) 73 + static int mcp23s08_write(struct mcp23s08 *mcp, unsigned reg, unsigned val) 91 74 { 92 75 u8 tx[3]; 93 76 ··· 98 81 } 99 82 100 83 static int 101 - mcp23s08_read_regs(struct mcp23s08 *mcp, unsigned reg, u8 *vals, unsigned n) 84 + mcp23s08_read_regs(struct mcp23s08 *mcp, unsigned reg, u16 *vals, unsigned n) 102 85 { 103 - u8 tx[2]; 86 + u8 tx[2], *tmp; 87 + int status; 104 88 105 89 if ((n + reg) > sizeof mcp->cache) 106 90 return -EINVAL; 107 91 tx[0] = mcp->addr | 0x01; 108 92 tx[1] = reg; 109 - return spi_write_then_read(mcp->spi, tx, sizeof tx, vals, n); 93 + 94 + tmp = (u8 *)vals; 95 + status = spi_write_then_read(mcp->spi, tx, sizeof tx, tmp, n); 96 + if (status >= 0) { 97 + while (n--) 98 + vals[n] = tmp[n]; /* expand to 16bit */ 99 + } 100 + return status; 110 101 } 102 + 103 + static int mcp23s17_read(struct mcp23s08 *mcp, unsigned reg) 104 + { 105 + u8 tx[2], rx[2]; 106 + int status; 107 + 108 + tx[0] = mcp->addr | 0x01; 109 + tx[1] = reg << 1; 110 + status = spi_write_then_read(mcp->spi, tx, sizeof tx, rx, sizeof rx); 111 + return (status < 0) ? status : (rx[0] | (rx[1] << 8)); 112 + } 113 + 114 + static int mcp23s17_write(struct mcp23s08 *mcp, unsigned reg, unsigned val) 115 + { 116 + u8 tx[4]; 117 + 118 + tx[0] = mcp->addr; 119 + tx[1] = reg << 1; 120 + tx[2] = val; 121 + tx[3] = val >> 8; 122 + return spi_write_then_read(mcp->spi, tx, sizeof tx, NULL, 0); 123 + } 124 + 125 + static int 126 + mcp23s17_read_regs(struct mcp23s08 *mcp, unsigned reg, u16 *vals, unsigned n) 127 + { 128 + u8 tx[2]; 129 + int status; 130 + 131 + if ((n + reg) > sizeof mcp->cache) 132 + return -EINVAL; 133 + tx[0] = mcp->addr | 0x01; 134 + tx[1] = reg << 1; 135 + 136 + status = spi_write_then_read(mcp->spi, tx, sizeof tx, 137 + (u8 *)vals, n * 2); 138 + if (status >= 0) { 139 + while (n--) 140 + vals[n] = __le16_to_cpu((__le16)vals[n]); 141 + } 142 + 143 + return status; 144 + } 145 + 146 + static const struct mcp23s08_ops mcp23s08_ops = { 147 + .read = mcp23s08_read, 148 + .write = mcp23s08_write, 149 + .read_regs = mcp23s08_read_regs, 150 + }; 151 + 152 + static const struct mcp23s08_ops mcp23s17_ops = { 153 + .read = mcp23s17_read, 154 + .write = mcp23s17_write, 155 + .read_regs = mcp23s17_read_regs, 156 + }; 157 + 111 158 112 159 /*----------------------------------------------------------------------*/ 113 160 ··· 182 101 183 102 mutex_lock(&mcp->lock); 184 103 mcp->cache[MCP_IODIR] |= (1 << offset); 185 - status = mcp23s08_write(mcp, MCP_IODIR, mcp->cache[MCP_IODIR]); 104 + status = mcp->ops->write(mcp, MCP_IODIR, mcp->cache[MCP_IODIR]); 186 105 mutex_unlock(&mcp->lock); 187 106 return status; 188 107 } ··· 195 114 mutex_lock(&mcp->lock); 196 115 197 116 /* REVISIT reading this clears any IRQ ... */ 198 - status = mcp23s08_read(mcp, MCP_GPIO); 117 + status = mcp->ops->read(mcp, MCP_GPIO); 199 118 if (status < 0) 200 119 status = 0; 201 120 else { ··· 208 127 209 128 static int __mcp23s08_set(struct mcp23s08 *mcp, unsigned mask, int value) 210 129 { 211 - u8 olat = mcp->cache[MCP_OLAT]; 130 + unsigned olat = mcp->cache[MCP_OLAT]; 212 131 213 132 if (value) 214 133 olat |= mask; 215 134 else 216 135 olat &= ~mask; 217 136 mcp->cache[MCP_OLAT] = olat; 218 - return mcp23s08_write(mcp, MCP_OLAT, olat); 137 + return mcp->ops->write(mcp, MCP_OLAT, olat); 219 138 } 220 139 221 140 static void mcp23s08_set(struct gpio_chip *chip, unsigned offset, int value) 222 141 { 223 142 struct mcp23s08 *mcp = container_of(chip, struct mcp23s08, chip); 224 - u8 mask = 1 << offset; 143 + unsigned mask = 1 << offset; 225 144 226 145 mutex_lock(&mcp->lock); 227 146 __mcp23s08_set(mcp, mask, value); ··· 232 151 mcp23s08_direction_output(struct gpio_chip *chip, unsigned offset, int value) 233 152 { 234 153 struct mcp23s08 *mcp = container_of(chip, struct mcp23s08, chip); 235 - u8 mask = 1 << offset; 154 + unsigned mask = 1 << offset; 236 155 int status; 237 156 238 157 mutex_lock(&mcp->lock); 239 158 status = __mcp23s08_set(mcp, mask, value); 240 159 if (status == 0) { 241 160 mcp->cache[MCP_IODIR] &= ~mask; 242 - status = mcp23s08_write(mcp, MCP_IODIR, mcp->cache[MCP_IODIR]); 161 + status = mcp->ops->write(mcp, MCP_IODIR, mcp->cache[MCP_IODIR]); 243 162 } 244 163 mutex_unlock(&mcp->lock); 245 164 return status; ··· 265 184 mcp = container_of(chip, struct mcp23s08, chip); 266 185 267 186 /* NOTE: we only handle one bank for now ... */ 268 - bank = '0' + ((mcp->addr >> 1) & 0x3); 187 + bank = '0' + ((mcp->addr >> 1) & 0x7); 269 188 270 189 mutex_lock(&mcp->lock); 271 - t = mcp23s08_read_regs(mcp, 0, mcp->cache, sizeof mcp->cache); 190 + t = mcp->ops->read_regs(mcp, 0, mcp->cache, ARRAY_SIZE(mcp->cache)); 272 191 if (t < 0) { 273 192 seq_printf(s, " I/O ERROR %d\n", t); 274 193 goto done; 275 194 } 276 195 277 - for (t = 0, mask = 1; t < 8; t++, mask <<= 1) { 196 + for (t = 0, mask = 1; t < chip->ngpio; t++, mask <<= 1) { 278 197 const char *label; 279 198 280 199 label = gpiochip_is_requested(chip, t); ··· 300 219 /*----------------------------------------------------------------------*/ 301 220 302 221 static int mcp23s08_probe_one(struct spi_device *spi, unsigned addr, 303 - unsigned base, unsigned pullups) 222 + unsigned type, unsigned base, unsigned pullups) 304 223 { 305 224 struct mcp23s08_driver_data *data = spi_get_drvdata(spi); 306 225 struct mcp23s08 *mcp = data->mcp[addr]; 307 226 int status; 308 - int do_update = 0; 309 227 310 228 mutex_init(&mcp->lock); 311 229 312 230 mcp->spi = spi; 313 231 mcp->addr = 0x40 | (addr << 1); 314 - 315 - mcp->chip.label = "mcp23s08", 316 232 317 233 mcp->chip.direction_input = mcp23s08_direction_input; 318 234 mcp->chip.get = mcp23s08_get; ··· 317 239 mcp->chip.set = mcp23s08_set; 318 240 mcp->chip.dbg_show = mcp23s08_dbg_show; 319 241 242 + if (type == MCP_TYPE_S17) { 243 + mcp->ops = &mcp23s17_ops; 244 + mcp->chip.ngpio = 16; 245 + mcp->chip.label = "mcp23s17"; 246 + } else { 247 + mcp->ops = &mcp23s08_ops; 248 + mcp->chip.ngpio = 8; 249 + mcp->chip.label = "mcp23s08"; 250 + } 320 251 mcp->chip.base = base; 321 - mcp->chip.ngpio = 8; 322 252 mcp->chip.can_sleep = 1; 323 253 mcp->chip.dev = &spi->dev; 324 254 mcp->chip.owner = THIS_MODULE; ··· 334 248 /* verify MCP_IOCON.SEQOP = 0, so sequential reads work, 335 249 * and MCP_IOCON.HAEN = 1, so we work with all chips. 336 250 */ 337 - status = mcp23s08_read(mcp, MCP_IOCON); 251 + status = mcp->ops->read(mcp, MCP_IOCON); 338 252 if (status < 0) 339 253 goto fail; 340 254 if ((status & IOCON_SEQOP) || !(status & IOCON_HAEN)) { 341 - status &= ~IOCON_SEQOP; 342 - status |= IOCON_HAEN; 343 - status = mcp23s08_write(mcp, MCP_IOCON, (u8) status); 255 + /* mcp23s17 has IOCON twice, make sure they are in sync */ 256 + status &= ~(IOCON_SEQOP | (IOCON_SEQOP << 8)); 257 + status |= IOCON_HAEN | (IOCON_HAEN << 8); 258 + status = mcp->ops->write(mcp, MCP_IOCON, status); 344 259 if (status < 0) 345 260 goto fail; 346 261 } 347 262 348 263 /* configure ~100K pullups */ 349 - status = mcp23s08_write(mcp, MCP_GPPU, pullups); 264 + status = mcp->ops->write(mcp, MCP_GPPU, pullups); 350 265 if (status < 0) 351 266 goto fail; 352 267 353 - status = mcp23s08_read_regs(mcp, 0, mcp->cache, sizeof mcp->cache); 268 + status = mcp->ops->read_regs(mcp, 0, mcp->cache, ARRAY_SIZE(mcp->cache)); 354 269 if (status < 0) 355 270 goto fail; 356 271 357 272 /* disable inverter on input */ 358 273 if (mcp->cache[MCP_IPOL] != 0) { 359 274 mcp->cache[MCP_IPOL] = 0; 360 - do_update = 1; 275 + status = mcp->ops->write(mcp, MCP_IPOL, 0); 276 + if (status < 0) 277 + goto fail; 361 278 } 362 279 363 280 /* disable irqs */ 364 281 if (mcp->cache[MCP_GPINTEN] != 0) { 365 282 mcp->cache[MCP_GPINTEN] = 0; 366 - do_update = 1; 367 - } 368 - 369 - if (do_update) { 370 - u8 tx[4]; 371 - 372 - tx[0] = mcp->addr; 373 - tx[1] = MCP_IPOL; 374 - memcpy(&tx[2], &mcp->cache[MCP_IPOL], sizeof(tx) - 2); 375 - status = spi_write_then_read(mcp->spi, tx, sizeof tx, NULL, 0); 283 + status = mcp->ops->write(mcp, MCP_GPINTEN, 0); 376 284 if (status < 0) 377 285 goto fail; 378 286 } ··· 385 305 unsigned addr; 386 306 unsigned chips = 0; 387 307 struct mcp23s08_driver_data *data; 388 - int status; 308 + int status, type; 389 309 unsigned base; 310 + 311 + type = spi_get_device_id(spi)->driver_data; 390 312 391 313 pdata = spi->dev.platform_data; 392 314 if (!pdata || !gpio_is_valid(pdata->base)) { ··· 396 314 return -EINVAL; 397 315 } 398 316 399 - for (addr = 0; addr < 4; addr++) { 317 + for (addr = 0; addr < ARRAY_SIZE(pdata->chip); addr++) { 400 318 if (!pdata->chip[addr].is_present) 401 319 continue; 402 320 chips++; 321 + if ((type == MCP_TYPE_S08) && (addr > 3)) { 322 + dev_err(&spi->dev, 323 + "mcp23s08 only supports address 0..3\n"); 324 + return -EINVAL; 325 + } 403 326 } 404 327 if (!chips) 405 328 return -ENODEV; ··· 416 329 spi_set_drvdata(spi, data); 417 330 418 331 base = pdata->base; 419 - for (addr = 0; addr < 4; addr++) { 332 + for (addr = 0; addr < ARRAY_SIZE(pdata->chip); addr++) { 420 333 if (!pdata->chip[addr].is_present) 421 334 continue; 422 335 chips--; 423 336 data->mcp[addr] = &data->chip[chips]; 424 - status = mcp23s08_probe_one(spi, addr, base, 425 - pdata->chip[addr].pullups); 337 + status = mcp23s08_probe_one(spi, addr, type, base, 338 + pdata->chip[addr].pullups); 426 339 if (status < 0) 427 340 goto fail; 428 - base += 8; 341 + 342 + base += (type == MCP_TYPE_S17) ? 16 : 8; 429 343 } 430 344 data->ngpio = base - pdata->base; 431 345 ··· 446 358 return 0; 447 359 448 360 fail: 449 - for (addr = 0; addr < 4; addr++) { 361 + for (addr = 0; addr < ARRAY_SIZE(data->mcp); addr++) { 450 362 int tmp; 451 363 452 364 if (!data->mcp[addr]) ··· 476 388 } 477 389 } 478 390 479 - for (addr = 0; addr < 4; addr++) { 391 + for (addr = 0; addr < ARRAY_SIZE(data->mcp); addr++) { 480 392 int tmp; 481 393 482 394 if (!data->mcp[addr]) ··· 493 405 return status; 494 406 } 495 407 408 + static const struct spi_device_id mcp23s08_ids[] = { 409 + { "mcp23s08", MCP_TYPE_S08 }, 410 + { "mcp23s17", MCP_TYPE_S17 }, 411 + { }, 412 + }; 413 + MODULE_DEVICE_TABLE(spi, mcp23s08_ids); 414 + 496 415 static struct spi_driver mcp23s08_driver = { 497 416 .probe = mcp23s08_probe, 498 417 .remove = mcp23s08_remove, 418 + .id_table = mcp23s08_ids, 499 419 .driver = { 500 420 .name = "mcp23s08", 501 421 .owner = THIS_MODULE, ··· 528 432 module_exit(mcp23s08_exit); 529 433 530 434 MODULE_LICENSE("GPL"); 531 - MODULE_ALIAS("spi:mcp23s08");
+9 -6
include/linux/spi/mcp23s08.h
··· 2 2 /* FIXME driver should be able to handle IRQs... */ 3 3 4 4 struct mcp23s08_chip_info { 5 - bool is_present; /* true iff populated */ 6 - u8 pullups; /* BIT(x) means enable pullup x */ 5 + bool is_present; /* true if populated */ 6 + unsigned pullups; /* BIT(x) means enable pullup x */ 7 7 }; 8 8 9 9 struct mcp23s08_platform_data { 10 - /* Four slaves (numbered 0..3) can share one SPI chipselect, and 11 - * will provide 8..32 GPIOs using 1..4 gpio_chip instances. 10 + /* For mcp23s08, up to 4 slaves (numbered 0..3) can share one SPI 11 + * chipselect, each providing 1 gpio_chip instance with 8 gpios. 12 + * For mpc23s17, up to 8 slaves (numbered 0..7) can share one SPI 13 + * chipselect, each providing 1 gpio_chip (port A + port B) with 14 + * 16 gpios. 12 15 */ 13 - struct mcp23s08_chip_info chip[4]; 16 + struct mcp23s08_chip_info chip[8]; 14 17 15 18 /* "base" is the number of the first GPIO. Dynamic assignment is 16 19 * not currently supported, and even if there are gaps in chip 17 20 * addressing the GPIO numbers are sequential .. so for example 18 21 * if only slaves 0 and 3 are present, their GPIOs range from 19 - * base to base+15. 22 + * base to base+15 (or base+31 for s17 variant). 20 23 */ 21 24 unsigned base; 22 25