1 // SPDX-License-Identifier: GPL-2.0
3 * Loongson LPC Interrupt Controller support
5 * Copyright (C) 2020-2022 Loongson Technology Corporation Limited
8 #define pr_fmt(fmt) "lpc: " fmt
10 #include <linux/interrupt.h>
11 #include <linux/irq.h>
12 #include <linux/irqchip.h>
13 #include <linux/irqchip/chained_irq.h>
14 #include <linux/irqdomain.h>
15 #include <linux/kernel.h>
16 #include <linux/syscore_ops.h>
18 #include "irq-loongson.h"
21 #define LPC_INT_CTL 0x00
22 #define LPC_INT_ENA 0x04
23 #define LPC_INT_STS 0x08
24 #define LPC_INT_CLR 0x0c
25 #define LPC_INT_POL 0x10
29 #define LPC_INT_CTL_EN BIT(31)
33 struct irq_domain
*lpc_domain
;
34 raw_spinlock_t lpc_lock
;
40 static struct pch_lpc
*pch_lpc_priv
;
41 struct fwnode_handle
*pch_lpc_handle
;
43 static void lpc_irq_ack(struct irq_data
*d
)
46 struct pch_lpc
*priv
= d
->domain
->host_data
;
48 raw_spin_lock_irqsave(&priv
->lpc_lock
, flags
);
49 writel(0x1 << d
->hwirq
, priv
->base
+ LPC_INT_CLR
);
50 raw_spin_unlock_irqrestore(&priv
->lpc_lock
, flags
);
53 static void lpc_irq_mask(struct irq_data
*d
)
56 struct pch_lpc
*priv
= d
->domain
->host_data
;
58 raw_spin_lock_irqsave(&priv
->lpc_lock
, flags
);
59 writel(readl(priv
->base
+ LPC_INT_ENA
) & (~(0x1 << (d
->hwirq
))),
60 priv
->base
+ LPC_INT_ENA
);
61 raw_spin_unlock_irqrestore(&priv
->lpc_lock
, flags
);
64 static void lpc_irq_unmask(struct irq_data
*d
)
67 struct pch_lpc
*priv
= d
->domain
->host_data
;
69 raw_spin_lock_irqsave(&priv
->lpc_lock
, flags
);
70 writel(readl(priv
->base
+ LPC_INT_ENA
) | (0x1 << (d
->hwirq
)),
71 priv
->base
+ LPC_INT_ENA
);
72 raw_spin_unlock_irqrestore(&priv
->lpc_lock
, flags
);
75 static int lpc_irq_set_type(struct irq_data
*d
, unsigned int type
)
78 u32 mask
= 0x1 << (d
->hwirq
);
79 struct pch_lpc
*priv
= d
->domain
->host_data
;
81 if (!(type
& IRQ_TYPE_LEVEL_MASK
))
84 val
= readl(priv
->base
+ LPC_INT_POL
);
86 if (type
== IRQ_TYPE_LEVEL_HIGH
)
91 writel(val
, priv
->base
+ LPC_INT_POL
);
96 static const struct irq_chip pch_lpc_irq_chip
= {
98 .irq_mask
= lpc_irq_mask
,
99 .irq_unmask
= lpc_irq_unmask
,
100 .irq_ack
= lpc_irq_ack
,
101 .irq_set_type
= lpc_irq_set_type
,
102 .flags
= IRQCHIP_SKIP_SET_WAKE
,
105 static void lpc_irq_dispatch(struct irq_desc
*desc
)
108 struct irq_chip
*chip
= irq_desc_get_chip(desc
);
109 struct pch_lpc
*priv
= irq_desc_get_handler_data(desc
);
111 chained_irq_enter(chip
, desc
);
113 pending
= readl(priv
->base
+ LPC_INT_ENA
);
114 pending
&= readl(priv
->base
+ LPC_INT_STS
);
116 spurious_interrupt();
119 bit
= __ffs(pending
);
121 generic_handle_domain_irq(priv
->lpc_domain
, bit
);
122 pending
&= ~BIT(bit
);
124 chained_irq_exit(chip
, desc
);
127 static int pch_lpc_map(struct irq_domain
*d
, unsigned int irq
,
130 irq_set_chip_and_handler(irq
, &pch_lpc_irq_chip
, handle_level_irq
);
134 static const struct irq_domain_ops pch_lpc_domain_ops
= {
136 .translate
= irq_domain_translate_twocell
,
139 static void pch_lpc_reset(struct pch_lpc
*priv
)
141 /* Enable the LPC interrupt, bit31: en bit30: edge */
142 writel(LPC_INT_CTL_EN
, priv
->base
+ LPC_INT_CTL
);
143 writel(0, priv
->base
+ LPC_INT_ENA
);
144 /* Clear all 18-bit interrpt bit */
145 writel(GENMASK(17, 0), priv
->base
+ LPC_INT_CLR
);
148 static int pch_lpc_disabled(struct pch_lpc
*priv
)
150 return (readl(priv
->base
+ LPC_INT_ENA
) == 0xffffffff) &&
151 (readl(priv
->base
+ LPC_INT_STS
) == 0xffffffff);
154 static int pch_lpc_suspend(void)
156 pch_lpc_priv
->saved_reg_ctl
= readl(pch_lpc_priv
->base
+ LPC_INT_CTL
);
157 pch_lpc_priv
->saved_reg_ena
= readl(pch_lpc_priv
->base
+ LPC_INT_ENA
);
158 pch_lpc_priv
->saved_reg_pol
= readl(pch_lpc_priv
->base
+ LPC_INT_POL
);
162 static void pch_lpc_resume(void)
164 writel(pch_lpc_priv
->saved_reg_ctl
, pch_lpc_priv
->base
+ LPC_INT_CTL
);
165 writel(pch_lpc_priv
->saved_reg_ena
, pch_lpc_priv
->base
+ LPC_INT_ENA
);
166 writel(pch_lpc_priv
->saved_reg_pol
, pch_lpc_priv
->base
+ LPC_INT_POL
);
169 static struct syscore_ops pch_lpc_syscore_ops
= {
170 .suspend
= pch_lpc_suspend
,
171 .resume
= pch_lpc_resume
,
174 int __init
pch_lpc_acpi_init(struct irq_domain
*parent
,
175 struct acpi_madt_lpc_pic
*acpi_pchlpc
)
178 struct pch_lpc
*priv
;
179 struct irq_fwspec fwspec
;
180 struct fwnode_handle
*irq_handle
;
182 priv
= kzalloc(sizeof(*priv
), GFP_KERNEL
);
186 raw_spin_lock_init(&priv
->lpc_lock
);
188 priv
->base
= ioremap(acpi_pchlpc
->address
, acpi_pchlpc
->size
);
192 if (pch_lpc_disabled(priv
)) {
193 pr_err("Failed to get LPC status\n");
197 irq_handle
= irq_domain_alloc_named_fwnode("lpcintc");
199 pr_err("Unable to allocate domain handle\n");
203 priv
->lpc_domain
= irq_domain_create_linear(irq_handle
, LPC_COUNT
,
204 &pch_lpc_domain_ops
, priv
);
205 if (!priv
->lpc_domain
) {
206 pr_err("Failed to create IRQ domain\n");
207 goto free_irq_handle
;
211 fwspec
.fwnode
= parent
->fwnode
;
212 fwspec
.param
[0] = acpi_pchlpc
->cascade
+ GSI_MIN_PCH_IRQ
;
213 fwspec
.param
[1] = IRQ_TYPE_LEVEL_HIGH
;
214 fwspec
.param_count
= 2;
215 parent_irq
= irq_create_fwspec_mapping(&fwspec
);
216 irq_set_chained_handler_and_data(parent_irq
, lpc_irq_dispatch
, priv
);
219 pch_lpc_handle
= irq_handle
;
220 register_syscore_ops(&pch_lpc_syscore_ops
);
225 irq_domain_free_fwnode(irq_handle
);