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