root/drivers/irqchip/irq-partition-percpu.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. partition_check_cpu
  2. partition_irq_mask
  3. partition_irq_unmask
  4. partition_irq_set_irqchip_state
  5. partition_irq_get_irqchip_state
  6. partition_irq_set_type
  7. partition_irq_print_chip
  8. partition_handle_irq
  9. partition_domain_alloc
  10. partition_domain_free
  11. partition_translate_id
  12. partition_create_desc
  13. partition_get_domain

   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                 unsigned int irq;
 131                 irq = irq_find_mapping(part->domain, hwirq);
 132                 generic_handle_irq(irq);
 133         }
 134 
 135         chained_irq_exit(chip, desc);
 136 }
 137 
 138 static int partition_domain_alloc(struct irq_domain *domain, unsigned int virq,
 139                                   unsigned int nr_irqs, void *arg)
 140 {
 141         int ret;
 142         irq_hw_number_t hwirq;
 143         unsigned int type;
 144         struct irq_fwspec *fwspec = arg;
 145         struct partition_desc *part;
 146 
 147         BUG_ON(nr_irqs != 1);
 148         ret = domain->ops->translate(domain, fwspec, &hwirq, &type);
 149         if (ret)
 150                 return ret;
 151 
 152         part = domain->host_data;
 153 
 154         set_bit(hwirq, part->bitmap);
 155         irq_set_chained_handler_and_data(irq_desc_get_irq(part->chained_desc),
 156                                          partition_handle_irq, part);
 157         irq_set_percpu_devid_partition(virq, &part->parts[hwirq].mask);
 158         irq_domain_set_info(domain, virq, hwirq, &partition_irq_chip, part,
 159                             handle_percpu_devid_irq, NULL, NULL);
 160         irq_set_status_flags(virq, IRQ_NOAUTOEN);
 161 
 162         return 0;
 163 }
 164 
 165 static void partition_domain_free(struct irq_domain *domain, unsigned int virq,
 166                                   unsigned int nr_irqs)
 167 {
 168         struct irq_data *d;
 169 
 170         BUG_ON(nr_irqs != 1);
 171 
 172         d = irq_domain_get_irq_data(domain, virq);
 173         irq_set_handler(virq, NULL);
 174         irq_domain_reset_irq_data(d);
 175 }
 176 
 177 int partition_translate_id(struct partition_desc *desc, void *partition_id)
 178 {
 179         struct partition_affinity *part = NULL;
 180         int i;
 181 
 182         for (i = 0; i < desc->nr_parts; i++) {
 183                 if (desc->parts[i].partition_id == partition_id) {
 184                         part = &desc->parts[i];
 185                         break;
 186                 }
 187         }
 188 
 189         if (WARN_ON(!part)) {
 190                 pr_err("Failed to find partition\n");
 191                 return -EINVAL;
 192         }
 193 
 194         return i;
 195 }
 196 
 197 struct partition_desc *partition_create_desc(struct fwnode_handle *fwnode,
 198                                              struct partition_affinity *parts,
 199                                              int nr_parts,
 200                                              int chained_irq,
 201                                              const struct irq_domain_ops *ops)
 202 {
 203         struct partition_desc *desc;
 204         struct irq_domain *d;
 205 
 206         BUG_ON(!ops->select || !ops->translate);
 207 
 208         desc = kzalloc(sizeof(*desc), GFP_KERNEL);
 209         if (!desc)
 210                 return NULL;
 211 
 212         desc->ops = *ops;
 213         desc->ops.free = partition_domain_free;
 214         desc->ops.alloc = partition_domain_alloc;
 215 
 216         d = irq_domain_create_linear(fwnode, nr_parts, &desc->ops, desc);
 217         if (!d)
 218                 goto out;
 219         desc->domain = d;
 220 
 221         desc->bitmap = kcalloc(BITS_TO_LONGS(nr_parts), sizeof(long),
 222                                GFP_KERNEL);
 223         if (WARN_ON(!desc->bitmap))
 224                 goto out;
 225 
 226         desc->chained_desc = irq_to_desc(chained_irq);
 227         desc->nr_parts = nr_parts;
 228         desc->parts = parts;
 229 
 230         return desc;
 231 out:
 232         if (d)
 233                 irq_domain_remove(d);
 234         kfree(desc);
 235 
 236         return NULL;
 237 }
 238 
 239 struct irq_domain *partition_get_domain(struct partition_desc *dsc)
 240 {
 241         if (dsc)
 242                 return dsc->domain;
 243 
 244         return NULL;
 245 }

/* [<][>][^][v][top][bottom][index][help] */