root/drivers/base/platform-msi.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. platform_msi_calc_hwirq
  2. platform_msi_set_desc
  3. platform_msi_init
  4. platform_msi_update_dom_ops
  5. platform_msi_write_msg
  6. platform_msi_update_chip_ops
  7. platform_msi_free_descs
  8. platform_msi_alloc_descs_with_irq
  9. platform_msi_alloc_descs
  10. platform_msi_create_irq_domain
  11. platform_msi_alloc_priv_data
  12. platform_msi_free_priv_data
  13. platform_msi_domain_alloc_irqs
  14. platform_msi_domain_free_irqs
  15. platform_msi_get_host_data
  16. __platform_msi_create_device_domain
  17. platform_msi_domain_free
  18. platform_msi_domain_alloc

   1 // SPDX-License-Identifier: GPL-2.0
   2 /*
   3  * MSI framework for platform devices
   4  *
   5  * Copyright (C) 2015 ARM Limited, All Rights Reserved.
   6  * Author: Marc Zyngier <marc.zyngier@arm.com>
   7  */
   8 
   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>
  15 
  16 #define DEV_ID_SHIFT    21
  17 #define MAX_DEV_MSIS    (1 << (32 - DEV_ID_SHIFT))
  18 
  19 /*
  20  * Internal data structure containing a (made up, but unique) devid
  21  * and the callback to write the MSI message.
  22  */
  23 struct platform_msi_priv_data {
  24         struct device           *dev;
  25         void                    *host_data;
  26         msi_alloc_info_t        arg;
  27         irq_write_msi_msg_t     write_msg;
  28         int                     devid;
  29 };
  30 
  31 /* The devid allocator */
  32 static DEFINE_IDA(platform_msi_devid_ida);
  33 
  34 #ifdef GENERIC_MSI_DOMAIN_OPS
  35 /*
  36  * Convert an msi_desc to a globaly unique identifier (per-device
  37  * devid + msi_desc position in the msi_list).
  38  */
  39 static irq_hw_number_t platform_msi_calc_hwirq(struct msi_desc *desc)
  40 {
  41         u32 devid;
  42 
  43         devid = desc->platform.msi_priv_data->devid;
  44 
  45         return (devid << (32 - DEV_ID_SHIFT)) | desc->platform.msi_index;
  46 }
  47 
  48 static void platform_msi_set_desc(msi_alloc_info_t *arg, struct msi_desc *desc)
  49 {
  50         arg->desc = desc;
  51         arg->hwirq = platform_msi_calc_hwirq(desc);
  52 }
  53 
  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)
  58 {
  59         return irq_domain_set_hwirq_and_chip(domain, virq, hwirq,
  60                                              info->chip, info->chip_data);
  61 }
  62 #else
  63 #define platform_msi_set_desc           NULL
  64 #define platform_msi_init               NULL
  65 #endif
  66 
  67 static void platform_msi_update_dom_ops(struct msi_domain_info *info)
  68 {
  69         struct msi_domain_ops *ops = info->ops;
  70 
  71         BUG_ON(!ops);
  72 
  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;
  77 }
  78 
  79 static void platform_msi_write_msg(struct irq_data *data, struct msi_msg *msg)
  80 {
  81         struct msi_desc *desc = irq_data_get_msi_desc(data);
  82         struct platform_msi_priv_data *priv_data;
  83 
  84         priv_data = desc->platform.msi_priv_data;
  85 
  86         priv_data->write_msg(desc, msg);
  87 }
  88 
  89 static void platform_msi_update_chip_ops(struct msi_domain_info *info)
  90 {
  91         struct irq_chip *chip = info->chip;
  92 
  93         BUG_ON(!chip);
  94         if (!chip->irq_mask)
  95                 chip->irq_mask = irq_chip_mask_parent;
  96         if (!chip->irq_unmask)
  97                 chip->irq_unmask = irq_chip_unmask_parent;
  98         if (!chip->irq_eoi)
  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;
 104         if (WARN_ON((info->flags & MSI_FLAG_LEVEL_CAPABLE) &&
 105                     !(chip->flags & IRQCHIP_SUPPORTS_LEVEL_MSI)))
 106                 info->flags &= ~MSI_FLAG_LEVEL_CAPABLE;
 107 }
 108 
 109 static void platform_msi_free_descs(struct device *dev, int base, int nvec)
 110 {
 111         struct msi_desc *desc, *tmp;
 112 
 113         list_for_each_entry_safe(desc, tmp, dev_to_msi_list(dev), list) {
 114                 if (desc->platform.msi_index >= base &&
 115                     desc->platform.msi_index < (base + nvec)) {
 116                         list_del(&desc->list);
 117                         free_msi_entry(desc);
 118                 }
 119         }
 120 }
 121 
 122 static int platform_msi_alloc_descs_with_irq(struct device *dev, int virq,
 123                                              int nvec,
 124                                              struct platform_msi_priv_data *data)
 125 
 126 {
 127         struct msi_desc *desc;
 128         int i, base = 0;
 129 
 130         if (!list_empty(dev_to_msi_list(dev))) {
 131                 desc = list_last_entry(dev_to_msi_list(dev),
 132                                        struct msi_desc, list);
 133                 base = desc->platform.msi_index + 1;
 134         }
 135 
 136         for (i = 0; i < nvec; i++) {
 137                 desc = alloc_msi_entry(dev, 1, NULL);
 138                 if (!desc)
 139                         break;
 140 
 141                 desc->platform.msi_priv_data = data;
 142                 desc->platform.msi_index = base + i;
 143                 desc->irq = virq ? virq + i : 0;
 144 
 145                 list_add_tail(&desc->list, dev_to_msi_list(dev));
 146         }
 147 
 148         if (i != nvec) {
 149                 /* Clean up the mess */
 150                 platform_msi_free_descs(dev, base, nvec);
 151 
 152                 return -ENOMEM;
 153         }
 154 
 155         return 0;
 156 }
 157 
 158 static int platform_msi_alloc_descs(struct device *dev, int nvec,
 159                                     struct platform_msi_priv_data *data)
 160 
 161 {
 162         return platform_msi_alloc_descs_with_irq(dev, 0, nvec, data);
 163 }
 164 
 165 /**
 166  * platform_msi_create_irq_domain - Create a platform MSI interrupt domain
 167  * @fwnode:             Optional fwnode of the interrupt controller
 168  * @info:       MSI domain info
 169  * @parent:     Parent irq domain
 170  *
 171  * Updates the domain and chip ops and creates a platform MSI
 172  * interrupt domain.
 173  *
 174  * Returns:
 175  * A domain pointer or NULL in case of failure.
 176  */
 177 struct irq_domain *platform_msi_create_irq_domain(struct fwnode_handle *fwnode,
 178                                                   struct msi_domain_info *info,
 179                                                   struct irq_domain *parent)
 180 {
 181         struct irq_domain *domain;
 182 
 183         if (info->flags & MSI_FLAG_USE_DEF_DOM_OPS)
 184                 platform_msi_update_dom_ops(info);
 185         if (info->flags & MSI_FLAG_USE_DEF_CHIP_OPS)
 186                 platform_msi_update_chip_ops(info);
 187 
 188         domain = msi_create_irq_domain(fwnode, info, parent);
 189         if (domain)
 190                 irq_domain_update_bus_token(domain, DOMAIN_BUS_PLATFORM_MSI);
 191 
 192         return domain;
 193 }
 194 
 195 static struct platform_msi_priv_data *
 196 platform_msi_alloc_priv_data(struct device *dev, unsigned int nvec,
 197                              irq_write_msi_msg_t write_msi_msg)
 198 {
 199         struct platform_msi_priv_data *datap;
 200         /*
 201          * Limit the number of interrupts to 2048 per device. Should we
 202          * need to bump this up, DEV_ID_SHIFT should be adjusted
 203          * accordingly (which would impact the max number of MSI
 204          * capable devices).
 205          */
 206         if (!dev->msi_domain || !write_msi_msg || !nvec || nvec > MAX_DEV_MSIS)
 207                 return ERR_PTR(-EINVAL);
 208 
 209         if (dev->msi_domain->bus_token != DOMAIN_BUS_PLATFORM_MSI) {
 210                 dev_err(dev, "Incompatible msi_domain, giving up\n");
 211                 return ERR_PTR(-EINVAL);
 212         }
 213 
 214         /* Already had a helping of MSI? Greed... */
 215         if (!list_empty(dev_to_msi_list(dev)))
 216                 return ERR_PTR(-EBUSY);
 217 
 218         datap = kzalloc(sizeof(*datap), GFP_KERNEL);
 219         if (!datap)
 220                 return ERR_PTR(-ENOMEM);
 221 
 222         datap->devid = ida_simple_get(&platform_msi_devid_ida,
 223                                       0, 1 << DEV_ID_SHIFT, GFP_KERNEL);
 224         if (datap->devid < 0) {
 225                 int err = datap->devid;
 226                 kfree(datap);
 227                 return ERR_PTR(err);
 228         }
 229 
 230         datap->write_msg = write_msi_msg;
 231         datap->dev = dev;
 232 
 233         return datap;
 234 }
 235 
 236 static void platform_msi_free_priv_data(struct platform_msi_priv_data *data)
 237 {
 238         ida_simple_remove(&platform_msi_devid_ida, data->devid);
 239         kfree(data);
 240 }
 241 
 242 /**
 243  * platform_msi_domain_alloc_irqs - Allocate MSI interrupts for @dev
 244  * @dev:                The device for which to allocate interrupts
 245  * @nvec:               The number of interrupts to allocate
 246  * @write_msi_msg:      Callback to write an interrupt message for @dev
 247  *
 248  * Returns:
 249  * Zero for success, or an error code in case of failure
 250  */
 251 int platform_msi_domain_alloc_irqs(struct device *dev, unsigned int nvec,
 252                                    irq_write_msi_msg_t write_msi_msg)
 253 {
 254         struct platform_msi_priv_data *priv_data;
 255         int err;
 256 
 257         priv_data = platform_msi_alloc_priv_data(dev, nvec, write_msi_msg);
 258         if (IS_ERR(priv_data))
 259                 return PTR_ERR(priv_data);
 260 
 261         err = platform_msi_alloc_descs(dev, nvec, priv_data);
 262         if (err)
 263                 goto out_free_priv_data;
 264 
 265         err = msi_domain_alloc_irqs(dev->msi_domain, dev, nvec);
 266         if (err)
 267                 goto out_free_desc;
 268 
 269         return 0;
 270 
 271 out_free_desc:
 272         platform_msi_free_descs(dev, 0, nvec);
 273 out_free_priv_data:
 274         platform_msi_free_priv_data(priv_data);
 275 
 276         return err;
 277 }
 278 EXPORT_SYMBOL_GPL(platform_msi_domain_alloc_irqs);
 279 
 280 /**
 281  * platform_msi_domain_free_irqs - Free MSI interrupts for @dev
 282  * @dev:        The device for which to free interrupts
 283  */
 284 void platform_msi_domain_free_irqs(struct device *dev)
 285 {
 286         if (!list_empty(dev_to_msi_list(dev))) {
 287                 struct msi_desc *desc;
 288 
 289                 desc = first_msi_entry(dev);
 290                 platform_msi_free_priv_data(desc->platform.msi_priv_data);
 291         }
 292 
 293         msi_domain_free_irqs(dev->msi_domain, dev);
 294         platform_msi_free_descs(dev, 0, MAX_DEV_MSIS);
 295 }
 296 EXPORT_SYMBOL_GPL(platform_msi_domain_free_irqs);
 297 
 298 /**
 299  * platform_msi_get_host_data - Query the private data associated with
 300  *                              a platform-msi domain
 301  * @domain:     The platform-msi domain
 302  *
 303  * Returns the private data provided when calling
 304  * platform_msi_create_device_domain.
 305  */
 306 void *platform_msi_get_host_data(struct irq_domain *domain)
 307 {
 308         struct platform_msi_priv_data *data = domain->host_data;
 309         return data->host_data;
 310 }
 311 
 312 /**
 313  * platform_msi_create_device_domain - Create a platform-msi domain
 314  *
 315  * @dev:                The device generating the MSIs
 316  * @nvec:               The number of MSIs that need to be allocated
 317  * @write_msi_msg:      Callback to write an interrupt message for @dev
 318  * @ops:                The hierarchy domain operations to use
 319  * @host_data:          Private data associated to this domain
 320  *
 321  * Returns an irqdomain for @nvec interrupts
 322  */
 323 struct irq_domain *
 324 __platform_msi_create_device_domain(struct device *dev,
 325                                     unsigned int nvec,
 326                                     bool is_tree,
 327                                     irq_write_msi_msg_t write_msi_msg,
 328                                     const struct irq_domain_ops *ops,
 329                                     void *host_data)
 330 {
 331         struct platform_msi_priv_data *data;
 332         struct irq_domain *domain;
 333         int err;
 334 
 335         data = platform_msi_alloc_priv_data(dev, nvec, write_msi_msg);
 336         if (IS_ERR(data))
 337                 return NULL;
 338 
 339         data->host_data = host_data;
 340         domain = irq_domain_create_hierarchy(dev->msi_domain, 0,
 341                                              is_tree ? 0 : nvec,
 342                                              dev->fwnode, ops, data);
 343         if (!domain)
 344                 goto free_priv;
 345 
 346         err = msi_domain_prepare_irqs(domain->parent, dev, nvec, &data->arg);
 347         if (err)
 348                 goto free_domain;
 349 
 350         return domain;
 351 
 352 free_domain:
 353         irq_domain_remove(domain);
 354 free_priv:
 355         platform_msi_free_priv_data(data);
 356         return NULL;
 357 }
 358 
 359 /**
 360  * platform_msi_domain_free - Free interrupts associated with a platform-msi
 361  *                            domain
 362  *
 363  * @domain:     The platform-msi domain
 364  * @virq:       The base irq from which to perform the free operation
 365  * @nvec:       How many interrupts to free from @virq
 366  */
 367 void platform_msi_domain_free(struct irq_domain *domain, unsigned int virq,
 368                               unsigned int nvec)
 369 {
 370         struct platform_msi_priv_data *data = domain->host_data;
 371         struct msi_desc *desc, *tmp;
 372         for_each_msi_entry_safe(desc, tmp, data->dev) {
 373                 if (WARN_ON(!desc->irq || desc->nvec_used != 1))
 374                         return;
 375                 if (!(desc->irq >= virq && desc->irq < (virq + nvec)))
 376                         continue;
 377 
 378                 irq_domain_free_irqs_common(domain, desc->irq, 1);
 379                 list_del(&desc->list);
 380                 free_msi_entry(desc);
 381         }
 382 }
 383 
 384 /**
 385  * platform_msi_domain_alloc - Allocate interrupts associated with
 386  *                             a platform-msi domain
 387  *
 388  * @domain:     The platform-msi domain
 389  * @virq:       The base irq from which to perform the allocate operation
 390  * @nvec:       How many interrupts to free from @virq
 391  *
 392  * Return 0 on success, or an error code on failure. Must be called
 393  * with irq_domain_mutex held (which can only be done as part of a
 394  * top-level interrupt allocation).
 395  */
 396 int platform_msi_domain_alloc(struct irq_domain *domain, unsigned int virq,
 397                               unsigned int nr_irqs)
 398 {
 399         struct platform_msi_priv_data *data = domain->host_data;
 400         int err;
 401 
 402         err = platform_msi_alloc_descs_with_irq(data->dev, virq, nr_irqs, data);
 403         if (err)
 404                 return err;
 405 
 406         err = msi_domain_populate_irqs(domain->parent, data->dev,
 407                                        virq, nr_irqs, &data->arg);
 408         if (err)
 409                 platform_msi_domain_free(domain, virq, nr_irqs);
 410 
 411         return err;
 412 }

/* [<][>][^][v][top][bottom][index][help] */