linux/drivers/irqchip/irq-partition-percpu.c
<<
>>
Prefs
   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
  27struct 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
  36static 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
  42static 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
  53static 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
  64static 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
  79static 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
  94static 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
 106static 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
 115static 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
 124static 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
 149static 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
 176static 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
 188int 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
 208struct 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;
 242out:
 243        if (d)
 244                irq_domain_remove(d);
 245        kfree(desc);
 246
 247        return NULL;
 248}
 249
 250struct irq_domain *partition_get_domain(struct partition_desc *dsc)
 251{
 252        if (dsc)
 253                return dsc->domain;
 254
 255        return NULL;
 256}
 257