xref: /openbmc/linux/drivers/irqchip/irq-loongson-pch-pic.c (revision c900529f3d9161bfde5cca0754f83b4d3c3e0220)
1ef8c01ebSJiaxun Yang // SPDX-License-Identifier: GPL-2.0
2ef8c01ebSJiaxun Yang /*
3ef8c01ebSJiaxun Yang  *  Copyright (C) 2020, Jiaxun Yang <jiaxun.yang@flygoat.com>
4ef8c01ebSJiaxun Yang  *  Loongson PCH PIC support
5ef8c01ebSJiaxun Yang  */
6ef8c01ebSJiaxun Yang 
7ef8c01ebSJiaxun Yang #define pr_fmt(fmt) "pch-pic: " fmt
8ef8c01ebSJiaxun Yang 
9ef8c01ebSJiaxun Yang #include <linux/interrupt.h>
10ef8c01ebSJiaxun Yang #include <linux/irq.h>
11ef8c01ebSJiaxun Yang #include <linux/irqchip.h>
12ef8c01ebSJiaxun Yang #include <linux/irqdomain.h>
13ef8c01ebSJiaxun Yang #include <linux/kernel.h>
14ef8c01ebSJiaxun Yang #include <linux/platform_device.h>
15*ee076750SRob Herring #include <linux/of.h>
16ef8c01ebSJiaxun Yang #include <linux/of_address.h>
17ef8c01ebSJiaxun Yang #include <linux/of_irq.h>
181ed008a2SHuacai Chen #include <linux/syscore_ops.h>
19ef8c01ebSJiaxun Yang 
20ef8c01ebSJiaxun Yang /* Registers */
21ef8c01ebSJiaxun Yang #define PCH_PIC_MASK		0x20
22ef8c01ebSJiaxun Yang #define PCH_PIC_HTMSI_EN	0x40
23ef8c01ebSJiaxun Yang #define PCH_PIC_EDGE		0x60
24ef8c01ebSJiaxun Yang #define PCH_PIC_CLR		0x80
25ef8c01ebSJiaxun Yang #define PCH_PIC_AUTO0		0xc0
26ef8c01ebSJiaxun Yang #define PCH_PIC_AUTO1		0xe0
27ef8c01ebSJiaxun Yang #define PCH_INT_ROUTE(irq)	(0x100 + irq)
28ef8c01ebSJiaxun Yang #define PCH_INT_HTVEC(irq)	(0x200 + irq)
29ef8c01ebSJiaxun Yang #define PCH_PIC_POL		0x3e0
30ef8c01ebSJiaxun Yang 
31ef8c01ebSJiaxun Yang #define PIC_COUNT_PER_REG	32
32ef8c01ebSJiaxun Yang #define PIC_REG_COUNT		2
33ef8c01ebSJiaxun Yang #define PIC_COUNT		(PIC_COUNT_PER_REG * PIC_REG_COUNT)
34ef8c01ebSJiaxun Yang #define PIC_REG_IDX(irq_id)	((irq_id) / PIC_COUNT_PER_REG)
35ef8c01ebSJiaxun Yang #define PIC_REG_BIT(irq_id)	((irq_id) % PIC_COUNT_PER_REG)
36ef8c01ebSJiaxun Yang 
37bcdd75c5SHuacai Chen static int nr_pics;
38bcdd75c5SHuacai Chen 
39ef8c01ebSJiaxun Yang struct pch_pic {
40ef8c01ebSJiaxun Yang 	void __iomem		*base;
41ef8c01ebSJiaxun Yang 	struct irq_domain	*pic_domain;
42ef8c01ebSJiaxun Yang 	u32			ht_vec_base;
43ef8c01ebSJiaxun Yang 	raw_spinlock_t		pic_lock;
44bcdd75c5SHuacai Chen 	u32			vec_count;
45bcdd75c5SHuacai Chen 	u32			gsi_base;
461ed008a2SHuacai Chen 	u32			saved_vec_en[PIC_REG_COUNT];
471ed008a2SHuacai Chen 	u32			saved_vec_pol[PIC_REG_COUNT];
481ed008a2SHuacai Chen 	u32			saved_vec_edge[PIC_REG_COUNT];
49ef8c01ebSJiaxun Yang };
50ef8c01ebSJiaxun Yang 
51bcdd75c5SHuacai Chen static struct pch_pic *pch_pic_priv[MAX_IO_PICS];
52bcdd75c5SHuacai Chen 
53bcdd75c5SHuacai Chen struct fwnode_handle *pch_pic_handle[MAX_IO_PICS];
54bcdd75c5SHuacai Chen 
pch_pic_bitset(struct pch_pic * priv,int offset,int bit)55ef8c01ebSJiaxun Yang static void pch_pic_bitset(struct pch_pic *priv, int offset, int bit)
56ef8c01ebSJiaxun Yang {
57ef8c01ebSJiaxun Yang 	u32 reg;
58ef8c01ebSJiaxun Yang 	void __iomem *addr = priv->base + offset + PIC_REG_IDX(bit) * 4;
59ef8c01ebSJiaxun Yang 
60ef8c01ebSJiaxun Yang 	raw_spin_lock(&priv->pic_lock);
61ef8c01ebSJiaxun Yang 	reg = readl(addr);
62ef8c01ebSJiaxun Yang 	reg |= BIT(PIC_REG_BIT(bit));
63ef8c01ebSJiaxun Yang 	writel(reg, addr);
64ef8c01ebSJiaxun Yang 	raw_spin_unlock(&priv->pic_lock);
65ef8c01ebSJiaxun Yang }
66ef8c01ebSJiaxun Yang 
pch_pic_bitclr(struct pch_pic * priv,int offset,int bit)67ef8c01ebSJiaxun Yang static void pch_pic_bitclr(struct pch_pic *priv, int offset, int bit)
68ef8c01ebSJiaxun Yang {
69ef8c01ebSJiaxun Yang 	u32 reg;
70ef8c01ebSJiaxun Yang 	void __iomem *addr = priv->base + offset + PIC_REG_IDX(bit) * 4;
71ef8c01ebSJiaxun Yang 
72ef8c01ebSJiaxun Yang 	raw_spin_lock(&priv->pic_lock);
73ef8c01ebSJiaxun Yang 	reg = readl(addr);
74ef8c01ebSJiaxun Yang 	reg &= ~BIT(PIC_REG_BIT(bit));
75ef8c01ebSJiaxun Yang 	writel(reg, addr);
76ef8c01ebSJiaxun Yang 	raw_spin_unlock(&priv->pic_lock);
77ef8c01ebSJiaxun Yang }
78ef8c01ebSJiaxun Yang 
pch_pic_mask_irq(struct irq_data * d)79ef8c01ebSJiaxun Yang static void pch_pic_mask_irq(struct irq_data *d)
80ef8c01ebSJiaxun Yang {
81ef8c01ebSJiaxun Yang 	struct pch_pic *priv = irq_data_get_irq_chip_data(d);
82ef8c01ebSJiaxun Yang 
83ef8c01ebSJiaxun Yang 	pch_pic_bitset(priv, PCH_PIC_MASK, d->hwirq);
84ef8c01ebSJiaxun Yang 	irq_chip_mask_parent(d);
85ef8c01ebSJiaxun Yang }
86ef8c01ebSJiaxun Yang 
pch_pic_unmask_irq(struct irq_data * d)87ef8c01ebSJiaxun Yang static void pch_pic_unmask_irq(struct irq_data *d)
88ef8c01ebSJiaxun Yang {
89ef8c01ebSJiaxun Yang 	struct pch_pic *priv = irq_data_get_irq_chip_data(d);
90ef8c01ebSJiaxun Yang 
91ac62460cSHuacai Chen 	writel(BIT(PIC_REG_BIT(d->hwirq)),
92ac62460cSHuacai Chen 			priv->base + PCH_PIC_CLR + PIC_REG_IDX(d->hwirq) * 4);
93ac62460cSHuacai Chen 
94ef8c01ebSJiaxun Yang 	irq_chip_unmask_parent(d);
95ef8c01ebSJiaxun Yang 	pch_pic_bitclr(priv, PCH_PIC_MASK, d->hwirq);
96ef8c01ebSJiaxun Yang }
97ef8c01ebSJiaxun Yang 
pch_pic_set_type(struct irq_data * d,unsigned int type)98ef8c01ebSJiaxun Yang static int pch_pic_set_type(struct irq_data *d, unsigned int type)
99ef8c01ebSJiaxun Yang {
100ef8c01ebSJiaxun Yang 	struct pch_pic *priv = irq_data_get_irq_chip_data(d);
101ef8c01ebSJiaxun Yang 	int ret = 0;
102ef8c01ebSJiaxun Yang 
103ef8c01ebSJiaxun Yang 	switch (type) {
104ef8c01ebSJiaxun Yang 	case IRQ_TYPE_EDGE_RISING:
105ef8c01ebSJiaxun Yang 		pch_pic_bitset(priv, PCH_PIC_EDGE, d->hwirq);
106ef8c01ebSJiaxun Yang 		pch_pic_bitclr(priv, PCH_PIC_POL, d->hwirq);
107e5dec38aSHuacai Chen 		irq_set_handler_locked(d, handle_edge_irq);
108ef8c01ebSJiaxun Yang 		break;
109ef8c01ebSJiaxun Yang 	case IRQ_TYPE_EDGE_FALLING:
110ef8c01ebSJiaxun Yang 		pch_pic_bitset(priv, PCH_PIC_EDGE, d->hwirq);
111ef8c01ebSJiaxun Yang 		pch_pic_bitset(priv, PCH_PIC_POL, d->hwirq);
112e5dec38aSHuacai Chen 		irq_set_handler_locked(d, handle_edge_irq);
113ef8c01ebSJiaxun Yang 		break;
114ef8c01ebSJiaxun Yang 	case IRQ_TYPE_LEVEL_HIGH:
115ef8c01ebSJiaxun Yang 		pch_pic_bitclr(priv, PCH_PIC_EDGE, d->hwirq);
116ef8c01ebSJiaxun Yang 		pch_pic_bitclr(priv, PCH_PIC_POL, d->hwirq);
117e5dec38aSHuacai Chen 		irq_set_handler_locked(d, handle_level_irq);
118ef8c01ebSJiaxun Yang 		break;
119ef8c01ebSJiaxun Yang 	case IRQ_TYPE_LEVEL_LOW:
120ef8c01ebSJiaxun Yang 		pch_pic_bitclr(priv, PCH_PIC_EDGE, d->hwirq);
121ef8c01ebSJiaxun Yang 		pch_pic_bitset(priv, PCH_PIC_POL, d->hwirq);
122e5dec38aSHuacai Chen 		irq_set_handler_locked(d, handle_level_irq);
123ef8c01ebSJiaxun Yang 		break;
124ef8c01ebSJiaxun Yang 	default:
125ef8c01ebSJiaxun Yang 		ret = -EINVAL;
126ef8c01ebSJiaxun Yang 		break;
127ef8c01ebSJiaxun Yang 	}
128ef8c01ebSJiaxun Yang 
129ef8c01ebSJiaxun Yang 	return ret;
130ef8c01ebSJiaxun Yang }
131ef8c01ebSJiaxun Yang 
pch_pic_ack_irq(struct irq_data * d)132e5dec38aSHuacai Chen static void pch_pic_ack_irq(struct irq_data *d)
133e5dec38aSHuacai Chen {
134e5dec38aSHuacai Chen 	unsigned int reg;
135e5dec38aSHuacai Chen 	struct pch_pic *priv = irq_data_get_irq_chip_data(d);
136e5dec38aSHuacai Chen 
137e5dec38aSHuacai Chen 	reg = readl(priv->base + PCH_PIC_EDGE + PIC_REG_IDX(d->hwirq) * 4);
138e5dec38aSHuacai Chen 	if (reg & BIT(PIC_REG_BIT(d->hwirq))) {
139e5dec38aSHuacai Chen 		writel(BIT(PIC_REG_BIT(d->hwirq)),
140e5dec38aSHuacai Chen 			priv->base + PCH_PIC_CLR + PIC_REG_IDX(d->hwirq) * 4);
141e5dec38aSHuacai Chen 	}
142e5dec38aSHuacai Chen 	irq_chip_ack_parent(d);
143e5dec38aSHuacai Chen }
144e5dec38aSHuacai Chen 
145ef8c01ebSJiaxun Yang static struct irq_chip pch_pic_irq_chip = {
146ef8c01ebSJiaxun Yang 	.name			= "PCH PIC",
147ef8c01ebSJiaxun Yang 	.irq_mask		= pch_pic_mask_irq,
148ef8c01ebSJiaxun Yang 	.irq_unmask		= pch_pic_unmask_irq,
149e5dec38aSHuacai Chen 	.irq_ack		= pch_pic_ack_irq,
150ef8c01ebSJiaxun Yang 	.irq_set_affinity	= irq_chip_set_affinity_parent,
151ef8c01ebSJiaxun Yang 	.irq_set_type		= pch_pic_set_type,
1521ed008a2SHuacai Chen 	.flags			= IRQCHIP_SKIP_SET_WAKE,
153ef8c01ebSJiaxun Yang };
154ef8c01ebSJiaxun Yang 
pch_pic_domain_translate(struct irq_domain * d,struct irq_fwspec * fwspec,unsigned long * hwirq,unsigned int * type)155bcdd75c5SHuacai Chen static int pch_pic_domain_translate(struct irq_domain *d,
156bcdd75c5SHuacai Chen 					struct irq_fwspec *fwspec,
157bcdd75c5SHuacai Chen 					unsigned long *hwirq,
158bcdd75c5SHuacai Chen 					unsigned int *type)
159bcdd75c5SHuacai Chen {
160bcdd75c5SHuacai Chen 	struct pch_pic *priv = d->host_data;
161bcdd75c5SHuacai Chen 	struct device_node *of_node = to_of_node(fwspec->fwnode);
162bcdd75c5SHuacai Chen 
163bcdd75c5SHuacai Chen 	if (of_node) {
164c7c00138SJianmin Lv 		if (fwspec->param_count < 2)
165c7c00138SJianmin Lv 			return -EINVAL;
166c7c00138SJianmin Lv 
167783422e7SLiu Peibao 		*hwirq = fwspec->param[0];
168bcdd75c5SHuacai Chen 		*type = fwspec->param[1] & IRQ_TYPE_SENSE_MASK;
169bcdd75c5SHuacai Chen 	} else {
17025f3514aSJianmin Lv 		if (fwspec->param_count < 1)
17125f3514aSJianmin Lv 			return -EINVAL;
17225f3514aSJianmin Lv 
173bcdd75c5SHuacai Chen 		*hwirq = fwspec->param[0] - priv->gsi_base;
17425f3514aSJianmin Lv 		if (fwspec->param_count > 1)
17525f3514aSJianmin Lv 			*type = fwspec->param[1] & IRQ_TYPE_SENSE_MASK;
17625f3514aSJianmin Lv 		else
177bcdd75c5SHuacai Chen 			*type = IRQ_TYPE_NONE;
178bcdd75c5SHuacai Chen 	}
179bcdd75c5SHuacai Chen 
180bcdd75c5SHuacai Chen 	return 0;
181bcdd75c5SHuacai Chen }
182bcdd75c5SHuacai Chen 
pch_pic_alloc(struct irq_domain * domain,unsigned int virq,unsigned int nr_irqs,void * arg)183ef8c01ebSJiaxun Yang static int pch_pic_alloc(struct irq_domain *domain, unsigned int virq,
184ef8c01ebSJiaxun Yang 			      unsigned int nr_irqs, void *arg)
185ef8c01ebSJiaxun Yang {
186ef8c01ebSJiaxun Yang 	int err;
187ef8c01ebSJiaxun Yang 	unsigned int type;
188ef8c01ebSJiaxun Yang 	unsigned long hwirq;
18966a535c4STiezhu Yang 	struct irq_fwspec *fwspec = arg;
19066a535c4STiezhu Yang 	struct irq_fwspec parent_fwspec;
191ef8c01ebSJiaxun Yang 	struct pch_pic *priv = domain->host_data;
192ef8c01ebSJiaxun Yang 
193bcdd75c5SHuacai Chen 	err = pch_pic_domain_translate(domain, fwspec, &hwirq, &type);
19466a535c4STiezhu Yang 	if (err)
19566a535c4STiezhu Yang 		return err;
196ef8c01ebSJiaxun Yang 
19766a535c4STiezhu Yang 	parent_fwspec.fwnode = domain->parent->fwnode;
19866a535c4STiezhu Yang 	parent_fwspec.param_count = 1;
199783422e7SLiu Peibao 	parent_fwspec.param[0] = hwirq + priv->ht_vec_base;
200ef8c01ebSJiaxun Yang 
20166a535c4STiezhu Yang 	err = irq_domain_alloc_irqs_parent(domain, virq, 1, &parent_fwspec);
202ef8c01ebSJiaxun Yang 	if (err)
203ef8c01ebSJiaxun Yang 		return err;
204ef8c01ebSJiaxun Yang 
205ef8c01ebSJiaxun Yang 	irq_domain_set_info(domain, virq, hwirq,
206ef8c01ebSJiaxun Yang 			    &pch_pic_irq_chip, priv,
207ac62460cSHuacai Chen 			    handle_level_irq, NULL, NULL);
208ef8c01ebSJiaxun Yang 	irq_set_probe(virq);
209ef8c01ebSJiaxun Yang 
210ef8c01ebSJiaxun Yang 	return 0;
211ef8c01ebSJiaxun Yang }
212ef8c01ebSJiaxun Yang 
213ef8c01ebSJiaxun Yang static const struct irq_domain_ops pch_pic_domain_ops = {
214bcdd75c5SHuacai Chen 	.translate	= pch_pic_domain_translate,
215ef8c01ebSJiaxun Yang 	.alloc		= pch_pic_alloc,
216ef8c01ebSJiaxun Yang 	.free		= irq_domain_free_irqs_parent,
217ef8c01ebSJiaxun Yang };
218ef8c01ebSJiaxun Yang 
pch_pic_reset(struct pch_pic * priv)219ef8c01ebSJiaxun Yang static void pch_pic_reset(struct pch_pic *priv)
220ef8c01ebSJiaxun Yang {
221ef8c01ebSJiaxun Yang 	int i;
222ef8c01ebSJiaxun Yang 
223ef8c01ebSJiaxun Yang 	for (i = 0; i < PIC_COUNT; i++) {
224bcdd75c5SHuacai Chen 		/* Write vector ID */
225ef8c01ebSJiaxun Yang 		writeb(priv->ht_vec_base + i, priv->base + PCH_INT_HTVEC(i));
226ef8c01ebSJiaxun Yang 		/* Hardcode route to HT0 Lo */
227ef8c01ebSJiaxun Yang 		writeb(1, priv->base + PCH_INT_ROUTE(i));
228ef8c01ebSJiaxun Yang 	}
229ef8c01ebSJiaxun Yang 
230ef8c01ebSJiaxun Yang 	for (i = 0; i < PIC_REG_COUNT; i++) {
231ef8c01ebSJiaxun Yang 		/* Clear IRQ cause registers, mask all interrupts */
232ef8c01ebSJiaxun Yang 		writel_relaxed(0xFFFFFFFF, priv->base + PCH_PIC_MASK + 4 * i);
233ef8c01ebSJiaxun Yang 		writel_relaxed(0xFFFFFFFF, priv->base + PCH_PIC_CLR + 4 * i);
234ef8c01ebSJiaxun Yang 		/* Clear auto bounce, we don't need that */
235ef8c01ebSJiaxun Yang 		writel_relaxed(0, priv->base + PCH_PIC_AUTO0 + 4 * i);
236ef8c01ebSJiaxun Yang 		writel_relaxed(0, priv->base + PCH_PIC_AUTO1 + 4 * i);
237ef8c01ebSJiaxun Yang 		/* Enable HTMSI transformer */
238ef8c01ebSJiaxun Yang 		writel_relaxed(0xFFFFFFFF, priv->base + PCH_PIC_HTMSI_EN + 4 * i);
239ef8c01ebSJiaxun Yang 	}
240ef8c01ebSJiaxun Yang }
241ef8c01ebSJiaxun Yang 
pch_pic_suspend(void)2421ed008a2SHuacai Chen static int pch_pic_suspend(void)
2431ed008a2SHuacai Chen {
2441ed008a2SHuacai Chen 	int i, j;
2451ed008a2SHuacai Chen 
2461ed008a2SHuacai Chen 	for (i = 0; i < nr_pics; i++) {
2471ed008a2SHuacai Chen 		for (j = 0; j < PIC_REG_COUNT; j++) {
2481ed008a2SHuacai Chen 			pch_pic_priv[i]->saved_vec_pol[j] =
2491ed008a2SHuacai Chen 				readl(pch_pic_priv[i]->base + PCH_PIC_POL + 4 * j);
2501ed008a2SHuacai Chen 			pch_pic_priv[i]->saved_vec_edge[j] =
2511ed008a2SHuacai Chen 				readl(pch_pic_priv[i]->base + PCH_PIC_EDGE + 4 * j);
2521ed008a2SHuacai Chen 			pch_pic_priv[i]->saved_vec_en[j] =
2531ed008a2SHuacai Chen 				readl(pch_pic_priv[i]->base + PCH_PIC_MASK + 4 * j);
2541ed008a2SHuacai Chen 		}
2551ed008a2SHuacai Chen 	}
2561ed008a2SHuacai Chen 
2571ed008a2SHuacai Chen 	return 0;
2581ed008a2SHuacai Chen }
2591ed008a2SHuacai Chen 
pch_pic_resume(void)2601ed008a2SHuacai Chen static void pch_pic_resume(void)
2611ed008a2SHuacai Chen {
2621ed008a2SHuacai Chen 	int i, j;
2631ed008a2SHuacai Chen 
2641ed008a2SHuacai Chen 	for (i = 0; i < nr_pics; i++) {
2651ed008a2SHuacai Chen 		pch_pic_reset(pch_pic_priv[i]);
2661ed008a2SHuacai Chen 		for (j = 0; j < PIC_REG_COUNT; j++) {
2671ed008a2SHuacai Chen 			writel(pch_pic_priv[i]->saved_vec_pol[j],
2681ed008a2SHuacai Chen 					pch_pic_priv[i]->base + PCH_PIC_POL + 4 * j);
2691ed008a2SHuacai Chen 			writel(pch_pic_priv[i]->saved_vec_edge[j],
2701ed008a2SHuacai Chen 					pch_pic_priv[i]->base + PCH_PIC_EDGE + 4 * j);
2711ed008a2SHuacai Chen 			writel(pch_pic_priv[i]->saved_vec_en[j],
2721ed008a2SHuacai Chen 					pch_pic_priv[i]->base + PCH_PIC_MASK + 4 * j);
2731ed008a2SHuacai Chen 		}
2741ed008a2SHuacai Chen 	}
2751ed008a2SHuacai Chen }
2761ed008a2SHuacai Chen 
2771ed008a2SHuacai Chen static struct syscore_ops pch_pic_syscore_ops = {
2781ed008a2SHuacai Chen 	.suspend =  pch_pic_suspend,
2791ed008a2SHuacai Chen 	.resume =  pch_pic_resume,
2801ed008a2SHuacai Chen };
2811ed008a2SHuacai Chen 
pch_pic_init(phys_addr_t addr,unsigned long size,int vec_base,struct irq_domain * parent_domain,struct fwnode_handle * domain_handle,u32 gsi_base)282bcdd75c5SHuacai Chen static int pch_pic_init(phys_addr_t addr, unsigned long size, int vec_base,
283bcdd75c5SHuacai Chen 			struct irq_domain *parent_domain, struct fwnode_handle *domain_handle,
284bcdd75c5SHuacai Chen 			u32 gsi_base)
285ef8c01ebSJiaxun Yang {
286ef8c01ebSJiaxun Yang 	struct pch_pic *priv;
287ef8c01ebSJiaxun Yang 
288ef8c01ebSJiaxun Yang 	priv = kzalloc(sizeof(*priv), GFP_KERNEL);
289ef8c01ebSJiaxun Yang 	if (!priv)
290ef8c01ebSJiaxun Yang 		return -ENOMEM;
291ef8c01ebSJiaxun Yang 
292ef8c01ebSJiaxun Yang 	raw_spin_lock_init(&priv->pic_lock);
293bcdd75c5SHuacai Chen 	priv->base = ioremap(addr, size);
294bcdd75c5SHuacai Chen 	if (!priv->base)
295ef8c01ebSJiaxun Yang 		goto free_priv;
296ef8c01ebSJiaxun Yang 
297bcdd75c5SHuacai Chen 	priv->ht_vec_base = vec_base;
298bcdd75c5SHuacai Chen 	priv->vec_count = ((readq(priv->base) >> 48) & 0xff) + 1;
299bcdd75c5SHuacai Chen 	priv->gsi_base = gsi_base;
300ef8c01ebSJiaxun Yang 
301ef8c01ebSJiaxun Yang 	priv->pic_domain = irq_domain_create_hierarchy(parent_domain, 0,
302bcdd75c5SHuacai Chen 						priv->vec_count, domain_handle,
303bcdd75c5SHuacai Chen 						&pch_pic_domain_ops, priv);
304bcdd75c5SHuacai Chen 
305ef8c01ebSJiaxun Yang 	if (!priv->pic_domain) {
306ef8c01ebSJiaxun Yang 		pr_err("Failed to create IRQ domain\n");
307ef8c01ebSJiaxun Yang 		goto iounmap_base;
308ef8c01ebSJiaxun Yang 	}
309ef8c01ebSJiaxun Yang 
310ef8c01ebSJiaxun Yang 	pch_pic_reset(priv);
311bcdd75c5SHuacai Chen 	pch_pic_handle[nr_pics] = domain_handle;
312bcdd75c5SHuacai Chen 	pch_pic_priv[nr_pics++] = priv;
313ef8c01ebSJiaxun Yang 
314c84efbbaSJianmin Lv 	if (nr_pics == 1)
3151ed008a2SHuacai Chen 		register_syscore_ops(&pch_pic_syscore_ops);
3161ed008a2SHuacai Chen 
317ef8c01ebSJiaxun Yang 	return 0;
318ef8c01ebSJiaxun Yang 
319ef8c01ebSJiaxun Yang iounmap_base:
320ef8c01ebSJiaxun Yang 	iounmap(priv->base);
321ef8c01ebSJiaxun Yang free_priv:
322ef8c01ebSJiaxun Yang 	kfree(priv);
323ef8c01ebSJiaxun Yang 
324bcdd75c5SHuacai Chen 	return -EINVAL;
325bcdd75c5SHuacai Chen }
326bcdd75c5SHuacai Chen 
327bcdd75c5SHuacai Chen #ifdef CONFIG_OF
328bcdd75c5SHuacai Chen 
pch_pic_of_init(struct device_node * node,struct device_node * parent)329bcdd75c5SHuacai Chen static int pch_pic_of_init(struct device_node *node,
330bcdd75c5SHuacai Chen 				struct device_node *parent)
331bcdd75c5SHuacai Chen {
332bcdd75c5SHuacai Chen 	int err, vec_base;
333bcdd75c5SHuacai Chen 	struct resource res;
334bcdd75c5SHuacai Chen 	struct irq_domain *parent_domain;
335bcdd75c5SHuacai Chen 
336bcdd75c5SHuacai Chen 	if (of_address_to_resource(node, 0, &res))
337bcdd75c5SHuacai Chen 		return -EINVAL;
338bcdd75c5SHuacai Chen 
339bcdd75c5SHuacai Chen 	parent_domain = irq_find_host(parent);
340bcdd75c5SHuacai Chen 	if (!parent_domain) {
341bcdd75c5SHuacai Chen 		pr_err("Failed to find the parent domain\n");
342bcdd75c5SHuacai Chen 		return -ENXIO;
343bcdd75c5SHuacai Chen 	}
344bcdd75c5SHuacai Chen 
345bcdd75c5SHuacai Chen 	if (of_property_read_u32(node, "loongson,pic-base-vec", &vec_base)) {
346bcdd75c5SHuacai Chen 		pr_err("Failed to determine pic-base-vec\n");
347bcdd75c5SHuacai Chen 		return -EINVAL;
348bcdd75c5SHuacai Chen 	}
349bcdd75c5SHuacai Chen 
350bcdd75c5SHuacai Chen 	err = pch_pic_init(res.start, resource_size(&res), vec_base,
351bcdd75c5SHuacai Chen 				parent_domain, of_node_to_fwnode(node), 0);
352bcdd75c5SHuacai Chen 	if (err < 0)
353ef8c01ebSJiaxun Yang 		return err;
354bcdd75c5SHuacai Chen 
355bcdd75c5SHuacai Chen 	return 0;
356ef8c01ebSJiaxun Yang }
357ef8c01ebSJiaxun Yang 
358ef8c01ebSJiaxun Yang IRQCHIP_DECLARE(pch_pic, "loongson,pch-pic-1.0", pch_pic_of_init);
359bcdd75c5SHuacai Chen 
360bcdd75c5SHuacai Chen #endif
361bcdd75c5SHuacai Chen 
362bcdd75c5SHuacai Chen #ifdef CONFIG_ACPI
find_pch_pic(u32 gsi)363fda7409aSHuacai Chen int find_pch_pic(u32 gsi)
364fda7409aSHuacai Chen {
365fda7409aSHuacai Chen 	int i;
366fda7409aSHuacai Chen 
367fda7409aSHuacai Chen 	/* Find the PCH_PIC that manages this GSI. */
368fda7409aSHuacai Chen 	for (i = 0; i < MAX_IO_PICS; i++) {
369fda7409aSHuacai Chen 		struct pch_pic *priv = pch_pic_priv[i];
370fda7409aSHuacai Chen 
371fda7409aSHuacai Chen 		if (!priv)
372fda7409aSHuacai Chen 			return -1;
373fda7409aSHuacai Chen 
374fda7409aSHuacai Chen 		if (gsi >= priv->gsi_base && gsi < (priv->gsi_base + priv->vec_count))
375fda7409aSHuacai Chen 			return i;
376fda7409aSHuacai Chen 	}
377fda7409aSHuacai Chen 
378fda7409aSHuacai Chen 	pr_err("ERROR: Unable to locate PCH_PIC for GSI %d\n", gsi);
379fda7409aSHuacai Chen 	return -1;
380fda7409aSHuacai Chen }
381fda7409aSHuacai Chen 
pch_lpc_parse_madt(union acpi_subtable_headers * header,const unsigned long end)3823d12938dSHuacai Chen static int __init pch_lpc_parse_madt(union acpi_subtable_headers *header,
383bcdd75c5SHuacai Chen 					const unsigned long end)
384bcdd75c5SHuacai Chen {
385bcdd75c5SHuacai Chen 	struct acpi_madt_lpc_pic *pchlpc_entry = (struct acpi_madt_lpc_pic *)header;
386bcdd75c5SHuacai Chen 
387bcdd75c5SHuacai Chen 	return pch_lpc_acpi_init(pch_pic_priv[0]->pic_domain, pchlpc_entry);
388bcdd75c5SHuacai Chen }
389bcdd75c5SHuacai Chen 
acpi_cascade_irqdomain_init(void)390bcdd75c5SHuacai Chen static int __init acpi_cascade_irqdomain_init(void)
391bcdd75c5SHuacai Chen {
3923d12938dSHuacai Chen 	int r;
3933d12938dSHuacai Chen 
3943d12938dSHuacai Chen 	r = acpi_table_parse_madt(ACPI_MADT_TYPE_LPC_PIC, pch_lpc_parse_madt, 0);
3953d12938dSHuacai Chen 	if (r < 0)
3963d12938dSHuacai Chen 		return r;
3973d12938dSHuacai Chen 
398bcdd75c5SHuacai Chen 	return 0;
399bcdd75c5SHuacai Chen }
400bcdd75c5SHuacai Chen 
pch_pic_acpi_init(struct irq_domain * parent,struct acpi_madt_bio_pic * acpi_pchpic)401bcdd75c5SHuacai Chen int __init pch_pic_acpi_init(struct irq_domain *parent,
402bcdd75c5SHuacai Chen 					struct acpi_madt_bio_pic *acpi_pchpic)
403bcdd75c5SHuacai Chen {
404f6796165SJianmin Lv 	int ret;
405bcdd75c5SHuacai Chen 	struct fwnode_handle *domain_handle;
406bcdd75c5SHuacai Chen 
40748ce2d72SJianmin Lv 	if (find_pch_pic(acpi_pchpic->gsi_base) >= 0)
40848ce2d72SJianmin Lv 		return 0;
40948ce2d72SJianmin Lv 
4107e4fd7a1SMarc Zyngier 	domain_handle = irq_domain_alloc_fwnode(&acpi_pchpic->address);
411bcdd75c5SHuacai Chen 	if (!domain_handle) {
412bcdd75c5SHuacai Chen 		pr_err("Unable to allocate domain handle\n");
413bcdd75c5SHuacai Chen 		return -ENOMEM;
414bcdd75c5SHuacai Chen 	}
415bcdd75c5SHuacai Chen 
416bcdd75c5SHuacai Chen 	ret = pch_pic_init(acpi_pchpic->address, acpi_pchpic->size,
417f6796165SJianmin Lv 				0, parent, domain_handle, acpi_pchpic->gsi_base);
418bcdd75c5SHuacai Chen 
419bcdd75c5SHuacai Chen 	if (ret < 0) {
420bcdd75c5SHuacai Chen 		irq_domain_free_fwnode(domain_handle);
421bcdd75c5SHuacai Chen 		return ret;
422bcdd75c5SHuacai Chen 	}
423bcdd75c5SHuacai Chen 
424bcdd75c5SHuacai Chen 	if (acpi_pchpic->id == 0)
4253d12938dSHuacai Chen 		ret = acpi_cascade_irqdomain_init();
426bcdd75c5SHuacai Chen 
427bcdd75c5SHuacai Chen 	return ret;
428bcdd75c5SHuacai Chen }
429bcdd75c5SHuacai Chen #endif
430