• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // SPDX-License-Identifier: GPL-2.0
2 /*
3  *  Copyright (C) 2020, Jianmin Lv <lvjianmin@loongson.cn>
4  *  Loongson LPC support
5  */
6 
7 #define pr_fmt(fmt) "lpc: " fmt
8 
9 #include <linux/interrupt.h>
10 #include <linux/irq.h>
11 #include <linux/irqchip.h>
12 #include <linux/irqchip/chained_irq.h>
13 #include <linux/irqdomain.h>
14 #include <linux/kernel.h>
15 #include <linux/syscore_ops.h>
16 
17 /* Registers */
18 #define LPC_INT_CTL		0x00
19 #define LPC_INT_ENA		0x04
20 #define LPC_INT_STS		0x08
21 #define LPC_INT_CLR		0x0c
22 #define LPC_INT_POL		0x10
23 #define LPC_COUNT		16
24 
25 struct pch_lpc {
26 	void __iomem		*base;
27 	struct irq_domain	*lpc_domain;
28 	struct fwnode_handle	*domain_handle;
29 	raw_spinlock_t		lpc_lock;
30 	u32			saved_reg_ctl;
31 	u32			saved_reg_ena;
32 	u32			saved_reg_pol;
33 };
34 
35 static struct pch_lpc *pch_lpc_priv;
36 
ack_lpc_irq(struct irq_data * d)37 static void ack_lpc_irq(struct irq_data *d)
38 {
39 	unsigned long flags;
40 
41 	raw_spin_lock_irqsave(&pch_lpc_priv->lpc_lock, flags);
42 	writel(0x1 << d->irq, pch_lpc_priv->base + LPC_INT_CLR);
43 	raw_spin_unlock_irqrestore(&pch_lpc_priv->lpc_lock, flags);
44 }
mask_lpc_irq(struct irq_data * d)45 static void mask_lpc_irq(struct irq_data *d)
46 {
47 	unsigned long flags;
48 
49 	raw_spin_lock_irqsave(&pch_lpc_priv->lpc_lock, flags);
50 	writel(readl(pch_lpc_priv->base + LPC_INT_ENA) & (~(0x1 << (d->irq))),
51 			pch_lpc_priv->base + LPC_INT_ENA);
52 	raw_spin_unlock_irqrestore(&pch_lpc_priv->lpc_lock, flags);
53 }
54 
mask_ack_lpc_irq(struct irq_data * d)55 static void mask_ack_lpc_irq(struct irq_data *d)
56 {
57 }
58 
unmask_lpc_irq(struct irq_data * d)59 static void unmask_lpc_irq(struct irq_data *d)
60 {
61 	unsigned long flags;
62 	raw_spin_lock_irqsave(&pch_lpc_priv->lpc_lock, flags);
63 	writel(readl(pch_lpc_priv->base + LPC_INT_ENA) | (0x1 << (d->irq)),
64 			pch_lpc_priv->base + LPC_INT_ENA);
65 	raw_spin_unlock_irqrestore(&pch_lpc_priv->lpc_lock, flags);
66 }
67 
lpc_set_type(struct irq_data * d,unsigned int type)68 static int lpc_set_type(struct irq_data *d, unsigned int type)
69 {
70 	u32 val;
71 	u32 mask = 0x1 << (d->hwirq);
72 
73 	if (!(type & IRQ_TYPE_LEVEL_MASK))
74 		return 0;
75 
76 	val = readl(pch_lpc_priv->base + LPC_INT_POL);
77 
78 	if (type == IRQ_TYPE_LEVEL_HIGH)
79 		val |= mask;
80 	else
81 		val &= ~mask;
82 
83 	writel(val, pch_lpc_priv->base + LPC_INT_POL);
84 
85 	return 0;
86 }
87 
88 static struct irq_chip pch_lpc_irq_chip = {
89 	.name			= "PCH LPC",
90 	.irq_mask		= mask_lpc_irq,
91 	.irq_unmask		= unmask_lpc_irq,
92 	.irq_ack		= ack_lpc_irq,
93 	.irq_mask_ack		= mask_ack_lpc_irq,
94 	.irq_eoi		= unmask_lpc_irq,
95 	.irq_set_type		= lpc_set_type,
96 	.flags			= IRQCHIP_SKIP_SET_WAKE,
97 };
98 
lpc_irq_dispatch(struct irq_desc * desc)99 static void lpc_irq_dispatch(struct irq_desc *desc)
100 {
101 	struct irq_chip *chip = irq_desc_get_chip(desc);
102 	u32 pending;
103 
104 	chained_irq_enter(chip, desc);
105 
106 	pending = readl(pch_lpc_priv->base + LPC_INT_ENA);
107 	pending &= readl(pch_lpc_priv->base + LPC_INT_STS);
108 	if (!pending)
109 		spurious_interrupt();
110 
111 	while (pending) {
112 		int bit = __ffs(pending);
113 		generic_handle_irq(bit);
114 		pending &= ~BIT(bit);
115 	}
116 	chained_irq_exit(chip, desc);
117 }
118 
pch_lpc_map(struct irq_domain * d,unsigned int irq,irq_hw_number_t hw)119 static int pch_lpc_map(struct irq_domain *d, unsigned int irq,
120 			irq_hw_number_t hw)
121 {
122 	irq_set_chip_and_handler(irq, &pch_lpc_irq_chip, handle_level_irq);
123 	return 0;
124 }
125 
126 static const struct irq_domain_ops pch_lpc_domain_ops = {
127 	.map 		= pch_lpc_map,
128 	.translate	= irq_domain_translate_twocell,
129 };
130 
pch_lpc_reset(struct pch_lpc * priv)131 static void pch_lpc_reset(struct pch_lpc *priv)
132 {
133 	/* Enable the LPC interrupt, bit31: en  bit30: edge */
134 	writel(0x80000000, priv->base + LPC_INT_CTL);
135 	writel(0, priv->base + LPC_INT_ENA);
136 	/* Clear all 18-bit interrpt bit */
137 	writel(0x3ffff, priv->base + LPC_INT_CLR);
138 }
139 
pch_lpc_disabled(struct pch_lpc * priv)140 static int pch_lpc_disabled(struct pch_lpc *priv)
141 {
142 	return (readl(priv->base + LPC_INT_ENA) == 0xffffffff) &&
143 			(readl(priv->base + LPC_INT_STS) == 0xffffffff);
144 }
145 
pch_lpc_suspend(void)146 static int pch_lpc_suspend(void)
147 {
148 	pch_lpc_priv->saved_reg_ctl = readl(pch_lpc_priv->base + LPC_INT_CTL);
149 	pch_lpc_priv->saved_reg_ena = readl(pch_lpc_priv->base + LPC_INT_ENA);
150 	pch_lpc_priv->saved_reg_pol = readl(pch_lpc_priv->base + LPC_INT_POL);
151 	return 0;
152 }
153 
pch_lpc_resume(void)154 static void pch_lpc_resume(void)
155 {
156 	writel(pch_lpc_priv->saved_reg_ctl, pch_lpc_priv->base + LPC_INT_CTL);
157 	writel(pch_lpc_priv->saved_reg_ena, pch_lpc_priv->base + LPC_INT_ENA);
158 	writel(pch_lpc_priv->saved_reg_pol, pch_lpc_priv->base + LPC_INT_POL);
159 }
160 
161 static struct syscore_ops pch_lpc_syscore_ops = {
162 	.suspend = pch_lpc_suspend,
163 	.resume = pch_lpc_resume,
164 };
165 
pch_lpc_acpi_init(struct irq_domain * parent,struct acpi_madt_lpc_pic * acpi_pchlpc)166 struct irq_domain *pch_lpc_acpi_init(struct irq_domain *parent,
167 					struct acpi_madt_lpc_pic *acpi_pchlpc)
168 {
169 	int parent_irq;
170 	struct pch_lpc *priv;
171 	struct irq_fwspec fwspec;
172 
173 	if (!acpi_pchlpc)
174 		return NULL;
175 
176 	priv = kzalloc(sizeof(*priv), GFP_KERNEL);
177 	if (!priv)
178 		return NULL;
179 
180 	raw_spin_lock_init(&priv->lpc_lock);
181 
182 	priv->base = ioremap(acpi_pchlpc->address, acpi_pchlpc->size);
183 	if (!priv->base) {
184 		goto free_priv;
185 	}
186 
187 	if (pch_lpc_disabled(priv)) {
188 		pr_err("Failed to get LPC status\n");
189 		goto iounmap_base;
190 	}
191 
192 	priv->domain_handle = irq_domain_alloc_fwnode((phys_addr_t *)acpi_pchlpc);
193 	if (!priv->domain_handle) {
194 		pr_err("Unable to allocate domain handle\n");
195 		goto iounmap_base;
196 	}
197 	priv->lpc_domain = irq_domain_add_legacy(NULL, LPC_COUNT, 0, 0,
198 						&pch_lpc_domain_ops, priv);
199 	if (!priv->lpc_domain) {
200 		pr_err("Failed to create IRQ domain\n");
201 		goto iounmap_base;
202 	}
203 	pch_lpc_reset(priv);
204 
205 	fwspec.fwnode = parent->fwnode;
206 	fwspec.param[0] = acpi_pchlpc->cascade;
207 	fwspec.param[1] = IRQ_TYPE_LEVEL_HIGH;
208 	fwspec.param_count = 2;
209 	parent_irq = irq_create_fwspec_mapping(&fwspec);
210 	irq_set_chained_handler_and_data(parent_irq, lpc_irq_dispatch, priv);
211 	pch_lpc_priv = priv;
212 
213 	register_syscore_ops(&pch_lpc_syscore_ops);
214 
215 	return irq_find_matching_fwnode(priv->domain_handle, DOMAIN_BUS_ANY);
216 
217 iounmap_base:
218 	iounmap(priv->base);
219 free_priv:
220 	kfree(priv);
221 
222 	return NULL;
223 }
224