2 * Copyright (C) 2016 ARM Limited, All Rights Reserved.
3 * Author: Marc Zyngier <marc.zyngier@arm.com>
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License version 2 as
7 * published by the Free Software Foundation.
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
14 * You should have received a copy of the GNU General Public License
15 * along with this program. If not, see <http://www.gnu.org/licenses/>.
18 #include <linux/bitops.h>
19 #include <linux/interrupt.h>
20 #include <linux/irqchip.h>
21 #include <linux/irqchip/chained_irq.h>
22 #include <linux/irqchip/irq-partition-percpu.h>
23 #include <linux/irqdomain.h>
24 #include <linux/seq_file.h>
25 #include <linux/slab.h>
27 struct partition_desc
{
29 struct partition_affinity
*parts
;
30 struct irq_domain
*domain
;
31 struct irq_desc
*chained_desc
;
32 unsigned long *bitmap
;
33 struct irq_domain_ops ops
;
36 static bool partition_check_cpu(struct partition_desc
*part
,
37 unsigned int cpu
, unsigned int hwirq
)
39 return cpumask_test_cpu(cpu
, &part
->parts
[hwirq
].mask
);
42 static void partition_irq_mask(struct irq_data
*d
)
44 struct partition_desc
*part
= irq_data_get_irq_chip_data(d
);
45 struct irq_chip
*chip
= irq_desc_get_chip(part
->chained_desc
);
46 struct irq_data
*data
= irq_desc_get_irq_data(part
->chained_desc
);
48 if (partition_check_cpu(part
, smp_processor_id(), d
->hwirq
) &&
53 static void partition_irq_unmask(struct irq_data
*d
)
55 struct partition_desc
*part
= irq_data_get_irq_chip_data(d
);
56 struct irq_chip
*chip
= irq_desc_get_chip(part
->chained_desc
);
57 struct irq_data
*data
= irq_desc_get_irq_data(part
->chained_desc
);
59 if (partition_check_cpu(part
, smp_processor_id(), d
->hwirq
) &&
61 chip
->irq_unmask(data
);
64 static int partition_irq_set_irqchip_state(struct irq_data
*d
,
65 enum irqchip_irq_state which
,
68 struct partition_desc
*part
= irq_data_get_irq_chip_data(d
);
69 struct irq_chip
*chip
= irq_desc_get_chip(part
->chained_desc
);
70 struct irq_data
*data
= irq_desc_get_irq_data(part
->chained_desc
);
72 if (partition_check_cpu(part
, smp_processor_id(), d
->hwirq
) &&
73 chip
->irq_set_irqchip_state
)
74 return chip
->irq_set_irqchip_state(data
, which
, val
);
79 static int partition_irq_get_irqchip_state(struct irq_data
*d
,
80 enum irqchip_irq_state which
,
83 struct partition_desc
*part
= irq_data_get_irq_chip_data(d
);
84 struct irq_chip
*chip
= irq_desc_get_chip(part
->chained_desc
);
85 struct irq_data
*data
= irq_desc_get_irq_data(part
->chained_desc
);
87 if (partition_check_cpu(part
, smp_processor_id(), d
->hwirq
) &&
88 chip
->irq_get_irqchip_state
)
89 return chip
->irq_get_irqchip_state(data
, which
, val
);
94 static int partition_irq_set_type(struct irq_data
*d
, unsigned int type
)
96 struct partition_desc
*part
= irq_data_get_irq_chip_data(d
);
97 struct irq_chip
*chip
= irq_desc_get_chip(part
->chained_desc
);
98 struct irq_data
*data
= irq_desc_get_irq_data(part
->chained_desc
);
100 if (chip
->irq_set_type
)
101 return chip
->irq_set_type(data
, type
);
106 static void partition_irq_print_chip(struct irq_data
*d
, struct seq_file
*p
)
108 struct partition_desc
*part
= irq_data_get_irq_chip_data(d
);
109 struct irq_chip
*chip
= irq_desc_get_chip(part
->chained_desc
);
110 struct irq_data
*data
= irq_desc_get_irq_data(part
->chained_desc
);
112 seq_printf(p
, " %5s-%lu", chip
->name
, data
->hwirq
);
115 static struct irq_chip partition_irq_chip
= {
116 .irq_mask
= partition_irq_mask
,
117 .irq_unmask
= partition_irq_unmask
,
118 .irq_set_type
= partition_irq_set_type
,
119 .irq_get_irqchip_state
= partition_irq_get_irqchip_state
,
120 .irq_set_irqchip_state
= partition_irq_set_irqchip_state
,
121 .irq_print_chip
= partition_irq_print_chip
,
124 static void partition_handle_irq(struct irq_desc
*desc
)
126 struct partition_desc
*part
= irq_desc_get_handler_data(desc
);
127 struct irq_chip
*chip
= irq_desc_get_chip(desc
);
128 int cpu
= smp_processor_id();
131 chained_irq_enter(chip
, desc
);
133 for_each_set_bit(hwirq
, part
->bitmap
, part
->nr_parts
) {
134 if (partition_check_cpu(part
, cpu
, hwirq
))
138 if (unlikely(hwirq
== part
->nr_parts
)) {
139 handle_bad_irq(desc
);
142 irq
= irq_find_mapping(part
->domain
, hwirq
);
143 generic_handle_irq(irq
);
146 chained_irq_exit(chip
, desc
);
149 static int partition_domain_alloc(struct irq_domain
*domain
, unsigned int virq
,
150 unsigned int nr_irqs
, void *arg
)
153 irq_hw_number_t hwirq
;
155 struct irq_fwspec
*fwspec
= arg
;
156 struct partition_desc
*part
;
158 BUG_ON(nr_irqs
!= 1);
159 ret
= domain
->ops
->translate(domain
, fwspec
, &hwirq
, &type
);
163 part
= domain
->host_data
;
165 set_bit(hwirq
, part
->bitmap
);
166 irq_set_chained_handler_and_data(irq_desc_get_irq(part
->chained_desc
),
167 partition_handle_irq
, part
);
168 irq_set_percpu_devid_partition(virq
, &part
->parts
[hwirq
].mask
);
169 irq_domain_set_info(domain
, virq
, hwirq
, &partition_irq_chip
, part
,
170 handle_percpu_devid_irq
, NULL
, NULL
);
171 irq_set_status_flags(virq
, IRQ_NOAUTOEN
);
176 static void partition_domain_free(struct irq_domain
*domain
, unsigned int virq
,
177 unsigned int nr_irqs
)
181 BUG_ON(nr_irqs
!= 1);
183 d
= irq_domain_get_irq_data(domain
, virq
);
184 irq_set_handler(virq
, NULL
);
185 irq_domain_reset_irq_data(d
);
188 int partition_translate_id(struct partition_desc
*desc
, void *partition_id
)
190 struct partition_affinity
*part
= NULL
;
193 for (i
= 0; i
< desc
->nr_parts
; i
++) {
194 if (desc
->parts
[i
].partition_id
== partition_id
) {
195 part
= &desc
->parts
[i
];
200 if (WARN_ON(!part
)) {
201 pr_err("Failed to find partition\n");
208 struct partition_desc
*partition_create_desc(struct fwnode_handle
*fwnode
,
209 struct partition_affinity
*parts
,
212 const struct irq_domain_ops
*ops
)
214 struct partition_desc
*desc
;
215 struct irq_domain
*d
;
217 BUG_ON(!ops
->select
|| !ops
->translate
);
219 desc
= kzalloc(sizeof(*desc
), GFP_KERNEL
);
224 desc
->ops
.free
= partition_domain_free
;
225 desc
->ops
.alloc
= partition_domain_alloc
;
227 d
= irq_domain_create_linear(fwnode
, nr_parts
, &desc
->ops
, desc
);
232 desc
->bitmap
= kcalloc(BITS_TO_LONGS(nr_parts
), sizeof(long),
234 if (WARN_ON(!desc
->bitmap
))
237 desc
->chained_desc
= irq_to_desc(chained_irq
);
238 desc
->nr_parts
= nr_parts
;
244 irq_domain_remove(d
);
250 struct irq_domain
*partition_get_domain(struct partition_desc
*dsc
)