mcp23s08.c 12.1 KB
Newer Older
1 2 3 4 5 6 7 8
/*
 * mcp23s08.c - SPI gpio expander driver
 */

#include <linux/kernel.h>
#include <linux/device.h>
#include <linux/workqueue.h>
#include <linux/mutex.h>
9
#include <linux/gpio.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
20 21 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.
 *
 * Also, there are I2C versions of both chips.
 */
#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 56
struct mcp23s08 {
	struct spi_device	*spi;
	u8			addr;

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

	struct gpio_chip	chip;

	struct work_struct	work;
64 65

	const struct mcp23s08_ops	*ops;
66 67
};

68
/* A given spi_device can represent up to eight mcp23sxx chips
69 70 71 72 73 74
 * 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;
75
	struct mcp23s08		*mcp[8];
76 77 78
	struct mcp23s08		chip[];
};

79 80 81 82 83 84 85 86 87 88 89
static int mcp23s08_read(struct mcp23s08 *mcp, unsigned reg)
{
	u8	tx[2], rx[1];
	int	status;

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

90
static int mcp23s08_write(struct mcp23s08 *mcp, unsigned reg, unsigned val)
91 92 93 94 95 96 97 98 99 100
{
	u8	tx[3];

	tx[0] = mcp->addr;
	tx[1] = reg;
	tx[2] = val;
	return spi_write_then_read(mcp->spi, tx, sizeof tx, NULL, 0);
}

static int
101
mcp23s08_read_regs(struct mcp23s08 *mcp, unsigned reg, u16 *vals, unsigned n)
102
{
103 104
	u8	tx[2], *tmp;
	int	status;
105 106 107 108 109

	if ((n + reg) > sizeof mcp->cache)
		return -EINVAL;
	tx[0] = mcp->addr | 0x01;
	tx[1] = reg;
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 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160

	tmp = (u8 *)vals;
	status = spi_write_then_read(mcp->spi, tx, sizeof tx, tmp, n);
	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;
	status = spi_write_then_read(mcp->spi, tx, sizeof tx, rx, sizeof rx);
	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;
	return spi_write_then_read(mcp->spi, tx, sizeof tx, NULL, 0);
}

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;

	status = spi_write_then_read(mcp->spi, tx, sizeof tx,
				     (u8 *)vals, n * 2);
	if (status >= 0) {
		while (n--)
			vals[n] = __le16_to_cpu((__le16)vals[n]);
	}

	return status;
161 162
}

163 164 165 166 167 168 169 170 171 172 173 174 175
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,
};


176 177 178 179 180 181 182 183 184
/*----------------------------------------------------------------------*/

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);
185
	status = mcp->ops->write(mcp, MCP_IODIR, mcp->cache[MCP_IODIR]);
186 187 188 189 190 191 192 193 194 195 196 197
	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 ... */
198
	status = mcp->ops->read(mcp, MCP_GPIO);
199 200 201 202 203 204 205 206 207 208 209 210
	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)
{
211
	unsigned olat = mcp->cache[MCP_OLAT];
212 213 214 215 216 217

	if (value)
		olat |= mask;
	else
		olat &= ~mask;
	mcp->cache[MCP_OLAT] = olat;
218
	return mcp->ops->write(mcp, MCP_OLAT, olat);
219 220 221 222 223
}

static void mcp23s08_set(struct gpio_chip *chip, unsigned offset, int value)
{
	struct mcp23s08	*mcp = container_of(chip, struct mcp23s08, chip);
224
	unsigned mask = 1 << offset;
225 226 227 228 229 230 231 232 233 234

	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);
235
	unsigned mask = 1 << offset;
236 237 238 239 240 241
	int status;

	mutex_lock(&mcp->lock);
	status = __mcp23s08_set(mcp, mask, value);
	if (status == 0) {
		mcp->cache[MCP_IODIR] &= ~mask;
242
		status = mcp->ops->write(mcp, MCP_IODIR, mcp->cache[MCP_IODIR]);
243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261
	}
	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 已提交
262
	int		t;
263 264 265 266 267
	unsigned	mask;

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

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

	mutex_lock(&mcp->lock);
271
	t = mcp->ops->read_regs(mcp, 0, mcp->cache, ARRAY_SIZE(mcp->cache));
272 273 274 275 276
	if (t < 0) {
		seq_printf(s, " I/O ERROR %d\n", t);
		goto done;
	}

277
	for (t = 0, mask = 1; t < chip->ngpio; t++, mask <<= 1) {
278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301
		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

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

302
static int mcp23s08_probe_one(struct spi_device *spi, unsigned addr,
303
			      unsigned type, unsigned base, unsigned pullups)
304
{
305 306
	struct mcp23s08_driver_data	*data = spi_get_drvdata(spi);
	struct mcp23s08			*mcp = data->mcp[addr];
307 308 309 310 311
	int				status;

	mutex_init(&mcp->lock);

	mcp->spi = spi;
312
	mcp->addr = 0x40 | (addr << 1);
313 314 315 316 317 318 319

	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;

320 321 322 323 324 325 326 327 328
	if (type == MCP_TYPE_S17) {
		mcp->ops = &mcp23s17_ops;
		mcp->chip.ngpio = 16;
		mcp->chip.label = "mcp23s17";
	} else {
		mcp->ops = &mcp23s08_ops;
		mcp->chip.ngpio = 8;
		mcp->chip.label = "mcp23s08";
	}
329
	mcp->chip.base = base;
330
	mcp->chip.can_sleep = 1;
D
David Brownell 已提交
331
	mcp->chip.dev = &spi->dev;
332
	mcp->chip.owner = THIS_MODULE;
333

334 335 336
	/* verify MCP_IOCON.SEQOP = 0, so sequential reads work,
	 * and MCP_IOCON.HAEN = 1, so we work with all chips.
	 */
337
	status = mcp->ops->read(mcp, MCP_IOCON);
338 339
	if (status < 0)
		goto fail;
340
	if ((status & IOCON_SEQOP) || !(status & IOCON_HAEN)) {
341 342 343 344
		/* 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);
345 346 347 348 349
		if (status < 0)
			goto fail;
	}

	/* configure ~100K pullups */
350
	status = mcp->ops->write(mcp, MCP_GPPU, pullups);
351 352 353
	if (status < 0)
		goto fail;

354
	status = mcp->ops->read_regs(mcp, 0, mcp->cache, ARRAY_SIZE(mcp->cache));
355 356 357 358 359 360
	if (status < 0)
		goto fail;

	/* disable inverter on input */
	if (mcp->cache[MCP_IPOL] != 0) {
		mcp->cache[MCP_IPOL] = 0;
361 362 363
		status = mcp->ops->write(mcp, MCP_IPOL, 0);
		if (status < 0)
			goto fail;
364 365 366 367 368
	}

	/* disable irqs */
	if (mcp->cache[MCP_GPINTEN] != 0) {
		mcp->cache[MCP_GPINTEN] = 0;
369
		status = mcp->ops->write(mcp, MCP_GPINTEN, 0);
370 371
		if (status < 0)
			goto fail;
372 373 374
	}

	status = gpiochip_add(&mcp->chip);
375 376 377 378 379 380 381 382 383 384 385 386 387
fail:
	if (status < 0)
		dev_dbg(&spi->dev, "can't setup chip %d, --> %d\n",
				addr, status);
	return status;
}

static int mcp23s08_probe(struct spi_device *spi)
{
	struct mcp23s08_platform_data	*pdata;
	unsigned			addr;
	unsigned			chips = 0;
	struct mcp23s08_driver_data	*data;
388
	int				status, type;
389 390
	unsigned			base;

391 392
	type = spi_get_device_id(spi)->driver_data;

393
	pdata = spi->dev.platform_data;
394 395 396 397
	if (!pdata || !gpio_is_valid(pdata->base)) {
		dev_dbg(&spi->dev, "invalid or missing platform data\n");
		return -EINVAL;
	}
398

399
	for (addr = 0; addr < ARRAY_SIZE(pdata->chip); addr++) {
400 401 402
		if (!pdata->chip[addr].is_present)
			continue;
		chips++;
403 404 405 406 407
		if ((type == MCP_TYPE_S08) && (addr > 3)) {
			dev_err(&spi->dev,
				"mcp23s08 only supports address 0..3\n");
			return -EINVAL;
		}
408 409 410 411 412 413 414 415 416 417 418
	}
	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;
419
	for (addr = 0; addr < ARRAY_SIZE(pdata->chip); addr++) {
420 421 422 423
		if (!pdata->chip[addr].is_present)
			continue;
		chips--;
		data->mcp[addr] = &data->chip[chips];
424 425
		status = mcp23s08_probe_one(spi, addr, type, base,
					    pdata->chip[addr].pullups);
426 427
		if (status < 0)
			goto fail;
428 429

		base += (type == MCP_TYPE_S17) ? 16 : 8;
430 431
	}
	data->ngpio = base - pdata->base;
432 433 434 435 436 437 438

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

	if (pdata->setup) {
439 440 441
		status = pdata->setup(spi,
				pdata->base, data->ngpio,
				pdata->context);
442 443 444 445 446 447 448
		if (status < 0)
			dev_dbg(&spi->dev, "setup --> %d\n", status);
	}

	return 0;

fail:
449
	for (addr = 0; addr < ARRAY_SIZE(data->mcp); addr++) {
450 451 452 453 454 455 456 457 458
		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);
459 460 461 462 463
	return status;
}

static int mcp23s08_remove(struct spi_device *spi)
{
464
	struct mcp23s08_driver_data	*data = spi_get_drvdata(spi);
465
	struct mcp23s08_platform_data	*pdata = spi->dev.platform_data;
466
	unsigned			addr;
467 468 469 470
	int				status = 0;

	if (pdata->teardown) {
		status = pdata->teardown(spi,
471
				pdata->base, data->ngpio,
472 473 474 475 476 477 478
				pdata->context);
		if (status < 0) {
			dev_err(&spi->dev, "%s --> %d\n", "teardown", status);
			return status;
		}
	}

479
	for (addr = 0; addr < ARRAY_SIZE(data->mcp); addr++) {
480 481 482 483 484 485 486 487 488 489 490
		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;
		}
	}
491
	if (status == 0)
492
		kfree(data);
493 494 495
	return status;
}

496 497 498 499 500 501 502
static const struct spi_device_id mcp23s08_ids[] = {
	{ "mcp23s08", MCP_TYPE_S08 },
	{ "mcp23s17", MCP_TYPE_S17 },
	{ },
};
MODULE_DEVICE_TABLE(spi, mcp23s08_ids);

503 504 505
static struct spi_driver mcp23s08_driver = {
	.probe		= mcp23s08_probe,
	.remove		= mcp23s08_remove,
506
	.id_table	= mcp23s08_ids,
507 508 509 510 511 512 513 514 515 516 517 518
	.driver = {
		.name	= "mcp23s08",
		.owner	= THIS_MODULE,
	},
};

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

static int __init mcp23s08_init(void)
{
	return spi_register_driver(&mcp23s08_driver);
}
519 520 521 522
/* register after spi postcore initcall and before
 * subsys initcalls that may rely on these GPIOs
 */
subsys_initcall(mcp23s08_init);
523 524 525 526 527 528 529 530

static void __exit mcp23s08_exit(void)
{
	spi_unregister_driver(&mcp23s08_driver);
}
module_exit(mcp23s08_exit);

MODULE_LICENSE("GPL");