Commit | Line | Data |
---|---|---|
50528752 MZ |
1 | // SPDX-License-Identifier: GPL-2.0 |
2 | /* | |
3 | * Copyright (C) 2018 ARM Limited, All Rights Reserved. | |
4 | * Author: Marc Zyngier <marc.zyngier@arm.com> | |
5 | */ | |
6 | ||
7 | #define pr_fmt(fmt) "GICv3: " fmt | |
8 | ||
9 | #include <linux/dma-iommu.h> | |
10 | #include <linux/irq.h> | |
11 | #include <linux/irqdomain.h> | |
12 | #include <linux/kernel.h> | |
13 | #include <linux/msi.h> | |
14 | #include <linux/of_address.h> | |
38985351 | 15 | #include <linux/of_pci.h> |
50528752 MZ |
16 | #include <linux/slab.h> |
17 | #include <linux/spinlock.h> | |
18 | ||
19 | #include <linux/irqchip/arm-gic-v3.h> | |
20 | ||
21 | struct mbi_range { | |
22 | u32 spi_start; | |
23 | u32 nr_spis; | |
24 | unsigned long *bm; | |
25 | }; | |
26 | ||
27 | static struct mutex mbi_lock; | |
28 | static phys_addr_t mbi_phys_base; | |
29 | static struct mbi_range *mbi_ranges; | |
30 | static unsigned int mbi_range_nr; | |
31 | ||
32 | static struct irq_chip mbi_irq_chip = { | |
33 | .name = "MBI", | |
34 | .irq_mask = irq_chip_mask_parent, | |
35 | .irq_unmask = irq_chip_unmask_parent, | |
36 | .irq_eoi = irq_chip_eoi_parent, | |
37 | .irq_set_type = irq_chip_set_type_parent, | |
38 | .irq_set_affinity = irq_chip_set_affinity_parent, | |
39 | }; | |
40 | ||
41 | static int mbi_irq_gic_domain_alloc(struct irq_domain *domain, | |
42 | unsigned int virq, | |
43 | irq_hw_number_t hwirq) | |
44 | { | |
45 | struct irq_fwspec fwspec; | |
46 | struct irq_data *d; | |
47 | int err; | |
48 | ||
49 | /* | |
50 | * Using ACPI? There is no MBI support in the spec, you | |
51 | * shouldn't even be here. | |
52 | */ | |
53 | if (!is_of_node(domain->parent->fwnode)) | |
54 | return -EINVAL; | |
55 | ||
56 | /* | |
57 | * Let's default to edge. This is consistent with traditional | |
58 | * MSIs, and systems requiring level signaling will just | |
59 | * enforce the trigger on their own. | |
60 | */ | |
61 | fwspec.fwnode = domain->parent->fwnode; | |
62 | fwspec.param_count = 3; | |
63 | fwspec.param[0] = 0; | |
64 | fwspec.param[1] = hwirq - 32; | |
65 | fwspec.param[2] = IRQ_TYPE_EDGE_RISING; | |
66 | ||
67 | err = irq_domain_alloc_irqs_parent(domain, virq, 1, &fwspec); | |
68 | if (err) | |
69 | return err; | |
70 | ||
71 | d = irq_domain_get_irq_data(domain->parent, virq); | |
72 | return d->chip->irq_set_type(d, IRQ_TYPE_EDGE_RISING); | |
73 | } | |
74 | ||
75 | static void mbi_free_msi(struct mbi_range *mbi, unsigned int hwirq, | |
76 | int nr_irqs) | |
77 | { | |
78 | mutex_lock(&mbi_lock); | |
79 | bitmap_release_region(mbi->bm, hwirq - mbi->spi_start, | |
80 | get_count_order(nr_irqs)); | |
81 | mutex_unlock(&mbi_lock); | |
82 | } | |
83 | ||
84 | static int mbi_irq_domain_alloc(struct irq_domain *domain, unsigned int virq, | |
85 | unsigned int nr_irqs, void *args) | |
86 | { | |
87 | struct mbi_range *mbi = NULL; | |
88 | int hwirq, offset, i, err = 0; | |
89 | ||
90 | mutex_lock(&mbi_lock); | |
91 | for (i = 0; i < mbi_range_nr; i++) { | |
92 | offset = bitmap_find_free_region(mbi_ranges[i].bm, | |
93 | mbi_ranges[i].nr_spis, | |
94 | get_count_order(nr_irqs)); | |
95 | if (offset >= 0) { | |
96 | mbi = &mbi_ranges[i]; | |
97 | break; | |
98 | } | |
99 | } | |
100 | mutex_unlock(&mbi_lock); | |
101 | ||
102 | if (!mbi) | |
103 | return -ENOSPC; | |
104 | ||
105 | hwirq = mbi->spi_start + offset; | |
106 | ||
107 | for (i = 0; i < nr_irqs; i++) { | |
108 | err = mbi_irq_gic_domain_alloc(domain, virq + i, hwirq + i); | |
109 | if (err) | |
110 | goto fail; | |
111 | ||
112 | irq_domain_set_hwirq_and_chip(domain, virq + i, hwirq + i, | |
113 | &mbi_irq_chip, mbi); | |
114 | } | |
115 | ||
116 | return 0; | |
117 | ||
118 | fail: | |
119 | irq_domain_free_irqs_parent(domain, virq, nr_irqs); | |
120 | mbi_free_msi(mbi, hwirq, nr_irqs); | |
121 | return err; | |
122 | } | |
123 | ||
124 | static void mbi_irq_domain_free(struct irq_domain *domain, | |
125 | unsigned int virq, unsigned int nr_irqs) | |
126 | { | |
127 | struct irq_data *d = irq_domain_get_irq_data(domain, virq); | |
128 | struct mbi_range *mbi = irq_data_get_irq_chip_data(d); | |
129 | ||
130 | mbi_free_msi(mbi, d->hwirq, nr_irqs); | |
131 | irq_domain_free_irqs_parent(domain, virq, nr_irqs); | |
132 | } | |
133 | ||
134 | static const struct irq_domain_ops mbi_domain_ops = { | |
135 | .alloc = mbi_irq_domain_alloc, | |
136 | .free = mbi_irq_domain_free, | |
137 | }; | |
138 | ||
139 | static void mbi_compose_msi_msg(struct irq_data *data, struct msi_msg *msg) | |
140 | { | |
141 | msg[0].address_hi = upper_32_bits(mbi_phys_base + GICD_SETSPI_NSR); | |
142 | msg[0].address_lo = lower_32_bits(mbi_phys_base + GICD_SETSPI_NSR); | |
143 | msg[0].data = data->parent_data->hwirq; | |
144 | ||
145 | iommu_dma_map_msi_msg(data->irq, msg); | |
146 | } | |
147 | ||
38985351 MZ |
148 | #ifdef CONFIG_PCI_MSI |
149 | /* PCI-specific irqchip */ | |
150 | static void mbi_mask_msi_irq(struct irq_data *d) | |
151 | { | |
152 | pci_msi_mask_irq(d); | |
153 | irq_chip_mask_parent(d); | |
154 | } | |
155 | ||
156 | static void mbi_unmask_msi_irq(struct irq_data *d) | |
157 | { | |
158 | pci_msi_unmask_irq(d); | |
159 | irq_chip_unmask_parent(d); | |
160 | } | |
161 | ||
162 | static struct irq_chip mbi_msi_irq_chip = { | |
163 | .name = "MSI", | |
164 | .irq_mask = mbi_mask_msi_irq, | |
165 | .irq_unmask = mbi_unmask_msi_irq, | |
166 | .irq_eoi = irq_chip_eoi_parent, | |
167 | .irq_compose_msi_msg = mbi_compose_msi_msg, | |
168 | .irq_write_msi_msg = pci_msi_domain_write_msg, | |
169 | }; | |
170 | ||
171 | static struct msi_domain_info mbi_msi_domain_info = { | |
172 | .flags = (MSI_FLAG_USE_DEF_DOM_OPS | MSI_FLAG_USE_DEF_CHIP_OPS | | |
173 | MSI_FLAG_PCI_MSIX | MSI_FLAG_MULTI_PCI_MSI), | |
174 | .chip = &mbi_msi_irq_chip, | |
175 | }; | |
176 | ||
177 | static int mbi_allocate_pci_domain(struct irq_domain *nexus_domain, | |
178 | struct irq_domain **pci_domain) | |
179 | { | |
180 | *pci_domain = pci_msi_create_irq_domain(nexus_domain->parent->fwnode, | |
181 | &mbi_msi_domain_info, | |
182 | nexus_domain); | |
183 | if (!*pci_domain) | |
184 | return -ENOMEM; | |
185 | ||
186 | return 0; | |
187 | } | |
188 | #else | |
189 | static int mbi_allocate_pci_domain(struct irq_domain *nexus_domain, | |
190 | struct irq_domain **pci_domain) | |
191 | { | |
192 | *pci_domain = NULL; | |
193 | return 0; | |
194 | } | |
195 | #endif | |
196 | ||
50528752 MZ |
197 | static void mbi_compose_mbi_msg(struct irq_data *data, struct msi_msg *msg) |
198 | { | |
199 | mbi_compose_msi_msg(data, msg); | |
200 | ||
201 | msg[1].address_hi = upper_32_bits(mbi_phys_base + GICD_CLRSPI_NSR); | |
202 | msg[1].address_lo = lower_32_bits(mbi_phys_base + GICD_CLRSPI_NSR); | |
203 | msg[1].data = data->parent_data->hwirq; | |
204 | ||
205 | iommu_dma_map_msi_msg(data->irq, &msg[1]); | |
206 | } | |
207 | ||
208 | /* Platform-MSI specific irqchip */ | |
209 | static struct irq_chip mbi_pmsi_irq_chip = { | |
210 | .name = "pMSI", | |
211 | .irq_set_type = irq_chip_set_type_parent, | |
212 | .irq_compose_msi_msg = mbi_compose_mbi_msg, | |
213 | .flags = IRQCHIP_SUPPORTS_LEVEL_MSI, | |
214 | }; | |
215 | ||
216 | static struct msi_domain_ops mbi_pmsi_ops = { | |
217 | }; | |
218 | ||
219 | static struct msi_domain_info mbi_pmsi_domain_info = { | |
220 | .flags = (MSI_FLAG_USE_DEF_DOM_OPS | MSI_FLAG_USE_DEF_CHIP_OPS | | |
221 | MSI_FLAG_LEVEL_CAPABLE), | |
222 | .ops = &mbi_pmsi_ops, | |
223 | .chip = &mbi_pmsi_irq_chip, | |
224 | }; | |
225 | ||
226 | static int mbi_allocate_domains(struct irq_domain *parent) | |
227 | { | |
38985351 MZ |
228 | struct irq_domain *nexus_domain, *pci_domain, *plat_domain; |
229 | int err; | |
50528752 MZ |
230 | |
231 | nexus_domain = irq_domain_create_tree(parent->fwnode, | |
232 | &mbi_domain_ops, NULL); | |
233 | if (!nexus_domain) | |
234 | return -ENOMEM; | |
235 | ||
236 | irq_domain_update_bus_token(nexus_domain, DOMAIN_BUS_NEXUS); | |
237 | nexus_domain->parent = parent; | |
238 | ||
38985351 MZ |
239 | err = mbi_allocate_pci_domain(nexus_domain, &pci_domain); |
240 | ||
50528752 MZ |
241 | plat_domain = platform_msi_create_irq_domain(parent->fwnode, |
242 | &mbi_pmsi_domain_info, | |
243 | nexus_domain); | |
244 | ||
38985351 MZ |
245 | if (err || !plat_domain) { |
246 | if (plat_domain) | |
247 | irq_domain_remove(plat_domain); | |
248 | if (pci_domain) | |
249 | irq_domain_remove(pci_domain); | |
50528752 MZ |
250 | irq_domain_remove(nexus_domain); |
251 | return -ENOMEM; | |
252 | } | |
253 | ||
254 | return 0; | |
255 | } | |
256 | ||
257 | int __init mbi_init(struct fwnode_handle *fwnode, struct irq_domain *parent) | |
258 | { | |
259 | struct device_node *np; | |
260 | const __be32 *reg; | |
261 | int ret, n; | |
262 | ||
263 | np = to_of_node(fwnode); | |
264 | ||
265 | if (!of_property_read_bool(np, "msi-controller")) | |
266 | return 0; | |
267 | ||
268 | n = of_property_count_elems_of_size(np, "mbi-ranges", sizeof(u32)); | |
269 | if (n <= 0 || n % 2) | |
270 | return -EINVAL; | |
271 | ||
272 | mbi_range_nr = n / 2; | |
273 | mbi_ranges = kcalloc(mbi_range_nr, sizeof(*mbi_ranges), GFP_KERNEL); | |
274 | if (!mbi_ranges) | |
275 | return -ENOMEM; | |
276 | ||
277 | for (n = 0; n < mbi_range_nr; n++) { | |
278 | ret = of_property_read_u32_index(np, "mbi-ranges", n * 2, | |
279 | &mbi_ranges[n].spi_start); | |
280 | if (ret) | |
281 | goto err_free_mbi; | |
282 | ret = of_property_read_u32_index(np, "mbi-ranges", n * 2 + 1, | |
283 | &mbi_ranges[n].nr_spis); | |
284 | if (ret) | |
285 | goto err_free_mbi; | |
286 | ||
287 | mbi_ranges[n].bm = kcalloc(BITS_TO_LONGS(mbi_ranges[n].nr_spis), | |
288 | sizeof(long), GFP_KERNEL); | |
289 | if (!mbi_ranges[n].bm) { | |
290 | ret = -ENOMEM; | |
291 | goto err_free_mbi; | |
292 | } | |
293 | pr_info("MBI range [%d:%d]\n", mbi_ranges[n].spi_start, | |
294 | mbi_ranges[n].spi_start + mbi_ranges[n].nr_spis - 1); | |
295 | } | |
296 | ||
297 | reg = of_get_property(np, "mbi-alias", NULL); | |
298 | if (reg) { | |
299 | mbi_phys_base = of_translate_address(np, reg); | |
300 | if (mbi_phys_base == OF_BAD_ADDR) { | |
301 | ret = -ENXIO; | |
302 | goto err_free_mbi; | |
303 | } | |
304 | } else { | |
305 | struct resource res; | |
306 | ||
307 | if (of_address_to_resource(np, 0, &res)) { | |
308 | ret = -ENXIO; | |
309 | goto err_free_mbi; | |
310 | } | |
311 | ||
312 | mbi_phys_base = res.start; | |
313 | } | |
314 | ||
315 | pr_info("Using MBI frame %pa\n", &mbi_phys_base); | |
316 | ||
317 | ret = mbi_allocate_domains(parent); | |
318 | if (ret) | |
319 | goto err_free_mbi; | |
320 | ||
321 | return 0; | |
322 | ||
323 | err_free_mbi: | |
324 | if (mbi_ranges) { | |
325 | for (n = 0; n < mbi_range_nr; n++) | |
326 | kfree(mbi_ranges[n].bm); | |
327 | kfree(mbi_ranges); | |
328 | } | |
329 | ||
330 | return ret; | |
331 | } |