1 /*
2  * Copyright (C) 2016 ARM Limited, All Rights Reserved.
3  * Author: Marc Zyngier <marc.zyngier@arm.com>
4  *
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.
8  *
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.
13  *
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/>.
16  */
17 
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>
26 
27 struct partition_desc {
28 	int				nr_parts;
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;
34 };
35 
36 static bool partition_check_cpu(struct partition_desc *part,
37 				unsigned int cpu, unsigned int hwirq)
38 {
39 	return cpumask_test_cpu(cpu, &part->parts[hwirq].mask);
40 }
41 
42 static void partition_irq_mask(struct irq_data *d)
43 {
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);
47 
48 	if (partition_check_cpu(part, smp_processor_id(), d->hwirq) &&
49 	    chip->irq_mask)
50 		chip->irq_mask(data);
51 }
52 
53 static void partition_irq_unmask(struct irq_data *d)
54 {
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);
58 
59 	if (partition_check_cpu(part, smp_processor_id(), d->hwirq) &&
60 	    chip->irq_unmask)
61 		chip->irq_unmask(data);
62 }
63 
64 static int partition_irq_set_irqchip_state(struct irq_data *d,
65 					   enum irqchip_irq_state which,
66 					   bool val)
67 {
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);
71 
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);
75 
76 	return -EINVAL;
77 }
78 
79 static int partition_irq_get_irqchip_state(struct irq_data *d,
80 					   enum irqchip_irq_state which,
81 					   bool *val)
82 {
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);
86 
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);
90 
91 	return -EINVAL;
92 }
93 
94 static int partition_irq_set_type(struct irq_data *d, unsigned int type)
95 {
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);
99 
100 	if (chip->irq_set_type)
101 		return chip->irq_set_type(data, type);
102 
103 	return -EINVAL;
104 }
105 
106 static void partition_irq_print_chip(struct irq_data *d, struct seq_file *p)
107 {
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);
111 
112 	seq_printf(p, " %5s-%lu", chip->name, data->hwirq);
113 }
114 
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,
122 };
123 
124 static void partition_handle_irq(struct irq_desc *desc)
125 {
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();
129 	int hwirq;
130 
131 	chained_irq_enter(chip, desc);
132 
133 	for_each_set_bit(hwirq, part->bitmap, part->nr_parts) {
134 		if (partition_check_cpu(part, cpu, hwirq))
135 			break;
136 	}
137 
138 	if (unlikely(hwirq == part->nr_parts)) {
139 		handle_bad_irq(desc);
140 	} else {
141 		unsigned int irq;
142 		irq = irq_find_mapping(part->domain, hwirq);
143 		generic_handle_irq(irq);
144 	}
145 
146 	chained_irq_exit(chip, desc);
147 }
148 
149 static int partition_domain_alloc(struct irq_domain *domain, unsigned int virq,
150 				  unsigned int nr_irqs, void *arg)
151 {
152 	int ret;
153 	irq_hw_number_t hwirq;
154 	unsigned int type;
155 	struct irq_fwspec *fwspec = arg;
156 	struct partition_desc *part;
157 
158 	BUG_ON(nr_irqs != 1);
159 	ret = domain->ops->translate(domain, fwspec, &hwirq, &type);
160 	if (ret)
161 		return ret;
162 
163 	part = domain->host_data;
164 
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);
172 
173 	return 0;
174 }
175 
176 static void partition_domain_free(struct irq_domain *domain, unsigned int virq,
177 				  unsigned int nr_irqs)
178 {
179 	struct irq_data *d;
180 
181 	BUG_ON(nr_irqs != 1);
182 
183 	d = irq_domain_get_irq_data(domain, virq);
184 	irq_set_handler(virq, NULL);
185 	irq_domain_reset_irq_data(d);
186 }
187 
188 int partition_translate_id(struct partition_desc *desc, void *partition_id)
189 {
190 	struct partition_affinity *part = NULL;
191 	int i;
192 
193 	for (i = 0; i < desc->nr_parts; i++) {
194 		if (desc->parts[i].partition_id == partition_id) {
195 			part = &desc->parts[i];
196 			break;
197 		}
198 	}
199 
200 	if (WARN_ON(!part)) {
201 		pr_err("Failed to find partition\n");
202 		return -EINVAL;
203 	}
204 
205 	return i;
206 }
207 
208 struct partition_desc *partition_create_desc(struct fwnode_handle *fwnode,
209 					     struct partition_affinity *parts,
210 					     int nr_parts,
211 					     int chained_irq,
212 					     const struct irq_domain_ops *ops)
213 {
214 	struct partition_desc *desc;
215 	struct irq_domain *d;
216 
217 	BUG_ON(!ops->select || !ops->translate);
218 
219 	desc = kzalloc(sizeof(*desc), GFP_KERNEL);
220 	if (!desc)
221 		return NULL;
222 
223 	desc->ops = *ops;
224 	desc->ops.free = partition_domain_free;
225 	desc->ops.alloc = partition_domain_alloc;
226 
227 	d = irq_domain_create_linear(fwnode, nr_parts, &desc->ops, desc);
228 	if (!d)
229 		goto out;
230 	desc->domain = d;
231 
232 	desc->bitmap = kcalloc(BITS_TO_LONGS(nr_parts), sizeof(long),
233 			       GFP_KERNEL);
234 	if (WARN_ON(!desc->bitmap))
235 		goto out;
236 
237 	desc->chained_desc = irq_to_desc(chained_irq);
238 	desc->nr_parts = nr_parts;
239 	desc->parts = parts;
240 
241 	return desc;
242 out:
243 	if (d)
244 		irq_domain_remove(d);
245 	kfree(desc);
246 
247 	return NULL;
248 }
249 
250 struct irq_domain *partition_get_domain(struct partition_desc *dsc)
251 {
252 	if (dsc)
253 		return dsc->domain;
254 
255 	return NULL;
256 }
257