linux/drivers/soc/ti/ti_sci_inta_msi.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0
   2/*
   3 * Texas Instruments' K3 Interrupt Aggregator MSI bus
   4 *
   5 * Copyright (C) 2018-2019 Texas Instruments Incorporated - http://www.ti.com/
   6 *      Lokesh Vutla <lokeshvutla@ti.com>
   7 */
   8
   9#include <linux/irq.h>
  10#include <linux/irqdomain.h>
  11#include <linux/msi.h>
  12#include <linux/of_address.h>
  13#include <linux/of_device.h>
  14#include <linux/of_irq.h>
  15#include <linux/soc/ti/ti_sci_inta_msi.h>
  16#include <linux/soc/ti/ti_sci_protocol.h>
  17
  18static void ti_sci_inta_msi_write_msg(struct irq_data *data,
  19                                      struct msi_msg *msg)
  20{
  21        /* Nothing to do */
  22}
  23
  24static void ti_sci_inta_msi_compose_msi_msg(struct irq_data *data,
  25                                            struct msi_msg *msg)
  26{
  27        /* Nothing to do */
  28}
  29
  30static void ti_sci_inta_msi_update_chip_ops(struct msi_domain_info *info)
  31{
  32        struct irq_chip *chip = info->chip;
  33
  34        if (WARN_ON(!chip))
  35                return;
  36
  37        chip->irq_request_resources = irq_chip_request_resources_parent;
  38        chip->irq_release_resources = irq_chip_release_resources_parent;
  39        chip->irq_compose_msi_msg = ti_sci_inta_msi_compose_msi_msg;
  40        chip->irq_write_msi_msg = ti_sci_inta_msi_write_msg;
  41        chip->irq_set_type = irq_chip_set_type_parent;
  42        chip->irq_unmask = irq_chip_unmask_parent;
  43        chip->irq_mask = irq_chip_mask_parent;
  44        chip->irq_ack = irq_chip_ack_parent;
  45}
  46
  47struct irq_domain *ti_sci_inta_msi_create_irq_domain(struct fwnode_handle *fwnode,
  48                                                     struct msi_domain_info *info,
  49                                                     struct irq_domain *parent)
  50{
  51        struct irq_domain *domain;
  52
  53        ti_sci_inta_msi_update_chip_ops(info);
  54
  55        domain = msi_create_irq_domain(fwnode, info, parent);
  56        if (domain)
  57                irq_domain_update_bus_token(domain, DOMAIN_BUS_TI_SCI_INTA_MSI);
  58
  59        return domain;
  60}
  61EXPORT_SYMBOL_GPL(ti_sci_inta_msi_create_irq_domain);
  62
  63static void ti_sci_inta_msi_free_descs(struct device *dev)
  64{
  65        struct msi_desc *desc, *tmp;
  66
  67        list_for_each_entry_safe(desc, tmp, dev_to_msi_list(dev), list) {
  68                list_del(&desc->list);
  69                free_msi_entry(desc);
  70        }
  71}
  72
  73static int ti_sci_inta_msi_alloc_descs(struct device *dev,
  74                                       struct ti_sci_resource *res)
  75{
  76        struct msi_desc *msi_desc;
  77        int set, i, count = 0;
  78
  79        for (set = 0; set < res->sets; set++) {
  80                for (i = 0; i < res->desc[set].num; i++) {
  81                        msi_desc = alloc_msi_entry(dev, 1, NULL);
  82                        if (!msi_desc) {
  83                                ti_sci_inta_msi_free_descs(dev);
  84                                return -ENOMEM;
  85                        }
  86
  87                        msi_desc->inta.dev_index = res->desc[set].start + i;
  88                        INIT_LIST_HEAD(&msi_desc->list);
  89                        list_add_tail(&msi_desc->list, dev_to_msi_list(dev));
  90                        count++;
  91                }
  92                for (i = 0; i < res->desc[set].num_sec; i++) {
  93                        msi_desc = alloc_msi_entry(dev, 1, NULL);
  94                        if (!msi_desc) {
  95                                ti_sci_inta_msi_free_descs(dev);
  96                                return -ENOMEM;
  97                        }
  98
  99                        msi_desc->inta.dev_index = res->desc[set].start_sec + i;
 100                        INIT_LIST_HEAD(&msi_desc->list);
 101                        list_add_tail(&msi_desc->list, dev_to_msi_list(dev));
 102                        count++;
 103                }
 104        }
 105
 106        return count;
 107}
 108
 109int ti_sci_inta_msi_domain_alloc_irqs(struct device *dev,
 110                                      struct ti_sci_resource *res)
 111{
 112        struct platform_device *pdev = to_platform_device(dev);
 113        struct irq_domain *msi_domain;
 114        int ret, nvec;
 115
 116        msi_domain = dev_get_msi_domain(dev);
 117        if (!msi_domain)
 118                return -EINVAL;
 119
 120        if (pdev->id < 0)
 121                return -ENODEV;
 122
 123        nvec = ti_sci_inta_msi_alloc_descs(dev, res);
 124        if (nvec <= 0)
 125                return nvec;
 126
 127        ret = msi_domain_alloc_irqs(msi_domain, dev, nvec);
 128        if (ret) {
 129                dev_err(dev, "Failed to allocate IRQs %d\n", ret);
 130                goto cleanup;
 131        }
 132
 133        return 0;
 134
 135cleanup:
 136        ti_sci_inta_msi_free_descs(&pdev->dev);
 137        return ret;
 138}
 139EXPORT_SYMBOL_GPL(ti_sci_inta_msi_domain_alloc_irqs);
 140
 141void ti_sci_inta_msi_domain_free_irqs(struct device *dev)
 142{
 143        msi_domain_free_irqs(dev->msi_domain, dev);
 144        ti_sci_inta_msi_free_descs(dev);
 145}
 146EXPORT_SYMBOL_GPL(ti_sci_inta_msi_domain_free_irqs);
 147
 148unsigned int ti_sci_inta_msi_get_virq(struct device *dev, u32 dev_index)
 149{
 150        struct msi_desc *desc;
 151
 152        for_each_msi_entry(desc, dev)
 153                if (desc->inta.dev_index == dev_index)
 154                        return desc->irq;
 155
 156        return -ENODEV;
 157}
 158EXPORT_SYMBOL_GPL(ti_sci_inta_msi_get_virq);
 159