Commit aeeb59657c35da64068336c20068da237f41ab76
Committed by
Thomas Gleixner
1 parent
d9109698be
Exists in
ti-lsk-linux-4.1.y
and in
10 other branches
genirq: Provide default callbacks for msi_domain_ops
Extend struct msi_domain_info and provide default callbacks for msi_domain_ops. Signed-off-by: Jiang Liu <jiang.liu@linux.intel.com> Cc: Tony Luck <tony.luck@intel.com> Cc: linux-arm-kernel@lists.infradead.org Cc: Bjorn Helgaas <bhelgaas@google.com> Cc: Grant Likely <grant.likely@linaro.org> Cc: Marc Zyngier <marc.zyngier@arm.com> Cc: Yijing Wang <wangyijing@huawei.com> Cc: Yingjoe Chen <yingjoe.chen@mediatek.com> Cc: Borislav Petkov <bp@alien8.de> Cc: Matthias Brugger <matthias.bgg@gmail.com> Cc: Alexander Gordeev <agordeev@redhat.com> Link: http://lkml.kernel.org/r/1416061447-9472-8-git-send-email-jiang.liu@linux.intel.com Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
Showing 2 changed files with 140 additions and 13 deletions Side-by-side Diff
include/linux/msi.h
... | ... | @@ -116,6 +116,7 @@ |
116 | 116 | |
117 | 117 | #ifdef CONFIG_GENERIC_MSI_IRQ_DOMAIN |
118 | 118 | |
119 | +#include <linux/irqhandler.h> | |
119 | 120 | #include <asm/msi.h> |
120 | 121 | |
121 | 122 | struct irq_domain; |
122 | 123 | |
... | ... | @@ -142,11 +143,12 @@ |
142 | 143 | * interfaces which are based on msi_desc. |
143 | 144 | */ |
144 | 145 | struct msi_domain_ops { |
145 | - irq_hw_number_t (*get_hwirq)(struct msi_domain_info *info, void *arg); | |
146 | + irq_hw_number_t (*get_hwirq)(struct msi_domain_info *info, | |
147 | + msi_alloc_info_t *arg); | |
146 | 148 | int (*msi_init)(struct irq_domain *domain, |
147 | 149 | struct msi_domain_info *info, |
148 | 150 | unsigned int virq, irq_hw_number_t hwirq, |
149 | - void *arg); | |
151 | + msi_alloc_info_t *arg); | |
150 | 152 | void (*msi_free)(struct irq_domain *domain, |
151 | 153 | struct msi_domain_info *info, |
152 | 154 | unsigned int virq); |
153 | 155 | |
154 | 156 | |
155 | 157 | |
... | ... | @@ -165,14 +167,44 @@ |
165 | 167 | |
166 | 168 | /** |
167 | 169 | * struct msi_domain_info - MSI interrupt domain data |
168 | - * @ops: The callback data structure | |
169 | - * @chip: The associated interrupt chip | |
170 | - * @data: Domain specific data | |
170 | + * @flags: Flags to decribe features and capabilities | |
171 | + * @ops: The callback data structure | |
172 | + * @chip: Optional: associated interrupt chip | |
173 | + * @chip_data: Optional: associated interrupt chip data | |
174 | + * @handler: Optional: associated interrupt flow handler | |
175 | + * @handler_data: Optional: associated interrupt flow handler data | |
176 | + * @handler_name: Optional: associated interrupt flow handler name | |
177 | + * @data: Optional: domain specific data | |
171 | 178 | */ |
172 | 179 | struct msi_domain_info { |
180 | + u32 flags; | |
173 | 181 | struct msi_domain_ops *ops; |
174 | 182 | struct irq_chip *chip; |
183 | + void *chip_data; | |
184 | + irq_flow_handler_t handler; | |
185 | + void *handler_data; | |
186 | + const char *handler_name; | |
175 | 187 | void *data; |
188 | +}; | |
189 | + | |
190 | +/* Flags for msi_domain_info */ | |
191 | +enum { | |
192 | + /* | |
193 | + * Init non implemented ops callbacks with default MSI domain | |
194 | + * callbacks. | |
195 | + */ | |
196 | + MSI_FLAG_USE_DEF_DOM_OPS = (1 << 0), | |
197 | + /* | |
198 | + * Init non implemented chip callbacks with default MSI chip | |
199 | + * callbacks. | |
200 | + */ | |
201 | + MSI_FLAG_USE_DEF_CHIP_OPS = (1 << 1), | |
202 | + /* Build identity map between hwirq and irq */ | |
203 | + MSI_FLAG_IDENTITY_MAP = (1 << 2), | |
204 | + /* Support multiple PCI MSI interrupts */ | |
205 | + MSI_FLAG_MULTI_PCI_MSI = (1 << 3), | |
206 | + /* Support PCI MSIX interrupts */ | |
207 | + MSI_FLAG_PCI_MSIX = (1 << 4), | |
176 | 208 | }; |
177 | 209 | |
178 | 210 | int msi_domain_set_affinity(struct irq_data *data, const struct cpumask *mask, |
kernel/irq/msi.c
... | ... | @@ -9,6 +9,8 @@ |
9 | 9 | * This file contains common code to support Message Signalled Interrupt for |
10 | 10 | * PCI compatible and non PCI compatible devices. |
11 | 11 | */ |
12 | +#include <linux/types.h> | |
13 | +#include <linux/device.h> | |
12 | 14 | #include <linux/irq.h> |
13 | 15 | #include <linux/irqdomain.h> |
14 | 16 | #include <linux/msi.h> |
15 | 17 | |
16 | 18 | |
17 | 19 | |
... | ... | @@ -110,23 +112,112 @@ |
110 | 112 | .deactivate = msi_domain_deactivate, |
111 | 113 | }; |
112 | 114 | |
115 | +#ifdef GENERIC_MSI_DOMAIN_OPS | |
116 | +static irq_hw_number_t msi_domain_ops_get_hwirq(struct msi_domain_info *info, | |
117 | + msi_alloc_info_t *arg) | |
118 | +{ | |
119 | + return arg->hwirq; | |
120 | +} | |
121 | + | |
122 | +static int msi_domain_ops_prepare(struct irq_domain *domain, struct device *dev, | |
123 | + int nvec, msi_alloc_info_t *arg) | |
124 | +{ | |
125 | + memset(arg, 0, sizeof(*arg)); | |
126 | + return 0; | |
127 | +} | |
128 | + | |
129 | +static void msi_domain_ops_set_desc(msi_alloc_info_t *arg, | |
130 | + struct msi_desc *desc) | |
131 | +{ | |
132 | + arg->desc = desc; | |
133 | +} | |
134 | +#else | |
135 | +#define msi_domain_ops_get_hwirq NULL | |
136 | +#define msi_domain_ops_prepare NULL | |
137 | +#define msi_domain_ops_set_desc NULL | |
138 | +#endif /* !GENERIC_MSI_DOMAIN_OPS */ | |
139 | + | |
140 | +static int msi_domain_ops_init(struct irq_domain *domain, | |
141 | + struct msi_domain_info *info, | |
142 | + unsigned int virq, irq_hw_number_t hwirq, | |
143 | + msi_alloc_info_t *arg) | |
144 | +{ | |
145 | + irq_domain_set_hwirq_and_chip(domain, virq, hwirq, info->chip, | |
146 | + info->chip_data); | |
147 | + if (info->handler && info->handler_name) { | |
148 | + __irq_set_handler(virq, info->handler, 0, info->handler_name); | |
149 | + if (info->handler_data) | |
150 | + irq_set_handler_data(virq, info->handler_data); | |
151 | + } | |
152 | + return 0; | |
153 | +} | |
154 | + | |
155 | +static int msi_domain_ops_check(struct irq_domain *domain, | |
156 | + struct msi_domain_info *info, | |
157 | + struct device *dev) | |
158 | +{ | |
159 | + return 0; | |
160 | +} | |
161 | + | |
162 | +static struct msi_domain_ops msi_domain_ops_default = { | |
163 | + .get_hwirq = msi_domain_ops_get_hwirq, | |
164 | + .msi_init = msi_domain_ops_init, | |
165 | + .msi_check = msi_domain_ops_check, | |
166 | + .msi_prepare = msi_domain_ops_prepare, | |
167 | + .set_desc = msi_domain_ops_set_desc, | |
168 | +}; | |
169 | + | |
170 | +static void msi_domain_update_dom_ops(struct msi_domain_info *info) | |
171 | +{ | |
172 | + struct msi_domain_ops *ops = info->ops; | |
173 | + | |
174 | + if (ops == NULL) { | |
175 | + info->ops = &msi_domain_ops_default; | |
176 | + return; | |
177 | + } | |
178 | + | |
179 | + if (ops->get_hwirq == NULL) | |
180 | + ops->get_hwirq = msi_domain_ops_default.get_hwirq; | |
181 | + if (ops->msi_init == NULL) | |
182 | + ops->msi_init = msi_domain_ops_default.msi_init; | |
183 | + if (ops->msi_check == NULL) | |
184 | + ops->msi_check = msi_domain_ops_default.msi_check; | |
185 | + if (ops->msi_prepare == NULL) | |
186 | + ops->msi_prepare = msi_domain_ops_default.msi_prepare; | |
187 | + if (ops->set_desc == NULL) | |
188 | + ops->set_desc = msi_domain_ops_default.set_desc; | |
189 | +} | |
190 | + | |
191 | +static void msi_domain_update_chip_ops(struct msi_domain_info *info) | |
192 | +{ | |
193 | + struct irq_chip *chip = info->chip; | |
194 | + | |
195 | + BUG_ON(!chip); | |
196 | + if (!chip->irq_mask) | |
197 | + chip->irq_mask = pci_msi_mask_irq; | |
198 | + if (!chip->irq_unmask) | |
199 | + chip->irq_unmask = pci_msi_unmask_irq; | |
200 | + if (!chip->irq_set_affinity) | |
201 | + chip->irq_set_affinity = msi_domain_set_affinity; | |
202 | +} | |
203 | + | |
113 | 204 | /** |
114 | 205 | * msi_create_irq_domain - Create a MSI interrupt domain |
115 | 206 | * @of_node: Optional device-tree node of the interrupt controller |
116 | 207 | * @info: MSI domain info |
117 | 208 | * @parent: Parent irq domain |
118 | 209 | */ |
119 | -struct irq_domain *msi_create_irq_domain(struct device_node *of_node, | |
210 | +struct irq_domain *msi_create_irq_domain(struct device_node *node, | |
120 | 211 | struct msi_domain_info *info, |
121 | 212 | struct irq_domain *parent) |
122 | 213 | { |
123 | - struct irq_domain *domain; | |
214 | + if (info->flags & MSI_FLAG_USE_DEF_DOM_OPS) | |
215 | + msi_domain_update_dom_ops(info); | |
216 | + if (info->flags & MSI_FLAG_USE_DEF_CHIP_OPS) | |
217 | + msi_domain_update_chip_ops(info); | |
124 | 218 | |
125 | - domain = irq_domain_add_tree(of_node, &msi_domain_ops, info); | |
126 | - if (domain) | |
127 | - domain->parent = parent; | |
128 | - | |
129 | - return domain; | |
219 | + return irq_domain_add_hierarchy(parent, 0, 0, node, &msi_domain_ops, | |
220 | + info); | |
130 | 221 | } |
131 | 222 | |
132 | 223 | /** |
133 | 224 | |
... | ... | @@ -155,8 +246,12 @@ |
155 | 246 | |
156 | 247 | for_each_msi_entry(desc, dev) { |
157 | 248 | ops->set_desc(&arg, desc); |
249 | + if (info->flags & MSI_FLAG_IDENTITY_MAP) | |
250 | + virq = (int)ops->get_hwirq(info, &arg); | |
251 | + else | |
252 | + virq = -1; | |
158 | 253 | |
159 | - virq = __irq_domain_alloc_irqs(domain, -1, desc->nvec_used, | |
254 | + virq = __irq_domain_alloc_irqs(domain, virq, desc->nvec_used, | |
160 | 255 | dev_to_node(dev), &arg, false); |
161 | 256 | if (virq < 0) { |
162 | 257 | ret = -ENOSPC; |