xref: /openbmc/linux/drivers/base/platform-msi.c (revision 9a87ffc99ec8eb8d35eed7c4f816d75f5cc9662e)
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