1*4882a593Smuzhiyun // SPDX-License-Identifier: GPL-2.0
2*4882a593Smuzhiyun /*
3*4882a593Smuzhiyun * Copyright (C) 2020, Jiaxun Yang <jiaxun.yang@flygoat.com>
4*4882a593Smuzhiyun * Loongson PCH PIC support
5*4882a593Smuzhiyun */
6*4882a593Smuzhiyun
7*4882a593Smuzhiyun #define pr_fmt(fmt) "pch-pic: " fmt
8*4882a593Smuzhiyun
9*4882a593Smuzhiyun #include <linux/interrupt.h>
10*4882a593Smuzhiyun #include <linux/irq.h>
11*4882a593Smuzhiyun #include <linux/irqchip.h>
12*4882a593Smuzhiyun #include <linux/irqdomain.h>
13*4882a593Smuzhiyun #include <linux/kernel.h>
14*4882a593Smuzhiyun #include <linux/platform_device.h>
15*4882a593Smuzhiyun #include <linux/of_address.h>
16*4882a593Smuzhiyun #include <linux/of_irq.h>
17*4882a593Smuzhiyun #include <linux/of_platform.h>
18*4882a593Smuzhiyun
19*4882a593Smuzhiyun /* Registers */
20*4882a593Smuzhiyun #define PCH_PIC_MASK 0x20
21*4882a593Smuzhiyun #define PCH_PIC_HTMSI_EN 0x40
22*4882a593Smuzhiyun #define PCH_PIC_EDGE 0x60
23*4882a593Smuzhiyun #define PCH_PIC_CLR 0x80
24*4882a593Smuzhiyun #define PCH_PIC_AUTO0 0xc0
25*4882a593Smuzhiyun #define PCH_PIC_AUTO1 0xe0
26*4882a593Smuzhiyun #define PCH_INT_ROUTE(irq) (0x100 + irq)
27*4882a593Smuzhiyun #define PCH_INT_HTVEC(irq) (0x200 + irq)
28*4882a593Smuzhiyun #define PCH_PIC_POL 0x3e0
29*4882a593Smuzhiyun
30*4882a593Smuzhiyun #define PIC_COUNT_PER_REG 32
31*4882a593Smuzhiyun #define PIC_REG_COUNT 2
32*4882a593Smuzhiyun #define PIC_COUNT (PIC_COUNT_PER_REG * PIC_REG_COUNT)
33*4882a593Smuzhiyun #define PIC_REG_IDX(irq_id) ((irq_id) / PIC_COUNT_PER_REG)
34*4882a593Smuzhiyun #define PIC_REG_BIT(irq_id) ((irq_id) % PIC_COUNT_PER_REG)
35*4882a593Smuzhiyun
36*4882a593Smuzhiyun struct pch_pic {
37*4882a593Smuzhiyun void __iomem *base;
38*4882a593Smuzhiyun struct irq_domain *pic_domain;
39*4882a593Smuzhiyun u32 ht_vec_base;
40*4882a593Smuzhiyun raw_spinlock_t pic_lock;
41*4882a593Smuzhiyun };
42*4882a593Smuzhiyun
pch_pic_bitset(struct pch_pic * priv,int offset,int bit)43*4882a593Smuzhiyun static void pch_pic_bitset(struct pch_pic *priv, int offset, int bit)
44*4882a593Smuzhiyun {
45*4882a593Smuzhiyun u32 reg;
46*4882a593Smuzhiyun void __iomem *addr = priv->base + offset + PIC_REG_IDX(bit) * 4;
47*4882a593Smuzhiyun
48*4882a593Smuzhiyun raw_spin_lock(&priv->pic_lock);
49*4882a593Smuzhiyun reg = readl(addr);
50*4882a593Smuzhiyun reg |= BIT(PIC_REG_BIT(bit));
51*4882a593Smuzhiyun writel(reg, addr);
52*4882a593Smuzhiyun raw_spin_unlock(&priv->pic_lock);
53*4882a593Smuzhiyun }
54*4882a593Smuzhiyun
pch_pic_bitclr(struct pch_pic * priv,int offset,int bit)55*4882a593Smuzhiyun static void pch_pic_bitclr(struct pch_pic *priv, int offset, int bit)
56*4882a593Smuzhiyun {
57*4882a593Smuzhiyun u32 reg;
58*4882a593Smuzhiyun void __iomem *addr = priv->base + offset + PIC_REG_IDX(bit) * 4;
59*4882a593Smuzhiyun
60*4882a593Smuzhiyun raw_spin_lock(&priv->pic_lock);
61*4882a593Smuzhiyun reg = readl(addr);
62*4882a593Smuzhiyun reg &= ~BIT(PIC_REG_BIT(bit));
63*4882a593Smuzhiyun writel(reg, addr);
64*4882a593Smuzhiyun raw_spin_unlock(&priv->pic_lock);
65*4882a593Smuzhiyun }
66*4882a593Smuzhiyun
pch_pic_mask_irq(struct irq_data * d)67*4882a593Smuzhiyun static void pch_pic_mask_irq(struct irq_data *d)
68*4882a593Smuzhiyun {
69*4882a593Smuzhiyun struct pch_pic *priv = irq_data_get_irq_chip_data(d);
70*4882a593Smuzhiyun
71*4882a593Smuzhiyun pch_pic_bitset(priv, PCH_PIC_MASK, d->hwirq);
72*4882a593Smuzhiyun irq_chip_mask_parent(d);
73*4882a593Smuzhiyun }
74*4882a593Smuzhiyun
pch_pic_unmask_irq(struct irq_data * d)75*4882a593Smuzhiyun static void pch_pic_unmask_irq(struct irq_data *d)
76*4882a593Smuzhiyun {
77*4882a593Smuzhiyun struct pch_pic *priv = irq_data_get_irq_chip_data(d);
78*4882a593Smuzhiyun
79*4882a593Smuzhiyun writel(BIT(PIC_REG_BIT(d->hwirq)),
80*4882a593Smuzhiyun priv->base + PCH_PIC_CLR + PIC_REG_IDX(d->hwirq) * 4);
81*4882a593Smuzhiyun
82*4882a593Smuzhiyun irq_chip_unmask_parent(d);
83*4882a593Smuzhiyun pch_pic_bitclr(priv, PCH_PIC_MASK, d->hwirq);
84*4882a593Smuzhiyun }
85*4882a593Smuzhiyun
pch_pic_set_type(struct irq_data * d,unsigned int type)86*4882a593Smuzhiyun static int pch_pic_set_type(struct irq_data *d, unsigned int type)
87*4882a593Smuzhiyun {
88*4882a593Smuzhiyun struct pch_pic *priv = irq_data_get_irq_chip_data(d);
89*4882a593Smuzhiyun int ret = 0;
90*4882a593Smuzhiyun
91*4882a593Smuzhiyun switch (type) {
92*4882a593Smuzhiyun case IRQ_TYPE_EDGE_RISING:
93*4882a593Smuzhiyun pch_pic_bitset(priv, PCH_PIC_EDGE, d->hwirq);
94*4882a593Smuzhiyun pch_pic_bitclr(priv, PCH_PIC_POL, d->hwirq);
95*4882a593Smuzhiyun irq_set_handler_locked(d, handle_edge_irq);
96*4882a593Smuzhiyun break;
97*4882a593Smuzhiyun case IRQ_TYPE_EDGE_FALLING:
98*4882a593Smuzhiyun pch_pic_bitset(priv, PCH_PIC_EDGE, d->hwirq);
99*4882a593Smuzhiyun pch_pic_bitset(priv, PCH_PIC_POL, d->hwirq);
100*4882a593Smuzhiyun irq_set_handler_locked(d, handle_edge_irq);
101*4882a593Smuzhiyun break;
102*4882a593Smuzhiyun case IRQ_TYPE_LEVEL_HIGH:
103*4882a593Smuzhiyun pch_pic_bitclr(priv, PCH_PIC_EDGE, d->hwirq);
104*4882a593Smuzhiyun pch_pic_bitclr(priv, PCH_PIC_POL, d->hwirq);
105*4882a593Smuzhiyun irq_set_handler_locked(d, handle_level_irq);
106*4882a593Smuzhiyun break;
107*4882a593Smuzhiyun case IRQ_TYPE_LEVEL_LOW:
108*4882a593Smuzhiyun pch_pic_bitclr(priv, PCH_PIC_EDGE, d->hwirq);
109*4882a593Smuzhiyun pch_pic_bitset(priv, PCH_PIC_POL, d->hwirq);
110*4882a593Smuzhiyun irq_set_handler_locked(d, handle_level_irq);
111*4882a593Smuzhiyun break;
112*4882a593Smuzhiyun default:
113*4882a593Smuzhiyun ret = -EINVAL;
114*4882a593Smuzhiyun break;
115*4882a593Smuzhiyun }
116*4882a593Smuzhiyun
117*4882a593Smuzhiyun return ret;
118*4882a593Smuzhiyun }
119*4882a593Smuzhiyun
pch_pic_ack_irq(struct irq_data * d)120*4882a593Smuzhiyun static void pch_pic_ack_irq(struct irq_data *d)
121*4882a593Smuzhiyun {
122*4882a593Smuzhiyun unsigned int reg;
123*4882a593Smuzhiyun struct pch_pic *priv = irq_data_get_irq_chip_data(d);
124*4882a593Smuzhiyun
125*4882a593Smuzhiyun reg = readl(priv->base + PCH_PIC_EDGE + PIC_REG_IDX(d->hwirq) * 4);
126*4882a593Smuzhiyun if (reg & BIT(PIC_REG_BIT(d->hwirq))) {
127*4882a593Smuzhiyun writel(BIT(PIC_REG_BIT(d->hwirq)),
128*4882a593Smuzhiyun priv->base + PCH_PIC_CLR + PIC_REG_IDX(d->hwirq) * 4);
129*4882a593Smuzhiyun }
130*4882a593Smuzhiyun irq_chip_ack_parent(d);
131*4882a593Smuzhiyun }
132*4882a593Smuzhiyun
133*4882a593Smuzhiyun static struct irq_chip pch_pic_irq_chip = {
134*4882a593Smuzhiyun .name = "PCH PIC",
135*4882a593Smuzhiyun .irq_mask = pch_pic_mask_irq,
136*4882a593Smuzhiyun .irq_unmask = pch_pic_unmask_irq,
137*4882a593Smuzhiyun .irq_ack = pch_pic_ack_irq,
138*4882a593Smuzhiyun .irq_set_affinity = irq_chip_set_affinity_parent,
139*4882a593Smuzhiyun .irq_set_type = pch_pic_set_type,
140*4882a593Smuzhiyun };
141*4882a593Smuzhiyun
pch_pic_alloc(struct irq_domain * domain,unsigned int virq,unsigned int nr_irqs,void * arg)142*4882a593Smuzhiyun static int pch_pic_alloc(struct irq_domain *domain, unsigned int virq,
143*4882a593Smuzhiyun unsigned int nr_irqs, void *arg)
144*4882a593Smuzhiyun {
145*4882a593Smuzhiyun int err;
146*4882a593Smuzhiyun unsigned int type;
147*4882a593Smuzhiyun unsigned long hwirq;
148*4882a593Smuzhiyun struct irq_fwspec *fwspec = arg;
149*4882a593Smuzhiyun struct irq_fwspec parent_fwspec;
150*4882a593Smuzhiyun struct pch_pic *priv = domain->host_data;
151*4882a593Smuzhiyun
152*4882a593Smuzhiyun err = irq_domain_translate_twocell(domain, fwspec, &hwirq, &type);
153*4882a593Smuzhiyun if (err)
154*4882a593Smuzhiyun return err;
155*4882a593Smuzhiyun
156*4882a593Smuzhiyun parent_fwspec.fwnode = domain->parent->fwnode;
157*4882a593Smuzhiyun parent_fwspec.param_count = 1;
158*4882a593Smuzhiyun parent_fwspec.param[0] = hwirq + priv->ht_vec_base;
159*4882a593Smuzhiyun
160*4882a593Smuzhiyun err = irq_domain_alloc_irqs_parent(domain, virq, 1, &parent_fwspec);
161*4882a593Smuzhiyun if (err)
162*4882a593Smuzhiyun return err;
163*4882a593Smuzhiyun
164*4882a593Smuzhiyun irq_domain_set_info(domain, virq, hwirq,
165*4882a593Smuzhiyun &pch_pic_irq_chip, priv,
166*4882a593Smuzhiyun handle_level_irq, NULL, NULL);
167*4882a593Smuzhiyun irq_set_probe(virq);
168*4882a593Smuzhiyun
169*4882a593Smuzhiyun return 0;
170*4882a593Smuzhiyun }
171*4882a593Smuzhiyun
172*4882a593Smuzhiyun static const struct irq_domain_ops pch_pic_domain_ops = {
173*4882a593Smuzhiyun .translate = irq_domain_translate_twocell,
174*4882a593Smuzhiyun .alloc = pch_pic_alloc,
175*4882a593Smuzhiyun .free = irq_domain_free_irqs_parent,
176*4882a593Smuzhiyun };
177*4882a593Smuzhiyun
pch_pic_reset(struct pch_pic * priv)178*4882a593Smuzhiyun static void pch_pic_reset(struct pch_pic *priv)
179*4882a593Smuzhiyun {
180*4882a593Smuzhiyun int i;
181*4882a593Smuzhiyun
182*4882a593Smuzhiyun for (i = 0; i < PIC_COUNT; i++) {
183*4882a593Smuzhiyun /* Write vectore ID */
184*4882a593Smuzhiyun writeb(priv->ht_vec_base + i, priv->base + PCH_INT_HTVEC(i));
185*4882a593Smuzhiyun /* Hardcode route to HT0 Lo */
186*4882a593Smuzhiyun writeb(1, priv->base + PCH_INT_ROUTE(i));
187*4882a593Smuzhiyun }
188*4882a593Smuzhiyun
189*4882a593Smuzhiyun for (i = 0; i < PIC_REG_COUNT; i++) {
190*4882a593Smuzhiyun /* Clear IRQ cause registers, mask all interrupts */
191*4882a593Smuzhiyun writel_relaxed(0xFFFFFFFF, priv->base + PCH_PIC_MASK + 4 * i);
192*4882a593Smuzhiyun writel_relaxed(0xFFFFFFFF, priv->base + PCH_PIC_CLR + 4 * i);
193*4882a593Smuzhiyun /* Clear auto bounce, we don't need that */
194*4882a593Smuzhiyun writel_relaxed(0, priv->base + PCH_PIC_AUTO0 + 4 * i);
195*4882a593Smuzhiyun writel_relaxed(0, priv->base + PCH_PIC_AUTO1 + 4 * i);
196*4882a593Smuzhiyun /* Enable HTMSI transformer */
197*4882a593Smuzhiyun writel_relaxed(0xFFFFFFFF, priv->base + PCH_PIC_HTMSI_EN + 4 * i);
198*4882a593Smuzhiyun }
199*4882a593Smuzhiyun }
200*4882a593Smuzhiyun
pch_pic_of_init(struct device_node * node,struct device_node * parent)201*4882a593Smuzhiyun static int pch_pic_of_init(struct device_node *node,
202*4882a593Smuzhiyun struct device_node *parent)
203*4882a593Smuzhiyun {
204*4882a593Smuzhiyun struct pch_pic *priv;
205*4882a593Smuzhiyun struct irq_domain *parent_domain;
206*4882a593Smuzhiyun int err;
207*4882a593Smuzhiyun
208*4882a593Smuzhiyun priv = kzalloc(sizeof(*priv), GFP_KERNEL);
209*4882a593Smuzhiyun if (!priv)
210*4882a593Smuzhiyun return -ENOMEM;
211*4882a593Smuzhiyun
212*4882a593Smuzhiyun raw_spin_lock_init(&priv->pic_lock);
213*4882a593Smuzhiyun priv->base = of_iomap(node, 0);
214*4882a593Smuzhiyun if (!priv->base) {
215*4882a593Smuzhiyun err = -ENOMEM;
216*4882a593Smuzhiyun goto free_priv;
217*4882a593Smuzhiyun }
218*4882a593Smuzhiyun
219*4882a593Smuzhiyun parent_domain = irq_find_host(parent);
220*4882a593Smuzhiyun if (!parent_domain) {
221*4882a593Smuzhiyun pr_err("Failed to find the parent domain\n");
222*4882a593Smuzhiyun err = -ENXIO;
223*4882a593Smuzhiyun goto iounmap_base;
224*4882a593Smuzhiyun }
225*4882a593Smuzhiyun
226*4882a593Smuzhiyun if (of_property_read_u32(node, "loongson,pic-base-vec",
227*4882a593Smuzhiyun &priv->ht_vec_base)) {
228*4882a593Smuzhiyun pr_err("Failed to determine pic-base-vec\n");
229*4882a593Smuzhiyun err = -EINVAL;
230*4882a593Smuzhiyun goto iounmap_base;
231*4882a593Smuzhiyun }
232*4882a593Smuzhiyun
233*4882a593Smuzhiyun priv->pic_domain = irq_domain_create_hierarchy(parent_domain, 0,
234*4882a593Smuzhiyun PIC_COUNT,
235*4882a593Smuzhiyun of_node_to_fwnode(node),
236*4882a593Smuzhiyun &pch_pic_domain_ops,
237*4882a593Smuzhiyun priv);
238*4882a593Smuzhiyun if (!priv->pic_domain) {
239*4882a593Smuzhiyun pr_err("Failed to create IRQ domain\n");
240*4882a593Smuzhiyun err = -ENOMEM;
241*4882a593Smuzhiyun goto iounmap_base;
242*4882a593Smuzhiyun }
243*4882a593Smuzhiyun
244*4882a593Smuzhiyun pch_pic_reset(priv);
245*4882a593Smuzhiyun
246*4882a593Smuzhiyun return 0;
247*4882a593Smuzhiyun
248*4882a593Smuzhiyun iounmap_base:
249*4882a593Smuzhiyun iounmap(priv->base);
250*4882a593Smuzhiyun free_priv:
251*4882a593Smuzhiyun kfree(priv);
252*4882a593Smuzhiyun
253*4882a593Smuzhiyun return err;
254*4882a593Smuzhiyun }
255*4882a593Smuzhiyun
256*4882a593Smuzhiyun IRQCHIP_DECLARE(pch_pic, "loongson,pch-pic-1.0", pch_pic_of_init);
257