Blame view

drivers/gpio/gpio-mcp23s08.c 15.7 KB
e58b9e276   David Brownell   mcp23s08 spi gpio...
1
  /*
752ad5e82   Peter Korsgaard   mcp23s08: add i2c...
2
   * MCP23S08 SPI/GPIO gpio expander driver
e58b9e276   David Brownell   mcp23s08 spi gpio...
3
4
5
6
   */
  
  #include <linux/kernel.h>
  #include <linux/device.h>
e58b9e276   David Brownell   mcp23s08 spi gpio...
7
  #include <linux/mutex.h>
bb207ef1e   Paul Gortmaker   drivers/gpio: Fix...
8
  #include <linux/module.h>
d120c17fa   H Hartley Sweeten   gpio: include <li...
9
  #include <linux/gpio.h>
752ad5e82   Peter Korsgaard   mcp23s08: add i2c...
10
  #include <linux/i2c.h>
e58b9e276   David Brownell   mcp23s08 spi gpio...
11
12
  #include <linux/spi/spi.h>
  #include <linux/spi/mcp23s08.h>
5a0e3ad6a   Tejun Heo   include cleanup: ...
13
  #include <linux/slab.h>
0b7bb77fd   Peter Korsgaard   gpio/mcp23s08: su...
14
  #include <asm/byteorder.h>
e58b9e276   David Brownell   mcp23s08 spi gpio...
15

0b7bb77fd   Peter Korsgaard   gpio/mcp23s08: su...
16
17
18
19
20
  /**
   * MCP types supported by driver
   */
  #define MCP_TYPE_S08	0
  #define MCP_TYPE_S17	1
752ad5e82   Peter Korsgaard   mcp23s08: add i2c...
21
22
  #define MCP_TYPE_008	2
  #define MCP_TYPE_017	3
e58b9e276   David Brownell   mcp23s08 spi gpio...
23
24
25
26
27
  
  /* 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.
e58b9e276   David Brownell   mcp23s08 spi gpio...
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
   */
  #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
0b7bb77fd   Peter Korsgaard   gpio/mcp23s08: su...
44
45
46
47
48
49
50
51
  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);
  };
e58b9e276   David Brownell   mcp23s08 spi gpio...
52
  struct mcp23s08 {
e58b9e276   David Brownell   mcp23s08 spi gpio...
53
  	u8			addr;
0b7bb77fd   Peter Korsgaard   gpio/mcp23s08: su...
54
  	u16			cache[11];
e58b9e276   David Brownell   mcp23s08 spi gpio...
55
56
  	/* lock protects the cached values */
  	struct mutex		lock;
e58b9e276   David Brownell   mcp23s08 spi gpio...
57
58
  
  	struct gpio_chip	chip;
0b7bb77fd   Peter Korsgaard   gpio/mcp23s08: su...
59
  	const struct mcp23s08_ops	*ops;
d62b98f30   Peter Korsgaard   mcp23s08: isolate...
60
  	void			*data; /* ops specific data */
e58b9e276   David Brownell   mcp23s08 spi gpio...
61
  };
0b7bb77fd   Peter Korsgaard   gpio/mcp23s08: su...
62
  /* A given spi_device can represent up to eight mcp23sxx chips
8f1cc3b10   David Brownell   gpio: mcp23s08 ha...
63
64
65
66
67
68
   * 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;
0b7bb77fd   Peter Korsgaard   gpio/mcp23s08: su...
69
  	struct mcp23s08		*mcp[8];
8f1cc3b10   David Brownell   gpio: mcp23s08 ha...
70
71
  	struct mcp23s08		chip[];
  };
752ad5e82   Peter Korsgaard   mcp23s08: add i2c...
72
73
74
75
76
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
  /*----------------------------------------------------------------------*/
  
  #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 */
  
  /*----------------------------------------------------------------------*/
d62b98f30   Peter Korsgaard   mcp23s08: isolate...
137
  #ifdef CONFIG_SPI_MASTER
e58b9e276   David Brownell   mcp23s08 spi gpio...
138
139
140
141
142
143
144
  static int mcp23s08_read(struct mcp23s08 *mcp, unsigned reg)
  {
  	u8	tx[2], rx[1];
  	int	status;
  
  	tx[0] = mcp->addr | 0x01;
  	tx[1] = reg;
d62b98f30   Peter Korsgaard   mcp23s08: isolate...
145
  	status = spi_write_then_read(mcp->data, tx, sizeof tx, rx, sizeof rx);
e58b9e276   David Brownell   mcp23s08 spi gpio...
146
147
  	return (status < 0) ? status : rx[0];
  }
0b7bb77fd   Peter Korsgaard   gpio/mcp23s08: su...
148
  static int mcp23s08_write(struct mcp23s08 *mcp, unsigned reg, unsigned val)
e58b9e276   David Brownell   mcp23s08 spi gpio...
149
150
151
152
153
154
  {
  	u8	tx[3];
  
  	tx[0] = mcp->addr;
  	tx[1] = reg;
  	tx[2] = val;
d62b98f30   Peter Korsgaard   mcp23s08: isolate...
155
  	return spi_write_then_read(mcp->data, tx, sizeof tx, NULL, 0);
e58b9e276   David Brownell   mcp23s08 spi gpio...
156
157
158
  }
  
  static int
0b7bb77fd   Peter Korsgaard   gpio/mcp23s08: su...
159
  mcp23s08_read_regs(struct mcp23s08 *mcp, unsigned reg, u16 *vals, unsigned n)
e58b9e276   David Brownell   mcp23s08 spi gpio...
160
  {
0b7bb77fd   Peter Korsgaard   gpio/mcp23s08: su...
161
162
  	u8	tx[2], *tmp;
  	int	status;
e58b9e276   David Brownell   mcp23s08 spi gpio...
163
164
165
166
167
  
  	if ((n + reg) > sizeof mcp->cache)
  		return -EINVAL;
  	tx[0] = mcp->addr | 0x01;
  	tx[1] = reg;
0b7bb77fd   Peter Korsgaard   gpio/mcp23s08: su...
168
169
  
  	tmp = (u8 *)vals;
d62b98f30   Peter Korsgaard   mcp23s08: isolate...
170
  	status = spi_write_then_read(mcp->data, tx, sizeof tx, tmp, n);
0b7bb77fd   Peter Korsgaard   gpio/mcp23s08: su...
171
172
173
174
175
176
177
178
179
180
181
182
183
184
  	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;
d62b98f30   Peter Korsgaard   mcp23s08: isolate...
185
  	status = spi_write_then_read(mcp->data, tx, sizeof tx, rx, sizeof rx);
0b7bb77fd   Peter Korsgaard   gpio/mcp23s08: su...
186
187
188
189
190
191
192
193
194
195
196
  	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;
d62b98f30   Peter Korsgaard   mcp23s08: isolate...
197
  	return spi_write_then_read(mcp->data, tx, sizeof tx, NULL, 0);
0b7bb77fd   Peter Korsgaard   gpio/mcp23s08: su...
198
199
200
201
202
203
204
205
206
207
208
209
  }
  
  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;
d62b98f30   Peter Korsgaard   mcp23s08: isolate...
210
  	status = spi_write_then_read(mcp->data, tx, sizeof tx,
0b7bb77fd   Peter Korsgaard   gpio/mcp23s08: su...
211
212
213
214
215
216
217
  				     (u8 *)vals, n * 2);
  	if (status >= 0) {
  		while (n--)
  			vals[n] = __le16_to_cpu((__le16)vals[n]);
  	}
  
  	return status;
e58b9e276   David Brownell   mcp23s08 spi gpio...
218
  }
0b7bb77fd   Peter Korsgaard   gpio/mcp23s08: su...
219
220
221
222
223
224
225
226
227
228
229
  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,
  };
d62b98f30   Peter Korsgaard   mcp23s08: isolate...
230
  #endif /* CONFIG_SPI_MASTER */
0b7bb77fd   Peter Korsgaard   gpio/mcp23s08: su...
231

e58b9e276   David Brownell   mcp23s08 spi gpio...
232
233
234
235
236
237
238
239
240
  /*----------------------------------------------------------------------*/
  
  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);
0b7bb77fd   Peter Korsgaard   gpio/mcp23s08: su...
241
  	status = mcp->ops->write(mcp, MCP_IODIR, mcp->cache[MCP_IODIR]);
e58b9e276   David Brownell   mcp23s08 spi gpio...
242
243
244
245
246
247
248
249
250
251
252
253
  	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 ... */
0b7bb77fd   Peter Korsgaard   gpio/mcp23s08: su...
254
  	status = mcp->ops->read(mcp, MCP_GPIO);
e58b9e276   David Brownell   mcp23s08 spi gpio...
255
256
257
258
259
260
261
262
263
264
265
266
  	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)
  {
0b7bb77fd   Peter Korsgaard   gpio/mcp23s08: su...
267
  	unsigned olat = mcp->cache[MCP_OLAT];
e58b9e276   David Brownell   mcp23s08 spi gpio...
268
269
270
271
272
273
  
  	if (value)
  		olat |= mask;
  	else
  		olat &= ~mask;
  	mcp->cache[MCP_OLAT] = olat;
0b7bb77fd   Peter Korsgaard   gpio/mcp23s08: su...
274
  	return mcp->ops->write(mcp, MCP_OLAT, olat);
e58b9e276   David Brownell   mcp23s08 spi gpio...
275
276
277
278
279
  }
  
  static void mcp23s08_set(struct gpio_chip *chip, unsigned offset, int value)
  {
  	struct mcp23s08	*mcp = container_of(chip, struct mcp23s08, chip);
0b7bb77fd   Peter Korsgaard   gpio/mcp23s08: su...
280
  	unsigned mask = 1 << offset;
e58b9e276   David Brownell   mcp23s08 spi gpio...
281
282
283
284
285
286
287
288
289
290
  
  	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);
0b7bb77fd   Peter Korsgaard   gpio/mcp23s08: su...
291
  	unsigned mask = 1 << offset;
e58b9e276   David Brownell   mcp23s08 spi gpio...
292
293
294
295
296
297
  	int status;
  
  	mutex_lock(&mcp->lock);
  	status = __mcp23s08_set(mcp, mask, value);
  	if (status == 0) {
  		mcp->cache[MCP_IODIR] &= ~mask;
0b7bb77fd   Peter Korsgaard   gpio/mcp23s08: su...
298
  		status = mcp->ops->write(mcp, MCP_IODIR, mcp->cache[MCP_IODIR]);
e58b9e276   David Brownell   mcp23s08 spi gpio...
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
  	}
  	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;
1d1c1d9b5   Roel Kluin   gpio: mcp23s08 de...
318
  	int		t;
e58b9e276   David Brownell   mcp23s08 spi gpio...
319
320
321
322
323
  	unsigned	mask;
  
  	mcp = container_of(chip, struct mcp23s08, chip);
  
  	/* NOTE: we only handle one bank for now ... */
0b7bb77fd   Peter Korsgaard   gpio/mcp23s08: su...
324
  	bank = '0' + ((mcp->addr >> 1) & 0x7);
e58b9e276   David Brownell   mcp23s08 spi gpio...
325
326
  
  	mutex_lock(&mcp->lock);
0b7bb77fd   Peter Korsgaard   gpio/mcp23s08: su...
327
  	t = mcp->ops->read_regs(mcp, 0, mcp->cache, ARRAY_SIZE(mcp->cache));
e58b9e276   David Brownell   mcp23s08 spi gpio...
328
329
330
331
332
  	if (t < 0) {
  		seq_printf(s, " I/O ERROR %d
  ", t);
  		goto done;
  	}
0b7bb77fd   Peter Korsgaard   gpio/mcp23s08: su...
333
  	for (t = 0, mask = 1; t < chip->ngpio; t++, mask <<= 1) {
e58b9e276   David Brownell   mcp23s08 spi gpio...
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
  		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, "
  ");
  	}
  done:
  	mutex_unlock(&mcp->lock);
  }
  
  #else
  #define mcp23s08_dbg_show	NULL
  #endif
  
  /*----------------------------------------------------------------------*/
d62b98f30   Peter Korsgaard   mcp23s08: isolate...
358
359
  static int mcp23s08_probe_one(struct mcp23s08 *mcp, struct device *dev,
  			      void *data, unsigned addr,
0b7bb77fd   Peter Korsgaard   gpio/mcp23s08: su...
360
  			      unsigned type, unsigned base, unsigned pullups)
e58b9e276   David Brownell   mcp23s08 spi gpio...
361
  {
d62b98f30   Peter Korsgaard   mcp23s08: isolate...
362
  	int status;
e58b9e276   David Brownell   mcp23s08 spi gpio...
363

e58b9e276   David Brownell   mcp23s08 spi gpio...
364
  	mutex_init(&mcp->lock);
d62b98f30   Peter Korsgaard   mcp23s08: isolate...
365
366
  	mcp->data = data;
  	mcp->addr = addr;
e58b9e276   David Brownell   mcp23s08 spi gpio...
367

e58b9e276   David Brownell   mcp23s08 spi gpio...
368
369
370
371
372
  	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;
d62b98f30   Peter Korsgaard   mcp23s08: isolate...
373
374
375
  	switch (type) {
  #ifdef CONFIG_SPI_MASTER
  	case MCP_TYPE_S08:
0b7bb77fd   Peter Korsgaard   gpio/mcp23s08: su...
376
377
378
  		mcp->ops = &mcp23s08_ops;
  		mcp->chip.ngpio = 8;
  		mcp->chip.label = "mcp23s08";
d62b98f30   Peter Korsgaard   mcp23s08: isolate...
379
380
381
382
383
384
385
386
  		break;
  
  	case MCP_TYPE_S17:
  		mcp->ops = &mcp23s17_ops;
  		mcp->chip.ngpio = 16;
  		mcp->chip.label = "mcp23s17";
  		break;
  #endif /* CONFIG_SPI_MASTER */
752ad5e82   Peter Korsgaard   mcp23s08: add i2c...
387
388
389
390
391
392
393
394
395
396
397
398
399
  #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 */
d62b98f30   Peter Korsgaard   mcp23s08: isolate...
400
401
402
403
  	default:
  		dev_err(dev, "invalid device type (%d)
  ", type);
  		return -EINVAL;
0b7bb77fd   Peter Korsgaard   gpio/mcp23s08: su...
404
  	}
d62b98f30   Peter Korsgaard   mcp23s08: isolate...
405

8f1cc3b10   David Brownell   gpio: mcp23s08 ha...
406
  	mcp->chip.base = base;
e58b9e276   David Brownell   mcp23s08 spi gpio...
407
  	mcp->chip.can_sleep = 1;
d62b98f30   Peter Korsgaard   mcp23s08: isolate...
408
  	mcp->chip.dev = dev;
d72cbed0c   Guennadi Liakhovetski   gpiolib: i2c/spi ...
409
  	mcp->chip.owner = THIS_MODULE;
e58b9e276   David Brownell   mcp23s08 spi gpio...
410

8f1cc3b10   David Brownell   gpio: mcp23s08 ha...
411
412
413
  	/* verify MCP_IOCON.SEQOP = 0, so sequential reads work,
  	 * and MCP_IOCON.HAEN = 1, so we work with all chips.
  	 */
0b7bb77fd   Peter Korsgaard   gpio/mcp23s08: su...
414
  	status = mcp->ops->read(mcp, MCP_IOCON);
e58b9e276   David Brownell   mcp23s08 spi gpio...
415
416
  	if (status < 0)
  		goto fail;
8f1cc3b10   David Brownell   gpio: mcp23s08 ha...
417
  	if ((status & IOCON_SEQOP) || !(status & IOCON_HAEN)) {
0b7bb77fd   Peter Korsgaard   gpio/mcp23s08: su...
418
419
420
421
  		/* 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);
e58b9e276   David Brownell   mcp23s08 spi gpio...
422
423
424
425
426
  		if (status < 0)
  			goto fail;
  	}
  
  	/* configure ~100K pullups */
0b7bb77fd   Peter Korsgaard   gpio/mcp23s08: su...
427
  	status = mcp->ops->write(mcp, MCP_GPPU, pullups);
e58b9e276   David Brownell   mcp23s08 spi gpio...
428
429
  	if (status < 0)
  		goto fail;
0b7bb77fd   Peter Korsgaard   gpio/mcp23s08: su...
430
  	status = mcp->ops->read_regs(mcp, 0, mcp->cache, ARRAY_SIZE(mcp->cache));
e58b9e276   David Brownell   mcp23s08 spi gpio...
431
432
433
434
435
436
  	if (status < 0)
  		goto fail;
  
  	/* disable inverter on input */
  	if (mcp->cache[MCP_IPOL] != 0) {
  		mcp->cache[MCP_IPOL] = 0;
0b7bb77fd   Peter Korsgaard   gpio/mcp23s08: su...
437
438
439
  		status = mcp->ops->write(mcp, MCP_IPOL, 0);
  		if (status < 0)
  			goto fail;
e58b9e276   David Brownell   mcp23s08 spi gpio...
440
441
442
443
444
  	}
  
  	/* disable irqs */
  	if (mcp->cache[MCP_GPINTEN] != 0) {
  		mcp->cache[MCP_GPINTEN] = 0;
0b7bb77fd   Peter Korsgaard   gpio/mcp23s08: su...
445
  		status = mcp->ops->write(mcp, MCP_GPINTEN, 0);
8f1cc3b10   David Brownell   gpio: mcp23s08 ha...
446
447
  		if (status < 0)
  			goto fail;
e58b9e276   David Brownell   mcp23s08 spi gpio...
448
449
450
  	}
  
  	status = gpiochip_add(&mcp->chip);
8f1cc3b10   David Brownell   gpio: mcp23s08 ha...
451
452
  fail:
  	if (status < 0)
d62b98f30   Peter Korsgaard   mcp23s08: isolate...
453
454
455
  		dev_dbg(dev, "can't setup chip %d, --> %d
  ",
  			addr, status);
8f1cc3b10   David Brownell   gpio: mcp23s08 ha...
456
457
  	return status;
  }
752ad5e82   Peter Korsgaard   mcp23s08: add i2c...
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
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
  /*----------------------------------------------------------------------*/
  
  #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
  ");
  		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 */
  
  /*----------------------------------------------------------------------*/
d62b98f30   Peter Korsgaard   mcp23s08: isolate...
543
  #ifdef CONFIG_SPI_MASTER
8f1cc3b10   David Brownell   gpio: mcp23s08 ha...
544
545
546
547
548
549
  static int mcp23s08_probe(struct spi_device *spi)
  {
  	struct mcp23s08_platform_data	*pdata;
  	unsigned			addr;
  	unsigned			chips = 0;
  	struct mcp23s08_driver_data	*data;
0b7bb77fd   Peter Korsgaard   gpio/mcp23s08: su...
550
  	int				status, type;
8f1cc3b10   David Brownell   gpio: mcp23s08 ha...
551
  	unsigned			base;
0b7bb77fd   Peter Korsgaard   gpio/mcp23s08: su...
552
  	type = spi_get_device_id(spi)->driver_data;
8f1cc3b10   David Brownell   gpio: mcp23s08 ha...
553
  	pdata = spi->dev.platform_data;
a342d215c   Ben Dooks   gpio: fix probe()...
554
555
556
557
558
  	if (!pdata || !gpio_is_valid(pdata->base)) {
  		dev_dbg(&spi->dev, "invalid or missing platform data
  ");
  		return -EINVAL;
  	}
8f1cc3b10   David Brownell   gpio: mcp23s08 ha...
559

0b7bb77fd   Peter Korsgaard   gpio/mcp23s08: su...
560
  	for (addr = 0; addr < ARRAY_SIZE(pdata->chip); addr++) {
8f1cc3b10   David Brownell   gpio: mcp23s08 ha...
561
562
563
  		if (!pdata->chip[addr].is_present)
  			continue;
  		chips++;
0b7bb77fd   Peter Korsgaard   gpio/mcp23s08: su...
564
565
566
567
568
569
  		if ((type == MCP_TYPE_S08) && (addr > 3)) {
  			dev_err(&spi->dev,
  				"mcp23s08 only supports address 0..3
  ");
  			return -EINVAL;
  		}
8f1cc3b10   David Brownell   gpio: mcp23s08 ha...
570
571
572
573
574
575
576
577
578
579
580
  	}
  	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;
0b7bb77fd   Peter Korsgaard   gpio/mcp23s08: su...
581
  	for (addr = 0; addr < ARRAY_SIZE(pdata->chip); addr++) {
8f1cc3b10   David Brownell   gpio: mcp23s08 ha...
582
583
584
585
  		if (!pdata->chip[addr].is_present)
  			continue;
  		chips--;
  		data->mcp[addr] = &data->chip[chips];
d62b98f30   Peter Korsgaard   mcp23s08: isolate...
586
587
  		status = mcp23s08_probe_one(data->mcp[addr], &spi->dev, spi,
  					    0x40 | (addr << 1), type, base,
0b7bb77fd   Peter Korsgaard   gpio/mcp23s08: su...
588
  					    pdata->chip[addr].pullups);
8f1cc3b10   David Brownell   gpio: mcp23s08 ha...
589
590
  		if (status < 0)
  			goto fail;
0b7bb77fd   Peter Korsgaard   gpio/mcp23s08: su...
591
592
  
  		base += (type == MCP_TYPE_S17) ? 16 : 8;
8f1cc3b10   David Brownell   gpio: mcp23s08 ha...
593
594
  	}
  	data->ngpio = base - pdata->base;
e58b9e276   David Brownell   mcp23s08 spi gpio...
595
596
597
598
599
  
  	/* NOTE:  these chips have a relatively sane IRQ framework, with
  	 * per-signal masking and level/edge triggering.  It's not yet
  	 * handled here...
  	 */
e58b9e276   David Brownell   mcp23s08 spi gpio...
600
601
602
  	return 0;
  
  fail:
0b7bb77fd   Peter Korsgaard   gpio/mcp23s08: su...
603
  	for (addr = 0; addr < ARRAY_SIZE(data->mcp); addr++) {
8f1cc3b10   David Brownell   gpio: mcp23s08 ha...
604
605
606
607
608
609
610
611
612
613
  		int tmp;
  
  		if (!data->mcp[addr])
  			continue;
  		tmp = gpiochip_remove(&data->mcp[addr]->chip);
  		if (tmp < 0)
  			dev_err(&spi->dev, "%s --> %d
  ", "remove", tmp);
  	}
  	kfree(data);
e58b9e276   David Brownell   mcp23s08 spi gpio...
614
615
616
617
618
  	return status;
  }
  
  static int mcp23s08_remove(struct spi_device *spi)
  {
8f1cc3b10   David Brownell   gpio: mcp23s08 ha...
619
  	struct mcp23s08_driver_data	*data = spi_get_drvdata(spi);
8f1cc3b10   David Brownell   gpio: mcp23s08 ha...
620
  	unsigned			addr;
e58b9e276   David Brownell   mcp23s08 spi gpio...
621
  	int				status = 0;
0b7bb77fd   Peter Korsgaard   gpio/mcp23s08: su...
622
  	for (addr = 0; addr < ARRAY_SIZE(data->mcp); addr++) {
8f1cc3b10   David Brownell   gpio: mcp23s08 ha...
623
624
625
626
627
628
629
630
631
632
633
634
  		int tmp;
  
  		if (!data->mcp[addr])
  			continue;
  
  		tmp = gpiochip_remove(&data->mcp[addr]->chip);
  		if (tmp < 0) {
  			dev_err(&spi->dev, "%s --> %d
  ", "remove", tmp);
  			status = tmp;
  		}
  	}
e58b9e276   David Brownell   mcp23s08 spi gpio...
635
  	if (status == 0)
8f1cc3b10   David Brownell   gpio: mcp23s08 ha...
636
  		kfree(data);
e58b9e276   David Brownell   mcp23s08 spi gpio...
637
638
  	return status;
  }
0b7bb77fd   Peter Korsgaard   gpio/mcp23s08: su...
639
640
641
642
643
644
  static const struct spi_device_id mcp23s08_ids[] = {
  	{ "mcp23s08", MCP_TYPE_S08 },
  	{ "mcp23s17", MCP_TYPE_S17 },
  	{ },
  };
  MODULE_DEVICE_TABLE(spi, mcp23s08_ids);
e58b9e276   David Brownell   mcp23s08 spi gpio...
645
646
647
  static struct spi_driver mcp23s08_driver = {
  	.probe		= mcp23s08_probe,
  	.remove		= mcp23s08_remove,
0b7bb77fd   Peter Korsgaard   gpio/mcp23s08: su...
648
  	.id_table	= mcp23s08_ids,
e58b9e276   David Brownell   mcp23s08 spi gpio...
649
650
651
652
653
  	.driver = {
  		.name	= "mcp23s08",
  		.owner	= THIS_MODULE,
  	},
  };
d62b98f30   Peter Korsgaard   mcp23s08: isolate...
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
  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 */
e58b9e276   David Brownell   mcp23s08 spi gpio...
670
671
672
673
  /*----------------------------------------------------------------------*/
  
  static int __init mcp23s08_init(void)
  {
752ad5e82   Peter Korsgaard   mcp23s08: add i2c...
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
  	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;
e58b9e276   David Brownell   mcp23s08 spi gpio...
690
  }
752ad5e82   Peter Korsgaard   mcp23s08: add i2c...
691
  /* register after spi/i2c postcore initcall and before
673c0c003   David Brownell   spi: core and gpi...
692
693
694
   * subsys initcalls that may rely on these GPIOs
   */
  subsys_initcall(mcp23s08_init);
e58b9e276   David Brownell   mcp23s08 spi gpio...
695
696
697
  
  static void __exit mcp23s08_exit(void)
  {
d62b98f30   Peter Korsgaard   mcp23s08: isolate...
698
  	mcp23s08_spi_exit();
752ad5e82   Peter Korsgaard   mcp23s08: add i2c...
699
  	mcp23s08_i2c_exit();
e58b9e276   David Brownell   mcp23s08 spi gpio...
700
701
702
703
  }
  module_exit(mcp23s08_exit);
  
  MODULE_LICENSE("GPL");