xref: /openbmc/u-boot/arch/x86/cpu/irq.c (revision 87a62bce)
1 // SPDX-License-Identifier: GPL-2.0+
2 /*
3  * Copyright (C) 2015, Bin Meng <bmeng.cn@gmail.com>
4  */
5 
6 #include <common.h>
7 #include <dm.h>
8 #include <errno.h>
9 #include <fdtdec.h>
10 #include <malloc.h>
11 #include <asm/io.h>
12 #include <asm/irq.h>
13 #include <asm/pci.h>
14 #include <asm/pirq_routing.h>
15 #include <asm/tables.h>
16 
17 DECLARE_GLOBAL_DATA_PTR;
18 
19 bool pirq_check_irq_routed(struct udevice *dev, int link, u8 irq)
20 {
21 	struct irq_router *priv = dev_get_priv(dev);
22 	u8 pirq;
23 	int base = priv->link_base;
24 
25 	if (priv->config == PIRQ_VIA_PCI)
26 		dm_pci_read_config8(dev->parent, LINK_N2V(link, base), &pirq);
27 	else
28 		pirq = readb((uintptr_t)priv->ibase + LINK_N2V(link, base));
29 
30 	pirq &= 0xf;
31 
32 	/* IRQ# 0/1/2/8/13 are reserved */
33 	if (pirq < 3 || pirq == 8 || pirq == 13)
34 		return false;
35 
36 	return pirq == irq ? true : false;
37 }
38 
39 int pirq_translate_link(struct udevice *dev, int link)
40 {
41 	struct irq_router *priv = dev_get_priv(dev);
42 
43 	return LINK_V2N(link, priv->link_base);
44 }
45 
46 void pirq_assign_irq(struct udevice *dev, int link, u8 irq)
47 {
48 	struct irq_router *priv = dev_get_priv(dev);
49 	int base = priv->link_base;
50 
51 	/* IRQ# 0/1/2/8/13 are reserved */
52 	if (irq < 3 || irq == 8 || irq == 13)
53 		return;
54 
55 	if (priv->config == PIRQ_VIA_PCI)
56 		dm_pci_write_config8(dev->parent, LINK_N2V(link, base), irq);
57 	else
58 		writeb(irq, (uintptr_t)priv->ibase + LINK_N2V(link, base));
59 }
60 
61 static struct irq_info *check_dup_entry(struct irq_info *slot_base,
62 					int entry_num, int bus, int device)
63 {
64 	struct irq_info *slot = slot_base;
65 	int i;
66 
67 	for (i = 0; i < entry_num; i++) {
68 		if (slot->bus == bus && slot->devfn == (device << 3))
69 			break;
70 		slot++;
71 	}
72 
73 	return (i == entry_num) ? NULL : slot;
74 }
75 
76 static inline void fill_irq_info(struct irq_router *priv, struct irq_info *slot,
77 				 int bus, int device, int pin, int pirq)
78 {
79 	slot->bus = bus;
80 	slot->devfn = (device << 3) | 0;
81 	slot->irq[pin - 1].link = LINK_N2V(pirq, priv->link_base);
82 	slot->irq[pin - 1].bitmap = priv->irq_mask;
83 }
84 
85 static int create_pirq_routing_table(struct udevice *dev)
86 {
87 	struct irq_router *priv = dev_get_priv(dev);
88 	const void *blob = gd->fdt_blob;
89 	int node;
90 	int len, count;
91 	const u32 *cell;
92 	struct irq_routing_table *rt;
93 	struct irq_info *slot, *slot_base;
94 	int irq_entries = 0;
95 	int i;
96 	int ret;
97 
98 	node = dev_of_offset(dev);
99 
100 	/* extract the bdf from fdt_pci_addr */
101 	priv->bdf = dm_pci_get_bdf(dev->parent);
102 
103 	ret = fdt_stringlist_search(blob, node, "intel,pirq-config", "pci");
104 	if (!ret) {
105 		priv->config = PIRQ_VIA_PCI;
106 	} else {
107 		ret = fdt_stringlist_search(blob, node, "intel,pirq-config",
108 					    "ibase");
109 		if (!ret)
110 			priv->config = PIRQ_VIA_IBASE;
111 		else
112 			return -EINVAL;
113 	}
114 
115 	ret = fdtdec_get_int(blob, node, "intel,pirq-link", -1);
116 	if (ret == -1)
117 		return ret;
118 	priv->link_base = ret;
119 
120 	priv->irq_mask = fdtdec_get_int(blob, node,
121 					"intel,pirq-mask", PIRQ_BITMAP);
122 
123 	if (IS_ENABLED(CONFIG_GENERATE_ACPI_TABLE)) {
124 		/* Reserve IRQ9 for SCI */
125 		priv->irq_mask &= ~(1 << 9);
126 	}
127 
128 	if (priv->config == PIRQ_VIA_IBASE) {
129 		int ibase_off;
130 
131 		ibase_off = fdtdec_get_int(blob, node, "intel,ibase-offset", 0);
132 		if (!ibase_off)
133 			return -EINVAL;
134 
135 		/*
136 		 * Here we assume that the IBASE register has already been
137 		 * properly configured by U-Boot before.
138 		 *
139 		 * By 'valid' we mean:
140 		 *   1) a valid memory space carved within system memory space
141 		 *      assigned to IBASE register block.
142 		 *   2) memory range decoding is enabled.
143 		 * Hence we don't do any santify test here.
144 		 */
145 		dm_pci_read_config32(dev->parent, ibase_off, &priv->ibase);
146 		priv->ibase &= ~0xf;
147 	}
148 
149 	priv->actl_8bit = fdtdec_get_bool(blob, node, "intel,actl-8bit");
150 	priv->actl_addr = fdtdec_get_int(blob, node, "intel,actl-addr", 0);
151 
152 	cell = fdt_getprop(blob, node, "intel,pirq-routing", &len);
153 	if (!cell || len % sizeof(struct pirq_routing))
154 		return -EINVAL;
155 	count = len / sizeof(struct pirq_routing);
156 
157 	rt = calloc(1, sizeof(struct irq_routing_table));
158 	if (!rt)
159 		return -ENOMEM;
160 
161 	/* Populate the PIRQ table fields */
162 	rt->signature = PIRQ_SIGNATURE;
163 	rt->version = PIRQ_VERSION;
164 	rt->rtr_bus = PCI_BUS(priv->bdf);
165 	rt->rtr_devfn = (PCI_DEV(priv->bdf) << 3) | PCI_FUNC(priv->bdf);
166 	rt->rtr_vendor = PCI_VENDOR_ID_INTEL;
167 	rt->rtr_device = PCI_DEVICE_ID_INTEL_ICH7_31;
168 
169 	slot_base = rt->slots;
170 
171 	/* Now fill in the irq_info entries in the PIRQ table */
172 	for (i = 0; i < count;
173 	     i++, cell += sizeof(struct pirq_routing) / sizeof(u32)) {
174 		struct pirq_routing pr;
175 
176 		pr.bdf = fdt_addr_to_cpu(cell[0]);
177 		pr.pin = fdt_addr_to_cpu(cell[1]);
178 		pr.pirq = fdt_addr_to_cpu(cell[2]);
179 
180 		debug("irq_info %d: b.d.f %x.%x.%x INT%c PIRQ%c\n",
181 		      i, PCI_BUS(pr.bdf), PCI_DEV(pr.bdf),
182 		      PCI_FUNC(pr.bdf), 'A' + pr.pin - 1,
183 		      'A' + pr.pirq);
184 
185 		slot = check_dup_entry(slot_base, irq_entries,
186 				       PCI_BUS(pr.bdf), PCI_DEV(pr.bdf));
187 		if (slot) {
188 			debug("found entry for bus %d device %d, ",
189 			      PCI_BUS(pr.bdf), PCI_DEV(pr.bdf));
190 
191 			if (slot->irq[pr.pin - 1].link) {
192 				debug("skipping\n");
193 
194 				/*
195 				 * Sanity test on the routed PIRQ pin
196 				 *
197 				 * If they don't match, show a warning to tell
198 				 * there might be something wrong with the PIRQ
199 				 * routing information in the device tree.
200 				 */
201 				if (slot->irq[pr.pin - 1].link !=
202 					LINK_N2V(pr.pirq, priv->link_base))
203 					debug("WARNING: Inconsistent PIRQ routing information\n");
204 				continue;
205 			}
206 		} else {
207 			slot = slot_base + irq_entries++;
208 		}
209 		debug("writing INT%c\n", 'A' + pr.pin - 1);
210 		fill_irq_info(priv, slot, PCI_BUS(pr.bdf), PCI_DEV(pr.bdf),
211 			      pr.pin, pr.pirq);
212 	}
213 
214 	rt->size = irq_entries * sizeof(struct irq_info) + 32;
215 
216 	/* Fix up the table checksum */
217 	rt->checksum = table_compute_checksum(rt, rt->size);
218 
219 	gd->arch.pirq_routing_table = rt;
220 
221 	return 0;
222 }
223 
224 static void irq_enable_sci(struct udevice *dev)
225 {
226 	struct irq_router *priv = dev_get_priv(dev);
227 
228 	if (priv->actl_8bit) {
229 		/* Bit7 must be turned on to enable ACPI */
230 		dm_pci_write_config8(dev->parent, priv->actl_addr, 0x80);
231 	} else {
232 		/* Write 0 to enable SCI on IRQ9 */
233 		if (priv->config == PIRQ_VIA_PCI)
234 			dm_pci_write_config32(dev->parent, priv->actl_addr, 0);
235 		else
236 			writel(0, (uintptr_t)priv->ibase + priv->actl_addr);
237 	}
238 }
239 
240 int irq_router_common_init(struct udevice *dev)
241 {
242 	int ret;
243 
244 	ret = create_pirq_routing_table(dev);
245 	if (ret) {
246 		debug("Failed to create pirq routing table\n");
247 		return ret;
248 	}
249 	/* Route PIRQ */
250 	pirq_route_irqs(dev, gd->arch.pirq_routing_table->slots,
251 			get_irq_slot_count(gd->arch.pirq_routing_table));
252 
253 	if (IS_ENABLED(CONFIG_GENERATE_ACPI_TABLE))
254 		irq_enable_sci(dev);
255 
256 	return 0;
257 }
258 
259 int irq_router_probe(struct udevice *dev)
260 {
261 	return irq_router_common_init(dev);
262 }
263 
264 ulong write_pirq_routing_table(ulong addr)
265 {
266 	if (!gd->arch.pirq_routing_table)
267 		return addr;
268 
269 	return copy_pirq_routing_table(addr, gd->arch.pirq_routing_table);
270 }
271 
272 static const struct udevice_id irq_router_ids[] = {
273 	{ .compatible = "intel,irq-router" },
274 	{ }
275 };
276 
277 U_BOOT_DRIVER(irq_router_drv) = {
278 	.name		= "intel_irq",
279 	.id		= UCLASS_IRQ,
280 	.of_match	= irq_router_ids,
281 	.probe		= irq_router_probe,
282 	.priv_auto_alloc_size = sizeof(struct irq_router),
283 };
284 
285 UCLASS_DRIVER(irq) = {
286 	.id		= UCLASS_IRQ,
287 	.name		= "irq",
288 };
289