• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
mcp23s08_read(struct mcp23s08 * mcp,unsigned reg)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 
mcp23s08_write(struct mcp23s08 * mcp,unsigned reg,u8 val)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
mcp23s08_read_regs(struct mcp23s08 * mcp,unsigned reg,u8 * vals,unsigned n)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 
mcp23s08_direction_input(struct gpio_chip * chip,unsigned offset)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 
mcp23s08_get(struct gpio_chip * chip,unsigned offset)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 
__mcp23s08_set(struct mcp23s08 * mcp,unsigned mask,int value)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 
mcp23s08_set(struct gpio_chip * chip,unsigned offset,int value)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
mcp23s08_direction_output(struct gpio_chip * chip,unsigned offset,int value)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  */
mcp23s08_dbg_show(struct seq_file * s,struct gpio_chip * chip)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 = '0' + ((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 
mcp23s08_probe_one(struct spi_device * spi,unsigned addr,unsigned base,unsigned pullups)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 
mcp23s08_probe(struct spi_device * spi)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 
mcp23s08_remove(struct spi_device * spi)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 
mcp23s08_init(void)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 
mcp23s08_exit(void)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