1989d42e8SGreg Kroah-Hartman // SPDX-License-Identifier: GPL-2.0
2c09fcc4bSMarc Zyngier /*
3c09fcc4bSMarc Zyngier * MSI framework for platform devices
4c09fcc4bSMarc Zyngier *
5c09fcc4bSMarc Zyngier * Copyright (C) 2015 ARM Limited, All Rights Reserved.
6c09fcc4bSMarc Zyngier * Author: Marc Zyngier <marc.zyngier@arm.com>
7c09fcc4bSMarc Zyngier */
8c09fcc4bSMarc Zyngier
9c09fcc4bSMarc Zyngier #include <linux/device.h>
10c09fcc4bSMarc Zyngier #include <linux/idr.h>
11c09fcc4bSMarc Zyngier #include <linux/irq.h>
12c09fcc4bSMarc Zyngier #include <linux/irqdomain.h>
13c09fcc4bSMarc Zyngier #include <linux/msi.h>
14c09fcc4bSMarc Zyngier #include <linux/slab.h>
15c09fcc4bSMarc Zyngier
16aff5e06bSMaJun #define DEV_ID_SHIFT 21
17ab6484eeSMarc Zyngier #define MAX_DEV_MSIS (1 << (32 - DEV_ID_SHIFT))
18c09fcc4bSMarc Zyngier
19c09fcc4bSMarc Zyngier /*
20c09fcc4bSMarc Zyngier * Internal data structure containing a (made up, but unique) devid
21c09fcc4bSMarc Zyngier * and the callback to write the MSI message.
22c09fcc4bSMarc Zyngier */
23c09fcc4bSMarc Zyngier struct platform_msi_priv_data {
24552c494aSMarc Zyngier struct device *dev;
25552c494aSMarc Zyngier void *host_data;
26552c494aSMarc Zyngier msi_alloc_info_t arg;
27c09fcc4bSMarc Zyngier irq_write_msi_msg_t write_msg;
28c09fcc4bSMarc Zyngier int devid;
29c09fcc4bSMarc Zyngier };
30c09fcc4bSMarc Zyngier
31c09fcc4bSMarc Zyngier /* The devid allocator */
32c09fcc4bSMarc Zyngier static DEFINE_IDA(platform_msi_devid_ida);
33c09fcc4bSMarc Zyngier
34c09fcc4bSMarc Zyngier #ifdef GENERIC_MSI_DOMAIN_OPS
35c09fcc4bSMarc Zyngier /*
36c09fcc4bSMarc Zyngier * Convert an msi_desc to a globaly unique identifier (per-device
37c09fcc4bSMarc Zyngier * devid + msi_desc position in the msi_list).
38c09fcc4bSMarc Zyngier */
platform_msi_calc_hwirq(struct msi_desc * desc)39c09fcc4bSMarc Zyngier static irq_hw_number_t platform_msi_calc_hwirq(struct msi_desc *desc)
40c09fcc4bSMarc Zyngier {
41fc22e7dbSThomas Gleixner u32 devid = desc->dev->msi.data->platform_data->devid;
42c09fcc4bSMarc Zyngier
43dba27c7fSThomas Gleixner return (devid << (32 - DEV_ID_SHIFT)) | desc->msi_index;
44c09fcc4bSMarc Zyngier }
45c09fcc4bSMarc Zyngier
platform_msi_set_desc(msi_alloc_info_t * arg,struct msi_desc * desc)46c09fcc4bSMarc Zyngier static void platform_msi_set_desc(msi_alloc_info_t *arg, struct msi_desc *desc)
47c09fcc4bSMarc Zyngier {
48c09fcc4bSMarc Zyngier arg->desc = desc;
49c09fcc4bSMarc Zyngier arg->hwirq = platform_msi_calc_hwirq(desc);
50c09fcc4bSMarc Zyngier }
51c09fcc4bSMarc Zyngier
platform_msi_init(struct irq_domain * domain,struct msi_domain_info * info,unsigned int virq,irq_hw_number_t hwirq,msi_alloc_info_t * arg)52c09fcc4bSMarc Zyngier static int platform_msi_init(struct irq_domain *domain,
53c09fcc4bSMarc Zyngier struct msi_domain_info *info,
54c09fcc4bSMarc Zyngier unsigned int virq, irq_hw_number_t hwirq,
55c09fcc4bSMarc Zyngier msi_alloc_info_t *arg)
56c09fcc4bSMarc Zyngier {
57e4084a16SMarc Zyngier return irq_domain_set_hwirq_and_chip(domain, virq, hwirq,
58c09fcc4bSMarc Zyngier info->chip, info->chip_data);
59c09fcc4bSMarc Zyngier }
6091f90daaSMarc Zyngier
platform_msi_set_proxy_dev(msi_alloc_info_t * arg)6191f90daaSMarc Zyngier static void platform_msi_set_proxy_dev(msi_alloc_info_t *arg)
6291f90daaSMarc Zyngier {
6391f90daaSMarc Zyngier arg->flags |= MSI_ALLOC_FLAGS_PROXY_DEVICE;
6491f90daaSMarc Zyngier }
65c09fcc4bSMarc Zyngier #else
66c09fcc4bSMarc Zyngier #define platform_msi_set_desc NULL
67c09fcc4bSMarc Zyngier #define platform_msi_init NULL
6891f90daaSMarc Zyngier #define platform_msi_set_proxy_dev(x) do {} while(0)
69c09fcc4bSMarc Zyngier #endif
70c09fcc4bSMarc Zyngier
platform_msi_update_dom_ops(struct msi_domain_info * info)71c09fcc4bSMarc Zyngier static void platform_msi_update_dom_ops(struct msi_domain_info *info)
72c09fcc4bSMarc Zyngier {
73c09fcc4bSMarc Zyngier struct msi_domain_ops *ops = info->ops;
74c09fcc4bSMarc Zyngier
75c09fcc4bSMarc Zyngier BUG_ON(!ops);
76c09fcc4bSMarc Zyngier
77c09fcc4bSMarc Zyngier if (ops->msi_init == NULL)
78c09fcc4bSMarc Zyngier ops->msi_init = platform_msi_init;
79c09fcc4bSMarc Zyngier if (ops->set_desc == NULL)
80c09fcc4bSMarc Zyngier ops->set_desc = platform_msi_set_desc;
81c09fcc4bSMarc Zyngier }
82c09fcc4bSMarc Zyngier
platform_msi_write_msg(struct irq_data * data,struct msi_msg * msg)83c09fcc4bSMarc Zyngier static void platform_msi_write_msg(struct irq_data *data, struct msi_msg *msg)
84c09fcc4bSMarc Zyngier {
85e4084a16SMarc Zyngier struct msi_desc *desc = irq_data_get_msi_desc(data);
86c09fcc4bSMarc Zyngier
87fc22e7dbSThomas Gleixner desc->dev->msi.data->platform_data->write_msg(desc, msg);
88c09fcc4bSMarc Zyngier }
89c09fcc4bSMarc Zyngier
platform_msi_update_chip_ops(struct msi_domain_info * info)90c09fcc4bSMarc Zyngier static void platform_msi_update_chip_ops(struct msi_domain_info *info)
91c09fcc4bSMarc Zyngier {
92c09fcc4bSMarc Zyngier struct irq_chip *chip = info->chip;
93c09fcc4bSMarc Zyngier
94c09fcc4bSMarc Zyngier BUG_ON(!chip);
95c09fcc4bSMarc Zyngier if (!chip->irq_mask)
96c09fcc4bSMarc Zyngier chip->irq_mask = irq_chip_mask_parent;
97c09fcc4bSMarc Zyngier if (!chip->irq_unmask)
98c09fcc4bSMarc Zyngier chip->irq_unmask = irq_chip_unmask_parent;
99c09fcc4bSMarc Zyngier if (!chip->irq_eoi)
100c09fcc4bSMarc Zyngier chip->irq_eoi = irq_chip_eoi_parent;
101c09fcc4bSMarc Zyngier if (!chip->irq_set_affinity)
102c09fcc4bSMarc Zyngier chip->irq_set_affinity = msi_domain_set_affinity;
103c09fcc4bSMarc Zyngier if (!chip->irq_write_msi_msg)
104c09fcc4bSMarc Zyngier chip->irq_write_msi_msg = platform_msi_write_msg;
1056988e0e0SMarc Zyngier if (WARN_ON((info->flags & MSI_FLAG_LEVEL_CAPABLE) &&
1066988e0e0SMarc Zyngier !(chip->flags & IRQCHIP_SUPPORTS_LEVEL_MSI)))
1076988e0e0SMarc Zyngier info->flags &= ~MSI_FLAG_LEVEL_CAPABLE;
108c09fcc4bSMarc Zyngier }
109c09fcc4bSMarc Zyngier
110c09fcc4bSMarc Zyngier /**
111c09fcc4bSMarc Zyngier * platform_msi_create_irq_domain - Create a platform MSI interrupt domain
112be5436c8SMarc Zyngier * @fwnode: Optional fwnode of the interrupt controller
113c09fcc4bSMarc Zyngier * @info: MSI domain info
114c09fcc4bSMarc Zyngier * @parent: Parent irq domain
115c09fcc4bSMarc Zyngier *
116c09fcc4bSMarc Zyngier * Updates the domain and chip ops and creates a platform MSI
117c09fcc4bSMarc Zyngier * interrupt domain.
118c09fcc4bSMarc Zyngier *
119c09fcc4bSMarc Zyngier * Returns:
120c09fcc4bSMarc Zyngier * A domain pointer or NULL in case of failure.
121c09fcc4bSMarc Zyngier */
platform_msi_create_irq_domain(struct fwnode_handle * fwnode,struct msi_domain_info * info,struct irq_domain * parent)122be5436c8SMarc Zyngier struct irq_domain *platform_msi_create_irq_domain(struct fwnode_handle *fwnode,
123c09fcc4bSMarc Zyngier struct msi_domain_info *info,
124c09fcc4bSMarc Zyngier struct irq_domain *parent)
125c09fcc4bSMarc Zyngier {
126c09fcc4bSMarc Zyngier struct irq_domain *domain;
127c09fcc4bSMarc Zyngier
128c09fcc4bSMarc Zyngier if (info->flags & MSI_FLAG_USE_DEF_DOM_OPS)
129c09fcc4bSMarc Zyngier platform_msi_update_dom_ops(info);
130c09fcc4bSMarc Zyngier if (info->flags & MSI_FLAG_USE_DEF_CHIP_OPS)
131c09fcc4bSMarc Zyngier platform_msi_update_chip_ops(info);
132653b50c5SThomas Gleixner info->flags |= MSI_FLAG_DEV_SYSFS | MSI_FLAG_ALLOC_SIMPLE_MSI_DESCS |
133653b50c5SThomas Gleixner MSI_FLAG_FREE_MSI_DESCS;
134c09fcc4bSMarc Zyngier
135be5436c8SMarc Zyngier domain = msi_create_irq_domain(fwnode, info, parent);
136c09fcc4bSMarc Zyngier if (domain)
13796f0d93aSMarc Zyngier irq_domain_update_bus_token(domain, DOMAIN_BUS_PLATFORM_MSI);
138c09fcc4bSMarc Zyngier
139c09fcc4bSMarc Zyngier return domain;
140c09fcc4bSMarc Zyngier }
141aecd1de3SFrank Li EXPORT_SYMBOL_GPL(platform_msi_create_irq_domain);
142c09fcc4bSMarc Zyngier
platform_msi_alloc_priv_data(struct device * dev,unsigned int nvec,irq_write_msi_msg_t write_msi_msg)143fc22e7dbSThomas Gleixner static int platform_msi_alloc_priv_data(struct device *dev, unsigned int nvec,
14472f57f2fSMarc Zyngier irq_write_msi_msg_t write_msi_msg)
14572f57f2fSMarc Zyngier {
14672f57f2fSMarc Zyngier struct platform_msi_priv_data *datap;
147077aeadbSThomas Gleixner int err;
148077aeadbSThomas Gleixner
14972f57f2fSMarc Zyngier /*
150788d2393SMatthias Brugger * Limit the number of interrupts to 2048 per device. Should we
15172f57f2fSMarc Zyngier * need to bump this up, DEV_ID_SHIFT should be adjusted
15272f57f2fSMarc Zyngier * accordingly (which would impact the max number of MSI
15372f57f2fSMarc Zyngier * capable devices).
15472f57f2fSMarc Zyngier */
15534fff628SThomas Gleixner if (!dev->msi.domain || !write_msi_msg || !nvec || nvec > MAX_DEV_MSIS)
156fc22e7dbSThomas Gleixner return -EINVAL;
15772f57f2fSMarc Zyngier
15834fff628SThomas Gleixner if (dev->msi.domain->bus_token != DOMAIN_BUS_PLATFORM_MSI) {
15972f57f2fSMarc Zyngier dev_err(dev, "Incompatible msi_domain, giving up\n");
160fc22e7dbSThomas Gleixner return -EINVAL;
16172f57f2fSMarc Zyngier }
16272f57f2fSMarc Zyngier
163077aeadbSThomas Gleixner err = msi_setup_device_data(dev);
164077aeadbSThomas Gleixner if (err)
165fc22e7dbSThomas Gleixner return err;
166077aeadbSThomas Gleixner
167fc22e7dbSThomas Gleixner /* Already initialized? */
168fc22e7dbSThomas Gleixner if (dev->msi.data->platform_data)
169fc22e7dbSThomas Gleixner return -EBUSY;
17072f57f2fSMarc Zyngier
17172f57f2fSMarc Zyngier datap = kzalloc(sizeof(*datap), GFP_KERNEL);
17272f57f2fSMarc Zyngier if (!datap)
173fc22e7dbSThomas Gleixner return -ENOMEM;
17472f57f2fSMarc Zyngier
17572f57f2fSMarc Zyngier datap->devid = ida_simple_get(&platform_msi_devid_ida,
17672f57f2fSMarc Zyngier 0, 1 << DEV_ID_SHIFT, GFP_KERNEL);
17772f57f2fSMarc Zyngier if (datap->devid < 0) {
178077aeadbSThomas Gleixner err = datap->devid;
17972f57f2fSMarc Zyngier kfree(datap);
180fc22e7dbSThomas Gleixner return err;
18172f57f2fSMarc Zyngier }
18272f57f2fSMarc Zyngier
18372f57f2fSMarc Zyngier datap->write_msg = write_msi_msg;
184552c494aSMarc Zyngier datap->dev = dev;
185fc22e7dbSThomas Gleixner dev->msi.data->platform_data = datap;
186fc22e7dbSThomas Gleixner return 0;
18772f57f2fSMarc Zyngier }
18872f57f2fSMarc Zyngier
platform_msi_free_priv_data(struct device * dev)189fc22e7dbSThomas Gleixner static void platform_msi_free_priv_data(struct device *dev)
19072f57f2fSMarc Zyngier {
191fc22e7dbSThomas Gleixner struct platform_msi_priv_data *data = dev->msi.data->platform_data;
192fc22e7dbSThomas Gleixner
193fc22e7dbSThomas Gleixner dev->msi.data->platform_data = NULL;
19472f57f2fSMarc Zyngier ida_simple_remove(&platform_msi_devid_ida, data->devid);
19572f57f2fSMarc Zyngier kfree(data);
19672f57f2fSMarc Zyngier }
19772f57f2fSMarc Zyngier
198c09fcc4bSMarc Zyngier /**
199c09fcc4bSMarc Zyngier * platform_msi_domain_alloc_irqs - Allocate MSI interrupts for @dev
200c09fcc4bSMarc Zyngier * @dev: The device for which to allocate interrupts
201c09fcc4bSMarc Zyngier * @nvec: The number of interrupts to allocate
202c09fcc4bSMarc Zyngier * @write_msi_msg: Callback to write an interrupt message for @dev
203c09fcc4bSMarc Zyngier *
204c09fcc4bSMarc Zyngier * Returns:
205c09fcc4bSMarc Zyngier * Zero for success, or an error code in case of failure
206c09fcc4bSMarc Zyngier */
platform_msi_domain_alloc_irqs(struct device * dev,unsigned int nvec,irq_write_msi_msg_t write_msi_msg)207c09fcc4bSMarc Zyngier int platform_msi_domain_alloc_irqs(struct device *dev, unsigned int nvec,
208c09fcc4bSMarc Zyngier irq_write_msi_msg_t write_msi_msg)
209c09fcc4bSMarc Zyngier {
210c09fcc4bSMarc Zyngier int err;
211c09fcc4bSMarc Zyngier
212fc22e7dbSThomas Gleixner err = platform_msi_alloc_priv_data(dev, nvec, write_msi_msg);
213fc22e7dbSThomas Gleixner if (err)
214fc22e7dbSThomas Gleixner return err;
215c09fcc4bSMarc Zyngier
216b330ff9fSAhmed S. Darwish err = msi_domain_alloc_irqs_range(dev, MSI_DEFAULT_DOMAIN, 0, nvec - 1);
217c09fcc4bSMarc Zyngier if (err)
218fc22e7dbSThomas Gleixner platform_msi_free_priv_data(dev);
219653b50c5SThomas Gleixner
220c09fcc4bSMarc Zyngier return err;
221c09fcc4bSMarc Zyngier }
222bb1a7931SThomas Petazzoni EXPORT_SYMBOL_GPL(platform_msi_domain_alloc_irqs);
223c09fcc4bSMarc Zyngier
224c09fcc4bSMarc Zyngier /**
225c09fcc4bSMarc Zyngier * platform_msi_domain_free_irqs - Free MSI interrupts for @dev
226c09fcc4bSMarc Zyngier * @dev: The device for which to free interrupts
227c09fcc4bSMarc Zyngier */
platform_msi_domain_free_irqs(struct device * dev)228c09fcc4bSMarc Zyngier void platform_msi_domain_free_irqs(struct device *dev)
229c09fcc4bSMarc Zyngier {
230b330ff9fSAhmed S. Darwish msi_domain_free_irqs_all(dev, MSI_DEFAULT_DOMAIN);
231fc22e7dbSThomas Gleixner platform_msi_free_priv_data(dev);
232c09fcc4bSMarc Zyngier }
233bb1a7931SThomas Petazzoni EXPORT_SYMBOL_GPL(platform_msi_domain_free_irqs);
234552c494aSMarc Zyngier
235552c494aSMarc Zyngier /**
236552c494aSMarc Zyngier * platform_msi_get_host_data - Query the private data associated with
237552c494aSMarc Zyngier * a platform-msi domain
238552c494aSMarc Zyngier * @domain: The platform-msi domain
239552c494aSMarc Zyngier *
2409835cec6SThomas Gleixner * Return: The private data provided when calling
2419835cec6SThomas Gleixner * platform_msi_create_device_domain().
242552c494aSMarc Zyngier */
platform_msi_get_host_data(struct irq_domain * domain)243552c494aSMarc Zyngier void *platform_msi_get_host_data(struct irq_domain *domain)
244552c494aSMarc Zyngier {
245552c494aSMarc Zyngier struct platform_msi_priv_data *data = domain->host_data;
2469835cec6SThomas Gleixner
247552c494aSMarc Zyngier return data->host_data;
248552c494aSMarc Zyngier }
249552c494aSMarc Zyngier
250a80713feSThomas Gleixner static struct lock_class_key platform_device_msi_lock_class;
251a80713feSThomas Gleixner
252552c494aSMarc Zyngier /**
2539835cec6SThomas Gleixner * __platform_msi_create_device_domain - Create a platform-msi device domain
254552c494aSMarc Zyngier *
255552c494aSMarc Zyngier * @dev: The device generating the MSIs
256552c494aSMarc Zyngier * @nvec: The number of MSIs that need to be allocated
2573c652132SPierre-Louis Bossart * @is_tree: flag to indicate tree hierarchy
258552c494aSMarc Zyngier * @write_msi_msg: Callback to write an interrupt message for @dev
259552c494aSMarc Zyngier * @ops: The hierarchy domain operations to use
260552c494aSMarc Zyngier * @host_data: Private data associated to this domain
261552c494aSMarc Zyngier *
2629835cec6SThomas Gleixner * Return: An irqdomain for @nvec interrupts on success, NULL in case of error.
2639835cec6SThomas Gleixner *
2649835cec6SThomas Gleixner * This is for interrupt domains which stack on a platform-msi domain
2659835cec6SThomas Gleixner * created by platform_msi_create_irq_domain(). @dev->msi.domain points to
2669835cec6SThomas Gleixner * that platform-msi domain which is the parent for the new domain.
267552c494aSMarc Zyngier */
268552c494aSMarc Zyngier struct irq_domain *
__platform_msi_create_device_domain(struct device * dev,unsigned int nvec,bool is_tree,irq_write_msi_msg_t write_msi_msg,const struct irq_domain_ops * ops,void * host_data)2691f83515bSMarc Zyngier __platform_msi_create_device_domain(struct device *dev,
270552c494aSMarc Zyngier unsigned int nvec,
2711f83515bSMarc Zyngier bool is_tree,
272552c494aSMarc Zyngier irq_write_msi_msg_t write_msi_msg,
273552c494aSMarc Zyngier const struct irq_domain_ops *ops,
274552c494aSMarc Zyngier void *host_data)
275552c494aSMarc Zyngier {
276552c494aSMarc Zyngier struct platform_msi_priv_data *data;
277552c494aSMarc Zyngier struct irq_domain *domain;
278552c494aSMarc Zyngier int err;
279552c494aSMarc Zyngier
280fc22e7dbSThomas Gleixner err = platform_msi_alloc_priv_data(dev, nvec, write_msi_msg);
281fc22e7dbSThomas Gleixner if (err)
282552c494aSMarc Zyngier return NULL;
283552c494aSMarc Zyngier
284a80713feSThomas Gleixner /*
285a80713feSThomas Gleixner * Use a separate lock class for the MSI descriptor mutex on
286a80713feSThomas Gleixner * platform MSI device domains because the descriptor mutex nests
287a80713feSThomas Gleixner * into the domain mutex. See alloc/free below.
288a80713feSThomas Gleixner */
289a80713feSThomas Gleixner lockdep_set_class(&dev->msi.data->mutex, &platform_device_msi_lock_class);
290a80713feSThomas Gleixner
291fc22e7dbSThomas Gleixner data = dev->msi.data->platform_data;
292552c494aSMarc Zyngier data->host_data = host_data;
29334fff628SThomas Gleixner domain = irq_domain_create_hierarchy(dev->msi.domain, 0,
2941f83515bSMarc Zyngier is_tree ? 0 : nvec,
2956943acf6SHanjun Guo dev->fwnode, ops, data);
296552c494aSMarc Zyngier if (!domain)
297552c494aSMarc Zyngier goto free_priv;
298552c494aSMarc Zyngier
29991f90daaSMarc Zyngier platform_msi_set_proxy_dev(&data->arg);
300552c494aSMarc Zyngier err = msi_domain_prepare_irqs(domain->parent, dev, nvec, &data->arg);
301552c494aSMarc Zyngier if (err)
302552c494aSMarc Zyngier goto free_domain;
303552c494aSMarc Zyngier
304552c494aSMarc Zyngier return domain;
305552c494aSMarc Zyngier
306552c494aSMarc Zyngier free_domain:
307552c494aSMarc Zyngier irq_domain_remove(domain);
308552c494aSMarc Zyngier free_priv:
309fc22e7dbSThomas Gleixner platform_msi_free_priv_data(dev);
310552c494aSMarc Zyngier return NULL;
311552c494aSMarc Zyngier }
312552c494aSMarc Zyngier
313552c494aSMarc Zyngier /**
3149835cec6SThomas Gleixner * platform_msi_device_domain_free - Free interrupts associated with a platform-msi
3159835cec6SThomas Gleixner * device domain
316552c494aSMarc Zyngier *
3179835cec6SThomas Gleixner * @domain: The platform-msi device domain
318552c494aSMarc Zyngier * @virq: The base irq from which to perform the free operation
319a80713feSThomas Gleixner * @nr_irqs: How many interrupts to free from @virq
320552c494aSMarc Zyngier */
platform_msi_device_domain_free(struct irq_domain * domain,unsigned int virq,unsigned int nr_irqs)3219835cec6SThomas Gleixner void platform_msi_device_domain_free(struct irq_domain *domain, unsigned int virq,
322a80713feSThomas Gleixner unsigned int nr_irqs)
323552c494aSMarc Zyngier {
324552c494aSMarc Zyngier struct platform_msi_priv_data *data = domain->host_data;
3259835cec6SThomas Gleixner
326a80713feSThomas Gleixner msi_lock_descs(data->dev);
327*0fb7fb71SThomas Gleixner msi_domain_depopulate_descs(data->dev, virq, nr_irqs);
328a80713feSThomas Gleixner irq_domain_free_irqs_common(domain, virq, nr_irqs);
3292f2940d1SThomas Gleixner msi_free_msi_descs_range(data->dev, virq, virq + nr_irqs - 1);
330a80713feSThomas Gleixner msi_unlock_descs(data->dev);
331552c494aSMarc Zyngier }
332552c494aSMarc Zyngier
333552c494aSMarc Zyngier /**
3349835cec6SThomas Gleixner * platform_msi_device_domain_alloc - Allocate interrupts associated with
3359835cec6SThomas Gleixner * a platform-msi device domain
336552c494aSMarc Zyngier *
3379835cec6SThomas Gleixner * @domain: The platform-msi device domain
338552c494aSMarc Zyngier * @virq: The base irq from which to perform the allocate operation
339a80713feSThomas Gleixner * @nr_irqs: How many interrupts to allocate from @virq
340552c494aSMarc Zyngier *
341552c494aSMarc Zyngier * Return 0 on success, or an error code on failure. Must be called
342552c494aSMarc Zyngier * with irq_domain_mutex held (which can only be done as part of a
343552c494aSMarc Zyngier * top-level interrupt allocation).
344552c494aSMarc Zyngier */
platform_msi_device_domain_alloc(struct irq_domain * domain,unsigned int virq,unsigned int nr_irqs)3459835cec6SThomas Gleixner int platform_msi_device_domain_alloc(struct irq_domain *domain, unsigned int virq,
346552c494aSMarc Zyngier unsigned int nr_irqs)
347552c494aSMarc Zyngier {
348552c494aSMarc Zyngier struct platform_msi_priv_data *data = domain->host_data;
349a80713feSThomas Gleixner struct device *dev = data->dev;
350552c494aSMarc Zyngier
351a80713feSThomas Gleixner return msi_domain_populate_irqs(domain->parent, dev, virq, nr_irqs, &data->arg);
352552c494aSMarc Zyngier }
353