Merge tag 'm68knommu-for-v5.15' of git://git.kernel.org/pub/scm/linux/kernel/git...
[linux-2.6-microblaze.git] / drivers / irqchip / irq-partition-percpu.c
1 // SPDX-License-Identifier: GPL-2.0-only
2 /*
3  * Copyright (C) 2016 ARM Limited, All Rights Reserved.
4  * Author: Marc Zyngier <marc.zyngier@arm.com>
5  */
6
7 #include <linux/bitops.h>
8 #include <linux/interrupt.h>
9 #include <linux/irqchip.h>
10 #include <linux/irqchip/chained_irq.h>
11 #include <linux/irqchip/irq-partition-percpu.h>
12 #include <linux/irqdomain.h>
13 #include <linux/seq_file.h>
14 #include <linux/slab.h>
15
16 struct partition_desc {
17         int                             nr_parts;
18         struct partition_affinity       *parts;
19         struct irq_domain               *domain;
20         struct irq_desc                 *chained_desc;
21         unsigned long                   *bitmap;
22         struct irq_domain_ops           ops;
23 };
24
25 static bool partition_check_cpu(struct partition_desc *part,
26                                 unsigned int cpu, unsigned int hwirq)
27 {
28         return cpumask_test_cpu(cpu, &part->parts[hwirq].mask);
29 }
30
31 static void partition_irq_mask(struct irq_data *d)
32 {
33         struct partition_desc *part = irq_data_get_irq_chip_data(d);
34         struct irq_chip *chip = irq_desc_get_chip(part->chained_desc);
35         struct irq_data *data = irq_desc_get_irq_data(part->chained_desc);
36
37         if (partition_check_cpu(part, smp_processor_id(), d->hwirq) &&
38             chip->irq_mask)
39                 chip->irq_mask(data);
40 }
41
42 static void partition_irq_unmask(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_unmask)
50                 chip->irq_unmask(data);
51 }
52
53 static int partition_irq_set_irqchip_state(struct irq_data *d,
54                                            enum irqchip_irq_state which,
55                                            bool val)
56 {
57         struct partition_desc *part = irq_data_get_irq_chip_data(d);
58         struct irq_chip *chip = irq_desc_get_chip(part->chained_desc);
59         struct irq_data *data = irq_desc_get_irq_data(part->chained_desc);
60
61         if (partition_check_cpu(part, smp_processor_id(), d->hwirq) &&
62             chip->irq_set_irqchip_state)
63                 return chip->irq_set_irqchip_state(data, which, val);
64
65         return -EINVAL;
66 }
67
68 static int partition_irq_get_irqchip_state(struct irq_data *d,
69                                            enum irqchip_irq_state which,
70                                            bool *val)
71 {
72         struct partition_desc *part = irq_data_get_irq_chip_data(d);
73         struct irq_chip *chip = irq_desc_get_chip(part->chained_desc);
74         struct irq_data *data = irq_desc_get_irq_data(part->chained_desc);
75
76         if (partition_check_cpu(part, smp_processor_id(), d->hwirq) &&
77             chip->irq_get_irqchip_state)
78                 return chip->irq_get_irqchip_state(data, which, val);
79
80         return -EINVAL;
81 }
82
83 static int partition_irq_set_type(struct irq_data *d, unsigned int type)
84 {
85         struct partition_desc *part = irq_data_get_irq_chip_data(d);
86         struct irq_chip *chip = irq_desc_get_chip(part->chained_desc);
87         struct irq_data *data = irq_desc_get_irq_data(part->chained_desc);
88
89         if (chip->irq_set_type)
90                 return chip->irq_set_type(data, type);
91
92         return -EINVAL;
93 }
94
95 static void partition_irq_print_chip(struct irq_data *d, struct seq_file *p)
96 {
97         struct partition_desc *part = irq_data_get_irq_chip_data(d);
98         struct irq_chip *chip = irq_desc_get_chip(part->chained_desc);
99         struct irq_data *data = irq_desc_get_irq_data(part->chained_desc);
100
101         seq_printf(p, " %5s-%lu", chip->name, data->hwirq);
102 }
103
104 static struct irq_chip partition_irq_chip = {
105         .irq_mask               = partition_irq_mask,
106         .irq_unmask             = partition_irq_unmask,
107         .irq_set_type           = partition_irq_set_type,
108         .irq_get_irqchip_state  = partition_irq_get_irqchip_state,
109         .irq_set_irqchip_state  = partition_irq_set_irqchip_state,
110         .irq_print_chip         = partition_irq_print_chip,
111 };
112
113 static void partition_handle_irq(struct irq_desc *desc)
114 {
115         struct partition_desc *part = irq_desc_get_handler_data(desc);
116         struct irq_chip *chip = irq_desc_get_chip(desc);
117         int cpu = smp_processor_id();
118         int hwirq;
119
120         chained_irq_enter(chip, desc);
121
122         for_each_set_bit(hwirq, part->bitmap, part->nr_parts) {
123                 if (partition_check_cpu(part, cpu, hwirq))
124                         break;
125         }
126
127         if (unlikely(hwirq == part->nr_parts))
128                 handle_bad_irq(desc);
129         else
130                 generic_handle_domain_irq(part->domain, hwirq);
131
132         chained_irq_exit(chip, desc);
133 }
134
135 static int partition_domain_alloc(struct irq_domain *domain, unsigned int virq,
136                                   unsigned int nr_irqs, void *arg)
137 {
138         int ret;
139         irq_hw_number_t hwirq;
140         unsigned int type;
141         struct irq_fwspec *fwspec = arg;
142         struct partition_desc *part;
143
144         BUG_ON(nr_irqs != 1);
145         ret = domain->ops->translate(domain, fwspec, &hwirq, &type);
146         if (ret)
147                 return ret;
148
149         part = domain->host_data;
150
151         set_bit(hwirq, part->bitmap);
152         irq_set_chained_handler_and_data(irq_desc_get_irq(part->chained_desc),
153                                          partition_handle_irq, part);
154         irq_set_percpu_devid_partition(virq, &part->parts[hwirq].mask);
155         irq_domain_set_info(domain, virq, hwirq, &partition_irq_chip, part,
156                             handle_percpu_devid_irq, NULL, NULL);
157         irq_set_status_flags(virq, IRQ_NOAUTOEN);
158
159         return 0;
160 }
161
162 static void partition_domain_free(struct irq_domain *domain, unsigned int virq,
163                                   unsigned int nr_irqs)
164 {
165         struct irq_data *d;
166
167         BUG_ON(nr_irqs != 1);
168
169         d = irq_domain_get_irq_data(domain, virq);
170         irq_set_handler(virq, NULL);
171         irq_domain_reset_irq_data(d);
172 }
173
174 int partition_translate_id(struct partition_desc *desc, void *partition_id)
175 {
176         struct partition_affinity *part = NULL;
177         int i;
178
179         for (i = 0; i < desc->nr_parts; i++) {
180                 if (desc->parts[i].partition_id == partition_id) {
181                         part = &desc->parts[i];
182                         break;
183                 }
184         }
185
186         if (WARN_ON(!part)) {
187                 pr_err("Failed to find partition\n");
188                 return -EINVAL;
189         }
190
191         return i;
192 }
193
194 struct partition_desc *partition_create_desc(struct fwnode_handle *fwnode,
195                                              struct partition_affinity *parts,
196                                              int nr_parts,
197                                              int chained_irq,
198                                              const struct irq_domain_ops *ops)
199 {
200         struct partition_desc *desc;
201         struct irq_domain *d;
202
203         BUG_ON(!ops->select || !ops->translate);
204
205         desc = kzalloc(sizeof(*desc), GFP_KERNEL);
206         if (!desc)
207                 return NULL;
208
209         desc->ops = *ops;
210         desc->ops.free = partition_domain_free;
211         desc->ops.alloc = partition_domain_alloc;
212
213         d = irq_domain_create_linear(fwnode, nr_parts, &desc->ops, desc);
214         if (!d)
215                 goto out;
216         desc->domain = d;
217
218         desc->bitmap = bitmap_zalloc(nr_parts, GFP_KERNEL);
219         if (WARN_ON(!desc->bitmap))
220                 goto out;
221
222         desc->chained_desc = irq_to_desc(chained_irq);
223         desc->nr_parts = nr_parts;
224         desc->parts = parts;
225
226         return desc;
227 out:
228         if (d)
229                 irq_domain_remove(d);
230         kfree(desc);
231
232         return NULL;
233 }
234
235 struct irq_domain *partition_get_domain(struct partition_desc *dsc)
236 {
237         if (dsc)
238                 return dsc->domain;
239
240         return NULL;
241 }