Commit | Line | Data |
---|---|---|
8b37eb74 | 1 | // SPDX-License-Identifier: GPL-2.0 |
119f5e44 MD |
2 | /* |
3 | * Renesas R-Car GPIO Support | |
4 | * | |
1fd2b49d | 5 | * Copyright (C) 2014 Renesas Electronics Corporation |
119f5e44 | 6 | * Copyright (C) 2013 Magnus Damm |
119f5e44 MD |
7 | */ |
8 | ||
9 | #include <linux/err.h> | |
4b1d8007 | 10 | #include <linux/gpio/driver.h> |
119f5e44 MD |
11 | #include <linux/init.h> |
12 | #include <linux/interrupt.h> | |
13 | #include <linux/io.h> | |
14 | #include <linux/ioport.h> | |
15 | #include <linux/irq.h> | |
119f5e44 | 16 | #include <linux/module.h> |
bd0bf468 | 17 | #include <linux/of.h> |
dc3465a9 | 18 | #include <linux/pinctrl/consumer.h> |
119f5e44 | 19 | #include <linux/platform_device.h> |
df0c6c80 | 20 | #include <linux/pm_runtime.h> |
119f5e44 MD |
21 | #include <linux/spinlock.h> |
22 | #include <linux/slab.h> | |
23 | ||
51750fb1 HD |
24 | struct gpio_rcar_bank_info { |
25 | u32 iointsel; | |
26 | u32 inoutsel; | |
27 | u32 outdt; | |
28 | u32 posneg; | |
29 | u32 edglevel; | |
30 | u32 bothedge; | |
31 | u32 intmsk; | |
32 | }; | |
33 | ||
208c80f1 GU |
34 | struct gpio_rcar_info { |
35 | bool has_outdtsel; | |
36 | bool has_both_edge_trigger; | |
ecba1eaa | 37 | bool has_always_in; |
93ac0b0c | 38 | bool has_inen; |
208c80f1 GU |
39 | }; |
40 | ||
119f5e44 MD |
41 | struct gpio_rcar_priv { |
42 | void __iomem *base; | |
f02c41f8 | 43 | raw_spinlock_t lock; |
a53f7953 | 44 | struct device *dev; |
119f5e44 | 45 | struct gpio_chip gpio_chip; |
8b092be9 | 46 | unsigned int irq_parent; |
9ac79ba9 | 47 | atomic_t wakeup_path; |
208c80f1 | 48 | struct gpio_rcar_info info; |
51750fb1 | 49 | struct gpio_rcar_bank_info bank_info; |
119f5e44 MD |
50 | }; |
51 | ||
677d7d61 GU |
52 | #define IOINTSEL 0x00 /* General IO/Interrupt Switching Register */ |
53 | #define INOUTSEL 0x04 /* General Input/Output Switching Register */ | |
54 | #define OUTDT 0x08 /* General Output Register */ | |
55 | #define INDT 0x0c /* General Input Register */ | |
56 | #define INTDT 0x10 /* Interrupt Display Register */ | |
57 | #define INTCLR 0x14 /* Interrupt Clear Register */ | |
58 | #define INTMSK 0x18 /* Interrupt Mask Register */ | |
59 | #define MSKCLR 0x1c /* Interrupt Mask Clear Register */ | |
60 | #define POSNEG 0x20 /* Positive/Negative Logic Select Register */ | |
61 | #define EDGLEVEL 0x24 /* Edge/level Select Register */ | |
62 | #define FILONOFF 0x28 /* Chattering Prevention On/Off Register */ | |
63 | #define OUTDTSEL 0x40 /* Output Data Select Register */ | |
64 | #define BOTHEDGE 0x4c /* One Edge/Both Edge Select Register */ | |
93ac0b0c | 65 | #define INEN 0x50 /* General Input Enable Register */ |
119f5e44 | 66 | |
159f8a02 LP |
67 | #define RCAR_MAX_GPIO_PER_BANK 32 |
68 | ||
119f5e44 MD |
69 | static inline u32 gpio_rcar_read(struct gpio_rcar_priv *p, int offs) |
70 | { | |
71 | return ioread32(p->base + offs); | |
72 | } | |
73 | ||
74 | static inline void gpio_rcar_write(struct gpio_rcar_priv *p, int offs, | |
75 | u32 value) | |
76 | { | |
77 | iowrite32(value, p->base + offs); | |
78 | } | |
79 | ||
80 | static void gpio_rcar_modify_bit(struct gpio_rcar_priv *p, int offs, | |
81 | int bit, bool value) | |
82 | { | |
83 | u32 tmp = gpio_rcar_read(p, offs); | |
84 | ||
85 | if (value) | |
86 | tmp |= BIT(bit); | |
87 | else | |
88 | tmp &= ~BIT(bit); | |
89 | ||
90 | gpio_rcar_write(p, offs, tmp); | |
91 | } | |
92 | ||
93 | static void gpio_rcar_irq_disable(struct irq_data *d) | |
94 | { | |
c7f3c5d3 | 95 | struct gpio_chip *gc = irq_data_get_irq_chip_data(d); |
c7b6f457 | 96 | struct gpio_rcar_priv *p = gpiochip_get_data(gc); |
718b972d | 97 | irq_hw_number_t hwirq = irqd_to_hwirq(d); |
119f5e44 | 98 | |
718b972d GU |
99 | gpio_rcar_write(p, INTMSK, ~BIT(hwirq)); |
100 | gpiochip_disable_irq(gc, hwirq); | |
119f5e44 MD |
101 | } |
102 | ||
103 | static void gpio_rcar_irq_enable(struct irq_data *d) | |
104 | { | |
c7f3c5d3 | 105 | struct gpio_chip *gc = irq_data_get_irq_chip_data(d); |
c7b6f457 | 106 | struct gpio_rcar_priv *p = gpiochip_get_data(gc); |
718b972d | 107 | irq_hw_number_t hwirq = irqd_to_hwirq(d); |
119f5e44 | 108 | |
718b972d GU |
109 | gpiochip_enable_irq(gc, hwirq); |
110 | gpio_rcar_write(p, MSKCLR, BIT(hwirq)); | |
119f5e44 MD |
111 | } |
112 | ||
113 | static void gpio_rcar_config_interrupt_input_mode(struct gpio_rcar_priv *p, | |
114 | unsigned int hwirq, | |
115 | bool active_high_rising_edge, | |
7e1092b5 SH |
116 | bool level_trigger, |
117 | bool both) | |
119f5e44 MD |
118 | { |
119 | unsigned long flags; | |
120 | ||
121 | /* follow steps in the GPIO documentation for | |
122 | * "Setting Edge-Sensitive Interrupt Input Mode" and | |
123 | * "Setting Level-Sensitive Interrupt Input Mode" | |
124 | */ | |
125 | ||
f02c41f8 | 126 | raw_spin_lock_irqsave(&p->lock, flags); |
119f5e44 | 127 | |
b36368f6 | 128 | /* Configure positive or negative logic in POSNEG */ |
119f5e44 MD |
129 | gpio_rcar_modify_bit(p, POSNEG, hwirq, !active_high_rising_edge); |
130 | ||
131 | /* Configure edge or level trigger in EDGLEVEL */ | |
132 | gpio_rcar_modify_bit(p, EDGLEVEL, hwirq, !level_trigger); | |
133 | ||
7e1092b5 | 134 | /* Select one edge or both edges in BOTHEDGE */ |
208c80f1 | 135 | if (p->info.has_both_edge_trigger) |
7e1092b5 SH |
136 | gpio_rcar_modify_bit(p, BOTHEDGE, hwirq, both); |
137 | ||
119f5e44 MD |
138 | /* Select "Interrupt Input Mode" in IOINTSEL */ |
139 | gpio_rcar_modify_bit(p, IOINTSEL, hwirq, true); | |
140 | ||
141 | /* Write INTCLR in case of edge trigger */ | |
142 | if (!level_trigger) | |
143 | gpio_rcar_write(p, INTCLR, BIT(hwirq)); | |
144 | ||
f02c41f8 | 145 | raw_spin_unlock_irqrestore(&p->lock, flags); |
119f5e44 MD |
146 | } |
147 | ||
148 | static int gpio_rcar_irq_set_type(struct irq_data *d, unsigned int type) | |
149 | { | |
c7f3c5d3 | 150 | struct gpio_chip *gc = irq_data_get_irq_chip_data(d); |
c7b6f457 | 151 | struct gpio_rcar_priv *p = gpiochip_get_data(gc); |
119f5e44 MD |
152 | unsigned int hwirq = irqd_to_hwirq(d); |
153 | ||
a53f7953 | 154 | dev_dbg(p->dev, "sense irq = %d, type = %d\n", hwirq, type); |
119f5e44 MD |
155 | |
156 | switch (type & IRQ_TYPE_SENSE_MASK) { | |
157 | case IRQ_TYPE_LEVEL_HIGH: | |
7e1092b5 SH |
158 | gpio_rcar_config_interrupt_input_mode(p, hwirq, true, true, |
159 | false); | |
119f5e44 MD |
160 | break; |
161 | case IRQ_TYPE_LEVEL_LOW: | |
7e1092b5 SH |
162 | gpio_rcar_config_interrupt_input_mode(p, hwirq, false, true, |
163 | false); | |
119f5e44 MD |
164 | break; |
165 | case IRQ_TYPE_EDGE_RISING: | |
7e1092b5 SH |
166 | gpio_rcar_config_interrupt_input_mode(p, hwirq, true, false, |
167 | false); | |
119f5e44 MD |
168 | break; |
169 | case IRQ_TYPE_EDGE_FALLING: | |
7e1092b5 SH |
170 | gpio_rcar_config_interrupt_input_mode(p, hwirq, false, false, |
171 | false); | |
172 | break; | |
173 | case IRQ_TYPE_EDGE_BOTH: | |
208c80f1 | 174 | if (!p->info.has_both_edge_trigger) |
7e1092b5 SH |
175 | return -EINVAL; |
176 | gpio_rcar_config_interrupt_input_mode(p, hwirq, true, false, | |
177 | true); | |
119f5e44 MD |
178 | break; |
179 | default: | |
180 | return -EINVAL; | |
181 | } | |
182 | return 0; | |
183 | } | |
184 | ||
ab82fa7d GU |
185 | static int gpio_rcar_irq_set_wake(struct irq_data *d, unsigned int on) |
186 | { | |
187 | struct gpio_chip *gc = irq_data_get_irq_chip_data(d); | |
c7b6f457 | 188 | struct gpio_rcar_priv *p = gpiochip_get_data(gc); |
501ef0f9 GU |
189 | int error; |
190 | ||
191 | if (p->irq_parent) { | |
192 | error = irq_set_irq_wake(p->irq_parent, on); | |
193 | if (error) { | |
a53f7953 | 194 | dev_dbg(p->dev, "irq %u doesn't support irq_set_wake\n", |
501ef0f9 GU |
195 | p->irq_parent); |
196 | p->irq_parent = 0; | |
197 | } | |
198 | } | |
ab82fa7d | 199 | |
ab82fa7d | 200 | if (on) |
9ac79ba9 | 201 | atomic_inc(&p->wakeup_path); |
ab82fa7d | 202 | else |
9ac79ba9 | 203 | atomic_dec(&p->wakeup_path); |
ab82fa7d GU |
204 | |
205 | return 0; | |
206 | } | |
207 | ||
718b972d GU |
208 | static const struct irq_chip gpio_rcar_irq_chip = { |
209 | .name = "gpio-rcar", | |
210 | .irq_mask = gpio_rcar_irq_disable, | |
211 | .irq_unmask = gpio_rcar_irq_enable, | |
212 | .irq_set_type = gpio_rcar_irq_set_type, | |
213 | .irq_set_wake = gpio_rcar_irq_set_wake, | |
214 | .flags = IRQCHIP_IMMUTABLE | IRQCHIP_SET_TYPE_MASKED | | |
215 | IRQCHIP_MASK_ON_SUSPEND, | |
216 | GPIOCHIP_IRQ_RESOURCE_HELPERS, | |
217 | }; | |
218 | ||
119f5e44 MD |
219 | static irqreturn_t gpio_rcar_irq_handler(int irq, void *dev_id) |
220 | { | |
221 | struct gpio_rcar_priv *p = dev_id; | |
222 | u32 pending; | |
223 | unsigned int offset, irqs_handled = 0; | |
224 | ||
8808b64d VB |
225 | while ((pending = gpio_rcar_read(p, INTDT) & |
226 | gpio_rcar_read(p, INTMSK))) { | |
119f5e44 MD |
227 | offset = __ffs(pending); |
228 | gpio_rcar_write(p, INTCLR, BIT(offset)); | |
dbd1c54f MZ |
229 | generic_handle_domain_irq(p->gpio_chip.irq.domain, |
230 | offset); | |
119f5e44 MD |
231 | irqs_handled++; |
232 | } | |
233 | ||
234 | return irqs_handled ? IRQ_HANDLED : IRQ_NONE; | |
235 | } | |
236 | ||
119f5e44 MD |
237 | static void gpio_rcar_config_general_input_output_mode(struct gpio_chip *chip, |
238 | unsigned int gpio, | |
239 | bool output) | |
240 | { | |
c7b6f457 | 241 | struct gpio_rcar_priv *p = gpiochip_get_data(chip); |
119f5e44 MD |
242 | unsigned long flags; |
243 | ||
244 | /* follow steps in the GPIO documentation for | |
245 | * "Setting General Output Mode" and | |
246 | * "Setting General Input Mode" | |
247 | */ | |
248 | ||
f02c41f8 | 249 | raw_spin_lock_irqsave(&p->lock, flags); |
119f5e44 | 250 | |
b36368f6 | 251 | /* Configure positive logic in POSNEG */ |
119f5e44 MD |
252 | gpio_rcar_modify_bit(p, POSNEG, gpio, false); |
253 | ||
254 | /* Select "General Input/Output Mode" in IOINTSEL */ | |
255 | gpio_rcar_modify_bit(p, IOINTSEL, gpio, false); | |
256 | ||
257 | /* Select Input Mode or Output Mode in INOUTSEL */ | |
258 | gpio_rcar_modify_bit(p, INOUTSEL, gpio, output); | |
259 | ||
3ae4f3aa | 260 | /* Select General Output Register to output data in OUTDTSEL */ |
208c80f1 | 261 | if (p->info.has_outdtsel && output) |
3ae4f3aa VZ |
262 | gpio_rcar_modify_bit(p, OUTDTSEL, gpio, false); |
263 | ||
f02c41f8 | 264 | raw_spin_unlock_irqrestore(&p->lock, flags); |
119f5e44 MD |
265 | } |
266 | ||
dc3465a9 LP |
267 | static int gpio_rcar_request(struct gpio_chip *chip, unsigned offset) |
268 | { | |
2d65472b GU |
269 | struct gpio_rcar_priv *p = gpiochip_get_data(chip); |
270 | int error; | |
271 | ||
a53f7953 | 272 | error = pm_runtime_get_sync(p->dev); |
6f8cd246 DL |
273 | if (error < 0) { |
274 | pm_runtime_put(p->dev); | |
2d65472b | 275 | return error; |
6f8cd246 | 276 | } |
2d65472b | 277 | |
acb38be6 | 278 | error = pinctrl_gpio_request(chip, offset); |
2d65472b | 279 | if (error) |
a53f7953 | 280 | pm_runtime_put(p->dev); |
2d65472b GU |
281 | |
282 | return error; | |
dc3465a9 LP |
283 | } |
284 | ||
285 | static void gpio_rcar_free(struct gpio_chip *chip, unsigned offset) | |
286 | { | |
2d65472b GU |
287 | struct gpio_rcar_priv *p = gpiochip_get_data(chip); |
288 | ||
4fccb263 | 289 | pinctrl_gpio_free(chip, offset); |
dc3465a9 | 290 | |
ce0e2c60 LW |
291 | /* |
292 | * Set the GPIO as an input to ensure that the next GPIO request won't | |
dc3465a9 LP |
293 | * drive the GPIO pin as an output. |
294 | */ | |
295 | gpio_rcar_config_general_input_output_mode(chip, offset, false); | |
2d65472b | 296 | |
a53f7953 | 297 | pm_runtime_put(p->dev); |
dc3465a9 LP |
298 | } |
299 | ||
ad817297 GU |
300 | static int gpio_rcar_get_direction(struct gpio_chip *chip, unsigned int offset) |
301 | { | |
302 | struct gpio_rcar_priv *p = gpiochip_get_data(chip); | |
303 | ||
e42615ec MV |
304 | if (gpio_rcar_read(p, INOUTSEL) & BIT(offset)) |
305 | return GPIO_LINE_DIRECTION_OUT; | |
306 | ||
307 | return GPIO_LINE_DIRECTION_IN; | |
ad817297 GU |
308 | } |
309 | ||
119f5e44 MD |
310 | static int gpio_rcar_direction_input(struct gpio_chip *chip, unsigned offset) |
311 | { | |
312 | gpio_rcar_config_general_input_output_mode(chip, offset, false); | |
313 | return 0; | |
314 | } | |
315 | ||
316 | static int gpio_rcar_get(struct gpio_chip *chip, unsigned offset) | |
317 | { | |
714d3a29 | 318 | struct gpio_rcar_priv *p = gpiochip_get_data(chip); |
ae9550f6 MD |
319 | u32 bit = BIT(offset); |
320 | ||
ecba1eaa GU |
321 | /* |
322 | * Before R-Car Gen3, INDT does not show correct pin state when | |
323 | * configured as output, so use OUTDT in case of output pins | |
324 | */ | |
325 | if (!p->info.has_always_in && (gpio_rcar_read(p, INOUTSEL) & bit)) | |
714d3a29 | 326 | return !!(gpio_rcar_read(p, OUTDT) & bit); |
ae9550f6 | 327 | else |
714d3a29 | 328 | return !!(gpio_rcar_read(p, INDT) & bit); |
119f5e44 MD |
329 | } |
330 | ||
183245c4 GU |
331 | static int gpio_rcar_get_multiple(struct gpio_chip *chip, unsigned long *mask, |
332 | unsigned long *bits) | |
333 | { | |
334 | struct gpio_rcar_priv *p = gpiochip_get_data(chip); | |
335 | u32 bankmask, outputs, m, val = 0; | |
336 | unsigned long flags; | |
337 | ||
338 | bankmask = mask[0] & GENMASK(chip->ngpio - 1, 0); | |
183245c4 GU |
339 | if (!bankmask) |
340 | return 0; | |
341 | ||
ecba1eaa GU |
342 | if (p->info.has_always_in) { |
343 | bits[0] = gpio_rcar_read(p, INDT) & bankmask; | |
344 | return 0; | |
345 | } | |
346 | ||
f02c41f8 | 347 | raw_spin_lock_irqsave(&p->lock, flags); |
183245c4 GU |
348 | outputs = gpio_rcar_read(p, INOUTSEL); |
349 | m = outputs & bankmask; | |
350 | if (m) | |
351 | val |= gpio_rcar_read(p, OUTDT) & m; | |
352 | ||
353 | m = ~outputs & bankmask; | |
354 | if (m) | |
355 | val |= gpio_rcar_read(p, INDT) & m; | |
f02c41f8 | 356 | raw_spin_unlock_irqrestore(&p->lock, flags); |
183245c4 GU |
357 | |
358 | bits[0] = val; | |
359 | return 0; | |
360 | } | |
361 | ||
119f5e44 MD |
362 | static void gpio_rcar_set(struct gpio_chip *chip, unsigned offset, int value) |
363 | { | |
c7b6f457 | 364 | struct gpio_rcar_priv *p = gpiochip_get_data(chip); |
119f5e44 MD |
365 | unsigned long flags; |
366 | ||
f02c41f8 | 367 | raw_spin_lock_irqsave(&p->lock, flags); |
119f5e44 | 368 | gpio_rcar_modify_bit(p, OUTDT, offset, value); |
f02c41f8 | 369 | raw_spin_unlock_irqrestore(&p->lock, flags); |
119f5e44 MD |
370 | } |
371 | ||
dbb763b8 GU |
372 | static void gpio_rcar_set_multiple(struct gpio_chip *chip, unsigned long *mask, |
373 | unsigned long *bits) | |
374 | { | |
375 | struct gpio_rcar_priv *p = gpiochip_get_data(chip); | |
376 | unsigned long flags; | |
377 | u32 val, bankmask; | |
378 | ||
379 | bankmask = mask[0] & GENMASK(chip->ngpio - 1, 0); | |
380 | if (!bankmask) | |
381 | return; | |
382 | ||
f02c41f8 | 383 | raw_spin_lock_irqsave(&p->lock, flags); |
dbb763b8 GU |
384 | val = gpio_rcar_read(p, OUTDT); |
385 | val &= ~bankmask; | |
386 | val |= (bankmask & bits[0]); | |
387 | gpio_rcar_write(p, OUTDT, val); | |
f02c41f8 | 388 | raw_spin_unlock_irqrestore(&p->lock, flags); |
dbb763b8 GU |
389 | } |
390 | ||
119f5e44 MD |
391 | static int gpio_rcar_direction_output(struct gpio_chip *chip, unsigned offset, |
392 | int value) | |
393 | { | |
394 | /* write GPIO value to output before selecting output mode of pin */ | |
395 | gpio_rcar_set(chip, offset, value); | |
396 | gpio_rcar_config_general_input_output_mode(chip, offset, true); | |
397 | return 0; | |
398 | } | |
399 | ||
1fd2b49d | 400 | static const struct gpio_rcar_info gpio_rcar_info_gen1 = { |
3ae4f3aa | 401 | .has_outdtsel = false, |
1fd2b49d | 402 | .has_both_edge_trigger = false, |
ecba1eaa | 403 | .has_always_in = false, |
93ac0b0c | 404 | .has_inen = false, |
1fd2b49d HN |
405 | }; |
406 | ||
407 | static const struct gpio_rcar_info gpio_rcar_info_gen2 = { | |
3ae4f3aa | 408 | .has_outdtsel = true, |
1fd2b49d | 409 | .has_both_edge_trigger = true, |
ecba1eaa | 410 | .has_always_in = false, |
93ac0b0c | 411 | .has_inen = false, |
ecba1eaa GU |
412 | }; |
413 | ||
414 | static const struct gpio_rcar_info gpio_rcar_info_gen3 = { | |
415 | .has_outdtsel = true, | |
416 | .has_both_edge_trigger = true, | |
417 | .has_always_in = true, | |
93ac0b0c GU |
418 | .has_inen = false, |
419 | }; | |
420 | ||
43ebbb92 | 421 | static const struct gpio_rcar_info gpio_rcar_info_gen4 = { |
93ac0b0c GU |
422 | .has_outdtsel = true, |
423 | .has_both_edge_trigger = true, | |
424 | .has_always_in = true, | |
425 | .has_inen = true, | |
1fd2b49d HN |
426 | }; |
427 | ||
850dfe17 LP |
428 | static const struct of_device_id gpio_rcar_of_table[] = { |
429 | { | |
93ac0b0c | 430 | .compatible = "renesas,gpio-r8a779a0", |
43ebbb92 | 431 | .data = &gpio_rcar_info_gen4, |
93ac0b0c | 432 | }, { |
dbd1dad2 SH |
433 | .compatible = "renesas,rcar-gen1-gpio", |
434 | .data = &gpio_rcar_info_gen1, | |
435 | }, { | |
436 | .compatible = "renesas,rcar-gen2-gpio", | |
437 | .data = &gpio_rcar_info_gen2, | |
438 | }, { | |
439 | .compatible = "renesas,rcar-gen3-gpio", | |
ecba1eaa | 440 | .data = &gpio_rcar_info_gen3, |
43ebbb92 GU |
441 | }, { |
442 | .compatible = "renesas,rcar-gen4-gpio", | |
443 | .data = &gpio_rcar_info_gen4, | |
850dfe17 LP |
444 | }, { |
445 | .compatible = "renesas,gpio-rcar", | |
1fd2b49d | 446 | .data = &gpio_rcar_info_gen1, |
850dfe17 LP |
447 | }, { |
448 | /* Terminator */ | |
449 | }, | |
450 | }; | |
451 | ||
452 | MODULE_DEVICE_TABLE(of, gpio_rcar_of_table); | |
453 | ||
8b092be9 | 454 | static int gpio_rcar_parse_dt(struct gpio_rcar_priv *p, unsigned int *npins) |
159f8a02 | 455 | { |
a53f7953 | 456 | struct device_node *np = p->dev->of_node; |
8b092be9 | 457 | const struct gpio_rcar_info *info; |
159f8a02 LP |
458 | struct of_phandle_args args; |
459 | int ret; | |
159f8a02 | 460 | |
a53f7953 | 461 | info = of_device_get_match_data(p->dev); |
208c80f1 | 462 | p->info = *info; |
850dfe17 | 463 | |
8b092be9 | 464 | ret = of_parse_phandle_with_fixed_args(np, "gpio-ranges", 3, 0, &args); |
391b41f9 FC |
465 | if (ret) { |
466 | *npins = RCAR_MAX_GPIO_PER_BANK; | |
467 | } else { | |
468 | *npins = args.args[2]; | |
469 | of_node_put(args.np); | |
470 | } | |
850dfe17 | 471 | |
8b092be9 | 472 | if (*npins == 0 || *npins > RCAR_MAX_GPIO_PER_BANK) { |
a53f7953 VZ |
473 | dev_warn(p->dev, "Invalid number of gpio lines %u, using %u\n", |
474 | *npins, RCAR_MAX_GPIO_PER_BANK); | |
8b092be9 | 475 | *npins = RCAR_MAX_GPIO_PER_BANK; |
159f8a02 | 476 | } |
850dfe17 LP |
477 | |
478 | return 0; | |
159f8a02 LP |
479 | } |
480 | ||
93ac0b0c GU |
481 | static void gpio_rcar_enable_inputs(struct gpio_rcar_priv *p) |
482 | { | |
483 | u32 mask = GENMASK(p->gpio_chip.ngpio - 1, 0); | |
43b665c9 MV |
484 | const unsigned long *valid_mask; |
485 | ||
486 | valid_mask = gpiochip_query_valid_mask(&p->gpio_chip); | |
93ac0b0c GU |
487 | |
488 | /* Select "Input Enable" in INEN */ | |
43b665c9 MV |
489 | if (valid_mask) |
490 | mask &= valid_mask[0]; | |
93ac0b0c GU |
491 | if (mask) |
492 | gpio_rcar_write(p, INEN, gpio_rcar_read(p, INEN) | mask); | |
493 | } | |
494 | ||
119f5e44 MD |
495 | static int gpio_rcar_probe(struct platform_device *pdev) |
496 | { | |
119f5e44 | 497 | struct gpio_rcar_priv *p; |
119f5e44 | 498 | struct gpio_chip *gpio_chip; |
b470cef1 | 499 | struct gpio_irq_chip *girq; |
b22978fc GU |
500 | struct device *dev = &pdev->dev; |
501 | const char *name = dev_name(dev); | |
8b092be9 | 502 | unsigned int npins; |
119f5e44 MD |
503 | int ret; |
504 | ||
b22978fc | 505 | p = devm_kzalloc(dev, sizeof(*p), GFP_KERNEL); |
7d82bf34 GU |
506 | if (!p) |
507 | return -ENOMEM; | |
119f5e44 | 508 | |
a53f7953 | 509 | p->dev = dev; |
f02c41f8 | 510 | raw_spin_lock_init(&p->lock); |
119f5e44 | 511 | |
8b092be9 GU |
512 | /* Get device configuration from DT node */ |
513 | ret = gpio_rcar_parse_dt(p, &npins); | |
850dfe17 LP |
514 | if (ret < 0) |
515 | return ret; | |
159f8a02 LP |
516 | |
517 | platform_set_drvdata(pdev, p); | |
518 | ||
df0c6c80 | 519 | pm_runtime_enable(dev); |
df0c6c80 | 520 | |
f1ff272c LP |
521 | ret = platform_get_irq(pdev, 0); |
522 | if (ret < 0) | |
119f5e44 | 523 | goto err0; |
f1ff272c | 524 | p->irq_parent = ret; |
119f5e44 | 525 | |
ecbf7c2e | 526 | p->base = devm_platform_ioremap_resource(pdev, 0); |
5a24d4b6 SS |
527 | if (IS_ERR(p->base)) { |
528 | ret = PTR_ERR(p->base); | |
119f5e44 MD |
529 | goto err0; |
530 | } | |
531 | ||
532 | gpio_chip = &p->gpio_chip; | |
dc3465a9 LP |
533 | gpio_chip->request = gpio_rcar_request; |
534 | gpio_chip->free = gpio_rcar_free; | |
ad817297 | 535 | gpio_chip->get_direction = gpio_rcar_get_direction; |
119f5e44 MD |
536 | gpio_chip->direction_input = gpio_rcar_direction_input; |
537 | gpio_chip->get = gpio_rcar_get; | |
183245c4 | 538 | gpio_chip->get_multiple = gpio_rcar_get_multiple; |
119f5e44 MD |
539 | gpio_chip->direction_output = gpio_rcar_direction_output; |
540 | gpio_chip->set = gpio_rcar_set; | |
dbb763b8 | 541 | gpio_chip->set_multiple = gpio_rcar_set_multiple; |
119f5e44 | 542 | gpio_chip->label = name; |
58383c78 | 543 | gpio_chip->parent = dev; |
119f5e44 | 544 | gpio_chip->owner = THIS_MODULE; |
8b092be9 GU |
545 | gpio_chip->base = -1; |
546 | gpio_chip->ngpio = npins; | |
119f5e44 | 547 | |
b470cef1 | 548 | girq = &gpio_chip->irq; |
718b972d | 549 | gpio_irq_chip_set_chip(girq, &gpio_rcar_irq_chip); |
b470cef1 LW |
550 | /* This will let us handle the parent IRQ in the driver */ |
551 | girq->parent_handler = NULL; | |
552 | girq->num_parents = 0; | |
553 | girq->parents = NULL; | |
554 | girq->default_type = IRQ_TYPE_NONE; | |
555 | girq->handler = handle_level_irq; | |
556 | ||
c7b6f457 | 557 | ret = gpiochip_add_data(gpio_chip, p); |
c7f3c5d3 GU |
558 | if (ret) { |
559 | dev_err(dev, "failed to add GPIO controller\n"); | |
0c8aab8e | 560 | goto err0; |
119f5e44 MD |
561 | } |
562 | ||
373d664b | 563 | irq_domain_set_pm_device(gpio_chip->irq.domain, dev); |
ffe31c9e LP |
564 | ret = devm_request_irq(dev, p->irq_parent, gpio_rcar_irq_handler, |
565 | IRQF_SHARED, name, p); | |
566 | if (ret) { | |
b22978fc | 567 | dev_err(dev, "failed to request IRQ\n"); |
119f5e44 MD |
568 | goto err1; |
569 | } | |
570 | ||
93ac0b0c | 571 | if (p->info.has_inen) { |
3d134e75 | 572 | pm_runtime_get_sync(dev); |
93ac0b0c | 573 | gpio_rcar_enable_inputs(p); |
3d134e75 | 574 | pm_runtime_put(dev); |
93ac0b0c GU |
575 | } |
576 | ||
8b092be9 | 577 | dev_info(dev, "driving %d GPIOs\n", npins); |
dc3465a9 | 578 | |
119f5e44 MD |
579 | return 0; |
580 | ||
581 | err1: | |
4d84b9e4 | 582 | gpiochip_remove(gpio_chip); |
119f5e44 | 583 | err0: |
df0c6c80 | 584 | pm_runtime_disable(dev); |
119f5e44 MD |
585 | return ret; |
586 | } | |
587 | ||
31d81084 | 588 | static void gpio_rcar_remove(struct platform_device *pdev) |
119f5e44 MD |
589 | { |
590 | struct gpio_rcar_priv *p = platform_get_drvdata(pdev); | |
119f5e44 | 591 | |
9f5132ae | 592 | gpiochip_remove(&p->gpio_chip); |
119f5e44 | 593 | |
df0c6c80 | 594 | pm_runtime_disable(&pdev->dev); |
119f5e44 MD |
595 | } |
596 | ||
51750fb1 HD |
597 | #ifdef CONFIG_PM_SLEEP |
598 | static int gpio_rcar_suspend(struct device *dev) | |
599 | { | |
600 | struct gpio_rcar_priv *p = dev_get_drvdata(dev); | |
601 | ||
602 | p->bank_info.iointsel = gpio_rcar_read(p, IOINTSEL); | |
603 | p->bank_info.inoutsel = gpio_rcar_read(p, INOUTSEL); | |
604 | p->bank_info.outdt = gpio_rcar_read(p, OUTDT); | |
605 | p->bank_info.intmsk = gpio_rcar_read(p, INTMSK); | |
606 | p->bank_info.posneg = gpio_rcar_read(p, POSNEG); | |
607 | p->bank_info.edglevel = gpio_rcar_read(p, EDGLEVEL); | |
208c80f1 | 608 | if (p->info.has_both_edge_trigger) |
51750fb1 HD |
609 | p->bank_info.bothedge = gpio_rcar_read(p, BOTHEDGE); |
610 | ||
9ac79ba9 GU |
611 | if (atomic_read(&p->wakeup_path)) |
612 | device_set_wakeup_path(dev); | |
613 | ||
51750fb1 HD |
614 | return 0; |
615 | } | |
616 | ||
617 | static int gpio_rcar_resume(struct device *dev) | |
618 | { | |
619 | struct gpio_rcar_priv *p = dev_get_drvdata(dev); | |
620 | unsigned int offset; | |
621 | u32 mask; | |
622 | ||
623 | for (offset = 0; offset < p->gpio_chip.ngpio; offset++) { | |
496069b8 BD |
624 | if (!gpiochip_line_is_valid(&p->gpio_chip, offset)) |
625 | continue; | |
626 | ||
51750fb1 HD |
627 | mask = BIT(offset); |
628 | /* I/O pin */ | |
629 | if (!(p->bank_info.iointsel & mask)) { | |
630 | if (p->bank_info.inoutsel & mask) | |
631 | gpio_rcar_direction_output( | |
632 | &p->gpio_chip, offset, | |
633 | !!(p->bank_info.outdt & mask)); | |
634 | else | |
635 | gpio_rcar_direction_input(&p->gpio_chip, | |
636 | offset); | |
637 | } else { | |
638 | /* Interrupt pin */ | |
639 | gpio_rcar_config_interrupt_input_mode( | |
640 | p, | |
641 | offset, | |
642 | !(p->bank_info.posneg & mask), | |
643 | !(p->bank_info.edglevel & mask), | |
644 | !!(p->bank_info.bothedge & mask)); | |
645 | ||
646 | if (p->bank_info.intmsk & mask) | |
647 | gpio_rcar_write(p, MSKCLR, mask); | |
648 | } | |
649 | } | |
650 | ||
93ac0b0c GU |
651 | if (p->info.has_inen) |
652 | gpio_rcar_enable_inputs(p); | |
653 | ||
51750fb1 HD |
654 | return 0; |
655 | } | |
656 | #endif /* CONFIG_PM_SLEEP*/ | |
657 | ||
658 | static SIMPLE_DEV_PM_OPS(gpio_rcar_pm_ops, gpio_rcar_suspend, gpio_rcar_resume); | |
659 | ||
119f5e44 MD |
660 | static struct platform_driver gpio_rcar_device_driver = { |
661 | .probe = gpio_rcar_probe, | |
678eefc1 | 662 | .remove = gpio_rcar_remove, |
119f5e44 MD |
663 | .driver = { |
664 | .name = "gpio_rcar", | |
51750fb1 | 665 | .pm = &gpio_rcar_pm_ops, |
072de5a4 | 666 | .of_match_table = gpio_rcar_of_table, |
119f5e44 MD |
667 | } |
668 | }; | |
669 | ||
670 | module_platform_driver(gpio_rcar_device_driver); | |
671 | ||
672 | MODULE_AUTHOR("Magnus Damm"); | |
673 | MODULE_DESCRIPTION("Renesas R-Car GPIO Driver"); | |
674 | MODULE_LICENSE("GPL v2"); |