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