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

Configure Feed

Select the types of activity you want to include in your feed.

at v2.6.30-rc3 435 lines 9.9 kB view raw
1/* 2 * mcp23s08.c - SPI gpio expander driver 3 */ 4 5#include <linux/kernel.h> 6#include <linux/device.h> 7#include <linux/workqueue.h> 8#include <linux/mutex.h> 9 10#include <linux/spi/spi.h> 11#include <linux/spi/mcp23s08.h> 12 13#include <asm/gpio.h> 14 15 16/* Registers are all 8 bits wide. 17 * 18 * The mcp23s17 has twice as many bits, and can be configured to work 19 * with either 16 bit registers or with two adjacent 8 bit banks. 20 * 21 * Also, there are I2C versions of both chips. 22 */ 23#define MCP_IODIR 0x00 /* init/reset: all ones */ 24#define MCP_IPOL 0x01 25#define MCP_GPINTEN 0x02 26#define MCP_DEFVAL 0x03 27#define MCP_INTCON 0x04 28#define MCP_IOCON 0x05 29# define IOCON_SEQOP (1 << 5) 30# define IOCON_HAEN (1 << 3) 31# define IOCON_ODR (1 << 2) 32# define IOCON_INTPOL (1 << 1) 33#define MCP_GPPU 0x06 34#define MCP_INTF 0x07 35#define MCP_INTCAP 0x08 36#define MCP_GPIO 0x09 37#define MCP_OLAT 0x0a 38 39struct mcp23s08 { 40 struct spi_device *spi; 41 u8 addr; 42 43 u8 cache[11]; 44 /* lock protects the cached values */ 45 struct mutex lock; 46 47 struct gpio_chip chip; 48 49 struct work_struct work; 50}; 51 52/* A given spi_device can represent up to four mcp23s08 chips 53 * sharing the same chipselect but using different addresses 54 * (e.g. chips #0 and #3 might be populated, but not #1 or $2). 55 * Driver data holds all the per-chip data. 56 */ 57struct mcp23s08_driver_data { 58 unsigned ngpio; 59 struct mcp23s08 *mcp[4]; 60 struct mcp23s08 chip[]; 61}; 62 63static int mcp23s08_read(struct mcp23s08 *mcp, unsigned reg) 64{ 65 u8 tx[2], rx[1]; 66 int status; 67 68 tx[0] = mcp->addr | 0x01; 69 tx[1] = reg; 70 status = spi_write_then_read(mcp->spi, tx, sizeof tx, rx, sizeof rx); 71 return (status < 0) ? status : rx[0]; 72} 73 74static int mcp23s08_write(struct mcp23s08 *mcp, unsigned reg, u8 val) 75{ 76 u8 tx[3]; 77 78 tx[0] = mcp->addr; 79 tx[1] = reg; 80 tx[2] = val; 81 return spi_write_then_read(mcp->spi, tx, sizeof tx, NULL, 0); 82} 83 84static int 85mcp23s08_read_regs(struct mcp23s08 *mcp, unsigned reg, u8 *vals, unsigned n) 86{ 87 u8 tx[2]; 88 89 if ((n + reg) > sizeof mcp->cache) 90 return -EINVAL; 91 tx[0] = mcp->addr | 0x01; 92 tx[1] = reg; 93 return spi_write_then_read(mcp->spi, tx, sizeof tx, vals, n); 94} 95 96/*----------------------------------------------------------------------*/ 97 98static int mcp23s08_direction_input(struct gpio_chip *chip, unsigned offset) 99{ 100 struct mcp23s08 *mcp = container_of(chip, struct mcp23s08, chip); 101 int status; 102 103 mutex_lock(&mcp->lock); 104 mcp->cache[MCP_IODIR] |= (1 << offset); 105 status = mcp23s08_write(mcp, MCP_IODIR, mcp->cache[MCP_IODIR]); 106 mutex_unlock(&mcp->lock); 107 return status; 108} 109 110static int mcp23s08_get(struct gpio_chip *chip, unsigned offset) 111{ 112 struct mcp23s08 *mcp = container_of(chip, struct mcp23s08, chip); 113 int status; 114 115 mutex_lock(&mcp->lock); 116 117 /* REVISIT reading this clears any IRQ ... */ 118 status = mcp23s08_read(mcp, MCP_GPIO); 119 if (status < 0) 120 status = 0; 121 else { 122 mcp->cache[MCP_GPIO] = status; 123 status = !!(status & (1 << offset)); 124 } 125 mutex_unlock(&mcp->lock); 126 return status; 127} 128 129static int __mcp23s08_set(struct mcp23s08 *mcp, unsigned mask, int value) 130{ 131 u8 olat = mcp->cache[MCP_OLAT]; 132 133 if (value) 134 olat |= mask; 135 else 136 olat &= ~mask; 137 mcp->cache[MCP_OLAT] = olat; 138 return mcp23s08_write(mcp, MCP_OLAT, olat); 139} 140 141static void mcp23s08_set(struct gpio_chip *chip, unsigned offset, int value) 142{ 143 struct mcp23s08 *mcp = container_of(chip, struct mcp23s08, chip); 144 u8 mask = 1 << offset; 145 146 mutex_lock(&mcp->lock); 147 __mcp23s08_set(mcp, mask, value); 148 mutex_unlock(&mcp->lock); 149} 150 151static int 152mcp23s08_direction_output(struct gpio_chip *chip, unsigned offset, int value) 153{ 154 struct mcp23s08 *mcp = container_of(chip, struct mcp23s08, chip); 155 u8 mask = 1 << offset; 156 int status; 157 158 mutex_lock(&mcp->lock); 159 status = __mcp23s08_set(mcp, mask, value); 160 if (status == 0) { 161 mcp->cache[MCP_IODIR] &= ~mask; 162 status = mcp23s08_write(mcp, MCP_IODIR, mcp->cache[MCP_IODIR]); 163 } 164 mutex_unlock(&mcp->lock); 165 return status; 166} 167 168/*----------------------------------------------------------------------*/ 169 170#ifdef CONFIG_DEBUG_FS 171 172#include <linux/seq_file.h> 173 174/* 175 * This shows more info than the generic gpio dump code: 176 * pullups, deglitching, open drain drive. 177 */ 178static void mcp23s08_dbg_show(struct seq_file *s, struct gpio_chip *chip) 179{ 180 struct mcp23s08 *mcp; 181 char bank; 182 int t; 183 unsigned mask; 184 185 mcp = container_of(chip, struct mcp23s08, chip); 186 187 /* NOTE: we only handle one bank for now ... */ 188 bank = '0' + ((mcp->addr >> 1) & 0x3); 189 190 mutex_lock(&mcp->lock); 191 t = mcp23s08_read_regs(mcp, 0, mcp->cache, sizeof mcp->cache); 192 if (t < 0) { 193 seq_printf(s, " I/O ERROR %d\n", t); 194 goto done; 195 } 196 197 for (t = 0, mask = 1; t < 8; t++, mask <<= 1) { 198 const char *label; 199 200 label = gpiochip_is_requested(chip, t); 201 if (!label) 202 continue; 203 204 seq_printf(s, " gpio-%-3d P%c.%d (%-12s) %s %s %s", 205 chip->base + t, bank, t, label, 206 (mcp->cache[MCP_IODIR] & mask) ? "in " : "out", 207 (mcp->cache[MCP_GPIO] & mask) ? "hi" : "lo", 208 (mcp->cache[MCP_GPPU] & mask) ? " " : "up"); 209 /* NOTE: ignoring the irq-related registers */ 210 seq_printf(s, "\n"); 211 } 212done: 213 mutex_unlock(&mcp->lock); 214} 215 216#else 217#define mcp23s08_dbg_show NULL 218#endif 219 220/*----------------------------------------------------------------------*/ 221 222static int mcp23s08_probe_one(struct spi_device *spi, unsigned addr, 223 unsigned base, unsigned pullups) 224{ 225 struct mcp23s08_driver_data *data = spi_get_drvdata(spi); 226 struct mcp23s08 *mcp = data->mcp[addr]; 227 int status; 228 int do_update = 0; 229 230 mutex_init(&mcp->lock); 231 232 mcp->spi = spi; 233 mcp->addr = 0x40 | (addr << 1); 234 235 mcp->chip.label = "mcp23s08", 236 237 mcp->chip.direction_input = mcp23s08_direction_input; 238 mcp->chip.get = mcp23s08_get; 239 mcp->chip.direction_output = mcp23s08_direction_output; 240 mcp->chip.set = mcp23s08_set; 241 mcp->chip.dbg_show = mcp23s08_dbg_show; 242 243 mcp->chip.base = base; 244 mcp->chip.ngpio = 8; 245 mcp->chip.can_sleep = 1; 246 mcp->chip.dev = &spi->dev; 247 mcp->chip.owner = THIS_MODULE; 248 249 /* verify MCP_IOCON.SEQOP = 0, so sequential reads work, 250 * and MCP_IOCON.HAEN = 1, so we work with all chips. 251 */ 252 status = mcp23s08_read(mcp, MCP_IOCON); 253 if (status < 0) 254 goto fail; 255 if ((status & IOCON_SEQOP) || !(status & IOCON_HAEN)) { 256 status &= ~IOCON_SEQOP; 257 status |= IOCON_HAEN; 258 status = mcp23s08_write(mcp, MCP_IOCON, (u8) status); 259 if (status < 0) 260 goto fail; 261 } 262 263 /* configure ~100K pullups */ 264 status = mcp23s08_write(mcp, MCP_GPPU, pullups); 265 if (status < 0) 266 goto fail; 267 268 status = mcp23s08_read_regs(mcp, 0, mcp->cache, sizeof mcp->cache); 269 if (status < 0) 270 goto fail; 271 272 /* disable inverter on input */ 273 if (mcp->cache[MCP_IPOL] != 0) { 274 mcp->cache[MCP_IPOL] = 0; 275 do_update = 1; 276 } 277 278 /* disable irqs */ 279 if (mcp->cache[MCP_GPINTEN] != 0) { 280 mcp->cache[MCP_GPINTEN] = 0; 281 do_update = 1; 282 } 283 284 if (do_update) { 285 u8 tx[4]; 286 287 tx[0] = mcp->addr; 288 tx[1] = MCP_IPOL; 289 memcpy(&tx[2], &mcp->cache[MCP_IPOL], sizeof(tx) - 2); 290 status = spi_write_then_read(mcp->spi, tx, sizeof tx, NULL, 0); 291 if (status < 0) 292 goto fail; 293 } 294 295 status = gpiochip_add(&mcp->chip); 296fail: 297 if (status < 0) 298 dev_dbg(&spi->dev, "can't setup chip %d, --> %d\n", 299 addr, status); 300 return status; 301} 302 303static int mcp23s08_probe(struct spi_device *spi) 304{ 305 struct mcp23s08_platform_data *pdata; 306 unsigned addr; 307 unsigned chips = 0; 308 struct mcp23s08_driver_data *data; 309 int status; 310 unsigned base; 311 312 pdata = spi->dev.platform_data; 313 if (!pdata || !gpio_is_valid(pdata->base)) { 314 dev_dbg(&spi->dev, "invalid or missing platform data\n"); 315 return -EINVAL; 316 } 317 318 for (addr = 0; addr < 4; addr++) { 319 if (!pdata->chip[addr].is_present) 320 continue; 321 chips++; 322 } 323 if (!chips) 324 return -ENODEV; 325 326 data = kzalloc(sizeof *data + chips * sizeof(struct mcp23s08), 327 GFP_KERNEL); 328 if (!data) 329 return -ENOMEM; 330 spi_set_drvdata(spi, data); 331 332 base = pdata->base; 333 for (addr = 0; addr < 4; addr++) { 334 if (!pdata->chip[addr].is_present) 335 continue; 336 chips--; 337 data->mcp[addr] = &data->chip[chips]; 338 status = mcp23s08_probe_one(spi, addr, base, 339 pdata->chip[addr].pullups); 340 if (status < 0) 341 goto fail; 342 base += 8; 343 } 344 data->ngpio = base - pdata->base; 345 346 /* NOTE: these chips have a relatively sane IRQ framework, with 347 * per-signal masking and level/edge triggering. It's not yet 348 * handled here... 349 */ 350 351 if (pdata->setup) { 352 status = pdata->setup(spi, 353 pdata->base, data->ngpio, 354 pdata->context); 355 if (status < 0) 356 dev_dbg(&spi->dev, "setup --> %d\n", status); 357 } 358 359 return 0; 360 361fail: 362 for (addr = 0; addr < 4; addr++) { 363 int tmp; 364 365 if (!data->mcp[addr]) 366 continue; 367 tmp = gpiochip_remove(&data->mcp[addr]->chip); 368 if (tmp < 0) 369 dev_err(&spi->dev, "%s --> %d\n", "remove", tmp); 370 } 371 kfree(data); 372 return status; 373} 374 375static int mcp23s08_remove(struct spi_device *spi) 376{ 377 struct mcp23s08_driver_data *data = spi_get_drvdata(spi); 378 struct mcp23s08_platform_data *pdata = spi->dev.platform_data; 379 unsigned addr; 380 int status = 0; 381 382 if (pdata->teardown) { 383 status = pdata->teardown(spi, 384 pdata->base, data->ngpio, 385 pdata->context); 386 if (status < 0) { 387 dev_err(&spi->dev, "%s --> %d\n", "teardown", status); 388 return status; 389 } 390 } 391 392 for (addr = 0; addr < 4; addr++) { 393 int tmp; 394 395 if (!data->mcp[addr]) 396 continue; 397 398 tmp = gpiochip_remove(&data->mcp[addr]->chip); 399 if (tmp < 0) { 400 dev_err(&spi->dev, "%s --> %d\n", "remove", tmp); 401 status = tmp; 402 } 403 } 404 if (status == 0) 405 kfree(data); 406 return status; 407} 408 409static struct spi_driver mcp23s08_driver = { 410 .probe = mcp23s08_probe, 411 .remove = mcp23s08_remove, 412 .driver = { 413 .name = "mcp23s08", 414 .owner = THIS_MODULE, 415 }, 416}; 417 418/*----------------------------------------------------------------------*/ 419 420static int __init mcp23s08_init(void) 421{ 422 return spi_register_driver(&mcp23s08_driver); 423} 424/* register after spi postcore initcall and before 425 * subsys initcalls that may rely on these GPIOs 426 */ 427subsys_initcall(mcp23s08_init); 428 429static void __exit mcp23s08_exit(void) 430{ 431 spi_unregister_driver(&mcp23s08_driver); 432} 433module_exit(mcp23s08_exit); 434 435MODULE_LICENSE("GPL");