linux/arch/x86/kernel/apic/msi.c
<<
>>
Prefs
   1/*
   2 * Support of MSI, HPET and DMAR interrupts.
   3 *
   4 * Copyright (C) 1997, 1998, 1999, 2000, 2009 Ingo Molnar, Hajnalka Szabo
   5 *      Moved from arch/x86/kernel/apic/io_apic.c.
   6 * Jiang Liu <jiang.liu@linux.intel.com>
   7 *      Convert to hierarchical irqdomain
   8 *
   9 * This program is free software; you can redistribute it and/or modify
  10 * it under the terms of the GNU General Public License version 2 as
  11 * published by the Free Software Foundation.
  12 */
  13#include <linux/mm.h>
  14#include <linux/interrupt.h>
  15#include <linux/pci.h>
  16#include <linux/dmar.h>
  17#include <linux/hpet.h>
  18#include <linux/msi.h>
  19#include <asm/irqdomain.h>
  20#include <asm/msidef.h>
  21#include <asm/hpet.h>
  22#include <asm/hw_irq.h>
  23#include <asm/apic.h>
  24#include <asm/irq_remapping.h>
  25
  26static struct irq_domain *msi_default_domain;
  27
  28static void irq_msi_compose_msg(struct irq_data *data, struct msi_msg *msg)
  29{
  30        struct irq_cfg *cfg = irqd_cfg(data);
  31
  32        msg->address_hi = MSI_ADDR_BASE_HI;
  33
  34        if (x2apic_enabled())
  35                msg->address_hi |= MSI_ADDR_EXT_DEST_ID(cfg->dest_apicid);
  36
  37        msg->address_lo =
  38                MSI_ADDR_BASE_LO |
  39                ((apic->irq_dest_mode == 0) ?
  40                        MSI_ADDR_DEST_MODE_PHYSICAL :
  41                        MSI_ADDR_DEST_MODE_LOGICAL) |
  42                MSI_ADDR_REDIRECTION_CPU |
  43                MSI_ADDR_DEST_ID(cfg->dest_apicid);
  44
  45        msg->data =
  46                MSI_DATA_TRIGGER_EDGE |
  47                MSI_DATA_LEVEL_ASSERT |
  48                MSI_DATA_DELIVERY_FIXED |
  49                MSI_DATA_VECTOR(cfg->vector);
  50}
  51
  52/*
  53 * IRQ Chip for MSI PCI/PCI-X/PCI-Express Devices,
  54 * which implement the MSI or MSI-X Capability Structure.
  55 */
  56static struct irq_chip pci_msi_controller = {
  57        .name                   = "PCI-MSI",
  58        .irq_unmask             = pci_msi_unmask_irq,
  59        .irq_mask               = pci_msi_mask_irq,
  60        .irq_ack                = irq_chip_ack_parent,
  61        .irq_retrigger          = irq_chip_retrigger_hierarchy,
  62        .irq_compose_msi_msg    = irq_msi_compose_msg,
  63        .flags                  = IRQCHIP_SKIP_SET_WAKE,
  64};
  65
  66int native_setup_msi_irqs(struct pci_dev *dev, int nvec, int type)
  67{
  68        struct irq_domain *domain;
  69        struct irq_alloc_info info;
  70
  71        init_irq_alloc_info(&info, NULL);
  72        info.type = X86_IRQ_ALLOC_TYPE_MSI;
  73        info.msi_dev = dev;
  74
  75        domain = irq_remapping_get_irq_domain(&info);
  76        if (domain == NULL)
  77                domain = msi_default_domain;
  78        if (domain == NULL)
  79                return -ENOSYS;
  80
  81        return msi_domain_alloc_irqs(domain, &dev->dev, nvec);
  82}
  83
  84void native_teardown_msi_irq(unsigned int irq)
  85{
  86        irq_domain_free_irqs(irq, 1);
  87}
  88
  89static irq_hw_number_t pci_msi_get_hwirq(struct msi_domain_info *info,
  90                                         msi_alloc_info_t *arg)
  91{
  92        return arg->msi_hwirq;
  93}
  94
  95int pci_msi_prepare(struct irq_domain *domain, struct device *dev, int nvec,
  96                    msi_alloc_info_t *arg)
  97{
  98        struct pci_dev *pdev = to_pci_dev(dev);
  99        struct msi_desc *desc = first_pci_msi_entry(pdev);
 100
 101        init_irq_alloc_info(arg, NULL);
 102        arg->msi_dev = pdev;
 103        if (desc->msi_attrib.is_msix) {
 104                arg->type = X86_IRQ_ALLOC_TYPE_MSIX;
 105        } else {
 106                arg->type = X86_IRQ_ALLOC_TYPE_MSI;
 107                arg->flags |= X86_IRQ_ALLOC_CONTIGUOUS_VECTORS;
 108        }
 109
 110        return 0;
 111}
 112EXPORT_SYMBOL_GPL(pci_msi_prepare);
 113
 114void pci_msi_set_desc(msi_alloc_info_t *arg, struct msi_desc *desc)
 115{
 116        arg->msi_hwirq = pci_msi_domain_calc_hwirq(arg->msi_dev, desc);
 117}
 118EXPORT_SYMBOL_GPL(pci_msi_set_desc);
 119
 120static struct msi_domain_ops pci_msi_domain_ops = {
 121        .get_hwirq      = pci_msi_get_hwirq,
 122        .msi_prepare    = pci_msi_prepare,
 123        .set_desc       = pci_msi_set_desc,
 124};
 125
 126static struct msi_domain_info pci_msi_domain_info = {
 127        .flags          = MSI_FLAG_USE_DEF_DOM_OPS | MSI_FLAG_USE_DEF_CHIP_OPS |
 128                          MSI_FLAG_PCI_MSIX,
 129        .ops            = &pci_msi_domain_ops,
 130        .chip           = &pci_msi_controller,
 131        .handler        = handle_edge_irq,
 132        .handler_name   = "edge",
 133};
 134
 135void __init arch_init_msi_domain(struct irq_domain *parent)
 136{
 137        struct fwnode_handle *fn;
 138
 139        if (disable_apic)
 140                return;
 141
 142        fn = irq_domain_alloc_named_fwnode("PCI-MSI");
 143        if (fn) {
 144                msi_default_domain =
 145                        pci_msi_create_irq_domain(fn, &pci_msi_domain_info,
 146                                                  parent);
 147                irq_domain_free_fwnode(fn);
 148        }
 149        if (!msi_default_domain)
 150                pr_warn("failed to initialize irqdomain for MSI/MSI-x.\n");
 151}
 152
 153#ifdef CONFIG_IRQ_REMAP
 154static struct irq_chip pci_msi_ir_controller = {
 155        .name                   = "IR-PCI-MSI",
 156        .irq_unmask             = pci_msi_unmask_irq,
 157        .irq_mask               = pci_msi_mask_irq,
 158        .irq_ack                = irq_chip_ack_parent,
 159        .irq_retrigger          = irq_chip_retrigger_hierarchy,
 160        .irq_set_vcpu_affinity  = irq_chip_set_vcpu_affinity_parent,
 161        .flags                  = IRQCHIP_SKIP_SET_WAKE,
 162};
 163
 164static struct msi_domain_info pci_msi_ir_domain_info = {
 165        .flags          = MSI_FLAG_USE_DEF_DOM_OPS | MSI_FLAG_USE_DEF_CHIP_OPS |
 166                          MSI_FLAG_MULTI_PCI_MSI | MSI_FLAG_PCI_MSIX,
 167        .ops            = &pci_msi_domain_ops,
 168        .chip           = &pci_msi_ir_controller,
 169        .handler        = handle_edge_irq,
 170        .handler_name   = "edge",
 171};
 172
 173struct irq_domain *arch_create_remap_msi_irq_domain(struct irq_domain *parent,
 174                                                    const char *name, int id)
 175{
 176        struct fwnode_handle *fn;
 177        struct irq_domain *d;
 178
 179        fn = irq_domain_alloc_named_id_fwnode(name, id);
 180        if (!fn)
 181                return NULL;
 182        d = pci_msi_create_irq_domain(fn, &pci_msi_ir_domain_info, parent);
 183        irq_domain_free_fwnode(fn);
 184        return d;
 185}
 186#endif
 187
 188#ifdef CONFIG_DMAR_TABLE
 189static void dmar_msi_write_msg(struct irq_data *data, struct msi_msg *msg)
 190{
 191        dmar_msi_write(data->irq, msg);
 192}
 193
 194static struct irq_chip dmar_msi_controller = {
 195        .name                   = "DMAR-MSI",
 196        .irq_unmask             = dmar_msi_unmask,
 197        .irq_mask               = dmar_msi_mask,
 198        .irq_ack                = irq_chip_ack_parent,
 199        .irq_set_affinity       = msi_domain_set_affinity,
 200        .irq_retrigger          = irq_chip_retrigger_hierarchy,
 201        .irq_compose_msi_msg    = irq_msi_compose_msg,
 202        .irq_write_msi_msg      = dmar_msi_write_msg,
 203        .flags                  = IRQCHIP_SKIP_SET_WAKE,
 204};
 205
 206static irq_hw_number_t dmar_msi_get_hwirq(struct msi_domain_info *info,
 207                                          msi_alloc_info_t *arg)
 208{
 209        return arg->dmar_id;
 210}
 211
 212static int dmar_msi_init(struct irq_domain *domain,
 213                         struct msi_domain_info *info, unsigned int virq,
 214                         irq_hw_number_t hwirq, msi_alloc_info_t *arg)
 215{
 216        irq_domain_set_info(domain, virq, arg->dmar_id, info->chip, NULL,
 217                            handle_edge_irq, arg->dmar_data, "edge");
 218
 219        return 0;
 220}
 221
 222static struct msi_domain_ops dmar_msi_domain_ops = {
 223        .get_hwirq      = dmar_msi_get_hwirq,
 224        .msi_init       = dmar_msi_init,
 225};
 226
 227static struct msi_domain_info dmar_msi_domain_info = {
 228        .ops            = &dmar_msi_domain_ops,
 229        .chip           = &dmar_msi_controller,
 230};
 231
 232static struct irq_domain *dmar_get_irq_domain(void)
 233{
 234        static struct irq_domain *dmar_domain;
 235        static DEFINE_MUTEX(dmar_lock);
 236        struct fwnode_handle *fn;
 237
 238        mutex_lock(&dmar_lock);
 239        if (dmar_domain)
 240                goto out;
 241
 242        fn = irq_domain_alloc_named_fwnode("DMAR-MSI");
 243        if (fn) {
 244                dmar_domain = msi_create_irq_domain(fn, &dmar_msi_domain_info,
 245                                                    x86_vector_domain);
 246                irq_domain_free_fwnode(fn);
 247        }
 248out:
 249        mutex_unlock(&dmar_lock);
 250        return dmar_domain;
 251}
 252
 253int dmar_alloc_hwirq(int id, int node, void *arg)
 254{
 255        struct irq_domain *domain = dmar_get_irq_domain();
 256        struct irq_alloc_info info;
 257
 258        if (!domain)
 259                return -1;
 260
 261        init_irq_alloc_info(&info, NULL);
 262        info.type = X86_IRQ_ALLOC_TYPE_DMAR;
 263        info.dmar_id = id;
 264        info.dmar_data = arg;
 265
 266        return irq_domain_alloc_irqs(domain, 1, node, &info);
 267}
 268
 269void dmar_free_hwirq(int irq)
 270{
 271        irq_domain_free_irqs(irq, 1);
 272}
 273#endif
 274
 275/*
 276 * MSI message composition
 277 */
 278#ifdef CONFIG_HPET_TIMER
 279static inline int hpet_dev_id(struct irq_domain *domain)
 280{
 281        struct msi_domain_info *info = msi_get_domain_info(domain);
 282
 283        return (int)(long)info->data;
 284}
 285
 286static void hpet_msi_write_msg(struct irq_data *data, struct msi_msg *msg)
 287{
 288        hpet_msi_write(irq_data_get_irq_handler_data(data), msg);
 289}
 290
 291static struct irq_chip hpet_msi_controller __ro_after_init = {
 292        .name = "HPET-MSI",
 293        .irq_unmask = hpet_msi_unmask,
 294        .irq_mask = hpet_msi_mask,
 295        .irq_ack = irq_chip_ack_parent,
 296        .irq_set_affinity = msi_domain_set_affinity,
 297        .irq_retrigger = irq_chip_retrigger_hierarchy,
 298        .irq_compose_msi_msg = irq_msi_compose_msg,
 299        .irq_write_msi_msg = hpet_msi_write_msg,
 300        .flags = IRQCHIP_SKIP_SET_WAKE,
 301};
 302
 303static irq_hw_number_t hpet_msi_get_hwirq(struct msi_domain_info *info,
 304                                          msi_alloc_info_t *arg)
 305{
 306        return arg->hpet_index;
 307}
 308
 309static int hpet_msi_init(struct irq_domain *domain,
 310                         struct msi_domain_info *info, unsigned int virq,
 311                         irq_hw_number_t hwirq, msi_alloc_info_t *arg)
 312{
 313        irq_set_status_flags(virq, IRQ_MOVE_PCNTXT);
 314        irq_domain_set_info(domain, virq, arg->hpet_index, info->chip, NULL,
 315                            handle_edge_irq, arg->hpet_data, "edge");
 316
 317        return 0;
 318}
 319
 320static void hpet_msi_free(struct irq_domain *domain,
 321                          struct msi_domain_info *info, unsigned int virq)
 322{
 323        irq_clear_status_flags(virq, IRQ_MOVE_PCNTXT);
 324}
 325
 326static struct msi_domain_ops hpet_msi_domain_ops = {
 327        .get_hwirq      = hpet_msi_get_hwirq,
 328        .msi_init       = hpet_msi_init,
 329        .msi_free       = hpet_msi_free,
 330};
 331
 332static struct msi_domain_info hpet_msi_domain_info = {
 333        .ops            = &hpet_msi_domain_ops,
 334        .chip           = &hpet_msi_controller,
 335};
 336
 337struct irq_domain *hpet_create_irq_domain(int hpet_id)
 338{
 339        struct msi_domain_info *domain_info;
 340        struct irq_domain *parent, *d;
 341        struct irq_alloc_info info;
 342        struct fwnode_handle *fn;
 343
 344        if (x86_vector_domain == NULL)
 345                return NULL;
 346
 347        domain_info = kzalloc(sizeof(*domain_info), GFP_KERNEL);
 348        if (!domain_info)
 349                return NULL;
 350
 351        *domain_info = hpet_msi_domain_info;
 352        domain_info->data = (void *)(long)hpet_id;
 353
 354        init_irq_alloc_info(&info, NULL);
 355        info.type = X86_IRQ_ALLOC_TYPE_HPET;
 356        info.hpet_id = hpet_id;
 357        parent = irq_remapping_get_ir_irq_domain(&info);
 358        if (parent == NULL)
 359                parent = x86_vector_domain;
 360        else
 361                hpet_msi_controller.name = "IR-HPET-MSI";
 362
 363        fn = irq_domain_alloc_named_id_fwnode(hpet_msi_controller.name,
 364                                              hpet_id);
 365        if (!fn) {
 366                kfree(domain_info);
 367                return NULL;
 368        }
 369
 370        d = msi_create_irq_domain(fn, domain_info, parent);
 371        irq_domain_free_fwnode(fn);
 372        return d;
 373}
 374
 375int hpet_assign_irq(struct irq_domain *domain, struct hpet_dev *dev,
 376                    int dev_num)
 377{
 378        struct irq_alloc_info info;
 379
 380        init_irq_alloc_info(&info, NULL);
 381        info.type = X86_IRQ_ALLOC_TYPE_HPET;
 382        info.hpet_data = dev;
 383        info.hpet_id = hpet_dev_id(domain);
 384        info.hpet_index = dev_num;
 385
 386        return irq_domain_alloc_irqs(domain, 1, NUMA_NO_NODE, &info);
 387}
 388#endif
 389