xref: /linux/drivers/irqchip/irq-loongson-pch-lpc.c (revision c7546e2c3cb739a3c1a2f5acaf9bb629d401afe5)
1 // SPDX-License-Identifier: GPL-2.0
2 /*
3  * Loongson LPC Interrupt Controller support
4  *
5  * Copyright (C) 2020-2022 Loongson Technology Corporation Limited
6  */
7 
8 #define pr_fmt(fmt) "lpc: " fmt
9 
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>
17 
18 #include "irq-loongson.h"
19 
20 /* Registers */
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
26 #define LPC_COUNT		16
27 
28 /* LPC_INT_CTL */
29 #define LPC_INT_CTL_EN		BIT(31)
30 
31 struct pch_lpc {
32 	void __iomem		*base;
33 	struct irq_domain	*lpc_domain;
34 	raw_spinlock_t		lpc_lock;
35 	u32			saved_reg_ctl;
36 	u32			saved_reg_ena;
37 	u32			saved_reg_pol;
38 };
39 
40 static struct pch_lpc *pch_lpc_priv;
41 struct fwnode_handle *pch_lpc_handle;
42 
43 static void lpc_irq_ack(struct irq_data *d)
44 {
45 	unsigned long flags;
46 	struct pch_lpc *priv = d->domain->host_data;
47 
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);
51 }
52 
53 static void lpc_irq_mask(struct irq_data *d)
54 {
55 	unsigned long flags;
56 	struct pch_lpc *priv = d->domain->host_data;
57 
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);
62 }
63 
64 static void lpc_irq_unmask(struct irq_data *d)
65 {
66 	unsigned long flags;
67 	struct pch_lpc *priv = d->domain->host_data;
68 
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);
73 }
74 
75 static int lpc_irq_set_type(struct irq_data *d, unsigned int type)
76 {
77 	u32 val;
78 	u32 mask = 0x1 << (d->hwirq);
79 	struct pch_lpc *priv = d->domain->host_data;
80 
81 	if (!(type & IRQ_TYPE_LEVEL_MASK))
82 		return 0;
83 
84 	val = readl(priv->base + LPC_INT_POL);
85 
86 	if (type == IRQ_TYPE_LEVEL_HIGH)
87 		val |= mask;
88 	else
89 		val &= ~mask;
90 
91 	writel(val, priv->base + LPC_INT_POL);
92 
93 	return 0;
94 }
95 
96 static const struct irq_chip pch_lpc_irq_chip = {
97 	.name			= "PCH LPC",
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,
103 };
104 
105 static void lpc_irq_dispatch(struct irq_desc *desc)
106 {
107 	u32 pending, bit;
108 	struct irq_chip *chip = irq_desc_get_chip(desc);
109 	struct pch_lpc *priv = irq_desc_get_handler_data(desc);
110 
111 	chained_irq_enter(chip, desc);
112 
113 	pending = readl(priv->base + LPC_INT_ENA);
114 	pending &= readl(priv->base + LPC_INT_STS);
115 	if (!pending)
116 		spurious_interrupt();
117 
118 	while (pending) {
119 		bit = __ffs(pending);
120 
121 		generic_handle_domain_irq(priv->lpc_domain, bit);
122 		pending &= ~BIT(bit);
123 	}
124 	chained_irq_exit(chip, desc);
125 }
126 
127 static int pch_lpc_map(struct irq_domain *d, unsigned int irq,
128 			irq_hw_number_t hw)
129 {
130 	irq_set_chip_and_handler(irq, &pch_lpc_irq_chip, handle_level_irq);
131 	return 0;
132 }
133 
134 static const struct irq_domain_ops pch_lpc_domain_ops = {
135 	.map 		= pch_lpc_map,
136 	.translate	= irq_domain_translate_twocell,
137 };
138 
139 static void pch_lpc_reset(struct pch_lpc *priv)
140 {
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);
146 }
147 
148 static int pch_lpc_disabled(struct pch_lpc *priv)
149 {
150 	return (readl(priv->base + LPC_INT_ENA) == 0xffffffff) &&
151 			(readl(priv->base + LPC_INT_STS) == 0xffffffff);
152 }
153 
154 static int pch_lpc_suspend(void)
155 {
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);
159 	return 0;
160 }
161 
162 static void pch_lpc_resume(void)
163 {
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);
167 }
168 
169 static struct syscore_ops pch_lpc_syscore_ops = {
170 	.suspend = pch_lpc_suspend,
171 	.resume = pch_lpc_resume,
172 };
173 
174 int __init pch_lpc_acpi_init(struct irq_domain *parent,
175 					struct acpi_madt_lpc_pic *acpi_pchlpc)
176 {
177 	int parent_irq;
178 	struct pch_lpc *priv;
179 	struct irq_fwspec fwspec;
180 	struct fwnode_handle *irq_handle;
181 
182 	priv = kzalloc(sizeof(*priv), GFP_KERNEL);
183 	if (!priv)
184 		return -ENOMEM;
185 
186 	raw_spin_lock_init(&priv->lpc_lock);
187 
188 	priv->base = ioremap(acpi_pchlpc->address, acpi_pchlpc->size);
189 	if (!priv->base)
190 		goto free_priv;
191 
192 	if (pch_lpc_disabled(priv)) {
193 		pr_err("Failed to get LPC status\n");
194 		goto iounmap_base;
195 	}
196 
197 	irq_handle = irq_domain_alloc_named_fwnode("lpcintc");
198 	if (!irq_handle) {
199 		pr_err("Unable to allocate domain handle\n");
200 		goto iounmap_base;
201 	}
202 
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;
208 	}
209 	pch_lpc_reset(priv);
210 
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);
217 
218 	pch_lpc_priv = priv;
219 	pch_lpc_handle = irq_handle;
220 	register_syscore_ops(&pch_lpc_syscore_ops);
221 
222 	return 0;
223 
224 free_irq_handle:
225 	irq_domain_free_fwnode(irq_handle);
226 iounmap_base:
227 	iounmap(priv->base);
228 free_priv:
229 	kfree(priv);
230 
231 	return -ENOMEM;
232 }
233