18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * MSI framework for platform devices 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (C) 2015 ARM Limited, All Rights Reserved. 68c2ecf20Sopenharmony_ci * Author: Marc Zyngier <marc.zyngier@arm.com> 78c2ecf20Sopenharmony_ci */ 88c2ecf20Sopenharmony_ci 98c2ecf20Sopenharmony_ci#include <linux/device.h> 108c2ecf20Sopenharmony_ci#include <linux/idr.h> 118c2ecf20Sopenharmony_ci#include <linux/irq.h> 128c2ecf20Sopenharmony_ci#include <linux/irqdomain.h> 138c2ecf20Sopenharmony_ci#include <linux/msi.h> 148c2ecf20Sopenharmony_ci#include <linux/slab.h> 158c2ecf20Sopenharmony_ci 168c2ecf20Sopenharmony_ci#define DEV_ID_SHIFT 21 178c2ecf20Sopenharmony_ci#define MAX_DEV_MSIS (1 << (32 - DEV_ID_SHIFT)) 188c2ecf20Sopenharmony_ci 198c2ecf20Sopenharmony_ci/* 208c2ecf20Sopenharmony_ci * Internal data structure containing a (made up, but unique) devid 218c2ecf20Sopenharmony_ci * and the callback to write the MSI message. 228c2ecf20Sopenharmony_ci */ 238c2ecf20Sopenharmony_cistruct platform_msi_priv_data { 248c2ecf20Sopenharmony_ci struct device *dev; 258c2ecf20Sopenharmony_ci void *host_data; 268c2ecf20Sopenharmony_ci msi_alloc_info_t arg; 278c2ecf20Sopenharmony_ci irq_write_msi_msg_t write_msg; 288c2ecf20Sopenharmony_ci int devid; 298c2ecf20Sopenharmony_ci}; 308c2ecf20Sopenharmony_ci 318c2ecf20Sopenharmony_ci/* The devid allocator */ 328c2ecf20Sopenharmony_cistatic DEFINE_IDA(platform_msi_devid_ida); 338c2ecf20Sopenharmony_ci 348c2ecf20Sopenharmony_ci#ifdef GENERIC_MSI_DOMAIN_OPS 358c2ecf20Sopenharmony_ci/* 368c2ecf20Sopenharmony_ci * Convert an msi_desc to a globaly unique identifier (per-device 378c2ecf20Sopenharmony_ci * devid + msi_desc position in the msi_list). 388c2ecf20Sopenharmony_ci */ 398c2ecf20Sopenharmony_cistatic irq_hw_number_t platform_msi_calc_hwirq(struct msi_desc *desc) 408c2ecf20Sopenharmony_ci{ 418c2ecf20Sopenharmony_ci u32 devid; 428c2ecf20Sopenharmony_ci 438c2ecf20Sopenharmony_ci devid = desc->platform.msi_priv_data->devid; 448c2ecf20Sopenharmony_ci 458c2ecf20Sopenharmony_ci return (devid << (32 - DEV_ID_SHIFT)) | desc->platform.msi_index; 468c2ecf20Sopenharmony_ci} 478c2ecf20Sopenharmony_ci 488c2ecf20Sopenharmony_cistatic void platform_msi_set_desc(msi_alloc_info_t *arg, struct msi_desc *desc) 498c2ecf20Sopenharmony_ci{ 508c2ecf20Sopenharmony_ci arg->desc = desc; 518c2ecf20Sopenharmony_ci arg->hwirq = platform_msi_calc_hwirq(desc); 528c2ecf20Sopenharmony_ci} 538c2ecf20Sopenharmony_ci 548c2ecf20Sopenharmony_cistatic int platform_msi_init(struct irq_domain *domain, 558c2ecf20Sopenharmony_ci struct msi_domain_info *info, 568c2ecf20Sopenharmony_ci unsigned int virq, irq_hw_number_t hwirq, 578c2ecf20Sopenharmony_ci msi_alloc_info_t *arg) 588c2ecf20Sopenharmony_ci{ 598c2ecf20Sopenharmony_ci return irq_domain_set_hwirq_and_chip(domain, virq, hwirq, 608c2ecf20Sopenharmony_ci info->chip, info->chip_data); 618c2ecf20Sopenharmony_ci} 628c2ecf20Sopenharmony_ci#else 638c2ecf20Sopenharmony_ci#define platform_msi_set_desc NULL 648c2ecf20Sopenharmony_ci#define platform_msi_init NULL 658c2ecf20Sopenharmony_ci#endif 668c2ecf20Sopenharmony_ci 678c2ecf20Sopenharmony_cistatic void platform_msi_update_dom_ops(struct msi_domain_info *info) 688c2ecf20Sopenharmony_ci{ 698c2ecf20Sopenharmony_ci struct msi_domain_ops *ops = info->ops; 708c2ecf20Sopenharmony_ci 718c2ecf20Sopenharmony_ci BUG_ON(!ops); 728c2ecf20Sopenharmony_ci 738c2ecf20Sopenharmony_ci if (ops->msi_init == NULL) 748c2ecf20Sopenharmony_ci ops->msi_init = platform_msi_init; 758c2ecf20Sopenharmony_ci if (ops->set_desc == NULL) 768c2ecf20Sopenharmony_ci ops->set_desc = platform_msi_set_desc; 778c2ecf20Sopenharmony_ci} 788c2ecf20Sopenharmony_ci 798c2ecf20Sopenharmony_cistatic void platform_msi_write_msg(struct irq_data *data, struct msi_msg *msg) 808c2ecf20Sopenharmony_ci{ 818c2ecf20Sopenharmony_ci struct msi_desc *desc = irq_data_get_msi_desc(data); 828c2ecf20Sopenharmony_ci struct platform_msi_priv_data *priv_data; 838c2ecf20Sopenharmony_ci 848c2ecf20Sopenharmony_ci priv_data = desc->platform.msi_priv_data; 858c2ecf20Sopenharmony_ci 868c2ecf20Sopenharmony_ci priv_data->write_msg(desc, msg); 878c2ecf20Sopenharmony_ci} 888c2ecf20Sopenharmony_ci 898c2ecf20Sopenharmony_cistatic void platform_msi_update_chip_ops(struct msi_domain_info *info) 908c2ecf20Sopenharmony_ci{ 918c2ecf20Sopenharmony_ci struct irq_chip *chip = info->chip; 928c2ecf20Sopenharmony_ci 938c2ecf20Sopenharmony_ci BUG_ON(!chip); 948c2ecf20Sopenharmony_ci if (!chip->irq_mask) 958c2ecf20Sopenharmony_ci chip->irq_mask = irq_chip_mask_parent; 968c2ecf20Sopenharmony_ci if (!chip->irq_unmask) 978c2ecf20Sopenharmony_ci chip->irq_unmask = irq_chip_unmask_parent; 988c2ecf20Sopenharmony_ci if (!chip->irq_eoi) 998c2ecf20Sopenharmony_ci chip->irq_eoi = irq_chip_eoi_parent; 1008c2ecf20Sopenharmony_ci if (!chip->irq_set_affinity) 1018c2ecf20Sopenharmony_ci chip->irq_set_affinity = msi_domain_set_affinity; 1028c2ecf20Sopenharmony_ci if (!chip->irq_write_msi_msg) 1038c2ecf20Sopenharmony_ci chip->irq_write_msi_msg = platform_msi_write_msg; 1048c2ecf20Sopenharmony_ci if (WARN_ON((info->flags & MSI_FLAG_LEVEL_CAPABLE) && 1058c2ecf20Sopenharmony_ci !(chip->flags & IRQCHIP_SUPPORTS_LEVEL_MSI))) 1068c2ecf20Sopenharmony_ci info->flags &= ~MSI_FLAG_LEVEL_CAPABLE; 1078c2ecf20Sopenharmony_ci} 1088c2ecf20Sopenharmony_ci 1098c2ecf20Sopenharmony_cistatic void platform_msi_free_descs(struct device *dev, int base, int nvec) 1108c2ecf20Sopenharmony_ci{ 1118c2ecf20Sopenharmony_ci struct msi_desc *desc, *tmp; 1128c2ecf20Sopenharmony_ci 1138c2ecf20Sopenharmony_ci list_for_each_entry_safe(desc, tmp, dev_to_msi_list(dev), list) { 1148c2ecf20Sopenharmony_ci if (desc->platform.msi_index >= base && 1158c2ecf20Sopenharmony_ci desc->platform.msi_index < (base + nvec)) { 1168c2ecf20Sopenharmony_ci list_del(&desc->list); 1178c2ecf20Sopenharmony_ci free_msi_entry(desc); 1188c2ecf20Sopenharmony_ci } 1198c2ecf20Sopenharmony_ci } 1208c2ecf20Sopenharmony_ci} 1218c2ecf20Sopenharmony_ci 1228c2ecf20Sopenharmony_cistatic int platform_msi_alloc_descs_with_irq(struct device *dev, int virq, 1238c2ecf20Sopenharmony_ci int nvec, 1248c2ecf20Sopenharmony_ci struct platform_msi_priv_data *data) 1258c2ecf20Sopenharmony_ci 1268c2ecf20Sopenharmony_ci{ 1278c2ecf20Sopenharmony_ci struct msi_desc *desc; 1288c2ecf20Sopenharmony_ci int i, base = 0; 1298c2ecf20Sopenharmony_ci 1308c2ecf20Sopenharmony_ci if (!list_empty(dev_to_msi_list(dev))) { 1318c2ecf20Sopenharmony_ci desc = list_last_entry(dev_to_msi_list(dev), 1328c2ecf20Sopenharmony_ci struct msi_desc, list); 1338c2ecf20Sopenharmony_ci base = desc->platform.msi_index + 1; 1348c2ecf20Sopenharmony_ci } 1358c2ecf20Sopenharmony_ci 1368c2ecf20Sopenharmony_ci for (i = 0; i < nvec; i++) { 1378c2ecf20Sopenharmony_ci desc = alloc_msi_entry(dev, 1, NULL); 1388c2ecf20Sopenharmony_ci if (!desc) 1398c2ecf20Sopenharmony_ci break; 1408c2ecf20Sopenharmony_ci 1418c2ecf20Sopenharmony_ci desc->platform.msi_priv_data = data; 1428c2ecf20Sopenharmony_ci desc->platform.msi_index = base + i; 1438c2ecf20Sopenharmony_ci desc->irq = virq ? virq + i : 0; 1448c2ecf20Sopenharmony_ci 1458c2ecf20Sopenharmony_ci list_add_tail(&desc->list, dev_to_msi_list(dev)); 1468c2ecf20Sopenharmony_ci } 1478c2ecf20Sopenharmony_ci 1488c2ecf20Sopenharmony_ci if (i != nvec) { 1498c2ecf20Sopenharmony_ci /* Clean up the mess */ 1508c2ecf20Sopenharmony_ci platform_msi_free_descs(dev, base, nvec); 1518c2ecf20Sopenharmony_ci 1528c2ecf20Sopenharmony_ci return -ENOMEM; 1538c2ecf20Sopenharmony_ci } 1548c2ecf20Sopenharmony_ci 1558c2ecf20Sopenharmony_ci return 0; 1568c2ecf20Sopenharmony_ci} 1578c2ecf20Sopenharmony_ci 1588c2ecf20Sopenharmony_cistatic int platform_msi_alloc_descs(struct device *dev, int nvec, 1598c2ecf20Sopenharmony_ci struct platform_msi_priv_data *data) 1608c2ecf20Sopenharmony_ci 1618c2ecf20Sopenharmony_ci{ 1628c2ecf20Sopenharmony_ci return platform_msi_alloc_descs_with_irq(dev, 0, nvec, data); 1638c2ecf20Sopenharmony_ci} 1648c2ecf20Sopenharmony_ci 1658c2ecf20Sopenharmony_ci/** 1668c2ecf20Sopenharmony_ci * platform_msi_create_irq_domain - Create a platform MSI interrupt domain 1678c2ecf20Sopenharmony_ci * @fwnode: Optional fwnode of the interrupt controller 1688c2ecf20Sopenharmony_ci * @info: MSI domain info 1698c2ecf20Sopenharmony_ci * @parent: Parent irq domain 1708c2ecf20Sopenharmony_ci * 1718c2ecf20Sopenharmony_ci * Updates the domain and chip ops and creates a platform MSI 1728c2ecf20Sopenharmony_ci * interrupt domain. 1738c2ecf20Sopenharmony_ci * 1748c2ecf20Sopenharmony_ci * Returns: 1758c2ecf20Sopenharmony_ci * A domain pointer or NULL in case of failure. 1768c2ecf20Sopenharmony_ci */ 1778c2ecf20Sopenharmony_cistruct irq_domain *platform_msi_create_irq_domain(struct fwnode_handle *fwnode, 1788c2ecf20Sopenharmony_ci struct msi_domain_info *info, 1798c2ecf20Sopenharmony_ci struct irq_domain *parent) 1808c2ecf20Sopenharmony_ci{ 1818c2ecf20Sopenharmony_ci struct irq_domain *domain; 1828c2ecf20Sopenharmony_ci 1838c2ecf20Sopenharmony_ci if (info->flags & MSI_FLAG_USE_DEF_DOM_OPS) 1848c2ecf20Sopenharmony_ci platform_msi_update_dom_ops(info); 1858c2ecf20Sopenharmony_ci if (info->flags & MSI_FLAG_USE_DEF_CHIP_OPS) 1868c2ecf20Sopenharmony_ci platform_msi_update_chip_ops(info); 1878c2ecf20Sopenharmony_ci 1888c2ecf20Sopenharmony_ci domain = msi_create_irq_domain(fwnode, info, parent); 1898c2ecf20Sopenharmony_ci if (domain) 1908c2ecf20Sopenharmony_ci irq_domain_update_bus_token(domain, DOMAIN_BUS_PLATFORM_MSI); 1918c2ecf20Sopenharmony_ci 1928c2ecf20Sopenharmony_ci return domain; 1938c2ecf20Sopenharmony_ci} 1948c2ecf20Sopenharmony_ci 1958c2ecf20Sopenharmony_cistatic struct platform_msi_priv_data * 1968c2ecf20Sopenharmony_ciplatform_msi_alloc_priv_data(struct device *dev, unsigned int nvec, 1978c2ecf20Sopenharmony_ci irq_write_msi_msg_t write_msi_msg) 1988c2ecf20Sopenharmony_ci{ 1998c2ecf20Sopenharmony_ci struct platform_msi_priv_data *datap; 2008c2ecf20Sopenharmony_ci /* 2018c2ecf20Sopenharmony_ci * Limit the number of interrupts to 2048 per device. Should we 2028c2ecf20Sopenharmony_ci * need to bump this up, DEV_ID_SHIFT should be adjusted 2038c2ecf20Sopenharmony_ci * accordingly (which would impact the max number of MSI 2048c2ecf20Sopenharmony_ci * capable devices). 2058c2ecf20Sopenharmony_ci */ 2068c2ecf20Sopenharmony_ci if (!dev->msi_domain || !write_msi_msg || !nvec || nvec > MAX_DEV_MSIS) 2078c2ecf20Sopenharmony_ci return ERR_PTR(-EINVAL); 2088c2ecf20Sopenharmony_ci 2098c2ecf20Sopenharmony_ci if (dev->msi_domain->bus_token != DOMAIN_BUS_PLATFORM_MSI) { 2108c2ecf20Sopenharmony_ci dev_err(dev, "Incompatible msi_domain, giving up\n"); 2118c2ecf20Sopenharmony_ci return ERR_PTR(-EINVAL); 2128c2ecf20Sopenharmony_ci } 2138c2ecf20Sopenharmony_ci 2148c2ecf20Sopenharmony_ci /* Already had a helping of MSI? Greed... */ 2158c2ecf20Sopenharmony_ci if (!list_empty(dev_to_msi_list(dev))) 2168c2ecf20Sopenharmony_ci return ERR_PTR(-EBUSY); 2178c2ecf20Sopenharmony_ci 2188c2ecf20Sopenharmony_ci datap = kzalloc(sizeof(*datap), GFP_KERNEL); 2198c2ecf20Sopenharmony_ci if (!datap) 2208c2ecf20Sopenharmony_ci return ERR_PTR(-ENOMEM); 2218c2ecf20Sopenharmony_ci 2228c2ecf20Sopenharmony_ci datap->devid = ida_simple_get(&platform_msi_devid_ida, 2238c2ecf20Sopenharmony_ci 0, 1 << DEV_ID_SHIFT, GFP_KERNEL); 2248c2ecf20Sopenharmony_ci if (datap->devid < 0) { 2258c2ecf20Sopenharmony_ci int err = datap->devid; 2268c2ecf20Sopenharmony_ci kfree(datap); 2278c2ecf20Sopenharmony_ci return ERR_PTR(err); 2288c2ecf20Sopenharmony_ci } 2298c2ecf20Sopenharmony_ci 2308c2ecf20Sopenharmony_ci datap->write_msg = write_msi_msg; 2318c2ecf20Sopenharmony_ci datap->dev = dev; 2328c2ecf20Sopenharmony_ci 2338c2ecf20Sopenharmony_ci return datap; 2348c2ecf20Sopenharmony_ci} 2358c2ecf20Sopenharmony_ci 2368c2ecf20Sopenharmony_cistatic void platform_msi_free_priv_data(struct platform_msi_priv_data *data) 2378c2ecf20Sopenharmony_ci{ 2388c2ecf20Sopenharmony_ci ida_simple_remove(&platform_msi_devid_ida, data->devid); 2398c2ecf20Sopenharmony_ci kfree(data); 2408c2ecf20Sopenharmony_ci} 2418c2ecf20Sopenharmony_ci 2428c2ecf20Sopenharmony_ci/** 2438c2ecf20Sopenharmony_ci * platform_msi_domain_alloc_irqs - Allocate MSI interrupts for @dev 2448c2ecf20Sopenharmony_ci * @dev: The device for which to allocate interrupts 2458c2ecf20Sopenharmony_ci * @nvec: The number of interrupts to allocate 2468c2ecf20Sopenharmony_ci * @write_msi_msg: Callback to write an interrupt message for @dev 2478c2ecf20Sopenharmony_ci * 2488c2ecf20Sopenharmony_ci * Returns: 2498c2ecf20Sopenharmony_ci * Zero for success, or an error code in case of failure 2508c2ecf20Sopenharmony_ci */ 2518c2ecf20Sopenharmony_ciint platform_msi_domain_alloc_irqs(struct device *dev, unsigned int nvec, 2528c2ecf20Sopenharmony_ci irq_write_msi_msg_t write_msi_msg) 2538c2ecf20Sopenharmony_ci{ 2548c2ecf20Sopenharmony_ci struct platform_msi_priv_data *priv_data; 2558c2ecf20Sopenharmony_ci int err; 2568c2ecf20Sopenharmony_ci 2578c2ecf20Sopenharmony_ci priv_data = platform_msi_alloc_priv_data(dev, nvec, write_msi_msg); 2588c2ecf20Sopenharmony_ci if (IS_ERR(priv_data)) 2598c2ecf20Sopenharmony_ci return PTR_ERR(priv_data); 2608c2ecf20Sopenharmony_ci 2618c2ecf20Sopenharmony_ci err = platform_msi_alloc_descs(dev, nvec, priv_data); 2628c2ecf20Sopenharmony_ci if (err) 2638c2ecf20Sopenharmony_ci goto out_free_priv_data; 2648c2ecf20Sopenharmony_ci 2658c2ecf20Sopenharmony_ci err = msi_domain_alloc_irqs(dev->msi_domain, dev, nvec); 2668c2ecf20Sopenharmony_ci if (err) 2678c2ecf20Sopenharmony_ci goto out_free_desc; 2688c2ecf20Sopenharmony_ci 2698c2ecf20Sopenharmony_ci return 0; 2708c2ecf20Sopenharmony_ci 2718c2ecf20Sopenharmony_ciout_free_desc: 2728c2ecf20Sopenharmony_ci platform_msi_free_descs(dev, 0, nvec); 2738c2ecf20Sopenharmony_ciout_free_priv_data: 2748c2ecf20Sopenharmony_ci platform_msi_free_priv_data(priv_data); 2758c2ecf20Sopenharmony_ci 2768c2ecf20Sopenharmony_ci return err; 2778c2ecf20Sopenharmony_ci} 2788c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(platform_msi_domain_alloc_irqs); 2798c2ecf20Sopenharmony_ci 2808c2ecf20Sopenharmony_ci/** 2818c2ecf20Sopenharmony_ci * platform_msi_domain_free_irqs - Free MSI interrupts for @dev 2828c2ecf20Sopenharmony_ci * @dev: The device for which to free interrupts 2838c2ecf20Sopenharmony_ci */ 2848c2ecf20Sopenharmony_civoid platform_msi_domain_free_irqs(struct device *dev) 2858c2ecf20Sopenharmony_ci{ 2868c2ecf20Sopenharmony_ci if (!list_empty(dev_to_msi_list(dev))) { 2878c2ecf20Sopenharmony_ci struct msi_desc *desc; 2888c2ecf20Sopenharmony_ci 2898c2ecf20Sopenharmony_ci desc = first_msi_entry(dev); 2908c2ecf20Sopenharmony_ci platform_msi_free_priv_data(desc->platform.msi_priv_data); 2918c2ecf20Sopenharmony_ci } 2928c2ecf20Sopenharmony_ci 2938c2ecf20Sopenharmony_ci msi_domain_free_irqs(dev->msi_domain, dev); 2948c2ecf20Sopenharmony_ci platform_msi_free_descs(dev, 0, MAX_DEV_MSIS); 2958c2ecf20Sopenharmony_ci} 2968c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(platform_msi_domain_free_irqs); 2978c2ecf20Sopenharmony_ci 2988c2ecf20Sopenharmony_ci/** 2998c2ecf20Sopenharmony_ci * platform_msi_get_host_data - Query the private data associated with 3008c2ecf20Sopenharmony_ci * a platform-msi domain 3018c2ecf20Sopenharmony_ci * @domain: The platform-msi domain 3028c2ecf20Sopenharmony_ci * 3038c2ecf20Sopenharmony_ci * Returns the private data provided when calling 3048c2ecf20Sopenharmony_ci * platform_msi_create_device_domain. 3058c2ecf20Sopenharmony_ci */ 3068c2ecf20Sopenharmony_civoid *platform_msi_get_host_data(struct irq_domain *domain) 3078c2ecf20Sopenharmony_ci{ 3088c2ecf20Sopenharmony_ci struct platform_msi_priv_data *data = domain->host_data; 3098c2ecf20Sopenharmony_ci return data->host_data; 3108c2ecf20Sopenharmony_ci} 3118c2ecf20Sopenharmony_ci 3128c2ecf20Sopenharmony_ci/** 3138c2ecf20Sopenharmony_ci * platform_msi_create_device_domain - Create a platform-msi domain 3148c2ecf20Sopenharmony_ci * 3158c2ecf20Sopenharmony_ci * @dev: The device generating the MSIs 3168c2ecf20Sopenharmony_ci * @nvec: The number of MSIs that need to be allocated 3178c2ecf20Sopenharmony_ci * @write_msi_msg: Callback to write an interrupt message for @dev 3188c2ecf20Sopenharmony_ci * @ops: The hierarchy domain operations to use 3198c2ecf20Sopenharmony_ci * @host_data: Private data associated to this domain 3208c2ecf20Sopenharmony_ci * 3218c2ecf20Sopenharmony_ci * Returns an irqdomain for @nvec interrupts 3228c2ecf20Sopenharmony_ci */ 3238c2ecf20Sopenharmony_cistruct irq_domain * 3248c2ecf20Sopenharmony_ci__platform_msi_create_device_domain(struct device *dev, 3258c2ecf20Sopenharmony_ci unsigned int nvec, 3268c2ecf20Sopenharmony_ci bool is_tree, 3278c2ecf20Sopenharmony_ci irq_write_msi_msg_t write_msi_msg, 3288c2ecf20Sopenharmony_ci const struct irq_domain_ops *ops, 3298c2ecf20Sopenharmony_ci void *host_data) 3308c2ecf20Sopenharmony_ci{ 3318c2ecf20Sopenharmony_ci struct platform_msi_priv_data *data; 3328c2ecf20Sopenharmony_ci struct irq_domain *domain; 3338c2ecf20Sopenharmony_ci int err; 3348c2ecf20Sopenharmony_ci 3358c2ecf20Sopenharmony_ci data = platform_msi_alloc_priv_data(dev, nvec, write_msi_msg); 3368c2ecf20Sopenharmony_ci if (IS_ERR(data)) 3378c2ecf20Sopenharmony_ci return NULL; 3388c2ecf20Sopenharmony_ci 3398c2ecf20Sopenharmony_ci data->host_data = host_data; 3408c2ecf20Sopenharmony_ci domain = irq_domain_create_hierarchy(dev->msi_domain, 0, 3418c2ecf20Sopenharmony_ci is_tree ? 0 : nvec, 3428c2ecf20Sopenharmony_ci dev->fwnode, ops, data); 3438c2ecf20Sopenharmony_ci if (!domain) 3448c2ecf20Sopenharmony_ci goto free_priv; 3458c2ecf20Sopenharmony_ci 3468c2ecf20Sopenharmony_ci err = msi_domain_prepare_irqs(domain->parent, dev, nvec, &data->arg); 3478c2ecf20Sopenharmony_ci if (err < 0) 3488c2ecf20Sopenharmony_ci goto free_domain; 3498c2ecf20Sopenharmony_ci 3508c2ecf20Sopenharmony_ci return domain; 3518c2ecf20Sopenharmony_ci 3528c2ecf20Sopenharmony_cifree_domain: 3538c2ecf20Sopenharmony_ci irq_domain_remove(domain); 3548c2ecf20Sopenharmony_cifree_priv: 3558c2ecf20Sopenharmony_ci platform_msi_free_priv_data(data); 3568c2ecf20Sopenharmony_ci return NULL; 3578c2ecf20Sopenharmony_ci} 3588c2ecf20Sopenharmony_ci 3598c2ecf20Sopenharmony_ci/** 3608c2ecf20Sopenharmony_ci * platform_msi_domain_free - Free interrupts associated with a platform-msi 3618c2ecf20Sopenharmony_ci * domain 3628c2ecf20Sopenharmony_ci * 3638c2ecf20Sopenharmony_ci * @domain: The platform-msi domain 3648c2ecf20Sopenharmony_ci * @virq: The base irq from which to perform the free operation 3658c2ecf20Sopenharmony_ci * @nvec: How many interrupts to free from @virq 3668c2ecf20Sopenharmony_ci */ 3678c2ecf20Sopenharmony_civoid platform_msi_domain_free(struct irq_domain *domain, unsigned int virq, 3688c2ecf20Sopenharmony_ci unsigned int nvec) 3698c2ecf20Sopenharmony_ci{ 3708c2ecf20Sopenharmony_ci struct platform_msi_priv_data *data = domain->host_data; 3718c2ecf20Sopenharmony_ci struct msi_desc *desc, *tmp; 3728c2ecf20Sopenharmony_ci for_each_msi_entry_safe(desc, tmp, data->dev) { 3738c2ecf20Sopenharmony_ci if (WARN_ON(!desc->irq || desc->nvec_used != 1)) 3748c2ecf20Sopenharmony_ci return; 3758c2ecf20Sopenharmony_ci if (!(desc->irq >= virq && desc->irq < (virq + nvec))) 3768c2ecf20Sopenharmony_ci continue; 3778c2ecf20Sopenharmony_ci 3788c2ecf20Sopenharmony_ci irq_domain_free_irqs_common(domain, desc->irq, 1); 3798c2ecf20Sopenharmony_ci list_del(&desc->list); 3808c2ecf20Sopenharmony_ci free_msi_entry(desc); 3818c2ecf20Sopenharmony_ci } 3828c2ecf20Sopenharmony_ci} 3838c2ecf20Sopenharmony_ci 3848c2ecf20Sopenharmony_ci/** 3858c2ecf20Sopenharmony_ci * platform_msi_domain_alloc - Allocate interrupts associated with 3868c2ecf20Sopenharmony_ci * a platform-msi domain 3878c2ecf20Sopenharmony_ci * 3888c2ecf20Sopenharmony_ci * @domain: The platform-msi domain 3898c2ecf20Sopenharmony_ci * @virq: The base irq from which to perform the allocate operation 3908c2ecf20Sopenharmony_ci * @nr_irqs: How many interrupts to free from @virq 3918c2ecf20Sopenharmony_ci * 3928c2ecf20Sopenharmony_ci * Return 0 on success, or an error code on failure. Must be called 3938c2ecf20Sopenharmony_ci * with irq_domain_mutex held (which can only be done as part of a 3948c2ecf20Sopenharmony_ci * top-level interrupt allocation). 3958c2ecf20Sopenharmony_ci */ 3968c2ecf20Sopenharmony_ciint platform_msi_domain_alloc(struct irq_domain *domain, unsigned int virq, 3978c2ecf20Sopenharmony_ci unsigned int nr_irqs) 3988c2ecf20Sopenharmony_ci{ 3998c2ecf20Sopenharmony_ci struct platform_msi_priv_data *data = domain->host_data; 4008c2ecf20Sopenharmony_ci int err; 4018c2ecf20Sopenharmony_ci 4028c2ecf20Sopenharmony_ci err = platform_msi_alloc_descs_with_irq(data->dev, virq, nr_irqs, data); 4038c2ecf20Sopenharmony_ci if (err) 4048c2ecf20Sopenharmony_ci return err; 4058c2ecf20Sopenharmony_ci 4068c2ecf20Sopenharmony_ci err = msi_domain_populate_irqs(domain->parent, data->dev, 4078c2ecf20Sopenharmony_ci virq, nr_irqs, &data->arg); 4088c2ecf20Sopenharmony_ci if (err) 4098c2ecf20Sopenharmony_ci platform_msi_domain_free(domain, virq, nr_irqs); 4108c2ecf20Sopenharmony_ci 4118c2ecf20Sopenharmony_ci return err; 4128c2ecf20Sopenharmony_ci} 413