Commit | Line | Data |
---|---|---|
221173a3 KK |
1 | // SPDX-License-Identifier: GPL-2.0+ |
2 | // | |
3 | // Exynos specific support for Samsung pinctrl/gpiolib driver with eint support. | |
4 | // | |
5 | // Copyright (c) 2012 Samsung Electronics Co., Ltd. | |
6 | // http://www.samsung.com | |
7 | // Copyright (c) 2012 Linaro Ltd | |
8 | // http://www.linaro.org | |
9 | // | |
10 | // Author: Thomas Abraham <thomas.ab@samsung.com> | |
11 | // | |
12 | // This file contains the Samsung Exynos specific information required by the | |
13 | // the Samsung pinctrl/gpiolib driver. It also includes the implementation of | |
14 | // external gpio and wakeup interrupt support. | |
43b169db | 15 | |
f9c74474 | 16 | #include <linux/clk.h> |
43b169db TA |
17 | #include <linux/device.h> |
18 | #include <linux/interrupt.h> | |
19 | #include <linux/irqdomain.h> | |
20 | #include <linux/irq.h> | |
de88cbb7 | 21 | #include <linux/irqchip/chained_irq.h> |
cfa76ddf | 22 | #include <linux/of.h> |
43b169db | 23 | #include <linux/of_irq.h> |
43b169db | 24 | #include <linux/slab.h> |
19846950 | 25 | #include <linux/spinlock.h> |
07731019 | 26 | #include <linux/regmap.h> |
43b169db | 27 | #include <linux/err.h> |
07731019 | 28 | #include <linux/soc/samsung/exynos-pmu.h> |
a8be2af0 | 29 | #include <linux/soc/samsung/exynos-regs-pmu.h> |
43b169db | 30 | |
43b169db TA |
31 | #include "pinctrl-samsung.h" |
32 | #include "pinctrl-exynos.h" | |
33 | ||
2e4a4fda TF |
34 | struct exynos_irq_chip { |
35 | struct irq_chip chip; | |
36 | ||
37 | u32 eint_con; | |
38 | u32 eint_mask; | |
39 | u32 eint_pend; | |
85745c87 | 40 | u32 *eint_wake_mask_value; |
a8be2af0 | 41 | u32 eint_wake_mask_reg; |
b577a279 JB |
42 | void (*set_eint_wakeup_mask)(struct samsung_pinctrl_drv_data *drvdata, |
43 | struct exynos_irq_chip *irq_chip); | |
2e4a4fda TF |
44 | }; |
45 | ||
46 | static inline struct exynos_irq_chip *to_exynos_irq_chip(struct irq_chip *chip) | |
47 | { | |
48 | return container_of(chip, struct exynos_irq_chip, chip); | |
49 | } | |
499147c9 | 50 | |
2e4a4fda | 51 | static void exynos_irq_mask(struct irq_data *irqd) |
43b169db | 52 | { |
2e4a4fda TF |
53 | struct irq_chip *chip = irq_data_get_irq_chip(irqd); |
54 | struct exynos_irq_chip *our_chip = to_exynos_irq_chip(chip); | |
595be726 | 55 | struct samsung_pin_bank *bank = irq_data_get_irq_chip_data(irqd); |
884fdaa5 | 56 | unsigned long reg_mask; |
fa0c10a5 | 57 | unsigned int mask; |
5ae8cf79 DA |
58 | unsigned long flags; |
59 | ||
884fdaa5 JK |
60 | if (bank->eint_mask_offset) |
61 | reg_mask = bank->pctl_offset + bank->eint_mask_offset; | |
62 | else | |
63 | reg_mask = our_chip->eint_mask + bank->eint_offset; | |
64 | ||
f9c74474 AD |
65 | if (clk_enable(bank->drvdata->pclk)) { |
66 | dev_err(bank->gpio_chip.parent, | |
67 | "unable to enable clock for masking IRQ\n"); | |
68 | return; | |
69 | } | |
70 | ||
1f306ecb | 71 | raw_spin_lock_irqsave(&bank->slock, flags); |
43b169db | 72 | |
8b1bd11c | 73 | mask = readl(bank->eint_base + reg_mask); |
5ace03fb | 74 | mask |= 1 << irqd->hwirq; |
8b1bd11c | 75 | writel(mask, bank->eint_base + reg_mask); |
5ae8cf79 | 76 | |
1f306ecb | 77 | raw_spin_unlock_irqrestore(&bank->slock, flags); |
f9c74474 AD |
78 | |
79 | clk_disable(bank->drvdata->pclk); | |
43b169db TA |
80 | } |
81 | ||
2e4a4fda | 82 | static void exynos_irq_ack(struct irq_data *irqd) |
43b169db | 83 | { |
2e4a4fda TF |
84 | struct irq_chip *chip = irq_data_get_irq_chip(irqd); |
85 | struct exynos_irq_chip *our_chip = to_exynos_irq_chip(chip); | |
595be726 | 86 | struct samsung_pin_bank *bank = irq_data_get_irq_chip_data(irqd); |
884fdaa5 JK |
87 | unsigned long reg_pend; |
88 | ||
89 | if (bank->eint_pend_offset) | |
90 | reg_pend = bank->pctl_offset + bank->eint_pend_offset; | |
91 | else | |
92 | reg_pend = our_chip->eint_pend + bank->eint_offset; | |
43b169db | 93 | |
f9c74474 AD |
94 | if (clk_enable(bank->drvdata->pclk)) { |
95 | dev_err(bank->gpio_chip.parent, | |
96 | "unable to enable clock to ack IRQ\n"); | |
97 | return; | |
98 | } | |
99 | ||
8b1bd11c | 100 | writel(1 << irqd->hwirq, bank->eint_base + reg_pend); |
f9c74474 AD |
101 | |
102 | clk_disable(bank->drvdata->pclk); | |
43b169db TA |
103 | } |
104 | ||
2e4a4fda | 105 | static void exynos_irq_unmask(struct irq_data *irqd) |
43b169db | 106 | { |
2e4a4fda TF |
107 | struct irq_chip *chip = irq_data_get_irq_chip(irqd); |
108 | struct exynos_irq_chip *our_chip = to_exynos_irq_chip(chip); | |
595be726 | 109 | struct samsung_pin_bank *bank = irq_data_get_irq_chip_data(irqd); |
884fdaa5 | 110 | unsigned long reg_mask; |
fa0c10a5 | 111 | unsigned int mask; |
5ae8cf79 | 112 | unsigned long flags; |
43b169db | 113 | |
5a68e7a7 DA |
114 | /* |
115 | * Ack level interrupts right before unmask | |
116 | * | |
117 | * If we don't do this we'll get a double-interrupt. Level triggered | |
118 | * interrupts must not fire an interrupt if the level is not | |
119 | * _currently_ active, even if it was active while the interrupt was | |
120 | * masked. | |
121 | */ | |
122 | if (irqd_get_trigger_type(irqd) & IRQ_TYPE_LEVEL_MASK) | |
2e4a4fda | 123 | exynos_irq_ack(irqd); |
5a68e7a7 | 124 | |
884fdaa5 JK |
125 | if (bank->eint_mask_offset) |
126 | reg_mask = bank->pctl_offset + bank->eint_mask_offset; | |
127 | else | |
128 | reg_mask = our_chip->eint_mask + bank->eint_offset; | |
129 | ||
f9c74474 AD |
130 | if (clk_enable(bank->drvdata->pclk)) { |
131 | dev_err(bank->gpio_chip.parent, | |
132 | "unable to enable clock for unmasking IRQ\n"); | |
133 | return; | |
134 | } | |
135 | ||
1f306ecb | 136 | raw_spin_lock_irqsave(&bank->slock, flags); |
43b169db | 137 | |
8b1bd11c | 138 | mask = readl(bank->eint_base + reg_mask); |
5ace03fb | 139 | mask &= ~(1 << irqd->hwirq); |
8b1bd11c | 140 | writel(mask, bank->eint_base + reg_mask); |
5ae8cf79 | 141 | |
1f306ecb | 142 | raw_spin_unlock_irqrestore(&bank->slock, flags); |
f9c74474 AD |
143 | |
144 | clk_disable(bank->drvdata->pclk); | |
43b169db TA |
145 | } |
146 | ||
2e4a4fda | 147 | static int exynos_irq_set_type(struct irq_data *irqd, unsigned int type) |
43b169db | 148 | { |
2e4a4fda TF |
149 | struct irq_chip *chip = irq_data_get_irq_chip(irqd); |
150 | struct exynos_irq_chip *our_chip = to_exynos_irq_chip(chip); | |
595be726 | 151 | struct samsung_pin_bank *bank = irq_data_get_irq_chip_data(irqd); |
f6a8249f | 152 | unsigned int shift = EXYNOS_EINT_CON_LEN * irqd->hwirq; |
43b169db | 153 | unsigned int con, trig_type; |
884fdaa5 | 154 | unsigned long reg_con; |
f9c74474 | 155 | int ret; |
43b169db TA |
156 | |
157 | switch (type) { | |
158 | case IRQ_TYPE_EDGE_RISING: | |
159 | trig_type = EXYNOS_EINT_EDGE_RISING; | |
160 | break; | |
161 | case IRQ_TYPE_EDGE_FALLING: | |
162 | trig_type = EXYNOS_EINT_EDGE_FALLING; | |
163 | break; | |
164 | case IRQ_TYPE_EDGE_BOTH: | |
165 | trig_type = EXYNOS_EINT_EDGE_BOTH; | |
166 | break; | |
167 | case IRQ_TYPE_LEVEL_HIGH: | |
168 | trig_type = EXYNOS_EINT_LEVEL_HIGH; | |
169 | break; | |
170 | case IRQ_TYPE_LEVEL_LOW: | |
171 | trig_type = EXYNOS_EINT_LEVEL_LOW; | |
172 | break; | |
173 | default: | |
174 | pr_err("unsupported external interrupt type\n"); | |
175 | return -EINVAL; | |
176 | } | |
177 | ||
178 | if (type & IRQ_TYPE_EDGE_BOTH) | |
40ec168a | 179 | irq_set_handler_locked(irqd, handle_edge_irq); |
43b169db | 180 | else |
40ec168a | 181 | irq_set_handler_locked(irqd, handle_level_irq); |
43b169db | 182 | |
884fdaa5 JK |
183 | if (bank->eint_con_offset) |
184 | reg_con = bank->pctl_offset + bank->eint_con_offset; | |
185 | else | |
186 | reg_con = our_chip->eint_con + bank->eint_offset; | |
187 | ||
f9c74474 AD |
188 | ret = clk_enable(bank->drvdata->pclk); |
189 | if (ret) { | |
190 | dev_err(bank->gpio_chip.parent, | |
191 | "unable to enable clock for configuring IRQ type\n"); | |
192 | return ret; | |
193 | } | |
194 | ||
8b1bd11c | 195 | con = readl(bank->eint_base + reg_con); |
43b169db TA |
196 | con &= ~(EXYNOS_EINT_CON_MASK << shift); |
197 | con |= trig_type << shift; | |
8b1bd11c | 198 | writel(con, bank->eint_base + reg_con); |
ee2f573c | 199 | |
f9c74474 AD |
200 | clk_disable(bank->drvdata->pclk); |
201 | ||
f6a8249f TF |
202 | return 0; |
203 | } | |
204 | ||
b77f5ef8 YN |
205 | static int exynos_irq_set_affinity(struct irq_data *irqd, |
206 | const struct cpumask *dest, bool force) | |
207 | { | |
208 | struct samsung_pin_bank *bank = irq_data_get_irq_chip_data(irqd); | |
209 | struct samsung_pinctrl_drv_data *d = bank->drvdata; | |
210 | struct irq_data *parent = irq_get_irq_data(d->irq); | |
211 | ||
212 | if (parent) | |
213 | return parent->chip->irq_set_affinity(parent, dest, force); | |
214 | ||
215 | return -EINVAL; | |
216 | } | |
217 | ||
f6a8249f TF |
218 | static int exynos_irq_request_resources(struct irq_data *irqd) |
219 | { | |
f6a8249f | 220 | struct samsung_pin_bank *bank = irq_data_get_irq_chip_data(irqd); |
94ce944b | 221 | const struct samsung_pin_bank_type *bank_type = bank->type; |
bbed85f4 KK |
222 | unsigned long reg_con, flags; |
223 | unsigned int shift, mask, con; | |
f6a8249f TF |
224 | int ret; |
225 | ||
e3a2e878 | 226 | ret = gpiochip_lock_as_irq(&bank->gpio_chip, irqd->hwirq); |
f6a8249f | 227 | if (ret) { |
58383c78 LW |
228 | dev_err(bank->gpio_chip.parent, |
229 | "unable to lock pin %s-%lu IRQ\n", | |
f6a8249f TF |
230 | bank->name, irqd->hwirq); |
231 | return ret; | |
232 | } | |
233 | ||
43fc9e7f | 234 | reg_con = bank->pctl_offset + bank_type->reg_offset[PINCFG_TYPE_FUNC]; |
f6a8249f | 235 | shift = irqd->hwirq * bank_type->fld_width[PINCFG_TYPE_FUNC]; |
499147c9 | 236 | mask = (1 << bank_type->fld_width[PINCFG_TYPE_FUNC]) - 1; |
ee2f573c | 237 | |
f9c74474 AD |
238 | ret = clk_enable(bank->drvdata->pclk); |
239 | if (ret) { | |
240 | dev_err(bank->gpio_chip.parent, | |
241 | "unable to enable clock for configuring pin %s-%lu\n", | |
242 | bank->name, irqd->hwirq); | |
243 | return ret; | |
244 | } | |
245 | ||
1f306ecb | 246 | raw_spin_lock_irqsave(&bank->slock, flags); |
19846950 | 247 | |
af0b0baa | 248 | con = readl(bank->pctl_base + reg_con); |
ee2f573c | 249 | con &= ~(mask << shift); |
3eb12bce | 250 | con |= EXYNOS_PIN_CON_FUNC_EINT << shift; |
af0b0baa | 251 | writel(con, bank->pctl_base + reg_con); |
ee2f573c | 252 | |
1f306ecb | 253 | raw_spin_unlock_irqrestore(&bank->slock, flags); |
19846950 | 254 | |
f9c74474 AD |
255 | clk_disable(bank->drvdata->pclk); |
256 | ||
43b169db TA |
257 | return 0; |
258 | } | |
259 | ||
f6a8249f TF |
260 | static void exynos_irq_release_resources(struct irq_data *irqd) |
261 | { | |
f6a8249f | 262 | struct samsung_pin_bank *bank = irq_data_get_irq_chip_data(irqd); |
94ce944b | 263 | const struct samsung_pin_bank_type *bank_type = bank->type; |
bbed85f4 KK |
264 | unsigned long reg_con, flags; |
265 | unsigned int shift, mask, con; | |
f6a8249f TF |
266 | |
267 | reg_con = bank->pctl_offset + bank_type->reg_offset[PINCFG_TYPE_FUNC]; | |
268 | shift = irqd->hwirq * bank_type->fld_width[PINCFG_TYPE_FUNC]; | |
269 | mask = (1 << bank_type->fld_width[PINCFG_TYPE_FUNC]) - 1; | |
270 | ||
f9c74474 AD |
271 | if (clk_enable(bank->drvdata->pclk)) { |
272 | dev_err(bank->gpio_chip.parent, | |
273 | "unable to enable clock for deconfiguring pin %s-%lu\n", | |
274 | bank->name, irqd->hwirq); | |
275 | return; | |
276 | } | |
277 | ||
1f306ecb | 278 | raw_spin_lock_irqsave(&bank->slock, flags); |
f6a8249f | 279 | |
af0b0baa | 280 | con = readl(bank->pctl_base + reg_con); |
f6a8249f | 281 | con &= ~(mask << shift); |
3eb12bce | 282 | con |= PIN_CON_FUNC_INPUT << shift; |
af0b0baa | 283 | writel(con, bank->pctl_base + reg_con); |
f6a8249f | 284 | |
1f306ecb | 285 | raw_spin_unlock_irqrestore(&bank->slock, flags); |
f6a8249f | 286 | |
f9c74474 AD |
287 | clk_disable(bank->drvdata->pclk); |
288 | ||
e3a2e878 | 289 | gpiochip_unlock_as_irq(&bank->gpio_chip, irqd->hwirq); |
f6a8249f TF |
290 | } |
291 | ||
43b169db TA |
292 | /* |
293 | * irq_chip for gpio interrupts. | |
294 | */ | |
85745c87 | 295 | static const struct exynos_irq_chip exynos_gpio_irq_chip __initconst = { |
2e4a4fda TF |
296 | .chip = { |
297 | .name = "exynos_gpio_irq_chip", | |
298 | .irq_unmask = exynos_irq_unmask, | |
299 | .irq_mask = exynos_irq_mask, | |
300 | .irq_ack = exynos_irq_ack, | |
301 | .irq_set_type = exynos_irq_set_type, | |
b77f5ef8 | 302 | .irq_set_affinity = exynos_irq_set_affinity, |
f6a8249f TF |
303 | .irq_request_resources = exynos_irq_request_resources, |
304 | .irq_release_resources = exynos_irq_release_resources, | |
2e4a4fda TF |
305 | }, |
306 | .eint_con = EXYNOS_GPIO_ECON_OFFSET, | |
307 | .eint_mask = EXYNOS_GPIO_EMASK_OFFSET, | |
308 | .eint_pend = EXYNOS_GPIO_EPEND_OFFSET, | |
a8be2af0 | 309 | /* eint_wake_mask_value not used */ |
43b169db TA |
310 | }; |
311 | ||
6f5e41bd | 312 | static int exynos_eint_irq_map(struct irq_domain *h, unsigned int virq, |
43b169db TA |
313 | irq_hw_number_t hw) |
314 | { | |
595be726 | 315 | struct samsung_pin_bank *b = h->host_data; |
43b169db | 316 | |
595be726 | 317 | irq_set_chip_data(virq, b); |
0d3d30db | 318 | irq_set_chip_and_handler(virq, &b->irq_chip->chip, |
43b169db | 319 | handle_level_irq); |
43b169db TA |
320 | return 0; |
321 | } | |
322 | ||
43b169db | 323 | /* |
6f5e41bd | 324 | * irq domain callbacks for external gpio and wakeup interrupt controllers. |
43b169db | 325 | */ |
6f5e41bd AK |
326 | static const struct irq_domain_ops exynos_eint_irqd_ops = { |
327 | .map = exynos_eint_irq_map, | |
43b169db TA |
328 | .xlate = irq_domain_xlate_twocell, |
329 | }; | |
330 | ||
331 | static irqreturn_t exynos_eint_gpio_irq(int irq, void *data) | |
332 | { | |
333 | struct samsung_pinctrl_drv_data *d = data; | |
1bf00d7a | 334 | struct samsung_pin_bank *bank = d->pin_banks; |
a9cb09b7 MZ |
335 | unsigned int svc, group, pin; |
336 | int ret; | |
43b169db | 337 | |
f9c74474 AD |
338 | if (clk_enable(bank->drvdata->pclk)) { |
339 | dev_err(bank->gpio_chip.parent, | |
340 | "unable to enable clock for handling IRQ\n"); | |
341 | return IRQ_NONE; | |
342 | } | |
343 | ||
6cf96df7 JK |
344 | if (bank->eint_con_offset) |
345 | svc = readl(bank->eint_base + EXYNOSAUTO_SVC_OFFSET); | |
346 | else | |
347 | svc = readl(bank->eint_base + EXYNOS_SVC_OFFSET); | |
f9c74474 AD |
348 | |
349 | clk_disable(bank->drvdata->pclk); | |
350 | ||
43b169db TA |
351 | group = EXYNOS_SVC_GROUP(svc); |
352 | pin = svc & EXYNOS_SVC_NUM_MASK; | |
353 | ||
354 | if (!group) | |
355 | return IRQ_HANDLED; | |
356 | bank += (group - 1); | |
357 | ||
a9cb09b7 MZ |
358 | ret = generic_handle_domain_irq(bank->irq_domain, pin); |
359 | if (ret) | |
43b169db | 360 | return IRQ_NONE; |
a9cb09b7 | 361 | |
43b169db TA |
362 | return IRQ_HANDLED; |
363 | } | |
364 | ||
7ccbc60c TF |
365 | struct exynos_eint_gpio_save { |
366 | u32 eint_con; | |
367 | u32 eint_fltcon0; | |
368 | u32 eint_fltcon1; | |
f354157a | 369 | u32 eint_mask; |
7ccbc60c TF |
370 | }; |
371 | ||
43b169db TA |
372 | /* |
373 | * exynos_eint_gpio_init() - setup handling of external gpio interrupts. | |
374 | * @d: driver data of samsung pinctrl driver. | |
375 | */ | |
85745c87 | 376 | __init int exynos_eint_gpio_init(struct samsung_pinctrl_drv_data *d) |
43b169db | 377 | { |
595be726 | 378 | struct samsung_pin_bank *bank; |
43b169db | 379 | struct device *dev = d->dev; |
7ccbc60c TF |
380 | int ret; |
381 | int i; | |
43b169db TA |
382 | |
383 | if (!d->irq) { | |
384 | dev_err(dev, "irq number not available\n"); | |
385 | return -EINVAL; | |
386 | } | |
387 | ||
388 | ret = devm_request_irq(dev, d->irq, exynos_eint_gpio_irq, | |
389 | 0, dev_name(dev), d); | |
390 | if (ret) { | |
391 | dev_err(dev, "irq request failed\n"); | |
392 | return -ENXIO; | |
393 | } | |
394 | ||
1bf00d7a TF |
395 | bank = d->pin_banks; |
396 | for (i = 0; i < d->nr_banks; ++i, ++bank) { | |
595be726 TF |
397 | if (bank->eint_type != EINT_TYPE_GPIO) |
398 | continue; | |
85745c87 MS |
399 | |
400 | bank->irq_chip = devm_kmemdup(dev, &exynos_gpio_irq_chip, | |
401 | sizeof(*bank->irq_chip), GFP_KERNEL); | |
402 | if (!bank->irq_chip) { | |
403 | ret = -ENOMEM; | |
404 | goto err_domains; | |
405 | } | |
406 | bank->irq_chip->chip.name = bank->name; | |
407 | ||
492fca28 | 408 | bank->irq_domain = irq_domain_create_linear(bank->fwnode, |
6f5e41bd | 409 | bank->nr_pins, &exynos_eint_irqd_ops, bank); |
595be726 TF |
410 | if (!bank->irq_domain) { |
411 | dev_err(dev, "gpio irq domain add failed\n"); | |
7ccbc60c TF |
412 | ret = -ENXIO; |
413 | goto err_domains; | |
414 | } | |
415 | ||
416 | bank->soc_priv = devm_kzalloc(d->dev, | |
417 | sizeof(struct exynos_eint_gpio_save), GFP_KERNEL); | |
418 | if (!bank->soc_priv) { | |
419 | irq_domain_remove(bank->irq_domain); | |
420 | ret = -ENOMEM; | |
421 | goto err_domains; | |
595be726 | 422 | } |
0d3d30db | 423 | |
43b169db TA |
424 | } |
425 | ||
426 | return 0; | |
7ccbc60c TF |
427 | |
428 | err_domains: | |
429 | for (--i, --bank; i >= 0; --i, --bank) { | |
430 | if (bank->eint_type != EINT_TYPE_GPIO) | |
431 | continue; | |
432 | irq_domain_remove(bank->irq_domain); | |
433 | } | |
434 | ||
435 | return ret; | |
43b169db TA |
436 | } |
437 | ||
ad350cd9 TF |
438 | static int exynos_wkup_irq_set_wake(struct irq_data *irqd, unsigned int on) |
439 | { | |
a8be2af0 KK |
440 | struct irq_chip *chip = irq_data_get_irq_chip(irqd); |
441 | struct exynos_irq_chip *our_chip = to_exynos_irq_chip(chip); | |
ad350cd9 TF |
442 | struct samsung_pin_bank *bank = irq_data_get_irq_chip_data(irqd); |
443 | unsigned long bit = 1UL << (2 * bank->eint_offset + irqd->hwirq); | |
444 | ||
3652dc07 MJ |
445 | pr_info("wake %s for irq %u (%s-%lu)\n", on ? "enabled" : "disabled", |
446 | irqd->irq, bank->name, irqd->hwirq); | |
ad350cd9 TF |
447 | |
448 | if (!on) | |
85745c87 | 449 | *our_chip->eint_wake_mask_value |= bit; |
ad350cd9 | 450 | else |
85745c87 | 451 | *our_chip->eint_wake_mask_value &= ~bit; |
ad350cd9 TF |
452 | |
453 | return 0; | |
454 | } | |
455 | ||
b577a279 JB |
456 | static void |
457 | exynos_pinctrl_set_eint_wakeup_mask(struct samsung_pinctrl_drv_data *drvdata, | |
458 | struct exynos_irq_chip *irq_chip) | |
459 | { | |
460 | struct regmap *pmu_regs; | |
461 | ||
462 | if (!drvdata->retention_ctrl || !drvdata->retention_ctrl->priv) { | |
463 | dev_warn(drvdata->dev, | |
464 | "No retention data configured bank with external wakeup interrupt. Wake-up mask will not be set.\n"); | |
465 | return; | |
466 | } | |
467 | ||
468 | pmu_regs = drvdata->retention_ctrl->priv; | |
469 | dev_info(drvdata->dev, | |
470 | "Setting external wakeup interrupt mask: 0x%x\n", | |
85745c87 | 471 | *irq_chip->eint_wake_mask_value); |
b577a279 JB |
472 | |
473 | regmap_write(pmu_regs, irq_chip->eint_wake_mask_reg, | |
85745c87 | 474 | *irq_chip->eint_wake_mask_value); |
b577a279 JB |
475 | } |
476 | ||
477 | static void | |
478 | s5pv210_pinctrl_set_eint_wakeup_mask(struct samsung_pinctrl_drv_data *drvdata, | |
479 | struct exynos_irq_chip *irq_chip) | |
480 | ||
481 | { | |
482 | void __iomem *clk_base; | |
483 | ||
484 | if (!drvdata->retention_ctrl || !drvdata->retention_ctrl->priv) { | |
485 | dev_warn(drvdata->dev, | |
486 | "No retention data configured bank with external wakeup interrupt. Wake-up mask will not be set.\n"); | |
487 | return; | |
488 | } | |
489 | ||
490 | ||
491 | clk_base = (void __iomem *) drvdata->retention_ctrl->priv; | |
492 | ||
85745c87 | 493 | __raw_writel(*irq_chip->eint_wake_mask_value, |
b577a279 JB |
494 | clk_base + irq_chip->eint_wake_mask_reg); |
495 | } | |
496 | ||
85745c87 | 497 | static u32 eint_wake_mask_value = EXYNOS_EINT_WAKEUP_MASK_DISABLED; |
43b169db TA |
498 | /* |
499 | * irq_chip for wakeup interrupts | |
500 | */ | |
bb928dfd KK |
501 | static const struct exynos_irq_chip s5pv210_wkup_irq_chip __initconst = { |
502 | .chip = { | |
503 | .name = "s5pv210_wkup_irq_chip", | |
504 | .irq_unmask = exynos_irq_unmask, | |
505 | .irq_mask = exynos_irq_mask, | |
506 | .irq_ack = exynos_irq_ack, | |
507 | .irq_set_type = exynos_irq_set_type, | |
508 | .irq_set_wake = exynos_wkup_irq_set_wake, | |
509 | .irq_request_resources = exynos_irq_request_resources, | |
510 | .irq_release_resources = exynos_irq_release_resources, | |
511 | }, | |
512 | .eint_con = EXYNOS_WKUP_ECON_OFFSET, | |
513 | .eint_mask = EXYNOS_WKUP_EMASK_OFFSET, | |
514 | .eint_pend = EXYNOS_WKUP_EPEND_OFFSET, | |
85745c87 | 515 | .eint_wake_mask_value = &eint_wake_mask_value, |
b577a279 | 516 | /* Only differences with exynos4210_wkup_irq_chip: */ |
a8be2af0 | 517 | .eint_wake_mask_reg = S5PV210_EINT_WAKEUP_MASK, |
b577a279 | 518 | .set_eint_wakeup_mask = s5pv210_pinctrl_set_eint_wakeup_mask, |
bb928dfd KK |
519 | }; |
520 | ||
71b96c3a | 521 | static const struct exynos_irq_chip exynos4210_wkup_irq_chip __initconst = { |
2e4a4fda | 522 | .chip = { |
14c255d3 | 523 | .name = "exynos4210_wkup_irq_chip", |
2e4a4fda TF |
524 | .irq_unmask = exynos_irq_unmask, |
525 | .irq_mask = exynos_irq_mask, | |
526 | .irq_ack = exynos_irq_ack, | |
527 | .irq_set_type = exynos_irq_set_type, | |
528 | .irq_set_wake = exynos_wkup_irq_set_wake, | |
f6a8249f TF |
529 | .irq_request_resources = exynos_irq_request_resources, |
530 | .irq_release_resources = exynos_irq_release_resources, | |
2e4a4fda TF |
531 | }, |
532 | .eint_con = EXYNOS_WKUP_ECON_OFFSET, | |
533 | .eint_mask = EXYNOS_WKUP_EMASK_OFFSET, | |
534 | .eint_pend = EXYNOS_WKUP_EPEND_OFFSET, | |
85745c87 | 535 | .eint_wake_mask_value = &eint_wake_mask_value, |
a8be2af0 | 536 | .eint_wake_mask_reg = EXYNOS_EINT_WAKEUP_MASK, |
b577a279 | 537 | .set_eint_wakeup_mask = exynos_pinctrl_set_eint_wakeup_mask, |
43b169db TA |
538 | }; |
539 | ||
71b96c3a | 540 | static const struct exynos_irq_chip exynos7_wkup_irq_chip __initconst = { |
14c255d3 AK |
541 | .chip = { |
542 | .name = "exynos7_wkup_irq_chip", | |
543 | .irq_unmask = exynos_irq_unmask, | |
544 | .irq_mask = exynos_irq_mask, | |
545 | .irq_ack = exynos_irq_ack, | |
546 | .irq_set_type = exynos_irq_set_type, | |
547 | .irq_set_wake = exynos_wkup_irq_set_wake, | |
548 | .irq_request_resources = exynos_irq_request_resources, | |
549 | .irq_release_resources = exynos_irq_release_resources, | |
550 | }, | |
551 | .eint_con = EXYNOS7_WKUP_ECON_OFFSET, | |
552 | .eint_mask = EXYNOS7_WKUP_EMASK_OFFSET, | |
553 | .eint_pend = EXYNOS7_WKUP_EPEND_OFFSET, | |
85745c87 | 554 | .eint_wake_mask_value = &eint_wake_mask_value, |
a8be2af0 | 555 | .eint_wake_mask_reg = EXYNOS5433_EINT_WAKEUP_MASK, |
b577a279 | 556 | .set_eint_wakeup_mask = exynos_pinctrl_set_eint_wakeup_mask, |
14c255d3 AK |
557 | }; |
558 | ||
6cf96df7 JK |
559 | static const struct exynos_irq_chip exynosautov920_wkup_irq_chip __initconst = { |
560 | .chip = { | |
561 | .name = "exynosautov920_wkup_irq_chip", | |
562 | .irq_unmask = exynos_irq_unmask, | |
563 | .irq_mask = exynos_irq_mask, | |
564 | .irq_ack = exynos_irq_ack, | |
565 | .irq_set_type = exynos_irq_set_type, | |
566 | .irq_set_wake = exynos_wkup_irq_set_wake, | |
567 | .irq_request_resources = exynos_irq_request_resources, | |
568 | .irq_release_resources = exynos_irq_release_resources, | |
569 | }, | |
570 | .eint_wake_mask_value = &eint_wake_mask_value, | |
571 | .eint_wake_mask_reg = EXYNOS5433_EINT_WAKEUP_MASK, | |
572 | .set_eint_wakeup_mask = exynos_pinctrl_set_eint_wakeup_mask, | |
573 | }; | |
574 | ||
14c255d3 AK |
575 | /* list of external wakeup controllers supported */ |
576 | static const struct of_device_id exynos_wkup_irq_ids[] = { | |
bb928dfd KK |
577 | { .compatible = "samsung,s5pv210-wakeup-eint", |
578 | .data = &s5pv210_wkup_irq_chip }, | |
14c255d3 AK |
579 | { .compatible = "samsung,exynos4210-wakeup-eint", |
580 | .data = &exynos4210_wkup_irq_chip }, | |
581 | { .compatible = "samsung,exynos7-wakeup-eint", | |
582 | .data = &exynos7_wkup_irq_chip }, | |
832ae134 KK |
583 | { .compatible = "samsung,exynos850-wakeup-eint", |
584 | .data = &exynos7_wkup_irq_chip }, | |
585 | { .compatible = "samsung,exynosautov9-wakeup-eint", | |
586 | .data = &exynos7_wkup_irq_chip }, | |
6cf96df7 JK |
587 | { .compatible = "samsung,exynosautov920-wakeup-eint", |
588 | .data = &exynosautov920_wkup_irq_chip }, | |
14c255d3 AK |
589 | { } |
590 | }; | |
591 | ||
43b169db | 592 | /* interrupt handler for wakeup interrupts 0..15 */ |
bd0b9ac4 | 593 | static void exynos_irq_eint0_15(struct irq_desc *desc) |
43b169db | 594 | { |
5663bb27 | 595 | struct exynos_weint_data *eintd = irq_desc_get_handler_data(desc); |
a04b07c0 | 596 | struct samsung_pin_bank *bank = eintd->bank; |
5663bb27 | 597 | struct irq_chip *chip = irq_desc_get_chip(desc); |
43b169db TA |
598 | |
599 | chained_irq_enter(chip, desc); | |
43b169db | 600 | |
a9cb09b7 | 601 | generic_handle_domain_irq(bank->irq_domain, eintd->irq); |
26fecf0b | 602 | |
43b169db TA |
603 | chained_irq_exit(chip, desc); |
604 | } | |
605 | ||
fa0c10a5 | 606 | static inline void exynos_irq_demux_eint(unsigned int pend, |
a04b07c0 | 607 | struct irq_domain *domain) |
43b169db TA |
608 | { |
609 | unsigned int irq; | |
610 | ||
611 | while (pend) { | |
612 | irq = fls(pend) - 1; | |
a9cb09b7 | 613 | generic_handle_domain_irq(domain, irq); |
43b169db TA |
614 | pend &= ~(1 << irq); |
615 | } | |
616 | } | |
617 | ||
618 | /* interrupt handler for wakeup interrupt 16 */ | |
bd0b9ac4 | 619 | static void exynos_irq_demux_eint16_31(struct irq_desc *desc) |
43b169db | 620 | { |
5663bb27 JL |
621 | struct irq_chip *chip = irq_desc_get_chip(desc); |
622 | struct exynos_muxed_weint_data *eintd = irq_desc_get_handler_data(desc); | |
fa0c10a5 KK |
623 | unsigned int pend; |
624 | unsigned int mask; | |
a04b07c0 | 625 | int i; |
43b169db TA |
626 | |
627 | chained_irq_enter(chip, desc); | |
a04b07c0 | 628 | |
f9c74474 AD |
629 | /* |
630 | * just enable the clock once here, to avoid an enable/disable dance for | |
631 | * each bank. | |
632 | */ | |
633 | if (eintd->nr_banks) { | |
634 | struct samsung_pin_bank *b = eintd->banks[0]; | |
635 | ||
636 | if (clk_enable(b->drvdata->pclk)) { | |
637 | dev_err(b->gpio_chip.parent, | |
638 | "unable to enable clock for pending IRQs\n"); | |
639 | return; | |
640 | } | |
641 | } | |
642 | ||
a04b07c0 TF |
643 | for (i = 0; i < eintd->nr_banks; ++i) { |
644 | struct samsung_pin_bank *b = eintd->banks[i]; | |
8b1bd11c | 645 | pend = readl(b->eint_base + b->irq_chip->eint_pend |
2e4a4fda | 646 | + b->eint_offset); |
8b1bd11c | 647 | mask = readl(b->eint_base + b->irq_chip->eint_mask |
2e4a4fda | 648 | + b->eint_offset); |
a04b07c0 TF |
649 | exynos_irq_demux_eint(pend & ~mask, b->irq_domain); |
650 | } | |
651 | ||
f9c74474 AD |
652 | if (eintd->nr_banks) |
653 | clk_disable(eintd->banks[0]->drvdata->pclk); | |
654 | ||
43b169db TA |
655 | chained_irq_exit(chip, desc); |
656 | } | |
657 | ||
43b169db TA |
658 | /* |
659 | * exynos_eint_wkup_init() - setup handling of external wakeup interrupts. | |
660 | * @d: driver data of samsung pinctrl driver. | |
661 | */ | |
85745c87 | 662 | __init int exynos_eint_wkup_init(struct samsung_pinctrl_drv_data *d) |
43b169db TA |
663 | { |
664 | struct device *dev = d->dev; | |
c3ad056b TF |
665 | struct device_node *wkup_np = NULL; |
666 | struct device_node *np; | |
a04b07c0 | 667 | struct samsung_pin_bank *bank; |
43b169db | 668 | struct exynos_weint_data *weint_data; |
a04b07c0 | 669 | struct exynos_muxed_weint_data *muxed_data; |
85745c87 | 670 | const struct exynos_irq_chip *irq_chip; |
a04b07c0 TF |
671 | unsigned int muxed_banks = 0; |
672 | unsigned int i; | |
43b169db TA |
673 | int idx, irq; |
674 | ||
c3ad056b | 675 | for_each_child_of_node(dev->of_node, np) { |
14c255d3 AK |
676 | const struct of_device_id *match; |
677 | ||
678 | match = of_match_node(exynos_wkup_irq_ids, np); | |
679 | if (match) { | |
85745c87 | 680 | irq_chip = match->data; |
c3ad056b TF |
681 | wkup_np = np; |
682 | break; | |
683 | } | |
43b169db | 684 | } |
c3ad056b TF |
685 | if (!wkup_np) |
686 | return -ENODEV; | |
43b169db | 687 | |
1bf00d7a TF |
688 | bank = d->pin_banks; |
689 | for (i = 0; i < d->nr_banks; ++i, ++bank) { | |
a04b07c0 TF |
690 | if (bank->eint_type != EINT_TYPE_WKUP) |
691 | continue; | |
43b169db | 692 | |
85745c87 MS |
693 | bank->irq_chip = devm_kmemdup(dev, irq_chip, sizeof(*irq_chip), |
694 | GFP_KERNEL); | |
695 | if (!bank->irq_chip) { | |
696 | of_node_put(wkup_np); | |
697 | return -ENOMEM; | |
698 | } | |
699 | bank->irq_chip->chip.name = bank->name; | |
700 | ||
492fca28 | 701 | bank->irq_domain = irq_domain_create_linear(bank->fwnode, |
6f5e41bd | 702 | bank->nr_pins, &exynos_eint_irqd_ops, bank); |
a04b07c0 TF |
703 | if (!bank->irq_domain) { |
704 | dev_err(dev, "wkup irq domain add failed\n"); | |
5c7f48dd | 705 | of_node_put(wkup_np); |
a04b07c0 TF |
706 | return -ENXIO; |
707 | } | |
43b169db | 708 | |
492fca28 | 709 | if (!fwnode_property_present(bank->fwnode, "interrupts")) { |
a04b07c0 TF |
710 | bank->eint_type = EINT_TYPE_WKUP_MUX; |
711 | ++muxed_banks; | |
712 | continue; | |
713 | } | |
43b169db | 714 | |
a86854d0 KC |
715 | weint_data = devm_kcalloc(dev, |
716 | bank->nr_pins, sizeof(*weint_data), | |
717 | GFP_KERNEL); | |
5c7f48dd KK |
718 | if (!weint_data) { |
719 | of_node_put(wkup_np); | |
a04b07c0 | 720 | return -ENOMEM; |
5c7f48dd | 721 | } |
43b169db | 722 | |
a04b07c0 | 723 | for (idx = 0; idx < bank->nr_pins; ++idx) { |
492fca28 | 724 | irq = irq_of_parse_and_map(to_of_node(bank->fwnode), idx); |
a04b07c0 TF |
725 | if (!irq) { |
726 | dev_err(dev, "irq number for eint-%s-%d not found\n", | |
727 | bank->name, idx); | |
728 | continue; | |
729 | } | |
730 | weint_data[idx].irq = idx; | |
731 | weint_data[idx].bank = bank; | |
c21f7849 TG |
732 | irq_set_chained_handler_and_data(irq, |
733 | exynos_irq_eint0_15, | |
734 | &weint_data[idx]); | |
43b169db TA |
735 | } |
736 | } | |
a04b07c0 | 737 | |
5c7f48dd KK |
738 | if (!muxed_banks) { |
739 | of_node_put(wkup_np); | |
a04b07c0 | 740 | return 0; |
5c7f48dd | 741 | } |
a04b07c0 TF |
742 | |
743 | irq = irq_of_parse_and_map(wkup_np, 0); | |
5c7f48dd | 744 | of_node_put(wkup_np); |
a04b07c0 TF |
745 | if (!irq) { |
746 | dev_err(dev, "irq number for muxed EINTs not found\n"); | |
747 | return 0; | |
748 | } | |
749 | ||
750 | muxed_data = devm_kzalloc(dev, sizeof(*muxed_data) | |
751 | + muxed_banks*sizeof(struct samsung_pin_bank *), GFP_KERNEL); | |
fa5c0f46 | 752 | if (!muxed_data) |
a04b07c0 | 753 | return -ENOMEM; |
4e1e2111 | 754 | muxed_data->nr_banks = muxed_banks; |
a04b07c0 | 755 | |
bb56fc35 TG |
756 | irq_set_chained_handler_and_data(irq, exynos_irq_demux_eint16_31, |
757 | muxed_data); | |
a04b07c0 | 758 | |
1bf00d7a | 759 | bank = d->pin_banks; |
a04b07c0 | 760 | idx = 0; |
1bf00d7a | 761 | for (i = 0; i < d->nr_banks; ++i, ++bank) { |
a04b07c0 TF |
762 | if (bank->eint_type != EINT_TYPE_WKUP_MUX) |
763 | continue; | |
764 | ||
765 | muxed_data->banks[idx++] = bank; | |
766 | } | |
a04b07c0 | 767 | |
43b169db TA |
768 | return 0; |
769 | } | |
770 | ||
7ccbc60c TF |
771 | static void exynos_pinctrl_suspend_bank( |
772 | struct samsung_pinctrl_drv_data *drvdata, | |
773 | struct samsung_pin_bank *bank) | |
774 | { | |
775 | struct exynos_eint_gpio_save *save = bank->soc_priv; | |
1b09c2b8 | 776 | const void __iomem *regs = bank->eint_base; |
7ccbc60c | 777 | |
f9c74474 AD |
778 | if (clk_enable(bank->drvdata->pclk)) { |
779 | dev_err(bank->gpio_chip.parent, | |
780 | "unable to enable clock for saving state\n"); | |
781 | return; | |
782 | } | |
783 | ||
7ccbc60c TF |
784 | save->eint_con = readl(regs + EXYNOS_GPIO_ECON_OFFSET |
785 | + bank->eint_offset); | |
786 | save->eint_fltcon0 = readl(regs + EXYNOS_GPIO_EFLTCON_OFFSET | |
787 | + 2 * bank->eint_offset); | |
788 | save->eint_fltcon1 = readl(regs + EXYNOS_GPIO_EFLTCON_OFFSET | |
789 | + 2 * bank->eint_offset + 4); | |
f354157a JB |
790 | save->eint_mask = readl(regs + bank->irq_chip->eint_mask |
791 | + bank->eint_offset); | |
7ccbc60c | 792 | |
f9c74474 AD |
793 | clk_disable(bank->drvdata->pclk); |
794 | ||
7ccbc60c TF |
795 | pr_debug("%s: save con %#010x\n", bank->name, save->eint_con); |
796 | pr_debug("%s: save fltcon0 %#010x\n", bank->name, save->eint_fltcon0); | |
797 | pr_debug("%s: save fltcon1 %#010x\n", bank->name, save->eint_fltcon1); | |
f354157a | 798 | pr_debug("%s: save mask %#010x\n", bank->name, save->eint_mask); |
7ccbc60c TF |
799 | } |
800 | ||
884fdaa5 JK |
801 | static void exynosauto_pinctrl_suspend_bank(struct samsung_pinctrl_drv_data *drvdata, |
802 | struct samsung_pin_bank *bank) | |
803 | { | |
804 | struct exynos_eint_gpio_save *save = bank->soc_priv; | |
1b09c2b8 | 805 | const void __iomem *regs = bank->eint_base; |
884fdaa5 | 806 | |
f9c74474 AD |
807 | if (clk_enable(bank->drvdata->pclk)) { |
808 | dev_err(bank->gpio_chip.parent, | |
809 | "unable to enable clock for saving state\n"); | |
810 | return; | |
811 | } | |
812 | ||
884fdaa5 JK |
813 | save->eint_con = readl(regs + bank->pctl_offset + bank->eint_con_offset); |
814 | save->eint_mask = readl(regs + bank->pctl_offset + bank->eint_mask_offset); | |
815 | ||
f9c74474 AD |
816 | clk_disable(bank->drvdata->pclk); |
817 | ||
884fdaa5 JK |
818 | pr_debug("%s: save con %#010x\n", bank->name, save->eint_con); |
819 | pr_debug("%s: save mask %#010x\n", bank->name, save->eint_mask); | |
820 | } | |
821 | ||
cfa76ddf | 822 | void exynos_pinctrl_suspend(struct samsung_pinctrl_drv_data *drvdata) |
7ccbc60c | 823 | { |
1bf00d7a | 824 | struct samsung_pin_bank *bank = drvdata->pin_banks; |
a8be2af0 | 825 | struct exynos_irq_chip *irq_chip = NULL; |
7ccbc60c TF |
826 | int i; |
827 | ||
a8be2af0 | 828 | for (i = 0; i < drvdata->nr_banks; ++i, ++bank) { |
884fdaa5 JK |
829 | if (bank->eint_type == EINT_TYPE_GPIO) { |
830 | if (bank->eint_con_offset) | |
831 | exynosauto_pinctrl_suspend_bank(drvdata, bank); | |
832 | else | |
833 | exynos_pinctrl_suspend_bank(drvdata, bank); | |
834 | } | |
a8be2af0 KK |
835 | else if (bank->eint_type == EINT_TYPE_WKUP) { |
836 | if (!irq_chip) { | |
837 | irq_chip = bank->irq_chip; | |
b577a279 JB |
838 | irq_chip->set_eint_wakeup_mask(drvdata, |
839 | irq_chip); | |
a8be2af0 KK |
840 | } |
841 | } | |
842 | } | |
7ccbc60c TF |
843 | } |
844 | ||
845 | static void exynos_pinctrl_resume_bank( | |
846 | struct samsung_pinctrl_drv_data *drvdata, | |
847 | struct samsung_pin_bank *bank) | |
848 | { | |
849 | struct exynos_eint_gpio_save *save = bank->soc_priv; | |
8b1bd11c | 850 | void __iomem *regs = bank->eint_base; |
7ccbc60c | 851 | |
f9c74474 AD |
852 | if (clk_enable(bank->drvdata->pclk)) { |
853 | dev_err(bank->gpio_chip.parent, | |
854 | "unable to enable clock for restoring state\n"); | |
855 | return; | |
856 | } | |
857 | ||
7ccbc60c TF |
858 | pr_debug("%s: con %#010x => %#010x\n", bank->name, |
859 | readl(regs + EXYNOS_GPIO_ECON_OFFSET | |
860 | + bank->eint_offset), save->eint_con); | |
861 | pr_debug("%s: fltcon0 %#010x => %#010x\n", bank->name, | |
862 | readl(regs + EXYNOS_GPIO_EFLTCON_OFFSET | |
863 | + 2 * bank->eint_offset), save->eint_fltcon0); | |
864 | pr_debug("%s: fltcon1 %#010x => %#010x\n", bank->name, | |
865 | readl(regs + EXYNOS_GPIO_EFLTCON_OFFSET | |
866 | + 2 * bank->eint_offset + 4), save->eint_fltcon1); | |
f354157a JB |
867 | pr_debug("%s: mask %#010x => %#010x\n", bank->name, |
868 | readl(regs + bank->irq_chip->eint_mask | |
869 | + bank->eint_offset), save->eint_mask); | |
7ccbc60c TF |
870 | |
871 | writel(save->eint_con, regs + EXYNOS_GPIO_ECON_OFFSET | |
872 | + bank->eint_offset); | |
873 | writel(save->eint_fltcon0, regs + EXYNOS_GPIO_EFLTCON_OFFSET | |
874 | + 2 * bank->eint_offset); | |
875 | writel(save->eint_fltcon1, regs + EXYNOS_GPIO_EFLTCON_OFFSET | |
876 | + 2 * bank->eint_offset + 4); | |
f354157a JB |
877 | writel(save->eint_mask, regs + bank->irq_chip->eint_mask |
878 | + bank->eint_offset); | |
f9c74474 AD |
879 | |
880 | clk_disable(bank->drvdata->pclk); | |
7ccbc60c TF |
881 | } |
882 | ||
884fdaa5 JK |
883 | static void exynosauto_pinctrl_resume_bank(struct samsung_pinctrl_drv_data *drvdata, |
884 | struct samsung_pin_bank *bank) | |
885 | { | |
886 | struct exynos_eint_gpio_save *save = bank->soc_priv; | |
887 | void __iomem *regs = bank->eint_base; | |
888 | ||
f9c74474 AD |
889 | if (clk_enable(bank->drvdata->pclk)) { |
890 | dev_err(bank->gpio_chip.parent, | |
891 | "unable to enable clock for restoring state\n"); | |
892 | return; | |
893 | } | |
894 | ||
884fdaa5 JK |
895 | pr_debug("%s: con %#010x => %#010x\n", bank->name, |
896 | readl(regs + bank->pctl_offset + bank->eint_con_offset), save->eint_con); | |
897 | pr_debug("%s: mask %#010x => %#010x\n", bank->name, | |
898 | readl(regs + bank->pctl_offset + bank->eint_mask_offset), save->eint_mask); | |
899 | ||
900 | writel(save->eint_con, regs + bank->pctl_offset + bank->eint_con_offset); | |
901 | writel(save->eint_mask, regs + bank->pctl_offset + bank->eint_mask_offset); | |
f9c74474 AD |
902 | |
903 | clk_disable(bank->drvdata->pclk); | |
884fdaa5 JK |
904 | } |
905 | ||
cfa76ddf | 906 | void exynos_pinctrl_resume(struct samsung_pinctrl_drv_data *drvdata) |
7ccbc60c | 907 | { |
1bf00d7a | 908 | struct samsung_pin_bank *bank = drvdata->pin_banks; |
7ccbc60c TF |
909 | int i; |
910 | ||
1bf00d7a | 911 | for (i = 0; i < drvdata->nr_banks; ++i, ++bank) |
884fdaa5 JK |
912 | if (bank->eint_type == EINT_TYPE_GPIO) { |
913 | if (bank->eint_con_offset) | |
914 | exynosauto_pinctrl_resume_bank(drvdata, bank); | |
915 | else | |
916 | exynos_pinctrl_resume_bank(drvdata, bank); | |
917 | } | |
7ccbc60c TF |
918 | } |
919 | ||
07731019 MS |
920 | static void exynos_retention_enable(struct samsung_pinctrl_drv_data *drvdata) |
921 | { | |
922 | if (drvdata->retention_ctrl->refcnt) | |
923 | atomic_inc(drvdata->retention_ctrl->refcnt); | |
924 | } | |
925 | ||
926 | static void exynos_retention_disable(struct samsung_pinctrl_drv_data *drvdata) | |
927 | { | |
928 | struct samsung_retention_ctrl *ctrl = drvdata->retention_ctrl; | |
929 | struct regmap *pmu_regs = ctrl->priv; | |
930 | int i; | |
931 | ||
932 | if (ctrl->refcnt && !atomic_dec_and_test(ctrl->refcnt)) | |
933 | return; | |
934 | ||
935 | for (i = 0; i < ctrl->nr_regs; i++) | |
936 | regmap_write(pmu_regs, ctrl->regs[i], ctrl->value); | |
937 | } | |
938 | ||
cfa76ddf | 939 | struct samsung_retention_ctrl * |
07731019 MS |
940 | exynos_retention_init(struct samsung_pinctrl_drv_data *drvdata, |
941 | const struct samsung_retention_data *data) | |
942 | { | |
943 | struct samsung_retention_ctrl *ctrl; | |
944 | struct regmap *pmu_regs; | |
8fe9bf07 | 945 | int i; |
07731019 MS |
946 | |
947 | ctrl = devm_kzalloc(drvdata->dev, sizeof(*ctrl), GFP_KERNEL); | |
948 | if (!ctrl) | |
949 | return ERR_PTR(-ENOMEM); | |
950 | ||
951 | pmu_regs = exynos_get_pmu_regmap(); | |
952 | if (IS_ERR(pmu_regs)) | |
953 | return ERR_CAST(pmu_regs); | |
954 | ||
955 | ctrl->priv = pmu_regs; | |
956 | ctrl->regs = data->regs; | |
957 | ctrl->nr_regs = data->nr_regs; | |
958 | ctrl->value = data->value; | |
959 | ctrl->refcnt = data->refcnt; | |
960 | ctrl->enable = exynos_retention_enable; | |
961 | ctrl->disable = exynos_retention_disable; | |
962 | ||
8fe9bf07 MS |
963 | /* Ensure that retention is disabled on driver init */ |
964 | for (i = 0; i < ctrl->nr_regs; i++) | |
965 | regmap_write(pmu_regs, ctrl->regs[i], ctrl->value); | |
966 | ||
07731019 MS |
967 | return ctrl; |
968 | } |