Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0-or-later
0002 /*
0003  * MSI support for PPC4xx SoCs using High Speed Transfer Assist (HSTA) for
0004  * generation of the interrupt.
0005  *
0006  * Copyright © 2013 Alistair Popple <alistair@popple.id.au> IBM Corporation
0007  */
0008 
0009 #include <linux/kernel.h>
0010 #include <linux/interrupt.h>
0011 #include <linux/msi.h>
0012 #include <linux/of.h>
0013 #include <linux/of_irq.h>
0014 #include <linux/of_platform.h>
0015 #include <linux/pci.h>
0016 #include <linux/semaphore.h>
0017 #include <asm/msi_bitmap.h>
0018 #include <asm/ppc-pci.h>
0019 
0020 struct ppc4xx_hsta_msi {
0021     struct device *dev;
0022 
0023     /* The ioremapped HSTA MSI IO space */
0024     u32 __iomem *data;
0025 
0026     /* Physical address of HSTA MSI IO space */
0027     u64 address;
0028     struct msi_bitmap bmp;
0029 
0030     /* An array mapping offsets to hardware IRQs */
0031     int *irq_map;
0032 
0033     /* Number of hwirqs supported */
0034     int irq_count;
0035 };
0036 static struct ppc4xx_hsta_msi ppc4xx_hsta_msi;
0037 
0038 static int hsta_setup_msi_irqs(struct pci_dev *dev, int nvec, int type)
0039 {
0040     struct msi_msg msg;
0041     struct msi_desc *entry;
0042     int irq, hwirq;
0043     u64 addr;
0044 
0045     /* We don't support MSI-X */
0046     if (type == PCI_CAP_ID_MSIX) {
0047         pr_debug("%s: MSI-X not supported.\n", __func__);
0048         return -EINVAL;
0049     }
0050 
0051     msi_for_each_desc(entry, &dev->dev, MSI_DESC_NOTASSOCIATED) {
0052         irq = msi_bitmap_alloc_hwirqs(&ppc4xx_hsta_msi.bmp, 1);
0053         if (irq < 0) {
0054             pr_debug("%s: Failed to allocate msi interrupt\n",
0055                  __func__);
0056             return irq;
0057         }
0058 
0059         hwirq = ppc4xx_hsta_msi.irq_map[irq];
0060         if (!hwirq) {
0061             pr_err("%s: Failed mapping irq %d\n", __func__, irq);
0062             return -EINVAL;
0063         }
0064 
0065         /*
0066          * HSTA generates interrupts on writes to 128-bit aligned
0067          * addresses.
0068          */
0069         addr = ppc4xx_hsta_msi.address + irq*0x10;
0070         msg.address_hi = upper_32_bits(addr);
0071         msg.address_lo = lower_32_bits(addr);
0072 
0073         /* Data is not used by the HSTA. */
0074         msg.data = 0;
0075 
0076         pr_debug("%s: Setup irq %d (0x%0llx)\n", __func__, hwirq,
0077              (((u64) msg.address_hi) << 32) | msg.address_lo);
0078 
0079         if (irq_set_msi_desc(hwirq, entry)) {
0080             pr_err(
0081             "%s: Invalid hwirq %d specified in device tree\n",
0082             __func__, hwirq);
0083             msi_bitmap_free_hwirqs(&ppc4xx_hsta_msi.bmp, irq, 1);
0084             return -EINVAL;
0085         }
0086         pci_write_msi_msg(hwirq, &msg);
0087     }
0088 
0089     return 0;
0090 }
0091 
0092 static int hsta_find_hwirq_offset(int hwirq)
0093 {
0094     int irq;
0095 
0096     /* Find the offset given the hwirq */
0097     for (irq = 0; irq < ppc4xx_hsta_msi.irq_count; irq++)
0098         if (ppc4xx_hsta_msi.irq_map[irq] == hwirq)
0099             return irq;
0100 
0101     return -EINVAL;
0102 }
0103 
0104 static void hsta_teardown_msi_irqs(struct pci_dev *dev)
0105 {
0106     struct msi_desc *entry;
0107     int irq;
0108 
0109     msi_for_each_desc(entry, &dev->dev, MSI_DESC_ASSOCIATED) {
0110         irq = hsta_find_hwirq_offset(entry->irq);
0111 
0112         /* entry->irq should always be in irq_map */
0113         BUG_ON(irq < 0);
0114         irq_set_msi_desc(entry->irq, NULL);
0115         msi_bitmap_free_hwirqs(&ppc4xx_hsta_msi.bmp, irq, 1);
0116         pr_debug("%s: Teardown IRQ %u (index %u)\n", __func__,
0117              entry->irq, irq);
0118     }
0119 }
0120 
0121 static int hsta_msi_probe(struct platform_device *pdev)
0122 {
0123     struct device *dev = &pdev->dev;
0124     struct resource *mem;
0125     int irq, ret, irq_count;
0126     struct pci_controller *phb;
0127 
0128     mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
0129     if (!mem) {
0130         dev_err(dev, "Unable to get mmio space\n");
0131         return -EINVAL;
0132     }
0133 
0134     irq_count = of_irq_count(dev->of_node);
0135     if (!irq_count) {
0136         dev_err(dev, "Unable to find IRQ range\n");
0137         return -EINVAL;
0138     }
0139 
0140     ppc4xx_hsta_msi.dev = dev;
0141     ppc4xx_hsta_msi.address = mem->start;
0142     ppc4xx_hsta_msi.data = ioremap(mem->start, resource_size(mem));
0143     ppc4xx_hsta_msi.irq_count = irq_count;
0144     if (!ppc4xx_hsta_msi.data) {
0145         dev_err(dev, "Unable to map memory\n");
0146         return -ENOMEM;
0147     }
0148 
0149     ret = msi_bitmap_alloc(&ppc4xx_hsta_msi.bmp, irq_count, dev->of_node);
0150     if (ret)
0151         goto out;
0152 
0153     ppc4xx_hsta_msi.irq_map = kmalloc_array(irq_count, sizeof(int),
0154                         GFP_KERNEL);
0155     if (!ppc4xx_hsta_msi.irq_map) {
0156         ret = -ENOMEM;
0157         goto out1;
0158     }
0159 
0160     /* Setup a mapping from irq offsets to hardware irq numbers */
0161     for (irq = 0; irq < irq_count; irq++) {
0162         ppc4xx_hsta_msi.irq_map[irq] =
0163             irq_of_parse_and_map(dev->of_node, irq);
0164         if (!ppc4xx_hsta_msi.irq_map[irq]) {
0165             dev_err(dev, "Unable to map IRQ\n");
0166             ret = -EINVAL;
0167             goto out2;
0168         }
0169     }
0170 
0171     list_for_each_entry(phb, &hose_list, list_node) {
0172         phb->controller_ops.setup_msi_irqs = hsta_setup_msi_irqs;
0173         phb->controller_ops.teardown_msi_irqs = hsta_teardown_msi_irqs;
0174     }
0175     return 0;
0176 
0177 out2:
0178     kfree(ppc4xx_hsta_msi.irq_map);
0179 
0180 out1:
0181     msi_bitmap_free(&ppc4xx_hsta_msi.bmp);
0182 
0183 out:
0184     iounmap(ppc4xx_hsta_msi.data);
0185     return ret;
0186 }
0187 
0188 static const struct of_device_id hsta_msi_ids[] = {
0189     {
0190         .compatible = "ibm,hsta-msi",
0191     },
0192     {}
0193 };
0194 
0195 static struct platform_driver hsta_msi_driver = {
0196     .probe = hsta_msi_probe,
0197     .driver = {
0198         .name = "hsta-msi",
0199         .of_match_table = hsta_msi_ids,
0200     },
0201 };
0202 
0203 static int hsta_msi_init(void)
0204 {
0205     return platform_driver_register(&hsta_msi_driver);
0206 }
0207 subsys_initcall(hsta_msi_init);