xref: /OK3568_Linux_fs/kernel/drivers/irqchip/irq-loongson-pch-pic.c (revision 4882a59341e53eb6f0b4789bf948001014eff981)
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