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