Path: blob/master/arch/powerpc/platforms/44x/hsta_msi.c
26481 views
// SPDX-License-Identifier: GPL-2.0-or-later1/*2* MSI support for PPC4xx SoCs using High Speed Transfer Assist (HSTA) for3* generation of the interrupt.4*5* Copyright © 2013 Alistair Popple <[email protected]> IBM Corporation6*/78#include <linux/kernel.h>9#include <linux/interrupt.h>10#include <linux/msi.h>11#include <linux/of.h>12#include <linux/of_irq.h>13#include <linux/platform_device.h>14#include <linux/pci.h>15#include <linux/semaphore.h>16#include <asm/msi_bitmap.h>17#include <asm/ppc-pci.h>1819struct ppc4xx_hsta_msi {20struct device *dev;2122/* The ioremapped HSTA MSI IO space */23u32 __iomem *data;2425/* Physical address of HSTA MSI IO space */26u64 address;27struct msi_bitmap bmp;2829/* An array mapping offsets to hardware IRQs */30int *irq_map;3132/* Number of hwirqs supported */33int irq_count;34};35static struct ppc4xx_hsta_msi ppc4xx_hsta_msi;3637static int hsta_setup_msi_irqs(struct pci_dev *dev, int nvec, int type)38{39struct msi_msg msg;40struct msi_desc *entry;41int irq, hwirq;42u64 addr;4344/* We don't support MSI-X */45if (type == PCI_CAP_ID_MSIX) {46pr_debug("%s: MSI-X not supported.\n", __func__);47return -EINVAL;48}4950msi_for_each_desc(entry, &dev->dev, MSI_DESC_NOTASSOCIATED) {51irq = msi_bitmap_alloc_hwirqs(&ppc4xx_hsta_msi.bmp, 1);52if (irq < 0) {53pr_debug("%s: Failed to allocate msi interrupt\n",54__func__);55return irq;56}5758hwirq = ppc4xx_hsta_msi.irq_map[irq];59if (!hwirq) {60pr_err("%s: Failed mapping irq %d\n", __func__, irq);61return -EINVAL;62}6364/*65* HSTA generates interrupts on writes to 128-bit aligned66* addresses.67*/68addr = ppc4xx_hsta_msi.address + irq*0x10;69msg.address_hi = upper_32_bits(addr);70msg.address_lo = lower_32_bits(addr);7172/* Data is not used by the HSTA. */73msg.data = 0;7475pr_debug("%s: Setup irq %d (0x%0llx)\n", __func__, hwirq,76(((u64) msg.address_hi) << 32) | msg.address_lo);7778if (irq_set_msi_desc(hwirq, entry)) {79pr_err(80"%s: Invalid hwirq %d specified in device tree\n",81__func__, hwirq);82msi_bitmap_free_hwirqs(&ppc4xx_hsta_msi.bmp, irq, 1);83return -EINVAL;84}85pci_write_msi_msg(hwirq, &msg);86}8788return 0;89}9091static int hsta_find_hwirq_offset(int hwirq)92{93int irq;9495/* Find the offset given the hwirq */96for (irq = 0; irq < ppc4xx_hsta_msi.irq_count; irq++)97if (ppc4xx_hsta_msi.irq_map[irq] == hwirq)98return irq;99100return -EINVAL;101}102103static void hsta_teardown_msi_irqs(struct pci_dev *dev)104{105struct msi_desc *entry;106int irq;107108msi_for_each_desc(entry, &dev->dev, MSI_DESC_ASSOCIATED) {109irq = hsta_find_hwirq_offset(entry->irq);110111/* entry->irq should always be in irq_map */112BUG_ON(irq < 0);113irq_set_msi_desc(entry->irq, NULL);114msi_bitmap_free_hwirqs(&ppc4xx_hsta_msi.bmp, irq, 1);115pr_debug("%s: Teardown IRQ %u (index %u)\n", __func__,116entry->irq, irq);117entry->irq = 0;118}119}120121static int hsta_msi_probe(struct platform_device *pdev)122{123struct device *dev = &pdev->dev;124struct resource *mem;125int irq, ret, irq_count;126struct pci_controller *phb;127128mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);129if (!mem) {130dev_err(dev, "Unable to get mmio space\n");131return -EINVAL;132}133134irq_count = of_irq_count(dev->of_node);135if (!irq_count) {136dev_err(dev, "Unable to find IRQ range\n");137return -EINVAL;138}139140ppc4xx_hsta_msi.dev = dev;141ppc4xx_hsta_msi.address = mem->start;142ppc4xx_hsta_msi.data = ioremap(mem->start, resource_size(mem));143ppc4xx_hsta_msi.irq_count = irq_count;144if (!ppc4xx_hsta_msi.data) {145dev_err(dev, "Unable to map memory\n");146return -ENOMEM;147}148149ret = msi_bitmap_alloc(&ppc4xx_hsta_msi.bmp, irq_count, dev->of_node);150if (ret)151goto out;152153ppc4xx_hsta_msi.irq_map = kmalloc_array(irq_count, sizeof(int),154GFP_KERNEL);155if (!ppc4xx_hsta_msi.irq_map) {156ret = -ENOMEM;157goto out1;158}159160/* Setup a mapping from irq offsets to hardware irq numbers */161for (irq = 0; irq < irq_count; irq++) {162ppc4xx_hsta_msi.irq_map[irq] =163irq_of_parse_and_map(dev->of_node, irq);164if (!ppc4xx_hsta_msi.irq_map[irq]) {165dev_err(dev, "Unable to map IRQ\n");166ret = -EINVAL;167goto out2;168}169}170171list_for_each_entry(phb, &hose_list, list_node) {172phb->controller_ops.setup_msi_irqs = hsta_setup_msi_irqs;173phb->controller_ops.teardown_msi_irqs = hsta_teardown_msi_irqs;174}175return 0;176177out2:178kfree(ppc4xx_hsta_msi.irq_map);179180out1:181msi_bitmap_free(&ppc4xx_hsta_msi.bmp);182183out:184iounmap(ppc4xx_hsta_msi.data);185return ret;186}187188static const struct of_device_id hsta_msi_ids[] = {189{190.compatible = "ibm,hsta-msi",191},192{}193};194195static struct platform_driver hsta_msi_driver = {196.probe = hsta_msi_probe,197.driver = {198.name = "hsta-msi",199.of_match_table = hsta_msi_ids,200},201};202203static int hsta_msi_init(void)204{205return platform_driver_register(&hsta_msi_driver);206}207subsys_initcall(hsta_msi_init);208209210