Commit f3cf8bb0d6c3c11ddedf01f02f856f2ae8c33aa4

Authored by Jiang Liu
Committed by Thomas Gleixner
1 parent 9dde55b72d

genirq: Add generic msi irq domain support

Implement the basic functions for MSI interrupt support with
hierarchical interrupt domains.

[ tglx: Extracted and combined from several patches ]

Signed-off-by: Jiang Liu <jiang.liu@linux.intel.com>
Cc: Bjorn Helgaas <bhelgaas@google.com>
Cc: Grant Likely <grant.likely@linaro.org>
Cc: Marc Zyngier <marc.zyngier@arm.com>
Cc: Yingjoe Chen <yingjoe.chen@mediatek.com>
Cc: Yijing Wang <wangyijing@huawei.com>
Signed-off-by: Thomas Gleixner <tglx@linutronix.de>

Showing 4 changed files with 197 additions and 0 deletions Side-by-side Diff

... ... @@ -114,5 +114,50 @@
114 114 void (*teardown_irq)(struct msi_controller *chip, unsigned int irq);
115 115 };
116 116  
  117 +#ifdef CONFIG_GENERIC_MSI_IRQ_DOMAIN
  118 +struct irq_domain;
  119 +struct irq_chip;
  120 +struct device_node;
  121 +struct msi_domain_info;
  122 +
  123 +/**
  124 + * struct msi_domain_ops - MSI interrupt domain callbacks
  125 + * @get_hwirq: Retrieve the resulting hw irq number
  126 + * @msi_init: Domain specific init function for MSI interrupts
  127 + * @msi_free: Domain specific function to free a MSI interrupts
  128 + */
  129 +struct msi_domain_ops {
  130 + irq_hw_number_t (*get_hwirq)(struct msi_domain_info *info, void *arg);
  131 + int (*msi_init)(struct irq_domain *domain,
  132 + struct msi_domain_info *info,
  133 + unsigned int virq, irq_hw_number_t hwirq,
  134 + void *arg);
  135 + void (*msi_free)(struct irq_domain *domain,
  136 + struct msi_domain_info *info,
  137 + unsigned int virq);
  138 +};
  139 +
  140 +/**
  141 + * struct msi_domain_info - MSI interrupt domain data
  142 + * @ops: The callback data structure
  143 + * @chip: The associated interrupt chip
  144 + * @data: Domain specific data
  145 + */
  146 +struct msi_domain_info {
  147 + struct msi_domain_ops *ops;
  148 + struct irq_chip *chip;
  149 + void *data;
  150 +};
  151 +
  152 +int msi_domain_set_affinity(struct irq_data *data, const struct cpumask *mask,
  153 + bool force);
  154 +
  155 +struct irq_domain *msi_create_irq_domain(struct device_node *of_node,
  156 + struct msi_domain_info *info,
  157 + struct irq_domain *parent);
  158 +struct msi_domain_info *msi_get_domain_info(struct irq_domain *domain);
  159 +
  160 +#endif /* CONFIG_GENERIC_MSI_IRQ_DOMAIN */
  161 +
117 162 #endif /* LINUX_MSI_H */
... ... @@ -60,6 +60,16 @@
60 60 bool
61 61 select IRQ_DOMAIN
62 62  
  63 +# Generic MSI interrupt support
  64 +config GENERIC_MSI_IRQ
  65 + bool
  66 +
  67 +# Generic MSI hierarchical interrupt domain support
  68 +config GENERIC_MSI_IRQ_DOMAIN
  69 + bool
  70 + select IRQ_DOMAIN_HIERARCHY
  71 + select GENERIC_MSI_IRQ
  72 +
63 73 config HANDLE_DOMAIN_IRQ
64 74 bool
65 75  
... ... @@ -6,4 +6,5 @@
6 6 obj-$(CONFIG_PROC_FS) += proc.o
7 7 obj-$(CONFIG_GENERIC_PENDING_IRQ) += migration.o
8 8 obj-$(CONFIG_PM_SLEEP) += pm.o
  9 +obj-$(CONFIG_GENERIC_MSI_IRQ) += msi.o
  1 +/*
  2 + * linux/kernel/irq/msi.c
  3 + *
  4 + * Copyright (C) 2014 Intel Corp.
  5 + * Author: Jiang Liu <jiang.liu@linux.intel.com>
  6 + *
  7 + * This file is licensed under GPLv2.
  8 + *
  9 + * This file contains common code to support Message Signalled Interrupt for
  10 + * PCI compatible and non PCI compatible devices.
  11 + */
  12 +#include <linux/irq.h>
  13 +#include <linux/irqdomain.h>
  14 +#include <linux/msi.h>
  15 +
  16 +#ifdef CONFIG_GENERIC_MSI_IRQ_DOMAIN
  17 +/**
  18 + * msi_domain_set_affinity - Generic affinity setter function for MSI domains
  19 + * @irq_data: The irq data associated to the interrupt
  20 + * @mask: The affinity mask to set
  21 + * @force: Flag to enforce setting (disable online checks)
  22 + *
  23 + * Intended to be used by MSI interrupt controllers which are
  24 + * implemented with hierarchical domains.
  25 + */
  26 +int msi_domain_set_affinity(struct irq_data *irq_data,
  27 + const struct cpumask *mask, bool force)
  28 +{
  29 + struct irq_data *parent = irq_data->parent_data;
  30 + struct msi_msg msg;
  31 + int ret;
  32 +
  33 + ret = parent->chip->irq_set_affinity(parent, mask, force);
  34 + if (ret >= 0 && ret != IRQ_SET_MASK_OK_DONE) {
  35 + BUG_ON(irq_chip_compose_msi_msg(irq_data, &msg));
  36 + irq_chip_write_msi_msg(irq_data, &msg);
  37 + }
  38 +
  39 + return ret;
  40 +}
  41 +
  42 +static void msi_domain_activate(struct irq_domain *domain,
  43 + struct irq_data *irq_data)
  44 +{
  45 + struct msi_msg msg;
  46 +
  47 + BUG_ON(irq_chip_compose_msi_msg(irq_data, &msg));
  48 + irq_chip_write_msi_msg(irq_data, &msg);
  49 +}
  50 +
  51 +static void msi_domain_deactivate(struct irq_domain *domain,
  52 + struct irq_data *irq_data)
  53 +{
  54 + struct msi_msg msg;
  55 +
  56 + memset(&msg, 0, sizeof(msg));
  57 + irq_chip_write_msi_msg(irq_data, &msg);
  58 +}
  59 +
  60 +static int msi_domain_alloc(struct irq_domain *domain, unsigned int virq,
  61 + unsigned int nr_irqs, void *arg)
  62 +{
  63 + struct msi_domain_info *info = domain->host_data;
  64 + struct msi_domain_ops *ops = info->ops;
  65 + irq_hw_number_t hwirq = ops->get_hwirq(info, arg);
  66 + int i, ret;
  67 +
  68 + if (irq_find_mapping(domain, hwirq) > 0)
  69 + return -EEXIST;
  70 +
  71 + ret = irq_domain_alloc_irqs_parent(domain, virq, nr_irqs, arg);
  72 + if (ret < 0)
  73 + return ret;
  74 +
  75 + for (i = 0; i < nr_irqs; i++) {
  76 + ret = ops->msi_init(domain, info, virq + i, hwirq + i, arg);
  77 + if (ret < 0) {
  78 + if (ops->msi_free) {
  79 + for (i--; i > 0; i--)
  80 + ops->msi_free(domain, info, virq + i);
  81 + }
  82 + irq_domain_free_irqs_top(domain, virq, nr_irqs);
  83 + return ret;
  84 + }
  85 + }
  86 +
  87 + return 0;
  88 +}
  89 +
  90 +static void msi_domain_free(struct irq_domain *domain, unsigned int virq,
  91 + unsigned int nr_irqs)
  92 +{
  93 + struct msi_domain_info *info = domain->host_data;
  94 + int i;
  95 +
  96 + if (info->ops->msi_free) {
  97 + for (i = 0; i < nr_irqs; i++)
  98 + info->ops->msi_free(domain, info, virq + i);
  99 + }
  100 + irq_domain_free_irqs_top(domain, virq, nr_irqs);
  101 +}
  102 +
  103 +static struct irq_domain_ops msi_domain_ops = {
  104 + .alloc = msi_domain_alloc,
  105 + .free = msi_domain_free,
  106 + .activate = msi_domain_activate,
  107 + .deactivate = msi_domain_deactivate,
  108 +};
  109 +
  110 +/**
  111 + * msi_create_irq_domain - Create a MSI interrupt domain
  112 + * @of_node: Optional device-tree node of the interrupt controller
  113 + * @info: MSI domain info
  114 + * @parent: Parent irq domain
  115 + */
  116 +struct irq_domain *msi_create_irq_domain(struct device_node *of_node,
  117 + struct msi_domain_info *info,
  118 + struct irq_domain *parent)
  119 +{
  120 + struct irq_domain *domain;
  121 +
  122 + domain = irq_domain_add_tree(of_node, &msi_domain_ops, info);
  123 + if (domain)
  124 + domain->parent = parent;
  125 +
  126 + return domain;
  127 +}
  128 +
  129 +/**
  130 + * msi_get_domain_info - Get the MSI interrupt domain info for @domain
  131 + * @domain: The interrupt domain to retrieve data from
  132 + *
  133 + * Returns the pointer to the msi_domain_info stored in
  134 + * @domain->host_data.
  135 + */
  136 +struct msi_domain_info *msi_get_domain_info(struct irq_domain *domain)
  137 +{
  138 + return (struct msi_domain_info *)domain->host_data;
  139 +}
  140 +
  141 +#endif /* CONFIG_GENERIC_MSI_IRQ_DOMAIN */