root/drivers/irqchip/irq-alpine-msi.c

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

DEFINITIONS

This source file includes following definitions.
  1. alpine_msix_mask_msi_irq
  2. alpine_msix_unmask_msi_irq
  3. alpine_msix_allocate_sgi
  4. alpine_msix_free_sgi
  5. alpine_msix_compose_msi_msg
  6. alpine_msix_gic_domain_alloc
  7. alpine_msix_middle_domain_alloc
  8. alpine_msix_middle_domain_free
  9. alpine_msix_init_domains
  10. alpine_msix_init

   1 /*
   2  * Annapurna Labs MSIX support services
   3  *
   4  * Copyright (C) 2016, Amazon.com, Inc. or its affiliates. All Rights Reserved.
   5  *
   6  * Antoine Tenart <antoine.tenart@free-electrons.com>
   7  *
   8  * This file is licensed under the terms of the GNU General Public
   9  * License version 2. This program is licensed "as is" without any
  10  * warranty of any kind, whether express or implied.
  11  */
  12 
  13 #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
  14 
  15 #include <linux/irqchip.h>
  16 #include <linux/irqchip/arm-gic.h>
  17 #include <linux/msi.h>
  18 #include <linux/of.h>
  19 #include <linux/of_address.h>
  20 #include <linux/of_irq.h>
  21 #include <linux/of_pci.h>
  22 #include <linux/pci.h>
  23 #include <linux/slab.h>
  24 
  25 #include <asm/irq.h>
  26 #include <asm/msi.h>
  27 
  28 /* MSIX message address format: local GIC target */
  29 #define ALPINE_MSIX_SPI_TARGET_CLUSTER0         BIT(16)
  30 
  31 struct alpine_msix_data {
  32         spinlock_t msi_map_lock;
  33         phys_addr_t addr;
  34         u32 spi_first;          /* The SGI number that MSIs start */
  35         u32 num_spis;           /* The number of SGIs for MSIs */
  36         unsigned long *msi_map;
  37 };
  38 
  39 static void alpine_msix_mask_msi_irq(struct irq_data *d)
  40 {
  41         pci_msi_mask_irq(d);
  42         irq_chip_mask_parent(d);
  43 }
  44 
  45 static void alpine_msix_unmask_msi_irq(struct irq_data *d)
  46 {
  47         pci_msi_unmask_irq(d);
  48         irq_chip_unmask_parent(d);
  49 }
  50 
  51 static struct irq_chip alpine_msix_irq_chip = {
  52         .name                   = "MSIx",
  53         .irq_mask               = alpine_msix_mask_msi_irq,
  54         .irq_unmask             = alpine_msix_unmask_msi_irq,
  55         .irq_eoi                = irq_chip_eoi_parent,
  56         .irq_set_affinity       = irq_chip_set_affinity_parent,
  57 };
  58 
  59 static int alpine_msix_allocate_sgi(struct alpine_msix_data *priv, int num_req)
  60 {
  61         int first;
  62 
  63         spin_lock(&priv->msi_map_lock);
  64 
  65         first = bitmap_find_next_zero_area(priv->msi_map, priv->num_spis, 0,
  66                                            num_req, 0);
  67         if (first >= priv->num_spis) {
  68                 spin_unlock(&priv->msi_map_lock);
  69                 return -ENOSPC;
  70         }
  71 
  72         bitmap_set(priv->msi_map, first, num_req);
  73 
  74         spin_unlock(&priv->msi_map_lock);
  75 
  76         return priv->spi_first + first;
  77 }
  78 
  79 static void alpine_msix_free_sgi(struct alpine_msix_data *priv, unsigned sgi,
  80                                  int num_req)
  81 {
  82         int first = sgi - priv->spi_first;
  83 
  84         spin_lock(&priv->msi_map_lock);
  85 
  86         bitmap_clear(priv->msi_map, first, num_req);
  87 
  88         spin_unlock(&priv->msi_map_lock);
  89 }
  90 
  91 static void alpine_msix_compose_msi_msg(struct irq_data *data,
  92                                         struct msi_msg *msg)
  93 {
  94         struct alpine_msix_data *priv = irq_data_get_irq_chip_data(data);
  95         phys_addr_t msg_addr = priv->addr;
  96 
  97         msg_addr |= (data->hwirq << 3);
  98 
  99         msg->address_hi = upper_32_bits(msg_addr);
 100         msg->address_lo = lower_32_bits(msg_addr);
 101         msg->data = 0;
 102 }
 103 
 104 static struct msi_domain_info alpine_msix_domain_info = {
 105         .flags  = MSI_FLAG_USE_DEF_DOM_OPS | MSI_FLAG_USE_DEF_CHIP_OPS |
 106                   MSI_FLAG_PCI_MSIX,
 107         .chip   = &alpine_msix_irq_chip,
 108 };
 109 
 110 static struct irq_chip middle_irq_chip = {
 111         .name                   = "alpine_msix_middle",
 112         .irq_mask               = irq_chip_mask_parent,
 113         .irq_unmask             = irq_chip_unmask_parent,
 114         .irq_eoi                = irq_chip_eoi_parent,
 115         .irq_set_affinity       = irq_chip_set_affinity_parent,
 116         .irq_compose_msi_msg    = alpine_msix_compose_msi_msg,
 117 };
 118 
 119 static int alpine_msix_gic_domain_alloc(struct irq_domain *domain,
 120                                         unsigned int virq, int sgi)
 121 {
 122         struct irq_fwspec fwspec;
 123         struct irq_data *d;
 124         int ret;
 125 
 126         if (!is_of_node(domain->parent->fwnode))
 127                 return -EINVAL;
 128 
 129         fwspec.fwnode = domain->parent->fwnode;
 130         fwspec.param_count = 3;
 131         fwspec.param[0] = 0;
 132         fwspec.param[1] = sgi;
 133         fwspec.param[2] = IRQ_TYPE_EDGE_RISING;
 134 
 135         ret = irq_domain_alloc_irqs_parent(domain, virq, 1, &fwspec);
 136         if (ret)
 137                 return ret;
 138 
 139         d = irq_domain_get_irq_data(domain->parent, virq);
 140         d->chip->irq_set_type(d, IRQ_TYPE_EDGE_RISING);
 141 
 142         return 0;
 143 }
 144 
 145 static int alpine_msix_middle_domain_alloc(struct irq_domain *domain,
 146                                            unsigned int virq,
 147                                            unsigned int nr_irqs, void *args)
 148 {
 149         struct alpine_msix_data *priv = domain->host_data;
 150         int sgi, err, i;
 151 
 152         sgi = alpine_msix_allocate_sgi(priv, nr_irqs);
 153         if (sgi < 0)
 154                 return sgi;
 155 
 156         for (i = 0; i < nr_irqs; i++) {
 157                 err = alpine_msix_gic_domain_alloc(domain, virq + i, sgi + i);
 158                 if (err)
 159                         goto err_sgi;
 160 
 161                 irq_domain_set_hwirq_and_chip(domain, virq + i, sgi + i,
 162                                               &middle_irq_chip, priv);
 163         }
 164 
 165         return 0;
 166 
 167 err_sgi:
 168         while (--i >= 0)
 169                 irq_domain_free_irqs_parent(domain, virq, i);
 170         alpine_msix_free_sgi(priv, sgi, nr_irqs);
 171         return err;
 172 }
 173 
 174 static void alpine_msix_middle_domain_free(struct irq_domain *domain,
 175                                            unsigned int virq,
 176                                            unsigned int nr_irqs)
 177 {
 178         struct irq_data *d = irq_domain_get_irq_data(domain, virq);
 179         struct alpine_msix_data *priv = irq_data_get_irq_chip_data(d);
 180 
 181         irq_domain_free_irqs_parent(domain, virq, nr_irqs);
 182         alpine_msix_free_sgi(priv, d->hwirq, nr_irqs);
 183 }
 184 
 185 static const struct irq_domain_ops alpine_msix_middle_domain_ops = {
 186         .alloc  = alpine_msix_middle_domain_alloc,
 187         .free   = alpine_msix_middle_domain_free,
 188 };
 189 
 190 static int alpine_msix_init_domains(struct alpine_msix_data *priv,
 191                                     struct device_node *node)
 192 {
 193         struct irq_domain *middle_domain, *msi_domain, *gic_domain;
 194         struct device_node *gic_node;
 195 
 196         gic_node = of_irq_find_parent(node);
 197         if (!gic_node) {
 198                 pr_err("Failed to find the GIC node\n");
 199                 return -ENODEV;
 200         }
 201 
 202         gic_domain = irq_find_host(gic_node);
 203         if (!gic_domain) {
 204                 pr_err("Failed to find the GIC domain\n");
 205                 return -ENXIO;
 206         }
 207 
 208         middle_domain = irq_domain_add_tree(NULL,
 209                                             &alpine_msix_middle_domain_ops,
 210                                             priv);
 211         if (!middle_domain) {
 212                 pr_err("Failed to create the MSIX middle domain\n");
 213                 return -ENOMEM;
 214         }
 215 
 216         middle_domain->parent = gic_domain;
 217 
 218         msi_domain = pci_msi_create_irq_domain(of_node_to_fwnode(node),
 219                                                &alpine_msix_domain_info,
 220                                                middle_domain);
 221         if (!msi_domain) {
 222                 pr_err("Failed to create MSI domain\n");
 223                 irq_domain_remove(middle_domain);
 224                 return -ENOMEM;
 225         }
 226 
 227         return 0;
 228 }
 229 
 230 static int alpine_msix_init(struct device_node *node,
 231                             struct device_node *parent)
 232 {
 233         struct alpine_msix_data *priv;
 234         struct resource res;
 235         int ret;
 236 
 237         priv = kzalloc(sizeof(*priv), GFP_KERNEL);
 238         if (!priv)
 239                 return -ENOMEM;
 240 
 241         spin_lock_init(&priv->msi_map_lock);
 242 
 243         ret = of_address_to_resource(node, 0, &res);
 244         if (ret) {
 245                 pr_err("Failed to allocate resource\n");
 246                 goto err_priv;
 247         }
 248 
 249         /*
 250          * The 20 least significant bits of addr provide direct information
 251          * regarding the interrupt destination.
 252          *
 253          * To select the primary GIC as the target GIC, bits [18:17] must be set
 254          * to 0x0. In this case, bit 16 (SPI_TARGET_CLUSTER0) must be set.
 255          */
 256         priv->addr = res.start & GENMASK_ULL(63,20);
 257         priv->addr |= ALPINE_MSIX_SPI_TARGET_CLUSTER0;
 258 
 259         if (of_property_read_u32(node, "al,msi-base-spi", &priv->spi_first)) {
 260                 pr_err("Unable to parse MSI base\n");
 261                 ret = -EINVAL;
 262                 goto err_priv;
 263         }
 264 
 265         if (of_property_read_u32(node, "al,msi-num-spis", &priv->num_spis)) {
 266                 pr_err("Unable to parse MSI numbers\n");
 267                 ret = -EINVAL;
 268                 goto err_priv;
 269         }
 270 
 271         priv->msi_map = kcalloc(BITS_TO_LONGS(priv->num_spis),
 272                                 sizeof(*priv->msi_map),
 273                                 GFP_KERNEL);
 274         if (!priv->msi_map) {
 275                 ret = -ENOMEM;
 276                 goto err_priv;
 277         }
 278 
 279         pr_debug("Registering %d msixs, starting at %d\n",
 280                  priv->num_spis, priv->spi_first);
 281 
 282         ret = alpine_msix_init_domains(priv, node);
 283         if (ret)
 284                 goto err_map;
 285 
 286         return 0;
 287 
 288 err_map:
 289         kfree(priv->msi_map);
 290 err_priv:
 291         kfree(priv);
 292         return ret;
 293 }
 294 IRQCHIP_DECLARE(alpine_msix, "al,alpine-msix", alpine_msix_init);

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