gpio-mcp23s08.c 15.6 KB
Newer Older
1
/*
P
Peter Korsgaard 已提交
2
 * MCP23S08 SPI/GPIO gpio expander driver
3 4 5 6 7
 */

#include <linux/kernel.h>
#include <linux/device.h>
#include <linux/mutex.h>
8
#include <linux/gpio.h>
P
Peter Korsgaard 已提交
9
#include <linux/i2c.h>
10 11
#include <linux/spi/spi.h>
#include <linux/spi/mcp23s08.h>
12
#include <linux/slab.h>
13
#include <asm/byteorder.h>
14

15 16 17 18 19
/**
 * MCP types supported by driver
 */
#define MCP_TYPE_S08	0
#define MCP_TYPE_S17	1
P
Peter Korsgaard 已提交
20 21
#define MCP_TYPE_008	2
#define MCP_TYPE_017	3
22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43

/* Registers are all 8 bits wide.
 *
 * The mcp23s17 has twice as many bits, and can be configured to work
 * with either 16 bit registers or with two adjacent 8 bit banks.
 */
#define MCP_IODIR	0x00		/* init/reset:  all ones */
#define MCP_IPOL	0x01
#define MCP_GPINTEN	0x02
#define MCP_DEFVAL	0x03
#define MCP_INTCON	0x04
#define MCP_IOCON	0x05
#	define IOCON_SEQOP	(1 << 5)
#	define IOCON_HAEN	(1 << 3)
#	define IOCON_ODR	(1 << 2)
#	define IOCON_INTPOL	(1 << 1)
#define MCP_GPPU	0x06
#define MCP_INTF	0x07
#define MCP_INTCAP	0x08
#define MCP_GPIO	0x09
#define MCP_OLAT	0x0a

44 45 46 47 48 49 50 51 52
struct mcp23s08;

struct mcp23s08_ops {
	int	(*read)(struct mcp23s08 *mcp, unsigned reg);
	int	(*write)(struct mcp23s08 *mcp, unsigned reg, unsigned val);
	int	(*read_regs)(struct mcp23s08 *mcp, unsigned reg,
			     u16 *vals, unsigned n);
};

53 54 55
struct mcp23s08 {
	u8			addr;

56
	u16			cache[11];
57 58 59 60 61
	/* lock protects the cached values */
	struct mutex		lock;

	struct gpio_chip	chip;

62
	const struct mcp23s08_ops	*ops;
63
	void			*data; /* ops specific data */
64 65
};

66
/* A given spi_device can represent up to eight mcp23sxx chips
67 68 69 70 71 72
 * sharing the same chipselect but using different addresses
 * (e.g. chips #0 and #3 might be populated, but not #1 or $2).
 * Driver data holds all the per-chip data.
 */
struct mcp23s08_driver_data {
	unsigned		ngpio;
73
	struct mcp23s08		*mcp[8];
74 75 76
	struct mcp23s08		chip[];
};

P
Peter Korsgaard 已提交
77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142
/*----------------------------------------------------------------------*/

#ifdef CONFIG_I2C

static int mcp23008_read(struct mcp23s08 *mcp, unsigned reg)
{
	return i2c_smbus_read_byte_data(mcp->data, reg);
}

static int mcp23008_write(struct mcp23s08 *mcp, unsigned reg, unsigned val)
{
	return i2c_smbus_write_byte_data(mcp->data, reg, val);
}

static int
mcp23008_read_regs(struct mcp23s08 *mcp, unsigned reg, u16 *vals, unsigned n)
{
	while (n--) {
		int ret = mcp23008_read(mcp, reg++);
		if (ret < 0)
			return ret;
		*vals++ = ret;
	}

	return 0;
}

static int mcp23017_read(struct mcp23s08 *mcp, unsigned reg)
{
	return i2c_smbus_read_word_data(mcp->data, reg << 1);
}

static int mcp23017_write(struct mcp23s08 *mcp, unsigned reg, unsigned val)
{
	return i2c_smbus_write_word_data(mcp->data, reg << 1, val);
}

static int
mcp23017_read_regs(struct mcp23s08 *mcp, unsigned reg, u16 *vals, unsigned n)
{
	while (n--) {
		int ret = mcp23017_read(mcp, reg++);
		if (ret < 0)
			return ret;
		*vals++ = ret;
	}

	return 0;
}

static const struct mcp23s08_ops mcp23008_ops = {
	.read		= mcp23008_read,
	.write		= mcp23008_write,
	.read_regs	= mcp23008_read_regs,
};

static const struct mcp23s08_ops mcp23017_ops = {
	.read		= mcp23017_read,
	.write		= mcp23017_write,
	.read_regs	= mcp23017_read_regs,
};

#endif /* CONFIG_I2C */

/*----------------------------------------------------------------------*/

143 144
#ifdef CONFIG_SPI_MASTER

145 146 147 148 149 150 151
static int mcp23s08_read(struct mcp23s08 *mcp, unsigned reg)
{
	u8	tx[2], rx[1];
	int	status;

	tx[0] = mcp->addr | 0x01;
	tx[1] = reg;
152
	status = spi_write_then_read(mcp->data, tx, sizeof tx, rx, sizeof rx);
153 154 155
	return (status < 0) ? status : rx[0];
}

156
static int mcp23s08_write(struct mcp23s08 *mcp, unsigned reg, unsigned val)
157 158 159 160 161 162
{
	u8	tx[3];

	tx[0] = mcp->addr;
	tx[1] = reg;
	tx[2] = val;
163
	return spi_write_then_read(mcp->data, tx, sizeof tx, NULL, 0);
164 165 166
}

static int
167
mcp23s08_read_regs(struct mcp23s08 *mcp, unsigned reg, u16 *vals, unsigned n)
168
{
169 170
	u8	tx[2], *tmp;
	int	status;
171 172 173 174 175

	if ((n + reg) > sizeof mcp->cache)
		return -EINVAL;
	tx[0] = mcp->addr | 0x01;
	tx[1] = reg;
176 177

	tmp = (u8 *)vals;
178
	status = spi_write_then_read(mcp->data, tx, sizeof tx, tmp, n);
179 180 181 182 183 184 185 186 187 188 189 190 191 192
	if (status >= 0) {
		while (n--)
			vals[n] = tmp[n]; /* expand to 16bit */
	}
	return status;
}

static int mcp23s17_read(struct mcp23s08 *mcp, unsigned reg)
{
	u8	tx[2], rx[2];
	int	status;

	tx[0] = mcp->addr | 0x01;
	tx[1] = reg << 1;
193
	status = spi_write_then_read(mcp->data, tx, sizeof tx, rx, sizeof rx);
194 195 196 197 198 199 200 201 202 203 204
	return (status < 0) ? status : (rx[0] | (rx[1] << 8));
}

static int mcp23s17_write(struct mcp23s08 *mcp, unsigned reg, unsigned val)
{
	u8	tx[4];

	tx[0] = mcp->addr;
	tx[1] = reg << 1;
	tx[2] = val;
	tx[3] = val >> 8;
205
	return spi_write_then_read(mcp->data, tx, sizeof tx, NULL, 0);
206 207 208 209 210 211 212 213 214 215 216 217 218
}

static int
mcp23s17_read_regs(struct mcp23s08 *mcp, unsigned reg, u16 *vals, unsigned n)
{
	u8	tx[2];
	int	status;

	if ((n + reg) > sizeof mcp->cache)
		return -EINVAL;
	tx[0] = mcp->addr | 0x01;
	tx[1] = reg << 1;

219
	status = spi_write_then_read(mcp->data, tx, sizeof tx,
220 221 222 223 224 225 226
				     (u8 *)vals, n * 2);
	if (status >= 0) {
		while (n--)
			vals[n] = __le16_to_cpu((__le16)vals[n]);
	}

	return status;
227 228
}

229 230 231 232 233 234 235 236 237 238 239 240
static const struct mcp23s08_ops mcp23s08_ops = {
	.read		= mcp23s08_read,
	.write		= mcp23s08_write,
	.read_regs	= mcp23s08_read_regs,
};

static const struct mcp23s08_ops mcp23s17_ops = {
	.read		= mcp23s17_read,
	.write		= mcp23s17_write,
	.read_regs	= mcp23s17_read_regs,
};

241
#endif /* CONFIG_SPI_MASTER */
242

243 244 245 246 247 248 249 250 251
/*----------------------------------------------------------------------*/

static int mcp23s08_direction_input(struct gpio_chip *chip, unsigned offset)
{
	struct mcp23s08	*mcp = container_of(chip, struct mcp23s08, chip);
	int status;

	mutex_lock(&mcp->lock);
	mcp->cache[MCP_IODIR] |= (1 << offset);
252
	status = mcp->ops->write(mcp, MCP_IODIR, mcp->cache[MCP_IODIR]);
253 254 255 256 257 258 259 260 261 262 263 264
	mutex_unlock(&mcp->lock);
	return status;
}

static int mcp23s08_get(struct gpio_chip *chip, unsigned offset)
{
	struct mcp23s08	*mcp = container_of(chip, struct mcp23s08, chip);
	int status;

	mutex_lock(&mcp->lock);

	/* REVISIT reading this clears any IRQ ... */
265
	status = mcp->ops->read(mcp, MCP_GPIO);
266 267 268 269 270 271 272 273 274 275 276 277
	if (status < 0)
		status = 0;
	else {
		mcp->cache[MCP_GPIO] = status;
		status = !!(status & (1 << offset));
	}
	mutex_unlock(&mcp->lock);
	return status;
}

static int __mcp23s08_set(struct mcp23s08 *mcp, unsigned mask, int value)
{
278
	unsigned olat = mcp->cache[MCP_OLAT];
279 280 281 282 283 284

	if (value)
		olat |= mask;
	else
		olat &= ~mask;
	mcp->cache[MCP_OLAT] = olat;
285
	return mcp->ops->write(mcp, MCP_OLAT, olat);
286 287 288 289 290
}

static void mcp23s08_set(struct gpio_chip *chip, unsigned offset, int value)
{
	struct mcp23s08	*mcp = container_of(chip, struct mcp23s08, chip);
291
	unsigned mask = 1 << offset;
292 293 294 295 296 297 298 299 300 301

	mutex_lock(&mcp->lock);
	__mcp23s08_set(mcp, mask, value);
	mutex_unlock(&mcp->lock);
}

static int
mcp23s08_direction_output(struct gpio_chip *chip, unsigned offset, int value)
{
	struct mcp23s08	*mcp = container_of(chip, struct mcp23s08, chip);
302
	unsigned mask = 1 << offset;
303 304 305 306 307 308
	int status;

	mutex_lock(&mcp->lock);
	status = __mcp23s08_set(mcp, mask, value);
	if (status == 0) {
		mcp->cache[MCP_IODIR] &= ~mask;
309
		status = mcp->ops->write(mcp, MCP_IODIR, mcp->cache[MCP_IODIR]);
310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328
	}
	mutex_unlock(&mcp->lock);
	return status;
}

/*----------------------------------------------------------------------*/

#ifdef CONFIG_DEBUG_FS

#include <linux/seq_file.h>

/*
 * This shows more info than the generic gpio dump code:
 * pullups, deglitching, open drain drive.
 */
static void mcp23s08_dbg_show(struct seq_file *s, struct gpio_chip *chip)
{
	struct mcp23s08	*mcp;
	char		bank;
R
Roel Kluin 已提交
329
	int		t;
330 331 332 333 334
	unsigned	mask;

	mcp = container_of(chip, struct mcp23s08, chip);

	/* NOTE: we only handle one bank for now ... */
335
	bank = '0' + ((mcp->addr >> 1) & 0x7);
336 337

	mutex_lock(&mcp->lock);
338
	t = mcp->ops->read_regs(mcp, 0, mcp->cache, ARRAY_SIZE(mcp->cache));
339 340 341 342 343
	if (t < 0) {
		seq_printf(s, " I/O ERROR %d\n", t);
		goto done;
	}

344
	for (t = 0, mask = 1; t < chip->ngpio; t++, mask <<= 1) {
345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368
		const char	*label;

		label = gpiochip_is_requested(chip, t);
		if (!label)
			continue;

		seq_printf(s, " gpio-%-3d P%c.%d (%-12s) %s %s %s",
			chip->base + t, bank, t, label,
			(mcp->cache[MCP_IODIR] & mask) ? "in " : "out",
			(mcp->cache[MCP_GPIO] & mask) ? "hi" : "lo",
			(mcp->cache[MCP_GPPU] & mask) ? "  " : "up");
		/* NOTE:  ignoring the irq-related registers */
		seq_printf(s, "\n");
	}
done:
	mutex_unlock(&mcp->lock);
}

#else
#define mcp23s08_dbg_show	NULL
#endif

/*----------------------------------------------------------------------*/

369 370
static int mcp23s08_probe_one(struct mcp23s08 *mcp, struct device *dev,
			      void *data, unsigned addr,
371
			      unsigned type, unsigned base, unsigned pullups)
372
{
373
	int status;
374 375 376

	mutex_init(&mcp->lock);

377 378
	mcp->data = data;
	mcp->addr = addr;
379 380 381 382 383 384 385

	mcp->chip.direction_input = mcp23s08_direction_input;
	mcp->chip.get = mcp23s08_get;
	mcp->chip.direction_output = mcp23s08_direction_output;
	mcp->chip.set = mcp23s08_set;
	mcp->chip.dbg_show = mcp23s08_dbg_show;

386 387 388
	switch (type) {
#ifdef CONFIG_SPI_MASTER
	case MCP_TYPE_S08:
389 390 391
		mcp->ops = &mcp23s08_ops;
		mcp->chip.ngpio = 8;
		mcp->chip.label = "mcp23s08";
392 393 394 395 396 397 398 399 400
		break;

	case MCP_TYPE_S17:
		mcp->ops = &mcp23s17_ops;
		mcp->chip.ngpio = 16;
		mcp->chip.label = "mcp23s17";
		break;
#endif /* CONFIG_SPI_MASTER */

P
Peter Korsgaard 已提交
401 402 403 404 405 406 407 408 409 410 411 412 413 414
#ifdef CONFIG_I2C
	case MCP_TYPE_008:
		mcp->ops = &mcp23008_ops;
		mcp->chip.ngpio = 8;
		mcp->chip.label = "mcp23008";
		break;

	case MCP_TYPE_017:
		mcp->ops = &mcp23017_ops;
		mcp->chip.ngpio = 16;
		mcp->chip.label = "mcp23017";
		break;
#endif /* CONFIG_I2C */

415 416 417
	default:
		dev_err(dev, "invalid device type (%d)\n", type);
		return -EINVAL;
418
	}
419

420
	mcp->chip.base = base;
421
	mcp->chip.can_sleep = 1;
422
	mcp->chip.dev = dev;
423
	mcp->chip.owner = THIS_MODULE;
424

425 426 427
	/* verify MCP_IOCON.SEQOP = 0, so sequential reads work,
	 * and MCP_IOCON.HAEN = 1, so we work with all chips.
	 */
428
	status = mcp->ops->read(mcp, MCP_IOCON);
429 430
	if (status < 0)
		goto fail;
431
	if ((status & IOCON_SEQOP) || !(status & IOCON_HAEN)) {
432 433 434 435
		/* mcp23s17 has IOCON twice, make sure they are in sync */
		status &= ~(IOCON_SEQOP | (IOCON_SEQOP << 8));
		status |= IOCON_HAEN | (IOCON_HAEN << 8);
		status = mcp->ops->write(mcp, MCP_IOCON, status);
436 437 438 439 440
		if (status < 0)
			goto fail;
	}

	/* configure ~100K pullups */
441
	status = mcp->ops->write(mcp, MCP_GPPU, pullups);
442 443 444
	if (status < 0)
		goto fail;

445
	status = mcp->ops->read_regs(mcp, 0, mcp->cache, ARRAY_SIZE(mcp->cache));
446 447 448 449 450 451
	if (status < 0)
		goto fail;

	/* disable inverter on input */
	if (mcp->cache[MCP_IPOL] != 0) {
		mcp->cache[MCP_IPOL] = 0;
452 453 454
		status = mcp->ops->write(mcp, MCP_IPOL, 0);
		if (status < 0)
			goto fail;
455 456 457 458 459
	}

	/* disable irqs */
	if (mcp->cache[MCP_GPINTEN] != 0) {
		mcp->cache[MCP_GPINTEN] = 0;
460
		status = mcp->ops->write(mcp, MCP_GPINTEN, 0);
461 462
		if (status < 0)
			goto fail;
463 464 465
	}

	status = gpiochip_add(&mcp->chip);
466 467
fail:
	if (status < 0)
468 469
		dev_dbg(dev, "can't setup chip %d, --> %d\n",
			addr, status);
470 471 472
	return status;
}

P
Peter Korsgaard 已提交
473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557
/*----------------------------------------------------------------------*/

#ifdef CONFIG_I2C

static int __devinit mcp230xx_probe(struct i2c_client *client,
				    const struct i2c_device_id *id)
{
	struct mcp23s08_platform_data *pdata;
	struct mcp23s08 *mcp;
	int status;

	pdata = client->dev.platform_data;
	if (!pdata || !gpio_is_valid(pdata->base)) {
		dev_dbg(&client->dev, "invalid or missing platform data\n");
		return -EINVAL;
	}

	mcp = kzalloc(sizeof *mcp, GFP_KERNEL);
	if (!mcp)
		return -ENOMEM;

	status = mcp23s08_probe_one(mcp, &client->dev, client, client->addr,
				    id->driver_data, pdata->base,
				    pdata->chip[0].pullups);
	if (status)
		goto fail;

	i2c_set_clientdata(client, mcp);

	return 0;

fail:
	kfree(mcp);

	return status;
}

static int __devexit mcp230xx_remove(struct i2c_client *client)
{
	struct mcp23s08 *mcp = i2c_get_clientdata(client);
	int status;

	status = gpiochip_remove(&mcp->chip);
	if (status == 0)
		kfree(mcp);

	return status;
}

static const struct i2c_device_id mcp230xx_id[] = {
	{ "mcp23008", MCP_TYPE_008 },
	{ "mcp23017", MCP_TYPE_017 },
	{ },
};
MODULE_DEVICE_TABLE(i2c, mcp230xx_id);

static struct i2c_driver mcp230xx_driver = {
	.driver = {
		.name	= "mcp230xx",
		.owner	= THIS_MODULE,
	},
	.probe		= mcp230xx_probe,
	.remove		= __devexit_p(mcp230xx_remove),
	.id_table	= mcp230xx_id,
};

static int __init mcp23s08_i2c_init(void)
{
	return i2c_add_driver(&mcp230xx_driver);
}

static void mcp23s08_i2c_exit(void)
{
	i2c_del_driver(&mcp230xx_driver);
}

#else

static int __init mcp23s08_i2c_init(void) { return 0; }
static void mcp23s08_i2c_exit(void) { }

#endif /* CONFIG_I2C */

/*----------------------------------------------------------------------*/

558 559
#ifdef CONFIG_SPI_MASTER

560 561 562 563 564 565
static int mcp23s08_probe(struct spi_device *spi)
{
	struct mcp23s08_platform_data	*pdata;
	unsigned			addr;
	unsigned			chips = 0;
	struct mcp23s08_driver_data	*data;
566
	int				status, type;
567 568
	unsigned			base;

569 570
	type = spi_get_device_id(spi)->driver_data;

571
	pdata = spi->dev.platform_data;
572 573 574 575
	if (!pdata || !gpio_is_valid(pdata->base)) {
		dev_dbg(&spi->dev, "invalid or missing platform data\n");
		return -EINVAL;
	}
576

577
	for (addr = 0; addr < ARRAY_SIZE(pdata->chip); addr++) {
578 579 580
		if (!pdata->chip[addr].is_present)
			continue;
		chips++;
581 582 583 584 585
		if ((type == MCP_TYPE_S08) && (addr > 3)) {
			dev_err(&spi->dev,
				"mcp23s08 only supports address 0..3\n");
			return -EINVAL;
		}
586 587 588 589 590 591 592 593 594 595 596
	}
	if (!chips)
		return -ENODEV;

	data = kzalloc(sizeof *data + chips * sizeof(struct mcp23s08),
			GFP_KERNEL);
	if (!data)
		return -ENOMEM;
	spi_set_drvdata(spi, data);

	base = pdata->base;
597
	for (addr = 0; addr < ARRAY_SIZE(pdata->chip); addr++) {
598 599 600 601
		if (!pdata->chip[addr].is_present)
			continue;
		chips--;
		data->mcp[addr] = &data->chip[chips];
602 603
		status = mcp23s08_probe_one(data->mcp[addr], &spi->dev, spi,
					    0x40 | (addr << 1), type, base,
604
					    pdata->chip[addr].pullups);
605 606
		if (status < 0)
			goto fail;
607 608

		base += (type == MCP_TYPE_S17) ? 16 : 8;
609 610
	}
	data->ngpio = base - pdata->base;
611 612 613 614 615 616 617 618 619

	/* NOTE:  these chips have a relatively sane IRQ framework, with
	 * per-signal masking and level/edge triggering.  It's not yet
	 * handled here...
	 */

	return 0;

fail:
620
	for (addr = 0; addr < ARRAY_SIZE(data->mcp); addr++) {
621 622 623 624 625 626 627 628 629
		int tmp;

		if (!data->mcp[addr])
			continue;
		tmp = gpiochip_remove(&data->mcp[addr]->chip);
		if (tmp < 0)
			dev_err(&spi->dev, "%s --> %d\n", "remove", tmp);
	}
	kfree(data);
630 631 632 633 634
	return status;
}

static int mcp23s08_remove(struct spi_device *spi)
{
635 636
	struct mcp23s08_driver_data	*data = spi_get_drvdata(spi);
	unsigned			addr;
637 638
	int				status = 0;

639
	for (addr = 0; addr < ARRAY_SIZE(data->mcp); addr++) {
640 641 642 643 644 645 646 647 648 649 650
		int tmp;

		if (!data->mcp[addr])
			continue;

		tmp = gpiochip_remove(&data->mcp[addr]->chip);
		if (tmp < 0) {
			dev_err(&spi->dev, "%s --> %d\n", "remove", tmp);
			status = tmp;
		}
	}
651
	if (status == 0)
652
		kfree(data);
653 654 655
	return status;
}

656 657 658 659 660 661 662
static const struct spi_device_id mcp23s08_ids[] = {
	{ "mcp23s08", MCP_TYPE_S08 },
	{ "mcp23s17", MCP_TYPE_S17 },
	{ },
};
MODULE_DEVICE_TABLE(spi, mcp23s08_ids);

663 664 665
static struct spi_driver mcp23s08_driver = {
	.probe		= mcp23s08_probe,
	.remove		= mcp23s08_remove,
666
	.id_table	= mcp23s08_ids,
667 668 669 670 671 672
	.driver = {
		.name	= "mcp23s08",
		.owner	= THIS_MODULE,
	},
};

673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689
static int __init mcp23s08_spi_init(void)
{
	return spi_register_driver(&mcp23s08_driver);
}

static void mcp23s08_spi_exit(void)
{
	spi_unregister_driver(&mcp23s08_driver);
}

#else

static int __init mcp23s08_spi_init(void) { return 0; }
static void mcp23s08_spi_exit(void) { }

#endif /* CONFIG_SPI_MASTER */

690 691 692 693
/*----------------------------------------------------------------------*/

static int __init mcp23s08_init(void)
{
P
Peter Korsgaard 已提交
694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709
	int ret;

	ret = mcp23s08_spi_init();
	if (ret)
		goto spi_fail;

	ret = mcp23s08_i2c_init();
	if (ret)
		goto i2c_fail;

	return 0;

 i2c_fail:
	mcp23s08_spi_exit();
 spi_fail:
	return ret;
710
}
P
Peter Korsgaard 已提交
711
/* register after spi/i2c postcore initcall and before
712 713 714
 * subsys initcalls that may rely on these GPIOs
 */
subsys_initcall(mcp23s08_init);
715 716 717

static void __exit mcp23s08_exit(void)
{
718
	mcp23s08_spi_exit();
P
Peter Korsgaard 已提交
719
	mcp23s08_i2c_exit();
720 721 722 723
}
module_exit(mcp23s08_exit);

MODULE_LICENSE("GPL");