Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

powerpc/4xx: Adding PCIe MSI support

This patch adds MSI support for 440SPe, 460Ex, 460Sx and 405Ex.

Signed-off-by: Rupjyoti Sarmah <rsarmah@apm.com>
Signed-off-by: Tirumala R Marri <tmarri@apm.com>
Acked-by: Josh Boyer <jwboyer@linux.vnet.ibm.com>
Signed-off-by: Benjamin Herrenschmidt <benh@kernel.crashing.org>

authored by

Rupjyoti Sarmah and committed by
Benjamin Herrenschmidt
3fb79338 4dd60290

+376
+18
arch/powerpc/boot/dts/canyonlands.dts
··· 530 530 0x0 0x0 0x0 0x3 &UIC3 0x12 0x4 /* swizzled int C */ 531 531 0x0 0x0 0x0 0x4 &UIC3 0x13 0x4 /* swizzled int D */>; 532 532 }; 533 + 534 + MSI: ppc4xx-msi@C10000000 { 535 + compatible = "amcc,ppc4xx-msi", "ppc4xx-msi"; 536 + reg = < 0xC 0x10000000 0x100>; 537 + sdr-base = <0x36C>; 538 + msi-data = <0x00000000>; 539 + msi-mask = <0x44440000>; 540 + interrupt-count = <3>; 541 + interrupts = <0 1 2 3>; 542 + interrupt-parent = <&UIC3>; 543 + #interrupt-cells = <1>; 544 + #address-cells = <0>; 545 + #size-cells = <0>; 546 + interrupt-map = <0 &UIC3 0x18 1 547 + 1 &UIC3 0x19 1 548 + 2 &UIC3 0x1A 1 549 + 3 &UIC3 0x1B 1>; 550 + }; 533 551 }; 534 552 };
+18
arch/powerpc/boot/dts/katmai.dts
··· 442 442 0x0 0x0 0x0 0x4 &UIC3 0xb 0x4 /* swizzled int D */>; 443 443 }; 444 444 445 + MSI: ppc4xx-msi@400300000 { 446 + compatible = "amcc,ppc4xx-msi", "ppc4xx-msi"; 447 + reg = < 0x4 0x00300000 0x100>; 448 + sdr-base = <0x3B0>; 449 + msi-data = <0x00000000>; 450 + msi-mask = <0x44440000>; 451 + interrupt-count = <3>; 452 + interrupts =<0 1 2 3>; 453 + interrupt-parent = <&UIC0>; 454 + #interrupt-cells = <1>; 455 + #address-cells = <0>; 456 + #size-cells = <0>; 457 + interrupt-map = <0 &UIC0 0xC 1 458 + 1 &UIC0 0x0D 1 459 + 2 &UIC0 0x0E 1 460 + 3 &UIC0 0x0F 1>; 461 + }; 462 + 445 463 I2O: i2o@400100000 { 446 464 compatible = "ibm,i2o-440spe"; 447 465 reg = <0x00000004 0x00100000 0x100>;
+28
arch/powerpc/boot/dts/kilauea.dts
··· 403 403 0x0 0x0 0x0 0x3 &UIC2 0xd 0x4 /* swizzled int C */ 404 404 0x0 0x0 0x0 0x4 &UIC2 0xe 0x4 /* swizzled int D */>; 405 405 }; 406 + 407 + MSI: ppc4xx-msi@C10000000 { 408 + compatible = "amcc,ppc4xx-msi", "ppc4xx-msi"; 409 + reg = < 0x0 0xEF620000 0x100>; 410 + sdr-base = <0x4B0>; 411 + msi-data = <0x00000000>; 412 + msi-mask = <0x44440000>; 413 + interrupt-count = <12>; 414 + interrupts = <0 1 2 3 4 5 6 7 8 9 0xA 0xB 0xC 0xD>; 415 + interrupt-parent = <&UIC2>; 416 + #interrupt-cells = <1>; 417 + #address-cells = <0>; 418 + #size-cells = <0>; 419 + interrupt-map = <0 &UIC2 0x10 1 420 + 1 &UIC2 0x11 1 421 + 2 &UIC2 0x12 1 422 + 2 &UIC2 0x13 1 423 + 2 &UIC2 0x14 1 424 + 2 &UIC2 0x15 1 425 + 2 &UIC2 0x16 1 426 + 2 &UIC2 0x17 1 427 + 2 &UIC2 0x18 1 428 + 2 &UIC2 0x19 1 429 + 2 &UIC2 0x1A 1 430 + 2 &UIC2 0x1B 1 431 + 2 &UIC2 0x1C 1 432 + 3 &UIC2 0x1D 1>; 433 + }; 406 434 }; 407 435 };
+20
arch/powerpc/boot/dts/redwood.dts
··· 358 358 0x0 0x0 0x0 0x4 &UIC3 0xb 0x4 /* swizzled int D */>; 359 359 }; 360 360 361 + MSI: ppc4xx-msi@400300000 { 362 + compatible = "amcc,ppc4xx-msi", "ppc4xx-msi"; 363 + reg = < 0x4 0x00300000 0x100 364 + 0x4 0x00300000 0x100>; 365 + sdr-base = <0x3B0>; 366 + msi-data = <0x00000000>; 367 + msi-mask = <0x44440000>; 368 + interrupt-count = <3>; 369 + interrupts =<0 1 2 3>; 370 + interrupt-parent = <&UIC0>; 371 + #interrupt-cells = <1>; 372 + #address-cells = <0>; 373 + #size-cells = <0>; 374 + interrupt-map = <0 &UIC0 0xC 1 375 + 1 &UIC0 0x0D 1 376 + 2 &UIC0 0x0E 1 377 + 3 &UIC0 0x0F 1>; 378 + }; 379 + 361 380 }; 381 + 362 382 363 383 chosen { 364 384 linux,stdout-path = "/plb/opb/serial@ef600200";
+2
arch/powerpc/platforms/40x/Kconfig
··· 57 57 select 405EX 58 58 select PPC40x_SIMPLE 59 59 select PPC4xx_PCI_EXPRESS 60 + select PCI_MSI 61 + select PPC4xx_MSI 60 62 help 61 63 This option enables support for the AMCC PPC405EX evaluation board. 62 64
+6
arch/powerpc/platforms/44x/Kconfig
··· 74 74 select 440SPe 75 75 select PCI 76 76 select PPC4xx_PCI_EXPRESS 77 + select PCI_MSI 78 + select PCC4xx_MSI 77 79 help 78 80 This option enables support for the AMCC PPC440SPe evaluation board. 79 81 ··· 120 118 select 460EX 121 119 select PCI 122 120 select PPC4xx_PCI_EXPRESS 121 + select PCI_MSI 122 + select PPC4xx_MSI 123 123 select IBM_NEW_EMAC_RGMII 124 124 select IBM_NEW_EMAC_ZMII 125 125 help ··· 148 144 select 460SX 149 145 select PCI 150 146 select PPC4xx_PCI_EXPRESS 147 + select PCI_MSI 148 + select PPC4xx_MSI 151 149 help 152 150 This option enables support for the AMCC PPC460SX Redwood board. 153 151
+7
arch/powerpc/sysdev/Kconfig
··· 7 7 depends on PCI && 4xx 8 8 default n 9 9 10 + config PPC4xx_MSI 11 + bool 12 + depends on PCI_MSI 13 + depends on PCI && 4xx 14 + default n 15 + 10 16 config PPC_MSI_BITMAP 11 17 bool 12 18 depends on PCI_MSI 13 19 default y if MPIC 14 20 default y if FSL_PCI 21 + default y if PPC4xx_MSI 15 22 16 23 source "arch/powerpc/sysdev/xics/Kconfig" 17 24
+1
arch/powerpc/sysdev/Makefile
··· 41 41 ifeq ($(CONFIG_PCI),y) 42 42 obj-$(CONFIG_4xx) += ppc4xx_pci.o 43 43 endif 44 + obj-$(CONFIG_PPC4xx_MSI) += ppc4xx_msi.o 44 45 obj-$(CONFIG_PPC4xx_CPM) += ppc4xx_cpm.o 45 46 obj-$(CONFIG_PPC4xx_GPIO) += ppc4xx_gpio.o 46 47
+276
arch/powerpc/sysdev/ppc4xx_msi.c
··· 1 + /* 2 + * Adding PCI-E MSI support for PPC4XX SoCs. 3 + * 4 + * Copyright (c) 2010, Applied Micro Circuits Corporation 5 + * Authors: Tirumala R Marri <tmarri@apm.com> 6 + * Feng Kan <fkan@apm.com> 7 + * 8 + * This program is free software; you can redistribute it and/or 9 + * modify it under the terms of the GNU General Public License as 10 + * published by the Free Software Foundation; either version 2 of 11 + * the License, or (at your option) any later version. 12 + * 13 + * This program is distributed in the hope that it will be useful, 14 + * but WITHOUT ANY WARRANTY; without even the implied warranty of 15 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 + * GNU General Public License for more details. 17 + * 18 + * You should have received a copy of the GNU General Public License 19 + * along with this program; if not, write to the Free Software 20 + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, 21 + * MA 02111-1307 USA 22 + */ 23 + 24 + #include <linux/irq.h> 25 + #include <linux/bootmem.h> 26 + #include <linux/pci.h> 27 + #include <linux/msi.h> 28 + #include <linux/of_platform.h> 29 + #include <linux/interrupt.h> 30 + #include <asm/prom.h> 31 + #include <asm/hw_irq.h> 32 + #include <asm/ppc-pci.h> 33 + #include <boot/dcr.h> 34 + #include <asm/dcr-regs.h> 35 + #include <asm/msi_bitmap.h> 36 + 37 + #define PEIH_TERMADH 0x00 38 + #define PEIH_TERMADL 0x08 39 + #define PEIH_MSIED 0x10 40 + #define PEIH_MSIMK 0x18 41 + #define PEIH_MSIASS 0x20 42 + #define PEIH_FLUSH0 0x30 43 + #define PEIH_FLUSH1 0x38 44 + #define PEIH_CNTRST 0x48 45 + #define NR_MSI_IRQS 4 46 + 47 + struct ppc4xx_msi { 48 + u32 msi_addr_lo; 49 + u32 msi_addr_hi; 50 + void __iomem *msi_regs; 51 + int msi_virqs[NR_MSI_IRQS]; 52 + struct msi_bitmap bitmap; 53 + struct device_node *msi_dev; 54 + }; 55 + 56 + static struct ppc4xx_msi ppc4xx_msi; 57 + 58 + static int ppc4xx_msi_init_allocator(struct platform_device *dev, 59 + struct ppc4xx_msi *msi_data) 60 + { 61 + int err; 62 + 63 + err = msi_bitmap_alloc(&msi_data->bitmap, NR_MSI_IRQS, 64 + dev->dev.of_node); 65 + if (err) 66 + return err; 67 + 68 + err = msi_bitmap_reserve_dt_hwirqs(&msi_data->bitmap); 69 + if (err < 0) { 70 + msi_bitmap_free(&msi_data->bitmap); 71 + return err; 72 + } 73 + 74 + return 0; 75 + } 76 + 77 + static int ppc4xx_setup_msi_irqs(struct pci_dev *dev, int nvec, int type) 78 + { 79 + int int_no = -ENOMEM; 80 + unsigned int virq; 81 + struct msi_msg msg; 82 + struct msi_desc *entry; 83 + struct ppc4xx_msi *msi_data = &ppc4xx_msi; 84 + 85 + list_for_each_entry(entry, &dev->msi_list, list) { 86 + int_no = msi_bitmap_alloc_hwirqs(&msi_data->bitmap, 1); 87 + if (int_no >= 0) 88 + break; 89 + if (int_no < 0) { 90 + pr_debug("%s: fail allocating msi interrupt\n", 91 + __func__); 92 + } 93 + virq = irq_of_parse_and_map(msi_data->msi_dev, int_no); 94 + if (virq == NO_IRQ) { 95 + dev_err(&dev->dev, "%s: fail mapping irq\n", __func__); 96 + msi_bitmap_free_hwirqs(&msi_data->bitmap, int_no, 1); 97 + return -ENOSPC; 98 + } 99 + dev_dbg(&dev->dev, "%s: virq = %d\n", __func__, virq); 100 + 101 + /* Setup msi address space */ 102 + msg.address_hi = msi_data->msi_addr_hi; 103 + msg.address_lo = msi_data->msi_addr_lo; 104 + 105 + irq_set_msi_desc(virq, entry); 106 + msg.data = int_no; 107 + write_msi_msg(virq, &msg); 108 + } 109 + return 0; 110 + } 111 + 112 + void ppc4xx_teardown_msi_irqs(struct pci_dev *dev) 113 + { 114 + struct msi_desc *entry; 115 + struct ppc4xx_msi *msi_data = &ppc4xx_msi; 116 + 117 + dev_dbg(&dev->dev, "PCIE-MSI: tearing down msi irqs\n"); 118 + 119 + list_for_each_entry(entry, &dev->msi_list, list) { 120 + if (entry->irq == NO_IRQ) 121 + continue; 122 + irq_set_msi_desc(entry->irq, NULL); 123 + msi_bitmap_free_hwirqs(&msi_data->bitmap, 124 + virq_to_hw(entry->irq), 1); 125 + irq_dispose_mapping(entry->irq); 126 + } 127 + } 128 + 129 + static int ppc4xx_msi_check_device(struct pci_dev *pdev, int nvec, int type) 130 + { 131 + dev_dbg(&pdev->dev, "PCIE-MSI:%s called. vec %x type %d\n", 132 + __func__, nvec, type); 133 + if (type == PCI_CAP_ID_MSIX) 134 + pr_debug("ppc4xx msi: MSI-X untested, trying anyway.\n"); 135 + 136 + return 0; 137 + } 138 + 139 + static int ppc4xx_setup_pcieh_hw(struct platform_device *dev, 140 + struct resource res, struct ppc4xx_msi *msi) 141 + { 142 + const u32 *msi_data; 143 + const u32 *msi_mask; 144 + const u32 *sdr_addr; 145 + dma_addr_t msi_phys; 146 + void *msi_virt; 147 + 148 + sdr_addr = of_get_property(dev->dev.of_node, "sdr-base", NULL); 149 + if (!sdr_addr) 150 + return -1; 151 + 152 + SDR0_WRITE(sdr_addr, (u64)res.start >> 32); /*HIGH addr */ 153 + SDR0_WRITE(sdr_addr + 1, res.start & 0xFFFFFFFF); /* Low addr */ 154 + 155 + 156 + msi->msi_dev = of_find_node_by_name(NULL, "ppc4xx-msi"); 157 + if (msi->msi_dev) 158 + return -ENODEV; 159 + 160 + msi->msi_regs = of_iomap(msi->msi_dev, 0); 161 + if (!msi->msi_regs) { 162 + dev_err(&dev->dev, "of_iomap problem failed\n"); 163 + return -ENOMEM; 164 + } 165 + dev_dbg(&dev->dev, "PCIE-MSI: msi register mapped 0x%x 0x%x\n", 166 + (u32) (msi->msi_regs + PEIH_TERMADH), (u32) (msi->msi_regs)); 167 + 168 + msi_virt = dma_alloc_coherent(&dev->dev, 64, &msi_phys, GFP_KERNEL); 169 + msi->msi_addr_hi = 0x0; 170 + msi->msi_addr_lo = (u32) msi_phys; 171 + dev_dbg(&dev->dev, "PCIE-MSI: msi address 0x%x\n", msi->msi_addr_lo); 172 + 173 + /* Progam the Interrupt handler Termination addr registers */ 174 + out_be32(msi->msi_regs + PEIH_TERMADH, msi->msi_addr_hi); 175 + out_be32(msi->msi_regs + PEIH_TERMADL, msi->msi_addr_lo); 176 + 177 + msi_data = of_get_property(dev->dev.of_node, "msi-data", NULL); 178 + if (!msi_data) 179 + return -1; 180 + msi_mask = of_get_property(dev->dev.of_node, "msi-mask", NULL); 181 + if (!msi_mask) 182 + return -1; 183 + /* Program MSI Expected data and Mask bits */ 184 + out_be32(msi->msi_regs + PEIH_MSIED, *msi_data); 185 + out_be32(msi->msi_regs + PEIH_MSIMK, *msi_mask); 186 + 187 + return 0; 188 + } 189 + 190 + static int ppc4xx_of_msi_remove(struct platform_device *dev) 191 + { 192 + struct ppc4xx_msi *msi = dev->dev.platform_data; 193 + int i; 194 + int virq; 195 + 196 + for (i = 0; i < NR_MSI_IRQS; i++) { 197 + virq = msi->msi_virqs[i]; 198 + if (virq != NO_IRQ) 199 + irq_dispose_mapping(virq); 200 + } 201 + 202 + if (msi->bitmap.bitmap) 203 + msi_bitmap_free(&msi->bitmap); 204 + iounmap(msi->msi_regs); 205 + of_node_put(msi->msi_dev); 206 + kfree(msi); 207 + 208 + return 0; 209 + } 210 + 211 + static int __devinit ppc4xx_msi_probe(struct platform_device *dev) 212 + { 213 + struct ppc4xx_msi *msi; 214 + struct resource res; 215 + int err = 0; 216 + 217 + msi = &ppc4xx_msi;/*keep the msi data for further use*/ 218 + 219 + dev_dbg(&dev->dev, "PCIE-MSI: Setting up MSI support...\n"); 220 + 221 + msi = kzalloc(sizeof(struct ppc4xx_msi), GFP_KERNEL); 222 + if (!msi) { 223 + dev_err(&dev->dev, "No memory for MSI structure\n"); 224 + return -ENOMEM; 225 + } 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, "%s resource error!\n", 232 + dev->dev.of_node->full_name); 233 + goto error_out; 234 + } 235 + 236 + if (ppc4xx_setup_pcieh_hw(dev, res, msi)) 237 + goto error_out; 238 + 239 + err = ppc4xx_msi_init_allocator(dev, msi); 240 + if (err) { 241 + dev_err(&dev->dev, "Error allocating MSI bitmap\n"); 242 + goto error_out; 243 + } 244 + 245 + ppc_md.setup_msi_irqs = ppc4xx_setup_msi_irqs; 246 + ppc_md.teardown_msi_irqs = ppc4xx_teardown_msi_irqs; 247 + ppc_md.msi_check_device = ppc4xx_msi_check_device; 248 + return err; 249 + 250 + error_out: 251 + ppc4xx_of_msi_remove(dev); 252 + return err; 253 + } 254 + static const struct of_device_id ppc4xx_msi_ids[] = { 255 + { 256 + .compatible = "amcc,ppc4xx-msi", 257 + }, 258 + {} 259 + }; 260 + static struct platform_driver ppc4xx_msi_driver = { 261 + .probe = ppc4xx_msi_probe, 262 + .remove = ppc4xx_of_msi_remove, 263 + .driver = { 264 + .name = "ppc4xx-msi", 265 + .owner = THIS_MODULE, 266 + .of_match_table = ppc4xx_msi_ids, 267 + }, 268 + 269 + }; 270 + 271 + static __init int ppc4xx_msi_init(void) 272 + { 273 + return platform_driver_register(&ppc4xx_msi_driver); 274 + } 275 + 276 + subsys_initcall(ppc4xx_msi_init);