Merge tag 'fuse-update-5.2' of git://git.kernel.org/pub/scm/linux/kernel/git/mszeredi...
[sfrench/cifs-2.6.git] / drivers / irqchip / irq-imx-gpcv2.c
1 /*
2  * Copyright (C) 2015 Freescale Semiconductor, Inc.
3  *
4  * This program is free software; you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License version 2 as
6  * published by the Free Software Foundation.
7  */
8
9 #include <linux/of_address.h>
10 #include <linux/of_irq.h>
11 #include <linux/slab.h>
12 #include <linux/irqchip.h>
13 #include <linux/syscore_ops.h>
14
15 #define IMR_NUM                 4
16 #define GPC_MAX_IRQS            (IMR_NUM * 32)
17
18 #define GPC_IMR1_CORE0          0x30
19 #define GPC_IMR1_CORE1          0x40
20 #define GPC_IMR1_CORE2          0x1c0
21 #define GPC_IMR1_CORE3          0x1d0
22
23
24 struct gpcv2_irqchip_data {
25         struct raw_spinlock     rlock;
26         void __iomem            *gpc_base;
27         u32                     wakeup_sources[IMR_NUM];
28         u32                     saved_irq_mask[IMR_NUM];
29         u32                     cpu2wakeup;
30 };
31
32 static struct gpcv2_irqchip_data *imx_gpcv2_instance;
33
34 static void __iomem *gpcv2_idx_to_reg(struct gpcv2_irqchip_data *cd, int i)
35 {
36         return cd->gpc_base + cd->cpu2wakeup + i * 4;
37 }
38
39 static int gpcv2_wakeup_source_save(void)
40 {
41         struct gpcv2_irqchip_data *cd;
42         void __iomem *reg;
43         int i;
44
45         cd = imx_gpcv2_instance;
46         if (!cd)
47                 return 0;
48
49         for (i = 0; i < IMR_NUM; i++) {
50                 reg = gpcv2_idx_to_reg(cd, i);
51                 cd->saved_irq_mask[i] = readl_relaxed(reg);
52                 writel_relaxed(cd->wakeup_sources[i], reg);
53         }
54
55         return 0;
56 }
57
58 static void gpcv2_wakeup_source_restore(void)
59 {
60         struct gpcv2_irqchip_data *cd;
61         int i;
62
63         cd = imx_gpcv2_instance;
64         if (!cd)
65                 return;
66
67         for (i = 0; i < IMR_NUM; i++)
68                 writel_relaxed(cd->saved_irq_mask[i], gpcv2_idx_to_reg(cd, i));
69 }
70
71 static struct syscore_ops imx_gpcv2_syscore_ops = {
72         .suspend        = gpcv2_wakeup_source_save,
73         .resume         = gpcv2_wakeup_source_restore,
74 };
75
76 static int imx_gpcv2_irq_set_wake(struct irq_data *d, unsigned int on)
77 {
78         struct gpcv2_irqchip_data *cd = d->chip_data;
79         unsigned int idx = d->hwirq / 32;
80         unsigned long flags;
81         u32 mask, val;
82
83         raw_spin_lock_irqsave(&cd->rlock, flags);
84         mask = BIT(d->hwirq % 32);
85         val = cd->wakeup_sources[idx];
86
87         cd->wakeup_sources[idx] = on ? (val & ~mask) : (val | mask);
88         raw_spin_unlock_irqrestore(&cd->rlock, flags);
89
90         /*
91          * Do *not* call into the parent, as the GIC doesn't have any
92          * wake-up facility...
93          */
94
95         return 0;
96 }
97
98 static void imx_gpcv2_irq_unmask(struct irq_data *d)
99 {
100         struct gpcv2_irqchip_data *cd = d->chip_data;
101         void __iomem *reg;
102         u32 val;
103
104         raw_spin_lock(&cd->rlock);
105         reg = gpcv2_idx_to_reg(cd, d->hwirq / 32);
106         val = readl_relaxed(reg);
107         val &= ~BIT(d->hwirq % 32);
108         writel_relaxed(val, reg);
109         raw_spin_unlock(&cd->rlock);
110
111         irq_chip_unmask_parent(d);
112 }
113
114 static void imx_gpcv2_irq_mask(struct irq_data *d)
115 {
116         struct gpcv2_irqchip_data *cd = d->chip_data;
117         void __iomem *reg;
118         u32 val;
119
120         raw_spin_lock(&cd->rlock);
121         reg = gpcv2_idx_to_reg(cd, d->hwirq / 32);
122         val = readl_relaxed(reg);
123         val |= BIT(d->hwirq % 32);
124         writel_relaxed(val, reg);
125         raw_spin_unlock(&cd->rlock);
126
127         irq_chip_mask_parent(d);
128 }
129
130 static struct irq_chip gpcv2_irqchip_data_chip = {
131         .name                   = "GPCv2",
132         .irq_eoi                = irq_chip_eoi_parent,
133         .irq_mask               = imx_gpcv2_irq_mask,
134         .irq_unmask             = imx_gpcv2_irq_unmask,
135         .irq_set_wake           = imx_gpcv2_irq_set_wake,
136         .irq_retrigger          = irq_chip_retrigger_hierarchy,
137 #ifdef CONFIG_SMP
138         .irq_set_affinity       = irq_chip_set_affinity_parent,
139 #endif
140 };
141
142 static int imx_gpcv2_domain_translate(struct irq_domain *d,
143                                       struct irq_fwspec *fwspec,
144                                       unsigned long *hwirq,
145                                       unsigned int *type)
146 {
147         if (is_of_node(fwspec->fwnode)) {
148                 if (fwspec->param_count != 3)
149                         return -EINVAL;
150
151                 /* No PPI should point to this domain */
152                 if (fwspec->param[0] != 0)
153                         return -EINVAL;
154
155                 *hwirq = fwspec->param[1];
156                 *type = fwspec->param[2];
157                 return 0;
158         }
159
160         return -EINVAL;
161 }
162
163 static int imx_gpcv2_domain_alloc(struct irq_domain *domain,
164                                   unsigned int irq, unsigned int nr_irqs,
165                                   void *data)
166 {
167         struct irq_fwspec *fwspec = data;
168         struct irq_fwspec parent_fwspec;
169         irq_hw_number_t hwirq;
170         unsigned int type;
171         int err;
172         int i;
173
174         err = imx_gpcv2_domain_translate(domain, fwspec, &hwirq, &type);
175         if (err)
176                 return err;
177
178         if (hwirq >= GPC_MAX_IRQS)
179                 return -EINVAL;
180
181         for (i = 0; i < nr_irqs; i++) {
182                 irq_domain_set_hwirq_and_chip(domain, irq + i, hwirq + i,
183                                 &gpcv2_irqchip_data_chip, domain->host_data);
184         }
185
186         parent_fwspec = *fwspec;
187         parent_fwspec.fwnode = domain->parent->fwnode;
188         return irq_domain_alloc_irqs_parent(domain, irq, nr_irqs,
189                                             &parent_fwspec);
190 }
191
192 static const struct irq_domain_ops gpcv2_irqchip_data_domain_ops = {
193         .translate      = imx_gpcv2_domain_translate,
194         .alloc          = imx_gpcv2_domain_alloc,
195         .free           = irq_domain_free_irqs_common,
196 };
197
198 static const struct of_device_id gpcv2_of_match[] = {
199         { .compatible = "fsl,imx7d-gpc",  .data = (const void *) 2 },
200         { .compatible = "fsl,imx8mq-gpc", .data = (const void *) 4 },
201         { /* END */ }
202 };
203
204 static int __init imx_gpcv2_irqchip_init(struct device_node *node,
205                                struct device_node *parent)
206 {
207         struct irq_domain *parent_domain, *domain;
208         struct gpcv2_irqchip_data *cd;
209         const struct of_device_id *id;
210         unsigned long core_num;
211         int i;
212
213         if (!parent) {
214                 pr_err("%pOF: no parent, giving up\n", node);
215                 return -ENODEV;
216         }
217
218         id = of_match_node(gpcv2_of_match, node);
219         if (!id) {
220                 pr_err("%pOF: unknown compatibility string\n", node);
221                 return -ENODEV;
222         }
223
224         core_num = (unsigned long)id->data;
225
226         parent_domain = irq_find_host(parent);
227         if (!parent_domain) {
228                 pr_err("%pOF: unable to get parent domain\n", node);
229                 return -ENXIO;
230         }
231
232         cd = kzalloc(sizeof(struct gpcv2_irqchip_data), GFP_KERNEL);
233         if (!cd) {
234                 pr_err("%pOF: kzalloc failed!\n", node);
235                 return -ENOMEM;
236         }
237
238         raw_spin_lock_init(&cd->rlock);
239
240         cd->gpc_base = of_iomap(node, 0);
241         if (!cd->gpc_base) {
242                 pr_err("%pOF: unable to map gpc registers\n", node);
243                 kfree(cd);
244                 return -ENOMEM;
245         }
246
247         domain = irq_domain_add_hierarchy(parent_domain, 0, GPC_MAX_IRQS,
248                                 node, &gpcv2_irqchip_data_domain_ops, cd);
249         if (!domain) {
250                 iounmap(cd->gpc_base);
251                 kfree(cd);
252                 return -ENOMEM;
253         }
254         irq_set_default_host(domain);
255
256         /* Initially mask all interrupts */
257         for (i = 0; i < IMR_NUM; i++) {
258                 void __iomem *reg = cd->gpc_base + i * 4;
259
260                 switch (core_num) {
261                 case 4:
262                         writel_relaxed(~0, reg + GPC_IMR1_CORE2);
263                         writel_relaxed(~0, reg + GPC_IMR1_CORE3);
264                         /* fall through */
265                 case 2:
266                         writel_relaxed(~0, reg + GPC_IMR1_CORE0);
267                         writel_relaxed(~0, reg + GPC_IMR1_CORE1);
268                 }
269                 cd->wakeup_sources[i] = ~0;
270         }
271
272         /* Let CORE0 as the default CPU to wake up by GPC */
273         cd->cpu2wakeup = GPC_IMR1_CORE0;
274
275         /*
276          * Due to hardware design failure, need to make sure GPR
277          * interrupt(#32) is unmasked during RUN mode to avoid entering
278          * DSM by mistake.
279          */
280         writel_relaxed(~0x1, cd->gpc_base + cd->cpu2wakeup);
281
282         imx_gpcv2_instance = cd;
283         register_syscore_ops(&imx_gpcv2_syscore_ops);
284
285         /*
286          * Clear the OF_POPULATED flag set in of_irq_init so that
287          * later the GPC power domain driver will not be skipped.
288          */
289         of_node_clear_flag(node, OF_POPULATED);
290         return 0;
291 }
292
293 IRQCHIP_DECLARE(imx_gpcv2_imx7d, "fsl,imx7d-gpc", imx_gpcv2_irqchip_init);
294 IRQCHIP_DECLARE(imx_gpcv2_imx8mq, "fsl,imx8mq-gpc", imx_gpcv2_irqchip_init);