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