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