Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0
0002 /*
0003  * MSI framework for platform devices
0004  *
0005  * Copyright (C) 2015 ARM Limited, All Rights Reserved.
0006  * Author: Marc Zyngier <marc.zyngier@arm.com>
0007  */
0008 
0009 #include <linux/device.h>
0010 #include <linux/idr.h>
0011 #include <linux/irq.h>
0012 #include <linux/irqdomain.h>
0013 #include <linux/msi.h>
0014 #include <linux/slab.h>
0015 
0016 #define DEV_ID_SHIFT    21
0017 #define MAX_DEV_MSIS    (1 << (32 - DEV_ID_SHIFT))
0018 
0019 /*
0020  * Internal data structure containing a (made up, but unique) devid
0021  * and the callback to write the MSI message.
0022  */
0023 struct platform_msi_priv_data {
0024     struct device           *dev;
0025     void                *host_data;
0026     msi_alloc_info_t        arg;
0027     irq_write_msi_msg_t     write_msg;
0028     int             devid;
0029 };
0030 
0031 /* The devid allocator */
0032 static DEFINE_IDA(platform_msi_devid_ida);
0033 
0034 #ifdef GENERIC_MSI_DOMAIN_OPS
0035 /*
0036  * Convert an msi_desc to a globaly unique identifier (per-device
0037  * devid + msi_desc position in the msi_list).
0038  */
0039 static irq_hw_number_t platform_msi_calc_hwirq(struct msi_desc *desc)
0040 {
0041     u32 devid = desc->dev->msi.data->platform_data->devid;
0042 
0043     return (devid << (32 - DEV_ID_SHIFT)) | desc->msi_index;
0044 }
0045 
0046 static void platform_msi_set_desc(msi_alloc_info_t *arg, struct msi_desc *desc)
0047 {
0048     arg->desc = desc;
0049     arg->hwirq = platform_msi_calc_hwirq(desc);
0050 }
0051 
0052 static int platform_msi_init(struct irq_domain *domain,
0053                  struct msi_domain_info *info,
0054                  unsigned int virq, irq_hw_number_t hwirq,
0055                  msi_alloc_info_t *arg)
0056 {
0057     return irq_domain_set_hwirq_and_chip(domain, virq, hwirq,
0058                          info->chip, info->chip_data);
0059 }
0060 
0061 static void platform_msi_set_proxy_dev(msi_alloc_info_t *arg)
0062 {
0063     arg->flags |= MSI_ALLOC_FLAGS_PROXY_DEVICE;
0064 }
0065 #else
0066 #define platform_msi_set_desc       NULL
0067 #define platform_msi_init       NULL
0068 #define platform_msi_set_proxy_dev(x)   do {} while(0)
0069 #endif
0070 
0071 static void platform_msi_update_dom_ops(struct msi_domain_info *info)
0072 {
0073     struct msi_domain_ops *ops = info->ops;
0074 
0075     BUG_ON(!ops);
0076 
0077     if (ops->msi_init == NULL)
0078         ops->msi_init = platform_msi_init;
0079     if (ops->set_desc == NULL)
0080         ops->set_desc = platform_msi_set_desc;
0081 }
0082 
0083 static void platform_msi_write_msg(struct irq_data *data, struct msi_msg *msg)
0084 {
0085     struct msi_desc *desc = irq_data_get_msi_desc(data);
0086 
0087     desc->dev->msi.data->platform_data->write_msg(desc, msg);
0088 }
0089 
0090 static void platform_msi_update_chip_ops(struct msi_domain_info *info)
0091 {
0092     struct irq_chip *chip = info->chip;
0093 
0094     BUG_ON(!chip);
0095     if (!chip->irq_mask)
0096         chip->irq_mask = irq_chip_mask_parent;
0097     if (!chip->irq_unmask)
0098         chip->irq_unmask = irq_chip_unmask_parent;
0099     if (!chip->irq_eoi)
0100         chip->irq_eoi = irq_chip_eoi_parent;
0101     if (!chip->irq_set_affinity)
0102         chip->irq_set_affinity = msi_domain_set_affinity;
0103     if (!chip->irq_write_msi_msg)
0104         chip->irq_write_msi_msg = platform_msi_write_msg;
0105     if (WARN_ON((info->flags & MSI_FLAG_LEVEL_CAPABLE) &&
0106             !(chip->flags & IRQCHIP_SUPPORTS_LEVEL_MSI)))
0107         info->flags &= ~MSI_FLAG_LEVEL_CAPABLE;
0108 }
0109 
0110 /**
0111  * platform_msi_create_irq_domain - Create a platform MSI interrupt domain
0112  * @fwnode:     Optional fwnode of the interrupt controller
0113  * @info:   MSI domain info
0114  * @parent: Parent irq domain
0115  *
0116  * Updates the domain and chip ops and creates a platform MSI
0117  * interrupt domain.
0118  *
0119  * Returns:
0120  * A domain pointer or NULL in case of failure.
0121  */
0122 struct irq_domain *platform_msi_create_irq_domain(struct fwnode_handle *fwnode,
0123                           struct msi_domain_info *info,
0124                           struct irq_domain *parent)
0125 {
0126     struct irq_domain *domain;
0127 
0128     if (info->flags & MSI_FLAG_USE_DEF_DOM_OPS)
0129         platform_msi_update_dom_ops(info);
0130     if (info->flags & MSI_FLAG_USE_DEF_CHIP_OPS)
0131         platform_msi_update_chip_ops(info);
0132     info->flags |= MSI_FLAG_DEV_SYSFS | MSI_FLAG_ALLOC_SIMPLE_MSI_DESCS |
0133                MSI_FLAG_FREE_MSI_DESCS;
0134 
0135     domain = msi_create_irq_domain(fwnode, info, parent);
0136     if (domain)
0137         irq_domain_update_bus_token(domain, DOMAIN_BUS_PLATFORM_MSI);
0138 
0139     return domain;
0140 }
0141 
0142 static int platform_msi_alloc_priv_data(struct device *dev, unsigned int nvec,
0143                     irq_write_msi_msg_t write_msi_msg)
0144 {
0145     struct platform_msi_priv_data *datap;
0146     int err;
0147 
0148     /*
0149      * Limit the number of interrupts to 2048 per device. Should we
0150      * need to bump this up, DEV_ID_SHIFT should be adjusted
0151      * accordingly (which would impact the max number of MSI
0152      * capable devices).
0153      */
0154     if (!dev->msi.domain || !write_msi_msg || !nvec || nvec > MAX_DEV_MSIS)
0155         return -EINVAL;
0156 
0157     if (dev->msi.domain->bus_token != DOMAIN_BUS_PLATFORM_MSI) {
0158         dev_err(dev, "Incompatible msi_domain, giving up\n");
0159         return -EINVAL;
0160     }
0161 
0162     err = msi_setup_device_data(dev);
0163     if (err)
0164         return err;
0165 
0166     /* Already initialized? */
0167     if (dev->msi.data->platform_data)
0168         return -EBUSY;
0169 
0170     datap = kzalloc(sizeof(*datap), GFP_KERNEL);
0171     if (!datap)
0172         return -ENOMEM;
0173 
0174     datap->devid = ida_simple_get(&platform_msi_devid_ida,
0175                       0, 1 << DEV_ID_SHIFT, GFP_KERNEL);
0176     if (datap->devid < 0) {
0177         err = datap->devid;
0178         kfree(datap);
0179         return err;
0180     }
0181 
0182     datap->write_msg = write_msi_msg;
0183     datap->dev = dev;
0184     dev->msi.data->platform_data = datap;
0185     return 0;
0186 }
0187 
0188 static void platform_msi_free_priv_data(struct device *dev)
0189 {
0190     struct platform_msi_priv_data *data = dev->msi.data->platform_data;
0191 
0192     dev->msi.data->platform_data = NULL;
0193     ida_simple_remove(&platform_msi_devid_ida, data->devid);
0194     kfree(data);
0195 }
0196 
0197 /**
0198  * platform_msi_domain_alloc_irqs - Allocate MSI interrupts for @dev
0199  * @dev:        The device for which to allocate interrupts
0200  * @nvec:       The number of interrupts to allocate
0201  * @write_msi_msg:  Callback to write an interrupt message for @dev
0202  *
0203  * Returns:
0204  * Zero for success, or an error code in case of failure
0205  */
0206 int platform_msi_domain_alloc_irqs(struct device *dev, unsigned int nvec,
0207                    irq_write_msi_msg_t write_msi_msg)
0208 {
0209     int err;
0210 
0211     err = platform_msi_alloc_priv_data(dev, nvec, write_msi_msg);
0212     if (err)
0213         return err;
0214 
0215     err = msi_domain_alloc_irqs(dev->msi.domain, dev, nvec);
0216     if (err)
0217         platform_msi_free_priv_data(dev);
0218 
0219     return err;
0220 }
0221 EXPORT_SYMBOL_GPL(platform_msi_domain_alloc_irqs);
0222 
0223 /**
0224  * platform_msi_domain_free_irqs - Free MSI interrupts for @dev
0225  * @dev:    The device for which to free interrupts
0226  */
0227 void platform_msi_domain_free_irqs(struct device *dev)
0228 {
0229     msi_domain_free_irqs(dev->msi.domain, dev);
0230     platform_msi_free_priv_data(dev);
0231 }
0232 EXPORT_SYMBOL_GPL(platform_msi_domain_free_irqs);
0233 
0234 /**
0235  * platform_msi_get_host_data - Query the private data associated with
0236  *                              a platform-msi domain
0237  * @domain: The platform-msi domain
0238  *
0239  * Return: The private data provided when calling
0240  * platform_msi_create_device_domain().
0241  */
0242 void *platform_msi_get_host_data(struct irq_domain *domain)
0243 {
0244     struct platform_msi_priv_data *data = domain->host_data;
0245 
0246     return data->host_data;
0247 }
0248 
0249 static struct lock_class_key platform_device_msi_lock_class;
0250 
0251 /**
0252  * __platform_msi_create_device_domain - Create a platform-msi device domain
0253  *
0254  * @dev:        The device generating the MSIs
0255  * @nvec:       The number of MSIs that need to be allocated
0256  * @is_tree:        flag to indicate tree hierarchy
0257  * @write_msi_msg:  Callback to write an interrupt message for @dev
0258  * @ops:        The hierarchy domain operations to use
0259  * @host_data:      Private data associated to this domain
0260  *
0261  * Return: An irqdomain for @nvec interrupts on success, NULL in case of error.
0262  *
0263  * This is for interrupt domains which stack on a platform-msi domain
0264  * created by platform_msi_create_irq_domain(). @dev->msi.domain points to
0265  * that platform-msi domain which is the parent for the new domain.
0266  */
0267 struct irq_domain *
0268 __platform_msi_create_device_domain(struct device *dev,
0269                     unsigned int nvec,
0270                     bool is_tree,
0271                     irq_write_msi_msg_t write_msi_msg,
0272                     const struct irq_domain_ops *ops,
0273                     void *host_data)
0274 {
0275     struct platform_msi_priv_data *data;
0276     struct irq_domain *domain;
0277     int err;
0278 
0279     err = platform_msi_alloc_priv_data(dev, nvec, write_msi_msg);
0280     if (err)
0281         return NULL;
0282 
0283     /*
0284      * Use a separate lock class for the MSI descriptor mutex on
0285      * platform MSI device domains because the descriptor mutex nests
0286      * into the domain mutex. See alloc/free below.
0287      */
0288     lockdep_set_class(&dev->msi.data->mutex, &platform_device_msi_lock_class);
0289 
0290     data = dev->msi.data->platform_data;
0291     data->host_data = host_data;
0292     domain = irq_domain_create_hierarchy(dev->msi.domain, 0,
0293                          is_tree ? 0 : nvec,
0294                          dev->fwnode, ops, data);
0295     if (!domain)
0296         goto free_priv;
0297 
0298     platform_msi_set_proxy_dev(&data->arg);
0299     err = msi_domain_prepare_irqs(domain->parent, dev, nvec, &data->arg);
0300     if (err)
0301         goto free_domain;
0302 
0303     return domain;
0304 
0305 free_domain:
0306     irq_domain_remove(domain);
0307 free_priv:
0308     platform_msi_free_priv_data(dev);
0309     return NULL;
0310 }
0311 
0312 /**
0313  * platform_msi_device_domain_free - Free interrupts associated with a platform-msi
0314  *                   device domain
0315  *
0316  * @domain: The platform-msi device domain
0317  * @virq:   The base irq from which to perform the free operation
0318  * @nr_irqs:    How many interrupts to free from @virq
0319  */
0320 void platform_msi_device_domain_free(struct irq_domain *domain, unsigned int virq,
0321                      unsigned int nr_irqs)
0322 {
0323     struct platform_msi_priv_data *data = domain->host_data;
0324 
0325     msi_lock_descs(data->dev);
0326     irq_domain_free_irqs_common(domain, virq, nr_irqs);
0327     msi_free_msi_descs_range(data->dev, MSI_DESC_ALL, virq, virq + nr_irqs - 1);
0328     msi_unlock_descs(data->dev);
0329 }
0330 
0331 /**
0332  * platform_msi_device_domain_alloc - Allocate interrupts associated with
0333  *                    a platform-msi device domain
0334  *
0335  * @domain: The platform-msi device domain
0336  * @virq:   The base irq from which to perform the allocate operation
0337  * @nr_irqs:    How many interrupts to allocate from @virq
0338  *
0339  * Return 0 on success, or an error code on failure. Must be called
0340  * with irq_domain_mutex held (which can only be done as part of a
0341  * top-level interrupt allocation).
0342  */
0343 int platform_msi_device_domain_alloc(struct irq_domain *domain, unsigned int virq,
0344                      unsigned int nr_irqs)
0345 {
0346     struct platform_msi_priv_data *data = domain->host_data;
0347     struct device *dev = data->dev;
0348 
0349     return msi_domain_populate_irqs(domain->parent, dev, virq, nr_irqs, &data->arg);
0350 }