1 // SPDX-License-Identifier: GPL-2.0
3 * MSI framework for platform devices
5 * Copyright (C) 2015 ARM Limited, All Rights Reserved.
6 * Author: Marc Zyngier <marc.zyngier@arm.com>
9 #include <linux/device.h>
10 #include <linux/idr.h>
11 #include <linux/irq.h>
12 #include <linux/irqdomain.h>
13 #include <linux/msi.h>
14 #include <linux/slab.h>
16 #define DEV_ID_SHIFT 21
17 #define MAX_DEV_MSIS (1 << (32 - DEV_ID_SHIFT))
20 * Internal data structure containing a (made up, but unique) devid
21 * and the callback to write the MSI message.
23 struct platform_msi_priv_data
{
27 irq_write_msi_msg_t write_msg
;
31 /* The devid allocator */
32 static DEFINE_IDA(platform_msi_devid_ida
);
34 #ifdef GENERIC_MSI_DOMAIN_OPS
36 * Convert an msi_desc to a globaly unique identifier (per-device
37 * devid + msi_desc position in the msi_list).
39 static irq_hw_number_t
platform_msi_calc_hwirq(struct msi_desc
*desc
)
43 devid
= desc
->platform
.msi_priv_data
->devid
;
45 return (devid
<< (32 - DEV_ID_SHIFT
)) | desc
->platform
.msi_index
;
48 static void platform_msi_set_desc(msi_alloc_info_t
*arg
, struct msi_desc
*desc
)
51 arg
->hwirq
= platform_msi_calc_hwirq(desc
);
54 static int platform_msi_init(struct irq_domain
*domain
,
55 struct msi_domain_info
*info
,
56 unsigned int virq
, irq_hw_number_t hwirq
,
57 msi_alloc_info_t
*arg
)
59 return irq_domain_set_hwirq_and_chip(domain
, virq
, hwirq
,
60 info
->chip
, info
->chip_data
);
63 #define platform_msi_set_desc NULL
64 #define platform_msi_init NULL
67 static void platform_msi_update_dom_ops(struct msi_domain_info
*info
)
69 struct msi_domain_ops
*ops
= info
->ops
;
73 if (ops
->msi_init
== NULL
)
74 ops
->msi_init
= platform_msi_init
;
75 if (ops
->set_desc
== NULL
)
76 ops
->set_desc
= platform_msi_set_desc
;
79 static void platform_msi_write_msg(struct irq_data
*data
, struct msi_msg
*msg
)
81 struct msi_desc
*desc
= irq_data_get_msi_desc(data
);
82 struct platform_msi_priv_data
*priv_data
;
84 priv_data
= desc
->platform
.msi_priv_data
;
86 priv_data
->write_msg(desc
, msg
);
89 static void platform_msi_update_chip_ops(struct msi_domain_info
*info
)
91 struct irq_chip
*chip
= info
->chip
;
95 chip
->irq_mask
= irq_chip_mask_parent
;
96 if (!chip
->irq_unmask
)
97 chip
->irq_unmask
= irq_chip_unmask_parent
;
99 chip
->irq_eoi
= irq_chip_eoi_parent
;
100 if (!chip
->irq_set_affinity
)
101 chip
->irq_set_affinity
= msi_domain_set_affinity
;
102 if (!chip
->irq_write_msi_msg
)
103 chip
->irq_write_msi_msg
= platform_msi_write_msg
;
106 static void platform_msi_free_descs(struct device
*dev
, int base
, int nvec
)
108 struct msi_desc
*desc
, *tmp
;
110 list_for_each_entry_safe(desc
, tmp
, dev_to_msi_list(dev
), list
) {
111 if (desc
->platform
.msi_index
>= base
&&
112 desc
->platform
.msi_index
< (base
+ nvec
)) {
113 list_del(&desc
->list
);
114 free_msi_entry(desc
);
119 static int platform_msi_alloc_descs_with_irq(struct device
*dev
, int virq
,
121 struct platform_msi_priv_data
*data
)
124 struct msi_desc
*desc
;
127 if (!list_empty(dev_to_msi_list(dev
))) {
128 desc
= list_last_entry(dev_to_msi_list(dev
),
129 struct msi_desc
, list
);
130 base
= desc
->platform
.msi_index
+ 1;
133 for (i
= 0; i
< nvec
; i
++) {
134 desc
= alloc_msi_entry(dev
, 1, NULL
);
138 desc
->platform
.msi_priv_data
= data
;
139 desc
->platform
.msi_index
= base
+ i
;
140 desc
->irq
= virq
? virq
+ i
: 0;
142 list_add_tail(&desc
->list
, dev_to_msi_list(dev
));
146 /* Clean up the mess */
147 platform_msi_free_descs(dev
, base
, nvec
);
155 static int platform_msi_alloc_descs(struct device
*dev
, int nvec
,
156 struct platform_msi_priv_data
*data
)
159 return platform_msi_alloc_descs_with_irq(dev
, 0, nvec
, data
);
163 * platform_msi_create_irq_domain - Create a platform MSI interrupt domain
164 * @fwnode: Optional fwnode of the interrupt controller
165 * @info: MSI domain info
166 * @parent: Parent irq domain
168 * Updates the domain and chip ops and creates a platform MSI
172 * A domain pointer or NULL in case of failure.
174 struct irq_domain
*platform_msi_create_irq_domain(struct fwnode_handle
*fwnode
,
175 struct msi_domain_info
*info
,
176 struct irq_domain
*parent
)
178 struct irq_domain
*domain
;
180 if (info
->flags
& MSI_FLAG_USE_DEF_DOM_OPS
)
181 platform_msi_update_dom_ops(info
);
182 if (info
->flags
& MSI_FLAG_USE_DEF_CHIP_OPS
)
183 platform_msi_update_chip_ops(info
);
185 domain
= msi_create_irq_domain(fwnode
, info
, parent
);
187 irq_domain_update_bus_token(domain
, DOMAIN_BUS_PLATFORM_MSI
);
192 static struct platform_msi_priv_data
*
193 platform_msi_alloc_priv_data(struct device
*dev
, unsigned int nvec
,
194 irq_write_msi_msg_t write_msi_msg
)
196 struct platform_msi_priv_data
*datap
;
198 * Limit the number of interrupts to 2048 per device. Should we
199 * need to bump this up, DEV_ID_SHIFT should be adjusted
200 * accordingly (which would impact the max number of MSI
203 if (!dev
->msi_domain
|| !write_msi_msg
|| !nvec
|| nvec
> MAX_DEV_MSIS
)
204 return ERR_PTR(-EINVAL
);
206 if (dev
->msi_domain
->bus_token
!= DOMAIN_BUS_PLATFORM_MSI
) {
207 dev_err(dev
, "Incompatible msi_domain, giving up\n");
208 return ERR_PTR(-EINVAL
);
211 /* Already had a helping of MSI? Greed... */
212 if (!list_empty(dev_to_msi_list(dev
)))
213 return ERR_PTR(-EBUSY
);
215 datap
= kzalloc(sizeof(*datap
), GFP_KERNEL
);
217 return ERR_PTR(-ENOMEM
);
219 datap
->devid
= ida_simple_get(&platform_msi_devid_ida
,
220 0, 1 << DEV_ID_SHIFT
, GFP_KERNEL
);
221 if (datap
->devid
< 0) {
222 int err
= datap
->devid
;
227 datap
->write_msg
= write_msi_msg
;
233 static void platform_msi_free_priv_data(struct platform_msi_priv_data
*data
)
235 ida_simple_remove(&platform_msi_devid_ida
, data
->devid
);
240 * platform_msi_domain_alloc_irqs - Allocate MSI interrupts for @dev
241 * @dev: The device for which to allocate interrupts
242 * @nvec: The number of interrupts to allocate
243 * @write_msi_msg: Callback to write an interrupt message for @dev
246 * Zero for success, or an error code in case of failure
248 int platform_msi_domain_alloc_irqs(struct device
*dev
, unsigned int nvec
,
249 irq_write_msi_msg_t write_msi_msg
)
251 struct platform_msi_priv_data
*priv_data
;
254 priv_data
= platform_msi_alloc_priv_data(dev
, nvec
, write_msi_msg
);
255 if (IS_ERR(priv_data
))
256 return PTR_ERR(priv_data
);
258 err
= platform_msi_alloc_descs(dev
, nvec
, priv_data
);
260 goto out_free_priv_data
;
262 err
= msi_domain_alloc_irqs(dev
->msi_domain
, dev
, nvec
);
269 platform_msi_free_descs(dev
, 0, nvec
);
271 platform_msi_free_priv_data(priv_data
);
275 EXPORT_SYMBOL_GPL(platform_msi_domain_alloc_irqs
);
278 * platform_msi_domain_free_irqs - Free MSI interrupts for @dev
279 * @dev: The device for which to free interrupts
281 void platform_msi_domain_free_irqs(struct device
*dev
)
283 if (!list_empty(dev_to_msi_list(dev
))) {
284 struct msi_desc
*desc
;
286 desc
= first_msi_entry(dev
);
287 platform_msi_free_priv_data(desc
->platform
.msi_priv_data
);
290 msi_domain_free_irqs(dev
->msi_domain
, dev
);
291 platform_msi_free_descs(dev
, 0, MAX_DEV_MSIS
);
293 EXPORT_SYMBOL_GPL(platform_msi_domain_free_irqs
);
296 * platform_msi_get_host_data - Query the private data associated with
297 * a platform-msi domain
298 * @domain: The platform-msi domain
300 * Returns the private data provided when calling
301 * platform_msi_create_device_domain.
303 void *platform_msi_get_host_data(struct irq_domain
*domain
)
305 struct platform_msi_priv_data
*data
= domain
->host_data
;
306 return data
->host_data
;
310 * platform_msi_create_device_domain - Create a platform-msi domain
312 * @dev: The device generating the MSIs
313 * @nvec: The number of MSIs that need to be allocated
314 * @write_msi_msg: Callback to write an interrupt message for @dev
315 * @ops: The hierarchy domain operations to use
316 * @host_data: Private data associated to this domain
318 * Returns an irqdomain for @nvec interrupts
321 platform_msi_create_device_domain(struct device
*dev
,
323 irq_write_msi_msg_t write_msi_msg
,
324 const struct irq_domain_ops
*ops
,
327 struct platform_msi_priv_data
*data
;
328 struct irq_domain
*domain
;
331 data
= platform_msi_alloc_priv_data(dev
, nvec
, write_msi_msg
);
335 data
->host_data
= host_data
;
336 domain
= irq_domain_create_hierarchy(dev
->msi_domain
, 0, nvec
,
337 dev
->fwnode
, ops
, data
);
341 err
= msi_domain_prepare_irqs(domain
->parent
, dev
, nvec
, &data
->arg
);
348 irq_domain_remove(domain
);
350 platform_msi_free_priv_data(data
);
355 * platform_msi_domain_free - Free interrupts associated with a platform-msi
358 * @domain: The platform-msi domain
359 * @virq: The base irq from which to perform the free operation
360 * @nvec: How many interrupts to free from @virq
362 void platform_msi_domain_free(struct irq_domain
*domain
, unsigned int virq
,
365 struct platform_msi_priv_data
*data
= domain
->host_data
;
366 struct msi_desc
*desc
;
367 for_each_msi_entry(desc
, data
->dev
) {
368 if (WARN_ON(!desc
->irq
|| desc
->nvec_used
!= 1))
370 if (!(desc
->irq
>= virq
&& desc
->irq
< (virq
+ nvec
)))
373 irq_domain_free_irqs_common(domain
, desc
->irq
, 1);
378 * platform_msi_domain_alloc - Allocate interrupts associated with
379 * a platform-msi domain
381 * @domain: The platform-msi domain
382 * @virq: The base irq from which to perform the allocate operation
383 * @nvec: How many interrupts to free from @virq
385 * Return 0 on success, or an error code on failure. Must be called
386 * with irq_domain_mutex held (which can only be done as part of a
387 * top-level interrupt allocation).
389 int platform_msi_domain_alloc(struct irq_domain
*domain
, unsigned int virq
,
390 unsigned int nr_irqs
)
392 struct platform_msi_priv_data
*data
= domain
->host_data
;
395 err
= platform_msi_alloc_descs_with_irq(data
->dev
, virq
, nr_irqs
, data
);
399 err
= msi_domain_populate_irqs(domain
->parent
, data
->dev
,
400 virq
, nr_irqs
, &data
->arg
);
402 platform_msi_domain_free(domain
, virq
, nr_irqs
);