2 * Copyright (c) 2016 MediaTek Inc.
3 * Author: Youlin.Pei <youlin.pei@mediatek.com>
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License version 2 as
7 * published by the Free Software Foundation.
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
15 #include <linux/interrupt.h>
17 #include <linux/irq.h>
18 #include <linux/irqchip.h>
19 #include <linux/irqdomain.h>
21 #include <linux/of_irq.h>
22 #include <linux/of_address.h>
23 #include <linux/slab.h>
24 #include <linux/syscore_ops.h>
27 #define CIRQ_MASK_SET 0xc0
28 #define CIRQ_MASK_CLR 0x100
29 #define CIRQ_SENS_SET 0x180
30 #define CIRQ_SENS_CLR 0x1c0
31 #define CIRQ_POL_SET 0x240
32 #define CIRQ_POL_CLR 0x280
33 #define CIRQ_CONTROL 0x300
37 #define CIRQ_FLUSH 0x4
39 struct mtk_cirq_chip_data
{
41 unsigned int ext_irq_start
;
42 unsigned int ext_irq_end
;
43 struct irq_domain
*domain
;
46 static struct mtk_cirq_chip_data
*cirq_data
;
48 static void mtk_cirq_write_mask(struct irq_data
*data
, unsigned int offset
)
50 struct mtk_cirq_chip_data
*chip_data
= data
->chip_data
;
51 unsigned int cirq_num
= data
->hwirq
;
52 u32 mask
= 1 << (cirq_num
% 32);
54 writel_relaxed(mask
, chip_data
->base
+ offset
+ (cirq_num
/ 32) * 4);
57 static void mtk_cirq_mask(struct irq_data
*data
)
59 mtk_cirq_write_mask(data
, CIRQ_MASK_SET
);
60 irq_chip_mask_parent(data
);
63 static void mtk_cirq_unmask(struct irq_data
*data
)
65 mtk_cirq_write_mask(data
, CIRQ_MASK_CLR
);
66 irq_chip_unmask_parent(data
);
69 static int mtk_cirq_set_type(struct irq_data
*data
, unsigned int type
)
73 switch (type
& IRQ_TYPE_SENSE_MASK
) {
74 case IRQ_TYPE_EDGE_FALLING
:
75 mtk_cirq_write_mask(data
, CIRQ_POL_CLR
);
76 mtk_cirq_write_mask(data
, CIRQ_SENS_CLR
);
78 case IRQ_TYPE_EDGE_RISING
:
79 mtk_cirq_write_mask(data
, CIRQ_POL_SET
);
80 mtk_cirq_write_mask(data
, CIRQ_SENS_CLR
);
82 case IRQ_TYPE_LEVEL_LOW
:
83 mtk_cirq_write_mask(data
, CIRQ_POL_CLR
);
84 mtk_cirq_write_mask(data
, CIRQ_SENS_SET
);
86 case IRQ_TYPE_LEVEL_HIGH
:
87 mtk_cirq_write_mask(data
, CIRQ_POL_SET
);
88 mtk_cirq_write_mask(data
, CIRQ_SENS_SET
);
94 data
= data
->parent_data
;
95 ret
= data
->chip
->irq_set_type(data
, type
);
99 static struct irq_chip mtk_cirq_chip
= {
101 .irq_mask
= mtk_cirq_mask
,
102 .irq_unmask
= mtk_cirq_unmask
,
103 .irq_eoi
= irq_chip_eoi_parent
,
104 .irq_set_type
= mtk_cirq_set_type
,
105 .irq_retrigger
= irq_chip_retrigger_hierarchy
,
107 .irq_set_affinity
= irq_chip_set_affinity_parent
,
111 static int mtk_cirq_domain_translate(struct irq_domain
*d
,
112 struct irq_fwspec
*fwspec
,
113 unsigned long *hwirq
,
116 if (is_of_node(fwspec
->fwnode
)) {
117 if (fwspec
->param_count
!= 3)
120 /* No PPI should point to this domain */
121 if (fwspec
->param
[0] != 0)
124 /* cirq support irq number check */
125 if (fwspec
->param
[1] < cirq_data
->ext_irq_start
||
126 fwspec
->param
[1] > cirq_data
->ext_irq_end
)
129 *hwirq
= fwspec
->param
[1] - cirq_data
->ext_irq_start
;
130 *type
= fwspec
->param
[2] & IRQ_TYPE_SENSE_MASK
;
137 static int mtk_cirq_domain_alloc(struct irq_domain
*domain
, unsigned int virq
,
138 unsigned int nr_irqs
, void *arg
)
141 irq_hw_number_t hwirq
;
143 struct irq_fwspec
*fwspec
= arg
;
144 struct irq_fwspec parent_fwspec
= *fwspec
;
146 ret
= mtk_cirq_domain_translate(domain
, fwspec
, &hwirq
, &type
);
150 if (WARN_ON(nr_irqs
!= 1))
153 irq_domain_set_hwirq_and_chip(domain
, virq
, hwirq
,
157 parent_fwspec
.fwnode
= domain
->parent
->fwnode
;
158 return irq_domain_alloc_irqs_parent(domain
, virq
, nr_irqs
,
162 static const struct irq_domain_ops cirq_domain_ops
= {
163 .translate
= mtk_cirq_domain_translate
,
164 .alloc
= mtk_cirq_domain_alloc
,
165 .free
= irq_domain_free_irqs_common
,
168 #ifdef CONFIG_PM_SLEEP
169 static int mtk_cirq_suspend(void)
172 unsigned int irq
, hwirq_num
;
173 bool pending
, masked
;
174 int i
, pendret
, maskret
;
177 * When external interrupts happened, CIRQ will record the status
178 * even CIRQ is not enabled. When execute flush command, CIRQ will
179 * resend the signals according to the status. So if don't clear the
180 * status, CIRQ will resend the wrong signals.
182 * arch_suspend_disable_irqs() will be called before CIRQ suspend
183 * callback. If clear all the status simply, the external interrupts
184 * which happened between arch_suspend_disable_irqs and CIRQ suspend
185 * callback will be lost. Using following steps to avoid this issue;
187 * - Iterate over all the CIRQ supported interrupts;
188 * - For each interrupt, inspect its pending and masked status at GIC
190 * - If pending and unmasked, it happened between
191 * arch_suspend_disable_irqs and CIRQ suspend callback, don't ACK
192 * it. Otherwise, ACK it.
194 hwirq_num
= cirq_data
->ext_irq_end
- cirq_data
->ext_irq_start
+ 1;
195 for (i
= 0; i
< hwirq_num
; i
++) {
196 irq
= irq_find_mapping(cirq_data
->domain
, i
);
198 pendret
= irq_get_irqchip_state(irq
,
199 IRQCHIP_STATE_PENDING
,
202 maskret
= irq_get_irqchip_state(irq
,
203 IRQCHIP_STATE_MASKED
,
206 if (pendret
== 0 && maskret
== 0 &&
207 (pending
&& !masked
))
211 mask
= 1 << (i
% 32);
212 writel_relaxed(mask
, cirq_data
->base
+ CIRQ_ACK
+ (i
/ 32) * 4);
215 /* set edge_only mode, record edge-triggerd interrupts */
217 value
= readl_relaxed(cirq_data
->base
+ CIRQ_CONTROL
);
218 value
|= (CIRQ_EDGE
| CIRQ_EN
);
219 writel_relaxed(value
, cirq_data
->base
+ CIRQ_CONTROL
);
224 static void mtk_cirq_resume(void)
228 /* flush recored interrupts, will send signals to parent controller */
229 value
= readl_relaxed(cirq_data
->base
+ CIRQ_CONTROL
);
230 writel_relaxed(value
| CIRQ_FLUSH
, cirq_data
->base
+ CIRQ_CONTROL
);
233 value
= readl_relaxed(cirq_data
->base
+ CIRQ_CONTROL
);
234 value
&= ~(CIRQ_EDGE
| CIRQ_EN
);
235 writel_relaxed(value
, cirq_data
->base
+ CIRQ_CONTROL
);
238 static struct syscore_ops mtk_cirq_syscore_ops
= {
239 .suspend
= mtk_cirq_suspend
,
240 .resume
= mtk_cirq_resume
,
243 static void mtk_cirq_syscore_init(void)
245 register_syscore_ops(&mtk_cirq_syscore_ops
);
248 static inline void mtk_cirq_syscore_init(void) {}
251 static int __init
mtk_cirq_of_init(struct device_node
*node
,
252 struct device_node
*parent
)
254 struct irq_domain
*domain
, *domain_parent
;
255 unsigned int irq_num
;
258 domain_parent
= irq_find_host(parent
);
259 if (!domain_parent
) {
260 pr_err("mtk_cirq: interrupt-parent not found\n");
264 cirq_data
= kzalloc(sizeof(*cirq_data
), GFP_KERNEL
);
268 cirq_data
->base
= of_iomap(node
, 0);
269 if (!cirq_data
->base
) {
270 pr_err("mtk_cirq: unable to map cirq register\n");
275 ret
= of_property_read_u32_index(node
, "mediatek,ext-irq-range", 0,
276 &cirq_data
->ext_irq_start
);
280 ret
= of_property_read_u32_index(node
, "mediatek,ext-irq-range", 1,
281 &cirq_data
->ext_irq_end
);
285 irq_num
= cirq_data
->ext_irq_end
- cirq_data
->ext_irq_start
+ 1;
286 domain
= irq_domain_add_hierarchy(domain_parent
, 0,
288 &cirq_domain_ops
, cirq_data
);
293 cirq_data
->domain
= domain
;
295 mtk_cirq_syscore_init();
300 iounmap(cirq_data
->base
);
306 IRQCHIP_DECLARE(mtk_cirq
, "mediatek,mtk-cirq", mtk_cirq_of_init
);