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