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/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> |
15 | #include <linux/of_pci.h> |
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 DEFINE_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(fwnode: 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, irq_base: virq, nr_irqs: 1, arg: &fwspec); |
68 | if (err) |
69 | return err; |
70 | |
71 | d = irq_domain_get_irq_data(domain: 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(bitmap: mbi->bm, pos: hwirq - mbi->spi_start, |
80 | order: get_count_order(count: nr_irqs)); |
81 | mutex_unlock(lock: &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 | msi_alloc_info_t *info = args; |
88 | struct mbi_range *mbi = NULL; |
89 | int hwirq, offset, i, err = 0; |
90 | |
91 | mutex_lock(&mbi_lock); |
92 | for (i = 0; i < mbi_range_nr; i++) { |
93 | offset = bitmap_find_free_region(bitmap: mbi_ranges[i].bm, |
94 | bits: mbi_ranges[i].nr_spis, |
95 | order: get_count_order(count: nr_irqs)); |
96 | if (offset >= 0) { |
97 | mbi = &mbi_ranges[i]; |
98 | break; |
99 | } |
100 | } |
101 | mutex_unlock(lock: &mbi_lock); |
102 | |
103 | if (!mbi) |
104 | return -ENOSPC; |
105 | |
106 | hwirq = mbi->spi_start + offset; |
107 | |
108 | err = iommu_dma_prepare_msi(desc: info->desc, |
109 | msi_addr: mbi_phys_base + GICD_SETSPI_NSR); |
110 | if (err) |
111 | return err; |
112 | |
113 | for (i = 0; i < nr_irqs; i++) { |
114 | err = mbi_irq_gic_domain_alloc(domain, virq: virq + i, hwirq: hwirq + i); |
115 | if (err) |
116 | goto fail; |
117 | |
118 | irq_domain_set_hwirq_and_chip(domain, virq: virq + i, hwirq: hwirq + i, |
119 | chip: &mbi_irq_chip, chip_data: mbi); |
120 | } |
121 | |
122 | return 0; |
123 | |
124 | fail: |
125 | irq_domain_free_irqs_parent(domain, irq_base: virq, nr_irqs); |
126 | mbi_free_msi(mbi, hwirq, nr_irqs); |
127 | return err; |
128 | } |
129 | |
130 | static void mbi_irq_domain_free(struct irq_domain *domain, |
131 | unsigned int virq, unsigned int nr_irqs) |
132 | { |
133 | struct irq_data *d = irq_domain_get_irq_data(domain, virq); |
134 | struct mbi_range *mbi = irq_data_get_irq_chip_data(d); |
135 | |
136 | mbi_free_msi(mbi, hwirq: d->hwirq, nr_irqs); |
137 | irq_domain_free_irqs_parent(domain, irq_base: virq, nr_irqs); |
138 | } |
139 | |
140 | static const struct irq_domain_ops mbi_domain_ops = { |
141 | .alloc = mbi_irq_domain_alloc, |
142 | .free = mbi_irq_domain_free, |
143 | }; |
144 | |
145 | static void mbi_compose_msi_msg(struct irq_data *data, struct msi_msg *msg) |
146 | { |
147 | msg[0].address_hi = upper_32_bits(mbi_phys_base + GICD_SETSPI_NSR); |
148 | msg[0].address_lo = lower_32_bits(mbi_phys_base + GICD_SETSPI_NSR); |
149 | msg[0].data = data->parent_data->hwirq; |
150 | |
151 | iommu_dma_compose_msi_msg(desc: irq_data_get_msi_desc(d: data), msg); |
152 | } |
153 | |
154 | #ifdef CONFIG_PCI_MSI |
155 | /* PCI-specific irqchip */ |
156 | static void mbi_mask_msi_irq(struct irq_data *d) |
157 | { |
158 | pci_msi_mask_irq(data: d); |
159 | irq_chip_mask_parent(data: d); |
160 | } |
161 | |
162 | static void mbi_unmask_msi_irq(struct irq_data *d) |
163 | { |
164 | pci_msi_unmask_irq(data: d); |
165 | irq_chip_unmask_parent(data: d); |
166 | } |
167 | |
168 | static struct irq_chip mbi_msi_irq_chip = { |
169 | .name = "MSI" , |
170 | .irq_mask = mbi_mask_msi_irq, |
171 | .irq_unmask = mbi_unmask_msi_irq, |
172 | .irq_eoi = irq_chip_eoi_parent, |
173 | .irq_compose_msi_msg = mbi_compose_msi_msg, |
174 | }; |
175 | |
176 | static struct msi_domain_info mbi_msi_domain_info = { |
177 | .flags = (MSI_FLAG_USE_DEF_DOM_OPS | MSI_FLAG_USE_DEF_CHIP_OPS | |
178 | MSI_FLAG_PCI_MSIX | MSI_FLAG_MULTI_PCI_MSI), |
179 | .chip = &mbi_msi_irq_chip, |
180 | }; |
181 | |
182 | static int mbi_allocate_pci_domain(struct irq_domain *nexus_domain, |
183 | struct irq_domain **pci_domain) |
184 | { |
185 | *pci_domain = pci_msi_create_irq_domain(fwnode: nexus_domain->parent->fwnode, |
186 | info: &mbi_msi_domain_info, |
187 | parent: nexus_domain); |
188 | if (!*pci_domain) |
189 | return -ENOMEM; |
190 | |
191 | return 0; |
192 | } |
193 | #else |
194 | static int mbi_allocate_pci_domain(struct irq_domain *nexus_domain, |
195 | struct irq_domain **pci_domain) |
196 | { |
197 | *pci_domain = NULL; |
198 | return 0; |
199 | } |
200 | #endif |
201 | |
202 | static void mbi_compose_mbi_msg(struct irq_data *data, struct msi_msg *msg) |
203 | { |
204 | mbi_compose_msi_msg(data, msg); |
205 | |
206 | msg[1].address_hi = upper_32_bits(mbi_phys_base + GICD_CLRSPI_NSR); |
207 | msg[1].address_lo = lower_32_bits(mbi_phys_base + GICD_CLRSPI_NSR); |
208 | msg[1].data = data->parent_data->hwirq; |
209 | |
210 | iommu_dma_compose_msi_msg(desc: irq_data_get_msi_desc(d: data), msg: &msg[1]); |
211 | } |
212 | |
213 | /* Platform-MSI specific irqchip */ |
214 | static struct irq_chip mbi_pmsi_irq_chip = { |
215 | .name = "pMSI" , |
216 | .irq_set_type = irq_chip_set_type_parent, |
217 | .irq_compose_msi_msg = mbi_compose_mbi_msg, |
218 | .flags = IRQCHIP_SUPPORTS_LEVEL_MSI, |
219 | }; |
220 | |
221 | static struct msi_domain_ops mbi_pmsi_ops = { |
222 | }; |
223 | |
224 | static struct msi_domain_info mbi_pmsi_domain_info = { |
225 | .flags = (MSI_FLAG_USE_DEF_DOM_OPS | MSI_FLAG_USE_DEF_CHIP_OPS | |
226 | MSI_FLAG_LEVEL_CAPABLE), |
227 | .ops = &mbi_pmsi_ops, |
228 | .chip = &mbi_pmsi_irq_chip, |
229 | }; |
230 | |
231 | static int mbi_allocate_domains(struct irq_domain *parent) |
232 | { |
233 | struct irq_domain *nexus_domain, *pci_domain, *plat_domain; |
234 | int err; |
235 | |
236 | nexus_domain = irq_domain_create_hierarchy(parent, flags: 0, size: 0, fwnode: parent->fwnode, |
237 | ops: &mbi_domain_ops, NULL); |
238 | if (!nexus_domain) |
239 | return -ENOMEM; |
240 | |
241 | irq_domain_update_bus_token(domain: nexus_domain, bus_token: DOMAIN_BUS_NEXUS); |
242 | |
243 | err = mbi_allocate_pci_domain(nexus_domain, pci_domain: &pci_domain); |
244 | |
245 | plat_domain = platform_msi_create_irq_domain(fwnode: parent->fwnode, |
246 | info: &mbi_pmsi_domain_info, |
247 | parent: nexus_domain); |
248 | |
249 | if (err || !plat_domain) { |
250 | if (plat_domain) |
251 | irq_domain_remove(host: plat_domain); |
252 | if (pci_domain) |
253 | irq_domain_remove(host: pci_domain); |
254 | irq_domain_remove(host: nexus_domain); |
255 | return -ENOMEM; |
256 | } |
257 | |
258 | return 0; |
259 | } |
260 | |
261 | int __init mbi_init(struct fwnode_handle *fwnode, struct irq_domain *parent) |
262 | { |
263 | struct device_node *np; |
264 | const __be32 *reg; |
265 | int ret, n; |
266 | |
267 | np = to_of_node(fwnode); |
268 | |
269 | if (!of_property_read_bool(np, propname: "msi-controller" )) |
270 | return 0; |
271 | |
272 | n = of_property_count_elems_of_size(np, propname: "mbi-ranges" , elem_size: sizeof(u32)); |
273 | if (n <= 0 || n % 2) |
274 | return -EINVAL; |
275 | |
276 | mbi_range_nr = n / 2; |
277 | mbi_ranges = kcalloc(n: mbi_range_nr, size: sizeof(*mbi_ranges), GFP_KERNEL); |
278 | if (!mbi_ranges) |
279 | return -ENOMEM; |
280 | |
281 | for (n = 0; n < mbi_range_nr; n++) { |
282 | ret = of_property_read_u32_index(np, propname: "mbi-ranges" , index: n * 2, |
283 | out_value: &mbi_ranges[n].spi_start); |
284 | if (ret) |
285 | goto err_free_mbi; |
286 | ret = of_property_read_u32_index(np, propname: "mbi-ranges" , index: n * 2 + 1, |
287 | out_value: &mbi_ranges[n].nr_spis); |
288 | if (ret) |
289 | goto err_free_mbi; |
290 | |
291 | mbi_ranges[n].bm = bitmap_zalloc(nbits: mbi_ranges[n].nr_spis, GFP_KERNEL); |
292 | if (!mbi_ranges[n].bm) { |
293 | ret = -ENOMEM; |
294 | goto err_free_mbi; |
295 | } |
296 | pr_info("MBI range [%d:%d]\n" , mbi_ranges[n].spi_start, |
297 | mbi_ranges[n].spi_start + mbi_ranges[n].nr_spis - 1); |
298 | } |
299 | |
300 | reg = of_get_property(node: np, name: "mbi-alias" , NULL); |
301 | if (reg) { |
302 | mbi_phys_base = of_translate_address(np, addr: reg); |
303 | if (mbi_phys_base == (phys_addr_t)OF_BAD_ADDR) { |
304 | ret = -ENXIO; |
305 | goto err_free_mbi; |
306 | } |
307 | } else { |
308 | struct resource res; |
309 | |
310 | if (of_address_to_resource(dev: np, index: 0, r: &res)) { |
311 | ret = -ENXIO; |
312 | goto err_free_mbi; |
313 | } |
314 | |
315 | mbi_phys_base = res.start; |
316 | } |
317 | |
318 | pr_info("Using MBI frame %pa\n" , &mbi_phys_base); |
319 | |
320 | ret = mbi_allocate_domains(parent); |
321 | if (ret) |
322 | goto err_free_mbi; |
323 | |
324 | return 0; |
325 | |
326 | err_free_mbi: |
327 | if (mbi_ranges) { |
328 | for (n = 0; n < mbi_range_nr; n++) |
329 | bitmap_free(bitmap: mbi_ranges[n].bm); |
330 | kfree(objp: mbi_ranges); |
331 | } |
332 | |
333 | return ret; |
334 | } |
335 | |