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