Commit | Line | Data |
---|---|---|
b64333ce RJ |
1 | /* |
2 | * Copyright (C) 2014-2015 Broadcom Corporation | |
3 | * | |
4 | * This program is free software; you can redistribute it and/or | |
5 | * modify it under the terms of the GNU General Public License as | |
6 | * published by the Free Software Foundation version 2. | |
7 | * | |
8 | * This program is distributed "as is" WITHOUT ANY WARRANTY of any | |
9 | * kind, whether express or implied; without even the implied warranty | |
10 | * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
11 | * GNU General Public License for more details. | |
12 | * | |
13 | * This file contains the Broadcom Cygnus GPIO driver that supports 3 | |
14 | * GPIO controllers on Cygnus including the ASIU GPIO controller, the | |
15 | * chipCommonG GPIO controller, and the always-on GPIO controller. Basic | |
16 | * PINCONF such as bias pull up/down, and drive strength are also supported | |
17 | * in this driver. | |
18 | * | |
19 | * Pins from the ASIU GPIO can be individually muxed to GPIO function, | |
20 | * through the interaction with the Cygnus IOMUX controller | |
21 | */ | |
22 | ||
23 | #include <linux/kernel.h> | |
24 | #include <linux/slab.h> | |
25 | #include <linux/interrupt.h> | |
26 | #include <linux/io.h> | |
27 | #include <linux/gpio.h> | |
28 | #include <linux/ioport.h> | |
29 | #include <linux/of_device.h> | |
30 | #include <linux/of_irq.h> | |
31 | #include <linux/pinctrl/pinctrl.h> | |
32 | #include <linux/pinctrl/pinmux.h> | |
33 | #include <linux/pinctrl/pinconf.h> | |
34 | #include <linux/pinctrl/pinconf-generic.h> | |
35 | ||
36 | #include "../pinctrl-utils.h" | |
37 | ||
38 | #define CYGNUS_GPIO_DATA_IN_OFFSET 0x00 | |
39 | #define CYGNUS_GPIO_DATA_OUT_OFFSET 0x04 | |
40 | #define CYGNUS_GPIO_OUT_EN_OFFSET 0x08 | |
6dce6d24 | 41 | #define CYGNUS_GPIO_INT_TYPE_OFFSET 0x0c |
b64333ce RJ |
42 | #define CYGNUS_GPIO_INT_DE_OFFSET 0x10 |
43 | #define CYGNUS_GPIO_INT_EDGE_OFFSET 0x14 | |
44 | #define CYGNUS_GPIO_INT_MSK_OFFSET 0x18 | |
45 | #define CYGNUS_GPIO_INT_STAT_OFFSET 0x1c | |
46 | #define CYGNUS_GPIO_INT_MSTAT_OFFSET 0x20 | |
47 | #define CYGNUS_GPIO_INT_CLR_OFFSET 0x24 | |
48 | #define CYGNUS_GPIO_PAD_RES_OFFSET 0x34 | |
49 | #define CYGNUS_GPIO_RES_EN_OFFSET 0x38 | |
50 | ||
51 | /* drive strength control for ASIU GPIO */ | |
52 | #define CYGNUS_GPIO_ASIU_DRV0_CTRL_OFFSET 0x58 | |
53 | ||
54 | /* drive strength control for CCM/CRMU (AON) GPIO */ | |
55 | #define CYGNUS_GPIO_DRV0_CTRL_OFFSET 0x00 | |
56 | ||
57 | #define GPIO_BANK_SIZE 0x200 | |
58 | #define NGPIOS_PER_BANK 32 | |
59 | #define GPIO_BANK(pin) ((pin) / NGPIOS_PER_BANK) | |
60 | ||
61 | #define CYGNUS_GPIO_REG(pin, reg) (GPIO_BANK(pin) * GPIO_BANK_SIZE + (reg)) | |
62 | #define CYGNUS_GPIO_SHIFT(pin) ((pin) % NGPIOS_PER_BANK) | |
63 | ||
64 | #define GPIO_DRV_STRENGTH_BIT_SHIFT 20 | |
65 | #define GPIO_DRV_STRENGTH_BITS 3 | |
66 | #define GPIO_DRV_STRENGTH_BIT_MASK ((1 << GPIO_DRV_STRENGTH_BITS) - 1) | |
67 | ||
68 | /* | |
69 | * Cygnus GPIO core | |
70 | * | |
71 | * @dev: pointer to device | |
72 | * @base: I/O register base for Cygnus GPIO controller | |
73 | * @io_ctrl: I/O register base for certain type of Cygnus GPIO controller that | |
74 | * has the PINCONF support implemented outside of the GPIO block | |
75 | * @lock: lock to protect access to I/O registers | |
76 | * @gc: GPIO chip | |
77 | * @num_banks: number of GPIO banks, each bank supports up to 32 GPIOs | |
78 | * @pinmux_is_supported: flag to indicate this GPIO controller contains pins | |
79 | * that can be individually muxed to GPIO | |
80 | * @pctl: pointer to pinctrl_dev | |
81 | * @pctldesc: pinctrl descriptor | |
82 | */ | |
83 | struct cygnus_gpio { | |
84 | struct device *dev; | |
85 | ||
86 | void __iomem *base; | |
87 | void __iomem *io_ctrl; | |
88 | ||
89 | spinlock_t lock; | |
90 | ||
91 | struct gpio_chip gc; | |
92 | unsigned num_banks; | |
93 | ||
94 | bool pinmux_is_supported; | |
95 | ||
96 | struct pinctrl_dev *pctl; | |
97 | struct pinctrl_desc pctldesc; | |
98 | }; | |
99 | ||
100 | static inline struct cygnus_gpio *to_cygnus_gpio(struct gpio_chip *gc) | |
101 | { | |
102 | return container_of(gc, struct cygnus_gpio, gc); | |
103 | } | |
104 | ||
105 | /* | |
106 | * Mapping from PINCONF pins to GPIO pins is 1-to-1 | |
107 | */ | |
108 | static inline unsigned cygnus_pin_to_gpio(unsigned pin) | |
109 | { | |
110 | return pin; | |
111 | } | |
112 | ||
113 | /** | |
114 | * cygnus_set_bit - set or clear one bit (corresponding to the GPIO pin) in a | |
115 | * Cygnus GPIO register | |
116 | * | |
117 | * @cygnus_gpio: Cygnus GPIO device | |
118 | * @reg: register offset | |
119 | * @gpio: GPIO pin | |
120 | * @set: set or clear | |
121 | */ | |
122 | static inline void cygnus_set_bit(struct cygnus_gpio *chip, unsigned int reg, | |
123 | unsigned gpio, bool set) | |
124 | { | |
125 | unsigned int offset = CYGNUS_GPIO_REG(gpio, reg); | |
126 | unsigned int shift = CYGNUS_GPIO_SHIFT(gpio); | |
127 | u32 val; | |
128 | ||
129 | val = readl(chip->base + offset); | |
130 | if (set) | |
131 | val |= BIT(shift); | |
132 | else | |
133 | val &= ~BIT(shift); | |
134 | writel(val, chip->base + offset); | |
135 | } | |
136 | ||
137 | static inline bool cygnus_get_bit(struct cygnus_gpio *chip, unsigned int reg, | |
138 | unsigned gpio) | |
139 | { | |
140 | unsigned int offset = CYGNUS_GPIO_REG(gpio, reg); | |
141 | unsigned int shift = CYGNUS_GPIO_SHIFT(gpio); | |
142 | ||
143 | return !!(readl(chip->base + offset) & BIT(shift)); | |
144 | } | |
145 | ||
bd0b9ac4 | 146 | static void cygnus_gpio_irq_handler(struct irq_desc *desc) |
b64333ce RJ |
147 | { |
148 | struct gpio_chip *gc = irq_desc_get_handler_data(desc); | |
149 | struct cygnus_gpio *chip = to_cygnus_gpio(gc); | |
150 | struct irq_chip *irq_chip = irq_desc_get_chip(desc); | |
151 | int i, bit; | |
152 | ||
153 | chained_irq_enter(irq_chip, desc); | |
154 | ||
155 | /* go through the entire GPIO banks and handle all interrupts */ | |
156 | for (i = 0; i < chip->num_banks; i++) { | |
157 | unsigned long val = readl(chip->base + (i * GPIO_BANK_SIZE) + | |
158 | CYGNUS_GPIO_INT_MSTAT_OFFSET); | |
159 | ||
160 | for_each_set_bit(bit, &val, NGPIOS_PER_BANK) { | |
161 | unsigned pin = NGPIOS_PER_BANK * i + bit; | |
162 | int child_irq = irq_find_mapping(gc->irqdomain, pin); | |
163 | ||
164 | /* | |
165 | * Clear the interrupt before invoking the | |
166 | * handler, so we do not leave any window | |
167 | */ | |
168 | writel(BIT(bit), chip->base + (i * GPIO_BANK_SIZE) + | |
169 | CYGNUS_GPIO_INT_CLR_OFFSET); | |
170 | ||
171 | generic_handle_irq(child_irq); | |
172 | } | |
173 | } | |
174 | ||
175 | chained_irq_exit(irq_chip, desc); | |
176 | } | |
177 | ||
178 | ||
179 | static void cygnus_gpio_irq_ack(struct irq_data *d) | |
180 | { | |
181 | struct gpio_chip *gc = irq_data_get_irq_chip_data(d); | |
182 | struct cygnus_gpio *chip = to_cygnus_gpio(gc); | |
183 | unsigned gpio = d->hwirq; | |
184 | unsigned int offset = CYGNUS_GPIO_REG(gpio, | |
185 | CYGNUS_GPIO_INT_CLR_OFFSET); | |
186 | unsigned int shift = CYGNUS_GPIO_SHIFT(gpio); | |
187 | u32 val = BIT(shift); | |
188 | ||
189 | writel(val, chip->base + offset); | |
190 | } | |
191 | ||
192 | /** | |
193 | * cygnus_gpio_irq_set_mask - mask/unmask a GPIO interrupt | |
194 | * | |
195 | * @d: IRQ chip data | |
196 | * @unmask: mask/unmask GPIO interrupt | |
197 | */ | |
198 | static void cygnus_gpio_irq_set_mask(struct irq_data *d, bool unmask) | |
199 | { | |
200 | struct gpio_chip *gc = irq_data_get_irq_chip_data(d); | |
201 | struct cygnus_gpio *chip = to_cygnus_gpio(gc); | |
202 | unsigned gpio = d->hwirq; | |
203 | ||
204 | cygnus_set_bit(chip, CYGNUS_GPIO_INT_MSK_OFFSET, gpio, unmask); | |
205 | } | |
206 | ||
207 | static void cygnus_gpio_irq_mask(struct irq_data *d) | |
208 | { | |
209 | struct gpio_chip *gc = irq_data_get_irq_chip_data(d); | |
210 | struct cygnus_gpio *chip = to_cygnus_gpio(gc); | |
211 | unsigned long flags; | |
212 | ||
213 | spin_lock_irqsave(&chip->lock, flags); | |
214 | cygnus_gpio_irq_set_mask(d, false); | |
215 | spin_unlock_irqrestore(&chip->lock, flags); | |
216 | } | |
217 | ||
218 | static void cygnus_gpio_irq_unmask(struct irq_data *d) | |
219 | { | |
220 | struct gpio_chip *gc = irq_data_get_irq_chip_data(d); | |
221 | struct cygnus_gpio *chip = to_cygnus_gpio(gc); | |
222 | unsigned long flags; | |
223 | ||
224 | spin_lock_irqsave(&chip->lock, flags); | |
225 | cygnus_gpio_irq_set_mask(d, true); | |
226 | spin_unlock_irqrestore(&chip->lock, flags); | |
227 | } | |
228 | ||
229 | static int cygnus_gpio_irq_set_type(struct irq_data *d, unsigned int type) | |
230 | { | |
231 | struct gpio_chip *gc = irq_data_get_irq_chip_data(d); | |
232 | struct cygnus_gpio *chip = to_cygnus_gpio(gc); | |
233 | unsigned gpio = d->hwirq; | |
234 | bool level_triggered = false; | |
235 | bool dual_edge = false; | |
236 | bool rising_or_high = false; | |
237 | unsigned long flags; | |
238 | ||
239 | switch (type & IRQ_TYPE_SENSE_MASK) { | |
240 | case IRQ_TYPE_EDGE_RISING: | |
241 | rising_or_high = true; | |
242 | break; | |
243 | ||
244 | case IRQ_TYPE_EDGE_FALLING: | |
245 | break; | |
246 | ||
247 | case IRQ_TYPE_EDGE_BOTH: | |
248 | dual_edge = true; | |
249 | break; | |
250 | ||
251 | case IRQ_TYPE_LEVEL_HIGH: | |
252 | level_triggered = true; | |
253 | rising_or_high = true; | |
254 | break; | |
255 | ||
256 | case IRQ_TYPE_LEVEL_LOW: | |
257 | level_triggered = true; | |
258 | break; | |
259 | ||
260 | default: | |
261 | dev_err(chip->dev, "invalid GPIO IRQ type 0x%x\n", | |
262 | type); | |
263 | return -EINVAL; | |
264 | } | |
265 | ||
266 | spin_lock_irqsave(&chip->lock, flags); | |
6dce6d24 | 267 | cygnus_set_bit(chip, CYGNUS_GPIO_INT_TYPE_OFFSET, gpio, |
b64333ce RJ |
268 | level_triggered); |
269 | cygnus_set_bit(chip, CYGNUS_GPIO_INT_DE_OFFSET, gpio, dual_edge); | |
270 | cygnus_set_bit(chip, CYGNUS_GPIO_INT_EDGE_OFFSET, gpio, | |
271 | rising_or_high); | |
272 | spin_unlock_irqrestore(&chip->lock, flags); | |
273 | ||
274 | dev_dbg(chip->dev, | |
275 | "gpio:%u level_triggered:%d dual_edge:%d rising_or_high:%d\n", | |
276 | gpio, level_triggered, dual_edge, rising_or_high); | |
277 | ||
278 | return 0; | |
279 | } | |
280 | ||
281 | static struct irq_chip cygnus_gpio_irq_chip = { | |
282 | .name = "bcm-cygnus-gpio", | |
283 | .irq_ack = cygnus_gpio_irq_ack, | |
284 | .irq_mask = cygnus_gpio_irq_mask, | |
285 | .irq_unmask = cygnus_gpio_irq_unmask, | |
286 | .irq_set_type = cygnus_gpio_irq_set_type, | |
287 | }; | |
288 | ||
289 | /* | |
290 | * Request the Cygnus IOMUX pinmux controller to mux individual pins to GPIO | |
291 | */ | |
292 | static int cygnus_gpio_request(struct gpio_chip *gc, unsigned offset) | |
293 | { | |
294 | struct cygnus_gpio *chip = to_cygnus_gpio(gc); | |
295 | unsigned gpio = gc->base + offset; | |
296 | ||
297 | /* not all Cygnus GPIO pins can be muxed individually */ | |
298 | if (!chip->pinmux_is_supported) | |
299 | return 0; | |
300 | ||
301 | return pinctrl_request_gpio(gpio); | |
302 | } | |
303 | ||
304 | static void cygnus_gpio_free(struct gpio_chip *gc, unsigned offset) | |
305 | { | |
306 | struct cygnus_gpio *chip = to_cygnus_gpio(gc); | |
307 | unsigned gpio = gc->base + offset; | |
308 | ||
309 | if (!chip->pinmux_is_supported) | |
310 | return; | |
311 | ||
312 | pinctrl_free_gpio(gpio); | |
313 | } | |
314 | ||
315 | static int cygnus_gpio_direction_input(struct gpio_chip *gc, unsigned gpio) | |
316 | { | |
317 | struct cygnus_gpio *chip = to_cygnus_gpio(gc); | |
318 | unsigned long flags; | |
319 | ||
320 | spin_lock_irqsave(&chip->lock, flags); | |
321 | cygnus_set_bit(chip, CYGNUS_GPIO_OUT_EN_OFFSET, gpio, false); | |
322 | spin_unlock_irqrestore(&chip->lock, flags); | |
323 | ||
324 | dev_dbg(chip->dev, "gpio:%u set input\n", gpio); | |
325 | ||
326 | return 0; | |
327 | } | |
328 | ||
329 | static int cygnus_gpio_direction_output(struct gpio_chip *gc, unsigned gpio, | |
330 | int val) | |
331 | { | |
332 | struct cygnus_gpio *chip = to_cygnus_gpio(gc); | |
333 | unsigned long flags; | |
334 | ||
335 | spin_lock_irqsave(&chip->lock, flags); | |
336 | cygnus_set_bit(chip, CYGNUS_GPIO_OUT_EN_OFFSET, gpio, true); | |
337 | cygnus_set_bit(chip, CYGNUS_GPIO_DATA_OUT_OFFSET, gpio, !!(val)); | |
338 | spin_unlock_irqrestore(&chip->lock, flags); | |
339 | ||
340 | dev_dbg(chip->dev, "gpio:%u set output, value:%d\n", gpio, val); | |
341 | ||
342 | return 0; | |
343 | } | |
344 | ||
345 | static void cygnus_gpio_set(struct gpio_chip *gc, unsigned gpio, int val) | |
346 | { | |
347 | struct cygnus_gpio *chip = to_cygnus_gpio(gc); | |
348 | unsigned long flags; | |
349 | ||
350 | spin_lock_irqsave(&chip->lock, flags); | |
351 | cygnus_set_bit(chip, CYGNUS_GPIO_DATA_OUT_OFFSET, gpio, !!(val)); | |
352 | spin_unlock_irqrestore(&chip->lock, flags); | |
353 | ||
354 | dev_dbg(chip->dev, "gpio:%u set, value:%d\n", gpio, val); | |
355 | } | |
356 | ||
357 | static int cygnus_gpio_get(struct gpio_chip *gc, unsigned gpio) | |
358 | { | |
359 | struct cygnus_gpio *chip = to_cygnus_gpio(gc); | |
360 | unsigned int offset = CYGNUS_GPIO_REG(gpio, | |
361 | CYGNUS_GPIO_DATA_IN_OFFSET); | |
362 | unsigned int shift = CYGNUS_GPIO_SHIFT(gpio); | |
363 | ||
364 | return !!(readl(chip->base + offset) & BIT(shift)); | |
365 | } | |
366 | ||
367 | static int cygnus_get_groups_count(struct pinctrl_dev *pctldev) | |
368 | { | |
369 | return 1; | |
370 | } | |
371 | ||
372 | /* | |
373 | * Only one group: "gpio_grp", since this local pinctrl device only performs | |
374 | * GPIO specific PINCONF configurations | |
375 | */ | |
376 | static const char *cygnus_get_group_name(struct pinctrl_dev *pctldev, | |
377 | unsigned selector) | |
378 | { | |
379 | return "gpio_grp"; | |
380 | } | |
381 | ||
382 | static const struct pinctrl_ops cygnus_pctrl_ops = { | |
383 | .get_groups_count = cygnus_get_groups_count, | |
384 | .get_group_name = cygnus_get_group_name, | |
385 | .dt_node_to_map = pinconf_generic_dt_node_to_map_pin, | |
386 | .dt_free_map = pinctrl_utils_dt_free_map, | |
387 | }; | |
388 | ||
389 | static int cygnus_gpio_set_pull(struct cygnus_gpio *chip, unsigned gpio, | |
390 | bool disable, bool pull_up) | |
391 | { | |
392 | unsigned long flags; | |
393 | ||
394 | spin_lock_irqsave(&chip->lock, flags); | |
395 | ||
396 | if (disable) { | |
397 | cygnus_set_bit(chip, CYGNUS_GPIO_RES_EN_OFFSET, gpio, false); | |
398 | } else { | |
399 | cygnus_set_bit(chip, CYGNUS_GPIO_PAD_RES_OFFSET, gpio, | |
400 | pull_up); | |
401 | cygnus_set_bit(chip, CYGNUS_GPIO_RES_EN_OFFSET, gpio, true); | |
402 | } | |
403 | ||
404 | spin_unlock_irqrestore(&chip->lock, flags); | |
405 | ||
406 | dev_dbg(chip->dev, "gpio:%u set pullup:%d\n", gpio, pull_up); | |
407 | ||
408 | return 0; | |
409 | } | |
410 | ||
411 | static void cygnus_gpio_get_pull(struct cygnus_gpio *chip, unsigned gpio, | |
412 | bool *disable, bool *pull_up) | |
413 | { | |
414 | unsigned long flags; | |
415 | ||
416 | spin_lock_irqsave(&chip->lock, flags); | |
417 | *disable = !cygnus_get_bit(chip, CYGNUS_GPIO_RES_EN_OFFSET, gpio); | |
418 | *pull_up = cygnus_get_bit(chip, CYGNUS_GPIO_PAD_RES_OFFSET, gpio); | |
419 | spin_unlock_irqrestore(&chip->lock, flags); | |
420 | } | |
421 | ||
422 | static int cygnus_gpio_set_strength(struct cygnus_gpio *chip, unsigned gpio, | |
423 | unsigned strength) | |
424 | { | |
425 | void __iomem *base; | |
426 | unsigned int i, offset, shift; | |
427 | u32 val; | |
428 | unsigned long flags; | |
429 | ||
430 | /* make sure drive strength is supported */ | |
431 | if (strength < 2 || strength > 16 || (strength % 2)) | |
432 | return -ENOTSUPP; | |
433 | ||
434 | if (chip->io_ctrl) { | |
435 | base = chip->io_ctrl; | |
436 | offset = CYGNUS_GPIO_DRV0_CTRL_OFFSET; | |
437 | } else { | |
438 | base = chip->base; | |
439 | offset = CYGNUS_GPIO_REG(gpio, | |
440 | CYGNUS_GPIO_ASIU_DRV0_CTRL_OFFSET); | |
441 | } | |
442 | ||
443 | shift = CYGNUS_GPIO_SHIFT(gpio); | |
444 | ||
445 | dev_dbg(chip->dev, "gpio:%u set drive strength:%d mA\n", gpio, | |
446 | strength); | |
447 | ||
448 | spin_lock_irqsave(&chip->lock, flags); | |
449 | strength = (strength / 2) - 1; | |
450 | for (i = 0; i < GPIO_DRV_STRENGTH_BITS; i++) { | |
451 | val = readl(base + offset); | |
452 | val &= ~BIT(shift); | |
453 | val |= ((strength >> i) & 0x1) << shift; | |
454 | writel(val, base + offset); | |
455 | offset += 4; | |
456 | } | |
457 | spin_unlock_irqrestore(&chip->lock, flags); | |
458 | ||
459 | return 0; | |
460 | } | |
461 | ||
462 | static int cygnus_gpio_get_strength(struct cygnus_gpio *chip, unsigned gpio, | |
463 | u16 *strength) | |
464 | { | |
465 | void __iomem *base; | |
466 | unsigned int i, offset, shift; | |
467 | u32 val; | |
468 | unsigned long flags; | |
469 | ||
470 | if (chip->io_ctrl) { | |
471 | base = chip->io_ctrl; | |
472 | offset = CYGNUS_GPIO_DRV0_CTRL_OFFSET; | |
473 | } else { | |
474 | base = chip->base; | |
475 | offset = CYGNUS_GPIO_REG(gpio, | |
476 | CYGNUS_GPIO_ASIU_DRV0_CTRL_OFFSET); | |
477 | } | |
478 | ||
479 | shift = CYGNUS_GPIO_SHIFT(gpio); | |
480 | ||
481 | spin_lock_irqsave(&chip->lock, flags); | |
482 | *strength = 0; | |
483 | for (i = 0; i < GPIO_DRV_STRENGTH_BITS; i++) { | |
484 | val = readl(base + offset) & BIT(shift); | |
485 | val >>= shift; | |
486 | *strength += (val << i); | |
487 | offset += 4; | |
488 | } | |
489 | ||
490 | /* convert to mA */ | |
491 | *strength = (*strength + 1) * 2; | |
492 | spin_unlock_irqrestore(&chip->lock, flags); | |
493 | ||
494 | return 0; | |
495 | } | |
496 | ||
497 | static int cygnus_pin_config_get(struct pinctrl_dev *pctldev, unsigned pin, | |
498 | unsigned long *config) | |
499 | { | |
500 | struct cygnus_gpio *chip = pinctrl_dev_get_drvdata(pctldev); | |
501 | enum pin_config_param param = pinconf_to_config_param(*config); | |
502 | unsigned gpio = cygnus_pin_to_gpio(pin); | |
503 | u16 arg; | |
504 | bool disable, pull_up; | |
505 | int ret; | |
506 | ||
507 | switch (param) { | |
508 | case PIN_CONFIG_BIAS_DISABLE: | |
509 | cygnus_gpio_get_pull(chip, gpio, &disable, &pull_up); | |
510 | if (disable) | |
511 | return 0; | |
512 | else | |
513 | return -EINVAL; | |
514 | ||
515 | case PIN_CONFIG_BIAS_PULL_UP: | |
516 | cygnus_gpio_get_pull(chip, gpio, &disable, &pull_up); | |
517 | if (!disable && pull_up) | |
518 | return 0; | |
519 | else | |
520 | return -EINVAL; | |
521 | ||
522 | case PIN_CONFIG_BIAS_PULL_DOWN: | |
523 | cygnus_gpio_get_pull(chip, gpio, &disable, &pull_up); | |
524 | if (!disable && !pull_up) | |
525 | return 0; | |
526 | else | |
527 | return -EINVAL; | |
528 | ||
529 | case PIN_CONFIG_DRIVE_STRENGTH: | |
530 | ret = cygnus_gpio_get_strength(chip, gpio, &arg); | |
531 | if (ret) | |
532 | return ret; | |
533 | else | |
534 | *config = pinconf_to_config_packed(param, arg); | |
535 | ||
536 | return 0; | |
537 | ||
538 | default: | |
539 | return -ENOTSUPP; | |
540 | } | |
541 | ||
542 | return -ENOTSUPP; | |
543 | } | |
544 | ||
545 | static int cygnus_pin_config_set(struct pinctrl_dev *pctldev, unsigned pin, | |
546 | unsigned long *configs, unsigned num_configs) | |
547 | { | |
548 | struct cygnus_gpio *chip = pinctrl_dev_get_drvdata(pctldev); | |
549 | enum pin_config_param param; | |
550 | u16 arg; | |
551 | unsigned i, gpio = cygnus_pin_to_gpio(pin); | |
552 | int ret = -ENOTSUPP; | |
553 | ||
554 | for (i = 0; i < num_configs; i++) { | |
555 | param = pinconf_to_config_param(configs[i]); | |
556 | arg = pinconf_to_config_argument(configs[i]); | |
557 | ||
558 | switch (param) { | |
559 | case PIN_CONFIG_BIAS_DISABLE: | |
560 | ret = cygnus_gpio_set_pull(chip, gpio, true, false); | |
561 | if (ret < 0) | |
562 | goto out; | |
563 | break; | |
564 | ||
565 | case PIN_CONFIG_BIAS_PULL_UP: | |
566 | ret = cygnus_gpio_set_pull(chip, gpio, false, true); | |
567 | if (ret < 0) | |
568 | goto out; | |
569 | break; | |
570 | ||
571 | case PIN_CONFIG_BIAS_PULL_DOWN: | |
572 | ret = cygnus_gpio_set_pull(chip, gpio, false, false); | |
573 | if (ret < 0) | |
574 | goto out; | |
575 | break; | |
576 | ||
577 | case PIN_CONFIG_DRIVE_STRENGTH: | |
578 | ret = cygnus_gpio_set_strength(chip, gpio, arg); | |
579 | if (ret < 0) | |
580 | goto out; | |
581 | break; | |
582 | ||
583 | default: | |
584 | dev_err(chip->dev, "invalid configuration\n"); | |
585 | return -ENOTSUPP; | |
586 | } | |
587 | } /* for each config */ | |
588 | ||
589 | out: | |
590 | return ret; | |
591 | } | |
592 | ||
593 | static const struct pinconf_ops cygnus_pconf_ops = { | |
594 | .is_generic = true, | |
595 | .pin_config_get = cygnus_pin_config_get, | |
596 | .pin_config_set = cygnus_pin_config_set, | |
597 | }; | |
598 | ||
599 | /* | |
600 | * Map a GPIO in the local gpio_chip pin space to a pin in the Cygnus IOMUX | |
601 | * pinctrl pin space | |
602 | */ | |
603 | struct cygnus_gpio_pin_range { | |
604 | unsigned offset; | |
605 | unsigned pin_base; | |
606 | unsigned num_pins; | |
607 | }; | |
608 | ||
609 | #define CYGNUS_PINRANGE(o, p, n) { .offset = o, .pin_base = p, .num_pins = n } | |
610 | ||
611 | /* | |
612 | * Pin mapping table for mapping local GPIO pins to Cygnus IOMUX pinctrl pins | |
613 | */ | |
614 | static const struct cygnus_gpio_pin_range cygnus_gpio_pintable[] = { | |
615 | CYGNUS_PINRANGE(0, 42, 1), | |
616 | CYGNUS_PINRANGE(1, 44, 3), | |
617 | CYGNUS_PINRANGE(4, 48, 1), | |
618 | CYGNUS_PINRANGE(5, 50, 3), | |
619 | CYGNUS_PINRANGE(8, 126, 1), | |
620 | CYGNUS_PINRANGE(9, 155, 1), | |
621 | CYGNUS_PINRANGE(10, 152, 1), | |
622 | CYGNUS_PINRANGE(11, 154, 1), | |
623 | CYGNUS_PINRANGE(12, 153, 1), | |
624 | CYGNUS_PINRANGE(13, 127, 3), | |
625 | CYGNUS_PINRANGE(16, 140, 1), | |
626 | CYGNUS_PINRANGE(17, 145, 7), | |
627 | CYGNUS_PINRANGE(24, 130, 10), | |
628 | CYGNUS_PINRANGE(34, 141, 4), | |
629 | CYGNUS_PINRANGE(38, 54, 1), | |
630 | CYGNUS_PINRANGE(39, 56, 3), | |
631 | CYGNUS_PINRANGE(42, 60, 3), | |
632 | CYGNUS_PINRANGE(45, 64, 3), | |
633 | CYGNUS_PINRANGE(48, 68, 2), | |
634 | CYGNUS_PINRANGE(50, 84, 6), | |
635 | CYGNUS_PINRANGE(56, 94, 6), | |
636 | CYGNUS_PINRANGE(62, 72, 1), | |
637 | CYGNUS_PINRANGE(63, 70, 1), | |
638 | CYGNUS_PINRANGE(64, 80, 1), | |
639 | CYGNUS_PINRANGE(65, 74, 3), | |
640 | CYGNUS_PINRANGE(68, 78, 1), | |
641 | CYGNUS_PINRANGE(69, 82, 1), | |
642 | CYGNUS_PINRANGE(70, 156, 17), | |
643 | CYGNUS_PINRANGE(87, 104, 12), | |
644 | CYGNUS_PINRANGE(99, 102, 2), | |
645 | CYGNUS_PINRANGE(101, 90, 4), | |
75e91439 RJ |
646 | CYGNUS_PINRANGE(105, 116, 6), |
647 | CYGNUS_PINRANGE(111, 100, 2), | |
648 | CYGNUS_PINRANGE(113, 122, 4), | |
b64333ce RJ |
649 | CYGNUS_PINRANGE(123, 11, 1), |
650 | CYGNUS_PINRANGE(124, 38, 4), | |
651 | CYGNUS_PINRANGE(128, 43, 1), | |
652 | CYGNUS_PINRANGE(129, 47, 1), | |
653 | CYGNUS_PINRANGE(130, 49, 1), | |
654 | CYGNUS_PINRANGE(131, 53, 1), | |
655 | CYGNUS_PINRANGE(132, 55, 1), | |
656 | CYGNUS_PINRANGE(133, 59, 1), | |
657 | CYGNUS_PINRANGE(134, 63, 1), | |
658 | CYGNUS_PINRANGE(135, 67, 1), | |
659 | CYGNUS_PINRANGE(136, 71, 1), | |
660 | CYGNUS_PINRANGE(137, 73, 1), | |
661 | CYGNUS_PINRANGE(138, 77, 1), | |
662 | CYGNUS_PINRANGE(139, 79, 1), | |
663 | CYGNUS_PINRANGE(140, 81, 1), | |
664 | CYGNUS_PINRANGE(141, 83, 1), | |
665 | CYGNUS_PINRANGE(142, 10, 1) | |
666 | }; | |
667 | ||
668 | /* | |
669 | * The Cygnus IOMUX controller mainly supports group based mux configuration, | |
670 | * but certain pins can be muxed to GPIO individually. Only the ASIU GPIO | |
671 | * controller can support this, so it's an optional configuration | |
672 | * | |
673 | * Return -ENODEV means no support and that's fine | |
674 | */ | |
675 | static int cygnus_gpio_pinmux_add_range(struct cygnus_gpio *chip) | |
676 | { | |
677 | struct device_node *node = chip->dev->of_node; | |
678 | struct device_node *pinmux_node; | |
679 | struct platform_device *pinmux_pdev; | |
680 | struct gpio_chip *gc = &chip->gc; | |
681 | int i, ret = 0; | |
682 | ||
683 | /* parse DT to find the phandle to the pinmux controller */ | |
684 | pinmux_node = of_parse_phandle(node, "pinmux", 0); | |
685 | if (!pinmux_node) | |
686 | return -ENODEV; | |
687 | ||
688 | pinmux_pdev = of_find_device_by_node(pinmux_node); | |
689 | /* no longer need the pinmux node */ | |
690 | of_node_put(pinmux_node); | |
691 | if (!pinmux_pdev) { | |
692 | dev_err(chip->dev, "failed to get pinmux device\n"); | |
693 | return -EINVAL; | |
694 | } | |
695 | ||
696 | /* now need to create the mapping between local GPIO and PINMUX pins */ | |
697 | for (i = 0; i < ARRAY_SIZE(cygnus_gpio_pintable); i++) { | |
698 | ret = gpiochip_add_pin_range(gc, dev_name(&pinmux_pdev->dev), | |
699 | cygnus_gpio_pintable[i].offset, | |
700 | cygnus_gpio_pintable[i].pin_base, | |
701 | cygnus_gpio_pintable[i].num_pins); | |
702 | if (ret) { | |
703 | dev_err(chip->dev, "unable to add GPIO pin range\n"); | |
704 | goto err_put_device; | |
705 | } | |
706 | } | |
707 | ||
708 | chip->pinmux_is_supported = true; | |
709 | ||
710 | /* no need for pinmux_pdev device reference anymore */ | |
711 | put_device(&pinmux_pdev->dev); | |
712 | return 0; | |
713 | ||
714 | err_put_device: | |
715 | put_device(&pinmux_pdev->dev); | |
716 | gpiochip_remove_pin_ranges(gc); | |
717 | return ret; | |
718 | } | |
719 | ||
720 | /* | |
721 | * Cygnus GPIO controller supports some PINCONF related configurations such as | |
722 | * pull up, pull down, and drive strength, when the pin is configured to GPIO | |
723 | * | |
724 | * Here a local pinctrl device is created with simple 1-to-1 pin mapping to the | |
725 | * local GPIO pins | |
726 | */ | |
727 | static int cygnus_gpio_register_pinconf(struct cygnus_gpio *chip) | |
728 | { | |
729 | struct pinctrl_desc *pctldesc = &chip->pctldesc; | |
730 | struct pinctrl_pin_desc *pins; | |
731 | struct gpio_chip *gc = &chip->gc; | |
732 | int i; | |
733 | ||
734 | pins = devm_kcalloc(chip->dev, gc->ngpio, sizeof(*pins), GFP_KERNEL); | |
735 | if (!pins) | |
736 | return -ENOMEM; | |
737 | ||
738 | for (i = 0; i < gc->ngpio; i++) { | |
739 | pins[i].number = i; | |
740 | pins[i].name = devm_kasprintf(chip->dev, GFP_KERNEL, | |
741 | "gpio-%d", i); | |
742 | if (!pins[i].name) | |
743 | return -ENOMEM; | |
744 | } | |
745 | ||
746 | pctldesc->name = dev_name(chip->dev); | |
747 | pctldesc->pctlops = &cygnus_pctrl_ops; | |
748 | pctldesc->pins = pins; | |
749 | pctldesc->npins = gc->ngpio; | |
750 | pctldesc->confops = &cygnus_pconf_ops; | |
751 | ||
752 | chip->pctl = pinctrl_register(pctldesc, chip->dev, chip); | |
323de9ef | 753 | if (IS_ERR(chip->pctl)) { |
b64333ce | 754 | dev_err(chip->dev, "unable to register pinctrl device\n"); |
323de9ef | 755 | return PTR_ERR(chip->pctl); |
b64333ce RJ |
756 | } |
757 | ||
758 | return 0; | |
759 | } | |
760 | ||
761 | static void cygnus_gpio_unregister_pinconf(struct cygnus_gpio *chip) | |
762 | { | |
763 | if (chip->pctl) | |
764 | pinctrl_unregister(chip->pctl); | |
765 | } | |
766 | ||
767 | struct cygnus_gpio_data { | |
768 | unsigned num_gpios; | |
769 | }; | |
770 | ||
771 | static const struct cygnus_gpio_data cygnus_cmm_gpio_data = { | |
772 | .num_gpios = 24, | |
773 | }; | |
774 | ||
775 | static const struct cygnus_gpio_data cygnus_asiu_gpio_data = { | |
776 | .num_gpios = 146, | |
777 | }; | |
778 | ||
779 | static const struct cygnus_gpio_data cygnus_crmu_gpio_data = { | |
780 | .num_gpios = 6, | |
781 | }; | |
782 | ||
783 | static const struct of_device_id cygnus_gpio_of_match[] = { | |
784 | { | |
785 | .compatible = "brcm,cygnus-ccm-gpio", | |
786 | .data = &cygnus_cmm_gpio_data, | |
787 | }, | |
788 | { | |
789 | .compatible = "brcm,cygnus-asiu-gpio", | |
790 | .data = &cygnus_asiu_gpio_data, | |
791 | }, | |
792 | { | |
793 | .compatible = "brcm,cygnus-crmu-gpio", | |
794 | .data = &cygnus_crmu_gpio_data, | |
795 | } | |
796 | }; | |
797 | ||
798 | static int cygnus_gpio_probe(struct platform_device *pdev) | |
799 | { | |
800 | struct device *dev = &pdev->dev; | |
801 | struct resource *res; | |
802 | struct cygnus_gpio *chip; | |
803 | struct gpio_chip *gc; | |
804 | u32 ngpios; | |
805 | int irq, ret; | |
806 | const struct of_device_id *match; | |
807 | const struct cygnus_gpio_data *gpio_data; | |
808 | ||
809 | match = of_match_device(cygnus_gpio_of_match, dev); | |
810 | if (!match) | |
811 | return -ENODEV; | |
812 | gpio_data = match->data; | |
813 | ngpios = gpio_data->num_gpios; | |
814 | ||
815 | chip = devm_kzalloc(dev, sizeof(*chip), GFP_KERNEL); | |
816 | if (!chip) | |
817 | return -ENOMEM; | |
818 | ||
819 | chip->dev = dev; | |
820 | platform_set_drvdata(pdev, chip); | |
821 | ||
822 | res = platform_get_resource(pdev, IORESOURCE_MEM, 0); | |
823 | chip->base = devm_ioremap_resource(dev, res); | |
824 | if (IS_ERR(chip->base)) { | |
825 | dev_err(dev, "unable to map I/O memory\n"); | |
826 | return PTR_ERR(chip->base); | |
827 | } | |
828 | ||
829 | res = platform_get_resource(pdev, IORESOURCE_MEM, 1); | |
830 | if (res) { | |
831 | chip->io_ctrl = devm_ioremap_resource(dev, res); | |
832 | if (IS_ERR(chip->io_ctrl)) { | |
833 | dev_err(dev, "unable to map I/O memory\n"); | |
834 | return PTR_ERR(chip->io_ctrl); | |
835 | } | |
836 | } | |
837 | ||
838 | spin_lock_init(&chip->lock); | |
839 | ||
840 | gc = &chip->gc; | |
841 | gc->base = -1; | |
842 | gc->ngpio = ngpios; | |
843 | chip->num_banks = (ngpios + NGPIOS_PER_BANK - 1) / NGPIOS_PER_BANK; | |
844 | gc->label = dev_name(dev); | |
845 | gc->dev = dev; | |
846 | gc->of_node = dev->of_node; | |
847 | gc->request = cygnus_gpio_request; | |
848 | gc->free = cygnus_gpio_free; | |
849 | gc->direction_input = cygnus_gpio_direction_input; | |
850 | gc->direction_output = cygnus_gpio_direction_output; | |
851 | gc->set = cygnus_gpio_set; | |
852 | gc->get = cygnus_gpio_get; | |
853 | ||
854 | ret = gpiochip_add(gc); | |
855 | if (ret < 0) { | |
856 | dev_err(dev, "unable to add GPIO chip\n"); | |
857 | return ret; | |
858 | } | |
859 | ||
860 | ret = cygnus_gpio_pinmux_add_range(chip); | |
861 | if (ret && ret != -ENODEV) { | |
862 | dev_err(dev, "unable to add GPIO pin range\n"); | |
863 | goto err_rm_gpiochip; | |
864 | } | |
865 | ||
866 | ret = cygnus_gpio_register_pinconf(chip); | |
867 | if (ret) { | |
868 | dev_err(dev, "unable to register pinconf\n"); | |
869 | goto err_rm_gpiochip; | |
870 | } | |
871 | ||
872 | /* optional GPIO interrupt support */ | |
873 | irq = platform_get_irq(pdev, 0); | |
874 | if (irq) { | |
875 | ret = gpiochip_irqchip_add(gc, &cygnus_gpio_irq_chip, 0, | |
876 | handle_simple_irq, IRQ_TYPE_NONE); | |
877 | if (ret) { | |
878 | dev_err(dev, "no GPIO irqchip\n"); | |
879 | goto err_unregister_pinconf; | |
880 | } | |
881 | ||
882 | gpiochip_set_chained_irqchip(gc, &cygnus_gpio_irq_chip, irq, | |
883 | cygnus_gpio_irq_handler); | |
884 | } | |
885 | ||
886 | return 0; | |
887 | ||
888 | err_unregister_pinconf: | |
889 | cygnus_gpio_unregister_pinconf(chip); | |
890 | ||
891 | err_rm_gpiochip: | |
892 | gpiochip_remove(gc); | |
893 | ||
894 | return ret; | |
895 | } | |
896 | ||
897 | static struct platform_driver cygnus_gpio_driver = { | |
898 | .driver = { | |
899 | .name = "cygnus-gpio", | |
900 | .of_match_table = cygnus_gpio_of_match, | |
901 | }, | |
902 | .probe = cygnus_gpio_probe, | |
903 | }; | |
904 | ||
905 | static int __init cygnus_gpio_init(void) | |
906 | { | |
907 | return platform_driver_probe(&cygnus_gpio_driver, cygnus_gpio_probe); | |
908 | } | |
909 | arch_initcall_sync(cygnus_gpio_init); |