blob: f4e9799fa4f519e438eb0ea6c4a354c8769bb5bd [file] [log] [blame]
Maulik Shahe0a55832018-01-23 14:24:18 +05301/* Copyright (c) 2017-2018, The Linux Foundation. All rights reserved.
Archana Sathyakumar741a37a2017-05-10 11:03:39 -06002 *
3 * This program is free software; you can redistribute it and/or modify
4 * it under the terms of the GNU General Public License version 2 and
5 * only version 2 as published by the Free Software Foundation.
6 *
7 * This program is distributed in the hope that it will be useful,
8 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 * GNU General Public License for more details.
11 *
12 */
13
14#include <linux/delay.h>
15#include <linux/err.h>
16#include <linux/init.h>
17#include <linux/irq.h>
18#include <linux/irqchip.h>
Maulik Shahe0a55832018-01-23 14:24:18 +053019#include <linux/interrupt.h>
Archana Sathyakumar741a37a2017-05-10 11:03:39 -060020#include <linux/irqdomain.h>
21#include <linux/io.h>
22#include <linux/kernel.h>
23#include <linux/of.h>
24#include <linux/of_address.h>
25#include <linux/of_device.h>
26#include <linux/spinlock.h>
27#include <linux/platform_device.h>
28#include <linux/slab.h>
29#include <linux/types.h>
30#include "pdc.h"
31#define CREATE_TRACE_POINTS
32#include "trace/events/pdc.h"
33
34#define MAX_IRQS 126
35#define CLEAR_INTR(reg, intr) (reg & ~(1 << intr))
36#define ENABLE_INTR(reg, intr) (reg | (1 << intr))
37
38enum pdc_register_offsets {
39 IRQ_ENABLE_BANK = 0x10,
40 IRQ_i_CFG = 0x110,
41};
42
43static DEFINE_SPINLOCK(pdc_lock);
44static void __iomem *pdc_base;
45
46static int get_pdc_pin(irq_hw_number_t hwirq, void *data)
47{
48 int i;
49 struct pdc_pin *pdc_data = (struct pdc_pin *) data;
50
51 for (i = 0; pdc_data[i].pin >= 0; i++) {
52 if (pdc_data[i].hwirq == hwirq)
53 return pdc_data[i].pin;
54 }
55
56 return -EINVAL;
57}
58
59static inline int pdc_enable_intr(struct irq_data *d, bool on)
60{
61 int pin_out = get_pdc_pin(d->hwirq, d->chip_data);
62 unsigned int index, mask;
63 u32 enable, r_enable;
64 unsigned long flags;
65
66 if (pin_out < 0)
67 return 0;
68
69 index = pin_out / 32;
70 mask = pin_out % 32;
71 spin_lock_irqsave(&pdc_lock, flags);
72
73 enable = readl_relaxed(pdc_base + IRQ_ENABLE_BANK + (index *
74 sizeof(uint32_t)));
75 if (on)
76 enable = ENABLE_INTR(enable, mask);
77 else
78 enable = CLEAR_INTR(enable, mask);
79
80 writel_relaxed(enable, pdc_base + IRQ_ENABLE_BANK + (index *
81 sizeof(uint32_t)));
82
83 do {
84 r_enable = readl_relaxed(pdc_base + IRQ_ENABLE_BANK +
85 (index * sizeof(uint32_t)));
86 if (r_enable == enable)
87 break;
88 udelay(5);
89 } while (1);
90
91 spin_unlock_irqrestore(&pdc_lock, flags);
92
93 trace_irq_pin_config("enable", (u32)pin_out, (u32)d->hwirq,
94 0, on);
95
96 return 0;
97}
98
Maulik Shahe0a55832018-01-23 14:24:18 +053099static int qcom_pdc_gic_get_irqchip_state(struct irq_data *d,
100 enum irqchip_irq_state which, bool *state)
101{
Maulik Shahdf374692018-02-08 09:54:14 +0530102 return d->parent_data->chip->irq_get_irqchip_state(d->parent_data,
Maulik Shahe0a55832018-01-23 14:24:18 +0530103 which, state);
104}
105
106static int qcom_pdc_gic_set_irqchip_state(struct irq_data *d,
107 enum irqchip_irq_state which, bool value)
108{
Maulik Shahdf374692018-02-08 09:54:14 +0530109 return d->parent_data->chip->irq_set_irqchip_state(d->parent_data,
Maulik Shahe0a55832018-01-23 14:24:18 +0530110 which, value);
111}
112
Archana Sathyakumar741a37a2017-05-10 11:03:39 -0600113static void qcom_pdc_gic_mask(struct irq_data *d)
114{
115 pdc_enable_intr(d, false);
116 irq_chip_mask_parent(d);
117}
118
119static void qcom_pdc_gic_unmask(struct irq_data *d)
120{
121 pdc_enable_intr(d, true);
122 irq_chip_unmask_parent(d);
123}
124
125static void qcom_pdc_gic_enable(struct irq_data *d)
126{
127 pdc_enable_intr(d, true);
128 irq_chip_enable_parent(d);
129}
130
131static void qcom_pdc_gic_disable(struct irq_data *d)
132{
133 pdc_enable_intr(d, false);
134 irq_chip_disable_parent(d);
135}
136
137/*
138 * GIC does not handle falling edge or active low. To allow falling edge and
139 * active low interrupts to be handled at GIC, PDC has an inverter that inverts
140 * falling edge into a rising edge and active low into an active high.
141 * For the inverter to work, the polarity bit in the IRQ_CONFIG register has to
142 * set as per the table below.
143 * (polarity, falling edge, rising edge ) ORIG POL CONV POLARITY
144 * 3'b0 00 Level sensitive active low (~~~|_____) (___|~~~~~) LOW
145 * 3'b0 01 Rising edge sensitive (___|~~|__) (~~~|__|~~) NOT USED
146 * 3'b0 10 Falling edge sensitive (~~~|__|~~) (___|~~|__) LOW
147 * 3'b0 11 Dual Edge sensitive NOT USED
148 * 3'b1 00 Level senstive active High (___|~~~~~) (___|~~~~~) HIGH
149 * 3'b1 01 Falling Edge sensitive (~~~|__|~~) (~~~|__|~~) NOT USED
150 * 3'b1 10 Rising edge sensitive (___|~~|__) (___|~~|__) HIGH
151 * 3'b1 11 Dual Edge sensitive HIGH
152 */
153enum pdc_irq_config_bits {
154 POLARITY_LOW = 0, //0 00
155 FALLING_EDGE = 2, //0 10
156 POLARITY_HIGH = 4,//1 00
157 RISING_EDGE = 6, //1 10
158 DUAL_EDGE = 7, //1 11
159};
160
161static int qcom_pdc_gic_set_type(struct irq_data *d, unsigned int type)
162{
163 int pin_out = get_pdc_pin(d->hwirq, d->chip_data);
164 u32 pdc_type = 0, config;
165
166 if (pin_out < 0)
167 goto fwd_to_parent;
168
169 switch (type) {
170 case IRQ_TYPE_EDGE_RISING:
171 pdc_type = RISING_EDGE;
172 break;
173 case IRQ_TYPE_EDGE_FALLING:
174 pdc_type = FALLING_EDGE;
175 break;
176 case IRQ_TYPE_EDGE_BOTH:
177 pdc_type = DUAL_EDGE;
178 break;
179 case IRQ_TYPE_LEVEL_HIGH:
180 pdc_type = POLARITY_HIGH;
181 break;
182 case IRQ_TYPE_LEVEL_LOW:
183 pdc_type = POLARITY_LOW;
184 break;
185 default:
186 pdc_type = POLARITY_HIGH;
187 break;
188 }
189 writel_relaxed(pdc_type, pdc_base + IRQ_i_CFG +
190 (pin_out * sizeof(uint32_t)));
191
192 do {
193 config = readl_relaxed(pdc_base + IRQ_i_CFG +
194 (pin_out * sizeof(uint32_t)));
195 if (config == pdc_type)
196 break;
197 udelay(5);
198 } while (1);
199
200 trace_irq_pin_config("type_config", (u32)pin_out, (u32)d->hwirq,
201 pdc_type, 0);
202
203 /*
204 * If type is edge triggered, forward that as Rising edge as PDC
205 * takes care of converting falling edge to rising edge signal
206 */
207 if (type & (IRQ_TYPE_EDGE_RISING | IRQ_TYPE_EDGE_FALLING))
208 type = IRQ_TYPE_EDGE_RISING;
209
210 /*
211 * If type is level, then forward that as level high as PDC
212 * takes care of converting falling edge to rising edge signal
213 */
214 if (type & (IRQ_TYPE_LEVEL_LOW | IRQ_TYPE_LEVEL_HIGH))
215 type = IRQ_TYPE_LEVEL_HIGH;
216
217fwd_to_parent:
218
219 return irq_chip_set_type_parent(d, type);
220}
221
222static struct irq_chip qcom_pdc_gic_chip = {
223 .name = "PDC-GIC",
224 .irq_eoi = irq_chip_eoi_parent,
225 .irq_mask = qcom_pdc_gic_mask,
226 .irq_enable = qcom_pdc_gic_enable,
227 .irq_unmask = qcom_pdc_gic_unmask,
228 .irq_disable = qcom_pdc_gic_disable,
229 .irq_retrigger = irq_chip_retrigger_hierarchy,
230 .irq_set_type = qcom_pdc_gic_set_type,
231 .flags = IRQCHIP_MASK_ON_SUSPEND |
232 IRQCHIP_SET_TYPE_MASKED |
233 IRQCHIP_SKIP_SET_WAKE,
234 .irq_set_vcpu_affinity = irq_chip_set_vcpu_affinity_parent,
235#ifdef CONFIG_SMP
236 .irq_set_affinity = irq_chip_set_affinity_parent,
237#endif
Maulik Shahe0a55832018-01-23 14:24:18 +0530238 .irq_get_irqchip_state = qcom_pdc_gic_get_irqchip_state,
239 .irq_set_irqchip_state = qcom_pdc_gic_set_irqchip_state,
Archana Sathyakumar741a37a2017-05-10 11:03:39 -0600240};
241
242static int qcom_pdc_translate(struct irq_domain *d,
243 struct irq_fwspec *fwspec, unsigned long *hwirq, unsigned int *type)
244{
245 return d->parent->ops->translate(d->parent, fwspec, hwirq, type);
246}
247
248static int qcom_pdc_alloc(struct irq_domain *domain,
249 unsigned int virq, unsigned int nr_irqs, void *data)
250{
251 struct irq_fwspec *fwspec = data;
252 struct irq_fwspec parent_fwspec;
253 irq_hw_number_t hwirq;
254 int i;
255 unsigned int type;
256 int ret;
257
258 ret = qcom_pdc_translate(domain, fwspec, &hwirq, &type);
259 if (ret)
260 return -EINVAL;
261
262 for (i = 0; i < nr_irqs; i++)
263 irq_domain_set_hwirq_and_chip(domain, virq + i, hwirq + i,
264 &qcom_pdc_gic_chip, domain->host_data);
265
266 parent_fwspec = *fwspec;
267 parent_fwspec.fwnode = domain->parent->fwnode;
268
269 return irq_domain_alloc_irqs_parent(domain, virq, nr_irqs,
270 &parent_fwspec);
271}
272
273static const struct irq_domain_ops qcom_pdc_ops = {
274 .translate = qcom_pdc_translate,
275 .alloc = qcom_pdc_alloc,
276 .free = irq_domain_free_irqs_common,
277};
278
279int qcom_pdc_init(struct device_node *node,
280 struct device_node *parent, void *data)
281{
282 struct irq_domain *parent_domain;
283 int ret;
284 struct irq_domain *pdc_domain;
285
286 pdc_base = of_iomap(node, 0);
287 if (!pdc_base) {
288 pr_err("%s(): unable to map PDC registers\n", node->full_name);
289 return -ENXIO;
290 }
291
292 parent_domain = irq_find_host(parent);
293 if (!parent_domain) {
294 pr_err("unable to obtain PDC parent domain\n");
295 ret = -ENXIO;
296 goto failure;
297 }
298
299 pdc_domain = irq_domain_add_hierarchy(parent_domain, 0, MAX_IRQS,
300 node, &qcom_pdc_ops, data);
301 if (!pdc_domain) {
302 pr_err("GIC domain add failed\n");
303 ret = -ENOMEM;
304 goto failure;
305 }
306
307 pdc_domain->name = "qcom,pdc";
308
309 return 0;
310
311failure:
312 iounmap(pdc_base);
313
314 return ret;
315}
316EXPORT_SYMBOL(qcom_pdc_init);