Blame view

kernel/irq/msi.c 13.7 KB
52a65ff56   Thomas Gleixner   genirq: Add missi...
1
  // SPDX-License-Identifier: GPL-2.0
f3cf8bb0d   Jiang Liu   genirq: Add gener...
2
  /*
f3cf8bb0d   Jiang Liu   genirq: Add gener...
3
4
5
6
7
8
9
10
   * Copyright (C) 2014 Intel Corp.
   * Author: Jiang Liu <jiang.liu@linux.intel.com>
   *
   * This file is licensed under GPLv2.
   *
   * This file contains common code to support Message Signalled Interrupt for
   * PCI compatible and non PCI compatible devices.
   */
aeeb59657   Jiang Liu   genirq: Provide d...
11
12
  #include <linux/types.h>
  #include <linux/device.h>
f3cf8bb0d   Jiang Liu   genirq: Add gener...
13
14
15
  #include <linux/irq.h>
  #include <linux/irqdomain.h>
  #include <linux/msi.h>
4e2015664   Marc Zyngier   genirq/msi: Drop ...
16
  #include <linux/slab.h>
d9109698b   Jiang Liu   genirq: Introduce...
17

07557ccb8   Thomas Gleixner   genirq/msi: Captu...
18
  #include "internals.h"
28f4b0414   Thomas Gleixner   genirq/msi: Add c...
19
20
21
22
23
24
  /**
   * alloc_msi_entry - Allocate an initialize msi_entry
   * @dev:	Pointer to the device for which this is allocated
   * @nvec:	The number of vectors used in this entry
   * @affinity:	Optional pointer to an affinity mask array size of @nvec
   *
bec04037e   Dou Liyang   genirq/core: Intr...
25
26
   * If @affinity is not NULL then an affinity array[@nvec] is allocated
   * and the affinity masks and flags from @affinity are copied.
28f4b0414   Thomas Gleixner   genirq/msi: Add c...
27
   */
bec04037e   Dou Liyang   genirq/core: Intr...
28
29
  struct msi_desc *alloc_msi_entry(struct device *dev, int nvec,
  				 const struct irq_affinity_desc *affinity)
aa48b6f70   Jiang Liu   genirq/MSI: Move ...
30
  {
28f4b0414   Thomas Gleixner   genirq/msi: Add c...
31
32
33
  	struct msi_desc *desc;
  
  	desc = kzalloc(sizeof(*desc), GFP_KERNEL);
aa48b6f70   Jiang Liu   genirq/MSI: Move ...
34
35
36
37
38
  	if (!desc)
  		return NULL;
  
  	INIT_LIST_HEAD(&desc->list);
  	desc->dev = dev;
28f4b0414   Thomas Gleixner   genirq/msi: Add c...
39
40
41
42
43
44
45
46
47
  	desc->nvec_used = nvec;
  	if (affinity) {
  		desc->affinity = kmemdup(affinity,
  			nvec * sizeof(*desc->affinity), GFP_KERNEL);
  		if (!desc->affinity) {
  			kfree(desc);
  			return NULL;
  		}
  	}
aa48b6f70   Jiang Liu   genirq/MSI: Move ...
48
49
50
51
52
53
  
  	return desc;
  }
  
  void free_msi_entry(struct msi_desc *entry)
  {
28f4b0414   Thomas Gleixner   genirq/msi: Add c...
54
  	kfree(entry->affinity);
aa48b6f70   Jiang Liu   genirq/MSI: Move ...
55
56
  	kfree(entry);
  }
38b6a1cf3   Jiang Liu   PCI/MSI: Move cac...
57
58
59
60
61
62
63
64
65
66
67
68
  void __get_cached_msi_msg(struct msi_desc *entry, struct msi_msg *msg)
  {
  	*msg = entry->msg;
  }
  
  void get_cached_msi_msg(unsigned int irq, struct msi_msg *msg)
  {
  	struct msi_desc *entry = irq_get_msi_desc(irq);
  
  	__get_cached_msi_msg(entry, msg);
  }
  EXPORT_SYMBOL_GPL(get_cached_msi_msg);
f3cf8bb0d   Jiang Liu   genirq: Add gener...
69
  #ifdef CONFIG_GENERIC_MSI_IRQ_DOMAIN
74faaf7aa   Thomas Gleixner   genirq: Move irq_...
70
71
72
73
74
  static inline void irq_chip_write_msi_msg(struct irq_data *data,
  					  struct msi_msg *msg)
  {
  	data->chip->irq_write_msi_msg(data, msg);
  }
0be8153cb   Marc Zyngier   genirq/msi: Allow...
75
76
77
78
79
80
81
82
83
84
85
86
  static void msi_check_level(struct irq_domain *domain, struct msi_msg *msg)
  {
  	struct msi_domain_info *info = domain->host_data;
  
  	/*
  	 * If the MSI provider has messed with the second message and
  	 * not advertized that it is level-capable, signal the breakage.
  	 */
  	WARN_ON(!((info->flags & MSI_FLAG_LEVEL_CAPABLE) &&
  		  (info->chip->flags & IRQCHIP_SUPPORTS_LEVEL_MSI)) &&
  		(msg[1].address_lo || msg[1].address_hi || msg[1].data));
  }
f3cf8bb0d   Jiang Liu   genirq: Add gener...
87
88
89
90
91
92
93
94
95
96
97
98
99
  /**
   * msi_domain_set_affinity - Generic affinity setter function for MSI domains
   * @irq_data:	The irq data associated to the interrupt
   * @mask:	The affinity mask to set
   * @force:	Flag to enforce setting (disable online checks)
   *
   * Intended to be used by MSI interrupt controllers which are
   * implemented with hierarchical domains.
   */
  int msi_domain_set_affinity(struct irq_data *irq_data,
  			    const struct cpumask *mask, bool force)
  {
  	struct irq_data *parent = irq_data->parent_data;
0be8153cb   Marc Zyngier   genirq/msi: Allow...
100
  	struct msi_msg msg[2] = { [1] = { }, };
f3cf8bb0d   Jiang Liu   genirq: Add gener...
101
102
103
104
  	int ret;
  
  	ret = parent->chip->irq_set_affinity(parent, mask, force);
  	if (ret >= 0 && ret != IRQ_SET_MASK_OK_DONE) {
0be8153cb   Marc Zyngier   genirq/msi: Allow...
105
106
107
  		BUG_ON(irq_chip_compose_msi_msg(irq_data, msg));
  		msi_check_level(irq_data->domain, msg);
  		irq_chip_write_msi_msg(irq_data, msg);
f3cf8bb0d   Jiang Liu   genirq: Add gener...
108
109
110
111
  	}
  
  	return ret;
  }
724916434   Thomas Gleixner   genirq/irqdomain:...
112
113
  static int msi_domain_activate(struct irq_domain *domain,
  			       struct irq_data *irq_data, bool early)
f3cf8bb0d   Jiang Liu   genirq: Add gener...
114
  {
0be8153cb   Marc Zyngier   genirq/msi: Allow...
115
  	struct msi_msg msg[2] = { [1] = { }, };
f3cf8bb0d   Jiang Liu   genirq: Add gener...
116

0be8153cb   Marc Zyngier   genirq/msi: Allow...
117
118
119
  	BUG_ON(irq_chip_compose_msi_msg(irq_data, msg));
  	msi_check_level(irq_data->domain, msg);
  	irq_chip_write_msi_msg(irq_data, msg);
724916434   Thomas Gleixner   genirq/irqdomain:...
120
  	return 0;
f3cf8bb0d   Jiang Liu   genirq: Add gener...
121
122
123
124
125
  }
  
  static void msi_domain_deactivate(struct irq_domain *domain,
  				  struct irq_data *irq_data)
  {
0be8153cb   Marc Zyngier   genirq/msi: Allow...
126
  	struct msi_msg msg[2];
f3cf8bb0d   Jiang Liu   genirq: Add gener...
127

0be8153cb   Marc Zyngier   genirq/msi: Allow...
128
129
  	memset(msg, 0, sizeof(msg));
  	irq_chip_write_msi_msg(irq_data, msg);
f3cf8bb0d   Jiang Liu   genirq: Add gener...
130
131
132
133
134
135
136
137
138
139
140
141
  }
  
  static int msi_domain_alloc(struct irq_domain *domain, unsigned int virq,
  			    unsigned int nr_irqs, void *arg)
  {
  	struct msi_domain_info *info = domain->host_data;
  	struct msi_domain_ops *ops = info->ops;
  	irq_hw_number_t hwirq = ops->get_hwirq(info, arg);
  	int i, ret;
  
  	if (irq_find_mapping(domain, hwirq) > 0)
  		return -EEXIST;
bf6f869f8   Liu Jiang   genirq/MSI: Relax...
142
143
144
145
146
  	if (domain->parent) {
  		ret = irq_domain_alloc_irqs_parent(domain, virq, nr_irqs, arg);
  		if (ret < 0)
  			return ret;
  	}
f3cf8bb0d   Jiang Liu   genirq: Add gener...
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
  
  	for (i = 0; i < nr_irqs; i++) {
  		ret = ops->msi_init(domain, info, virq + i, hwirq + i, arg);
  		if (ret < 0) {
  			if (ops->msi_free) {
  				for (i--; i > 0; i--)
  					ops->msi_free(domain, info, virq + i);
  			}
  			irq_domain_free_irqs_top(domain, virq, nr_irqs);
  			return ret;
  		}
  	}
  
  	return 0;
  }
  
  static void msi_domain_free(struct irq_domain *domain, unsigned int virq,
  			    unsigned int nr_irqs)
  {
  	struct msi_domain_info *info = domain->host_data;
  	int i;
  
  	if (info->ops->msi_free) {
  		for (i = 0; i < nr_irqs; i++)
  			info->ops->msi_free(domain, info, virq + i);
  	}
  	irq_domain_free_irqs_top(domain, virq, nr_irqs);
  }
01364028b   Krzysztof Kozlowski   genirq: MSI: Cons...
175
  static const struct irq_domain_ops msi_domain_ops = {
f3cf8bb0d   Jiang Liu   genirq: Add gener...
176
177
178
179
180
  	.alloc		= msi_domain_alloc,
  	.free		= msi_domain_free,
  	.activate	= msi_domain_activate,
  	.deactivate	= msi_domain_deactivate,
  };
aeeb59657   Jiang Liu   genirq: Provide d...
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
  #ifdef GENERIC_MSI_DOMAIN_OPS
  static irq_hw_number_t msi_domain_ops_get_hwirq(struct msi_domain_info *info,
  						msi_alloc_info_t *arg)
  {
  	return arg->hwirq;
  }
  
  static int msi_domain_ops_prepare(struct irq_domain *domain, struct device *dev,
  				  int nvec, msi_alloc_info_t *arg)
  {
  	memset(arg, 0, sizeof(*arg));
  	return 0;
  }
  
  static void msi_domain_ops_set_desc(msi_alloc_info_t *arg,
  				    struct msi_desc *desc)
  {
  	arg->desc = desc;
  }
  #else
  #define msi_domain_ops_get_hwirq	NULL
  #define msi_domain_ops_prepare		NULL
  #define msi_domain_ops_set_desc		NULL
  #endif /* !GENERIC_MSI_DOMAIN_OPS */
  
  static int msi_domain_ops_init(struct irq_domain *domain,
  			       struct msi_domain_info *info,
  			       unsigned int virq, irq_hw_number_t hwirq,
  			       msi_alloc_info_t *arg)
  {
  	irq_domain_set_hwirq_and_chip(domain, virq, hwirq, info->chip,
  				      info->chip_data);
  	if (info->handler && info->handler_name) {
  		__irq_set_handler(virq, info->handler, 0, info->handler_name);
  		if (info->handler_data)
  			irq_set_handler_data(virq, info->handler_data);
  	}
  	return 0;
  }
  
  static int msi_domain_ops_check(struct irq_domain *domain,
  				struct msi_domain_info *info,
  				struct device *dev)
  {
  	return 0;
  }
  
  static struct msi_domain_ops msi_domain_ops_default = {
  	.get_hwirq	= msi_domain_ops_get_hwirq,
  	.msi_init	= msi_domain_ops_init,
  	.msi_check	= msi_domain_ops_check,
  	.msi_prepare	= msi_domain_ops_prepare,
  	.set_desc	= msi_domain_ops_set_desc,
  };
  
  static void msi_domain_update_dom_ops(struct msi_domain_info *info)
  {
  	struct msi_domain_ops *ops = info->ops;
  
  	if (ops == NULL) {
  		info->ops = &msi_domain_ops_default;
  		return;
  	}
  
  	if (ops->get_hwirq == NULL)
  		ops->get_hwirq = msi_domain_ops_default.get_hwirq;
  	if (ops->msi_init == NULL)
  		ops->msi_init = msi_domain_ops_default.msi_init;
  	if (ops->msi_check == NULL)
  		ops->msi_check = msi_domain_ops_default.msi_check;
  	if (ops->msi_prepare == NULL)
  		ops->msi_prepare = msi_domain_ops_default.msi_prepare;
  	if (ops->set_desc == NULL)
  		ops->set_desc = msi_domain_ops_default.set_desc;
  }
  
  static void msi_domain_update_chip_ops(struct msi_domain_info *info)
  {
  	struct irq_chip *chip = info->chip;
0701c53e4   Marc Zyngier   genirq/msi: Do no...
260
  	BUG_ON(!chip || !chip->irq_mask || !chip->irq_unmask);
aeeb59657   Jiang Liu   genirq: Provide d...
261
262
263
  	if (!chip->irq_set_affinity)
  		chip->irq_set_affinity = msi_domain_set_affinity;
  }
f3cf8bb0d   Jiang Liu   genirq: Add gener...
264
265
  /**
   * msi_create_irq_domain - Create a MSI interrupt domain
be5436c83   Marc Zyngier   irqdomain/msi: Us...
266
   * @fwnode:	Optional fwnode of the interrupt controller
f3cf8bb0d   Jiang Liu   genirq: Add gener...
267
268
269
   * @info:	MSI domain info
   * @parent:	Parent irq domain
   */
be5436c83   Marc Zyngier   irqdomain/msi: Us...
270
  struct irq_domain *msi_create_irq_domain(struct fwnode_handle *fwnode,
f3cf8bb0d   Jiang Liu   genirq: Add gener...
271
272
273
  					 struct msi_domain_info *info,
  					 struct irq_domain *parent)
  {
a97b852b4   Marc Zyngier   genirq/msi: Popul...
274
  	struct irq_domain *domain;
aeeb59657   Jiang Liu   genirq: Provide d...
275
276
277
278
  	if (info->flags & MSI_FLAG_USE_DEF_DOM_OPS)
  		msi_domain_update_dom_ops(info);
  	if (info->flags & MSI_FLAG_USE_DEF_CHIP_OPS)
  		msi_domain_update_chip_ops(info);
f3cf8bb0d   Jiang Liu   genirq: Add gener...
279

a97b852b4   Marc Zyngier   genirq/msi: Popul...
280
281
  	domain = irq_domain_create_hierarchy(parent, IRQ_DOMAIN_FLAG_MSI, 0,
  					     fwnode, &msi_domain_ops, info);
0165308a2   Thomas Gleixner   genirq/msi: Preve...
282
283
  
  	if (domain && !domain->name && info->chip)
a97b852b4   Marc Zyngier   genirq/msi: Popul...
284
285
286
  		domain->name = info->chip->name;
  
  	return domain;
f3cf8bb0d   Jiang Liu   genirq: Add gener...
287
  }
b2eba39bc   Marc Zyngier   genirq/msi: Make ...
288
289
290
291
292
293
294
295
296
297
298
299
300
  int msi_domain_prepare_irqs(struct irq_domain *domain, struct device *dev,
  			    int nvec, msi_alloc_info_t *arg)
  {
  	struct msi_domain_info *info = domain->host_data;
  	struct msi_domain_ops *ops = info->ops;
  	int ret;
  
  	ret = ops->msi_check(domain, info, dev);
  	if (ret == 0)
  		ret = ops->msi_prepare(domain, dev, nvec, arg);
  
  	return ret;
  }
2145ac931   Marc Zyngier   genirq/msi: Add m...
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
  int msi_domain_populate_irqs(struct irq_domain *domain, struct device *dev,
  			     int virq, int nvec, msi_alloc_info_t *arg)
  {
  	struct msi_domain_info *info = domain->host_data;
  	struct msi_domain_ops *ops = info->ops;
  	struct msi_desc *desc;
  	int ret = 0;
  
  	for_each_msi_entry(desc, dev) {
  		/* Don't even try the multi-MSI brain damage. */
  		if (WARN_ON(!desc->irq || desc->nvec_used != 1)) {
  			ret = -EINVAL;
  			break;
  		}
  
  		if (!(desc->irq >= virq && desc->irq < (virq + nvec)))
  			continue;
  
  		ops->set_desc(arg, desc);
  		/* Assumes the domain mutex is held! */
596a7a1d0   John Keeping   genirq/msi: Fix p...
321
322
  		ret = irq_domain_alloc_irqs_hierarchy(domain, desc->irq, 1,
  						      arg);
2145ac931   Marc Zyngier   genirq/msi: Add m...
323
324
  		if (ret)
  			break;
596a7a1d0   John Keeping   genirq/msi: Fix p...
325
  		irq_set_msi_desc_off(desc->irq, 0, desc);
2145ac931   Marc Zyngier   genirq/msi: Add m...
326
327
328
329
330
331
332
333
334
335
336
337
338
339
  	}
  
  	if (ret) {
  		/* Mop up the damage */
  		for_each_msi_entry(desc, dev) {
  			if (!(desc->irq >= virq && desc->irq < (virq + nvec)))
  				continue;
  
  			irq_domain_free_irqs_common(domain, desc->irq, 1);
  		}
  	}
  
  	return ret;
  }
bc976233a   Thomas Gleixner   genirq/msi, x86/v...
340
341
342
343
344
345
346
347
348
349
350
351
352
353
  /*
   * Carefully check whether the device can use reservation mode. If
   * reservation mode is enabled then the early activation will assign a
   * dummy vector to the device. If the PCI/MSI device does not support
   * masking of the entry then this can result in spurious interrupts when
   * the device driver is not absolutely careful. But even then a malfunction
   * of the hardware could result in a spurious interrupt on the dummy vector
   * and render the device unusable. If the entry can be masked then the core
   * logic will prevent the spurious interrupt and reservation mode can be
   * used. For now reservation mode is restricted to PCI/MSI.
   */
  static bool msi_check_reservation_mode(struct irq_domain *domain,
  				       struct msi_domain_info *info,
  				       struct device *dev)
da5dd9e85   Thomas Gleixner   genirq/msi: Handl...
354
  {
bc976233a   Thomas Gleixner   genirq/msi, x86/v...
355
356
357
358
  	struct msi_desc *desc;
  
  	if (domain->bus_token != DOMAIN_BUS_PCI_MSI)
  		return false;
da5dd9e85   Thomas Gleixner   genirq/msi: Handl...
359
360
  	if (!(info->flags & MSI_FLAG_MUST_REACTIVATE))
  		return false;
bc976233a   Thomas Gleixner   genirq/msi, x86/v...
361
362
363
364
365
366
367
368
369
370
  
  	if (IS_ENABLED(CONFIG_PCI_MSI) && pci_msi_ignore_mask)
  		return false;
  
  	/*
  	 * Checking the first MSI descriptor is sufficient. MSIX supports
  	 * masking and MSI does so when the maskbit is set.
  	 */
  	desc = first_msi_entry(dev);
  	return desc->msi_attrib.is_msix || desc->msi_attrib.maskbit;
da5dd9e85   Thomas Gleixner   genirq/msi: Handl...
371
  }
f3cf8bb0d   Jiang Liu   genirq: Add gener...
372
  /**
d9109698b   Jiang Liu   genirq: Introduce...
373
374
375
376
377
378
379
380
381
382
383
384
385
   * msi_domain_alloc_irqs - Allocate interrupts from a MSI interrupt domain
   * @domain:	The domain to allocate from
   * @dev:	Pointer to device struct of the device for which the interrupts
   *		are allocated
   * @nvec:	The number of interrupts to allocate
   *
   * Returns 0 on success or an error code.
   */
  int msi_domain_alloc_irqs(struct irq_domain *domain, struct device *dev,
  			  int nvec)
  {
  	struct msi_domain_info *info = domain->host_data;
  	struct msi_domain_ops *ops = info->ops;
da5dd9e85   Thomas Gleixner   genirq/msi: Handl...
386
  	struct irq_data *irq_data;
d9109698b   Jiang Liu   genirq: Introduce...
387
  	struct msi_desc *desc;
da5dd9e85   Thomas Gleixner   genirq/msi: Handl...
388
  	msi_alloc_info_t arg;
b6140914f   Thomas Gleixner   genirq/msi: Remov...
389
  	int i, ret, virq;
da5dd9e85   Thomas Gleixner   genirq/msi: Handl...
390
  	bool can_reserve;
d9109698b   Jiang Liu   genirq: Introduce...
391

b2eba39bc   Marc Zyngier   genirq/msi: Make ...
392
  	ret = msi_domain_prepare_irqs(domain, dev, nvec, &arg);
d9109698b   Jiang Liu   genirq: Introduce...
393
394
395
396
397
  	if (ret)
  		return ret;
  
  	for_each_msi_entry(desc, dev) {
  		ops->set_desc(&arg, desc);
b6140914f   Thomas Gleixner   genirq/msi: Remov...
398
  		virq = __irq_domain_alloc_irqs(domain, -1, desc->nvec_used,
06ee6d571   Thomas Gleixner   genirq: Add affin...
399
  					       dev_to_node(dev), &arg, false,
0972fa57f   Thomas Gleixner   genirq/msi: Make ...
400
  					       desc->affinity);
d9109698b   Jiang Liu   genirq: Introduce...
401
402
403
404
405
406
407
408
  		if (virq < 0) {
  			ret = -ENOSPC;
  			if (ops->handle_error)
  				ret = ops->handle_error(domain, desc, ret);
  			if (ops->msi_finish)
  				ops->msi_finish(&arg, ret);
  			return ret;
  		}
07557ccb8   Thomas Gleixner   genirq/msi: Captu...
409
  		for (i = 0; i < desc->nvec_used; i++) {
d9109698b   Jiang Liu   genirq: Introduce...
410
  			irq_set_msi_desc_off(virq, i, desc);
07557ccb8   Thomas Gleixner   genirq/msi: Captu...
411
412
  			irq_debugfs_copy_devname(virq + i, dev);
  		}
d9109698b   Jiang Liu   genirq: Introduce...
413
414
415
416
  	}
  
  	if (ops->msi_finish)
  		ops->msi_finish(&arg, 0);
bc976233a   Thomas Gleixner   genirq/msi, x86/v...
417
  	can_reserve = msi_check_reservation_mode(domain, info, dev);
da5dd9e85   Thomas Gleixner   genirq/msi: Handl...
418

d9109698b   Jiang Liu   genirq: Introduce...
419
  	for_each_msi_entry(desc, dev) {
4364e1a29   Thomas Gleixner   genirq/msi: Fix b...
420
  		virq = desc->irq;
d9109698b   Jiang Liu   genirq: Introduce...
421
422
423
424
425
426
427
  		if (desc->nvec_used == 1)
  			dev_dbg(dev, "irq %d for MSI
  ", virq);
  		else
  			dev_dbg(dev, "irq [%d-%d] for MSI
  ",
  				virq, virq + desc->nvec_used - 1);
f3b0946d6   Marc Zyngier   genirq/msi: Make ...
428
429
430
431
432
  		/*
  		 * This flag is set by the PCI layer as we need to activate
  		 * the MSI entries before the PCI layer enables MSI in the
  		 * card. Otherwise the card latches a random msi message.
  		 */
da5dd9e85   Thomas Gleixner   genirq/msi: Handl...
433
434
  		if (!(info->flags & MSI_FLAG_ACTIVATE_EARLY))
  			continue;
f3b0946d6   Marc Zyngier   genirq/msi: Make ...
435

da5dd9e85   Thomas Gleixner   genirq/msi: Handl...
436
  		irq_data = irq_domain_get_irq_data(domain, desc->irq);
bc976233a   Thomas Gleixner   genirq/msi, x86/v...
437
438
439
  		if (!can_reserve)
  			irqd_clr_can_reserve(irq_data);
  		ret = irq_domain_activate_irq(irq_data, can_reserve);
da5dd9e85   Thomas Gleixner   genirq/msi: Handl...
440
441
442
443
444
445
446
447
448
449
  		if (ret)
  			goto cleanup;
  	}
  
  	/*
  	 * If these interrupts use reservation mode, clear the activated bit
  	 * so request_irq() will assign the final vector.
  	 */
  	if (can_reserve) {
  		for_each_msi_entry(desc, dev) {
f3b0946d6   Marc Zyngier   genirq/msi: Make ...
450
  			irq_data = irq_domain_get_irq_data(domain, desc->irq);
da5dd9e85   Thomas Gleixner   genirq/msi: Handl...
451
  			irqd_clr_activated(irq_data);
f3b0946d6   Marc Zyngier   genirq/msi: Make ...
452
  		}
d9109698b   Jiang Liu   genirq: Introduce...
453
  	}
d9109698b   Jiang Liu   genirq: Introduce...
454
  	return 0;
bb9b428a5   Thomas Gleixner   genirq/irqdomain:...
455
456
457
458
459
460
461
462
463
464
465
466
467
468
  
  cleanup:
  	for_each_msi_entry(desc, dev) {
  		struct irq_data *irqd;
  
  		if (desc->irq == virq)
  			break;
  
  		irqd = irq_domain_get_irq_data(domain, desc->irq);
  		if (irqd_is_activated(irqd))
  			irq_domain_deactivate_irq(irqd);
  	}
  	msi_domain_free_irqs(domain, dev);
  	return ret;
d9109698b   Jiang Liu   genirq: Introduce...
469
470
471
472
473
474
475
476
477
478
479
480
481
  }
  
  /**
   * msi_domain_free_irqs - Free interrupts from a MSI interrupt @domain associated tp @dev
   * @domain:	The domain to managing the interrupts
   * @dev:	Pointer to device struct of the device for which the interrupts
   *		are free
   */
  void msi_domain_free_irqs(struct irq_domain *domain, struct device *dev)
  {
  	struct msi_desc *desc;
  
  	for_each_msi_entry(desc, dev) {
fe0c52fc0   Marc Zyngier   genirq: MSI: Fix ...
482
483
484
485
486
487
488
489
490
  		/*
  		 * We might have failed to allocate an MSI early
  		 * enough that there is no IRQ associated to this
  		 * entry. If that's the case, don't do anything.
  		 */
  		if (desc->irq) {
  			irq_domain_free_irqs(desc->irq, desc->nvec_used);
  			desc->irq = 0;
  		}
d9109698b   Jiang Liu   genirq: Introduce...
491
492
493
494
  	}
  }
  
  /**
f3cf8bb0d   Jiang Liu   genirq: Add gener...
495
496
497
498
499
500
501
502
503
504
505
506
   * msi_get_domain_info - Get the MSI interrupt domain info for @domain
   * @domain:	The interrupt domain to retrieve data from
   *
   * Returns the pointer to the msi_domain_info stored in
   * @domain->host_data.
   */
  struct msi_domain_info *msi_get_domain_info(struct irq_domain *domain)
  {
  	return (struct msi_domain_info *)domain->host_data;
  }
  
  #endif /* CONFIG_GENERIC_MSI_IRQ_DOMAIN */