aboutsummaryrefslogtreecommitdiffstats
path: root/arch/powerpc/sysdev
diff options
context:
space:
mode:
authorAlistair Popple <alistair@popple.id.au>2014-03-05 22:52:28 -0500
committerBenjamin Herrenschmidt <benh@kernel.crashing.org>2014-04-30 18:26:30 -0400
commite2c37d908336dc27c8b405f063c2a163124947fa (patch)
tree2a5582122d13305abcd4558ed6810329369ea211 /arch/powerpc/sysdev
parent2a2c74b2efcb1a0ca3fdcb5fbb96ad8de6a29177 (diff)
powerpc: Added PCI MSI support using the HSTA module
The PPC476GTR SoC supports message signalled interrupts (MSI) by writing to special addresses within the High Speed Transfer Assist (HSTA) module. This patch adds support for PCI MSI with a new system device. The DMA window is also updated to allow access to the entire 42-bit address range to allow PCI devices write access to the HSTA module. Signed-off-by: Alistair Popple <alistair@popple.id.au> Signed-off-by: Benjamin Herrenschmidt <benh@kernel.crashing.org>
Diffstat (limited to 'arch/powerpc/sysdev')
-rw-r--r--arch/powerpc/sysdev/Kconfig6
-rw-r--r--arch/powerpc/sysdev/Makefile1
-rw-r--r--arch/powerpc/sysdev/ppc4xx_hsta_msi.c215
-rw-r--r--arch/powerpc/sysdev/ppc4xx_pci.c8
4 files changed, 228 insertions, 2 deletions
diff --git a/arch/powerpc/sysdev/Kconfig b/arch/powerpc/sysdev/Kconfig
index 7baa70d6dc01..a19332a38715 100644
--- a/arch/powerpc/sysdev/Kconfig
+++ b/arch/powerpc/sysdev/Kconfig
@@ -7,6 +7,12 @@ config PPC4xx_PCI_EXPRESS
7 depends on PCI && 4xx 7 depends on PCI && 4xx
8 default n 8 default n
9 9
10config PPC4xx_HSTA_MSI
11 bool
12 depends on PCI_MSI
13 depends on PCI && 4xx
14 default n
15
10config PPC4xx_MSI 16config PPC4xx_MSI
11 bool 17 bool
12 depends on PCI_MSI 18 depends on PCI_MSI
diff --git a/arch/powerpc/sysdev/Makefile b/arch/powerpc/sysdev/Makefile
index afbcc37aa094..f7cb2a1b01fa 100644
--- a/arch/powerpc/sysdev/Makefile
+++ b/arch/powerpc/sysdev/Makefile
@@ -45,6 +45,7 @@ obj-$(CONFIG_OF_RTC) += of_rtc.o
45ifeq ($(CONFIG_PCI),y) 45ifeq ($(CONFIG_PCI),y)
46obj-$(CONFIG_4xx) += ppc4xx_pci.o 46obj-$(CONFIG_4xx) += ppc4xx_pci.o
47endif 47endif
48obj-$(CONFIG_PPC4xx_HSTA_MSI) += ppc4xx_hsta_msi.o
48obj-$(CONFIG_PPC4xx_MSI) += ppc4xx_msi.o 49obj-$(CONFIG_PPC4xx_MSI) += ppc4xx_msi.o
49obj-$(CONFIG_PPC4xx_CPM) += ppc4xx_cpm.o 50obj-$(CONFIG_PPC4xx_CPM) += ppc4xx_cpm.o
50obj-$(CONFIG_PPC4xx_GPIO) += ppc4xx_gpio.o 51obj-$(CONFIG_PPC4xx_GPIO) += ppc4xx_gpio.o
diff --git a/arch/powerpc/sysdev/ppc4xx_hsta_msi.c b/arch/powerpc/sysdev/ppc4xx_hsta_msi.c
new file mode 100644
index 000000000000..11c888416f0a
--- /dev/null
+++ b/arch/powerpc/sysdev/ppc4xx_hsta_msi.c
@@ -0,0 +1,215 @@
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 list_for_each_entry(entry, &dev->msi_list, list) {
48 irq = msi_bitmap_alloc_hwirqs(&ppc4xx_hsta_msi.bmp, 1);
49 if (irq < 0) {
50 pr_debug("%s: Failed to allocate msi interrupt\n",
51 __func__);
52 return irq;
53 }
54
55 hwirq = ppc4xx_hsta_msi.irq_map[irq];
56 if (hwirq == NO_IRQ) {
57 pr_err("%s: Failed mapping irq %d\n", __func__, irq);
58 return -EINVAL;
59 }
60
61 /*
62 * HSTA generates interrupts on writes to 128-bit aligned
63 * addresses.
64 */
65 addr = ppc4xx_hsta_msi.address + irq*0x10;
66 msg.address_hi = upper_32_bits(addr);
67 msg.address_lo = lower_32_bits(addr);
68
69 /* Data is not used by the HSTA. */
70 msg.data = 0;
71
72 pr_debug("%s: Setup irq %d (0x%0llx)\n", __func__, hwirq,
73 (((u64) msg.address_hi) << 32) | msg.address_lo);
74
75 if (irq_set_msi_desc(hwirq, entry)) {
76 pr_err(
77 "%s: Invalid hwirq %d specified in device tree\n",
78 __func__, hwirq);
79 msi_bitmap_free_hwirqs(&ppc4xx_hsta_msi.bmp, irq, 1);
80 return -EINVAL;
81 }
82 write_msi_msg(hwirq, &msg);
83 }
84
85 return 0;
86}
87
88static int hsta_find_hwirq_offset(int hwirq)
89{
90 int irq;
91
92 /* Find the offset given the hwirq */
93 for (irq = 0; irq < ppc4xx_hsta_msi.irq_count; irq++)
94 if (ppc4xx_hsta_msi.irq_map[irq] == hwirq)
95 return irq;
96
97 return -EINVAL;
98}
99
100static void hsta_teardown_msi_irqs(struct pci_dev *dev)
101{
102 struct msi_desc *entry;
103 int irq;
104
105 list_for_each_entry(entry, &dev->msi_list, list) {
106 if (entry->irq == NO_IRQ)
107 continue;
108
109 irq = hsta_find_hwirq_offset(entry->irq);
110
111 /* entry->irq should always be in irq_map */
112 BUG_ON(irq < 0);
113 irq_set_msi_desc(entry->irq, NULL);
114 msi_bitmap_free_hwirqs(&ppc4xx_hsta_msi.bmp, irq, 1);
115 pr_debug("%s: Teardown IRQ %u (index %u)\n", __func__,
116 entry->irq, irq);
117 }
118}
119
120static int hsta_msi_check_device(struct pci_dev *pdev, int nvec, int type)
121{
122 /* We don't support MSI-X */
123 if (type == PCI_CAP_ID_MSIX) {
124 pr_debug("%s: MSI-X not supported.\n", __func__);
125 return -EINVAL;
126 }
127
128 return 0;
129}
130
131static int hsta_msi_probe(struct platform_device *pdev)
132{
133 struct device *dev = &pdev->dev;
134 struct resource *mem;
135 int irq, ret, irq_count;
136
137 mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
138 if (IS_ERR(mem)) {
139 dev_err(dev, "Unable to get mmio space\n");
140 return -EINVAL;
141 }
142
143 irq_count = of_irq_count(dev->of_node);
144 if (!irq_count) {
145 dev_err(dev, "Unable to find IRQ range\n");
146 return -EINVAL;
147 }
148
149 ppc4xx_hsta_msi.dev = dev;
150 ppc4xx_hsta_msi.address = mem->start;
151 ppc4xx_hsta_msi.data = ioremap(mem->start, resource_size(mem));
152 ppc4xx_hsta_msi.irq_count = irq_count;
153 if (IS_ERR(ppc4xx_hsta_msi.data)) {
154 dev_err(dev, "Unable to map memory\n");
155 return -ENOMEM;
156 }
157
158 ret = msi_bitmap_alloc(&ppc4xx_hsta_msi.bmp, irq_count, dev->of_node);
159 if (ret)
160 goto out;
161
162 ppc4xx_hsta_msi.irq_map = kmalloc(sizeof(int) * irq_count, GFP_KERNEL);
163 if (IS_ERR(ppc4xx_hsta_msi.irq_map)) {
164 ret = -ENOMEM;
165 goto out1;
166 }
167
168 /* Setup a mapping from irq offsets to hardware irq numbers */
169 for (irq = 0; irq < irq_count; irq++) {
170 ppc4xx_hsta_msi.irq_map[irq] =
171 irq_of_parse_and_map(dev->of_node, irq);
172 if (ppc4xx_hsta_msi.irq_map[irq] == NO_IRQ) {
173 dev_err(dev, "Unable to map IRQ\n");
174 ret = -EINVAL;
175 goto out2;
176 }
177 }
178
179 ppc_md.setup_msi_irqs = hsta_setup_msi_irqs;
180 ppc_md.teardown_msi_irqs = hsta_teardown_msi_irqs;
181 ppc_md.msi_check_device = hsta_msi_check_device;
182 return 0;
183
184out2:
185 kfree(ppc4xx_hsta_msi.irq_map);
186
187out1:
188 msi_bitmap_free(&ppc4xx_hsta_msi.bmp);
189
190out:
191 iounmap(ppc4xx_hsta_msi.data);
192 return ret;
193}
194
195static const struct of_device_id hsta_msi_ids[] = {
196 {
197 .compatible = "ibm,hsta-msi",
198 },
199 {}
200};
201
202static struct platform_driver hsta_msi_driver = {
203 .probe = hsta_msi_probe,
204 .driver = {
205 .name = "hsta-msi",
206 .owner = THIS_MODULE,
207 .of_match_table = hsta_msi_ids,
208 },
209};
210
211static int hsta_msi_init(void)
212{
213 return platform_driver_register(&hsta_msi_driver);
214}
215subsys_initcall(hsta_msi_init);
diff --git a/arch/powerpc/sysdev/ppc4xx_pci.c b/arch/powerpc/sysdev/ppc4xx_pci.c
index 5a4f61ee6b33..df6e2fc4ff92 100644
--- a/arch/powerpc/sysdev/ppc4xx_pci.c
+++ b/arch/powerpc/sysdev/ppc4xx_pci.c
@@ -176,8 +176,12 @@ static int __init ppc4xx_parse_dma_ranges(struct pci_controller *hose,
176 return -ENXIO; 176 return -ENXIO;
177 } 177 }
178 178
179 /* Check that we are fully contained within 32 bits space */ 179 /* Check that we are fully contained within 32 bits space if we are not
180 if (res->end > 0xffffffff) { 180 * running on a 460sx or 476fpe which have 64 bit bus addresses.
181 */
182 if (res->end > 0xffffffff &&
183 !(of_device_is_compatible(hose->dn, "ibm,plb-pciex-460sx")
184 || of_device_is_compatible(hose->dn, "ibm,plb-pciex-476fpe"))) {
181 printk(KERN_ERR "%s: dma-ranges outside of 32 bits space\n", 185 printk(KERN_ERR "%s: dma-ranges outside of 32 bits space\n",
182 hose->dn->full_name); 186 hose->dn->full_name);
183 return -ENXIO; 187 return -ENXIO;