xref: /linux/drivers/pinctrl/samsung/pinctrl-exynos.c (revision 3f36bffab9e324873333304a0fd6c5ec8c9a10fc)
1221173a3SKrzysztof Kozlowski // SPDX-License-Identifier: GPL-2.0+
2221173a3SKrzysztof Kozlowski //
3221173a3SKrzysztof Kozlowski // Exynos specific support for Samsung pinctrl/gpiolib driver with eint support.
4221173a3SKrzysztof Kozlowski //
5221173a3SKrzysztof Kozlowski // Copyright (c) 2012 Samsung Electronics Co., Ltd.
6221173a3SKrzysztof Kozlowski //		http://www.samsung.com
7221173a3SKrzysztof Kozlowski // Copyright (c) 2012 Linaro Ltd
8221173a3SKrzysztof Kozlowski //		http://www.linaro.org
9221173a3SKrzysztof Kozlowski //
10221173a3SKrzysztof Kozlowski // Author: Thomas Abraham <thomas.ab@samsung.com>
11221173a3SKrzysztof Kozlowski //
12221173a3SKrzysztof Kozlowski // This file contains the Samsung Exynos specific information required by the
13221173a3SKrzysztof Kozlowski // the Samsung pinctrl/gpiolib driver. It also includes the implementation of
14221173a3SKrzysztof Kozlowski // external gpio and wakeup interrupt support.
1543b169dbSThomas Abraham 
16f9c74474SAndré Draszik #include <linux/clk.h>
1743b169dbSThomas Abraham #include <linux/device.h>
1843b169dbSThomas Abraham #include <linux/interrupt.h>
1943b169dbSThomas Abraham #include <linux/irqdomain.h>
2043b169dbSThomas Abraham #include <linux/irq.h>
21de88cbb7SCatalin Marinas #include <linux/irqchip/chained_irq.h>
22cfa76ddfSKrzysztof Kozlowski #include <linux/of.h>
2343b169dbSThomas Abraham #include <linux/of_irq.h>
2443b169dbSThomas Abraham #include <linux/slab.h>
2519846950STomasz Figa #include <linux/spinlock.h>
26*3f36bffaSKrzysztof Kozlowski #include <linux/string_choices.h>
2707731019SMarek Szyprowski #include <linux/regmap.h>
2843b169dbSThomas Abraham #include <linux/err.h>
2907731019SMarek Szyprowski #include <linux/soc/samsung/exynos-pmu.h>
30a8be2af0SKrzysztof Kozlowski #include <linux/soc/samsung/exynos-regs-pmu.h>
3143b169dbSThomas Abraham 
3243b169dbSThomas Abraham #include "pinctrl-samsung.h"
3343b169dbSThomas Abraham #include "pinctrl-exynos.h"
3443b169dbSThomas Abraham 
352e4a4fdaSTomasz Figa struct exynos_irq_chip {
362e4a4fdaSTomasz Figa 	struct irq_chip chip;
372e4a4fdaSTomasz Figa 
382e4a4fdaSTomasz Figa 	u32 eint_con;
392e4a4fdaSTomasz Figa 	u32 eint_mask;
402e4a4fdaSTomasz Figa 	u32 eint_pend;
4185745c87SMarek Szyprowski 	u32 *eint_wake_mask_value;
42a8be2af0SKrzysztof Kozlowski 	u32 eint_wake_mask_reg;
43b577a279SJonathan Bakker 	void (*set_eint_wakeup_mask)(struct samsung_pinctrl_drv_data *drvdata,
44b577a279SJonathan Bakker 				     struct exynos_irq_chip *irq_chip);
452e4a4fdaSTomasz Figa };
462e4a4fdaSTomasz Figa 
472e4a4fdaSTomasz Figa static inline struct exynos_irq_chip *to_exynos_irq_chip(struct irq_chip *chip)
482e4a4fdaSTomasz Figa {
492e4a4fdaSTomasz Figa 	return container_of(chip, struct exynos_irq_chip, chip);
502e4a4fdaSTomasz Figa }
51499147c9STomasz Figa 
522e4a4fdaSTomasz Figa static void exynos_irq_mask(struct irq_data *irqd)
5343b169dbSThomas Abraham {
542e4a4fdaSTomasz Figa 	struct irq_chip *chip = irq_data_get_irq_chip(irqd);
552e4a4fdaSTomasz Figa 	struct exynos_irq_chip *our_chip = to_exynos_irq_chip(chip);
56595be726STomasz Figa 	struct samsung_pin_bank *bank = irq_data_get_irq_chip_data(irqd);
57884fdaa5SJaewon Kim 	unsigned long reg_mask;
58fa0c10a5SKrzysztof Kozlowski 	unsigned int mask;
595ae8cf79SDoug Anderson 	unsigned long flags;
605ae8cf79SDoug Anderson 
61884fdaa5SJaewon Kim 	if (bank->eint_mask_offset)
62884fdaa5SJaewon Kim 		reg_mask = bank->pctl_offset + bank->eint_mask_offset;
63884fdaa5SJaewon Kim 	else
64884fdaa5SJaewon Kim 		reg_mask = our_chip->eint_mask + bank->eint_offset;
65884fdaa5SJaewon Kim 
66f9c74474SAndré Draszik 	if (clk_enable(bank->drvdata->pclk)) {
67f9c74474SAndré Draszik 		dev_err(bank->gpio_chip.parent,
68f9c74474SAndré Draszik 			"unable to enable clock for masking IRQ\n");
69f9c74474SAndré Draszik 		return;
70f9c74474SAndré Draszik 	}
71f9c74474SAndré Draszik 
721f306ecbSChanho Park 	raw_spin_lock_irqsave(&bank->slock, flags);
7343b169dbSThomas Abraham 
748b1bd11cSChanwoo Choi 	mask = readl(bank->eint_base + reg_mask);
75595be726STomasz Figa 	mask |= 1 << irqd->hwirq;
768b1bd11cSChanwoo Choi 	writel(mask, bank->eint_base + reg_mask);
775ae8cf79SDoug Anderson 
781f306ecbSChanho Park 	raw_spin_unlock_irqrestore(&bank->slock, flags);
79f9c74474SAndré Draszik 
80f9c74474SAndré Draszik 	clk_disable(bank->drvdata->pclk);
8143b169dbSThomas Abraham }
8243b169dbSThomas Abraham 
832e4a4fdaSTomasz Figa static void exynos_irq_ack(struct irq_data *irqd)
8443b169dbSThomas Abraham {
852e4a4fdaSTomasz Figa 	struct irq_chip *chip = irq_data_get_irq_chip(irqd);
862e4a4fdaSTomasz Figa 	struct exynos_irq_chip *our_chip = to_exynos_irq_chip(chip);
87595be726STomasz Figa 	struct samsung_pin_bank *bank = irq_data_get_irq_chip_data(irqd);
88884fdaa5SJaewon Kim 	unsigned long reg_pend;
89884fdaa5SJaewon Kim 
90884fdaa5SJaewon Kim 	if (bank->eint_pend_offset)
91884fdaa5SJaewon Kim 		reg_pend = bank->pctl_offset + bank->eint_pend_offset;
92884fdaa5SJaewon Kim 	else
93884fdaa5SJaewon Kim 		reg_pend = our_chip->eint_pend + bank->eint_offset;
9443b169dbSThomas Abraham 
95f9c74474SAndré Draszik 	if (clk_enable(bank->drvdata->pclk)) {
96f9c74474SAndré Draszik 		dev_err(bank->gpio_chip.parent,
97f9c74474SAndré Draszik 			"unable to enable clock to ack IRQ\n");
98f9c74474SAndré Draszik 		return;
99f9c74474SAndré Draszik 	}
100f9c74474SAndré Draszik 
1018b1bd11cSChanwoo Choi 	writel(1 << irqd->hwirq, bank->eint_base + reg_pend);
102f9c74474SAndré Draszik 
103f9c74474SAndré Draszik 	clk_disable(bank->drvdata->pclk);
10443b169dbSThomas Abraham }
10543b169dbSThomas Abraham 
1062e4a4fdaSTomasz Figa static void exynos_irq_unmask(struct irq_data *irqd)
1075ace03fbSDoug Anderson {
1082e4a4fdaSTomasz Figa 	struct irq_chip *chip = irq_data_get_irq_chip(irqd);
1092e4a4fdaSTomasz Figa 	struct exynos_irq_chip *our_chip = to_exynos_irq_chip(chip);
1105ace03fbSDoug Anderson 	struct samsung_pin_bank *bank = irq_data_get_irq_chip_data(irqd);
111884fdaa5SJaewon Kim 	unsigned long reg_mask;
112fa0c10a5SKrzysztof Kozlowski 	unsigned int mask;
1135ace03fbSDoug Anderson 	unsigned long flags;
1145ace03fbSDoug Anderson 
1155a68e7a7SDoug Anderson 	/*
1165a68e7a7SDoug Anderson 	 * Ack level interrupts right before unmask
1175a68e7a7SDoug Anderson 	 *
1185a68e7a7SDoug Anderson 	 * If we don't do this we'll get a double-interrupt.  Level triggered
1195a68e7a7SDoug Anderson 	 * interrupts must not fire an interrupt if the level is not
1205a68e7a7SDoug Anderson 	 * _currently_ active, even if it was active while the interrupt was
1215a68e7a7SDoug Anderson 	 * masked.
1225a68e7a7SDoug Anderson 	 */
1235a68e7a7SDoug Anderson 	if (irqd_get_trigger_type(irqd) & IRQ_TYPE_LEVEL_MASK)
1242e4a4fdaSTomasz Figa 		exynos_irq_ack(irqd);
1255a68e7a7SDoug Anderson 
126884fdaa5SJaewon Kim 	if (bank->eint_mask_offset)
127884fdaa5SJaewon Kim 		reg_mask = bank->pctl_offset + bank->eint_mask_offset;
128884fdaa5SJaewon Kim 	else
129884fdaa5SJaewon Kim 		reg_mask = our_chip->eint_mask + bank->eint_offset;
130884fdaa5SJaewon Kim 
131f9c74474SAndré Draszik 	if (clk_enable(bank->drvdata->pclk)) {
132f9c74474SAndré Draszik 		dev_err(bank->gpio_chip.parent,
133f9c74474SAndré Draszik 			"unable to enable clock for unmasking IRQ\n");
134f9c74474SAndré Draszik 		return;
135f9c74474SAndré Draszik 	}
136f9c74474SAndré Draszik 
1371f306ecbSChanho Park 	raw_spin_lock_irqsave(&bank->slock, flags);
1385ace03fbSDoug Anderson 
1398b1bd11cSChanwoo Choi 	mask = readl(bank->eint_base + reg_mask);
1405ace03fbSDoug Anderson 	mask &= ~(1 << irqd->hwirq);
1418b1bd11cSChanwoo Choi 	writel(mask, bank->eint_base + reg_mask);
1425ace03fbSDoug Anderson 
1431f306ecbSChanho Park 	raw_spin_unlock_irqrestore(&bank->slock, flags);
144f9c74474SAndré Draszik 
145f9c74474SAndré Draszik 	clk_disable(bank->drvdata->pclk);
1465ace03fbSDoug Anderson }
1475ace03fbSDoug Anderson 
1482e4a4fdaSTomasz Figa static int exynos_irq_set_type(struct irq_data *irqd, unsigned int type)
14943b169dbSThomas Abraham {
1502e4a4fdaSTomasz Figa 	struct irq_chip *chip = irq_data_get_irq_chip(irqd);
1512e4a4fdaSTomasz Figa 	struct exynos_irq_chip *our_chip = to_exynos_irq_chip(chip);
152595be726STomasz Figa 	struct samsung_pin_bank *bank = irq_data_get_irq_chip_data(irqd);
153f6a8249fSTomasz Figa 	unsigned int shift = EXYNOS_EINT_CON_LEN * irqd->hwirq;
15443b169dbSThomas Abraham 	unsigned int con, trig_type;
155884fdaa5SJaewon Kim 	unsigned long reg_con;
156f9c74474SAndré Draszik 	int ret;
15743b169dbSThomas Abraham 
15843b169dbSThomas Abraham 	switch (type) {
15943b169dbSThomas Abraham 	case IRQ_TYPE_EDGE_RISING:
16043b169dbSThomas Abraham 		trig_type = EXYNOS_EINT_EDGE_RISING;
16143b169dbSThomas Abraham 		break;
16243b169dbSThomas Abraham 	case IRQ_TYPE_EDGE_FALLING:
16343b169dbSThomas Abraham 		trig_type = EXYNOS_EINT_EDGE_FALLING;
16443b169dbSThomas Abraham 		break;
16543b169dbSThomas Abraham 	case IRQ_TYPE_EDGE_BOTH:
16643b169dbSThomas Abraham 		trig_type = EXYNOS_EINT_EDGE_BOTH;
16743b169dbSThomas Abraham 		break;
16843b169dbSThomas Abraham 	case IRQ_TYPE_LEVEL_HIGH:
16943b169dbSThomas Abraham 		trig_type = EXYNOS_EINT_LEVEL_HIGH;
17043b169dbSThomas Abraham 		break;
17143b169dbSThomas Abraham 	case IRQ_TYPE_LEVEL_LOW:
17243b169dbSThomas Abraham 		trig_type = EXYNOS_EINT_LEVEL_LOW;
17343b169dbSThomas Abraham 		break;
17443b169dbSThomas Abraham 	default:
17543b169dbSThomas Abraham 		pr_err("unsupported external interrupt type\n");
17643b169dbSThomas Abraham 		return -EINVAL;
17743b169dbSThomas Abraham 	}
17843b169dbSThomas Abraham 
17943b169dbSThomas Abraham 	if (type & IRQ_TYPE_EDGE_BOTH)
18040ec168aSThomas Gleixner 		irq_set_handler_locked(irqd, handle_edge_irq);
18143b169dbSThomas Abraham 	else
18240ec168aSThomas Gleixner 		irq_set_handler_locked(irqd, handle_level_irq);
18343b169dbSThomas Abraham 
184884fdaa5SJaewon Kim 	if (bank->eint_con_offset)
185884fdaa5SJaewon Kim 		reg_con = bank->pctl_offset + bank->eint_con_offset;
186884fdaa5SJaewon Kim 	else
187884fdaa5SJaewon Kim 		reg_con = our_chip->eint_con + bank->eint_offset;
188884fdaa5SJaewon Kim 
189f9c74474SAndré Draszik 	ret = clk_enable(bank->drvdata->pclk);
190f9c74474SAndré Draszik 	if (ret) {
191f9c74474SAndré Draszik 		dev_err(bank->gpio_chip.parent,
192f9c74474SAndré Draszik 			"unable to enable clock for configuring IRQ type\n");
193f9c74474SAndré Draszik 		return ret;
194f9c74474SAndré Draszik 	}
195f9c74474SAndré Draszik 
1968b1bd11cSChanwoo Choi 	con = readl(bank->eint_base + reg_con);
19743b169dbSThomas Abraham 	con &= ~(EXYNOS_EINT_CON_MASK << shift);
19843b169dbSThomas Abraham 	con |= trig_type << shift;
1998b1bd11cSChanwoo Choi 	writel(con, bank->eint_base + reg_con);
200ee2f573cSTomasz Figa 
201f9c74474SAndré Draszik 	clk_disable(bank->drvdata->pclk);
202f9c74474SAndré Draszik 
203f6a8249fSTomasz Figa 	return 0;
204f6a8249fSTomasz Figa }
205f6a8249fSTomasz Figa 
206b77f5ef8SYoungmin Nam static int exynos_irq_set_affinity(struct irq_data *irqd,
207b77f5ef8SYoungmin Nam 				   const struct cpumask *dest, bool force)
208b77f5ef8SYoungmin Nam {
209b77f5ef8SYoungmin Nam 	struct samsung_pin_bank *bank = irq_data_get_irq_chip_data(irqd);
210b77f5ef8SYoungmin Nam 	struct samsung_pinctrl_drv_data *d = bank->drvdata;
211b77f5ef8SYoungmin Nam 	struct irq_data *parent = irq_get_irq_data(d->irq);
212b77f5ef8SYoungmin Nam 
213b77f5ef8SYoungmin Nam 	if (parent)
214b77f5ef8SYoungmin Nam 		return parent->chip->irq_set_affinity(parent, dest, force);
215b77f5ef8SYoungmin Nam 
216b77f5ef8SYoungmin Nam 	return -EINVAL;
217b77f5ef8SYoungmin Nam }
218b77f5ef8SYoungmin Nam 
219f6a8249fSTomasz Figa static int exynos_irq_request_resources(struct irq_data *irqd)
220f6a8249fSTomasz Figa {
221f6a8249fSTomasz Figa 	struct samsung_pin_bank *bank = irq_data_get_irq_chip_data(irqd);
22294ce944bSTomasz Figa 	const struct samsung_pin_bank_type *bank_type = bank->type;
223bbed85f4SKrzysztof Kozlowski 	unsigned long reg_con, flags;
224bbed85f4SKrzysztof Kozlowski 	unsigned int shift, mask, con;
225f6a8249fSTomasz Figa 	int ret;
226f6a8249fSTomasz Figa 
227e3a2e878SAlexandre Courbot 	ret = gpiochip_lock_as_irq(&bank->gpio_chip, irqd->hwirq);
228f6a8249fSTomasz Figa 	if (ret) {
22958383c78SLinus Walleij 		dev_err(bank->gpio_chip.parent,
23058383c78SLinus Walleij 			"unable to lock pin %s-%lu IRQ\n",
231f6a8249fSTomasz Figa 			bank->name, irqd->hwirq);
232f6a8249fSTomasz Figa 		return ret;
233f6a8249fSTomasz Figa 	}
234f6a8249fSTomasz Figa 
23543fc9e7fSTomasz Figa 	reg_con = bank->pctl_offset + bank_type->reg_offset[PINCFG_TYPE_FUNC];
236f6a8249fSTomasz Figa 	shift = irqd->hwirq * bank_type->fld_width[PINCFG_TYPE_FUNC];
237499147c9STomasz Figa 	mask = (1 << bank_type->fld_width[PINCFG_TYPE_FUNC]) - 1;
238ee2f573cSTomasz Figa 
239f9c74474SAndré Draszik 	ret = clk_enable(bank->drvdata->pclk);
240f9c74474SAndré Draszik 	if (ret) {
241f9c74474SAndré Draszik 		dev_err(bank->gpio_chip.parent,
242f9c74474SAndré Draszik 			"unable to enable clock for configuring pin %s-%lu\n",
243f9c74474SAndré Draszik 			bank->name, irqd->hwirq);
244f9c74474SAndré Draszik 		return ret;
245f9c74474SAndré Draszik 	}
246f9c74474SAndré Draszik 
2471f306ecbSChanho Park 	raw_spin_lock_irqsave(&bank->slock, flags);
24819846950STomasz Figa 
249af0b0baaSKrzysztof Kozlowski 	con = readl(bank->pctl_base + reg_con);
250ee2f573cSTomasz Figa 	con &= ~(mask << shift);
2513eb12bceSKrzysztof Kozlowski 	con |= EXYNOS_PIN_CON_FUNC_EINT << shift;
252af0b0baaSKrzysztof Kozlowski 	writel(con, bank->pctl_base + reg_con);
253ee2f573cSTomasz Figa 
2541f306ecbSChanho Park 	raw_spin_unlock_irqrestore(&bank->slock, flags);
25519846950STomasz Figa 
256f9c74474SAndré Draszik 	clk_disable(bank->drvdata->pclk);
257f9c74474SAndré Draszik 
25843b169dbSThomas Abraham 	return 0;
25943b169dbSThomas Abraham }
26043b169dbSThomas Abraham 
261f6a8249fSTomasz Figa static void exynos_irq_release_resources(struct irq_data *irqd)
262f6a8249fSTomasz Figa {
263f6a8249fSTomasz Figa 	struct samsung_pin_bank *bank = irq_data_get_irq_chip_data(irqd);
26494ce944bSTomasz Figa 	const struct samsung_pin_bank_type *bank_type = bank->type;
265bbed85f4SKrzysztof Kozlowski 	unsigned long reg_con, flags;
266bbed85f4SKrzysztof Kozlowski 	unsigned int shift, mask, con;
267f6a8249fSTomasz Figa 
268f6a8249fSTomasz Figa 	reg_con = bank->pctl_offset + bank_type->reg_offset[PINCFG_TYPE_FUNC];
269f6a8249fSTomasz Figa 	shift = irqd->hwirq * bank_type->fld_width[PINCFG_TYPE_FUNC];
270f6a8249fSTomasz Figa 	mask = (1 << bank_type->fld_width[PINCFG_TYPE_FUNC]) - 1;
271f6a8249fSTomasz Figa 
272f9c74474SAndré Draszik 	if (clk_enable(bank->drvdata->pclk)) {
273f9c74474SAndré Draszik 		dev_err(bank->gpio_chip.parent,
274f9c74474SAndré Draszik 			"unable to enable clock for deconfiguring pin %s-%lu\n",
275f9c74474SAndré Draszik 			bank->name, irqd->hwirq);
276f9c74474SAndré Draszik 		return;
277f9c74474SAndré Draszik 	}
278f9c74474SAndré Draszik 
2791f306ecbSChanho Park 	raw_spin_lock_irqsave(&bank->slock, flags);
280f6a8249fSTomasz Figa 
281af0b0baaSKrzysztof Kozlowski 	con = readl(bank->pctl_base + reg_con);
282f6a8249fSTomasz Figa 	con &= ~(mask << shift);
2833eb12bceSKrzysztof Kozlowski 	con |= PIN_CON_FUNC_INPUT << shift;
284af0b0baaSKrzysztof Kozlowski 	writel(con, bank->pctl_base + reg_con);
285f6a8249fSTomasz Figa 
2861f306ecbSChanho Park 	raw_spin_unlock_irqrestore(&bank->slock, flags);
287f6a8249fSTomasz Figa 
288f9c74474SAndré Draszik 	clk_disable(bank->drvdata->pclk);
289f9c74474SAndré Draszik 
290e3a2e878SAlexandre Courbot 	gpiochip_unlock_as_irq(&bank->gpio_chip, irqd->hwirq);
291f6a8249fSTomasz Figa }
292f6a8249fSTomasz Figa 
29343b169dbSThomas Abraham /*
29443b169dbSThomas Abraham  * irq_chip for gpio interrupts.
29543b169dbSThomas Abraham  */
29685745c87SMarek Szyprowski static const struct exynos_irq_chip exynos_gpio_irq_chip __initconst = {
2972e4a4fdaSTomasz Figa 	.chip = {
29843b169dbSThomas Abraham 		.name = "exynos_gpio_irq_chip",
2992e4a4fdaSTomasz Figa 		.irq_unmask = exynos_irq_unmask,
3002e4a4fdaSTomasz Figa 		.irq_mask = exynos_irq_mask,
3012e4a4fdaSTomasz Figa 		.irq_ack = exynos_irq_ack,
3022e4a4fdaSTomasz Figa 		.irq_set_type = exynos_irq_set_type,
303b77f5ef8SYoungmin Nam 		.irq_set_affinity = exynos_irq_set_affinity,
304f6a8249fSTomasz Figa 		.irq_request_resources = exynos_irq_request_resources,
305f6a8249fSTomasz Figa 		.irq_release_resources = exynos_irq_release_resources,
3062e4a4fdaSTomasz Figa 	},
3072e4a4fdaSTomasz Figa 	.eint_con = EXYNOS_GPIO_ECON_OFFSET,
3082e4a4fdaSTomasz Figa 	.eint_mask = EXYNOS_GPIO_EMASK_OFFSET,
3092e4a4fdaSTomasz Figa 	.eint_pend = EXYNOS_GPIO_EPEND_OFFSET,
310a8be2af0SKrzysztof Kozlowski 	/* eint_wake_mask_value not used */
31143b169dbSThomas Abraham };
31243b169dbSThomas Abraham 
3136f5e41bdSAbhilash Kesavan static int exynos_eint_irq_map(struct irq_domain *h, unsigned int virq,
31443b169dbSThomas Abraham 					irq_hw_number_t hw)
31543b169dbSThomas Abraham {
316595be726STomasz Figa 	struct samsung_pin_bank *b = h->host_data;
31743b169dbSThomas Abraham 
318595be726STomasz Figa 	irq_set_chip_data(virq, b);
3190d3d30dbSAbhilash Kesavan 	irq_set_chip_and_handler(virq, &b->irq_chip->chip,
32043b169dbSThomas Abraham 					handle_level_irq);
32143b169dbSThomas Abraham 	return 0;
32243b169dbSThomas Abraham }
32343b169dbSThomas Abraham 
32443b169dbSThomas Abraham /*
3256f5e41bdSAbhilash Kesavan  * irq domain callbacks for external gpio and wakeup interrupt controllers.
32643b169dbSThomas Abraham  */
3276f5e41bdSAbhilash Kesavan static const struct irq_domain_ops exynos_eint_irqd_ops = {
3286f5e41bdSAbhilash Kesavan 	.map	= exynos_eint_irq_map,
32943b169dbSThomas Abraham 	.xlate	= irq_domain_xlate_twocell,
33043b169dbSThomas Abraham };
33143b169dbSThomas Abraham 
33243b169dbSThomas Abraham static irqreturn_t exynos_eint_gpio_irq(int irq, void *data)
33343b169dbSThomas Abraham {
33443b169dbSThomas Abraham 	struct samsung_pinctrl_drv_data *d = data;
3351bf00d7aSTomasz Figa 	struct samsung_pin_bank *bank = d->pin_banks;
336a9cb09b7SMarc Zyngier 	unsigned int svc, group, pin;
337a9cb09b7SMarc Zyngier 	int ret;
33843b169dbSThomas Abraham 
339f9c74474SAndré Draszik 	if (clk_enable(bank->drvdata->pclk)) {
340f9c74474SAndré Draszik 		dev_err(bank->gpio_chip.parent,
341f9c74474SAndré Draszik 			"unable to enable clock for handling IRQ\n");
342f9c74474SAndré Draszik 		return IRQ_NONE;
343f9c74474SAndré Draszik 	}
344f9c74474SAndré Draszik 
3456cf96df7SJaewon Kim 	if (bank->eint_con_offset)
3466cf96df7SJaewon Kim 		svc = readl(bank->eint_base + EXYNOSAUTO_SVC_OFFSET);
3476cf96df7SJaewon Kim 	else
3488b1bd11cSChanwoo Choi 		svc = readl(bank->eint_base + EXYNOS_SVC_OFFSET);
349f9c74474SAndré Draszik 
350f9c74474SAndré Draszik 	clk_disable(bank->drvdata->pclk);
351f9c74474SAndré Draszik 
35243b169dbSThomas Abraham 	group = EXYNOS_SVC_GROUP(svc);
35343b169dbSThomas Abraham 	pin = svc & EXYNOS_SVC_NUM_MASK;
35443b169dbSThomas Abraham 
35543b169dbSThomas Abraham 	if (!group)
35643b169dbSThomas Abraham 		return IRQ_HANDLED;
35743b169dbSThomas Abraham 	bank += (group - 1);
35843b169dbSThomas Abraham 
359a9cb09b7SMarc Zyngier 	ret = generic_handle_domain_irq(bank->irq_domain, pin);
360a9cb09b7SMarc Zyngier 	if (ret)
36143b169dbSThomas Abraham 		return IRQ_NONE;
362a9cb09b7SMarc Zyngier 
36343b169dbSThomas Abraham 	return IRQ_HANDLED;
36443b169dbSThomas Abraham }
36543b169dbSThomas Abraham 
3667ccbc60cSTomasz Figa struct exynos_eint_gpio_save {
3677ccbc60cSTomasz Figa 	u32 eint_con;
3687ccbc60cSTomasz Figa 	u32 eint_fltcon0;
3697ccbc60cSTomasz Figa 	u32 eint_fltcon1;
370f354157aSJonathan Bakker 	u32 eint_mask;
3717ccbc60cSTomasz Figa };
3727ccbc60cSTomasz Figa 
37343b169dbSThomas Abraham /*
37443b169dbSThomas Abraham  * exynos_eint_gpio_init() - setup handling of external gpio interrupts.
37543b169dbSThomas Abraham  * @d: driver data of samsung pinctrl driver.
37643b169dbSThomas Abraham  */
37785745c87SMarek Szyprowski __init int exynos_eint_gpio_init(struct samsung_pinctrl_drv_data *d)
37843b169dbSThomas Abraham {
379595be726STomasz Figa 	struct samsung_pin_bank *bank;
38043b169dbSThomas Abraham 	struct device *dev = d->dev;
3817ccbc60cSTomasz Figa 	int ret;
3827ccbc60cSTomasz Figa 	int i;
38343b169dbSThomas Abraham 
38443b169dbSThomas Abraham 	if (!d->irq) {
38543b169dbSThomas Abraham 		dev_err(dev, "irq number not available\n");
38643b169dbSThomas Abraham 		return -EINVAL;
38743b169dbSThomas Abraham 	}
38843b169dbSThomas Abraham 
38943b169dbSThomas Abraham 	ret = devm_request_irq(dev, d->irq, exynos_eint_gpio_irq,
39043b169dbSThomas Abraham 					0, dev_name(dev), d);
39143b169dbSThomas Abraham 	if (ret) {
39243b169dbSThomas Abraham 		dev_err(dev, "irq request failed\n");
39343b169dbSThomas Abraham 		return -ENXIO;
39443b169dbSThomas Abraham 	}
39543b169dbSThomas Abraham 
3961bf00d7aSTomasz Figa 	bank = d->pin_banks;
3971bf00d7aSTomasz Figa 	for (i = 0; i < d->nr_banks; ++i, ++bank) {
398595be726STomasz Figa 		if (bank->eint_type != EINT_TYPE_GPIO)
399595be726STomasz Figa 			continue;
40085745c87SMarek Szyprowski 
40185745c87SMarek Szyprowski 		bank->irq_chip = devm_kmemdup(dev, &exynos_gpio_irq_chip,
40285745c87SMarek Szyprowski 					   sizeof(*bank->irq_chip), GFP_KERNEL);
40385745c87SMarek Szyprowski 		if (!bank->irq_chip) {
40485745c87SMarek Szyprowski 			ret = -ENOMEM;
40585745c87SMarek Szyprowski 			goto err_domains;
40685745c87SMarek Szyprowski 		}
40785745c87SMarek Szyprowski 		bank->irq_chip->chip.name = bank->name;
40885745c87SMarek Szyprowski 
409492fca28SAndy Shevchenko 		bank->irq_domain = irq_domain_create_linear(bank->fwnode,
4106f5e41bdSAbhilash Kesavan 				bank->nr_pins, &exynos_eint_irqd_ops, bank);
411595be726STomasz Figa 		if (!bank->irq_domain) {
412595be726STomasz Figa 			dev_err(dev, "gpio irq domain add failed\n");
4137ccbc60cSTomasz Figa 			ret = -ENXIO;
4147ccbc60cSTomasz Figa 			goto err_domains;
4157ccbc60cSTomasz Figa 		}
4167ccbc60cSTomasz Figa 
4177ccbc60cSTomasz Figa 		bank->soc_priv = devm_kzalloc(d->dev,
4187ccbc60cSTomasz Figa 			sizeof(struct exynos_eint_gpio_save), GFP_KERNEL);
4197ccbc60cSTomasz Figa 		if (!bank->soc_priv) {
4207ccbc60cSTomasz Figa 			irq_domain_remove(bank->irq_domain);
4217ccbc60cSTomasz Figa 			ret = -ENOMEM;
4227ccbc60cSTomasz Figa 			goto err_domains;
42343b169dbSThomas Abraham 		}
4240d3d30dbSAbhilash Kesavan 
425595be726STomasz Figa 	}
42643b169dbSThomas Abraham 
42743b169dbSThomas Abraham 	return 0;
4287ccbc60cSTomasz Figa 
4297ccbc60cSTomasz Figa err_domains:
4307ccbc60cSTomasz Figa 	for (--i, --bank; i >= 0; --i, --bank) {
4317ccbc60cSTomasz Figa 		if (bank->eint_type != EINT_TYPE_GPIO)
4327ccbc60cSTomasz Figa 			continue;
4337ccbc60cSTomasz Figa 		irq_domain_remove(bank->irq_domain);
4347ccbc60cSTomasz Figa 	}
4357ccbc60cSTomasz Figa 
4367ccbc60cSTomasz Figa 	return ret;
43743b169dbSThomas Abraham }
43843b169dbSThomas Abraham 
439ad350cd9STomasz Figa static int exynos_wkup_irq_set_wake(struct irq_data *irqd, unsigned int on)
440ad350cd9STomasz Figa {
441a8be2af0SKrzysztof Kozlowski 	struct irq_chip *chip = irq_data_get_irq_chip(irqd);
442a8be2af0SKrzysztof Kozlowski 	struct exynos_irq_chip *our_chip = to_exynos_irq_chip(chip);
443ad350cd9STomasz Figa 	struct samsung_pin_bank *bank = irq_data_get_irq_chip_data(irqd);
444ad350cd9STomasz Figa 	unsigned long bit = 1UL << (2 * bank->eint_offset + irqd->hwirq);
445ad350cd9STomasz Figa 
446*3f36bffaSKrzysztof Kozlowski 	pr_info("wake %s for irq %u (%s-%lu)\n", str_enabled_disabled(on),
4473652dc07SMartin Jücker 		irqd->irq, bank->name, irqd->hwirq);
448ad350cd9STomasz Figa 
449ad350cd9STomasz Figa 	if (!on)
45085745c87SMarek Szyprowski 		*our_chip->eint_wake_mask_value |= bit;
451ad350cd9STomasz Figa 	else
45285745c87SMarek Szyprowski 		*our_chip->eint_wake_mask_value &= ~bit;
453ad350cd9STomasz Figa 
454ad350cd9STomasz Figa 	return 0;
455ad350cd9STomasz Figa }
456ad350cd9STomasz Figa 
457b577a279SJonathan Bakker static void
458b577a279SJonathan Bakker exynos_pinctrl_set_eint_wakeup_mask(struct samsung_pinctrl_drv_data *drvdata,
459b577a279SJonathan Bakker 				    struct exynos_irq_chip *irq_chip)
460b577a279SJonathan Bakker {
461b577a279SJonathan Bakker 	struct regmap *pmu_regs;
462b577a279SJonathan Bakker 
463b577a279SJonathan Bakker 	if (!drvdata->retention_ctrl || !drvdata->retention_ctrl->priv) {
464b577a279SJonathan Bakker 		dev_warn(drvdata->dev,
465b577a279SJonathan Bakker 			 "No retention data configured bank with external wakeup interrupt. Wake-up mask will not be set.\n");
466b577a279SJonathan Bakker 		return;
467b577a279SJonathan Bakker 	}
468b577a279SJonathan Bakker 
469b577a279SJonathan Bakker 	pmu_regs = drvdata->retention_ctrl->priv;
470b577a279SJonathan Bakker 	dev_info(drvdata->dev,
471b577a279SJonathan Bakker 		 "Setting external wakeup interrupt mask: 0x%x\n",
47285745c87SMarek Szyprowski 		 *irq_chip->eint_wake_mask_value);
473b577a279SJonathan Bakker 
474b577a279SJonathan Bakker 	regmap_write(pmu_regs, irq_chip->eint_wake_mask_reg,
47585745c87SMarek Szyprowski 		     *irq_chip->eint_wake_mask_value);
476b577a279SJonathan Bakker }
477b577a279SJonathan Bakker 
478b577a279SJonathan Bakker static void
479b577a279SJonathan Bakker s5pv210_pinctrl_set_eint_wakeup_mask(struct samsung_pinctrl_drv_data *drvdata,
480b577a279SJonathan Bakker 				    struct exynos_irq_chip *irq_chip)
481b577a279SJonathan Bakker 
482b577a279SJonathan Bakker {
483b577a279SJonathan Bakker 	void __iomem *clk_base;
484b577a279SJonathan Bakker 
485b577a279SJonathan Bakker 	if (!drvdata->retention_ctrl || !drvdata->retention_ctrl->priv) {
486b577a279SJonathan Bakker 		dev_warn(drvdata->dev,
487b577a279SJonathan Bakker 			 "No retention data configured bank with external wakeup interrupt. Wake-up mask will not be set.\n");
488b577a279SJonathan Bakker 		return;
489b577a279SJonathan Bakker 	}
490b577a279SJonathan Bakker 
491b577a279SJonathan Bakker 
492b577a279SJonathan Bakker 	clk_base = (void __iomem *) drvdata->retention_ctrl->priv;
493b577a279SJonathan Bakker 
49485745c87SMarek Szyprowski 	__raw_writel(*irq_chip->eint_wake_mask_value,
495b577a279SJonathan Bakker 		     clk_base + irq_chip->eint_wake_mask_reg);
496b577a279SJonathan Bakker }
497b577a279SJonathan Bakker 
49885745c87SMarek Szyprowski static u32 eint_wake_mask_value = EXYNOS_EINT_WAKEUP_MASK_DISABLED;
49943b169dbSThomas Abraham /*
50043b169dbSThomas Abraham  * irq_chip for wakeup interrupts
50143b169dbSThomas Abraham  */
502bb928dfdSKrzysztof Kozlowski static const struct exynos_irq_chip s5pv210_wkup_irq_chip __initconst = {
503bb928dfdSKrzysztof Kozlowski 	.chip = {
504bb928dfdSKrzysztof Kozlowski 		.name = "s5pv210_wkup_irq_chip",
505bb928dfdSKrzysztof Kozlowski 		.irq_unmask = exynos_irq_unmask,
506bb928dfdSKrzysztof Kozlowski 		.irq_mask = exynos_irq_mask,
507bb928dfdSKrzysztof Kozlowski 		.irq_ack = exynos_irq_ack,
508bb928dfdSKrzysztof Kozlowski 		.irq_set_type = exynos_irq_set_type,
509bb928dfdSKrzysztof Kozlowski 		.irq_set_wake = exynos_wkup_irq_set_wake,
510bb928dfdSKrzysztof Kozlowski 		.irq_request_resources = exynos_irq_request_resources,
511bb928dfdSKrzysztof Kozlowski 		.irq_release_resources = exynos_irq_release_resources,
512bb928dfdSKrzysztof Kozlowski 	},
513bb928dfdSKrzysztof Kozlowski 	.eint_con = EXYNOS_WKUP_ECON_OFFSET,
514bb928dfdSKrzysztof Kozlowski 	.eint_mask = EXYNOS_WKUP_EMASK_OFFSET,
515bb928dfdSKrzysztof Kozlowski 	.eint_pend = EXYNOS_WKUP_EPEND_OFFSET,
51685745c87SMarek Szyprowski 	.eint_wake_mask_value = &eint_wake_mask_value,
517b577a279SJonathan Bakker 	/* Only differences with exynos4210_wkup_irq_chip: */
518a8be2af0SKrzysztof Kozlowski 	.eint_wake_mask_reg = S5PV210_EINT_WAKEUP_MASK,
519b577a279SJonathan Bakker 	.set_eint_wakeup_mask = s5pv210_pinctrl_set_eint_wakeup_mask,
520bb928dfdSKrzysztof Kozlowski };
521bb928dfdSKrzysztof Kozlowski 
52271b96c3aSKrzysztof Kozlowski static const struct exynos_irq_chip exynos4210_wkup_irq_chip __initconst = {
5232e4a4fdaSTomasz Figa 	.chip = {
52414c255d3SAbhilash Kesavan 		.name = "exynos4210_wkup_irq_chip",
5252e4a4fdaSTomasz Figa 		.irq_unmask = exynos_irq_unmask,
5262e4a4fdaSTomasz Figa 		.irq_mask = exynos_irq_mask,
5272e4a4fdaSTomasz Figa 		.irq_ack = exynos_irq_ack,
5282e4a4fdaSTomasz Figa 		.irq_set_type = exynos_irq_set_type,
529ad350cd9STomasz Figa 		.irq_set_wake = exynos_wkup_irq_set_wake,
530f6a8249fSTomasz Figa 		.irq_request_resources = exynos_irq_request_resources,
531f6a8249fSTomasz Figa 		.irq_release_resources = exynos_irq_release_resources,
5322e4a4fdaSTomasz Figa 	},
5332e4a4fdaSTomasz Figa 	.eint_con = EXYNOS_WKUP_ECON_OFFSET,
5342e4a4fdaSTomasz Figa 	.eint_mask = EXYNOS_WKUP_EMASK_OFFSET,
5352e4a4fdaSTomasz Figa 	.eint_pend = EXYNOS_WKUP_EPEND_OFFSET,
53685745c87SMarek Szyprowski 	.eint_wake_mask_value = &eint_wake_mask_value,
537a8be2af0SKrzysztof Kozlowski 	.eint_wake_mask_reg = EXYNOS_EINT_WAKEUP_MASK,
538b577a279SJonathan Bakker 	.set_eint_wakeup_mask = exynos_pinctrl_set_eint_wakeup_mask,
53943b169dbSThomas Abraham };
54043b169dbSThomas Abraham 
54171b96c3aSKrzysztof Kozlowski static const struct exynos_irq_chip exynos7_wkup_irq_chip __initconst = {
54214c255d3SAbhilash Kesavan 	.chip = {
54314c255d3SAbhilash Kesavan 		.name = "exynos7_wkup_irq_chip",
54414c255d3SAbhilash Kesavan 		.irq_unmask = exynos_irq_unmask,
54514c255d3SAbhilash Kesavan 		.irq_mask = exynos_irq_mask,
54614c255d3SAbhilash Kesavan 		.irq_ack = exynos_irq_ack,
54714c255d3SAbhilash Kesavan 		.irq_set_type = exynos_irq_set_type,
54814c255d3SAbhilash Kesavan 		.irq_set_wake = exynos_wkup_irq_set_wake,
54914c255d3SAbhilash Kesavan 		.irq_request_resources = exynos_irq_request_resources,
55014c255d3SAbhilash Kesavan 		.irq_release_resources = exynos_irq_release_resources,
55114c255d3SAbhilash Kesavan 	},
55214c255d3SAbhilash Kesavan 	.eint_con = EXYNOS7_WKUP_ECON_OFFSET,
55314c255d3SAbhilash Kesavan 	.eint_mask = EXYNOS7_WKUP_EMASK_OFFSET,
55414c255d3SAbhilash Kesavan 	.eint_pend = EXYNOS7_WKUP_EPEND_OFFSET,
55585745c87SMarek Szyprowski 	.eint_wake_mask_value = &eint_wake_mask_value,
556a8be2af0SKrzysztof Kozlowski 	.eint_wake_mask_reg = EXYNOS5433_EINT_WAKEUP_MASK,
557b577a279SJonathan Bakker 	.set_eint_wakeup_mask = exynos_pinctrl_set_eint_wakeup_mask,
55814c255d3SAbhilash Kesavan };
55914c255d3SAbhilash Kesavan 
5606cf96df7SJaewon Kim static const struct exynos_irq_chip exynosautov920_wkup_irq_chip __initconst = {
5616cf96df7SJaewon Kim 	.chip = {
5626cf96df7SJaewon Kim 		.name = "exynosautov920_wkup_irq_chip",
5636cf96df7SJaewon Kim 		.irq_unmask = exynos_irq_unmask,
5646cf96df7SJaewon Kim 		.irq_mask = exynos_irq_mask,
5656cf96df7SJaewon Kim 		.irq_ack = exynos_irq_ack,
5666cf96df7SJaewon Kim 		.irq_set_type = exynos_irq_set_type,
5676cf96df7SJaewon Kim 		.irq_set_wake = exynos_wkup_irq_set_wake,
5686cf96df7SJaewon Kim 		.irq_request_resources = exynos_irq_request_resources,
5696cf96df7SJaewon Kim 		.irq_release_resources = exynos_irq_release_resources,
5706cf96df7SJaewon Kim 	},
5716cf96df7SJaewon Kim 	.eint_wake_mask_value = &eint_wake_mask_value,
5726cf96df7SJaewon Kim 	.eint_wake_mask_reg = EXYNOS5433_EINT_WAKEUP_MASK,
5736cf96df7SJaewon Kim 	.set_eint_wakeup_mask = exynos_pinctrl_set_eint_wakeup_mask,
5746cf96df7SJaewon Kim };
5756cf96df7SJaewon Kim 
57614c255d3SAbhilash Kesavan /* list of external wakeup controllers supported */
57714c255d3SAbhilash Kesavan static const struct of_device_id exynos_wkup_irq_ids[] = {
578bb928dfdSKrzysztof Kozlowski 	{ .compatible = "samsung,s5pv210-wakeup-eint",
579bb928dfdSKrzysztof Kozlowski 			.data = &s5pv210_wkup_irq_chip },
58014c255d3SAbhilash Kesavan 	{ .compatible = "samsung,exynos4210-wakeup-eint",
58114c255d3SAbhilash Kesavan 			.data = &exynos4210_wkup_irq_chip },
58214c255d3SAbhilash Kesavan 	{ .compatible = "samsung,exynos7-wakeup-eint",
58314c255d3SAbhilash Kesavan 			.data = &exynos7_wkup_irq_chip },
584832ae134SKrzysztof Kozlowski 	{ .compatible = "samsung,exynos850-wakeup-eint",
585832ae134SKrzysztof Kozlowski 			.data = &exynos7_wkup_irq_chip },
586832ae134SKrzysztof Kozlowski 	{ .compatible = "samsung,exynosautov9-wakeup-eint",
587832ae134SKrzysztof Kozlowski 			.data = &exynos7_wkup_irq_chip },
5886cf96df7SJaewon Kim 	{ .compatible = "samsung,exynosautov920-wakeup-eint",
5896cf96df7SJaewon Kim 			.data = &exynosautov920_wkup_irq_chip },
59014c255d3SAbhilash Kesavan 	{ }
59114c255d3SAbhilash Kesavan };
59214c255d3SAbhilash Kesavan 
59343b169dbSThomas Abraham /* interrupt handler for wakeup interrupts 0..15 */
594bd0b9ac4SThomas Gleixner static void exynos_irq_eint0_15(struct irq_desc *desc)
59543b169dbSThomas Abraham {
5965663bb27SJiang Liu 	struct exynos_weint_data *eintd = irq_desc_get_handler_data(desc);
597a04b07c0STomasz Figa 	struct samsung_pin_bank *bank = eintd->bank;
5985663bb27SJiang Liu 	struct irq_chip *chip = irq_desc_get_chip(desc);
59943b169dbSThomas Abraham 
60043b169dbSThomas Abraham 	chained_irq_enter(chip, desc);
60143b169dbSThomas Abraham 
602a9cb09b7SMarc Zyngier 	generic_handle_domain_irq(bank->irq_domain, eintd->irq);
60326fecf0bSperr perr 
60443b169dbSThomas Abraham 	chained_irq_exit(chip, desc);
60543b169dbSThomas Abraham }
60643b169dbSThomas Abraham 
607fa0c10a5SKrzysztof Kozlowski static inline void exynos_irq_demux_eint(unsigned int pend,
60843b169dbSThomas Abraham 						struct irq_domain *domain)
60943b169dbSThomas Abraham {
61043b169dbSThomas Abraham 	unsigned int irq;
61143b169dbSThomas Abraham 
61243b169dbSThomas Abraham 	while (pend) {
61343b169dbSThomas Abraham 		irq = fls(pend) - 1;
614a9cb09b7SMarc Zyngier 		generic_handle_domain_irq(domain, irq);
61543b169dbSThomas Abraham 		pend &= ~(1 << irq);
61643b169dbSThomas Abraham 	}
61743b169dbSThomas Abraham }
61843b169dbSThomas Abraham 
61943b169dbSThomas Abraham /* interrupt handler for wakeup interrupt 16 */
620bd0b9ac4SThomas Gleixner static void exynos_irq_demux_eint16_31(struct irq_desc *desc)
62143b169dbSThomas Abraham {
6225663bb27SJiang Liu 	struct irq_chip *chip = irq_desc_get_chip(desc);
6235663bb27SJiang Liu 	struct exynos_muxed_weint_data *eintd = irq_desc_get_handler_data(desc);
624fa0c10a5SKrzysztof Kozlowski 	unsigned int pend;
625fa0c10a5SKrzysztof Kozlowski 	unsigned int mask;
626a04b07c0STomasz Figa 	int i;
62743b169dbSThomas Abraham 
62843b169dbSThomas Abraham 	chained_irq_enter(chip, desc);
629a04b07c0STomasz Figa 
630f9c74474SAndré Draszik 	/*
631f9c74474SAndré Draszik 	 * just enable the clock once here, to avoid an enable/disable dance for
632f9c74474SAndré Draszik 	 * each bank.
633f9c74474SAndré Draszik 	 */
634f9c74474SAndré Draszik 	if (eintd->nr_banks) {
635f9c74474SAndré Draszik 		struct samsung_pin_bank *b = eintd->banks[0];
636f9c74474SAndré Draszik 
637f9c74474SAndré Draszik 		if (clk_enable(b->drvdata->pclk)) {
638f9c74474SAndré Draszik 			dev_err(b->gpio_chip.parent,
639f9c74474SAndré Draszik 				"unable to enable clock for pending IRQs\n");
640f686a2b5SChristophe JAILLET 			goto out;
641f9c74474SAndré Draszik 		}
642f9c74474SAndré Draszik 	}
643f9c74474SAndré Draszik 
644a04b07c0STomasz Figa 	for (i = 0; i < eintd->nr_banks; ++i) {
645a04b07c0STomasz Figa 		struct samsung_pin_bank *b = eintd->banks[i];
6468b1bd11cSChanwoo Choi 		pend = readl(b->eint_base + b->irq_chip->eint_pend
6472e4a4fdaSTomasz Figa 				+ b->eint_offset);
6488b1bd11cSChanwoo Choi 		mask = readl(b->eint_base + b->irq_chip->eint_mask
6492e4a4fdaSTomasz Figa 				+ b->eint_offset);
650a04b07c0STomasz Figa 		exynos_irq_demux_eint(pend & ~mask, b->irq_domain);
651a04b07c0STomasz Figa 	}
652a04b07c0STomasz Figa 
653f9c74474SAndré Draszik 	if (eintd->nr_banks)
654f9c74474SAndré Draszik 		clk_disable(eintd->banks[0]->drvdata->pclk);
655f9c74474SAndré Draszik 
656f686a2b5SChristophe JAILLET out:
65743b169dbSThomas Abraham 	chained_irq_exit(chip, desc);
65843b169dbSThomas Abraham }
65943b169dbSThomas Abraham 
66043b169dbSThomas Abraham /*
66143b169dbSThomas Abraham  * exynos_eint_wkup_init() - setup handling of external wakeup interrupts.
66243b169dbSThomas Abraham  * @d: driver data of samsung pinctrl driver.
66343b169dbSThomas Abraham  */
66485745c87SMarek Szyprowski __init int exynos_eint_wkup_init(struct samsung_pinctrl_drv_data *d)
66543b169dbSThomas Abraham {
66643b169dbSThomas Abraham 	struct device *dev = d->dev;
667d59c2396SPeng Fan 	struct device_node *wkup_np __free(device_node) = NULL;
668c3ad056bSTomasz Figa 	struct device_node *np;
669a04b07c0STomasz Figa 	struct samsung_pin_bank *bank;
67043b169dbSThomas Abraham 	struct exynos_weint_data *weint_data;
671a04b07c0STomasz Figa 	struct exynos_muxed_weint_data *muxed_data;
67285745c87SMarek Szyprowski 	const struct exynos_irq_chip *irq_chip;
673a04b07c0STomasz Figa 	unsigned int muxed_banks = 0;
674a04b07c0STomasz Figa 	unsigned int i;
67543b169dbSThomas Abraham 	int idx, irq;
67643b169dbSThomas Abraham 
677c3ad056bSTomasz Figa 	for_each_child_of_node(dev->of_node, np) {
67814c255d3SAbhilash Kesavan 		const struct of_device_id *match;
67914c255d3SAbhilash Kesavan 
68014c255d3SAbhilash Kesavan 		match = of_match_node(exynos_wkup_irq_ids, np);
68114c255d3SAbhilash Kesavan 		if (match) {
68285745c87SMarek Szyprowski 			irq_chip = match->data;
683c3ad056bSTomasz Figa 			wkup_np = np;
684c3ad056bSTomasz Figa 			break;
68543b169dbSThomas Abraham 		}
686c3ad056bSTomasz Figa 	}
687c3ad056bSTomasz Figa 	if (!wkup_np)
688c3ad056bSTomasz Figa 		return -ENODEV;
68943b169dbSThomas Abraham 
6901bf00d7aSTomasz Figa 	bank = d->pin_banks;
6911bf00d7aSTomasz Figa 	for (i = 0; i < d->nr_banks; ++i, ++bank) {
692a04b07c0STomasz Figa 		if (bank->eint_type != EINT_TYPE_WKUP)
693a04b07c0STomasz Figa 			continue;
694a04b07c0STomasz Figa 
69585745c87SMarek Szyprowski 		bank->irq_chip = devm_kmemdup(dev, irq_chip, sizeof(*irq_chip),
69685745c87SMarek Szyprowski 					      GFP_KERNEL);
697d59c2396SPeng Fan 		if (!bank->irq_chip)
69885745c87SMarek Szyprowski 			return -ENOMEM;
69985745c87SMarek Szyprowski 		bank->irq_chip->chip.name = bank->name;
70085745c87SMarek Szyprowski 
701492fca28SAndy Shevchenko 		bank->irq_domain = irq_domain_create_linear(bank->fwnode,
7026f5e41bdSAbhilash Kesavan 				bank->nr_pins, &exynos_eint_irqd_ops, bank);
703a04b07c0STomasz Figa 		if (!bank->irq_domain) {
704a04b07c0STomasz Figa 			dev_err(dev, "wkup irq domain add failed\n");
70543b169dbSThomas Abraham 			return -ENXIO;
70643b169dbSThomas Abraham 		}
70743b169dbSThomas Abraham 
708492fca28SAndy Shevchenko 		if (!fwnode_property_present(bank->fwnode, "interrupts")) {
709a04b07c0STomasz Figa 			bank->eint_type = EINT_TYPE_WKUP_MUX;
710a04b07c0STomasz Figa 			++muxed_banks;
711a04b07c0STomasz Figa 			continue;
712a04b07c0STomasz Figa 		}
713a04b07c0STomasz Figa 
714a86854d0SKees Cook 		weint_data = devm_kcalloc(dev,
715a86854d0SKees Cook 					  bank->nr_pins, sizeof(*weint_data),
716a86854d0SKees Cook 					  GFP_KERNEL);
717d59c2396SPeng Fan 		if (!weint_data)
71843b169dbSThomas Abraham 			return -ENOMEM;
71943b169dbSThomas Abraham 
720a04b07c0STomasz Figa 		for (idx = 0; idx < bank->nr_pins; ++idx) {
721492fca28SAndy Shevchenko 			irq = irq_of_parse_and_map(to_of_node(bank->fwnode), idx);
722a04b07c0STomasz Figa 			if (!irq) {
723a04b07c0STomasz Figa 				dev_err(dev, "irq number for eint-%s-%d not found\n",
724a04b07c0STomasz Figa 							bank->name, idx);
725a04b07c0STomasz Figa 				continue;
72643b169dbSThomas Abraham 			}
72743b169dbSThomas Abraham 			weint_data[idx].irq = idx;
728a04b07c0STomasz Figa 			weint_data[idx].bank = bank;
729c21f7849SThomas Gleixner 			irq_set_chained_handler_and_data(irq,
730c21f7849SThomas Gleixner 							 exynos_irq_eint0_15,
731c21f7849SThomas Gleixner 							 &weint_data[idx]);
73243b169dbSThomas Abraham 		}
73343b169dbSThomas Abraham 	}
734a04b07c0STomasz Figa 
735d59c2396SPeng Fan 	if (!muxed_banks)
736a04b07c0STomasz Figa 		return 0;
737a04b07c0STomasz Figa 
738a04b07c0STomasz Figa 	irq = irq_of_parse_and_map(wkup_np, 0);
739a04b07c0STomasz Figa 	if (!irq) {
740a04b07c0STomasz Figa 		dev_err(dev, "irq number for muxed EINTs not found\n");
741a04b07c0STomasz Figa 		return 0;
742a04b07c0STomasz Figa 	}
743a04b07c0STomasz Figa 
744a04b07c0STomasz Figa 	muxed_data = devm_kzalloc(dev, sizeof(*muxed_data)
745a04b07c0STomasz Figa 		+ muxed_banks*sizeof(struct samsung_pin_bank *), GFP_KERNEL);
746fa5c0f46SMarek Szyprowski 	if (!muxed_data)
747a04b07c0STomasz Figa 		return -ENOMEM;
7484e1e2111SKees Cook 	muxed_data->nr_banks = muxed_banks;
749a04b07c0STomasz Figa 
750bb56fc35SThomas Gleixner 	irq_set_chained_handler_and_data(irq, exynos_irq_demux_eint16_31,
751bb56fc35SThomas Gleixner 					 muxed_data);
752a04b07c0STomasz Figa 
7531bf00d7aSTomasz Figa 	bank = d->pin_banks;
754a04b07c0STomasz Figa 	idx = 0;
7551bf00d7aSTomasz Figa 	for (i = 0; i < d->nr_banks; ++i, ++bank) {
756a04b07c0STomasz Figa 		if (bank->eint_type != EINT_TYPE_WKUP_MUX)
757a04b07c0STomasz Figa 			continue;
758a04b07c0STomasz Figa 
759a04b07c0STomasz Figa 		muxed_data->banks[idx++] = bank;
760a04b07c0STomasz Figa 	}
761a04b07c0STomasz Figa 
76243b169dbSThomas Abraham 	return 0;
76343b169dbSThomas Abraham }
76443b169dbSThomas Abraham 
7657ccbc60cSTomasz Figa static void exynos_pinctrl_suspend_bank(
7667ccbc60cSTomasz Figa 				struct samsung_pinctrl_drv_data *drvdata,
7677ccbc60cSTomasz Figa 				struct samsung_pin_bank *bank)
7687ccbc60cSTomasz Figa {
7697ccbc60cSTomasz Figa 	struct exynos_eint_gpio_save *save = bank->soc_priv;
7701b09c2b8SKrzysztof Kozlowski 	const void __iomem *regs = bank->eint_base;
7717ccbc60cSTomasz Figa 
772f9c74474SAndré Draszik 	if (clk_enable(bank->drvdata->pclk)) {
773f9c74474SAndré Draszik 		dev_err(bank->gpio_chip.parent,
774f9c74474SAndré Draszik 			"unable to enable clock for saving state\n");
775f9c74474SAndré Draszik 		return;
776f9c74474SAndré Draszik 	}
777f9c74474SAndré Draszik 
7787ccbc60cSTomasz Figa 	save->eint_con = readl(regs + EXYNOS_GPIO_ECON_OFFSET
7797ccbc60cSTomasz Figa 						+ bank->eint_offset);
7807ccbc60cSTomasz Figa 	save->eint_fltcon0 = readl(regs + EXYNOS_GPIO_EFLTCON_OFFSET
7817ccbc60cSTomasz Figa 						+ 2 * bank->eint_offset);
7827ccbc60cSTomasz Figa 	save->eint_fltcon1 = readl(regs + EXYNOS_GPIO_EFLTCON_OFFSET
7837ccbc60cSTomasz Figa 						+ 2 * bank->eint_offset + 4);
784f354157aSJonathan Bakker 	save->eint_mask = readl(regs + bank->irq_chip->eint_mask
785f354157aSJonathan Bakker 						+ bank->eint_offset);
7867ccbc60cSTomasz Figa 
787f9c74474SAndré Draszik 	clk_disable(bank->drvdata->pclk);
788f9c74474SAndré Draszik 
7897ccbc60cSTomasz Figa 	pr_debug("%s: save     con %#010x\n", bank->name, save->eint_con);
7907ccbc60cSTomasz Figa 	pr_debug("%s: save fltcon0 %#010x\n", bank->name, save->eint_fltcon0);
7917ccbc60cSTomasz Figa 	pr_debug("%s: save fltcon1 %#010x\n", bank->name, save->eint_fltcon1);
792f354157aSJonathan Bakker 	pr_debug("%s: save    mask %#010x\n", bank->name, save->eint_mask);
7937ccbc60cSTomasz Figa }
7947ccbc60cSTomasz Figa 
795884fdaa5SJaewon Kim static void exynosauto_pinctrl_suspend_bank(struct samsung_pinctrl_drv_data *drvdata,
796884fdaa5SJaewon Kim 					    struct samsung_pin_bank *bank)
797884fdaa5SJaewon Kim {
798884fdaa5SJaewon Kim 	struct exynos_eint_gpio_save *save = bank->soc_priv;
7991b09c2b8SKrzysztof Kozlowski 	const void __iomem *regs = bank->eint_base;
800884fdaa5SJaewon Kim 
801f9c74474SAndré Draszik 	if (clk_enable(bank->drvdata->pclk)) {
802f9c74474SAndré Draszik 		dev_err(bank->gpio_chip.parent,
803f9c74474SAndré Draszik 			"unable to enable clock for saving state\n");
804f9c74474SAndré Draszik 		return;
805f9c74474SAndré Draszik 	}
806f9c74474SAndré Draszik 
807884fdaa5SJaewon Kim 	save->eint_con = readl(regs + bank->pctl_offset + bank->eint_con_offset);
808884fdaa5SJaewon Kim 	save->eint_mask = readl(regs + bank->pctl_offset + bank->eint_mask_offset);
809884fdaa5SJaewon Kim 
810f9c74474SAndré Draszik 	clk_disable(bank->drvdata->pclk);
811f9c74474SAndré Draszik 
812884fdaa5SJaewon Kim 	pr_debug("%s: save     con %#010x\n", bank->name, save->eint_con);
813884fdaa5SJaewon Kim 	pr_debug("%s: save    mask %#010x\n", bank->name, save->eint_mask);
814884fdaa5SJaewon Kim }
815884fdaa5SJaewon Kim 
816cfa76ddfSKrzysztof Kozlowski void exynos_pinctrl_suspend(struct samsung_pinctrl_drv_data *drvdata)
8177ccbc60cSTomasz Figa {
8181bf00d7aSTomasz Figa 	struct samsung_pin_bank *bank = drvdata->pin_banks;
819a8be2af0SKrzysztof Kozlowski 	struct exynos_irq_chip *irq_chip = NULL;
8207ccbc60cSTomasz Figa 	int i;
8217ccbc60cSTomasz Figa 
822a8be2af0SKrzysztof Kozlowski 	for (i = 0; i < drvdata->nr_banks; ++i, ++bank) {
823884fdaa5SJaewon Kim 		if (bank->eint_type == EINT_TYPE_GPIO) {
824884fdaa5SJaewon Kim 			if (bank->eint_con_offset)
825884fdaa5SJaewon Kim 				exynosauto_pinctrl_suspend_bank(drvdata, bank);
826884fdaa5SJaewon Kim 			else
8277ccbc60cSTomasz Figa 				exynos_pinctrl_suspend_bank(drvdata, bank);
828884fdaa5SJaewon Kim 		}
829a8be2af0SKrzysztof Kozlowski 		else if (bank->eint_type == EINT_TYPE_WKUP) {
830a8be2af0SKrzysztof Kozlowski 			if (!irq_chip) {
831a8be2af0SKrzysztof Kozlowski 				irq_chip = bank->irq_chip;
832b577a279SJonathan Bakker 				irq_chip->set_eint_wakeup_mask(drvdata,
833a8be2af0SKrzysztof Kozlowski 							       irq_chip);
834a8be2af0SKrzysztof Kozlowski 			}
835a8be2af0SKrzysztof Kozlowski 		}
836a8be2af0SKrzysztof Kozlowski 	}
8377ccbc60cSTomasz Figa }
8387ccbc60cSTomasz Figa 
8397ccbc60cSTomasz Figa static void exynos_pinctrl_resume_bank(
8407ccbc60cSTomasz Figa 				struct samsung_pinctrl_drv_data *drvdata,
8417ccbc60cSTomasz Figa 				struct samsung_pin_bank *bank)
8427ccbc60cSTomasz Figa {
8437ccbc60cSTomasz Figa 	struct exynos_eint_gpio_save *save = bank->soc_priv;
8448b1bd11cSChanwoo Choi 	void __iomem *regs = bank->eint_base;
8457ccbc60cSTomasz Figa 
846f9c74474SAndré Draszik 	if (clk_enable(bank->drvdata->pclk)) {
847f9c74474SAndré Draszik 		dev_err(bank->gpio_chip.parent,
848f9c74474SAndré Draszik 			"unable to enable clock for restoring state\n");
849f9c74474SAndré Draszik 		return;
850f9c74474SAndré Draszik 	}
851f9c74474SAndré Draszik 
8527ccbc60cSTomasz Figa 	pr_debug("%s:     con %#010x => %#010x\n", bank->name,
8537ccbc60cSTomasz Figa 			readl(regs + EXYNOS_GPIO_ECON_OFFSET
8547ccbc60cSTomasz Figa 			+ bank->eint_offset), save->eint_con);
8557ccbc60cSTomasz Figa 	pr_debug("%s: fltcon0 %#010x => %#010x\n", bank->name,
8567ccbc60cSTomasz Figa 			readl(regs + EXYNOS_GPIO_EFLTCON_OFFSET
8577ccbc60cSTomasz Figa 			+ 2 * bank->eint_offset), save->eint_fltcon0);
8587ccbc60cSTomasz Figa 	pr_debug("%s: fltcon1 %#010x => %#010x\n", bank->name,
8597ccbc60cSTomasz Figa 			readl(regs + EXYNOS_GPIO_EFLTCON_OFFSET
8607ccbc60cSTomasz Figa 			+ 2 * bank->eint_offset + 4), save->eint_fltcon1);
861f354157aSJonathan Bakker 	pr_debug("%s:    mask %#010x => %#010x\n", bank->name,
862f354157aSJonathan Bakker 			readl(regs + bank->irq_chip->eint_mask
863f354157aSJonathan Bakker 			+ bank->eint_offset), save->eint_mask);
8647ccbc60cSTomasz Figa 
8657ccbc60cSTomasz Figa 	writel(save->eint_con, regs + EXYNOS_GPIO_ECON_OFFSET
8667ccbc60cSTomasz Figa 						+ bank->eint_offset);
8677ccbc60cSTomasz Figa 	writel(save->eint_fltcon0, regs + EXYNOS_GPIO_EFLTCON_OFFSET
8687ccbc60cSTomasz Figa 						+ 2 * bank->eint_offset);
8697ccbc60cSTomasz Figa 	writel(save->eint_fltcon1, regs + EXYNOS_GPIO_EFLTCON_OFFSET
8707ccbc60cSTomasz Figa 						+ 2 * bank->eint_offset + 4);
871f354157aSJonathan Bakker 	writel(save->eint_mask, regs + bank->irq_chip->eint_mask
872f354157aSJonathan Bakker 						+ bank->eint_offset);
873f9c74474SAndré Draszik 
874f9c74474SAndré Draszik 	clk_disable(bank->drvdata->pclk);
8757ccbc60cSTomasz Figa }
8767ccbc60cSTomasz Figa 
877884fdaa5SJaewon Kim static void exynosauto_pinctrl_resume_bank(struct samsung_pinctrl_drv_data *drvdata,
878884fdaa5SJaewon Kim 					   struct samsung_pin_bank *bank)
879884fdaa5SJaewon Kim {
880884fdaa5SJaewon Kim 	struct exynos_eint_gpio_save *save = bank->soc_priv;
881884fdaa5SJaewon Kim 	void __iomem *regs = bank->eint_base;
882884fdaa5SJaewon Kim 
883f9c74474SAndré Draszik 	if (clk_enable(bank->drvdata->pclk)) {
884f9c74474SAndré Draszik 		dev_err(bank->gpio_chip.parent,
885f9c74474SAndré Draszik 			"unable to enable clock for restoring state\n");
886f9c74474SAndré Draszik 		return;
887f9c74474SAndré Draszik 	}
888f9c74474SAndré Draszik 
889884fdaa5SJaewon Kim 	pr_debug("%s:     con %#010x => %#010x\n", bank->name,
890884fdaa5SJaewon Kim 		 readl(regs + bank->pctl_offset + bank->eint_con_offset), save->eint_con);
891884fdaa5SJaewon Kim 	pr_debug("%s:    mask %#010x => %#010x\n", bank->name,
892884fdaa5SJaewon Kim 		 readl(regs + bank->pctl_offset + bank->eint_mask_offset), save->eint_mask);
893884fdaa5SJaewon Kim 
894884fdaa5SJaewon Kim 	writel(save->eint_con, regs + bank->pctl_offset + bank->eint_con_offset);
895884fdaa5SJaewon Kim 	writel(save->eint_mask, regs + bank->pctl_offset + bank->eint_mask_offset);
896f9c74474SAndré Draszik 
897f9c74474SAndré Draszik 	clk_disable(bank->drvdata->pclk);
898884fdaa5SJaewon Kim }
899884fdaa5SJaewon Kim 
900cfa76ddfSKrzysztof Kozlowski void exynos_pinctrl_resume(struct samsung_pinctrl_drv_data *drvdata)
9017ccbc60cSTomasz Figa {
9021bf00d7aSTomasz Figa 	struct samsung_pin_bank *bank = drvdata->pin_banks;
9037ccbc60cSTomasz Figa 	int i;
9047ccbc60cSTomasz Figa 
9051bf00d7aSTomasz Figa 	for (i = 0; i < drvdata->nr_banks; ++i, ++bank)
906884fdaa5SJaewon Kim 		if (bank->eint_type == EINT_TYPE_GPIO) {
907884fdaa5SJaewon Kim 			if (bank->eint_con_offset)
908884fdaa5SJaewon Kim 				exynosauto_pinctrl_resume_bank(drvdata, bank);
909884fdaa5SJaewon Kim 			else
9107ccbc60cSTomasz Figa 				exynos_pinctrl_resume_bank(drvdata, bank);
9117ccbc60cSTomasz Figa 		}
912884fdaa5SJaewon Kim }
9137ccbc60cSTomasz Figa 
91407731019SMarek Szyprowski static void exynos_retention_enable(struct samsung_pinctrl_drv_data *drvdata)
91507731019SMarek Szyprowski {
91607731019SMarek Szyprowski 	if (drvdata->retention_ctrl->refcnt)
91707731019SMarek Szyprowski 		atomic_inc(drvdata->retention_ctrl->refcnt);
91807731019SMarek Szyprowski }
91907731019SMarek Szyprowski 
92007731019SMarek Szyprowski static void exynos_retention_disable(struct samsung_pinctrl_drv_data *drvdata)
92107731019SMarek Szyprowski {
92207731019SMarek Szyprowski 	struct samsung_retention_ctrl *ctrl = drvdata->retention_ctrl;
92307731019SMarek Szyprowski 	struct regmap *pmu_regs = ctrl->priv;
92407731019SMarek Szyprowski 	int i;
92507731019SMarek Szyprowski 
92607731019SMarek Szyprowski 	if (ctrl->refcnt && !atomic_dec_and_test(ctrl->refcnt))
92707731019SMarek Szyprowski 		return;
92807731019SMarek Szyprowski 
92907731019SMarek Szyprowski 	for (i = 0; i < ctrl->nr_regs; i++)
93007731019SMarek Szyprowski 		regmap_write(pmu_regs, ctrl->regs[i], ctrl->value);
93107731019SMarek Szyprowski }
93207731019SMarek Szyprowski 
933cfa76ddfSKrzysztof Kozlowski struct samsung_retention_ctrl *
93407731019SMarek Szyprowski exynos_retention_init(struct samsung_pinctrl_drv_data *drvdata,
93507731019SMarek Szyprowski 		      const struct samsung_retention_data *data)
93607731019SMarek Szyprowski {
93707731019SMarek Szyprowski 	struct samsung_retention_ctrl *ctrl;
93807731019SMarek Szyprowski 	struct regmap *pmu_regs;
9398fe9bf07SMarek Szyprowski 	int i;
94007731019SMarek Szyprowski 
94107731019SMarek Szyprowski 	ctrl = devm_kzalloc(drvdata->dev, sizeof(*ctrl), GFP_KERNEL);
94207731019SMarek Szyprowski 	if (!ctrl)
94307731019SMarek Szyprowski 		return ERR_PTR(-ENOMEM);
94407731019SMarek Szyprowski 
94507731019SMarek Szyprowski 	pmu_regs = exynos_get_pmu_regmap();
94607731019SMarek Szyprowski 	if (IS_ERR(pmu_regs))
94707731019SMarek Szyprowski 		return ERR_CAST(pmu_regs);
94807731019SMarek Szyprowski 
94907731019SMarek Szyprowski 	ctrl->priv = pmu_regs;
95007731019SMarek Szyprowski 	ctrl->regs = data->regs;
95107731019SMarek Szyprowski 	ctrl->nr_regs = data->nr_regs;
95207731019SMarek Szyprowski 	ctrl->value = data->value;
95307731019SMarek Szyprowski 	ctrl->refcnt = data->refcnt;
95407731019SMarek Szyprowski 	ctrl->enable = exynos_retention_enable;
95507731019SMarek Szyprowski 	ctrl->disable = exynos_retention_disable;
95607731019SMarek Szyprowski 
9578fe9bf07SMarek Szyprowski 	/* Ensure that retention is disabled on driver init */
9588fe9bf07SMarek Szyprowski 	for (i = 0; i < ctrl->nr_regs; i++)
9598fe9bf07SMarek Szyprowski 		regmap_write(pmu_regs, ctrl->regs[i], ctrl->value);
9608fe9bf07SMarek Szyprowski 
96107731019SMarek Szyprowski 	return ctrl;
96207731019SMarek Szyprowski }
963