xref: /OK3568_Linux_fs/kernel/drivers/irqchip/irq-alpine-msi.c (revision 4882a59341e53eb6f0b4789bf948001014eff981)
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