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