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