Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/mason/linux...
[sfrench/cifs-2.6.git] / arch / powerpc / sysdev / ppc4xx_hsta_msi.c
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
22 struct 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 };
38 static struct ppc4xx_hsta_msi ppc4xx_hsta_msi;
39
40 static 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
88 static 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
100 static 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
120 static 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
131 static 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
184 out2:
185         kfree(ppc4xx_hsta_msi.irq_map);
186
187 out1:
188         msi_bitmap_free(&ppc4xx_hsta_msi.bmp);
189
190 out:
191         iounmap(ppc4xx_hsta_msi.data);
192         return ret;
193 }
194
195 static const struct of_device_id hsta_msi_ids[] = {
196         {
197                 .compatible = "ibm,hsta-msi",
198         },
199         {}
200 };
201
202 static 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
211 static int hsta_msi_init(void)
212 {
213         return platform_driver_register(&hsta_msi_driver);
214 }
215 subsys_initcall(hsta_msi_init);