Linux kernel & device driver programming

Cross-Referenced Linux and Device Driver Code

[ source navigation ] [ diff markup ] [ identifier search ] [ freetext search ] [ file search ]
Version: [ 2.6.11.8 ] [ 2.6.25 ] [ 2.6.25.8 ] [ 2.6.31.13 ] Architecture: [ i386 ]
  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 
 39 struct 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  */
 57 struct mcp23s08_driver_data {
 58         unsigned                ngpio;
 59         struct mcp23s08         *mcp[4];
 60         struct mcp23s08         chip[];
 61 };
 62 
 63 static 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 
 74 static 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 
 84 static int
 85 mcp23s08_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 
 98 static 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 
110 static 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 
129 static 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 
141 static 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 
151 static int
152 mcp23s08_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  */
178 static 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 = '' + ((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         }
212 done:
213         mutex_unlock(&mcp->lock);
214 }
215 
216 #else
217 #define mcp23s08_dbg_show       NULL
218 #endif
219 
220 /*----------------------------------------------------------------------*/
221 
222 static 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);
296 fail:
297         if (status < 0)
298                 dev_dbg(&spi->dev, "can't setup chip %d, --> %d\n",
299                                 addr, status);
300         return status;
301 }
302 
303 static 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 
361 fail:
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 
375 static 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 
409 static 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 
420 static 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  */
427 subsys_initcall(mcp23s08_init);
428 
429 static void __exit mcp23s08_exit(void)
430 {
431         spi_unregister_driver(&mcp23s08_driver);
432 }
433 module_exit(mcp23s08_exit);
434 
435 MODULE_LICENSE("GPL");
436 
  This page was automatically generated by the LXR engine.