aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/gpio
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/gpio')
-rw-r--r--drivers/gpio/mcp23s08.c133
1 files changed, 102 insertions, 31 deletions
diff --git a/drivers/gpio/mcp23s08.c b/drivers/gpio/mcp23s08.c
index 7efd7d3a81f9..8a1b405fefda 100644
--- a/drivers/gpio/mcp23s08.c
+++ b/drivers/gpio/mcp23s08.c
@@ -40,15 +40,26 @@ struct mcp23s08 {
40 struct spi_device *spi; 40 struct spi_device *spi;
41 u8 addr; 41 u8 addr;
42 42
43 u8 cache[11];
43 /* lock protects the cached values */ 44 /* lock protects the cached values */
44 struct mutex lock; 45 struct mutex lock;
45 u8 cache[11];
46 46
47 struct gpio_chip chip; 47 struct gpio_chip chip;
48 48
49 struct work_struct work; 49 struct work_struct work;
50}; 50};
51 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
52static int mcp23s08_read(struct mcp23s08 *mcp, unsigned reg) 63static int mcp23s08_read(struct mcp23s08 *mcp, unsigned reg)
53{ 64{
54 u8 tx[2], rx[1]; 65 u8 tx[2], rx[1];
@@ -208,25 +219,18 @@ done:
208 219
209/*----------------------------------------------------------------------*/ 220/*----------------------------------------------------------------------*/
210 221
211static int mcp23s08_probe(struct spi_device *spi) 222static int mcp23s08_probe_one(struct spi_device *spi, unsigned addr,
223 unsigned base, unsigned pullups)
212{ 224{
213 struct mcp23s08 *mcp; 225 struct mcp23s08_driver_data *data = spi_get_drvdata(spi);
214 struct mcp23s08_platform_data *pdata; 226 struct mcp23s08 *mcp = data->mcp[addr];
215 int status; 227 int status;
216 int do_update = 0; 228 int do_update = 0;
217 229
218 pdata = spi->dev.platform_data;
219 if (!pdata || pdata->slave > 3 || !pdata->base)
220 return -ENODEV;
221
222 mcp = kzalloc(sizeof *mcp, GFP_KERNEL);
223 if (!mcp)
224 return -ENOMEM;
225
226 mutex_init(&mcp->lock); 230 mutex_init(&mcp->lock);
227 231
228 mcp->spi = spi; 232 mcp->spi = spi;
229 mcp->addr = 0x40 | (pdata->slave << 1); 233 mcp->addr = 0x40 | (addr << 1);
230 234
231 mcp->chip.label = "mcp23s08", 235 mcp->chip.label = "mcp23s08",
232 236
@@ -236,27 +240,28 @@ static int mcp23s08_probe(struct spi_device *spi)
236 mcp->chip.set = mcp23s08_set; 240 mcp->chip.set = mcp23s08_set;
237 mcp->chip.dbg_show = mcp23s08_dbg_show; 241 mcp->chip.dbg_show = mcp23s08_dbg_show;
238 242
239 mcp->chip.base = pdata->base; 243 mcp->chip.base = base;
240 mcp->chip.ngpio = 8; 244 mcp->chip.ngpio = 8;
241 mcp->chip.can_sleep = 1; 245 mcp->chip.can_sleep = 1;
242 mcp->chip.dev = &spi->dev; 246 mcp->chip.dev = &spi->dev;
243 mcp->chip.owner = THIS_MODULE; 247 mcp->chip.owner = THIS_MODULE;
244 248
245 spi_set_drvdata(spi, mcp); 249 /* verify MCP_IOCON.SEQOP = 0, so sequential reads work,
246 250 * and MCP_IOCON.HAEN = 1, so we work with all chips.
247 /* verify MCP_IOCON.SEQOP = 0, so sequential reads work */ 251 */
248 status = mcp23s08_read(mcp, MCP_IOCON); 252 status = mcp23s08_read(mcp, MCP_IOCON);
249 if (status < 0) 253 if (status < 0)
250 goto fail; 254 goto fail;
251 if (status & IOCON_SEQOP) { 255 if ((status & IOCON_SEQOP) || !(status & IOCON_HAEN)) {
252 status &= ~IOCON_SEQOP; 256 status &= ~IOCON_SEQOP;
257 status |= IOCON_HAEN;
253 status = mcp23s08_write(mcp, MCP_IOCON, (u8) status); 258 status = mcp23s08_write(mcp, MCP_IOCON, (u8) status);
254 if (status < 0) 259 if (status < 0)
255 goto fail; 260 goto fail;
256 } 261 }
257 262
258 /* configure ~100K pullups */ 263 /* configure ~100K pullups */
259 status = mcp23s08_write(mcp, MCP_GPPU, pdata->pullups); 264 status = mcp23s08_write(mcp, MCP_GPPU, pullups);
260 if (status < 0) 265 if (status < 0)
261 goto fail; 266 goto fail;
262 267
@@ -283,11 +288,58 @@ static int mcp23s08_probe(struct spi_device *spi)
283 tx[1] = MCP_IPOL; 288 tx[1] = MCP_IPOL;
284 memcpy(&tx[2], &mcp->cache[MCP_IPOL], sizeof(tx) - 2); 289 memcpy(&tx[2], &mcp->cache[MCP_IPOL], sizeof(tx) - 2);
285 status = spi_write_then_read(mcp->spi, tx, sizeof tx, NULL, 0); 290 status = spi_write_then_read(mcp->spi, tx, sizeof tx, NULL, 0);
286 291 if (status < 0)
287 /* FIXME check status... */ 292 goto fail;
288 } 293 }
289 294
290 status = gpiochip_add(&mcp->chip); 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 return -ENODEV;
315
316 for (addr = 0; addr < 4; addr++) {
317 if (!pdata->chip[addr].is_present)
318 continue;
319 chips++;
320 }
321 if (!chips)
322 return -ENODEV;
323
324 data = kzalloc(sizeof *data + chips * sizeof(struct mcp23s08),
325 GFP_KERNEL);
326 if (!data)
327 return -ENOMEM;
328 spi_set_drvdata(spi, data);
329
330 base = pdata->base;
331 for (addr = 0; addr < 4; addr++) {
332 if (!pdata->chip[addr].is_present)
333 continue;
334 chips--;
335 data->mcp[addr] = &data->chip[chips];
336 status = mcp23s08_probe_one(spi, addr, base,
337 pdata->chip[addr].pullups);
338 if (status < 0)
339 goto fail;
340 base += 8;
341 }
342 data->ngpio = base - pdata->base;
291 343
292 /* NOTE: these chips have a relatively sane IRQ framework, with 344 /* NOTE: these chips have a relatively sane IRQ framework, with
293 * per-signal masking and level/edge triggering. It's not yet 345 * per-signal masking and level/edge triggering. It's not yet
@@ -295,8 +347,9 @@ static int mcp23s08_probe(struct spi_device *spi)
295 */ 347 */
296 348
297 if (pdata->setup) { 349 if (pdata->setup) {
298 status = pdata->setup(spi, mcp->chip.base, 350 status = pdata->setup(spi,
299 mcp->chip.ngpio, pdata->context); 351 pdata->base, data->ngpio,
352 pdata->context);
300 if (status < 0) 353 if (status < 0)
301 dev_dbg(&spi->dev, "setup --> %d\n", status); 354 dev_dbg(&spi->dev, "setup --> %d\n", status);
302 } 355 }
@@ -304,19 +357,29 @@ static int mcp23s08_probe(struct spi_device *spi)
304 return 0; 357 return 0;
305 358
306fail: 359fail:
307 kfree(mcp); 360 for (addr = 0; addr < 4; addr++) {
361 int tmp;
362
363 if (!data->mcp[addr])
364 continue;
365 tmp = gpiochip_remove(&data->mcp[addr]->chip);
366 if (tmp < 0)
367 dev_err(&spi->dev, "%s --> %d\n", "remove", tmp);
368 }
369 kfree(data);
308 return status; 370 return status;
309} 371}
310 372
311static int mcp23s08_remove(struct spi_device *spi) 373static int mcp23s08_remove(struct spi_device *spi)
312{ 374{
313 struct mcp23s08 *mcp = spi_get_drvdata(spi); 375 struct mcp23s08_driver_data *data = spi_get_drvdata(spi);
314 struct mcp23s08_platform_data *pdata = spi->dev.platform_data; 376 struct mcp23s08_platform_data *pdata = spi->dev.platform_data;
377 unsigned addr;
315 int status = 0; 378 int status = 0;
316 379
317 if (pdata->teardown) { 380 if (pdata->teardown) {
318 status = pdata->teardown(spi, 381 status = pdata->teardown(spi,
319 mcp->chip.base, mcp->chip.ngpio, 382 pdata->base, data->ngpio,
320 pdata->context); 383 pdata->context);
321 if (status < 0) { 384 if (status < 0) {
322 dev_err(&spi->dev, "%s --> %d\n", "teardown", status); 385 dev_err(&spi->dev, "%s --> %d\n", "teardown", status);
@@ -324,11 +387,20 @@ static int mcp23s08_remove(struct spi_device *spi)
324 } 387 }
325 } 388 }
326 389
327 status = gpiochip_remove(&mcp->chip); 390 for (addr = 0; addr < 4; addr++) {
391 int tmp;
392
393 if (!data->mcp[addr])
394 continue;
395
396 tmp = gpiochip_remove(&data->mcp[addr]->chip);
397 if (tmp < 0) {
398 dev_err(&spi->dev, "%s --> %d\n", "remove", tmp);
399 status = tmp;
400 }
401 }
328 if (status == 0) 402 if (status == 0)
329 kfree(mcp); 403 kfree(data);
330 else
331 dev_err(&spi->dev, "%s --> %d\n", "remove", status);
332 return status; 404 return status;
333} 405}
334 406
@@ -356,4 +428,3 @@ static void __exit mcp23s08_exit(void)
356module_exit(mcp23s08_exit); 428module_exit(mcp23s08_exit);
357 429
358MODULE_LICENSE("GPL"); 430MODULE_LICENSE("GPL");
359