linux/arch/powerpc/platforms/4xx/msi.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0-or-later
   2/*
   3 * Adding PCI-E MSI support for PPC4XX SoCs.
   4 *
   5 * Copyright (c) 2010, Applied Micro Circuits Corporation
   6 * Authors:     Tirumala R Marri <tmarri@apm.com>
   7 *              Feng Kan <fkan@apm.com>
   8 */
   9
  10#include <linux/irq.h>
  11#include <linux/pci.h>
  12#include <linux/msi.h>
  13#include <linux/of_platform.h>
  14#include <linux/interrupt.h>
  15#include <linux/export.h>
  16#include <linux/kernel.h>
  17#include <asm/prom.h>
  18#include <asm/hw_irq.h>
  19#include <asm/ppc-pci.h>
  20#include <asm/dcr.h>
  21#include <asm/dcr-regs.h>
  22#include <asm/msi_bitmap.h>
  23
  24#define PEIH_TERMADH    0x00
  25#define PEIH_TERMADL    0x08
  26#define PEIH_MSIED      0x10
  27#define PEIH_MSIMK      0x18
  28#define PEIH_MSIASS     0x20
  29#define PEIH_FLUSH0     0x30
  30#define PEIH_FLUSH1     0x38
  31#define PEIH_CNTRST     0x48
  32
  33static int msi_irqs;
  34
  35struct ppc4xx_msi {
  36        u32 msi_addr_lo;
  37        u32 msi_addr_hi;
  38        void __iomem *msi_regs;
  39        int *msi_virqs;
  40        struct msi_bitmap bitmap;
  41        struct device_node *msi_dev;
  42};
  43
  44static struct ppc4xx_msi ppc4xx_msi;
  45
  46static int ppc4xx_msi_init_allocator(struct platform_device *dev,
  47                struct ppc4xx_msi *msi_data)
  48{
  49        int err;
  50
  51        err = msi_bitmap_alloc(&msi_data->bitmap, msi_irqs,
  52                              dev->dev.of_node);
  53        if (err)
  54                return err;
  55
  56        err = msi_bitmap_reserve_dt_hwirqs(&msi_data->bitmap);
  57        if (err < 0) {
  58                msi_bitmap_free(&msi_data->bitmap);
  59                return err;
  60        }
  61
  62        return 0;
  63}
  64
  65static int ppc4xx_setup_msi_irqs(struct pci_dev *dev, int nvec, int type)
  66{
  67        int int_no = -ENOMEM;
  68        unsigned int virq;
  69        struct msi_msg msg;
  70        struct msi_desc *entry;
  71        struct ppc4xx_msi *msi_data = &ppc4xx_msi;
  72
  73        dev_dbg(&dev->dev, "PCIE-MSI:%s called. vec %x type %d\n",
  74                __func__, nvec, type);
  75        if (type == PCI_CAP_ID_MSIX)
  76                pr_debug("ppc4xx msi: MSI-X untested, trying anyway.\n");
  77
  78        msi_data->msi_virqs = kmalloc_array(msi_irqs, sizeof(int), GFP_KERNEL);
  79        if (!msi_data->msi_virqs)
  80                return -ENOMEM;
  81
  82        for_each_pci_msi_entry(entry, dev) {
  83                int_no = msi_bitmap_alloc_hwirqs(&msi_data->bitmap, 1);
  84                if (int_no >= 0)
  85                        break;
  86                if (int_no < 0) {
  87                        pr_debug("%s: fail allocating msi interrupt\n",
  88                                        __func__);
  89                }
  90                virq = irq_of_parse_and_map(msi_data->msi_dev, int_no);
  91                if (!virq) {
  92                        dev_err(&dev->dev, "%s: fail mapping irq\n", __func__);
  93                        msi_bitmap_free_hwirqs(&msi_data->bitmap, int_no, 1);
  94                        return -ENOSPC;
  95                }
  96                dev_dbg(&dev->dev, "%s: virq = %d\n", __func__, virq);
  97
  98                /* Setup msi address space */
  99                msg.address_hi = msi_data->msi_addr_hi;
 100                msg.address_lo = msi_data->msi_addr_lo;
 101
 102                irq_set_msi_desc(virq, entry);
 103                msg.data = int_no;
 104                pci_write_msi_msg(virq, &msg);
 105        }
 106        return 0;
 107}
 108
 109void ppc4xx_teardown_msi_irqs(struct pci_dev *dev)
 110{
 111        struct msi_desc *entry;
 112        struct ppc4xx_msi *msi_data = &ppc4xx_msi;
 113        irq_hw_number_t hwirq;
 114
 115        dev_dbg(&dev->dev, "PCIE-MSI: tearing down msi irqs\n");
 116
 117        for_each_pci_msi_entry(entry, dev) {
 118                if (!entry->irq)
 119                        continue;
 120                hwirq = virq_to_hw(entry->irq);
 121                irq_set_msi_desc(entry->irq, NULL);
 122                irq_dispose_mapping(entry->irq);
 123                msi_bitmap_free_hwirqs(&msi_data->bitmap, hwirq, 1);
 124        }
 125}
 126
 127static int ppc4xx_setup_pcieh_hw(struct platform_device *dev,
 128                                 struct resource res, struct ppc4xx_msi *msi)
 129{
 130        const u32 *msi_data;
 131        const u32 *msi_mask;
 132        const u32 *sdr_addr;
 133        dma_addr_t msi_phys;
 134        void *msi_virt;
 135        int err;
 136
 137        sdr_addr = of_get_property(dev->dev.of_node, "sdr-base", NULL);
 138        if (!sdr_addr)
 139                return -EINVAL;
 140
 141        msi_data = of_get_property(dev->dev.of_node, "msi-data", NULL);
 142        if (!msi_data)
 143                return -EINVAL;
 144
 145        msi_mask = of_get_property(dev->dev.of_node, "msi-mask", NULL);
 146        if (!msi_mask)
 147                return -EINVAL;
 148
 149        msi->msi_dev = of_find_node_by_name(NULL, "ppc4xx-msi");
 150        if (!msi->msi_dev)
 151                return -ENODEV;
 152
 153        msi->msi_regs = of_iomap(msi->msi_dev, 0);
 154        if (!msi->msi_regs) {
 155                dev_err(&dev->dev, "of_iomap failed\n");
 156                err = -ENOMEM;
 157                goto node_put;
 158        }
 159        dev_dbg(&dev->dev, "PCIE-MSI: msi register mapped 0x%x 0x%x\n",
 160                (u32) (msi->msi_regs + PEIH_TERMADH), (u32) (msi->msi_regs));
 161
 162        msi_virt = dma_alloc_coherent(&dev->dev, 64, &msi_phys, GFP_KERNEL);
 163        if (!msi_virt) {
 164                err = -ENOMEM;
 165                goto iounmap;
 166        }
 167        msi->msi_addr_hi = upper_32_bits(msi_phys);
 168        msi->msi_addr_lo = lower_32_bits(msi_phys & 0xffffffff);
 169        dev_dbg(&dev->dev, "PCIE-MSI: msi address high 0x%x, low 0x%x\n",
 170                msi->msi_addr_hi, msi->msi_addr_lo);
 171
 172        mtdcri(SDR0, *sdr_addr, upper_32_bits(res.start));      /*HIGH addr */
 173        mtdcri(SDR0, *sdr_addr + 1, lower_32_bits(res.start));  /* Low addr */
 174
 175        /* Progam the Interrupt handler Termination addr registers */
 176        out_be32(msi->msi_regs + PEIH_TERMADH, msi->msi_addr_hi);
 177        out_be32(msi->msi_regs + PEIH_TERMADL, msi->msi_addr_lo);
 178
 179        /* Program MSI Expected data and Mask bits */
 180        out_be32(msi->msi_regs + PEIH_MSIED, *msi_data);
 181        out_be32(msi->msi_regs + PEIH_MSIMK, *msi_mask);
 182
 183        dma_free_coherent(&dev->dev, 64, msi_virt, msi_phys);
 184
 185        return 0;
 186
 187iounmap:
 188        iounmap(msi->msi_regs);
 189node_put:
 190        of_node_put(msi->msi_dev);
 191        return err;
 192}
 193
 194static int ppc4xx_of_msi_remove(struct platform_device *dev)
 195{
 196        struct ppc4xx_msi *msi = dev->dev.platform_data;
 197        int i;
 198        int virq;
 199
 200        for (i = 0; i < msi_irqs; i++) {
 201                virq = msi->msi_virqs[i];
 202                if (virq)
 203                        irq_dispose_mapping(virq);
 204        }
 205
 206        if (msi->bitmap.bitmap)
 207                msi_bitmap_free(&msi->bitmap);
 208        iounmap(msi->msi_regs);
 209        of_node_put(msi->msi_dev);
 210
 211        return 0;
 212}
 213
 214static int ppc4xx_msi_probe(struct platform_device *dev)
 215{
 216        struct ppc4xx_msi *msi;
 217        struct resource res;
 218        int err = 0;
 219        struct pci_controller *phb;
 220
 221        dev_dbg(&dev->dev, "PCIE-MSI: Setting up MSI support...\n");
 222
 223        msi = devm_kzalloc(&dev->dev, sizeof(*msi), GFP_KERNEL);
 224        if (!msi)
 225                return -ENOMEM;
 226        dev->dev.platform_data = msi;
 227
 228        /* Get MSI ranges */
 229        err = of_address_to_resource(dev->dev.of_node, 0, &res);
 230        if (err) {
 231                dev_err(&dev->dev, "%pOF resource error!\n", dev->dev.of_node);
 232                return err;
 233        }
 234
 235        msi_irqs = of_irq_count(dev->dev.of_node);
 236        if (!msi_irqs)
 237                return -ENODEV;
 238
 239        err = ppc4xx_setup_pcieh_hw(dev, res, msi);
 240        if (err)
 241                return err;
 242
 243        err = ppc4xx_msi_init_allocator(dev, msi);
 244        if (err) {
 245                dev_err(&dev->dev, "Error allocating MSI bitmap\n");
 246                goto error_out;
 247        }
 248        ppc4xx_msi = *msi;
 249
 250        list_for_each_entry(phb, &hose_list, list_node) {
 251                phb->controller_ops.setup_msi_irqs = ppc4xx_setup_msi_irqs;
 252                phb->controller_ops.teardown_msi_irqs = ppc4xx_teardown_msi_irqs;
 253        }
 254        return 0;
 255
 256error_out:
 257        ppc4xx_of_msi_remove(dev);
 258        return err;
 259}
 260static const struct of_device_id ppc4xx_msi_ids[] = {
 261        {
 262                .compatible = "amcc,ppc4xx-msi",
 263        },
 264        {}
 265};
 266static struct platform_driver ppc4xx_msi_driver = {
 267        .probe = ppc4xx_msi_probe,
 268        .remove = ppc4xx_of_msi_remove,
 269        .driver = {
 270                   .name = "ppc4xx-msi",
 271                   .of_match_table = ppc4xx_msi_ids,
 272                   },
 273
 274};
 275
 276static __init int ppc4xx_msi_init(void)
 277{
 278        return platform_driver_register(&ppc4xx_msi_driver);
 279}
 280
 281subsys_initcall(ppc4xx_msi_init);
 282