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