2 * Copyright (C) 2017 Marvell
4 * Thomas Petazzoni <thomas.petazzoni@free-electrons.com>
6 * This file is licensed under the terms of the GNU General Public
7 * License version 2. This program is licensed "as is" without any
8 * warranty of any kind, whether express or implied.
12 #include <linux/irq.h>
13 #include <linux/irqdomain.h>
14 #include <linux/msi.h>
16 #include <linux/of_irq.h>
17 #include <linux/of_platform.h>
18 #include <linux/platform_device.h>
20 #include "irq-msi-lib.h"
22 #include <dt-bindings/interrupt-controller/arm-gic.h>
24 #define GICP_SETSPI_NSR_OFFSET 0x0
25 #define GICP_CLRSPI_NSR_OFFSET 0x8
27 struct mvebu_gicp_spi_range
{
33 struct mvebu_gicp_spi_range
*spi_ranges
;
34 unsigned int spi_ranges_cnt
;
36 unsigned long *spi_bitmap
;
42 static int gicp_idx_to_spi(struct mvebu_gicp
*gicp
, int idx
)
46 for (i
= 0; i
< gicp
->spi_ranges_cnt
; i
++) {
47 struct mvebu_gicp_spi_range
*r
= &gicp
->spi_ranges
[i
];
50 return r
->start
+ idx
;
58 static void gicp_compose_msi_msg(struct irq_data
*data
, struct msi_msg
*msg
)
60 struct mvebu_gicp
*gicp
= data
->chip_data
;
61 phys_addr_t setspi
= gicp
->res
->start
+ GICP_SETSPI_NSR_OFFSET
;
62 phys_addr_t clrspi
= gicp
->res
->start
+ GICP_CLRSPI_NSR_OFFSET
;
64 msg
[0].data
= data
->hwirq
;
65 msg
[0].address_lo
= lower_32_bits(setspi
);
66 msg
[0].address_hi
= upper_32_bits(setspi
);
67 msg
[1].data
= data
->hwirq
;
68 msg
[1].address_lo
= lower_32_bits(clrspi
);
69 msg
[1].address_hi
= upper_32_bits(clrspi
);
72 static struct irq_chip gicp_irq_chip
= {
74 .irq_mask
= irq_chip_mask_parent
,
75 .irq_unmask
= irq_chip_unmask_parent
,
76 .irq_eoi
= irq_chip_eoi_parent
,
77 .irq_set_affinity
= irq_chip_set_affinity_parent
,
78 .irq_set_type
= irq_chip_set_type_parent
,
79 .irq_compose_msi_msg
= gicp_compose_msi_msg
,
82 static int gicp_irq_domain_alloc(struct irq_domain
*domain
, unsigned int virq
,
83 unsigned int nr_irqs
, void *args
)
85 struct mvebu_gicp
*gicp
= domain
->host_data
;
86 struct irq_fwspec fwspec
;
90 spin_lock(&gicp
->spi_lock
);
91 hwirq
= find_first_zero_bit(gicp
->spi_bitmap
, gicp
->spi_cnt
);
92 if (hwirq
== gicp
->spi_cnt
) {
93 spin_unlock(&gicp
->spi_lock
);
96 __set_bit(hwirq
, gicp
->spi_bitmap
);
97 spin_unlock(&gicp
->spi_lock
);
99 fwspec
.fwnode
= domain
->parent
->fwnode
;
100 fwspec
.param_count
= 3;
101 fwspec
.param
[0] = GIC_SPI
;
102 fwspec
.param
[1] = gicp_idx_to_spi(gicp
, hwirq
) - 32;
104 * Assume edge rising for now, it will be properly set when
105 * ->set_type() is called
107 fwspec
.param
[2] = IRQ_TYPE_EDGE_RISING
;
109 ret
= irq_domain_alloc_irqs_parent(domain
, virq
, 1, &fwspec
);
111 dev_err(gicp
->dev
, "Cannot allocate parent IRQ\n");
115 ret
= irq_domain_set_hwirq_and_chip(domain
, virq
, hwirq
,
116 &gicp_irq_chip
, gicp
);
118 goto free_irqs_parent
;
123 irq_domain_free_irqs_parent(domain
, virq
, nr_irqs
);
125 spin_lock(&gicp
->spi_lock
);
126 __clear_bit(hwirq
, gicp
->spi_bitmap
);
127 spin_unlock(&gicp
->spi_lock
);
131 static void gicp_irq_domain_free(struct irq_domain
*domain
,
132 unsigned int virq
, unsigned int nr_irqs
)
134 struct mvebu_gicp
*gicp
= domain
->host_data
;
135 struct irq_data
*d
= irq_domain_get_irq_data(domain
, virq
);
137 if (d
->hwirq
>= gicp
->spi_cnt
) {
138 dev_err(gicp
->dev
, "Invalid hwirq %lu\n", d
->hwirq
);
142 irq_domain_free_irqs_parent(domain
, virq
, nr_irqs
);
144 spin_lock(&gicp
->spi_lock
);
145 __clear_bit(d
->hwirq
, gicp
->spi_bitmap
);
146 spin_unlock(&gicp
->spi_lock
);
149 static const struct irq_domain_ops gicp_domain_ops
= {
150 .select
= msi_lib_irq_domain_select
,
151 .alloc
= gicp_irq_domain_alloc
,
152 .free
= gicp_irq_domain_free
,
155 #define GICP_MSI_FLAGS_REQUIRED (MSI_FLAG_USE_DEF_DOM_OPS | \
156 MSI_FLAG_USE_DEF_CHIP_OPS)
158 #define GICP_MSI_FLAGS_SUPPORTED (MSI_GENERIC_FLAGS_MASK | \
159 MSI_FLAG_LEVEL_CAPABLE)
161 static const struct msi_parent_ops gicp_msi_parent_ops
= {
162 .supported_flags
= GICP_MSI_FLAGS_SUPPORTED
,
163 .required_flags
= GICP_MSI_FLAGS_REQUIRED
,
164 .bus_select_token
= DOMAIN_BUS_GENERIC_MSI
,
165 .bus_select_mask
= MATCH_PLATFORM_MSI
,
167 .init_dev_msi_info
= msi_lib_init_dev_msi_info
,
170 static int mvebu_gicp_probe(struct platform_device
*pdev
)
172 struct irq_domain
*inner_domain
, *parent_domain
;
173 struct device_node
*node
= pdev
->dev
.of_node
;
174 struct device_node
*irq_parent_dn
;
175 struct mvebu_gicp
*gicp
;
178 gicp
= devm_kzalloc(&pdev
->dev
, sizeof(*gicp
), GFP_KERNEL
);
182 gicp
->dev
= &pdev
->dev
;
183 spin_lock_init(&gicp
->spi_lock
);
185 gicp
->res
= platform_get_resource(pdev
, IORESOURCE_MEM
, 0);
189 ret
= of_property_count_u32_elems(node
, "marvell,spi-ranges");
193 gicp
->spi_ranges_cnt
= ret
/ 2;
196 devm_kcalloc(&pdev
->dev
,
197 gicp
->spi_ranges_cnt
,
198 sizeof(struct mvebu_gicp_spi_range
),
200 if (!gicp
->spi_ranges
)
203 for (i
= 0; i
< gicp
->spi_ranges_cnt
; i
++) {
204 of_property_read_u32_index(node
, "marvell,spi-ranges",
206 &gicp
->spi_ranges
[i
].start
);
208 of_property_read_u32_index(node
, "marvell,spi-ranges",
210 &gicp
->spi_ranges
[i
].count
);
212 gicp
->spi_cnt
+= gicp
->spi_ranges
[i
].count
;
215 gicp
->spi_bitmap
= devm_bitmap_zalloc(&pdev
->dev
, gicp
->spi_cnt
, GFP_KERNEL
);
216 if (!gicp
->spi_bitmap
)
219 irq_parent_dn
= of_irq_find_parent(node
);
220 if (!irq_parent_dn
) {
221 dev_err(&pdev
->dev
, "failed to find parent IRQ node\n");
225 parent_domain
= irq_find_host(irq_parent_dn
);
226 of_node_put(irq_parent_dn
);
227 if (!parent_domain
) {
228 dev_err(&pdev
->dev
, "failed to find parent IRQ domain\n");
232 inner_domain
= irq_domain_create_hierarchy(parent_domain
, 0,
234 of_node_to_fwnode(node
),
235 &gicp_domain_ops
, gicp
);
239 irq_domain_update_bus_token(inner_domain
, DOMAIN_BUS_GENERIC_MSI
);
240 inner_domain
->flags
|= IRQ_DOMAIN_FLAG_MSI_PARENT
;
241 inner_domain
->msi_parent_ops
= &gicp_msi_parent_ops
;
245 static const struct of_device_id mvebu_gicp_of_match
[] = {
246 { .compatible
= "marvell,ap806-gicp", },
250 static struct platform_driver mvebu_gicp_driver
= {
251 .probe
= mvebu_gicp_probe
,
253 .name
= "mvebu-gicp",
254 .of_match_table
= mvebu_gicp_of_match
,
257 builtin_platform_driver(mvebu_gicp_driver
);