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