1*4882a593Smuzhiyun /*
2*4882a593Smuzhiyun * Annapurna Labs MSIX support services
3*4882a593Smuzhiyun *
4*4882a593Smuzhiyun * Copyright (C) 2016, Amazon.com, Inc. or its affiliates. All Rights Reserved.
5*4882a593Smuzhiyun *
6*4882a593Smuzhiyun * Antoine Tenart <antoine.tenart@free-electrons.com>
7*4882a593Smuzhiyun *
8*4882a593Smuzhiyun * This file is licensed under the terms of the GNU General Public
9*4882a593Smuzhiyun * License version 2. This program is licensed "as is" without any
10*4882a593Smuzhiyun * warranty of any kind, whether express or implied.
11*4882a593Smuzhiyun */
12*4882a593Smuzhiyun
13*4882a593Smuzhiyun #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
14*4882a593Smuzhiyun
15*4882a593Smuzhiyun #include <linux/irqchip.h>
16*4882a593Smuzhiyun #include <linux/irqchip/arm-gic.h>
17*4882a593Smuzhiyun #include <linux/msi.h>
18*4882a593Smuzhiyun #include <linux/of.h>
19*4882a593Smuzhiyun #include <linux/of_address.h>
20*4882a593Smuzhiyun #include <linux/of_irq.h>
21*4882a593Smuzhiyun #include <linux/of_pci.h>
22*4882a593Smuzhiyun #include <linux/pci.h>
23*4882a593Smuzhiyun #include <linux/slab.h>
24*4882a593Smuzhiyun
25*4882a593Smuzhiyun #include <asm/irq.h>
26*4882a593Smuzhiyun #include <asm/msi.h>
27*4882a593Smuzhiyun
28*4882a593Smuzhiyun /* MSIX message address format: local GIC target */
29*4882a593Smuzhiyun #define ALPINE_MSIX_SPI_TARGET_CLUSTER0 BIT(16)
30*4882a593Smuzhiyun
31*4882a593Smuzhiyun struct alpine_msix_data {
32*4882a593Smuzhiyun spinlock_t msi_map_lock;
33*4882a593Smuzhiyun phys_addr_t addr;
34*4882a593Smuzhiyun u32 spi_first; /* The SGI number that MSIs start */
35*4882a593Smuzhiyun u32 num_spis; /* The number of SGIs for MSIs */
36*4882a593Smuzhiyun unsigned long *msi_map;
37*4882a593Smuzhiyun };
38*4882a593Smuzhiyun
alpine_msix_mask_msi_irq(struct irq_data * d)39*4882a593Smuzhiyun static void alpine_msix_mask_msi_irq(struct irq_data *d)
40*4882a593Smuzhiyun {
41*4882a593Smuzhiyun pci_msi_mask_irq(d);
42*4882a593Smuzhiyun irq_chip_mask_parent(d);
43*4882a593Smuzhiyun }
44*4882a593Smuzhiyun
alpine_msix_unmask_msi_irq(struct irq_data * d)45*4882a593Smuzhiyun static void alpine_msix_unmask_msi_irq(struct irq_data *d)
46*4882a593Smuzhiyun {
47*4882a593Smuzhiyun pci_msi_unmask_irq(d);
48*4882a593Smuzhiyun irq_chip_unmask_parent(d);
49*4882a593Smuzhiyun }
50*4882a593Smuzhiyun
51*4882a593Smuzhiyun static struct irq_chip alpine_msix_irq_chip = {
52*4882a593Smuzhiyun .name = "MSIx",
53*4882a593Smuzhiyun .irq_mask = alpine_msix_mask_msi_irq,
54*4882a593Smuzhiyun .irq_unmask = alpine_msix_unmask_msi_irq,
55*4882a593Smuzhiyun .irq_eoi = irq_chip_eoi_parent,
56*4882a593Smuzhiyun .irq_set_affinity = irq_chip_set_affinity_parent,
57*4882a593Smuzhiyun };
58*4882a593Smuzhiyun
alpine_msix_allocate_sgi(struct alpine_msix_data * priv,int num_req)59*4882a593Smuzhiyun static int alpine_msix_allocate_sgi(struct alpine_msix_data *priv, int num_req)
60*4882a593Smuzhiyun {
61*4882a593Smuzhiyun int first;
62*4882a593Smuzhiyun
63*4882a593Smuzhiyun spin_lock(&priv->msi_map_lock);
64*4882a593Smuzhiyun
65*4882a593Smuzhiyun first = bitmap_find_next_zero_area(priv->msi_map, priv->num_spis, 0,
66*4882a593Smuzhiyun num_req, 0);
67*4882a593Smuzhiyun if (first >= priv->num_spis) {
68*4882a593Smuzhiyun spin_unlock(&priv->msi_map_lock);
69*4882a593Smuzhiyun return -ENOSPC;
70*4882a593Smuzhiyun }
71*4882a593Smuzhiyun
72*4882a593Smuzhiyun bitmap_set(priv->msi_map, first, num_req);
73*4882a593Smuzhiyun
74*4882a593Smuzhiyun spin_unlock(&priv->msi_map_lock);
75*4882a593Smuzhiyun
76*4882a593Smuzhiyun return priv->spi_first + first;
77*4882a593Smuzhiyun }
78*4882a593Smuzhiyun
alpine_msix_free_sgi(struct alpine_msix_data * priv,unsigned sgi,int num_req)79*4882a593Smuzhiyun static void alpine_msix_free_sgi(struct alpine_msix_data *priv, unsigned sgi,
80*4882a593Smuzhiyun int num_req)
81*4882a593Smuzhiyun {
82*4882a593Smuzhiyun int first = sgi - priv->spi_first;
83*4882a593Smuzhiyun
84*4882a593Smuzhiyun spin_lock(&priv->msi_map_lock);
85*4882a593Smuzhiyun
86*4882a593Smuzhiyun bitmap_clear(priv->msi_map, first, num_req);
87*4882a593Smuzhiyun
88*4882a593Smuzhiyun spin_unlock(&priv->msi_map_lock);
89*4882a593Smuzhiyun }
90*4882a593Smuzhiyun
alpine_msix_compose_msi_msg(struct irq_data * data,struct msi_msg * msg)91*4882a593Smuzhiyun static void alpine_msix_compose_msi_msg(struct irq_data *data,
92*4882a593Smuzhiyun struct msi_msg *msg)
93*4882a593Smuzhiyun {
94*4882a593Smuzhiyun struct alpine_msix_data *priv = irq_data_get_irq_chip_data(data);
95*4882a593Smuzhiyun phys_addr_t msg_addr = priv->addr;
96*4882a593Smuzhiyun
97*4882a593Smuzhiyun msg_addr |= (data->hwirq << 3);
98*4882a593Smuzhiyun
99*4882a593Smuzhiyun msg->address_hi = upper_32_bits(msg_addr);
100*4882a593Smuzhiyun msg->address_lo = lower_32_bits(msg_addr);
101*4882a593Smuzhiyun msg->data = 0;
102*4882a593Smuzhiyun }
103*4882a593Smuzhiyun
104*4882a593Smuzhiyun static struct msi_domain_info alpine_msix_domain_info = {
105*4882a593Smuzhiyun .flags = MSI_FLAG_USE_DEF_DOM_OPS | MSI_FLAG_USE_DEF_CHIP_OPS |
106*4882a593Smuzhiyun MSI_FLAG_PCI_MSIX,
107*4882a593Smuzhiyun .chip = &alpine_msix_irq_chip,
108*4882a593Smuzhiyun };
109*4882a593Smuzhiyun
110*4882a593Smuzhiyun static struct irq_chip middle_irq_chip = {
111*4882a593Smuzhiyun .name = "alpine_msix_middle",
112*4882a593Smuzhiyun .irq_mask = irq_chip_mask_parent,
113*4882a593Smuzhiyun .irq_unmask = irq_chip_unmask_parent,
114*4882a593Smuzhiyun .irq_eoi = irq_chip_eoi_parent,
115*4882a593Smuzhiyun .irq_set_affinity = irq_chip_set_affinity_parent,
116*4882a593Smuzhiyun .irq_compose_msi_msg = alpine_msix_compose_msi_msg,
117*4882a593Smuzhiyun };
118*4882a593Smuzhiyun
alpine_msix_gic_domain_alloc(struct irq_domain * domain,unsigned int virq,int sgi)119*4882a593Smuzhiyun static int alpine_msix_gic_domain_alloc(struct irq_domain *domain,
120*4882a593Smuzhiyun unsigned int virq, int sgi)
121*4882a593Smuzhiyun {
122*4882a593Smuzhiyun struct irq_fwspec fwspec;
123*4882a593Smuzhiyun struct irq_data *d;
124*4882a593Smuzhiyun int ret;
125*4882a593Smuzhiyun
126*4882a593Smuzhiyun if (!is_of_node(domain->parent->fwnode))
127*4882a593Smuzhiyun return -EINVAL;
128*4882a593Smuzhiyun
129*4882a593Smuzhiyun fwspec.fwnode = domain->parent->fwnode;
130*4882a593Smuzhiyun fwspec.param_count = 3;
131*4882a593Smuzhiyun fwspec.param[0] = 0;
132*4882a593Smuzhiyun fwspec.param[1] = sgi;
133*4882a593Smuzhiyun fwspec.param[2] = IRQ_TYPE_EDGE_RISING;
134*4882a593Smuzhiyun
135*4882a593Smuzhiyun ret = irq_domain_alloc_irqs_parent(domain, virq, 1, &fwspec);
136*4882a593Smuzhiyun if (ret)
137*4882a593Smuzhiyun return ret;
138*4882a593Smuzhiyun
139*4882a593Smuzhiyun d = irq_domain_get_irq_data(domain->parent, virq);
140*4882a593Smuzhiyun d->chip->irq_set_type(d, IRQ_TYPE_EDGE_RISING);
141*4882a593Smuzhiyun
142*4882a593Smuzhiyun return 0;
143*4882a593Smuzhiyun }
144*4882a593Smuzhiyun
alpine_msix_middle_domain_alloc(struct irq_domain * domain,unsigned int virq,unsigned int nr_irqs,void * args)145*4882a593Smuzhiyun static int alpine_msix_middle_domain_alloc(struct irq_domain *domain,
146*4882a593Smuzhiyun unsigned int virq,
147*4882a593Smuzhiyun unsigned int nr_irqs, void *args)
148*4882a593Smuzhiyun {
149*4882a593Smuzhiyun struct alpine_msix_data *priv = domain->host_data;
150*4882a593Smuzhiyun int sgi, err, i;
151*4882a593Smuzhiyun
152*4882a593Smuzhiyun sgi = alpine_msix_allocate_sgi(priv, nr_irqs);
153*4882a593Smuzhiyun if (sgi < 0)
154*4882a593Smuzhiyun return sgi;
155*4882a593Smuzhiyun
156*4882a593Smuzhiyun for (i = 0; i < nr_irqs; i++) {
157*4882a593Smuzhiyun err = alpine_msix_gic_domain_alloc(domain, virq + i, sgi + i);
158*4882a593Smuzhiyun if (err)
159*4882a593Smuzhiyun goto err_sgi;
160*4882a593Smuzhiyun
161*4882a593Smuzhiyun irq_domain_set_hwirq_and_chip(domain, virq + i, sgi + i,
162*4882a593Smuzhiyun &middle_irq_chip, priv);
163*4882a593Smuzhiyun }
164*4882a593Smuzhiyun
165*4882a593Smuzhiyun return 0;
166*4882a593Smuzhiyun
167*4882a593Smuzhiyun err_sgi:
168*4882a593Smuzhiyun irq_domain_free_irqs_parent(domain, virq, i - 1);
169*4882a593Smuzhiyun alpine_msix_free_sgi(priv, sgi, nr_irqs);
170*4882a593Smuzhiyun return err;
171*4882a593Smuzhiyun }
172*4882a593Smuzhiyun
alpine_msix_middle_domain_free(struct irq_domain * domain,unsigned int virq,unsigned int nr_irqs)173*4882a593Smuzhiyun static void alpine_msix_middle_domain_free(struct irq_domain *domain,
174*4882a593Smuzhiyun unsigned int virq,
175*4882a593Smuzhiyun unsigned int nr_irqs)
176*4882a593Smuzhiyun {
177*4882a593Smuzhiyun struct irq_data *d = irq_domain_get_irq_data(domain, virq);
178*4882a593Smuzhiyun struct alpine_msix_data *priv = irq_data_get_irq_chip_data(d);
179*4882a593Smuzhiyun
180*4882a593Smuzhiyun irq_domain_free_irqs_parent(domain, virq, nr_irqs);
181*4882a593Smuzhiyun alpine_msix_free_sgi(priv, d->hwirq, nr_irqs);
182*4882a593Smuzhiyun }
183*4882a593Smuzhiyun
184*4882a593Smuzhiyun static const struct irq_domain_ops alpine_msix_middle_domain_ops = {
185*4882a593Smuzhiyun .alloc = alpine_msix_middle_domain_alloc,
186*4882a593Smuzhiyun .free = alpine_msix_middle_domain_free,
187*4882a593Smuzhiyun };
188*4882a593Smuzhiyun
alpine_msix_init_domains(struct alpine_msix_data * priv,struct device_node * node)189*4882a593Smuzhiyun static int alpine_msix_init_domains(struct alpine_msix_data *priv,
190*4882a593Smuzhiyun struct device_node *node)
191*4882a593Smuzhiyun {
192*4882a593Smuzhiyun struct irq_domain *middle_domain, *msi_domain, *gic_domain;
193*4882a593Smuzhiyun struct device_node *gic_node;
194*4882a593Smuzhiyun
195*4882a593Smuzhiyun gic_node = of_irq_find_parent(node);
196*4882a593Smuzhiyun if (!gic_node) {
197*4882a593Smuzhiyun pr_err("Failed to find the GIC node\n");
198*4882a593Smuzhiyun return -ENODEV;
199*4882a593Smuzhiyun }
200*4882a593Smuzhiyun
201*4882a593Smuzhiyun gic_domain = irq_find_host(gic_node);
202*4882a593Smuzhiyun if (!gic_domain) {
203*4882a593Smuzhiyun pr_err("Failed to find the GIC domain\n");
204*4882a593Smuzhiyun return -ENXIO;
205*4882a593Smuzhiyun }
206*4882a593Smuzhiyun
207*4882a593Smuzhiyun middle_domain = irq_domain_add_tree(NULL,
208*4882a593Smuzhiyun &alpine_msix_middle_domain_ops,
209*4882a593Smuzhiyun priv);
210*4882a593Smuzhiyun if (!middle_domain) {
211*4882a593Smuzhiyun pr_err("Failed to create the MSIX middle domain\n");
212*4882a593Smuzhiyun return -ENOMEM;
213*4882a593Smuzhiyun }
214*4882a593Smuzhiyun
215*4882a593Smuzhiyun middle_domain->parent = gic_domain;
216*4882a593Smuzhiyun
217*4882a593Smuzhiyun msi_domain = pci_msi_create_irq_domain(of_node_to_fwnode(node),
218*4882a593Smuzhiyun &alpine_msix_domain_info,
219*4882a593Smuzhiyun middle_domain);
220*4882a593Smuzhiyun if (!msi_domain) {
221*4882a593Smuzhiyun pr_err("Failed to create MSI domain\n");
222*4882a593Smuzhiyun irq_domain_remove(middle_domain);
223*4882a593Smuzhiyun return -ENOMEM;
224*4882a593Smuzhiyun }
225*4882a593Smuzhiyun
226*4882a593Smuzhiyun return 0;
227*4882a593Smuzhiyun }
228*4882a593Smuzhiyun
alpine_msix_init(struct device_node * node,struct device_node * parent)229*4882a593Smuzhiyun static int alpine_msix_init(struct device_node *node,
230*4882a593Smuzhiyun struct device_node *parent)
231*4882a593Smuzhiyun {
232*4882a593Smuzhiyun struct alpine_msix_data *priv;
233*4882a593Smuzhiyun struct resource res;
234*4882a593Smuzhiyun int ret;
235*4882a593Smuzhiyun
236*4882a593Smuzhiyun priv = kzalloc(sizeof(*priv), GFP_KERNEL);
237*4882a593Smuzhiyun if (!priv)
238*4882a593Smuzhiyun return -ENOMEM;
239*4882a593Smuzhiyun
240*4882a593Smuzhiyun spin_lock_init(&priv->msi_map_lock);
241*4882a593Smuzhiyun
242*4882a593Smuzhiyun ret = of_address_to_resource(node, 0, &res);
243*4882a593Smuzhiyun if (ret) {
244*4882a593Smuzhiyun pr_err("Failed to allocate resource\n");
245*4882a593Smuzhiyun goto err_priv;
246*4882a593Smuzhiyun }
247*4882a593Smuzhiyun
248*4882a593Smuzhiyun /*
249*4882a593Smuzhiyun * The 20 least significant bits of addr provide direct information
250*4882a593Smuzhiyun * regarding the interrupt destination.
251*4882a593Smuzhiyun *
252*4882a593Smuzhiyun * To select the primary GIC as the target GIC, bits [18:17] must be set
253*4882a593Smuzhiyun * to 0x0. In this case, bit 16 (SPI_TARGET_CLUSTER0) must be set.
254*4882a593Smuzhiyun */
255*4882a593Smuzhiyun priv->addr = res.start & GENMASK_ULL(63,20);
256*4882a593Smuzhiyun priv->addr |= ALPINE_MSIX_SPI_TARGET_CLUSTER0;
257*4882a593Smuzhiyun
258*4882a593Smuzhiyun if (of_property_read_u32(node, "al,msi-base-spi", &priv->spi_first)) {
259*4882a593Smuzhiyun pr_err("Unable to parse MSI base\n");
260*4882a593Smuzhiyun ret = -EINVAL;
261*4882a593Smuzhiyun goto err_priv;
262*4882a593Smuzhiyun }
263*4882a593Smuzhiyun
264*4882a593Smuzhiyun if (of_property_read_u32(node, "al,msi-num-spis", &priv->num_spis)) {
265*4882a593Smuzhiyun pr_err("Unable to parse MSI numbers\n");
266*4882a593Smuzhiyun ret = -EINVAL;
267*4882a593Smuzhiyun goto err_priv;
268*4882a593Smuzhiyun }
269*4882a593Smuzhiyun
270*4882a593Smuzhiyun priv->msi_map = kcalloc(BITS_TO_LONGS(priv->num_spis),
271*4882a593Smuzhiyun sizeof(*priv->msi_map),
272*4882a593Smuzhiyun GFP_KERNEL);
273*4882a593Smuzhiyun if (!priv->msi_map) {
274*4882a593Smuzhiyun ret = -ENOMEM;
275*4882a593Smuzhiyun goto err_priv;
276*4882a593Smuzhiyun }
277*4882a593Smuzhiyun
278*4882a593Smuzhiyun pr_debug("Registering %d msixs, starting at %d\n",
279*4882a593Smuzhiyun priv->num_spis, priv->spi_first);
280*4882a593Smuzhiyun
281*4882a593Smuzhiyun ret = alpine_msix_init_domains(priv, node);
282*4882a593Smuzhiyun if (ret)
283*4882a593Smuzhiyun goto err_map;
284*4882a593Smuzhiyun
285*4882a593Smuzhiyun return 0;
286*4882a593Smuzhiyun
287*4882a593Smuzhiyun err_map:
288*4882a593Smuzhiyun kfree(priv->msi_map);
289*4882a593Smuzhiyun err_priv:
290*4882a593Smuzhiyun kfree(priv);
291*4882a593Smuzhiyun return ret;
292*4882a593Smuzhiyun }
293*4882a593Smuzhiyun IRQCHIP_DECLARE(alpine_msix, "al,alpine-msix", alpine_msix_init);
294