linux/drivers/irqchip/irq-partition-percpu.c
<<
>>
Prefs
   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
  16struct 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
  25static 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
  31static 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
  42static 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
  53static 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
  68static 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
  83static 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
  95static 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
 104static 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
 113static 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
 138static 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
 165static 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
 177int 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
 197struct 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;
 231out:
 232        if (d)
 233                irq_domain_remove(d);
 234        kfree(desc);
 235
 236        return NULL;
 237}
 238
 239struct irq_domain *partition_get_domain(struct partition_desc *dsc)
 240{
 241        if (dsc)
 242                return dsc->domain;
 243
 244        return NULL;
 245}
 246