xref: /linux/drivers/pinctrl/samsung/pinctrl-exynos.c (revision a8be2af0218cf037704dc2e733bf56d6560fa324)
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 
1643b169dbSThomas Abraham #include <linux/device.h>
1743b169dbSThomas Abraham #include <linux/interrupt.h>
1843b169dbSThomas Abraham #include <linux/irqdomain.h>
1943b169dbSThomas Abraham #include <linux/irq.h>
20de88cbb7SCatalin Marinas #include <linux/irqchip/chained_irq.h>
21cfa76ddfSKrzysztof Kozlowski #include <linux/of.h>
2243b169dbSThomas Abraham #include <linux/of_irq.h>
2343b169dbSThomas Abraham #include <linux/slab.h>
2419846950STomasz Figa #include <linux/spinlock.h>
2507731019SMarek Szyprowski #include <linux/regmap.h>
2643b169dbSThomas Abraham #include <linux/err.h>
2707731019SMarek Szyprowski #include <linux/soc/samsung/exynos-pmu.h>
28*a8be2af0SKrzysztof Kozlowski #include <linux/soc/samsung/exynos-regs-pmu.h>
2943b169dbSThomas Abraham 
304460dc21SKrzysztof Kozlowski #include <dt-bindings/pinctrl/samsung.h>
314460dc21SKrzysztof Kozlowski 
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;
41*a8be2af0SKrzysztof Kozlowski 	u32 eint_wake_mask_value;
42*a8be2af0SKrzysztof Kozlowski 	u32 eint_wake_mask_reg;
432e4a4fdaSTomasz Figa };
442e4a4fdaSTomasz Figa 
452e4a4fdaSTomasz Figa static inline struct exynos_irq_chip *to_exynos_irq_chip(struct irq_chip *chip)
462e4a4fdaSTomasz Figa {
472e4a4fdaSTomasz Figa 	return container_of(chip, struct exynos_irq_chip, chip);
482e4a4fdaSTomasz Figa }
49499147c9STomasz Figa 
502e4a4fdaSTomasz Figa static void exynos_irq_mask(struct irq_data *irqd)
5143b169dbSThomas Abraham {
522e4a4fdaSTomasz Figa 	struct irq_chip *chip = irq_data_get_irq_chip(irqd);
532e4a4fdaSTomasz Figa 	struct exynos_irq_chip *our_chip = to_exynos_irq_chip(chip);
54595be726STomasz Figa 	struct samsung_pin_bank *bank = irq_data_get_irq_chip_data(irqd);
552e4a4fdaSTomasz Figa 	unsigned long reg_mask = our_chip->eint_mask + bank->eint_offset;
5643b169dbSThomas Abraham 	unsigned long mask;
575ae8cf79SDoug Anderson 	unsigned long flags;
585ae8cf79SDoug Anderson 
595ae8cf79SDoug Anderson 	spin_lock_irqsave(&bank->slock, flags);
6043b169dbSThomas Abraham 
618b1bd11cSChanwoo Choi 	mask = readl(bank->eint_base + reg_mask);
62595be726STomasz Figa 	mask |= 1 << irqd->hwirq;
638b1bd11cSChanwoo Choi 	writel(mask, bank->eint_base + reg_mask);
645ae8cf79SDoug Anderson 
655ae8cf79SDoug Anderson 	spin_unlock_irqrestore(&bank->slock, flags);
6643b169dbSThomas Abraham }
6743b169dbSThomas Abraham 
682e4a4fdaSTomasz Figa static void exynos_irq_ack(struct irq_data *irqd)
6943b169dbSThomas Abraham {
702e4a4fdaSTomasz Figa 	struct irq_chip *chip = irq_data_get_irq_chip(irqd);
712e4a4fdaSTomasz Figa 	struct exynos_irq_chip *our_chip = to_exynos_irq_chip(chip);
72595be726STomasz Figa 	struct samsung_pin_bank *bank = irq_data_get_irq_chip_data(irqd);
732e4a4fdaSTomasz Figa 	unsigned long reg_pend = our_chip->eint_pend + bank->eint_offset;
7443b169dbSThomas Abraham 
758b1bd11cSChanwoo Choi 	writel(1 << irqd->hwirq, bank->eint_base + reg_pend);
7643b169dbSThomas Abraham }
7743b169dbSThomas Abraham 
782e4a4fdaSTomasz Figa static void exynos_irq_unmask(struct irq_data *irqd)
795ace03fbSDoug Anderson {
802e4a4fdaSTomasz Figa 	struct irq_chip *chip = irq_data_get_irq_chip(irqd);
812e4a4fdaSTomasz Figa 	struct exynos_irq_chip *our_chip = to_exynos_irq_chip(chip);
825ace03fbSDoug Anderson 	struct samsung_pin_bank *bank = irq_data_get_irq_chip_data(irqd);
832e4a4fdaSTomasz Figa 	unsigned long reg_mask = our_chip->eint_mask + bank->eint_offset;
845ace03fbSDoug Anderson 	unsigned long mask;
855ace03fbSDoug Anderson 	unsigned long flags;
865ace03fbSDoug Anderson 
875a68e7a7SDoug Anderson 	/*
885a68e7a7SDoug Anderson 	 * Ack level interrupts right before unmask
895a68e7a7SDoug Anderson 	 *
905a68e7a7SDoug Anderson 	 * If we don't do this we'll get a double-interrupt.  Level triggered
915a68e7a7SDoug Anderson 	 * interrupts must not fire an interrupt if the level is not
925a68e7a7SDoug Anderson 	 * _currently_ active, even if it was active while the interrupt was
935a68e7a7SDoug Anderson 	 * masked.
945a68e7a7SDoug Anderson 	 */
955a68e7a7SDoug Anderson 	if (irqd_get_trigger_type(irqd) & IRQ_TYPE_LEVEL_MASK)
962e4a4fdaSTomasz Figa 		exynos_irq_ack(irqd);
975a68e7a7SDoug Anderson 
985ace03fbSDoug Anderson 	spin_lock_irqsave(&bank->slock, flags);
995ace03fbSDoug Anderson 
1008b1bd11cSChanwoo Choi 	mask = readl(bank->eint_base + reg_mask);
1015ace03fbSDoug Anderson 	mask &= ~(1 << irqd->hwirq);
1028b1bd11cSChanwoo Choi 	writel(mask, bank->eint_base + reg_mask);
1035ace03fbSDoug Anderson 
1045ace03fbSDoug Anderson 	spin_unlock_irqrestore(&bank->slock, flags);
1055ace03fbSDoug Anderson }
1065ace03fbSDoug Anderson 
1072e4a4fdaSTomasz Figa static int exynos_irq_set_type(struct irq_data *irqd, unsigned int type)
10843b169dbSThomas Abraham {
1092e4a4fdaSTomasz Figa 	struct irq_chip *chip = irq_data_get_irq_chip(irqd);
1102e4a4fdaSTomasz Figa 	struct exynos_irq_chip *our_chip = to_exynos_irq_chip(chip);
111595be726STomasz Figa 	struct samsung_pin_bank *bank = irq_data_get_irq_chip_data(irqd);
112f6a8249fSTomasz Figa 	unsigned int shift = EXYNOS_EINT_CON_LEN * irqd->hwirq;
11343b169dbSThomas Abraham 	unsigned int con, trig_type;
1142e4a4fdaSTomasz Figa 	unsigned long reg_con = our_chip->eint_con + bank->eint_offset;
11543b169dbSThomas Abraham 
11643b169dbSThomas Abraham 	switch (type) {
11743b169dbSThomas Abraham 	case IRQ_TYPE_EDGE_RISING:
11843b169dbSThomas Abraham 		trig_type = EXYNOS_EINT_EDGE_RISING;
11943b169dbSThomas Abraham 		break;
12043b169dbSThomas Abraham 	case IRQ_TYPE_EDGE_FALLING:
12143b169dbSThomas Abraham 		trig_type = EXYNOS_EINT_EDGE_FALLING;
12243b169dbSThomas Abraham 		break;
12343b169dbSThomas Abraham 	case IRQ_TYPE_EDGE_BOTH:
12443b169dbSThomas Abraham 		trig_type = EXYNOS_EINT_EDGE_BOTH;
12543b169dbSThomas Abraham 		break;
12643b169dbSThomas Abraham 	case IRQ_TYPE_LEVEL_HIGH:
12743b169dbSThomas Abraham 		trig_type = EXYNOS_EINT_LEVEL_HIGH;
12843b169dbSThomas Abraham 		break;
12943b169dbSThomas Abraham 	case IRQ_TYPE_LEVEL_LOW:
13043b169dbSThomas Abraham 		trig_type = EXYNOS_EINT_LEVEL_LOW;
13143b169dbSThomas Abraham 		break;
13243b169dbSThomas Abraham 	default:
13343b169dbSThomas Abraham 		pr_err("unsupported external interrupt type\n");
13443b169dbSThomas Abraham 		return -EINVAL;
13543b169dbSThomas Abraham 	}
13643b169dbSThomas Abraham 
13743b169dbSThomas Abraham 	if (type & IRQ_TYPE_EDGE_BOTH)
13840ec168aSThomas Gleixner 		irq_set_handler_locked(irqd, handle_edge_irq);
13943b169dbSThomas Abraham 	else
14040ec168aSThomas Gleixner 		irq_set_handler_locked(irqd, handle_level_irq);
14143b169dbSThomas Abraham 
1428b1bd11cSChanwoo Choi 	con = readl(bank->eint_base + reg_con);
14343b169dbSThomas Abraham 	con &= ~(EXYNOS_EINT_CON_MASK << shift);
14443b169dbSThomas Abraham 	con |= trig_type << shift;
1458b1bd11cSChanwoo Choi 	writel(con, bank->eint_base + reg_con);
146ee2f573cSTomasz Figa 
147f6a8249fSTomasz Figa 	return 0;
148f6a8249fSTomasz Figa }
149f6a8249fSTomasz Figa 
150f6a8249fSTomasz Figa static int exynos_irq_request_resources(struct irq_data *irqd)
151f6a8249fSTomasz Figa {
152f6a8249fSTomasz Figa 	struct samsung_pin_bank *bank = irq_data_get_irq_chip_data(irqd);
15394ce944bSTomasz Figa 	const struct samsung_pin_bank_type *bank_type = bank->type;
154bbed85f4SKrzysztof Kozlowski 	unsigned long reg_con, flags;
155bbed85f4SKrzysztof Kozlowski 	unsigned int shift, mask, con;
156f6a8249fSTomasz Figa 	int ret;
157f6a8249fSTomasz Figa 
158e3a2e878SAlexandre Courbot 	ret = gpiochip_lock_as_irq(&bank->gpio_chip, irqd->hwirq);
159f6a8249fSTomasz Figa 	if (ret) {
16058383c78SLinus Walleij 		dev_err(bank->gpio_chip.parent,
16158383c78SLinus Walleij 			"unable to lock pin %s-%lu IRQ\n",
162f6a8249fSTomasz Figa 			bank->name, irqd->hwirq);
163f6a8249fSTomasz Figa 		return ret;
164f6a8249fSTomasz Figa 	}
165f6a8249fSTomasz Figa 
16643fc9e7fSTomasz Figa 	reg_con = bank->pctl_offset + bank_type->reg_offset[PINCFG_TYPE_FUNC];
167f6a8249fSTomasz Figa 	shift = irqd->hwirq * bank_type->fld_width[PINCFG_TYPE_FUNC];
168499147c9STomasz Figa 	mask = (1 << bank_type->fld_width[PINCFG_TYPE_FUNC]) - 1;
169ee2f573cSTomasz Figa 
17019846950STomasz Figa 	spin_lock_irqsave(&bank->slock, flags);
17119846950STomasz Figa 
172af0b0baaSKrzysztof Kozlowski 	con = readl(bank->pctl_base + reg_con);
173ee2f573cSTomasz Figa 	con &= ~(mask << shift);
1744460dc21SKrzysztof Kozlowski 	con |= EXYNOS_PIN_FUNC_EINT << shift;
175af0b0baaSKrzysztof Kozlowski 	writel(con, bank->pctl_base + reg_con);
176ee2f573cSTomasz Figa 
17719846950STomasz Figa 	spin_unlock_irqrestore(&bank->slock, flags);
17819846950STomasz Figa 
17943b169dbSThomas Abraham 	return 0;
18043b169dbSThomas Abraham }
18143b169dbSThomas Abraham 
182f6a8249fSTomasz Figa static void exynos_irq_release_resources(struct irq_data *irqd)
183f6a8249fSTomasz Figa {
184f6a8249fSTomasz Figa 	struct samsung_pin_bank *bank = irq_data_get_irq_chip_data(irqd);
18594ce944bSTomasz Figa 	const struct samsung_pin_bank_type *bank_type = bank->type;
186bbed85f4SKrzysztof Kozlowski 	unsigned long reg_con, flags;
187bbed85f4SKrzysztof Kozlowski 	unsigned int shift, mask, con;
188f6a8249fSTomasz Figa 
189f6a8249fSTomasz Figa 	reg_con = bank->pctl_offset + bank_type->reg_offset[PINCFG_TYPE_FUNC];
190f6a8249fSTomasz Figa 	shift = irqd->hwirq * bank_type->fld_width[PINCFG_TYPE_FUNC];
191f6a8249fSTomasz Figa 	mask = (1 << bank_type->fld_width[PINCFG_TYPE_FUNC]) - 1;
192f6a8249fSTomasz Figa 
193f6a8249fSTomasz Figa 	spin_lock_irqsave(&bank->slock, flags);
194f6a8249fSTomasz Figa 
195af0b0baaSKrzysztof Kozlowski 	con = readl(bank->pctl_base + reg_con);
196f6a8249fSTomasz Figa 	con &= ~(mask << shift);
1974460dc21SKrzysztof Kozlowski 	con |= EXYNOS_PIN_FUNC_INPUT << shift;
198af0b0baaSKrzysztof Kozlowski 	writel(con, bank->pctl_base + reg_con);
199f6a8249fSTomasz Figa 
200f6a8249fSTomasz Figa 	spin_unlock_irqrestore(&bank->slock, flags);
201f6a8249fSTomasz Figa 
202e3a2e878SAlexandre Courbot 	gpiochip_unlock_as_irq(&bank->gpio_chip, irqd->hwirq);
203f6a8249fSTomasz Figa }
204f6a8249fSTomasz Figa 
20543b169dbSThomas Abraham /*
20643b169dbSThomas Abraham  * irq_chip for gpio interrupts.
20743b169dbSThomas Abraham  */
2082e4a4fdaSTomasz Figa static struct exynos_irq_chip exynos_gpio_irq_chip = {
2092e4a4fdaSTomasz Figa 	.chip = {
21043b169dbSThomas Abraham 		.name = "exynos_gpio_irq_chip",
2112e4a4fdaSTomasz Figa 		.irq_unmask = exynos_irq_unmask,
2122e4a4fdaSTomasz Figa 		.irq_mask = exynos_irq_mask,
2132e4a4fdaSTomasz Figa 		.irq_ack = exynos_irq_ack,
2142e4a4fdaSTomasz Figa 		.irq_set_type = exynos_irq_set_type,
215f6a8249fSTomasz Figa 		.irq_request_resources = exynos_irq_request_resources,
216f6a8249fSTomasz Figa 		.irq_release_resources = exynos_irq_release_resources,
2172e4a4fdaSTomasz Figa 	},
2182e4a4fdaSTomasz Figa 	.eint_con = EXYNOS_GPIO_ECON_OFFSET,
2192e4a4fdaSTomasz Figa 	.eint_mask = EXYNOS_GPIO_EMASK_OFFSET,
2202e4a4fdaSTomasz Figa 	.eint_pend = EXYNOS_GPIO_EPEND_OFFSET,
221*a8be2af0SKrzysztof Kozlowski 	/* eint_wake_mask_value not used */
22243b169dbSThomas Abraham };
22343b169dbSThomas Abraham 
2246f5e41bdSAbhilash Kesavan static int exynos_eint_irq_map(struct irq_domain *h, unsigned int virq,
22543b169dbSThomas Abraham 					irq_hw_number_t hw)
22643b169dbSThomas Abraham {
227595be726STomasz Figa 	struct samsung_pin_bank *b = h->host_data;
22843b169dbSThomas Abraham 
229595be726STomasz Figa 	irq_set_chip_data(virq, b);
2300d3d30dbSAbhilash Kesavan 	irq_set_chip_and_handler(virq, &b->irq_chip->chip,
23143b169dbSThomas Abraham 					handle_level_irq);
23243b169dbSThomas Abraham 	return 0;
23343b169dbSThomas Abraham }
23443b169dbSThomas Abraham 
23543b169dbSThomas Abraham /*
2366f5e41bdSAbhilash Kesavan  * irq domain callbacks for external gpio and wakeup interrupt controllers.
23743b169dbSThomas Abraham  */
2386f5e41bdSAbhilash Kesavan static const struct irq_domain_ops exynos_eint_irqd_ops = {
2396f5e41bdSAbhilash Kesavan 	.map	= exynos_eint_irq_map,
24043b169dbSThomas Abraham 	.xlate	= irq_domain_xlate_twocell,
24143b169dbSThomas Abraham };
24243b169dbSThomas Abraham 
24343b169dbSThomas Abraham static irqreturn_t exynos_eint_gpio_irq(int irq, void *data)
24443b169dbSThomas Abraham {
24543b169dbSThomas Abraham 	struct samsung_pinctrl_drv_data *d = data;
2461bf00d7aSTomasz Figa 	struct samsung_pin_bank *bank = d->pin_banks;
24743b169dbSThomas Abraham 	unsigned int svc, group, pin, virq;
24843b169dbSThomas Abraham 
2498b1bd11cSChanwoo Choi 	svc = readl(bank->eint_base + EXYNOS_SVC_OFFSET);
25043b169dbSThomas Abraham 	group = EXYNOS_SVC_GROUP(svc);
25143b169dbSThomas Abraham 	pin = svc & EXYNOS_SVC_NUM_MASK;
25243b169dbSThomas Abraham 
25343b169dbSThomas Abraham 	if (!group)
25443b169dbSThomas Abraham 		return IRQ_HANDLED;
25543b169dbSThomas Abraham 	bank += (group - 1);
25643b169dbSThomas Abraham 
257595be726STomasz Figa 	virq = irq_linear_revmap(bank->irq_domain, pin);
25843b169dbSThomas Abraham 	if (!virq)
25943b169dbSThomas Abraham 		return IRQ_NONE;
26043b169dbSThomas Abraham 	generic_handle_irq(virq);
26143b169dbSThomas Abraham 	return IRQ_HANDLED;
26243b169dbSThomas Abraham }
26343b169dbSThomas Abraham 
2647ccbc60cSTomasz Figa struct exynos_eint_gpio_save {
2657ccbc60cSTomasz Figa 	u32 eint_con;
2667ccbc60cSTomasz Figa 	u32 eint_fltcon0;
2677ccbc60cSTomasz Figa 	u32 eint_fltcon1;
2687ccbc60cSTomasz Figa };
2697ccbc60cSTomasz Figa 
27043b169dbSThomas Abraham /*
27143b169dbSThomas Abraham  * exynos_eint_gpio_init() - setup handling of external gpio interrupts.
27243b169dbSThomas Abraham  * @d: driver data of samsung pinctrl driver.
27343b169dbSThomas Abraham  */
274cfa76ddfSKrzysztof Kozlowski int exynos_eint_gpio_init(struct samsung_pinctrl_drv_data *d)
27543b169dbSThomas Abraham {
276595be726STomasz Figa 	struct samsung_pin_bank *bank;
27743b169dbSThomas Abraham 	struct device *dev = d->dev;
2787ccbc60cSTomasz Figa 	int ret;
2797ccbc60cSTomasz Figa 	int i;
28043b169dbSThomas Abraham 
28143b169dbSThomas Abraham 	if (!d->irq) {
28243b169dbSThomas Abraham 		dev_err(dev, "irq number not available\n");
28343b169dbSThomas Abraham 		return -EINVAL;
28443b169dbSThomas Abraham 	}
28543b169dbSThomas Abraham 
28643b169dbSThomas Abraham 	ret = devm_request_irq(dev, d->irq, exynos_eint_gpio_irq,
28743b169dbSThomas Abraham 					0, dev_name(dev), d);
28843b169dbSThomas Abraham 	if (ret) {
28943b169dbSThomas Abraham 		dev_err(dev, "irq request failed\n");
29043b169dbSThomas Abraham 		return -ENXIO;
29143b169dbSThomas Abraham 	}
29243b169dbSThomas Abraham 
2931bf00d7aSTomasz Figa 	bank = d->pin_banks;
2941bf00d7aSTomasz Figa 	for (i = 0; i < d->nr_banks; ++i, ++bank) {
295595be726STomasz Figa 		if (bank->eint_type != EINT_TYPE_GPIO)
296595be726STomasz Figa 			continue;
297595be726STomasz Figa 		bank->irq_domain = irq_domain_add_linear(bank->of_node,
2986f5e41bdSAbhilash Kesavan 				bank->nr_pins, &exynos_eint_irqd_ops, bank);
299595be726STomasz Figa 		if (!bank->irq_domain) {
300595be726STomasz Figa 			dev_err(dev, "gpio irq domain add failed\n");
3017ccbc60cSTomasz Figa 			ret = -ENXIO;
3027ccbc60cSTomasz Figa 			goto err_domains;
3037ccbc60cSTomasz Figa 		}
3047ccbc60cSTomasz Figa 
3057ccbc60cSTomasz Figa 		bank->soc_priv = devm_kzalloc(d->dev,
3067ccbc60cSTomasz Figa 			sizeof(struct exynos_eint_gpio_save), GFP_KERNEL);
3077ccbc60cSTomasz Figa 		if (!bank->soc_priv) {
3087ccbc60cSTomasz Figa 			irq_domain_remove(bank->irq_domain);
3097ccbc60cSTomasz Figa 			ret = -ENOMEM;
3107ccbc60cSTomasz Figa 			goto err_domains;
31143b169dbSThomas Abraham 		}
3120d3d30dbSAbhilash Kesavan 
3130d3d30dbSAbhilash Kesavan 		bank->irq_chip = &exynos_gpio_irq_chip;
314595be726STomasz Figa 	}
31543b169dbSThomas Abraham 
31643b169dbSThomas Abraham 	return 0;
3177ccbc60cSTomasz Figa 
3187ccbc60cSTomasz Figa err_domains:
3197ccbc60cSTomasz Figa 	for (--i, --bank; i >= 0; --i, --bank) {
3207ccbc60cSTomasz Figa 		if (bank->eint_type != EINT_TYPE_GPIO)
3217ccbc60cSTomasz Figa 			continue;
3227ccbc60cSTomasz Figa 		irq_domain_remove(bank->irq_domain);
3237ccbc60cSTomasz Figa 	}
3247ccbc60cSTomasz Figa 
3257ccbc60cSTomasz Figa 	return ret;
32643b169dbSThomas Abraham }
32743b169dbSThomas Abraham 
328ad350cd9STomasz Figa static u32 exynos_eint_wake_mask = 0xffffffff;
329ad350cd9STomasz Figa 
330ad350cd9STomasz Figa u32 exynos_get_eint_wake_mask(void)
331ad350cd9STomasz Figa {
332ad350cd9STomasz Figa 	return exynos_eint_wake_mask;
333ad350cd9STomasz Figa }
334ad350cd9STomasz Figa 
335ad350cd9STomasz Figa static int exynos_wkup_irq_set_wake(struct irq_data *irqd, unsigned int on)
336ad350cd9STomasz Figa {
337*a8be2af0SKrzysztof Kozlowski 	struct irq_chip *chip = irq_data_get_irq_chip(irqd);
338*a8be2af0SKrzysztof Kozlowski 	struct exynos_irq_chip *our_chip = to_exynos_irq_chip(chip);
339ad350cd9STomasz Figa 	struct samsung_pin_bank *bank = irq_data_get_irq_chip_data(irqd);
340ad350cd9STomasz Figa 	unsigned long bit = 1UL << (2 * bank->eint_offset + irqd->hwirq);
341ad350cd9STomasz Figa 
342ad350cd9STomasz Figa 	pr_info("wake %s for irq %d\n", on ? "enabled" : "disabled", irqd->irq);
343ad350cd9STomasz Figa 
344ad350cd9STomasz Figa 	if (!on)
345ad350cd9STomasz Figa 		exynos_eint_wake_mask |= bit;
346ad350cd9STomasz Figa 	else
347ad350cd9STomasz Figa 		exynos_eint_wake_mask &= ~bit;
348*a8be2af0SKrzysztof Kozlowski 	our_chip->eint_wake_mask_value = exynos_eint_wake_mask;
349ad350cd9STomasz Figa 
350ad350cd9STomasz Figa 	return 0;
351ad350cd9STomasz Figa }
352ad350cd9STomasz Figa 
35343b169dbSThomas Abraham /*
35443b169dbSThomas Abraham  * irq_chip for wakeup interrupts
35543b169dbSThomas Abraham  */
356bb928dfdSKrzysztof Kozlowski static const struct exynos_irq_chip s5pv210_wkup_irq_chip __initconst = {
357bb928dfdSKrzysztof Kozlowski 	.chip = {
358bb928dfdSKrzysztof Kozlowski 		.name = "s5pv210_wkup_irq_chip",
359bb928dfdSKrzysztof Kozlowski 		.irq_unmask = exynos_irq_unmask,
360bb928dfdSKrzysztof Kozlowski 		.irq_mask = exynos_irq_mask,
361bb928dfdSKrzysztof Kozlowski 		.irq_ack = exynos_irq_ack,
362bb928dfdSKrzysztof Kozlowski 		.irq_set_type = exynos_irq_set_type,
363bb928dfdSKrzysztof Kozlowski 		.irq_set_wake = exynos_wkup_irq_set_wake,
364bb928dfdSKrzysztof Kozlowski 		.irq_request_resources = exynos_irq_request_resources,
365bb928dfdSKrzysztof Kozlowski 		.irq_release_resources = exynos_irq_release_resources,
366bb928dfdSKrzysztof Kozlowski 	},
367bb928dfdSKrzysztof Kozlowski 	.eint_con = EXYNOS_WKUP_ECON_OFFSET,
368bb928dfdSKrzysztof Kozlowski 	.eint_mask = EXYNOS_WKUP_EMASK_OFFSET,
369bb928dfdSKrzysztof Kozlowski 	.eint_pend = EXYNOS_WKUP_EPEND_OFFSET,
370*a8be2af0SKrzysztof Kozlowski 	.eint_wake_mask_value = EXYNOS_EINT_WAKEUP_MASK_DISABLED,
371*a8be2af0SKrzysztof Kozlowski 	/* Only difference with exynos4210_wkup_irq_chip: */
372*a8be2af0SKrzysztof Kozlowski 	.eint_wake_mask_reg = S5PV210_EINT_WAKEUP_MASK,
373bb928dfdSKrzysztof Kozlowski };
374bb928dfdSKrzysztof Kozlowski 
37571b96c3aSKrzysztof Kozlowski static const struct exynos_irq_chip exynos4210_wkup_irq_chip __initconst = {
3762e4a4fdaSTomasz Figa 	.chip = {
37714c255d3SAbhilash Kesavan 		.name = "exynos4210_wkup_irq_chip",
3782e4a4fdaSTomasz Figa 		.irq_unmask = exynos_irq_unmask,
3792e4a4fdaSTomasz Figa 		.irq_mask = exynos_irq_mask,
3802e4a4fdaSTomasz Figa 		.irq_ack = exynos_irq_ack,
3812e4a4fdaSTomasz Figa 		.irq_set_type = exynos_irq_set_type,
382ad350cd9STomasz Figa 		.irq_set_wake = exynos_wkup_irq_set_wake,
383f6a8249fSTomasz Figa 		.irq_request_resources = exynos_irq_request_resources,
384f6a8249fSTomasz Figa 		.irq_release_resources = exynos_irq_release_resources,
3852e4a4fdaSTomasz Figa 	},
3862e4a4fdaSTomasz Figa 	.eint_con = EXYNOS_WKUP_ECON_OFFSET,
3872e4a4fdaSTomasz Figa 	.eint_mask = EXYNOS_WKUP_EMASK_OFFSET,
3882e4a4fdaSTomasz Figa 	.eint_pend = EXYNOS_WKUP_EPEND_OFFSET,
389*a8be2af0SKrzysztof Kozlowski 	.eint_wake_mask_value = EXYNOS_EINT_WAKEUP_MASK_DISABLED,
390*a8be2af0SKrzysztof Kozlowski 	.eint_wake_mask_reg = EXYNOS_EINT_WAKEUP_MASK,
39143b169dbSThomas Abraham };
39243b169dbSThomas Abraham 
39371b96c3aSKrzysztof Kozlowski static const struct exynos_irq_chip exynos7_wkup_irq_chip __initconst = {
39414c255d3SAbhilash Kesavan 	.chip = {
39514c255d3SAbhilash Kesavan 		.name = "exynos7_wkup_irq_chip",
39614c255d3SAbhilash Kesavan 		.irq_unmask = exynos_irq_unmask,
39714c255d3SAbhilash Kesavan 		.irq_mask = exynos_irq_mask,
39814c255d3SAbhilash Kesavan 		.irq_ack = exynos_irq_ack,
39914c255d3SAbhilash Kesavan 		.irq_set_type = exynos_irq_set_type,
40014c255d3SAbhilash Kesavan 		.irq_set_wake = exynos_wkup_irq_set_wake,
40114c255d3SAbhilash Kesavan 		.irq_request_resources = exynos_irq_request_resources,
40214c255d3SAbhilash Kesavan 		.irq_release_resources = exynos_irq_release_resources,
40314c255d3SAbhilash Kesavan 	},
40414c255d3SAbhilash Kesavan 	.eint_con = EXYNOS7_WKUP_ECON_OFFSET,
40514c255d3SAbhilash Kesavan 	.eint_mask = EXYNOS7_WKUP_EMASK_OFFSET,
40614c255d3SAbhilash Kesavan 	.eint_pend = EXYNOS7_WKUP_EPEND_OFFSET,
407*a8be2af0SKrzysztof Kozlowski 	.eint_wake_mask_value = EXYNOS_EINT_WAKEUP_MASK_DISABLED,
408*a8be2af0SKrzysztof Kozlowski 	.eint_wake_mask_reg = EXYNOS5433_EINT_WAKEUP_MASK,
40914c255d3SAbhilash Kesavan };
41014c255d3SAbhilash Kesavan 
41114c255d3SAbhilash Kesavan /* list of external wakeup controllers supported */
41214c255d3SAbhilash Kesavan static const struct of_device_id exynos_wkup_irq_ids[] = {
413bb928dfdSKrzysztof Kozlowski 	{ .compatible = "samsung,s5pv210-wakeup-eint",
414bb928dfdSKrzysztof Kozlowski 			.data = &s5pv210_wkup_irq_chip },
41514c255d3SAbhilash Kesavan 	{ .compatible = "samsung,exynos4210-wakeup-eint",
41614c255d3SAbhilash Kesavan 			.data = &exynos4210_wkup_irq_chip },
41714c255d3SAbhilash Kesavan 	{ .compatible = "samsung,exynos7-wakeup-eint",
41814c255d3SAbhilash Kesavan 			.data = &exynos7_wkup_irq_chip },
41914c255d3SAbhilash Kesavan 	{ }
42014c255d3SAbhilash Kesavan };
42114c255d3SAbhilash Kesavan 
42243b169dbSThomas Abraham /* interrupt handler for wakeup interrupts 0..15 */
423bd0b9ac4SThomas Gleixner static void exynos_irq_eint0_15(struct irq_desc *desc)
42443b169dbSThomas Abraham {
4255663bb27SJiang Liu 	struct exynos_weint_data *eintd = irq_desc_get_handler_data(desc);
426a04b07c0STomasz Figa 	struct samsung_pin_bank *bank = eintd->bank;
4275663bb27SJiang Liu 	struct irq_chip *chip = irq_desc_get_chip(desc);
42843b169dbSThomas Abraham 	int eint_irq;
42943b169dbSThomas Abraham 
43043b169dbSThomas Abraham 	chained_irq_enter(chip, desc);
43143b169dbSThomas Abraham 
432a04b07c0STomasz Figa 	eint_irq = irq_linear_revmap(bank->irq_domain, eintd->irq);
43343b169dbSThomas Abraham 	generic_handle_irq(eint_irq);
43426fecf0bSperr perr 
43543b169dbSThomas Abraham 	chained_irq_exit(chip, desc);
43643b169dbSThomas Abraham }
43743b169dbSThomas Abraham 
438a04b07c0STomasz Figa static inline void exynos_irq_demux_eint(unsigned long pend,
43943b169dbSThomas Abraham 						struct irq_domain *domain)
44043b169dbSThomas Abraham {
44143b169dbSThomas Abraham 	unsigned int irq;
44243b169dbSThomas Abraham 
44343b169dbSThomas Abraham 	while (pend) {
44443b169dbSThomas Abraham 		irq = fls(pend) - 1;
445a04b07c0STomasz Figa 		generic_handle_irq(irq_find_mapping(domain, irq));
44643b169dbSThomas Abraham 		pend &= ~(1 << irq);
44743b169dbSThomas Abraham 	}
44843b169dbSThomas Abraham }
44943b169dbSThomas Abraham 
45043b169dbSThomas Abraham /* interrupt handler for wakeup interrupt 16 */
451bd0b9ac4SThomas Gleixner static void exynos_irq_demux_eint16_31(struct irq_desc *desc)
45243b169dbSThomas Abraham {
4535663bb27SJiang Liu 	struct irq_chip *chip = irq_desc_get_chip(desc);
4545663bb27SJiang Liu 	struct exynos_muxed_weint_data *eintd = irq_desc_get_handler_data(desc);
45543b169dbSThomas Abraham 	unsigned long pend;
456de59049bSTomasz Figa 	unsigned long mask;
457a04b07c0STomasz Figa 	int i;
45843b169dbSThomas Abraham 
45943b169dbSThomas Abraham 	chained_irq_enter(chip, desc);
460a04b07c0STomasz Figa 
461a04b07c0STomasz Figa 	for (i = 0; i < eintd->nr_banks; ++i) {
462a04b07c0STomasz Figa 		struct samsung_pin_bank *b = eintd->banks[i];
4638b1bd11cSChanwoo Choi 		pend = readl(b->eint_base + b->irq_chip->eint_pend
4642e4a4fdaSTomasz Figa 				+ b->eint_offset);
4658b1bd11cSChanwoo Choi 		mask = readl(b->eint_base + b->irq_chip->eint_mask
4662e4a4fdaSTomasz Figa 				+ b->eint_offset);
467a04b07c0STomasz Figa 		exynos_irq_demux_eint(pend & ~mask, b->irq_domain);
468a04b07c0STomasz Figa 	}
469a04b07c0STomasz Figa 
47043b169dbSThomas Abraham 	chained_irq_exit(chip, desc);
47143b169dbSThomas Abraham }
47243b169dbSThomas Abraham 
47343b169dbSThomas Abraham /*
47443b169dbSThomas Abraham  * exynos_eint_wkup_init() - setup handling of external wakeup interrupts.
47543b169dbSThomas Abraham  * @d: driver data of samsung pinctrl driver.
47643b169dbSThomas Abraham  */
477cfa76ddfSKrzysztof Kozlowski int exynos_eint_wkup_init(struct samsung_pinctrl_drv_data *d)
47843b169dbSThomas Abraham {
47943b169dbSThomas Abraham 	struct device *dev = d->dev;
480c3ad056bSTomasz Figa 	struct device_node *wkup_np = NULL;
481c3ad056bSTomasz Figa 	struct device_node *np;
482a04b07c0STomasz Figa 	struct samsung_pin_bank *bank;
48343b169dbSThomas Abraham 	struct exynos_weint_data *weint_data;
484a04b07c0STomasz Figa 	struct exynos_muxed_weint_data *muxed_data;
48514c255d3SAbhilash Kesavan 	struct exynos_irq_chip *irq_chip;
486a04b07c0STomasz Figa 	unsigned int muxed_banks = 0;
487a04b07c0STomasz Figa 	unsigned int i;
48843b169dbSThomas Abraham 	int idx, irq;
48943b169dbSThomas Abraham 
490c3ad056bSTomasz Figa 	for_each_child_of_node(dev->of_node, np) {
49114c255d3SAbhilash Kesavan 		const struct of_device_id *match;
49214c255d3SAbhilash Kesavan 
49314c255d3SAbhilash Kesavan 		match = of_match_node(exynos_wkup_irq_ids, np);
49414c255d3SAbhilash Kesavan 		if (match) {
49514c255d3SAbhilash Kesavan 			irq_chip = kmemdup(match->data,
49614c255d3SAbhilash Kesavan 				sizeof(*irq_chip), GFP_KERNEL);
497a1ea9a40SKrzysztof Kozlowski 			if (!irq_chip)
498a1ea9a40SKrzysztof Kozlowski 				return -ENOMEM;
499c3ad056bSTomasz Figa 			wkup_np = np;
500c3ad056bSTomasz Figa 			break;
50143b169dbSThomas Abraham 		}
502c3ad056bSTomasz Figa 	}
503c3ad056bSTomasz Figa 	if (!wkup_np)
504c3ad056bSTomasz Figa 		return -ENODEV;
50543b169dbSThomas Abraham 
5061bf00d7aSTomasz Figa 	bank = d->pin_banks;
5071bf00d7aSTomasz Figa 	for (i = 0; i < d->nr_banks; ++i, ++bank) {
508a04b07c0STomasz Figa 		if (bank->eint_type != EINT_TYPE_WKUP)
509a04b07c0STomasz Figa 			continue;
510a04b07c0STomasz Figa 
511a04b07c0STomasz Figa 		bank->irq_domain = irq_domain_add_linear(bank->of_node,
5126f5e41bdSAbhilash Kesavan 				bank->nr_pins, &exynos_eint_irqd_ops, bank);
513a04b07c0STomasz Figa 		if (!bank->irq_domain) {
514a04b07c0STomasz Figa 			dev_err(dev, "wkup irq domain add failed\n");
51543b169dbSThomas Abraham 			return -ENXIO;
51643b169dbSThomas Abraham 		}
51743b169dbSThomas Abraham 
51814c255d3SAbhilash Kesavan 		bank->irq_chip = irq_chip;
5190d3d30dbSAbhilash Kesavan 
520a04b07c0STomasz Figa 		if (!of_find_property(bank->of_node, "interrupts", NULL)) {
521a04b07c0STomasz Figa 			bank->eint_type = EINT_TYPE_WKUP_MUX;
522a04b07c0STomasz Figa 			++muxed_banks;
523a04b07c0STomasz Figa 			continue;
524a04b07c0STomasz Figa 		}
525a04b07c0STomasz Figa 
526a86854d0SKees Cook 		weint_data = devm_kcalloc(dev,
527a86854d0SKees Cook 					  bank->nr_pins, sizeof(*weint_data),
528a86854d0SKees Cook 					  GFP_KERNEL);
529fa5c0f46SMarek Szyprowski 		if (!weint_data)
53043b169dbSThomas Abraham 			return -ENOMEM;
53143b169dbSThomas Abraham 
532a04b07c0STomasz Figa 		for (idx = 0; idx < bank->nr_pins; ++idx) {
533a04b07c0STomasz Figa 			irq = irq_of_parse_and_map(bank->of_node, idx);
534a04b07c0STomasz Figa 			if (!irq) {
535a04b07c0STomasz Figa 				dev_err(dev, "irq number for eint-%s-%d not found\n",
536a04b07c0STomasz Figa 							bank->name, idx);
537a04b07c0STomasz Figa 				continue;
53843b169dbSThomas Abraham 			}
53943b169dbSThomas Abraham 			weint_data[idx].irq = idx;
540a04b07c0STomasz Figa 			weint_data[idx].bank = bank;
541c21f7849SThomas Gleixner 			irq_set_chained_handler_and_data(irq,
542c21f7849SThomas Gleixner 							 exynos_irq_eint0_15,
543c21f7849SThomas Gleixner 							 &weint_data[idx]);
54443b169dbSThomas Abraham 		}
54543b169dbSThomas Abraham 	}
546a04b07c0STomasz Figa 
547a04b07c0STomasz Figa 	if (!muxed_banks)
548a04b07c0STomasz Figa 		return 0;
549a04b07c0STomasz Figa 
550a04b07c0STomasz Figa 	irq = irq_of_parse_and_map(wkup_np, 0);
551a04b07c0STomasz Figa 	if (!irq) {
552a04b07c0STomasz Figa 		dev_err(dev, "irq number for muxed EINTs not found\n");
553a04b07c0STomasz Figa 		return 0;
554a04b07c0STomasz Figa 	}
555a04b07c0STomasz Figa 
556a04b07c0STomasz Figa 	muxed_data = devm_kzalloc(dev, sizeof(*muxed_data)
557a04b07c0STomasz Figa 		+ muxed_banks*sizeof(struct samsung_pin_bank *), GFP_KERNEL);
558fa5c0f46SMarek Szyprowski 	if (!muxed_data)
559a04b07c0STomasz Figa 		return -ENOMEM;
560a04b07c0STomasz Figa 
561bb56fc35SThomas Gleixner 	irq_set_chained_handler_and_data(irq, exynos_irq_demux_eint16_31,
562bb56fc35SThomas Gleixner 					 muxed_data);
563a04b07c0STomasz Figa 
5641bf00d7aSTomasz Figa 	bank = d->pin_banks;
565a04b07c0STomasz Figa 	idx = 0;
5661bf00d7aSTomasz Figa 	for (i = 0; i < d->nr_banks; ++i, ++bank) {
567a04b07c0STomasz Figa 		if (bank->eint_type != EINT_TYPE_WKUP_MUX)
568a04b07c0STomasz Figa 			continue;
569a04b07c0STomasz Figa 
570a04b07c0STomasz Figa 		muxed_data->banks[idx++] = bank;
571a04b07c0STomasz Figa 	}
572a04b07c0STomasz Figa 	muxed_data->nr_banks = muxed_banks;
573a04b07c0STomasz Figa 
57443b169dbSThomas Abraham 	return 0;
57543b169dbSThomas Abraham }
57643b169dbSThomas Abraham 
577*a8be2af0SKrzysztof Kozlowski static void
578*a8be2af0SKrzysztof Kozlowski exynos_pinctrl_set_eint_wakeup_mask(struct samsung_pinctrl_drv_data *drvdata,
579*a8be2af0SKrzysztof Kozlowski 				    struct exynos_irq_chip *irq_chip)
580*a8be2af0SKrzysztof Kozlowski {
581*a8be2af0SKrzysztof Kozlowski 	struct regmap *pmu_regs;
582*a8be2af0SKrzysztof Kozlowski 
583*a8be2af0SKrzysztof Kozlowski 	if (!drvdata->retention_ctrl || !drvdata->retention_ctrl->priv) {
584*a8be2af0SKrzysztof Kozlowski 		dev_warn(drvdata->dev,
585*a8be2af0SKrzysztof Kozlowski 			 "No retention data configured bank with external wakeup interrupt. Wake-up mask will not be set.\n");
586*a8be2af0SKrzysztof Kozlowski 		return;
587*a8be2af0SKrzysztof Kozlowski 	}
588*a8be2af0SKrzysztof Kozlowski 
589*a8be2af0SKrzysztof Kozlowski 	pmu_regs = drvdata->retention_ctrl->priv;
590*a8be2af0SKrzysztof Kozlowski 	dev_info(drvdata->dev,
591*a8be2af0SKrzysztof Kozlowski 		 "Setting external wakeup interrupt wakeup mask: 0x%x\n",
592*a8be2af0SKrzysztof Kozlowski 		 irq_chip->eint_wake_mask_value);
593*a8be2af0SKrzysztof Kozlowski 
594*a8be2af0SKrzysztof Kozlowski 	regmap_write(pmu_regs, irq_chip->eint_wake_mask_reg,
595*a8be2af0SKrzysztof Kozlowski 		     irq_chip->eint_wake_mask_value);
596*a8be2af0SKrzysztof Kozlowski }
597*a8be2af0SKrzysztof Kozlowski 
5987ccbc60cSTomasz Figa static void exynos_pinctrl_suspend_bank(
5997ccbc60cSTomasz Figa 				struct samsung_pinctrl_drv_data *drvdata,
6007ccbc60cSTomasz Figa 				struct samsung_pin_bank *bank)
6017ccbc60cSTomasz Figa {
6027ccbc60cSTomasz Figa 	struct exynos_eint_gpio_save *save = bank->soc_priv;
6038b1bd11cSChanwoo Choi 	void __iomem *regs = bank->eint_base;
6047ccbc60cSTomasz Figa 
6057ccbc60cSTomasz Figa 	save->eint_con = readl(regs + EXYNOS_GPIO_ECON_OFFSET
6067ccbc60cSTomasz Figa 						+ bank->eint_offset);
6077ccbc60cSTomasz Figa 	save->eint_fltcon0 = readl(regs + EXYNOS_GPIO_EFLTCON_OFFSET
6087ccbc60cSTomasz Figa 						+ 2 * bank->eint_offset);
6097ccbc60cSTomasz Figa 	save->eint_fltcon1 = readl(regs + EXYNOS_GPIO_EFLTCON_OFFSET
6107ccbc60cSTomasz Figa 						+ 2 * bank->eint_offset + 4);
6117ccbc60cSTomasz Figa 
6127ccbc60cSTomasz Figa 	pr_debug("%s: save     con %#010x\n", bank->name, save->eint_con);
6137ccbc60cSTomasz Figa 	pr_debug("%s: save fltcon0 %#010x\n", bank->name, save->eint_fltcon0);
6147ccbc60cSTomasz Figa 	pr_debug("%s: save fltcon1 %#010x\n", bank->name, save->eint_fltcon1);
6157ccbc60cSTomasz Figa }
6167ccbc60cSTomasz Figa 
617cfa76ddfSKrzysztof Kozlowski void exynos_pinctrl_suspend(struct samsung_pinctrl_drv_data *drvdata)
6187ccbc60cSTomasz Figa {
6191bf00d7aSTomasz Figa 	struct samsung_pin_bank *bank = drvdata->pin_banks;
620*a8be2af0SKrzysztof Kozlowski 	struct exynos_irq_chip *irq_chip = NULL;
6217ccbc60cSTomasz Figa 	int i;
6227ccbc60cSTomasz Figa 
623*a8be2af0SKrzysztof Kozlowski 	for (i = 0; i < drvdata->nr_banks; ++i, ++bank) {
6247ccbc60cSTomasz Figa 		if (bank->eint_type == EINT_TYPE_GPIO)
6257ccbc60cSTomasz Figa 			exynos_pinctrl_suspend_bank(drvdata, bank);
626*a8be2af0SKrzysztof Kozlowski 		else if (bank->eint_type == EINT_TYPE_WKUP) {
627*a8be2af0SKrzysztof Kozlowski 			if (!irq_chip) {
628*a8be2af0SKrzysztof Kozlowski 				irq_chip = bank->irq_chip;
629*a8be2af0SKrzysztof Kozlowski 				exynos_pinctrl_set_eint_wakeup_mask(drvdata,
630*a8be2af0SKrzysztof Kozlowski 								    irq_chip);
631*a8be2af0SKrzysztof Kozlowski 			} else if (bank->irq_chip != irq_chip) {
632*a8be2af0SKrzysztof Kozlowski 				dev_warn(drvdata->dev,
633*a8be2af0SKrzysztof Kozlowski 					 "More than one external wakeup interrupt chip configured (bank: %s). This is not supported by hardware nor by driver.\n",
634*a8be2af0SKrzysztof Kozlowski 					 bank->name);
635*a8be2af0SKrzysztof Kozlowski 			}
636*a8be2af0SKrzysztof Kozlowski 		}
637*a8be2af0SKrzysztof Kozlowski 	}
6387ccbc60cSTomasz Figa }
6397ccbc60cSTomasz Figa 
6407ccbc60cSTomasz Figa static void exynos_pinctrl_resume_bank(
6417ccbc60cSTomasz Figa 				struct samsung_pinctrl_drv_data *drvdata,
6427ccbc60cSTomasz Figa 				struct samsung_pin_bank *bank)
6437ccbc60cSTomasz Figa {
6447ccbc60cSTomasz Figa 	struct exynos_eint_gpio_save *save = bank->soc_priv;
6458b1bd11cSChanwoo Choi 	void __iomem *regs = bank->eint_base;
6467ccbc60cSTomasz Figa 
6477ccbc60cSTomasz Figa 	pr_debug("%s:     con %#010x => %#010x\n", bank->name,
6487ccbc60cSTomasz Figa 			readl(regs + EXYNOS_GPIO_ECON_OFFSET
6497ccbc60cSTomasz Figa 			+ bank->eint_offset), save->eint_con);
6507ccbc60cSTomasz Figa 	pr_debug("%s: fltcon0 %#010x => %#010x\n", bank->name,
6517ccbc60cSTomasz Figa 			readl(regs + EXYNOS_GPIO_EFLTCON_OFFSET
6527ccbc60cSTomasz Figa 			+ 2 * bank->eint_offset), save->eint_fltcon0);
6537ccbc60cSTomasz Figa 	pr_debug("%s: fltcon1 %#010x => %#010x\n", bank->name,
6547ccbc60cSTomasz Figa 			readl(regs + EXYNOS_GPIO_EFLTCON_OFFSET
6557ccbc60cSTomasz Figa 			+ 2 * bank->eint_offset + 4), save->eint_fltcon1);
6567ccbc60cSTomasz Figa 
6577ccbc60cSTomasz Figa 	writel(save->eint_con, regs + EXYNOS_GPIO_ECON_OFFSET
6587ccbc60cSTomasz Figa 						+ bank->eint_offset);
6597ccbc60cSTomasz Figa 	writel(save->eint_fltcon0, regs + EXYNOS_GPIO_EFLTCON_OFFSET
6607ccbc60cSTomasz Figa 						+ 2 * bank->eint_offset);
6617ccbc60cSTomasz Figa 	writel(save->eint_fltcon1, regs + EXYNOS_GPIO_EFLTCON_OFFSET
6627ccbc60cSTomasz Figa 						+ 2 * bank->eint_offset + 4);
6637ccbc60cSTomasz Figa }
6647ccbc60cSTomasz Figa 
665cfa76ddfSKrzysztof Kozlowski void exynos_pinctrl_resume(struct samsung_pinctrl_drv_data *drvdata)
6667ccbc60cSTomasz Figa {
6671bf00d7aSTomasz Figa 	struct samsung_pin_bank *bank = drvdata->pin_banks;
6687ccbc60cSTomasz Figa 	int i;
6697ccbc60cSTomasz Figa 
6701bf00d7aSTomasz Figa 	for (i = 0; i < drvdata->nr_banks; ++i, ++bank)
6717ccbc60cSTomasz Figa 		if (bank->eint_type == EINT_TYPE_GPIO)
6727ccbc60cSTomasz Figa 			exynos_pinctrl_resume_bank(drvdata, bank);
6737ccbc60cSTomasz Figa }
6747ccbc60cSTomasz Figa 
67507731019SMarek Szyprowski static void exynos_retention_enable(struct samsung_pinctrl_drv_data *drvdata)
67607731019SMarek Szyprowski {
67707731019SMarek Szyprowski 	if (drvdata->retention_ctrl->refcnt)
67807731019SMarek Szyprowski 		atomic_inc(drvdata->retention_ctrl->refcnt);
67907731019SMarek Szyprowski }
68007731019SMarek Szyprowski 
68107731019SMarek Szyprowski static void exynos_retention_disable(struct samsung_pinctrl_drv_data *drvdata)
68207731019SMarek Szyprowski {
68307731019SMarek Szyprowski 	struct samsung_retention_ctrl *ctrl = drvdata->retention_ctrl;
68407731019SMarek Szyprowski 	struct regmap *pmu_regs = ctrl->priv;
68507731019SMarek Szyprowski 	int i;
68607731019SMarek Szyprowski 
68707731019SMarek Szyprowski 	if (ctrl->refcnt && !atomic_dec_and_test(ctrl->refcnt))
68807731019SMarek Szyprowski 		return;
68907731019SMarek Szyprowski 
69007731019SMarek Szyprowski 	for (i = 0; i < ctrl->nr_regs; i++)
69107731019SMarek Szyprowski 		regmap_write(pmu_regs, ctrl->regs[i], ctrl->value);
69207731019SMarek Szyprowski }
69307731019SMarek Szyprowski 
694cfa76ddfSKrzysztof Kozlowski struct samsung_retention_ctrl *
69507731019SMarek Szyprowski exynos_retention_init(struct samsung_pinctrl_drv_data *drvdata,
69607731019SMarek Szyprowski 		      const struct samsung_retention_data *data)
69707731019SMarek Szyprowski {
69807731019SMarek Szyprowski 	struct samsung_retention_ctrl *ctrl;
69907731019SMarek Szyprowski 	struct regmap *pmu_regs;
7008fe9bf07SMarek Szyprowski 	int i;
70107731019SMarek Szyprowski 
70207731019SMarek Szyprowski 	ctrl = devm_kzalloc(drvdata->dev, sizeof(*ctrl), GFP_KERNEL);
70307731019SMarek Szyprowski 	if (!ctrl)
70407731019SMarek Szyprowski 		return ERR_PTR(-ENOMEM);
70507731019SMarek Szyprowski 
70607731019SMarek Szyprowski 	pmu_regs = exynos_get_pmu_regmap();
70707731019SMarek Szyprowski 	if (IS_ERR(pmu_regs))
70807731019SMarek Szyprowski 		return ERR_CAST(pmu_regs);
70907731019SMarek Szyprowski 
71007731019SMarek Szyprowski 	ctrl->priv = pmu_regs;
71107731019SMarek Szyprowski 	ctrl->regs = data->regs;
71207731019SMarek Szyprowski 	ctrl->nr_regs = data->nr_regs;
71307731019SMarek Szyprowski 	ctrl->value = data->value;
71407731019SMarek Szyprowski 	ctrl->refcnt = data->refcnt;
71507731019SMarek Szyprowski 	ctrl->enable = exynos_retention_enable;
71607731019SMarek Szyprowski 	ctrl->disable = exynos_retention_disable;
71707731019SMarek Szyprowski 
7188fe9bf07SMarek Szyprowski 	/* Ensure that retention is disabled on driver init */
7198fe9bf07SMarek Szyprowski 	for (i = 0; i < ctrl->nr_regs; i++)
7208fe9bf07SMarek Szyprowski 		regmap_write(pmu_regs, ctrl->regs[i], ctrl->value);
7218fe9bf07SMarek Szyprowski 
72207731019SMarek Szyprowski 	return ctrl;
72307731019SMarek Szyprowski }
724