Commit | Line | Data |
---|---|---|
e58b9e27 | 1 | /* |
4e47f91b LP |
2 | * MCP23S08 SPI/I2C GPIO gpio expander driver |
3 | * | |
4 | * The inputs and outputs of the mcp23s08, mcp23s17, mcp23008 and mcp23017 are | |
5 | * supported. | |
6 | * For the I2C versions of the chips (mcp23008 and mcp23017) generation of | |
7 | * interrupts is also supported. | |
8 | * The hardware of the SPI versions of the chips (mcp23s08 and mcp23s17) is | |
9 | * also capable of generating interrupts, but the linux driver does not | |
10 | * support that yet. | |
e58b9e27 DB |
11 | */ |
12 | ||
13 | #include <linux/kernel.h> | |
14 | #include <linux/device.h> | |
e58b9e27 | 15 | #include <linux/mutex.h> |
bb207ef1 | 16 | #include <linux/module.h> |
d120c17f | 17 | #include <linux/gpio.h> |
752ad5e8 | 18 | #include <linux/i2c.h> |
e58b9e27 DB |
19 | #include <linux/spi/spi.h> |
20 | #include <linux/spi/mcp23s08.h> | |
5a0e3ad6 | 21 | #include <linux/slab.h> |
0b7bb77f | 22 | #include <asm/byteorder.h> |
4e47f91b LP |
23 | #include <linux/interrupt.h> |
24 | #include <linux/of_irq.h> | |
97ddb1c8 | 25 | #include <linux/of_device.h> |
3d84fdb3 | 26 | #include <linux/regmap.h> |
82039d24 SR |
27 | #include <linux/pinctrl/pinctrl.h> |
28 | #include <linux/pinctrl/pinconf.h> | |
29 | #include <linux/pinctrl/pinconf-generic.h> | |
e58b9e27 | 30 | |
0b7bb77f PK |
31 | /** |
32 | * MCP types supported by driver | |
33 | */ | |
34 | #define MCP_TYPE_S08 0 | |
35 | #define MCP_TYPE_S17 1 | |
752ad5e8 PK |
36 | #define MCP_TYPE_008 2 |
37 | #define MCP_TYPE_017 3 | |
28c5a41e | 38 | #define MCP_TYPE_S18 4 |
e58b9e27 DB |
39 | |
40 | /* Registers are all 8 bits wide. | |
41 | * | |
42 | * The mcp23s17 has twice as many bits, and can be configured to work | |
43 | * with either 16 bit registers or with two adjacent 8 bit banks. | |
e58b9e27 DB |
44 | */ |
45 | #define MCP_IODIR 0x00 /* init/reset: all ones */ | |
46 | #define MCP_IPOL 0x01 | |
47 | #define MCP_GPINTEN 0x02 | |
48 | #define MCP_DEFVAL 0x03 | |
49 | #define MCP_INTCON 0x04 | |
50 | #define MCP_IOCON 0x05 | |
4e47f91b | 51 | # define IOCON_MIRROR (1 << 6) |
e58b9e27 DB |
52 | # define IOCON_SEQOP (1 << 5) |
53 | # define IOCON_HAEN (1 << 3) | |
54 | # define IOCON_ODR (1 << 2) | |
55 | # define IOCON_INTPOL (1 << 1) | |
3539699c | 56 | # define IOCON_INTCC (1) |
e58b9e27 DB |
57 | #define MCP_GPPU 0x06 |
58 | #define MCP_INTF 0x07 | |
59 | #define MCP_INTCAP 0x08 | |
60 | #define MCP_GPIO 0x09 | |
61 | #define MCP_OLAT 0x0a | |
62 | ||
0b7bb77f PK |
63 | struct mcp23s08; |
64 | ||
e58b9e27 | 65 | struct mcp23s08 { |
e58b9e27 | 66 | u8 addr; |
a4e63554 | 67 | bool irq_active_high; |
3d84fdb3 | 68 | bool reg_shift; |
e58b9e27 | 69 | |
0b7bb77f | 70 | u16 cache[11]; |
4e47f91b LP |
71 | u16 irq_rise; |
72 | u16 irq_fall; | |
73 | int irq; | |
74 | bool irq_controller; | |
e58b9e27 DB |
75 | /* lock protects the cached values */ |
76 | struct mutex lock; | |
4e47f91b | 77 | struct mutex irq_lock; |
e58b9e27 DB |
78 | |
79 | struct gpio_chip chip; | |
80 | ||
3d84fdb3 SR |
81 | struct regmap *regmap; |
82 | struct device *dev; | |
82039d24 SR |
83 | |
84 | struct pinctrl_dev *pctldev; | |
85 | struct pinctrl_desc pinctrl_desc; | |
8f1cc3b1 DB |
86 | }; |
87 | ||
3d84fdb3 SR |
88 | static const struct regmap_config mcp23x08_regmap = { |
89 | .reg_bits = 8, | |
90 | .val_bits = 8, | |
752ad5e8 | 91 | |
3d84fdb3 SR |
92 | .reg_stride = 1, |
93 | .max_register = MCP_OLAT, | |
752ad5e8 PK |
94 | }; |
95 | ||
3d84fdb3 SR |
96 | static const struct regmap_config mcp23x17_regmap = { |
97 | .reg_bits = 8, | |
98 | .val_bits = 16, | |
752ad5e8 | 99 | |
3d84fdb3 SR |
100 | .reg_stride = 2, |
101 | .max_register = MCP_OLAT << 1, | |
102 | .val_format_endian = REGMAP_ENDIAN_LITTLE, | |
103 | }; | |
752ad5e8 | 104 | |
82039d24 SR |
105 | static int mcp_read(struct mcp23s08 *mcp, unsigned int reg, unsigned int *val) |
106 | { | |
107 | return regmap_read(mcp->regmap, reg << mcp->reg_shift, val); | |
108 | } | |
109 | ||
110 | static int mcp_write(struct mcp23s08 *mcp, unsigned int reg, unsigned int val) | |
111 | { | |
112 | return regmap_write(mcp->regmap, reg << mcp->reg_shift, val); | |
113 | } | |
114 | ||
115 | static int mcp_set_bit(struct mcp23s08 *mcp, unsigned int reg, | |
116 | unsigned int pin, bool enabled) | |
117 | { | |
118 | u16 val = enabled ? 0xffff : 0x0000; | |
119 | u16 mask = BIT(pin); | |
120 | return regmap_update_bits(mcp->regmap, reg << mcp->reg_shift, | |
121 | mask, val); | |
122 | } | |
123 | ||
124 | static int mcp_update_cache(struct mcp23s08 *mcp) | |
125 | { | |
126 | int ret, reg, i; | |
127 | ||
128 | for (i = 0; i < ARRAY_SIZE(mcp->cache); i++) { | |
129 | ret = mcp_read(mcp, i, ®); | |
130 | if (ret < 0) | |
131 | return ret; | |
132 | mcp->cache[i] = reg; | |
133 | } | |
134 | ||
135 | return 0; | |
136 | } | |
137 | ||
138 | static const struct pinctrl_pin_desc mcp23x08_pins[] = { | |
139 | PINCTRL_PIN(0, "gpio0"), | |
140 | PINCTRL_PIN(1, "gpio1"), | |
141 | PINCTRL_PIN(2, "gpio2"), | |
142 | PINCTRL_PIN(3, "gpio3"), | |
143 | PINCTRL_PIN(4, "gpio4"), | |
144 | PINCTRL_PIN(5, "gpio5"), | |
145 | PINCTRL_PIN(6, "gpio6"), | |
146 | PINCTRL_PIN(7, "gpio7"), | |
147 | }; | |
148 | ||
149 | static const struct pinctrl_pin_desc mcp23x17_pins[] = { | |
150 | PINCTRL_PIN(0, "gpio0"), | |
151 | PINCTRL_PIN(1, "gpio1"), | |
152 | PINCTRL_PIN(2, "gpio2"), | |
153 | PINCTRL_PIN(3, "gpio3"), | |
154 | PINCTRL_PIN(4, "gpio4"), | |
155 | PINCTRL_PIN(5, "gpio5"), | |
156 | PINCTRL_PIN(6, "gpio6"), | |
157 | PINCTRL_PIN(7, "gpio7"), | |
158 | PINCTRL_PIN(8, "gpio8"), | |
159 | PINCTRL_PIN(9, "gpio9"), | |
160 | PINCTRL_PIN(10, "gpio10"), | |
161 | PINCTRL_PIN(11, "gpio11"), | |
162 | PINCTRL_PIN(12, "gpio12"), | |
163 | PINCTRL_PIN(13, "gpio13"), | |
164 | PINCTRL_PIN(14, "gpio14"), | |
165 | PINCTRL_PIN(15, "gpio15"), | |
166 | }; | |
167 | ||
168 | static int mcp_pinctrl_get_groups_count(struct pinctrl_dev *pctldev) | |
169 | { | |
170 | return 0; | |
171 | } | |
172 | ||
173 | static const char *mcp_pinctrl_get_group_name(struct pinctrl_dev *pctldev, | |
174 | unsigned int group) | |
175 | { | |
176 | return NULL; | |
177 | } | |
178 | ||
179 | static int mcp_pinctrl_get_group_pins(struct pinctrl_dev *pctldev, | |
180 | unsigned int group, | |
181 | const unsigned int **pins, | |
182 | unsigned int *num_pins) | |
183 | { | |
184 | return -ENOTSUPP; | |
185 | } | |
186 | ||
187 | static const struct pinctrl_ops mcp_pinctrl_ops = { | |
188 | .get_groups_count = mcp_pinctrl_get_groups_count, | |
189 | .get_group_name = mcp_pinctrl_get_group_name, | |
190 | .get_group_pins = mcp_pinctrl_get_group_pins, | |
191 | #ifdef CONFIG_OF | |
192 | .dt_node_to_map = pinconf_generic_dt_node_to_map_pin, | |
193 | .dt_free_map = pinconf_generic_dt_free_map, | |
194 | #endif | |
195 | }; | |
196 | ||
197 | static int mcp_pinconf_get(struct pinctrl_dev *pctldev, unsigned int pin, | |
198 | unsigned long *config) | |
199 | { | |
200 | struct mcp23s08 *mcp = pinctrl_dev_get_drvdata(pctldev); | |
201 | enum pin_config_param param = pinconf_to_config_param(*config); | |
202 | unsigned int data, status; | |
203 | int ret; | |
204 | ||
205 | switch (param) { | |
206 | case PIN_CONFIG_BIAS_PULL_UP: | |
207 | ret = mcp_read(mcp, MCP_GPPU, &data); | |
208 | if (ret < 0) | |
209 | return ret; | |
210 | status = (data & BIT(pin)) ? 1 : 0; | |
211 | break; | |
212 | default: | |
213 | dev_err(mcp->dev, "Invalid config param %04x\n", param); | |
214 | return -ENOTSUPP; | |
215 | } | |
216 | ||
217 | *config = 0; | |
218 | ||
219 | return status ? 0 : -EINVAL; | |
220 | } | |
221 | ||
222 | static int mcp_pinconf_set(struct pinctrl_dev *pctldev, unsigned int pin, | |
223 | unsigned long *configs, unsigned int num_configs) | |
224 | { | |
225 | struct mcp23s08 *mcp = pinctrl_dev_get_drvdata(pctldev); | |
226 | enum pin_config_param param; | |
227 | u32 arg, mask; | |
228 | u16 val; | |
229 | int ret = 0; | |
230 | int i; | |
231 | ||
232 | for (i = 0; i < num_configs; i++) { | |
233 | param = pinconf_to_config_param(configs[i]); | |
234 | arg = pinconf_to_config_argument(configs[i]); | |
235 | ||
236 | switch (param) { | |
237 | case PIN_CONFIG_BIAS_PULL_UP: | |
238 | val = arg ? 0xFFFF : 0x0000; | |
239 | mask = BIT(pin); | |
240 | ret = mcp_set_bit(mcp, MCP_GPPU, pin, arg); | |
241 | break; | |
242 | default: | |
243 | dev_err(mcp->dev, "Invalid config param %04x\n", param); | |
244 | return -ENOTSUPP; | |
245 | } | |
246 | } | |
247 | ||
248 | return ret; | |
249 | } | |
250 | ||
251 | static const struct pinconf_ops mcp_pinconf_ops = { | |
252 | .pin_config_get = mcp_pinconf_get, | |
253 | .pin_config_set = mcp_pinconf_set, | |
254 | .is_generic = true, | |
255 | }; | |
256 | ||
752ad5e8 PK |
257 | /*----------------------------------------------------------------------*/ |
258 | ||
d62b98f3 PK |
259 | #ifdef CONFIG_SPI_MASTER |
260 | ||
3d84fdb3 | 261 | static int mcp23sxx_spi_write(void *context, const void *data, size_t count) |
e58b9e27 | 262 | { |
3d84fdb3 SR |
263 | struct mcp23s08 *mcp = context; |
264 | struct spi_device *spi = to_spi_device(mcp->dev); | |
265 | struct spi_message m; | |
266 | struct spi_transfer t[2] = { { .tx_buf = &mcp->addr, .len = 1, }, | |
267 | { .tx_buf = data, .len = count, }, }; | |
e58b9e27 | 268 | |
3d84fdb3 SR |
269 | spi_message_init(&m); |
270 | spi_message_add_tail(&t[0], &m); | |
271 | spi_message_add_tail(&t[1], &m); | |
272 | ||
273 | return spi_sync(spi, &m); | |
e58b9e27 DB |
274 | } |
275 | ||
3d84fdb3 SR |
276 | static int mcp23sxx_spi_gather_write(void *context, |
277 | const void *reg, size_t reg_size, | |
278 | const void *val, size_t val_size) | |
e58b9e27 | 279 | { |
3d84fdb3 SR |
280 | struct mcp23s08 *mcp = context; |
281 | struct spi_device *spi = to_spi_device(mcp->dev); | |
282 | struct spi_message m; | |
283 | struct spi_transfer t[3] = { { .tx_buf = &mcp->addr, .len = 1, }, | |
284 | { .tx_buf = reg, .len = reg_size, }, | |
285 | { .tx_buf = val, .len = val_size, }, }; | |
286 | ||
287 | spi_message_init(&m); | |
288 | spi_message_add_tail(&t[0], &m); | |
289 | spi_message_add_tail(&t[1], &m); | |
290 | spi_message_add_tail(&t[2], &m); | |
291 | ||
292 | return spi_sync(spi, &m); | |
e58b9e27 DB |
293 | } |
294 | ||
3d84fdb3 SR |
295 | static int mcp23sxx_spi_read(void *context, const void *reg, size_t reg_size, |
296 | void *val, size_t val_size) | |
e58b9e27 | 297 | { |
3d84fdb3 SR |
298 | struct mcp23s08 *mcp = context; |
299 | struct spi_device *spi = to_spi_device(mcp->dev); | |
300 | u8 tx[2]; | |
e58b9e27 | 301 | |
3d84fdb3 | 302 | if (reg_size != 1) |
e58b9e27 | 303 | return -EINVAL; |
3d84fdb3 | 304 | |
e58b9e27 | 305 | tx[0] = mcp->addr | 0x01; |
3d84fdb3 | 306 | tx[1] = *((u8 *) reg); |
0b7bb77f | 307 | |
3d84fdb3 | 308 | return spi_write_then_read(spi, tx, sizeof(tx), val, val_size); |
0b7bb77f PK |
309 | } |
310 | ||
3d84fdb3 SR |
311 | static const struct regmap_bus mcp23sxx_spi_regmap = { |
312 | .write = mcp23sxx_spi_write, | |
313 | .gather_write = mcp23sxx_spi_gather_write, | |
314 | .read = mcp23sxx_spi_read, | |
315 | }; | |
0b7bb77f | 316 | |
3d84fdb3 | 317 | #endif /* CONFIG_SPI_MASTER */ |
0b7bb77f | 318 | |
3d84fdb3 | 319 | /*----------------------------------------------------------------------*/ |
0b7bb77f | 320 | |
3d84fdb3 SR |
321 | /* A given spi_device can represent up to eight mcp23sxx chips |
322 | * sharing the same chipselect but using different addresses | |
323 | * (e.g. chips #0 and #3 might be populated, but not #1 or $2). | |
324 | * Driver data holds all the per-chip data. | |
325 | */ | |
326 | struct mcp23s08_driver_data { | |
327 | unsigned ngpio; | |
328 | struct mcp23s08 *mcp[8]; | |
329 | struct mcp23s08 chip[]; | |
0b7bb77f PK |
330 | }; |
331 | ||
e58b9e27 DB |
332 | |
333 | static int mcp23s08_direction_input(struct gpio_chip *chip, unsigned offset) | |
334 | { | |
9e03cf0b | 335 | struct mcp23s08 *mcp = gpiochip_get_data(chip); |
e58b9e27 DB |
336 | int status; |
337 | ||
338 | mutex_lock(&mcp->lock); | |
339 | mcp->cache[MCP_IODIR] |= (1 << offset); | |
3d84fdb3 | 340 | status = mcp_write(mcp, MCP_IODIR, mcp->cache[MCP_IODIR]); |
e58b9e27 DB |
341 | mutex_unlock(&mcp->lock); |
342 | return status; | |
343 | } | |
344 | ||
345 | static int mcp23s08_get(struct gpio_chip *chip, unsigned offset) | |
346 | { | |
9e03cf0b | 347 | struct mcp23s08 *mcp = gpiochip_get_data(chip); |
3d84fdb3 | 348 | int status, ret; |
e58b9e27 DB |
349 | |
350 | mutex_lock(&mcp->lock); | |
351 | ||
352 | /* REVISIT reading this clears any IRQ ... */ | |
3d84fdb3 SR |
353 | ret = mcp_read(mcp, MCP_GPIO, &status); |
354 | if (ret < 0) | |
e58b9e27 DB |
355 | status = 0; |
356 | else { | |
357 | mcp->cache[MCP_GPIO] = status; | |
358 | status = !!(status & (1 << offset)); | |
359 | } | |
360 | mutex_unlock(&mcp->lock); | |
361 | return status; | |
362 | } | |
363 | ||
364 | static int __mcp23s08_set(struct mcp23s08 *mcp, unsigned mask, int value) | |
365 | { | |
0b7bb77f | 366 | unsigned olat = mcp->cache[MCP_OLAT]; |
e58b9e27 DB |
367 | |
368 | if (value) | |
369 | olat |= mask; | |
370 | else | |
371 | olat &= ~mask; | |
372 | mcp->cache[MCP_OLAT] = olat; | |
3d84fdb3 | 373 | return mcp_write(mcp, MCP_OLAT, olat); |
e58b9e27 DB |
374 | } |
375 | ||
376 | static void mcp23s08_set(struct gpio_chip *chip, unsigned offset, int value) | |
377 | { | |
9e03cf0b | 378 | struct mcp23s08 *mcp = gpiochip_get_data(chip); |
0b7bb77f | 379 | unsigned mask = 1 << offset; |
e58b9e27 DB |
380 | |
381 | mutex_lock(&mcp->lock); | |
382 | __mcp23s08_set(mcp, mask, value); | |
383 | mutex_unlock(&mcp->lock); | |
384 | } | |
385 | ||
386 | static int | |
387 | mcp23s08_direction_output(struct gpio_chip *chip, unsigned offset, int value) | |
388 | { | |
9e03cf0b | 389 | struct mcp23s08 *mcp = gpiochip_get_data(chip); |
0b7bb77f | 390 | unsigned mask = 1 << offset; |
e58b9e27 DB |
391 | int status; |
392 | ||
393 | mutex_lock(&mcp->lock); | |
394 | status = __mcp23s08_set(mcp, mask, value); | |
395 | if (status == 0) { | |
396 | mcp->cache[MCP_IODIR] &= ~mask; | |
3d84fdb3 | 397 | status = mcp_write(mcp, MCP_IODIR, mcp->cache[MCP_IODIR]); |
e58b9e27 DB |
398 | } |
399 | mutex_unlock(&mcp->lock); | |
400 | return status; | |
401 | } | |
402 | ||
4e47f91b LP |
403 | /*----------------------------------------------------------------------*/ |
404 | static irqreturn_t mcp23s08_irq(int irq, void *data) | |
405 | { | |
406 | struct mcp23s08 *mcp = data; | |
2cd29f23 | 407 | int intcap, intf, i, gpio, gpio_orig, intcap_mask; |
4e47f91b | 408 | unsigned int child_irq; |
2cd29f23 RM |
409 | bool intf_set, intcap_changed, gpio_bit_changed, |
410 | defval_changed, gpio_set; | |
4e47f91b LP |
411 | |
412 | mutex_lock(&mcp->lock); | |
3d84fdb3 | 413 | if (mcp_read(mcp, MCP_INTF, &intf) < 0) { |
4e47f91b LP |
414 | mutex_unlock(&mcp->lock); |
415 | return IRQ_HANDLED; | |
416 | } | |
417 | ||
418 | mcp->cache[MCP_INTF] = intf; | |
419 | ||
3d84fdb3 | 420 | if (mcp_read(mcp, MCP_INTCAP, &intcap) < 0) { |
4e47f91b LP |
421 | mutex_unlock(&mcp->lock); |
422 | return IRQ_HANDLED; | |
423 | } | |
424 | ||
425 | mcp->cache[MCP_INTCAP] = intcap; | |
2cd29f23 RM |
426 | |
427 | /* This clears the interrupt(configurable on S18) */ | |
428 | if (mcp_read(mcp, MCP_GPIO, &gpio) < 0) { | |
429 | mutex_unlock(&mcp->lock); | |
430 | return IRQ_HANDLED; | |
431 | } | |
432 | gpio_orig = mcp->cache[MCP_GPIO]; | |
433 | mcp->cache[MCP_GPIO] = gpio; | |
4e47f91b LP |
434 | mutex_unlock(&mcp->lock); |
435 | ||
2cd29f23 RM |
436 | if (mcp->cache[MCP_INTF] == 0) { |
437 | /* There is no interrupt pending */ | |
438 | return IRQ_HANDLED; | |
439 | } | |
440 | ||
441 | dev_dbg(mcp->chip.parent, | |
442 | "intcap 0x%04X intf 0x%04X gpio_orig 0x%04X gpio 0x%04X\n", | |
443 | intcap, intf, gpio_orig, gpio); | |
4e47f91b LP |
444 | |
445 | for (i = 0; i < mcp->chip.ngpio; i++) { | |
2cd29f23 RM |
446 | /* We must check all of the inputs on the chip, |
447 | * otherwise we may not notice a change on >=2 pins. | |
448 | * | |
449 | * On at least the mcp23s17, INTCAP is only updated | |
450 | * one byte at a time(INTCAPA and INTCAPB are | |
451 | * not written to at the same time - only on a per-bank | |
452 | * basis). | |
453 | * | |
454 | * INTF only contains the single bit that caused the | |
455 | * interrupt per-bank. On the mcp23s17, there is | |
456 | * INTFA and INTFB. If two pins are changed on the A | |
457 | * side at the same time, INTF will only have one bit | |
458 | * set. If one pin on the A side and one pin on the B | |
459 | * side are changed at the same time, INTF will have | |
460 | * two bits set. Thus, INTF can't be the only check | |
461 | * to see if the input has changed. | |
462 | */ | |
463 | ||
464 | intf_set = BIT(i) & mcp->cache[MCP_INTF]; | |
465 | if (i < 8 && intf_set) | |
466 | intcap_mask = 0x00FF; | |
467 | else if (i >= 8 && intf_set) | |
468 | intcap_mask = 0xFF00; | |
469 | else | |
470 | intcap_mask = 0x00; | |
471 | ||
472 | intcap_changed = (intcap_mask & | |
473 | (BIT(i) & mcp->cache[MCP_INTCAP])) != | |
474 | (intcap_mask & (BIT(i) & gpio_orig)); | |
475 | gpio_set = BIT(i) & mcp->cache[MCP_GPIO]; | |
476 | gpio_bit_changed = (BIT(i) & gpio_orig) != | |
477 | (BIT(i) & mcp->cache[MCP_GPIO]); | |
478 | defval_changed = (BIT(i) & mcp->cache[MCP_INTCON]) && | |
479 | ((BIT(i) & mcp->cache[MCP_GPIO]) != | |
480 | (BIT(i) & mcp->cache[MCP_DEFVAL])); | |
481 | ||
482 | if (((gpio_bit_changed || intcap_changed) && | |
483 | (BIT(i) & mcp->irq_rise) && gpio_set) || | |
484 | ((gpio_bit_changed || intcap_changed) && | |
485 | (BIT(i) & mcp->irq_fall) && !gpio_set) || | |
486 | defval_changed) { | |
dad3d272 | 487 | child_irq = irq_find_mapping(mcp->chip.irqdomain, i); |
4e47f91b LP |
488 | handle_nested_irq(child_irq); |
489 | } | |
490 | } | |
491 | ||
492 | return IRQ_HANDLED; | |
493 | } | |
494 | ||
4e47f91b LP |
495 | static void mcp23s08_irq_mask(struct irq_data *data) |
496 | { | |
dad3d272 PR |
497 | struct gpio_chip *gc = irq_data_get_irq_chip_data(data); |
498 | struct mcp23s08 *mcp = gpiochip_get_data(gc); | |
4e47f91b LP |
499 | unsigned int pos = data->hwirq; |
500 | ||
501 | mcp->cache[MCP_GPINTEN] &= ~BIT(pos); | |
502 | } | |
503 | ||
504 | static void mcp23s08_irq_unmask(struct irq_data *data) | |
505 | { | |
dad3d272 PR |
506 | struct gpio_chip *gc = irq_data_get_irq_chip_data(data); |
507 | struct mcp23s08 *mcp = gpiochip_get_data(gc); | |
4e47f91b LP |
508 | unsigned int pos = data->hwirq; |
509 | ||
510 | mcp->cache[MCP_GPINTEN] |= BIT(pos); | |
511 | } | |
512 | ||
513 | static int mcp23s08_irq_set_type(struct irq_data *data, unsigned int type) | |
514 | { | |
dad3d272 PR |
515 | struct gpio_chip *gc = irq_data_get_irq_chip_data(data); |
516 | struct mcp23s08 *mcp = gpiochip_get_data(gc); | |
4e47f91b LP |
517 | unsigned int pos = data->hwirq; |
518 | int status = 0; | |
519 | ||
520 | if ((type & IRQ_TYPE_EDGE_BOTH) == IRQ_TYPE_EDGE_BOTH) { | |
521 | mcp->cache[MCP_INTCON] &= ~BIT(pos); | |
522 | mcp->irq_rise |= BIT(pos); | |
523 | mcp->irq_fall |= BIT(pos); | |
524 | } else if (type & IRQ_TYPE_EDGE_RISING) { | |
525 | mcp->cache[MCP_INTCON] &= ~BIT(pos); | |
526 | mcp->irq_rise |= BIT(pos); | |
527 | mcp->irq_fall &= ~BIT(pos); | |
528 | } else if (type & IRQ_TYPE_EDGE_FALLING) { | |
529 | mcp->cache[MCP_INTCON] &= ~BIT(pos); | |
530 | mcp->irq_rise &= ~BIT(pos); | |
531 | mcp->irq_fall |= BIT(pos); | |
16fe1ad2 AS |
532 | } else if (type & IRQ_TYPE_LEVEL_HIGH) { |
533 | mcp->cache[MCP_INTCON] |= BIT(pos); | |
534 | mcp->cache[MCP_DEFVAL] &= ~BIT(pos); | |
535 | } else if (type & IRQ_TYPE_LEVEL_LOW) { | |
536 | mcp->cache[MCP_INTCON] |= BIT(pos); | |
537 | mcp->cache[MCP_DEFVAL] |= BIT(pos); | |
4e47f91b LP |
538 | } else |
539 | return -EINVAL; | |
540 | ||
541 | return status; | |
542 | } | |
543 | ||
544 | static void mcp23s08_irq_bus_lock(struct irq_data *data) | |
545 | { | |
dad3d272 PR |
546 | struct gpio_chip *gc = irq_data_get_irq_chip_data(data); |
547 | struct mcp23s08 *mcp = gpiochip_get_data(gc); | |
4e47f91b LP |
548 | |
549 | mutex_lock(&mcp->irq_lock); | |
550 | } | |
551 | ||
552 | static void mcp23s08_irq_bus_unlock(struct irq_data *data) | |
553 | { | |
dad3d272 PR |
554 | struct gpio_chip *gc = irq_data_get_irq_chip_data(data); |
555 | struct mcp23s08 *mcp = gpiochip_get_data(gc); | |
4e47f91b LP |
556 | |
557 | mutex_lock(&mcp->lock); | |
3d84fdb3 SR |
558 | mcp_write(mcp, MCP_GPINTEN, mcp->cache[MCP_GPINTEN]); |
559 | mcp_write(mcp, MCP_DEFVAL, mcp->cache[MCP_DEFVAL]); | |
560 | mcp_write(mcp, MCP_INTCON, mcp->cache[MCP_INTCON]); | |
4e47f91b LP |
561 | mutex_unlock(&mcp->lock); |
562 | mutex_unlock(&mcp->irq_lock); | |
563 | } | |
564 | ||
4e47f91b LP |
565 | static struct irq_chip mcp23s08_irq_chip = { |
566 | .name = "gpio-mcp23xxx", | |
567 | .irq_mask = mcp23s08_irq_mask, | |
568 | .irq_unmask = mcp23s08_irq_unmask, | |
569 | .irq_set_type = mcp23s08_irq_set_type, | |
570 | .irq_bus_lock = mcp23s08_irq_bus_lock, | |
571 | .irq_bus_sync_unlock = mcp23s08_irq_bus_unlock, | |
4e47f91b LP |
572 | }; |
573 | ||
574 | static int mcp23s08_irq_setup(struct mcp23s08 *mcp) | |
575 | { | |
576 | struct gpio_chip *chip = &mcp->chip; | |
dad3d272 | 577 | int err; |
a4e63554 | 578 | unsigned long irqflags = IRQF_ONESHOT | IRQF_SHARED; |
4e47f91b LP |
579 | |
580 | mutex_init(&mcp->irq_lock); | |
581 | ||
a4e63554 AS |
582 | if (mcp->irq_active_high) |
583 | irqflags |= IRQF_TRIGGER_HIGH; | |
584 | else | |
585 | irqflags |= IRQF_TRIGGER_LOW; | |
586 | ||
58383c78 LW |
587 | err = devm_request_threaded_irq(chip->parent, mcp->irq, NULL, |
588 | mcp23s08_irq, | |
589 | irqflags, dev_name(chip->parent), mcp); | |
4e47f91b | 590 | if (err != 0) { |
58383c78 | 591 | dev_err(chip->parent, "unable to request IRQ#%d: %d\n", |
4e47f91b LP |
592 | mcp->irq, err); |
593 | return err; | |
594 | } | |
595 | ||
d245b3f9 LW |
596 | err = gpiochip_irqchip_add_nested(chip, |
597 | &mcp23s08_irq_chip, | |
598 | 0, | |
599 | handle_simple_irq, | |
600 | IRQ_TYPE_NONE); | |
dad3d272 PR |
601 | if (err) { |
602 | dev_err(chip->parent, | |
603 | "could not connect irqchip to gpiochip: %d\n", err); | |
604 | return err; | |
4e47f91b | 605 | } |
4e47f91b | 606 | |
d245b3f9 LW |
607 | gpiochip_set_nested_irqchip(chip, |
608 | &mcp23s08_irq_chip, | |
609 | mcp->irq); | |
4e47f91b | 610 | |
dad3d272 | 611 | return 0; |
4e47f91b LP |
612 | } |
613 | ||
e58b9e27 DB |
614 | /*----------------------------------------------------------------------*/ |
615 | ||
616 | #ifdef CONFIG_DEBUG_FS | |
617 | ||
618 | #include <linux/seq_file.h> | |
619 | ||
620 | /* | |
621 | * This shows more info than the generic gpio dump code: | |
622 | * pullups, deglitching, open drain drive. | |
623 | */ | |
624 | static void mcp23s08_dbg_show(struct seq_file *s, struct gpio_chip *chip) | |
625 | { | |
626 | struct mcp23s08 *mcp; | |
627 | char bank; | |
1d1c1d9b | 628 | int t; |
e58b9e27 DB |
629 | unsigned mask; |
630 | ||
9e03cf0b | 631 | mcp = gpiochip_get_data(chip); |
e58b9e27 DB |
632 | |
633 | /* NOTE: we only handle one bank for now ... */ | |
0b7bb77f | 634 | bank = '0' + ((mcp->addr >> 1) & 0x7); |
e58b9e27 DB |
635 | |
636 | mutex_lock(&mcp->lock); | |
3d84fdb3 | 637 | t = mcp_update_cache(mcp); |
e58b9e27 DB |
638 | if (t < 0) { |
639 | seq_printf(s, " I/O ERROR %d\n", t); | |
640 | goto done; | |
641 | } | |
642 | ||
0b7bb77f | 643 | for (t = 0, mask = 1; t < chip->ngpio; t++, mask <<= 1) { |
e58b9e27 DB |
644 | const char *label; |
645 | ||
646 | label = gpiochip_is_requested(chip, t); | |
647 | if (!label) | |
648 | continue; | |
649 | ||
650 | seq_printf(s, " gpio-%-3d P%c.%d (%-12s) %s %s %s", | |
651 | chip->base + t, bank, t, label, | |
652 | (mcp->cache[MCP_IODIR] & mask) ? "in " : "out", | |
653 | (mcp->cache[MCP_GPIO] & mask) ? "hi" : "lo", | |
eb1567f7 | 654 | (mcp->cache[MCP_GPPU] & mask) ? "up" : " "); |
e58b9e27 | 655 | /* NOTE: ignoring the irq-related registers */ |
33bc8411 | 656 | seq_puts(s, "\n"); |
e58b9e27 DB |
657 | } |
658 | done: | |
659 | mutex_unlock(&mcp->lock); | |
660 | } | |
661 | ||
662 | #else | |
663 | #define mcp23s08_dbg_show NULL | |
664 | #endif | |
665 | ||
666 | /*----------------------------------------------------------------------*/ | |
667 | ||
d62b98f3 | 668 | static int mcp23s08_probe_one(struct mcp23s08 *mcp, struct device *dev, |
4e47f91b | 669 | void *data, unsigned addr, unsigned type, |
3af0dbd5 | 670 | struct mcp23s08_platform_data *pdata, int cs) |
e58b9e27 | 671 | { |
3d84fdb3 | 672 | int status, ret; |
4e47f91b | 673 | bool mirror = false; |
e58b9e27 | 674 | |
e58b9e27 DB |
675 | mutex_init(&mcp->lock); |
676 | ||
3d84fdb3 | 677 | mcp->dev = dev; |
d62b98f3 | 678 | mcp->addr = addr; |
a4e63554 | 679 | mcp->irq_active_high = false; |
e58b9e27 | 680 | |
e58b9e27 DB |
681 | mcp->chip.direction_input = mcp23s08_direction_input; |
682 | mcp->chip.get = mcp23s08_get; | |
683 | mcp->chip.direction_output = mcp23s08_direction_output; | |
684 | mcp->chip.set = mcp23s08_set; | |
685 | mcp->chip.dbg_show = mcp23s08_dbg_show; | |
60f749f8 | 686 | #ifdef CONFIG_OF_GPIO |
97ddb1c8 LP |
687 | mcp->chip.of_gpio_n_cells = 2; |
688 | mcp->chip.of_node = dev->of_node; | |
689 | #endif | |
e58b9e27 | 690 | |
d62b98f3 PK |
691 | switch (type) { |
692 | #ifdef CONFIG_SPI_MASTER | |
693 | case MCP_TYPE_S08: | |
3d84fdb3 SR |
694 | mcp->regmap = devm_regmap_init(dev, &mcp23sxx_spi_regmap, mcp, |
695 | &mcp23x08_regmap); | |
696 | mcp->reg_shift = 0; | |
0b7bb77f PK |
697 | mcp->chip.ngpio = 8; |
698 | mcp->chip.label = "mcp23s08"; | |
d62b98f3 PK |
699 | break; |
700 | ||
701 | case MCP_TYPE_S17: | |
3d84fdb3 SR |
702 | mcp->regmap = devm_regmap_init(dev, &mcp23sxx_spi_regmap, mcp, |
703 | &mcp23x17_regmap); | |
704 | mcp->reg_shift = 1; | |
d62b98f3 PK |
705 | mcp->chip.ngpio = 16; |
706 | mcp->chip.label = "mcp23s17"; | |
707 | break; | |
28c5a41e PR |
708 | |
709 | case MCP_TYPE_S18: | |
3d84fdb3 SR |
710 | mcp->regmap = devm_regmap_init(dev, &mcp23sxx_spi_regmap, mcp, |
711 | &mcp23x17_regmap); | |
712 | mcp->reg_shift = 1; | |
28c5a41e PR |
713 | mcp->chip.ngpio = 16; |
714 | mcp->chip.label = "mcp23s18"; | |
715 | break; | |
d62b98f3 PK |
716 | #endif /* CONFIG_SPI_MASTER */ |
717 | ||
cbf24fad | 718 | #if IS_ENABLED(CONFIG_I2C) |
752ad5e8 | 719 | case MCP_TYPE_008: |
3d84fdb3 SR |
720 | mcp->regmap = devm_regmap_init_i2c(data, &mcp23x08_regmap); |
721 | mcp->reg_shift = 0; | |
752ad5e8 PK |
722 | mcp->chip.ngpio = 8; |
723 | mcp->chip.label = "mcp23008"; | |
724 | break; | |
725 | ||
726 | case MCP_TYPE_017: | |
3d84fdb3 SR |
727 | mcp->regmap = devm_regmap_init_i2c(data, &mcp23x17_regmap); |
728 | mcp->reg_shift = 1; | |
752ad5e8 PK |
729 | mcp->chip.ngpio = 16; |
730 | mcp->chip.label = "mcp23017"; | |
731 | break; | |
732 | #endif /* CONFIG_I2C */ | |
733 | ||
d62b98f3 PK |
734 | default: |
735 | dev_err(dev, "invalid device type (%d)\n", type); | |
736 | return -EINVAL; | |
0b7bb77f | 737 | } |
d62b98f3 | 738 | |
3d84fdb3 SR |
739 | if (IS_ERR(mcp->regmap)) |
740 | return PTR_ERR(mcp->regmap); | |
741 | ||
3af0dbd5 | 742 | mcp->chip.base = pdata->base; |
9fb1f39e | 743 | mcp->chip.can_sleep = true; |
58383c78 | 744 | mcp->chip.parent = dev; |
d72cbed0 | 745 | mcp->chip.owner = THIS_MODULE; |
e58b9e27 | 746 | |
8f1cc3b1 DB |
747 | /* verify MCP_IOCON.SEQOP = 0, so sequential reads work, |
748 | * and MCP_IOCON.HAEN = 1, so we work with all chips. | |
749 | */ | |
4e47f91b | 750 | |
3d84fdb3 SR |
751 | ret = mcp_read(mcp, MCP_IOCON, &status); |
752 | if (ret < 0) | |
e58b9e27 | 753 | goto fail; |
4e47f91b | 754 | |
3af0dbd5 | 755 | mcp->irq_controller = pdata->irq_controller; |
a4e63554 | 756 | if (mcp->irq && mcp->irq_controller) { |
170680ab | 757 | mcp->irq_active_high = |
58383c78 | 758 | of_property_read_bool(mcp->chip.parent->of_node, |
170680ab | 759 | "microchip,irq-active-high"); |
4e47f91b | 760 | |
28c5a41e | 761 | mirror = pdata->mirror; |
a4e63554 AS |
762 | } |
763 | ||
764 | if ((status & IOCON_SEQOP) || !(status & IOCON_HAEN) || mirror || | |
765 | mcp->irq_active_high) { | |
0b7bb77f PK |
766 | /* mcp23s17 has IOCON twice, make sure they are in sync */ |
767 | status &= ~(IOCON_SEQOP | (IOCON_SEQOP << 8)); | |
768 | status |= IOCON_HAEN | (IOCON_HAEN << 8); | |
a4e63554 AS |
769 | if (mcp->irq_active_high) |
770 | status |= IOCON_INTPOL | (IOCON_INTPOL << 8); | |
771 | else | |
772 | status &= ~(IOCON_INTPOL | (IOCON_INTPOL << 8)); | |
773 | ||
4e47f91b LP |
774 | if (mirror) |
775 | status |= IOCON_MIRROR | (IOCON_MIRROR << 8); | |
776 | ||
3539699c PR |
777 | if (type == MCP_TYPE_S18) |
778 | status |= IOCON_INTCC | (IOCON_INTCC << 8); | |
779 | ||
3d84fdb3 SR |
780 | ret = mcp_write(mcp, MCP_IOCON, status); |
781 | if (ret < 0) | |
e58b9e27 DB |
782 | goto fail; |
783 | } | |
784 | ||
3d84fdb3 SR |
785 | ret = mcp_update_cache(mcp); |
786 | if (ret < 0) | |
e58b9e27 DB |
787 | goto fail; |
788 | ||
789 | /* disable inverter on input */ | |
790 | if (mcp->cache[MCP_IPOL] != 0) { | |
791 | mcp->cache[MCP_IPOL] = 0; | |
3d84fdb3 SR |
792 | ret = mcp_write(mcp, MCP_IPOL, 0); |
793 | if (ret < 0) | |
0b7bb77f | 794 | goto fail; |
e58b9e27 DB |
795 | } |
796 | ||
797 | /* disable irqs */ | |
798 | if (mcp->cache[MCP_GPINTEN] != 0) { | |
799 | mcp->cache[MCP_GPINTEN] = 0; | |
3d84fdb3 SR |
800 | ret = mcp_write(mcp, MCP_GPINTEN, 0); |
801 | if (ret < 0) | |
8f1cc3b1 | 802 | goto fail; |
e58b9e27 DB |
803 | } |
804 | ||
3d84fdb3 SR |
805 | ret = gpiochip_add_data(&mcp->chip, mcp); |
806 | if (ret < 0) | |
4e47f91b LP |
807 | goto fail; |
808 | ||
809 | if (mcp->irq && mcp->irq_controller) { | |
3d84fdb3 SR |
810 | ret = mcp23s08_irq_setup(mcp); |
811 | if (ret) | |
4e47f91b | 812 | goto fail; |
4e47f91b | 813 | } |
82039d24 SR |
814 | |
815 | mcp->pinctrl_desc.name = "mcp23xxx-pinctrl"; | |
816 | mcp->pinctrl_desc.pctlops = &mcp_pinctrl_ops; | |
817 | mcp->pinctrl_desc.confops = &mcp_pinconf_ops; | |
818 | mcp->pinctrl_desc.npins = mcp->chip.ngpio; | |
819 | if (mcp->pinctrl_desc.npins == 8) | |
820 | mcp->pinctrl_desc.pins = mcp23x08_pins; | |
821 | else if (mcp->pinctrl_desc.npins == 16) | |
822 | mcp->pinctrl_desc.pins = mcp23x17_pins; | |
823 | mcp->pinctrl_desc.owner = THIS_MODULE; | |
824 | ||
825 | mcp->pctldev = devm_pinctrl_register(dev, &mcp->pinctrl_desc, mcp); | |
826 | if (IS_ERR(mcp->pctldev)) { | |
827 | ret = PTR_ERR(mcp->pctldev); | |
828 | goto fail; | |
829 | } | |
830 | ||
8f1cc3b1 | 831 | fail: |
3d84fdb3 SR |
832 | if (ret < 0) |
833 | dev_dbg(dev, "can't setup chip %d, --> %d\n", addr, ret); | |
834 | return ret; | |
8f1cc3b1 DB |
835 | } |
836 | ||
752ad5e8 PK |
837 | /*----------------------------------------------------------------------*/ |
838 | ||
97ddb1c8 LP |
839 | #ifdef CONFIG_OF |
840 | #ifdef CONFIG_SPI_MASTER | |
ac791804 | 841 | static const struct of_device_id mcp23s08_spi_of_match[] = { |
97ddb1c8 | 842 | { |
45971686 LP |
843 | .compatible = "microchip,mcp23s08", |
844 | .data = (void *) MCP_TYPE_S08, | |
97ddb1c8 LP |
845 | }, |
846 | { | |
45971686 LP |
847 | .compatible = "microchip,mcp23s17", |
848 | .data = (void *) MCP_TYPE_S17, | |
849 | }, | |
28c5a41e PR |
850 | { |
851 | .compatible = "microchip,mcp23s18", | |
852 | .data = (void *) MCP_TYPE_S18, | |
853 | }, | |
45971686 LP |
854 | /* NOTE: The use of the mcp prefix is deprecated and will be removed. */ |
855 | { | |
856 | .compatible = "mcp,mcp23s08", | |
857 | .data = (void *) MCP_TYPE_S08, | |
858 | }, | |
859 | { | |
860 | .compatible = "mcp,mcp23s17", | |
861 | .data = (void *) MCP_TYPE_S17, | |
97ddb1c8 LP |
862 | }, |
863 | { }, | |
864 | }; | |
865 | MODULE_DEVICE_TABLE(of, mcp23s08_spi_of_match); | |
866 | #endif | |
867 | ||
868 | #if IS_ENABLED(CONFIG_I2C) | |
ac791804 | 869 | static const struct of_device_id mcp23s08_i2c_of_match[] = { |
97ddb1c8 | 870 | { |
45971686 LP |
871 | .compatible = "microchip,mcp23008", |
872 | .data = (void *) MCP_TYPE_008, | |
97ddb1c8 LP |
873 | }, |
874 | { | |
45971686 LP |
875 | .compatible = "microchip,mcp23017", |
876 | .data = (void *) MCP_TYPE_017, | |
877 | }, | |
878 | /* NOTE: The use of the mcp prefix is deprecated and will be removed. */ | |
879 | { | |
880 | .compatible = "mcp,mcp23008", | |
881 | .data = (void *) MCP_TYPE_008, | |
882 | }, | |
883 | { | |
884 | .compatible = "mcp,mcp23017", | |
885 | .data = (void *) MCP_TYPE_017, | |
97ddb1c8 LP |
886 | }, |
887 | { }, | |
888 | }; | |
889 | MODULE_DEVICE_TABLE(of, mcp23s08_i2c_of_match); | |
890 | #endif | |
891 | #endif /* CONFIG_OF */ | |
892 | ||
893 | ||
cbf24fad | 894 | #if IS_ENABLED(CONFIG_I2C) |
752ad5e8 | 895 | |
3836309d | 896 | static int mcp230xx_probe(struct i2c_client *client, |
752ad5e8 PK |
897 | const struct i2c_device_id *id) |
898 | { | |
3af0dbd5 | 899 | struct mcp23s08_platform_data *pdata, local_pdata; |
752ad5e8 | 900 | struct mcp23s08 *mcp; |
3af0dbd5 | 901 | int status; |
97ddb1c8 LP |
902 | const struct of_device_id *match; |
903 | ||
904 | match = of_match_device(of_match_ptr(mcp23s08_i2c_of_match), | |
905 | &client->dev); | |
3af0dbd5 SZ |
906 | if (match) { |
907 | pdata = &local_pdata; | |
908 | pdata->base = -1; | |
3af0dbd5 SZ |
909 | pdata->irq_controller = of_property_read_bool( |
910 | client->dev.of_node, | |
911 | "interrupt-controller"); | |
912 | pdata->mirror = of_property_read_bool(client->dev.of_node, | |
913 | "microchip,irq-mirror"); | |
4e47f91b | 914 | client->irq = irq_of_parse_and_map(client->dev.of_node, 0); |
97ddb1c8 | 915 | } else { |
3af0dbd5 | 916 | pdata = dev_get_platdata(&client->dev); |
b184c388 SZ |
917 | if (!pdata) { |
918 | pdata = devm_kzalloc(&client->dev, | |
919 | sizeof(struct mcp23s08_platform_data), | |
920 | GFP_KERNEL); | |
aaf2b3af IY |
921 | if (!pdata) |
922 | return -ENOMEM; | |
b184c388 | 923 | pdata->base = -1; |
97ddb1c8 | 924 | } |
752ad5e8 PK |
925 | } |
926 | ||
33bc8411 | 927 | mcp = kzalloc(sizeof(*mcp), GFP_KERNEL); |
752ad5e8 PK |
928 | if (!mcp) |
929 | return -ENOMEM; | |
930 | ||
4e47f91b | 931 | mcp->irq = client->irq; |
752ad5e8 | 932 | status = mcp23s08_probe_one(mcp, &client->dev, client, client->addr, |
3af0dbd5 | 933 | id->driver_data, pdata, 0); |
752ad5e8 PK |
934 | if (status) |
935 | goto fail; | |
936 | ||
937 | i2c_set_clientdata(client, mcp); | |
938 | ||
939 | return 0; | |
940 | ||
941 | fail: | |
942 | kfree(mcp); | |
943 | ||
944 | return status; | |
945 | } | |
946 | ||
206210ce | 947 | static int mcp230xx_remove(struct i2c_client *client) |
752ad5e8 PK |
948 | { |
949 | struct mcp23s08 *mcp = i2c_get_clientdata(client); | |
752ad5e8 | 950 | |
9f5132ae | 951 | gpiochip_remove(&mcp->chip); |
952 | kfree(mcp); | |
752ad5e8 | 953 | |
9f5132ae | 954 | return 0; |
752ad5e8 PK |
955 | } |
956 | ||
957 | static const struct i2c_device_id mcp230xx_id[] = { | |
958 | { "mcp23008", MCP_TYPE_008 }, | |
959 | { "mcp23017", MCP_TYPE_017 }, | |
960 | { }, | |
961 | }; | |
962 | MODULE_DEVICE_TABLE(i2c, mcp230xx_id); | |
963 | ||
964 | static struct i2c_driver mcp230xx_driver = { | |
965 | .driver = { | |
966 | .name = "mcp230xx", | |
97ddb1c8 | 967 | .of_match_table = of_match_ptr(mcp23s08_i2c_of_match), |
752ad5e8 PK |
968 | }, |
969 | .probe = mcp230xx_probe, | |
8283c4ff | 970 | .remove = mcp230xx_remove, |
752ad5e8 PK |
971 | .id_table = mcp230xx_id, |
972 | }; | |
973 | ||
974 | static int __init mcp23s08_i2c_init(void) | |
975 | { | |
976 | return i2c_add_driver(&mcp230xx_driver); | |
977 | } | |
978 | ||
979 | static void mcp23s08_i2c_exit(void) | |
980 | { | |
981 | i2c_del_driver(&mcp230xx_driver); | |
982 | } | |
983 | ||
984 | #else | |
985 | ||
986 | static int __init mcp23s08_i2c_init(void) { return 0; } | |
987 | static void mcp23s08_i2c_exit(void) { } | |
988 | ||
989 | #endif /* CONFIG_I2C */ | |
990 | ||
991 | /*----------------------------------------------------------------------*/ | |
992 | ||
d62b98f3 PK |
993 | #ifdef CONFIG_SPI_MASTER |
994 | ||
8f1cc3b1 DB |
995 | static int mcp23s08_probe(struct spi_device *spi) |
996 | { | |
3af0dbd5 | 997 | struct mcp23s08_platform_data *pdata, local_pdata; |
8f1cc3b1 | 998 | unsigned addr; |
596a1c5f | 999 | int chips = 0; |
8f1cc3b1 | 1000 | struct mcp23s08_driver_data *data; |
0b7bb77f | 1001 | int status, type; |
3af0dbd5 | 1002 | unsigned ngpio = 0; |
97ddb1c8 LP |
1003 | const struct of_device_id *match; |
1004 | u32 spi_present_mask = 0; | |
1005 | ||
1006 | match = of_match_device(of_match_ptr(mcp23s08_spi_of_match), &spi->dev); | |
1007 | if (match) { | |
de755c33 | 1008 | type = (int)(uintptr_t)match->data; |
97ddb1c8 | 1009 | status = of_property_read_u32(spi->dev.of_node, |
45971686 | 1010 | "microchip,spi-present-mask", &spi_present_mask); |
97ddb1c8 | 1011 | if (status) { |
45971686 LP |
1012 | status = of_property_read_u32(spi->dev.of_node, |
1013 | "mcp,spi-present-mask", &spi_present_mask); | |
1014 | if (status) { | |
1015 | dev_err(&spi->dev, | |
1016 | "DT has no spi-present-mask\n"); | |
1017 | return -ENODEV; | |
1018 | } | |
97ddb1c8 LP |
1019 | } |
1020 | if ((spi_present_mask <= 0) || (spi_present_mask >= 256)) { | |
1021 | dev_err(&spi->dev, "invalid spi-present-mask\n"); | |
1022 | return -ENODEV; | |
1023 | } | |
8f1cc3b1 | 1024 | |
3af0dbd5 SZ |
1025 | pdata = &local_pdata; |
1026 | pdata->base = -1; | |
99e4b98d | 1027 | for (addr = 0; addr < ARRAY_SIZE(pdata->chip); addr++) { |
3e3bed91 MS |
1028 | if (spi_present_mask & (1 << addr)) |
1029 | chips++; | |
99e4b98d | 1030 | } |
3af0dbd5 SZ |
1031 | pdata->irq_controller = of_property_read_bool( |
1032 | spi->dev.of_node, | |
1033 | "interrupt-controller"); | |
1034 | pdata->mirror = of_property_read_bool(spi->dev.of_node, | |
1035 | "microchip,irq-mirror"); | |
97ddb1c8 LP |
1036 | } else { |
1037 | type = spi_get_device_id(spi)->driver_data; | |
e56aee18 | 1038 | pdata = dev_get_platdata(&spi->dev); |
b184c388 SZ |
1039 | if (!pdata) { |
1040 | pdata = devm_kzalloc(&spi->dev, | |
1041 | sizeof(struct mcp23s08_platform_data), | |
1042 | GFP_KERNEL); | |
1043 | pdata->base = -1; | |
0b7bb77f | 1044 | } |
97ddb1c8 LP |
1045 | |
1046 | for (addr = 0; addr < ARRAY_SIZE(pdata->chip); addr++) { | |
1047 | if (!pdata->chip[addr].is_present) | |
1048 | continue; | |
1049 | chips++; | |
1050 | if ((type == MCP_TYPE_S08) && (addr > 3)) { | |
1051 | dev_err(&spi->dev, | |
1052 | "mcp23s08 only supports address 0..3\n"); | |
1053 | return -EINVAL; | |
1054 | } | |
1055 | spi_present_mask |= 1 << addr; | |
97ddb1c8 | 1056 | } |
8f1cc3b1 | 1057 | } |
8f1cc3b1 | 1058 | |
99e4b98d MW |
1059 | if (!chips) |
1060 | return -ENODEV; | |
1061 | ||
7898b31e VB |
1062 | data = devm_kzalloc(&spi->dev, |
1063 | sizeof(*data) + chips * sizeof(struct mcp23s08), | |
1064 | GFP_KERNEL); | |
8f1cc3b1 DB |
1065 | if (!data) |
1066 | return -ENOMEM; | |
7898b31e | 1067 | |
8f1cc3b1 DB |
1068 | spi_set_drvdata(spi, data); |
1069 | ||
a231b88c AS |
1070 | spi->irq = irq_of_parse_and_map(spi->dev.of_node, 0); |
1071 | ||
0b7bb77f | 1072 | for (addr = 0; addr < ARRAY_SIZE(pdata->chip); addr++) { |
97ddb1c8 | 1073 | if (!(spi_present_mask & (1 << addr))) |
8f1cc3b1 DB |
1074 | continue; |
1075 | chips--; | |
1076 | data->mcp[addr] = &data->chip[chips]; | |
a231b88c | 1077 | data->mcp[addr]->irq = spi->irq; |
d62b98f3 | 1078 | status = mcp23s08_probe_one(data->mcp[addr], &spi->dev, spi, |
3af0dbd5 SZ |
1079 | 0x40 | (addr << 1), type, pdata, |
1080 | addr); | |
8f1cc3b1 DB |
1081 | if (status < 0) |
1082 | goto fail; | |
0b7bb77f | 1083 | |
3af0dbd5 | 1084 | if (pdata->base != -1) |
28c5a41e PR |
1085 | pdata->base += data->mcp[addr]->chip.ngpio; |
1086 | ngpio += data->mcp[addr]->chip.ngpio; | |
8f1cc3b1 | 1087 | } |
97ddb1c8 | 1088 | data->ngpio = ngpio; |
e58b9e27 DB |
1089 | |
1090 | /* NOTE: these chips have a relatively sane IRQ framework, with | |
1091 | * per-signal masking and level/edge triggering. It's not yet | |
1092 | * handled here... | |
1093 | */ | |
1094 | ||
e58b9e27 DB |
1095 | return 0; |
1096 | ||
1097 | fail: | |
0b7bb77f | 1098 | for (addr = 0; addr < ARRAY_SIZE(data->mcp); addr++) { |
8f1cc3b1 DB |
1099 | |
1100 | if (!data->mcp[addr]) | |
1101 | continue; | |
9f5132ae | 1102 | gpiochip_remove(&data->mcp[addr]->chip); |
8f1cc3b1 | 1103 | } |
e58b9e27 DB |
1104 | return status; |
1105 | } | |
1106 | ||
1107 | static int mcp23s08_remove(struct spi_device *spi) | |
1108 | { | |
8f1cc3b1 | 1109 | struct mcp23s08_driver_data *data = spi_get_drvdata(spi); |
8f1cc3b1 | 1110 | unsigned addr; |
e58b9e27 | 1111 | |
0b7bb77f | 1112 | for (addr = 0; addr < ARRAY_SIZE(data->mcp); addr++) { |
8f1cc3b1 DB |
1113 | |
1114 | if (!data->mcp[addr]) | |
1115 | continue; | |
1116 | ||
9f5132ae | 1117 | gpiochip_remove(&data->mcp[addr]->chip); |
8f1cc3b1 | 1118 | } |
c4941e07 | 1119 | |
9f5132ae | 1120 | return 0; |
e58b9e27 DB |
1121 | } |
1122 | ||
0b7bb77f PK |
1123 | static const struct spi_device_id mcp23s08_ids[] = { |
1124 | { "mcp23s08", MCP_TYPE_S08 }, | |
1125 | { "mcp23s17", MCP_TYPE_S17 }, | |
28c5a41e | 1126 | { "mcp23s18", MCP_TYPE_S18 }, |
0b7bb77f PK |
1127 | { }, |
1128 | }; | |
1129 | MODULE_DEVICE_TABLE(spi, mcp23s08_ids); | |
1130 | ||
e58b9e27 DB |
1131 | static struct spi_driver mcp23s08_driver = { |
1132 | .probe = mcp23s08_probe, | |
1133 | .remove = mcp23s08_remove, | |
0b7bb77f | 1134 | .id_table = mcp23s08_ids, |
e58b9e27 DB |
1135 | .driver = { |
1136 | .name = "mcp23s08", | |
97ddb1c8 | 1137 | .of_match_table = of_match_ptr(mcp23s08_spi_of_match), |
e58b9e27 DB |
1138 | }, |
1139 | }; | |
1140 | ||
d62b98f3 PK |
1141 | static int __init mcp23s08_spi_init(void) |
1142 | { | |
1143 | return spi_register_driver(&mcp23s08_driver); | |
1144 | } | |
1145 | ||
1146 | static void mcp23s08_spi_exit(void) | |
1147 | { | |
1148 | spi_unregister_driver(&mcp23s08_driver); | |
1149 | } | |
1150 | ||
1151 | #else | |
1152 | ||
1153 | static int __init mcp23s08_spi_init(void) { return 0; } | |
1154 | static void mcp23s08_spi_exit(void) { } | |
1155 | ||
1156 | #endif /* CONFIG_SPI_MASTER */ | |
1157 | ||
e58b9e27 DB |
1158 | /*----------------------------------------------------------------------*/ |
1159 | ||
1160 | static int __init mcp23s08_init(void) | |
1161 | { | |
752ad5e8 PK |
1162 | int ret; |
1163 | ||
1164 | ret = mcp23s08_spi_init(); | |
1165 | if (ret) | |
1166 | goto spi_fail; | |
1167 | ||
1168 | ret = mcp23s08_i2c_init(); | |
1169 | if (ret) | |
1170 | goto i2c_fail; | |
1171 | ||
1172 | return 0; | |
1173 | ||
1174 | i2c_fail: | |
1175 | mcp23s08_spi_exit(); | |
1176 | spi_fail: | |
1177 | return ret; | |
e58b9e27 | 1178 | } |
752ad5e8 | 1179 | /* register after spi/i2c postcore initcall and before |
673c0c00 DB |
1180 | * subsys initcalls that may rely on these GPIOs |
1181 | */ | |
1182 | subsys_initcall(mcp23s08_init); | |
e58b9e27 DB |
1183 | |
1184 | static void __exit mcp23s08_exit(void) | |
1185 | { | |
d62b98f3 | 1186 | mcp23s08_spi_exit(); |
752ad5e8 | 1187 | mcp23s08_i2c_exit(); |
e58b9e27 DB |
1188 | } |
1189 | module_exit(mcp23s08_exit); | |
1190 | ||
1191 | MODULE_LICENSE("GPL"); |