Merge branch 'misc' of git://git.kernel.org/pub/scm/linux/kernel/git/mmarek/kbuild
[platform/kernel/linux-exynos.git] / drivers / irqchip / irq-partition-percpu.c
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 = kzalloc(sizeof(long) * BITS_TO_LONGS(nr_parts),
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 }