1/* 2 * MSI support for PPC4xx SoCs using High Speed Transfer Assist (HSTA) for 3 * generation of the interrupt. 4 * 5 * Copyright © 2013 Alistair Popple <alistair@popple.id.au> IBM Corporation 6 * 7 * This program is free software; you can redistribute it and/or modify it 8 * under the terms of the GNU General Public License as published by the 9 * Free Software Foundation; either version 2 of the License, or (at your 10 * option) any later version. 11 */ 12 13#include <linux/kernel.h> 14#include <linux/interrupt.h> 15#include <linux/msi.h> 16#include <linux/of.h> 17#include <linux/of_platform.h> 18#include <linux/pci.h> 19#include <linux/semaphore.h> 20#include <asm/msi_bitmap.h> 21 22struct ppc4xx_hsta_msi { 23 struct device *dev; 24 25 /* The ioremapped HSTA MSI IO space */ 26 u32 __iomem *data; 27 28 /* Physical address of HSTA MSI IO space */ 29 u64 address; 30 struct msi_bitmap bmp; 31 32 /* An array mapping offsets to hardware IRQs */ 33 int *irq_map; 34 35 /* Number of hwirqs supported */ 36 int irq_count; 37}; 38static struct ppc4xx_hsta_msi ppc4xx_hsta_msi; 39 40static int hsta_setup_msi_irqs(struct pci_dev *dev, int nvec, int type) 41{ 42 struct msi_msg msg; 43 struct msi_desc *entry; 44 int irq, hwirq; 45 u64 addr; 46 47 /* We don't support MSI-X */ 48 if (type == PCI_CAP_ID_MSIX) { 49 pr_debug("%s: MSI-X not supported.\n", __func__); 50 return -EINVAL; 51 } 52 53 list_for_each_entry(entry, &dev->msi_list, list) { 54 irq = msi_bitmap_alloc_hwirqs(&ppc4xx_hsta_msi.bmp, 1); 55 if (irq < 0) { 56 pr_debug("%s: Failed to allocate msi interrupt\n", 57 __func__); 58 return irq; 59 } 60 61 hwirq = ppc4xx_hsta_msi.irq_map[irq]; 62 if (hwirq == NO_IRQ) { 63 pr_err("%s: Failed mapping irq %d\n", __func__, irq); 64 return -EINVAL; 65 } 66 67 /* 68 * HSTA generates interrupts on writes to 128-bit aligned 69 * addresses. 70 */ 71 addr = ppc4xx_hsta_msi.address + irq*0x10; 72 msg.address_hi = upper_32_bits(addr); 73 msg.address_lo = lower_32_bits(addr); 74 75 /* Data is not used by the HSTA. */ 76 msg.data = 0; 77 78 pr_debug("%s: Setup irq %d (0x%0llx)\n", __func__, hwirq, 79 (((u64) msg.address_hi) << 32) | msg.address_lo); 80 81 if (irq_set_msi_desc(hwirq, entry)) { 82 pr_err( 83 "%s: Invalid hwirq %d specified in device tree\n", 84 __func__, hwirq); 85 msi_bitmap_free_hwirqs(&ppc4xx_hsta_msi.bmp, irq, 1); 86 return -EINVAL; 87 } 88 pci_write_msi_msg(hwirq, &msg); 89 } 90 91 return 0; 92} 93 94static int hsta_find_hwirq_offset(int hwirq) 95{ 96 int irq; 97 98 /* Find the offset given the hwirq */ 99 for (irq = 0; irq < ppc4xx_hsta_msi.irq_count; irq++) 100 if (ppc4xx_hsta_msi.irq_map[irq] == hwirq) 101 return irq; 102 103 return -EINVAL; 104} 105 106static void hsta_teardown_msi_irqs(struct pci_dev *dev) 107{ 108 struct msi_desc *entry; 109 int irq; 110 111 list_for_each_entry(entry, &dev->msi_list, list) { 112 if (entry->irq == NO_IRQ) 113 continue; 114 115 irq = hsta_find_hwirq_offset(entry->irq); 116 117 /* entry->irq should always be in irq_map */ 118 BUG_ON(irq < 0); 119 irq_set_msi_desc(entry->irq, NULL); 120 msi_bitmap_free_hwirqs(&ppc4xx_hsta_msi.bmp, irq, 1); 121 pr_debug("%s: Teardown IRQ %u (index %u)\n", __func__, 122 entry->irq, irq); 123 } 124} 125 126static int hsta_msi_probe(struct platform_device *pdev) 127{ 128 struct device *dev = &pdev->dev; 129 struct resource *mem; 130 int irq, ret, irq_count; 131 132 mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); 133 if (IS_ERR(mem)) { 134 dev_err(dev, "Unable to get mmio space\n"); 135 return -EINVAL; 136 } 137 138 irq_count = of_irq_count(dev->of_node); 139 if (!irq_count) { 140 dev_err(dev, "Unable to find IRQ range\n"); 141 return -EINVAL; 142 } 143 144 ppc4xx_hsta_msi.dev = dev; 145 ppc4xx_hsta_msi.address = mem->start; 146 ppc4xx_hsta_msi.data = ioremap(mem->start, resource_size(mem)); 147 ppc4xx_hsta_msi.irq_count = irq_count; 148 if (!ppc4xx_hsta_msi.data) { 149 dev_err(dev, "Unable to map memory\n"); 150 return -ENOMEM; 151 } 152 153 ret = msi_bitmap_alloc(&ppc4xx_hsta_msi.bmp, irq_count, dev->of_node); 154 if (ret) 155 goto out; 156 157 ppc4xx_hsta_msi.irq_map = kmalloc(sizeof(int) * irq_count, GFP_KERNEL); 158 if (IS_ERR(ppc4xx_hsta_msi.irq_map)) { 159 ret = -ENOMEM; 160 goto out1; 161 } 162 163 /* Setup a mapping from irq offsets to hardware irq numbers */ 164 for (irq = 0; irq < irq_count; irq++) { 165 ppc4xx_hsta_msi.irq_map[irq] = 166 irq_of_parse_and_map(dev->of_node, irq); 167 if (ppc4xx_hsta_msi.irq_map[irq] == NO_IRQ) { 168 dev_err(dev, "Unable to map IRQ\n"); 169 ret = -EINVAL; 170 goto out2; 171 } 172 } 173 174 ppc_md.setup_msi_irqs = hsta_setup_msi_irqs; 175 ppc_md.teardown_msi_irqs = hsta_teardown_msi_irqs; 176 return 0; 177 178out2: 179 kfree(ppc4xx_hsta_msi.irq_map); 180 181out1: 182 msi_bitmap_free(&ppc4xx_hsta_msi.bmp); 183 184out: 185 iounmap(ppc4xx_hsta_msi.data); 186 return ret; 187} 188 189static const struct of_device_id hsta_msi_ids[] = { 190 { 191 .compatible = "ibm,hsta-msi", 192 }, 193 {} 194}; 195 196static struct platform_driver hsta_msi_driver = { 197 .probe = hsta_msi_probe, 198 .driver = { 199 .name = "hsta-msi", 200 .of_match_table = hsta_msi_ids, 201 }, 202}; 203 204static int hsta_msi_init(void) 205{ 206 return platform_driver_register(&hsta_msi_driver); 207} 208subsys_initcall(hsta_msi_init); 209