xref: /OK3568_Linux_fs/kernel/drivers/irqchip/irq-mvebu-gicp.c (revision 4882a59341e53eb6f0b4789bf948001014eff981)
1*4882a593Smuzhiyun /*
2*4882a593Smuzhiyun  * Copyright (C) 2017 Marvell
3*4882a593Smuzhiyun  *
4*4882a593Smuzhiyun  * Thomas Petazzoni <thomas.petazzoni@free-electrons.com>
5*4882a593Smuzhiyun  *
6*4882a593Smuzhiyun  * This file is licensed under the terms of the GNU General Public
7*4882a593Smuzhiyun  * License version 2. This program is licensed "as is" without any
8*4882a593Smuzhiyun  * warranty of any kind, whether express or implied.
9*4882a593Smuzhiyun  */
10*4882a593Smuzhiyun 
11*4882a593Smuzhiyun #include <linux/io.h>
12*4882a593Smuzhiyun #include <linux/irq.h>
13*4882a593Smuzhiyun #include <linux/irqdomain.h>
14*4882a593Smuzhiyun #include <linux/msi.h>
15*4882a593Smuzhiyun #include <linux/of.h>
16*4882a593Smuzhiyun #include <linux/of_irq.h>
17*4882a593Smuzhiyun #include <linux/of_platform.h>
18*4882a593Smuzhiyun #include <linux/platform_device.h>
19*4882a593Smuzhiyun 
20*4882a593Smuzhiyun #include <dt-bindings/interrupt-controller/arm-gic.h>
21*4882a593Smuzhiyun 
22*4882a593Smuzhiyun #define GICP_SETSPI_NSR_OFFSET	0x0
23*4882a593Smuzhiyun #define GICP_CLRSPI_NSR_OFFSET	0x8
24*4882a593Smuzhiyun 
25*4882a593Smuzhiyun struct mvebu_gicp_spi_range {
26*4882a593Smuzhiyun 	unsigned int start;
27*4882a593Smuzhiyun 	unsigned int count;
28*4882a593Smuzhiyun };
29*4882a593Smuzhiyun 
30*4882a593Smuzhiyun struct mvebu_gicp {
31*4882a593Smuzhiyun 	struct mvebu_gicp_spi_range *spi_ranges;
32*4882a593Smuzhiyun 	unsigned int spi_ranges_cnt;
33*4882a593Smuzhiyun 	unsigned int spi_cnt;
34*4882a593Smuzhiyun 	unsigned long *spi_bitmap;
35*4882a593Smuzhiyun 	spinlock_t spi_lock;
36*4882a593Smuzhiyun 	struct resource *res;
37*4882a593Smuzhiyun 	struct device *dev;
38*4882a593Smuzhiyun };
39*4882a593Smuzhiyun 
gicp_idx_to_spi(struct mvebu_gicp * gicp,int idx)40*4882a593Smuzhiyun static int gicp_idx_to_spi(struct mvebu_gicp *gicp, int idx)
41*4882a593Smuzhiyun {
42*4882a593Smuzhiyun 	int i;
43*4882a593Smuzhiyun 
44*4882a593Smuzhiyun 	for (i = 0; i < gicp->spi_ranges_cnt; i++) {
45*4882a593Smuzhiyun 		struct mvebu_gicp_spi_range *r = &gicp->spi_ranges[i];
46*4882a593Smuzhiyun 
47*4882a593Smuzhiyun 		if (idx < r->count)
48*4882a593Smuzhiyun 			return r->start + idx;
49*4882a593Smuzhiyun 
50*4882a593Smuzhiyun 		idx -= r->count;
51*4882a593Smuzhiyun 	}
52*4882a593Smuzhiyun 
53*4882a593Smuzhiyun 	return -EINVAL;
54*4882a593Smuzhiyun }
55*4882a593Smuzhiyun 
gicp_compose_msi_msg(struct irq_data * data,struct msi_msg * msg)56*4882a593Smuzhiyun static void gicp_compose_msi_msg(struct irq_data *data, struct msi_msg *msg)
57*4882a593Smuzhiyun {
58*4882a593Smuzhiyun 	struct mvebu_gicp *gicp = data->chip_data;
59*4882a593Smuzhiyun 	phys_addr_t setspi = gicp->res->start + GICP_SETSPI_NSR_OFFSET;
60*4882a593Smuzhiyun 	phys_addr_t clrspi = gicp->res->start + GICP_CLRSPI_NSR_OFFSET;
61*4882a593Smuzhiyun 
62*4882a593Smuzhiyun 	msg[0].data = data->hwirq;
63*4882a593Smuzhiyun 	msg[0].address_lo = lower_32_bits(setspi);
64*4882a593Smuzhiyun 	msg[0].address_hi = upper_32_bits(setspi);
65*4882a593Smuzhiyun 	msg[1].data = data->hwirq;
66*4882a593Smuzhiyun 	msg[1].address_lo = lower_32_bits(clrspi);
67*4882a593Smuzhiyun 	msg[1].address_hi = upper_32_bits(clrspi);
68*4882a593Smuzhiyun }
69*4882a593Smuzhiyun 
70*4882a593Smuzhiyun static struct irq_chip gicp_irq_chip = {
71*4882a593Smuzhiyun 	.name			= "GICP",
72*4882a593Smuzhiyun 	.irq_mask		= irq_chip_mask_parent,
73*4882a593Smuzhiyun 	.irq_unmask		= irq_chip_unmask_parent,
74*4882a593Smuzhiyun 	.irq_eoi		= irq_chip_eoi_parent,
75*4882a593Smuzhiyun 	.irq_set_affinity	= irq_chip_set_affinity_parent,
76*4882a593Smuzhiyun 	.irq_set_type		= irq_chip_set_type_parent,
77*4882a593Smuzhiyun 	.irq_compose_msi_msg	= gicp_compose_msi_msg,
78*4882a593Smuzhiyun };
79*4882a593Smuzhiyun 
gicp_irq_domain_alloc(struct irq_domain * domain,unsigned int virq,unsigned int nr_irqs,void * args)80*4882a593Smuzhiyun static int gicp_irq_domain_alloc(struct irq_domain *domain, unsigned int virq,
81*4882a593Smuzhiyun 				 unsigned int nr_irqs, void *args)
82*4882a593Smuzhiyun {
83*4882a593Smuzhiyun 	struct mvebu_gicp *gicp = domain->host_data;
84*4882a593Smuzhiyun 	struct irq_fwspec fwspec;
85*4882a593Smuzhiyun 	unsigned int hwirq;
86*4882a593Smuzhiyun 	int ret;
87*4882a593Smuzhiyun 
88*4882a593Smuzhiyun 	spin_lock(&gicp->spi_lock);
89*4882a593Smuzhiyun 	hwirq = find_first_zero_bit(gicp->spi_bitmap, gicp->spi_cnt);
90*4882a593Smuzhiyun 	if (hwirq == gicp->spi_cnt) {
91*4882a593Smuzhiyun 		spin_unlock(&gicp->spi_lock);
92*4882a593Smuzhiyun 		return -ENOSPC;
93*4882a593Smuzhiyun 	}
94*4882a593Smuzhiyun 	__set_bit(hwirq, gicp->spi_bitmap);
95*4882a593Smuzhiyun 	spin_unlock(&gicp->spi_lock);
96*4882a593Smuzhiyun 
97*4882a593Smuzhiyun 	fwspec.fwnode = domain->parent->fwnode;
98*4882a593Smuzhiyun 	fwspec.param_count = 3;
99*4882a593Smuzhiyun 	fwspec.param[0] = GIC_SPI;
100*4882a593Smuzhiyun 	fwspec.param[1] = gicp_idx_to_spi(gicp, hwirq) - 32;
101*4882a593Smuzhiyun 	/*
102*4882a593Smuzhiyun 	 * Assume edge rising for now, it will be properly set when
103*4882a593Smuzhiyun 	 * ->set_type() is called
104*4882a593Smuzhiyun 	 */
105*4882a593Smuzhiyun 	fwspec.param[2] = IRQ_TYPE_EDGE_RISING;
106*4882a593Smuzhiyun 
107*4882a593Smuzhiyun 	ret = irq_domain_alloc_irqs_parent(domain, virq, 1, &fwspec);
108*4882a593Smuzhiyun 	if (ret) {
109*4882a593Smuzhiyun 		dev_err(gicp->dev, "Cannot allocate parent IRQ\n");
110*4882a593Smuzhiyun 		goto free_hwirq;
111*4882a593Smuzhiyun 	}
112*4882a593Smuzhiyun 
113*4882a593Smuzhiyun 	ret = irq_domain_set_hwirq_and_chip(domain, virq, hwirq,
114*4882a593Smuzhiyun 					    &gicp_irq_chip, gicp);
115*4882a593Smuzhiyun 	if (ret)
116*4882a593Smuzhiyun 		goto free_irqs_parent;
117*4882a593Smuzhiyun 
118*4882a593Smuzhiyun 	return 0;
119*4882a593Smuzhiyun 
120*4882a593Smuzhiyun free_irqs_parent:
121*4882a593Smuzhiyun 	irq_domain_free_irqs_parent(domain, virq, nr_irqs);
122*4882a593Smuzhiyun free_hwirq:
123*4882a593Smuzhiyun 	spin_lock(&gicp->spi_lock);
124*4882a593Smuzhiyun 	__clear_bit(hwirq, gicp->spi_bitmap);
125*4882a593Smuzhiyun 	spin_unlock(&gicp->spi_lock);
126*4882a593Smuzhiyun 	return ret;
127*4882a593Smuzhiyun }
128*4882a593Smuzhiyun 
gicp_irq_domain_free(struct irq_domain * domain,unsigned int virq,unsigned int nr_irqs)129*4882a593Smuzhiyun static void gicp_irq_domain_free(struct irq_domain *domain,
130*4882a593Smuzhiyun 				 unsigned int virq, unsigned int nr_irqs)
131*4882a593Smuzhiyun {
132*4882a593Smuzhiyun 	struct mvebu_gicp *gicp = domain->host_data;
133*4882a593Smuzhiyun 	struct irq_data *d = irq_domain_get_irq_data(domain, virq);
134*4882a593Smuzhiyun 
135*4882a593Smuzhiyun 	if (d->hwirq >= gicp->spi_cnt) {
136*4882a593Smuzhiyun 		dev_err(gicp->dev, "Invalid hwirq %lu\n", d->hwirq);
137*4882a593Smuzhiyun 		return;
138*4882a593Smuzhiyun 	}
139*4882a593Smuzhiyun 
140*4882a593Smuzhiyun 	irq_domain_free_irqs_parent(domain, virq, nr_irqs);
141*4882a593Smuzhiyun 
142*4882a593Smuzhiyun 	spin_lock(&gicp->spi_lock);
143*4882a593Smuzhiyun 	__clear_bit(d->hwirq, gicp->spi_bitmap);
144*4882a593Smuzhiyun 	spin_unlock(&gicp->spi_lock);
145*4882a593Smuzhiyun }
146*4882a593Smuzhiyun 
147*4882a593Smuzhiyun static const struct irq_domain_ops gicp_domain_ops = {
148*4882a593Smuzhiyun 	.alloc	= gicp_irq_domain_alloc,
149*4882a593Smuzhiyun 	.free	= gicp_irq_domain_free,
150*4882a593Smuzhiyun };
151*4882a593Smuzhiyun 
152*4882a593Smuzhiyun static struct irq_chip gicp_msi_irq_chip = {
153*4882a593Smuzhiyun 	.name		= "GICP",
154*4882a593Smuzhiyun 	.irq_set_type	= irq_chip_set_type_parent,
155*4882a593Smuzhiyun 	.flags		= IRQCHIP_SUPPORTS_LEVEL_MSI,
156*4882a593Smuzhiyun };
157*4882a593Smuzhiyun 
158*4882a593Smuzhiyun static struct msi_domain_ops gicp_msi_ops = {
159*4882a593Smuzhiyun };
160*4882a593Smuzhiyun 
161*4882a593Smuzhiyun static struct msi_domain_info gicp_msi_domain_info = {
162*4882a593Smuzhiyun 	.flags	= (MSI_FLAG_USE_DEF_DOM_OPS | MSI_FLAG_USE_DEF_CHIP_OPS |
163*4882a593Smuzhiyun 		   MSI_FLAG_LEVEL_CAPABLE),
164*4882a593Smuzhiyun 	.ops	= &gicp_msi_ops,
165*4882a593Smuzhiyun 	.chip	= &gicp_msi_irq_chip,
166*4882a593Smuzhiyun };
167*4882a593Smuzhiyun 
mvebu_gicp_probe(struct platform_device * pdev)168*4882a593Smuzhiyun static int mvebu_gicp_probe(struct platform_device *pdev)
169*4882a593Smuzhiyun {
170*4882a593Smuzhiyun 	struct mvebu_gicp *gicp;
171*4882a593Smuzhiyun 	struct irq_domain *inner_domain, *plat_domain, *parent_domain;
172*4882a593Smuzhiyun 	struct device_node *node = pdev->dev.of_node;
173*4882a593Smuzhiyun 	struct device_node *irq_parent_dn;
174*4882a593Smuzhiyun 	int ret, i;
175*4882a593Smuzhiyun 
176*4882a593Smuzhiyun 	gicp = devm_kzalloc(&pdev->dev, sizeof(*gicp), GFP_KERNEL);
177*4882a593Smuzhiyun 	if (!gicp)
178*4882a593Smuzhiyun 		return -ENOMEM;
179*4882a593Smuzhiyun 
180*4882a593Smuzhiyun 	gicp->dev = &pdev->dev;
181*4882a593Smuzhiyun 	spin_lock_init(&gicp->spi_lock);
182*4882a593Smuzhiyun 
183*4882a593Smuzhiyun 	gicp->res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
184*4882a593Smuzhiyun 	if (!gicp->res)
185*4882a593Smuzhiyun 		return -ENODEV;
186*4882a593Smuzhiyun 
187*4882a593Smuzhiyun 	ret = of_property_count_u32_elems(node, "marvell,spi-ranges");
188*4882a593Smuzhiyun 	if (ret < 0)
189*4882a593Smuzhiyun 		return ret;
190*4882a593Smuzhiyun 
191*4882a593Smuzhiyun 	gicp->spi_ranges_cnt = ret / 2;
192*4882a593Smuzhiyun 
193*4882a593Smuzhiyun 	gicp->spi_ranges =
194*4882a593Smuzhiyun 		devm_kcalloc(&pdev->dev,
195*4882a593Smuzhiyun 			     gicp->spi_ranges_cnt,
196*4882a593Smuzhiyun 			     sizeof(struct mvebu_gicp_spi_range),
197*4882a593Smuzhiyun 			     GFP_KERNEL);
198*4882a593Smuzhiyun 	if (!gicp->spi_ranges)
199*4882a593Smuzhiyun 		return -ENOMEM;
200*4882a593Smuzhiyun 
201*4882a593Smuzhiyun 	for (i = 0; i < gicp->spi_ranges_cnt; i++) {
202*4882a593Smuzhiyun 		of_property_read_u32_index(node, "marvell,spi-ranges",
203*4882a593Smuzhiyun 					   i * 2,
204*4882a593Smuzhiyun 					   &gicp->spi_ranges[i].start);
205*4882a593Smuzhiyun 
206*4882a593Smuzhiyun 		of_property_read_u32_index(node, "marvell,spi-ranges",
207*4882a593Smuzhiyun 					   i * 2 + 1,
208*4882a593Smuzhiyun 					   &gicp->spi_ranges[i].count);
209*4882a593Smuzhiyun 
210*4882a593Smuzhiyun 		gicp->spi_cnt += gicp->spi_ranges[i].count;
211*4882a593Smuzhiyun 	}
212*4882a593Smuzhiyun 
213*4882a593Smuzhiyun 	gicp->spi_bitmap = devm_kcalloc(&pdev->dev,
214*4882a593Smuzhiyun 				BITS_TO_LONGS(gicp->spi_cnt), sizeof(long),
215*4882a593Smuzhiyun 				GFP_KERNEL);
216*4882a593Smuzhiyun 	if (!gicp->spi_bitmap)
217*4882a593Smuzhiyun 		return -ENOMEM;
218*4882a593Smuzhiyun 
219*4882a593Smuzhiyun 	irq_parent_dn = of_irq_find_parent(node);
220*4882a593Smuzhiyun 	if (!irq_parent_dn) {
221*4882a593Smuzhiyun 		dev_err(&pdev->dev, "failed to find parent IRQ node\n");
222*4882a593Smuzhiyun 		return -ENODEV;
223*4882a593Smuzhiyun 	}
224*4882a593Smuzhiyun 
225*4882a593Smuzhiyun 	parent_domain = irq_find_host(irq_parent_dn);
226*4882a593Smuzhiyun 	if (!parent_domain) {
227*4882a593Smuzhiyun 		dev_err(&pdev->dev, "failed to find parent IRQ domain\n");
228*4882a593Smuzhiyun 		return -ENODEV;
229*4882a593Smuzhiyun 	}
230*4882a593Smuzhiyun 
231*4882a593Smuzhiyun 	inner_domain = irq_domain_create_hierarchy(parent_domain, 0,
232*4882a593Smuzhiyun 						   gicp->spi_cnt,
233*4882a593Smuzhiyun 						   of_node_to_fwnode(node),
234*4882a593Smuzhiyun 						   &gicp_domain_ops, gicp);
235*4882a593Smuzhiyun 	if (!inner_domain)
236*4882a593Smuzhiyun 		return -ENOMEM;
237*4882a593Smuzhiyun 
238*4882a593Smuzhiyun 
239*4882a593Smuzhiyun 	plat_domain = platform_msi_create_irq_domain(of_node_to_fwnode(node),
240*4882a593Smuzhiyun 						     &gicp_msi_domain_info,
241*4882a593Smuzhiyun 						     inner_domain);
242*4882a593Smuzhiyun 	if (!plat_domain) {
243*4882a593Smuzhiyun 		irq_domain_remove(inner_domain);
244*4882a593Smuzhiyun 		return -ENOMEM;
245*4882a593Smuzhiyun 	}
246*4882a593Smuzhiyun 
247*4882a593Smuzhiyun 	platform_set_drvdata(pdev, gicp);
248*4882a593Smuzhiyun 
249*4882a593Smuzhiyun 	return 0;
250*4882a593Smuzhiyun }
251*4882a593Smuzhiyun 
252*4882a593Smuzhiyun static const struct of_device_id mvebu_gicp_of_match[] = {
253*4882a593Smuzhiyun 	{ .compatible = "marvell,ap806-gicp", },
254*4882a593Smuzhiyun 	{},
255*4882a593Smuzhiyun };
256*4882a593Smuzhiyun 
257*4882a593Smuzhiyun static struct platform_driver mvebu_gicp_driver = {
258*4882a593Smuzhiyun 	.probe  = mvebu_gicp_probe,
259*4882a593Smuzhiyun 	.driver = {
260*4882a593Smuzhiyun 		.name = "mvebu-gicp",
261*4882a593Smuzhiyun 		.of_match_table = mvebu_gicp_of_match,
262*4882a593Smuzhiyun 	},
263*4882a593Smuzhiyun };
264*4882a593Smuzhiyun builtin_platform_driver(mvebu_gicp_driver);
265