tty: n_gsm: Debug output allocation must use GFP_ATOMIC
[linux-2.6-microblaze.git] / drivers / irqchip / irq-mchp-eic.c
1 // SPDX-License-Identifier: GPL-2.0-only
2 /*
3  * Microchip External Interrupt Controller driver
4  *
5  * Copyright (C) 2021 Microchip Technology Inc. and its subsidiaries
6  *
7  * Author: Claudiu Beznea <claudiu.beznea@microchip.com>
8  */
9 #include <linux/clk.h>
10 #include <linux/delay.h>
11 #include <linux/interrupt.h>
12 #include <linux/irqchip.h>
13 #include <linux/of_address.h>
14 #include <linux/of_irq.h>
15 #include <linux/syscore_ops.h>
16
17 #include <dt-bindings/interrupt-controller/arm-gic.h>
18
19 #define MCHP_EIC_GFCS                   (0x0)
20 #define MCHP_EIC_SCFG(x)                (0x4 + (x) * 0x4)
21 #define MCHP_EIC_SCFG_EN                BIT(16)
22 #define MCHP_EIC_SCFG_LVL               BIT(9)
23 #define MCHP_EIC_SCFG_POL               BIT(8)
24
25 #define MCHP_EIC_NIRQ                   (2)
26
27 /*
28  * struct mchp_eic - EIC private data structure
29  * @base: base address
30  * @clk: peripheral clock
31  * @domain: irq domain
32  * @irqs: irqs b/w eic and gic
33  * @scfg: backup for scfg registers (necessary for backup and self-refresh mode)
34  * @wakeup_source: wakeup source mask
35  */
36 struct mchp_eic {
37         void __iomem *base;
38         struct clk *clk;
39         struct irq_domain *domain;
40         u32 irqs[MCHP_EIC_NIRQ];
41         u32 scfg[MCHP_EIC_NIRQ];
42         u32 wakeup_source;
43 };
44
45 static struct mchp_eic *eic;
46
47 static void mchp_eic_irq_mask(struct irq_data *d)
48 {
49         unsigned int tmp;
50
51         tmp = readl_relaxed(eic->base + MCHP_EIC_SCFG(d->hwirq));
52         tmp &= ~MCHP_EIC_SCFG_EN;
53         writel_relaxed(tmp, eic->base + MCHP_EIC_SCFG(d->hwirq));
54
55         irq_chip_mask_parent(d);
56 }
57
58 static void mchp_eic_irq_unmask(struct irq_data *d)
59 {
60         unsigned int tmp;
61
62         tmp = readl_relaxed(eic->base + MCHP_EIC_SCFG(d->hwirq));
63         tmp |= MCHP_EIC_SCFG_EN;
64         writel_relaxed(tmp, eic->base + MCHP_EIC_SCFG(d->hwirq));
65
66         irq_chip_unmask_parent(d);
67 }
68
69 static int mchp_eic_irq_set_type(struct irq_data *d, unsigned int type)
70 {
71         unsigned int parent_irq_type;
72         unsigned int tmp;
73
74         tmp = readl_relaxed(eic->base + MCHP_EIC_SCFG(d->hwirq));
75         tmp &= ~(MCHP_EIC_SCFG_POL | MCHP_EIC_SCFG_LVL);
76         switch (type) {
77         case IRQ_TYPE_LEVEL_HIGH:
78                 tmp |= MCHP_EIC_SCFG_POL | MCHP_EIC_SCFG_LVL;
79                 parent_irq_type = IRQ_TYPE_LEVEL_HIGH;
80                 break;
81         case IRQ_TYPE_LEVEL_LOW:
82                 tmp |= MCHP_EIC_SCFG_LVL;
83                 parent_irq_type = IRQ_TYPE_LEVEL_HIGH;
84                 break;
85         case IRQ_TYPE_EDGE_RISING:
86                 parent_irq_type = IRQ_TYPE_EDGE_RISING;
87                 break;
88         case IRQ_TYPE_EDGE_FALLING:
89                 tmp |= MCHP_EIC_SCFG_POL;
90                 parent_irq_type = IRQ_TYPE_EDGE_RISING;
91                 break;
92         default:
93                 return -EINVAL;
94         }
95
96         writel_relaxed(tmp, eic->base + MCHP_EIC_SCFG(d->hwirq));
97
98         return irq_chip_set_type_parent(d, parent_irq_type);
99 }
100
101 static int mchp_eic_irq_set_wake(struct irq_data *d, unsigned int on)
102 {
103         irq_set_irq_wake(eic->irqs[d->hwirq], on);
104         if (on)
105                 eic->wakeup_source |= BIT(d->hwirq);
106         else
107                 eic->wakeup_source &= ~BIT(d->hwirq);
108
109         return 0;
110 }
111
112 static int mchp_eic_irq_suspend(void)
113 {
114         unsigned int hwirq;
115
116         for (hwirq = 0; hwirq < MCHP_EIC_NIRQ; hwirq++)
117                 eic->scfg[hwirq] = readl_relaxed(eic->base +
118                                                  MCHP_EIC_SCFG(hwirq));
119
120         if (!eic->wakeup_source)
121                 clk_disable_unprepare(eic->clk);
122
123         return 0;
124 }
125
126 static void mchp_eic_irq_resume(void)
127 {
128         unsigned int hwirq;
129
130         if (!eic->wakeup_source)
131                 clk_prepare_enable(eic->clk);
132
133         for (hwirq = 0; hwirq < MCHP_EIC_NIRQ; hwirq++)
134                 writel_relaxed(eic->scfg[hwirq], eic->base +
135                                MCHP_EIC_SCFG(hwirq));
136 }
137
138 static struct syscore_ops mchp_eic_syscore_ops = {
139         .suspend = mchp_eic_irq_suspend,
140         .resume = mchp_eic_irq_resume,
141 };
142
143 static struct irq_chip mchp_eic_chip = {
144         .name           = "eic",
145         .flags          = IRQCHIP_MASK_ON_SUSPEND | IRQCHIP_SET_TYPE_MASKED,
146         .irq_mask       = mchp_eic_irq_mask,
147         .irq_unmask     = mchp_eic_irq_unmask,
148         .irq_set_type   = mchp_eic_irq_set_type,
149         .irq_ack        = irq_chip_ack_parent,
150         .irq_eoi        = irq_chip_eoi_parent,
151         .irq_retrigger  = irq_chip_retrigger_hierarchy,
152         .irq_set_wake   = mchp_eic_irq_set_wake,
153 };
154
155 static int mchp_eic_domain_alloc(struct irq_domain *domain, unsigned int virq,
156                                  unsigned int nr_irqs, void *data)
157 {
158         struct irq_fwspec *fwspec = data;
159         struct irq_fwspec parent_fwspec;
160         irq_hw_number_t hwirq;
161         unsigned int type;
162         int ret;
163
164         if (WARN_ON(nr_irqs != 1))
165                 return -EINVAL;
166
167         ret = irq_domain_translate_twocell(domain, fwspec, &hwirq, &type);
168         if (ret || hwirq >= MCHP_EIC_NIRQ)
169                 return ret;
170
171         switch (type) {
172         case IRQ_TYPE_EDGE_RISING:
173         case IRQ_TYPE_LEVEL_HIGH:
174                 break;
175         case IRQ_TYPE_EDGE_FALLING:
176                 type = IRQ_TYPE_EDGE_RISING;
177                 break;
178         case IRQ_TYPE_LEVEL_LOW:
179                 type = IRQ_TYPE_LEVEL_HIGH;
180                 break;
181         default:
182                 return -EINVAL;
183         }
184
185         irq_domain_set_hwirq_and_chip(domain, virq, hwirq, &mchp_eic_chip, eic);
186
187         parent_fwspec.fwnode = domain->parent->fwnode;
188         parent_fwspec.param_count = 3;
189         parent_fwspec.param[0] = GIC_SPI;
190         parent_fwspec.param[1] = eic->irqs[hwirq];
191         parent_fwspec.param[2] = type;
192
193         return irq_domain_alloc_irqs_parent(domain, virq, 1, &parent_fwspec);
194 }
195
196 static const struct irq_domain_ops mchp_eic_domain_ops = {
197         .translate      = irq_domain_translate_twocell,
198         .alloc          = mchp_eic_domain_alloc,
199         .free           = irq_domain_free_irqs_common,
200 };
201
202 static int mchp_eic_init(struct device_node *node, struct device_node *parent)
203 {
204         struct irq_domain *parent_domain = NULL;
205         int ret, i;
206
207         eic = kzalloc(sizeof(*eic), GFP_KERNEL);
208         if (!eic)
209                 return -ENOMEM;
210
211         eic->base = of_iomap(node, 0);
212         if (!eic->base) {
213                 ret = -ENOMEM;
214                 goto free;
215         }
216
217         parent_domain = irq_find_host(parent);
218         if (!parent_domain) {
219                 ret = -ENODEV;
220                 goto unmap;
221         }
222
223         eic->clk = of_clk_get_by_name(node, "pclk");
224         if (IS_ERR(eic->clk)) {
225                 ret = PTR_ERR(eic->clk);
226                 goto unmap;
227         }
228
229         ret = clk_prepare_enable(eic->clk);
230         if (ret)
231                 goto unmap;
232
233         for (i = 0; i < MCHP_EIC_NIRQ; i++) {
234                 struct of_phandle_args irq;
235
236                 /* Disable it, if any. */
237                 writel_relaxed(0UL, eic->base + MCHP_EIC_SCFG(i));
238
239                 ret = of_irq_parse_one(node, i, &irq);
240                 if (ret)
241                         goto clk_unprepare;
242
243                 if (WARN_ON(irq.args_count != 3)) {
244                         ret = -EINVAL;
245                         goto clk_unprepare;
246                 }
247
248                 eic->irqs[i] = irq.args[1];
249         }
250
251         eic->domain = irq_domain_add_hierarchy(parent_domain, 0, MCHP_EIC_NIRQ,
252                                                node, &mchp_eic_domain_ops, eic);
253         if (!eic->domain) {
254                 pr_err("%pOF: Failed to add domain\n", node);
255                 ret = -ENODEV;
256                 goto clk_unprepare;
257         }
258
259         register_syscore_ops(&mchp_eic_syscore_ops);
260
261         pr_info("%pOF: EIC registered, nr_irqs %u\n", node, MCHP_EIC_NIRQ);
262
263         return 0;
264
265 clk_unprepare:
266         clk_disable_unprepare(eic->clk);
267 unmap:
268         iounmap(eic->base);
269 free:
270         kfree(eic);
271         return ret;
272 }
273
274 IRQCHIP_PLATFORM_DRIVER_BEGIN(mchp_eic)
275 IRQCHIP_MATCH("microchip,sama7g5-eic", mchp_eic_init)
276 IRQCHIP_PLATFORM_DRIVER_END(mchp_eic)
277
278 MODULE_DESCRIPTION("Microchip External Interrupt Controller");
279 MODULE_LICENSE("GPL v2");
280 MODULE_AUTHOR("Claudiu Beznea <claudiu.beznea@microchip.com>");