1*4882a593Smuzhiyun // SPDX-License-Identifier: GPL-2.0-or-later
2*4882a593Smuzhiyun /*
3*4882a593Smuzhiyun * Copyright (C) 2009-2010 Freescale Semiconductor, Inc. All Rights Reserved.
4*4882a593Smuzhiyun * Copyright (C) 2014 Oleksij Rempel <linux@rempel-privat.de>
5*4882a593Smuzhiyun * Add Alphascale ASM9260 support.
6*4882a593Smuzhiyun */
7*4882a593Smuzhiyun
8*4882a593Smuzhiyun #include <linux/kernel.h>
9*4882a593Smuzhiyun #include <linux/init.h>
10*4882a593Smuzhiyun #include <linux/irq.h>
11*4882a593Smuzhiyun #include <linux/irqchip.h>
12*4882a593Smuzhiyun #include <linux/irqdomain.h>
13*4882a593Smuzhiyun #include <linux/io.h>
14*4882a593Smuzhiyun #include <linux/of.h>
15*4882a593Smuzhiyun #include <linux/of_address.h>
16*4882a593Smuzhiyun #include <linux/of_irq.h>
17*4882a593Smuzhiyun #include <linux/stmp_device.h>
18*4882a593Smuzhiyun #include <asm/exception.h>
19*4882a593Smuzhiyun
20*4882a593Smuzhiyun #include "alphascale_asm9260-icoll.h"
21*4882a593Smuzhiyun
22*4882a593Smuzhiyun /*
23*4882a593Smuzhiyun * this device provide 4 offsets for each register:
24*4882a593Smuzhiyun * 0x0 - plain read write mode
25*4882a593Smuzhiyun * 0x4 - set mode, OR logic.
26*4882a593Smuzhiyun * 0x8 - clr mode, XOR logic.
27*4882a593Smuzhiyun * 0xc - togle mode.
28*4882a593Smuzhiyun */
29*4882a593Smuzhiyun #define SET_REG 4
30*4882a593Smuzhiyun #define CLR_REG 8
31*4882a593Smuzhiyun
32*4882a593Smuzhiyun #define HW_ICOLL_VECTOR 0x0000
33*4882a593Smuzhiyun #define HW_ICOLL_LEVELACK 0x0010
34*4882a593Smuzhiyun #define HW_ICOLL_CTRL 0x0020
35*4882a593Smuzhiyun #define HW_ICOLL_STAT_OFFSET 0x0070
36*4882a593Smuzhiyun #define HW_ICOLL_INTERRUPT0 0x0120
37*4882a593Smuzhiyun #define HW_ICOLL_INTERRUPTn(n) ((n) * 0x10)
38*4882a593Smuzhiyun #define BM_ICOLL_INTR_ENABLE BIT(2)
39*4882a593Smuzhiyun #define BV_ICOLL_LEVELACK_IRQLEVELACK__LEVEL0 0x1
40*4882a593Smuzhiyun
41*4882a593Smuzhiyun #define ICOLL_NUM_IRQS 128
42*4882a593Smuzhiyun
43*4882a593Smuzhiyun enum icoll_type {
44*4882a593Smuzhiyun ICOLL,
45*4882a593Smuzhiyun ASM9260_ICOLL,
46*4882a593Smuzhiyun };
47*4882a593Smuzhiyun
48*4882a593Smuzhiyun struct icoll_priv {
49*4882a593Smuzhiyun void __iomem *vector;
50*4882a593Smuzhiyun void __iomem *levelack;
51*4882a593Smuzhiyun void __iomem *ctrl;
52*4882a593Smuzhiyun void __iomem *stat;
53*4882a593Smuzhiyun void __iomem *intr;
54*4882a593Smuzhiyun void __iomem *clear;
55*4882a593Smuzhiyun enum icoll_type type;
56*4882a593Smuzhiyun };
57*4882a593Smuzhiyun
58*4882a593Smuzhiyun static struct icoll_priv icoll_priv;
59*4882a593Smuzhiyun static struct irq_domain *icoll_domain;
60*4882a593Smuzhiyun
61*4882a593Smuzhiyun /* calculate bit offset depending on number of intterupt per register */
icoll_intr_bitshift(struct irq_data * d,u32 bit)62*4882a593Smuzhiyun static u32 icoll_intr_bitshift(struct irq_data *d, u32 bit)
63*4882a593Smuzhiyun {
64*4882a593Smuzhiyun /*
65*4882a593Smuzhiyun * mask lower part of hwirq to convert it
66*4882a593Smuzhiyun * in 0, 1, 2 or 3 and then multiply it by 8 (or shift by 3)
67*4882a593Smuzhiyun */
68*4882a593Smuzhiyun return bit << ((d->hwirq & 3) << 3);
69*4882a593Smuzhiyun }
70*4882a593Smuzhiyun
71*4882a593Smuzhiyun /* calculate mem offset depending on number of intterupt per register */
icoll_intr_reg(struct irq_data * d)72*4882a593Smuzhiyun static void __iomem *icoll_intr_reg(struct irq_data *d)
73*4882a593Smuzhiyun {
74*4882a593Smuzhiyun /* offset = hwirq / intr_per_reg * 0x10 */
75*4882a593Smuzhiyun return icoll_priv.intr + ((d->hwirq >> 2) * 0x10);
76*4882a593Smuzhiyun }
77*4882a593Smuzhiyun
icoll_ack_irq(struct irq_data * d)78*4882a593Smuzhiyun static void icoll_ack_irq(struct irq_data *d)
79*4882a593Smuzhiyun {
80*4882a593Smuzhiyun /*
81*4882a593Smuzhiyun * The Interrupt Collector is able to prioritize irqs.
82*4882a593Smuzhiyun * Currently only level 0 is used. So acking can use
83*4882a593Smuzhiyun * BV_ICOLL_LEVELACK_IRQLEVELACK__LEVEL0 unconditionally.
84*4882a593Smuzhiyun */
85*4882a593Smuzhiyun __raw_writel(BV_ICOLL_LEVELACK_IRQLEVELACK__LEVEL0,
86*4882a593Smuzhiyun icoll_priv.levelack);
87*4882a593Smuzhiyun }
88*4882a593Smuzhiyun
icoll_mask_irq(struct irq_data * d)89*4882a593Smuzhiyun static void icoll_mask_irq(struct irq_data *d)
90*4882a593Smuzhiyun {
91*4882a593Smuzhiyun __raw_writel(BM_ICOLL_INTR_ENABLE,
92*4882a593Smuzhiyun icoll_priv.intr + CLR_REG + HW_ICOLL_INTERRUPTn(d->hwirq));
93*4882a593Smuzhiyun }
94*4882a593Smuzhiyun
icoll_unmask_irq(struct irq_data * d)95*4882a593Smuzhiyun static void icoll_unmask_irq(struct irq_data *d)
96*4882a593Smuzhiyun {
97*4882a593Smuzhiyun __raw_writel(BM_ICOLL_INTR_ENABLE,
98*4882a593Smuzhiyun icoll_priv.intr + SET_REG + HW_ICOLL_INTERRUPTn(d->hwirq));
99*4882a593Smuzhiyun }
100*4882a593Smuzhiyun
asm9260_mask_irq(struct irq_data * d)101*4882a593Smuzhiyun static void asm9260_mask_irq(struct irq_data *d)
102*4882a593Smuzhiyun {
103*4882a593Smuzhiyun __raw_writel(icoll_intr_bitshift(d, BM_ICOLL_INTR_ENABLE),
104*4882a593Smuzhiyun icoll_intr_reg(d) + CLR_REG);
105*4882a593Smuzhiyun }
106*4882a593Smuzhiyun
asm9260_unmask_irq(struct irq_data * d)107*4882a593Smuzhiyun static void asm9260_unmask_irq(struct irq_data *d)
108*4882a593Smuzhiyun {
109*4882a593Smuzhiyun __raw_writel(ASM9260_BM_CLEAR_BIT(d->hwirq),
110*4882a593Smuzhiyun icoll_priv.clear +
111*4882a593Smuzhiyun ASM9260_HW_ICOLL_CLEARn(d->hwirq));
112*4882a593Smuzhiyun
113*4882a593Smuzhiyun __raw_writel(icoll_intr_bitshift(d, BM_ICOLL_INTR_ENABLE),
114*4882a593Smuzhiyun icoll_intr_reg(d) + SET_REG);
115*4882a593Smuzhiyun }
116*4882a593Smuzhiyun
117*4882a593Smuzhiyun static struct irq_chip mxs_icoll_chip = {
118*4882a593Smuzhiyun .irq_ack = icoll_ack_irq,
119*4882a593Smuzhiyun .irq_mask = icoll_mask_irq,
120*4882a593Smuzhiyun .irq_unmask = icoll_unmask_irq,
121*4882a593Smuzhiyun .flags = IRQCHIP_MASK_ON_SUSPEND |
122*4882a593Smuzhiyun IRQCHIP_SKIP_SET_WAKE,
123*4882a593Smuzhiyun };
124*4882a593Smuzhiyun
125*4882a593Smuzhiyun static struct irq_chip asm9260_icoll_chip = {
126*4882a593Smuzhiyun .irq_ack = icoll_ack_irq,
127*4882a593Smuzhiyun .irq_mask = asm9260_mask_irq,
128*4882a593Smuzhiyun .irq_unmask = asm9260_unmask_irq,
129*4882a593Smuzhiyun .flags = IRQCHIP_MASK_ON_SUSPEND |
130*4882a593Smuzhiyun IRQCHIP_SKIP_SET_WAKE,
131*4882a593Smuzhiyun };
132*4882a593Smuzhiyun
icoll_handle_irq(struct pt_regs * regs)133*4882a593Smuzhiyun asmlinkage void __exception_irq_entry icoll_handle_irq(struct pt_regs *regs)
134*4882a593Smuzhiyun {
135*4882a593Smuzhiyun u32 irqnr;
136*4882a593Smuzhiyun
137*4882a593Smuzhiyun irqnr = __raw_readl(icoll_priv.stat);
138*4882a593Smuzhiyun __raw_writel(irqnr, icoll_priv.vector);
139*4882a593Smuzhiyun handle_domain_irq(icoll_domain, irqnr, regs);
140*4882a593Smuzhiyun }
141*4882a593Smuzhiyun
icoll_irq_domain_map(struct irq_domain * d,unsigned int virq,irq_hw_number_t hw)142*4882a593Smuzhiyun static int icoll_irq_domain_map(struct irq_domain *d, unsigned int virq,
143*4882a593Smuzhiyun irq_hw_number_t hw)
144*4882a593Smuzhiyun {
145*4882a593Smuzhiyun struct irq_chip *chip;
146*4882a593Smuzhiyun
147*4882a593Smuzhiyun if (icoll_priv.type == ICOLL)
148*4882a593Smuzhiyun chip = &mxs_icoll_chip;
149*4882a593Smuzhiyun else
150*4882a593Smuzhiyun chip = &asm9260_icoll_chip;
151*4882a593Smuzhiyun
152*4882a593Smuzhiyun irq_set_chip_and_handler(virq, chip, handle_level_irq);
153*4882a593Smuzhiyun
154*4882a593Smuzhiyun return 0;
155*4882a593Smuzhiyun }
156*4882a593Smuzhiyun
157*4882a593Smuzhiyun static const struct irq_domain_ops icoll_irq_domain_ops = {
158*4882a593Smuzhiyun .map = icoll_irq_domain_map,
159*4882a593Smuzhiyun .xlate = irq_domain_xlate_onecell,
160*4882a593Smuzhiyun };
161*4882a593Smuzhiyun
icoll_add_domain(struct device_node * np,int num)162*4882a593Smuzhiyun static void __init icoll_add_domain(struct device_node *np,
163*4882a593Smuzhiyun int num)
164*4882a593Smuzhiyun {
165*4882a593Smuzhiyun icoll_domain = irq_domain_add_linear(np, num,
166*4882a593Smuzhiyun &icoll_irq_domain_ops, NULL);
167*4882a593Smuzhiyun
168*4882a593Smuzhiyun if (!icoll_domain)
169*4882a593Smuzhiyun panic("%pOF: unable to create irq domain", np);
170*4882a593Smuzhiyun }
171*4882a593Smuzhiyun
icoll_init_iobase(struct device_node * np)172*4882a593Smuzhiyun static void __iomem * __init icoll_init_iobase(struct device_node *np)
173*4882a593Smuzhiyun {
174*4882a593Smuzhiyun void __iomem *icoll_base;
175*4882a593Smuzhiyun
176*4882a593Smuzhiyun icoll_base = of_io_request_and_map(np, 0, np->name);
177*4882a593Smuzhiyun if (IS_ERR(icoll_base))
178*4882a593Smuzhiyun panic("%pOF: unable to map resource", np);
179*4882a593Smuzhiyun return icoll_base;
180*4882a593Smuzhiyun }
181*4882a593Smuzhiyun
icoll_of_init(struct device_node * np,struct device_node * interrupt_parent)182*4882a593Smuzhiyun static int __init icoll_of_init(struct device_node *np,
183*4882a593Smuzhiyun struct device_node *interrupt_parent)
184*4882a593Smuzhiyun {
185*4882a593Smuzhiyun void __iomem *icoll_base;
186*4882a593Smuzhiyun
187*4882a593Smuzhiyun icoll_priv.type = ICOLL;
188*4882a593Smuzhiyun
189*4882a593Smuzhiyun icoll_base = icoll_init_iobase(np);
190*4882a593Smuzhiyun icoll_priv.vector = icoll_base + HW_ICOLL_VECTOR;
191*4882a593Smuzhiyun icoll_priv.levelack = icoll_base + HW_ICOLL_LEVELACK;
192*4882a593Smuzhiyun icoll_priv.ctrl = icoll_base + HW_ICOLL_CTRL;
193*4882a593Smuzhiyun icoll_priv.stat = icoll_base + HW_ICOLL_STAT_OFFSET;
194*4882a593Smuzhiyun icoll_priv.intr = icoll_base + HW_ICOLL_INTERRUPT0;
195*4882a593Smuzhiyun icoll_priv.clear = NULL;
196*4882a593Smuzhiyun
197*4882a593Smuzhiyun /*
198*4882a593Smuzhiyun * Interrupt Collector reset, which initializes the priority
199*4882a593Smuzhiyun * for each irq to level 0.
200*4882a593Smuzhiyun */
201*4882a593Smuzhiyun stmp_reset_block(icoll_priv.ctrl);
202*4882a593Smuzhiyun
203*4882a593Smuzhiyun icoll_add_domain(np, ICOLL_NUM_IRQS);
204*4882a593Smuzhiyun
205*4882a593Smuzhiyun return 0;
206*4882a593Smuzhiyun }
207*4882a593Smuzhiyun IRQCHIP_DECLARE(mxs, "fsl,icoll", icoll_of_init);
208*4882a593Smuzhiyun
asm9260_of_init(struct device_node * np,struct device_node * interrupt_parent)209*4882a593Smuzhiyun static int __init asm9260_of_init(struct device_node *np,
210*4882a593Smuzhiyun struct device_node *interrupt_parent)
211*4882a593Smuzhiyun {
212*4882a593Smuzhiyun void __iomem *icoll_base;
213*4882a593Smuzhiyun int i;
214*4882a593Smuzhiyun
215*4882a593Smuzhiyun icoll_priv.type = ASM9260_ICOLL;
216*4882a593Smuzhiyun
217*4882a593Smuzhiyun icoll_base = icoll_init_iobase(np);
218*4882a593Smuzhiyun icoll_priv.vector = icoll_base + ASM9260_HW_ICOLL_VECTOR;
219*4882a593Smuzhiyun icoll_priv.levelack = icoll_base + ASM9260_HW_ICOLL_LEVELACK;
220*4882a593Smuzhiyun icoll_priv.ctrl = icoll_base + ASM9260_HW_ICOLL_CTRL;
221*4882a593Smuzhiyun icoll_priv.stat = icoll_base + ASM9260_HW_ICOLL_STAT_OFFSET;
222*4882a593Smuzhiyun icoll_priv.intr = icoll_base + ASM9260_HW_ICOLL_INTERRUPT0;
223*4882a593Smuzhiyun icoll_priv.clear = icoll_base + ASM9260_HW_ICOLL_CLEAR0;
224*4882a593Smuzhiyun
225*4882a593Smuzhiyun writel_relaxed(ASM9260_BM_CTRL_IRQ_ENABLE,
226*4882a593Smuzhiyun icoll_priv.ctrl);
227*4882a593Smuzhiyun /*
228*4882a593Smuzhiyun * ASM9260 don't provide reset bit. So, we need to set level 0
229*4882a593Smuzhiyun * manually.
230*4882a593Smuzhiyun */
231*4882a593Smuzhiyun for (i = 0; i < 16 * 0x10; i += 0x10)
232*4882a593Smuzhiyun writel(0, icoll_priv.intr + i);
233*4882a593Smuzhiyun
234*4882a593Smuzhiyun icoll_add_domain(np, ASM9260_NUM_IRQS);
235*4882a593Smuzhiyun set_handle_irq(icoll_handle_irq);
236*4882a593Smuzhiyun
237*4882a593Smuzhiyun return 0;
238*4882a593Smuzhiyun }
239*4882a593Smuzhiyun IRQCHIP_DECLARE(asm9260, "alphascale,asm9260-icoll", asm9260_of_init);
240