root/drivers/pci/controller/pcie-altera-msi.c

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

DEFINITIONS

This source file includes following definitions.
  1. msi_writel
  2. msi_readl
  3. altera_msi_isr
  4. altera_compose_msi_msg
  5. altera_msi_set_affinity
  6. altera_irq_domain_alloc
  7. altera_irq_domain_free
  8. altera_allocate_domains
  9. altera_free_domains
  10. altera_msi_remove
  11. altera_msi_probe
  12. altera_msi_init
  13. altera_msi_exit

   1 // SPDX-License-Identifier: GPL-2.0
   2 /*
   3  * Altera PCIe MSI support
   4  *
   5  * Author: Ley Foon Tan <lftan@altera.com>
   6  *
   7  * Copyright Altera Corporation (C) 2013-2015. All rights reserved
   8  */
   9 
  10 #include <linux/interrupt.h>
  11 #include <linux/irqchip/chained_irq.h>
  12 #include <linux/init.h>
  13 #include <linux/module.h>
  14 #include <linux/msi.h>
  15 #include <linux/of_address.h>
  16 #include <linux/of_irq.h>
  17 #include <linux/of_pci.h>
  18 #include <linux/pci.h>
  19 #include <linux/platform_device.h>
  20 #include <linux/slab.h>
  21 
  22 #define MSI_STATUS              0x0
  23 #define MSI_ERROR               0x4
  24 #define MSI_INTMASK             0x8
  25 
  26 #define MAX_MSI_VECTORS         32
  27 
  28 struct altera_msi {
  29         DECLARE_BITMAP(used, MAX_MSI_VECTORS);
  30         struct mutex            lock;   /* protect "used" bitmap */
  31         struct platform_device  *pdev;
  32         struct irq_domain       *msi_domain;
  33         struct irq_domain       *inner_domain;
  34         void __iomem            *csr_base;
  35         void __iomem            *vector_base;
  36         phys_addr_t             vector_phy;
  37         u32                     num_of_vectors;
  38         int                     irq;
  39 };
  40 
  41 static inline void msi_writel(struct altera_msi *msi, const u32 value,
  42                               const u32 reg)
  43 {
  44         writel_relaxed(value, msi->csr_base + reg);
  45 }
  46 
  47 static inline u32 msi_readl(struct altera_msi *msi, const u32 reg)
  48 {
  49         return readl_relaxed(msi->csr_base + reg);
  50 }
  51 
  52 static void altera_msi_isr(struct irq_desc *desc)
  53 {
  54         struct irq_chip *chip = irq_desc_get_chip(desc);
  55         struct altera_msi *msi;
  56         unsigned long status;
  57         u32 bit;
  58         u32 virq;
  59 
  60         chained_irq_enter(chip, desc);
  61         msi = irq_desc_get_handler_data(desc);
  62 
  63         while ((status = msi_readl(msi, MSI_STATUS)) != 0) {
  64                 for_each_set_bit(bit, &status, msi->num_of_vectors) {
  65                         /* Dummy read from vector to clear the interrupt */
  66                         readl_relaxed(msi->vector_base + (bit * sizeof(u32)));
  67 
  68                         virq = irq_find_mapping(msi->inner_domain, bit);
  69                         if (virq)
  70                                 generic_handle_irq(virq);
  71                         else
  72                                 dev_err(&msi->pdev->dev, "unexpected MSI\n");
  73                 }
  74         }
  75 
  76         chained_irq_exit(chip, desc);
  77 }
  78 
  79 static struct irq_chip altera_msi_irq_chip = {
  80         .name = "Altera PCIe MSI",
  81         .irq_mask = pci_msi_mask_irq,
  82         .irq_unmask = pci_msi_unmask_irq,
  83 };
  84 
  85 static struct msi_domain_info altera_msi_domain_info = {
  86         .flags  = (MSI_FLAG_USE_DEF_DOM_OPS | MSI_FLAG_USE_DEF_CHIP_OPS |
  87                      MSI_FLAG_PCI_MSIX),
  88         .chip   = &altera_msi_irq_chip,
  89 };
  90 
  91 static void altera_compose_msi_msg(struct irq_data *data, struct msi_msg *msg)
  92 {
  93         struct altera_msi *msi = irq_data_get_irq_chip_data(data);
  94         phys_addr_t addr = msi->vector_phy + (data->hwirq * sizeof(u32));
  95 
  96         msg->address_lo = lower_32_bits(addr);
  97         msg->address_hi = upper_32_bits(addr);
  98         msg->data = data->hwirq;
  99 
 100         dev_dbg(&msi->pdev->dev, "msi#%d address_hi %#x address_lo %#x\n",
 101                 (int)data->hwirq, msg->address_hi, msg->address_lo);
 102 }
 103 
 104 static int altera_msi_set_affinity(struct irq_data *irq_data,
 105                                    const struct cpumask *mask, bool force)
 106 {
 107          return -EINVAL;
 108 }
 109 
 110 static struct irq_chip altera_msi_bottom_irq_chip = {
 111         .name                   = "Altera MSI",
 112         .irq_compose_msi_msg    = altera_compose_msi_msg,
 113         .irq_set_affinity       = altera_msi_set_affinity,
 114 };
 115 
 116 static int altera_irq_domain_alloc(struct irq_domain *domain, unsigned int virq,
 117                                    unsigned int nr_irqs, void *args)
 118 {
 119         struct altera_msi *msi = domain->host_data;
 120         unsigned long bit;
 121         u32 mask;
 122 
 123         WARN_ON(nr_irqs != 1);
 124         mutex_lock(&msi->lock);
 125 
 126         bit = find_first_zero_bit(msi->used, msi->num_of_vectors);
 127         if (bit >= msi->num_of_vectors) {
 128                 mutex_unlock(&msi->lock);
 129                 return -ENOSPC;
 130         }
 131 
 132         set_bit(bit, msi->used);
 133 
 134         mutex_unlock(&msi->lock);
 135 
 136         irq_domain_set_info(domain, virq, bit, &altera_msi_bottom_irq_chip,
 137                             domain->host_data, handle_simple_irq,
 138                             NULL, NULL);
 139 
 140         mask = msi_readl(msi, MSI_INTMASK);
 141         mask |= 1 << bit;
 142         msi_writel(msi, mask, MSI_INTMASK);
 143 
 144         return 0;
 145 }
 146 
 147 static void altera_irq_domain_free(struct irq_domain *domain,
 148                                    unsigned int virq, unsigned int nr_irqs)
 149 {
 150         struct irq_data *d = irq_domain_get_irq_data(domain, virq);
 151         struct altera_msi *msi = irq_data_get_irq_chip_data(d);
 152         u32 mask;
 153 
 154         mutex_lock(&msi->lock);
 155 
 156         if (!test_bit(d->hwirq, msi->used)) {
 157                 dev_err(&msi->pdev->dev, "trying to free unused MSI#%lu\n",
 158                         d->hwirq);
 159         } else {
 160                 __clear_bit(d->hwirq, msi->used);
 161                 mask = msi_readl(msi, MSI_INTMASK);
 162                 mask &= ~(1 << d->hwirq);
 163                 msi_writel(msi, mask, MSI_INTMASK);
 164         }
 165 
 166         mutex_unlock(&msi->lock);
 167 }
 168 
 169 static const struct irq_domain_ops msi_domain_ops = {
 170         .alloc  = altera_irq_domain_alloc,
 171         .free   = altera_irq_domain_free,
 172 };
 173 
 174 static int altera_allocate_domains(struct altera_msi *msi)
 175 {
 176         struct fwnode_handle *fwnode = of_node_to_fwnode(msi->pdev->dev.of_node);
 177 
 178         msi->inner_domain = irq_domain_add_linear(NULL, msi->num_of_vectors,
 179                                              &msi_domain_ops, msi);
 180         if (!msi->inner_domain) {
 181                 dev_err(&msi->pdev->dev, "failed to create IRQ domain\n");
 182                 return -ENOMEM;
 183         }
 184 
 185         msi->msi_domain = pci_msi_create_irq_domain(fwnode,
 186                                 &altera_msi_domain_info, msi->inner_domain);
 187         if (!msi->msi_domain) {
 188                 dev_err(&msi->pdev->dev, "failed to create MSI domain\n");
 189                 irq_domain_remove(msi->inner_domain);
 190                 return -ENOMEM;
 191         }
 192 
 193         return 0;
 194 }
 195 
 196 static void altera_free_domains(struct altera_msi *msi)
 197 {
 198         irq_domain_remove(msi->msi_domain);
 199         irq_domain_remove(msi->inner_domain);
 200 }
 201 
 202 static int altera_msi_remove(struct platform_device *pdev)
 203 {
 204         struct altera_msi *msi = platform_get_drvdata(pdev);
 205 
 206         msi_writel(msi, 0, MSI_INTMASK);
 207         irq_set_chained_handler(msi->irq, NULL);
 208         irq_set_handler_data(msi->irq, NULL);
 209 
 210         altera_free_domains(msi);
 211 
 212         platform_set_drvdata(pdev, NULL);
 213         return 0;
 214 }
 215 
 216 static int altera_msi_probe(struct platform_device *pdev)
 217 {
 218         struct altera_msi *msi;
 219         struct device_node *np = pdev->dev.of_node;
 220         struct resource *res;
 221         int ret;
 222 
 223         msi = devm_kzalloc(&pdev->dev, sizeof(struct altera_msi),
 224                            GFP_KERNEL);
 225         if (!msi)
 226                 return -ENOMEM;
 227 
 228         mutex_init(&msi->lock);
 229         msi->pdev = pdev;
 230 
 231         res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "csr");
 232         msi->csr_base = devm_ioremap_resource(&pdev->dev, res);
 233         if (IS_ERR(msi->csr_base)) {
 234                 dev_err(&pdev->dev, "failed to map csr memory\n");
 235                 return PTR_ERR(msi->csr_base);
 236         }
 237 
 238         res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
 239                                            "vector_slave");
 240         msi->vector_base = devm_ioremap_resource(&pdev->dev, res);
 241         if (IS_ERR(msi->vector_base)) {
 242                 dev_err(&pdev->dev, "failed to map vector_slave memory\n");
 243                 return PTR_ERR(msi->vector_base);
 244         }
 245 
 246         msi->vector_phy = res->start;
 247 
 248         if (of_property_read_u32(np, "num-vectors", &msi->num_of_vectors)) {
 249                 dev_err(&pdev->dev, "failed to parse the number of vectors\n");
 250                 return -EINVAL;
 251         }
 252 
 253         ret = altera_allocate_domains(msi);
 254         if (ret)
 255                 return ret;
 256 
 257         msi->irq = platform_get_irq(pdev, 0);
 258         if (msi->irq < 0) {
 259                 dev_err(&pdev->dev, "failed to map IRQ: %d\n", msi->irq);
 260                 ret = msi->irq;
 261                 goto err;
 262         }
 263 
 264         irq_set_chained_handler_and_data(msi->irq, altera_msi_isr, msi);
 265         platform_set_drvdata(pdev, msi);
 266 
 267         return 0;
 268 
 269 err:
 270         altera_msi_remove(pdev);
 271         return ret;
 272 }
 273 
 274 static const struct of_device_id altera_msi_of_match[] = {
 275         { .compatible = "altr,msi-1.0", NULL },
 276         { },
 277 };
 278 
 279 static struct platform_driver altera_msi_driver = {
 280         .driver = {
 281                 .name = "altera-msi",
 282                 .of_match_table = altera_msi_of_match,
 283         },
 284         .probe = altera_msi_probe,
 285         .remove = altera_msi_remove,
 286 };
 287 
 288 static int __init altera_msi_init(void)
 289 {
 290         return platform_driver_register(&altera_msi_driver);
 291 }
 292 
 293 static void __exit altera_msi_exit(void)
 294 {
 295         platform_driver_unregister(&altera_msi_driver);
 296 }
 297 
 298 subsys_initcall(altera_msi_init);
 299 MODULE_DEVICE_TABLE(of, altera_msi_of_match);
 300 module_exit(altera_msi_exit);
 301 MODULE_LICENSE("GPL v2");

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