Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0-only
0002 /*
0003  * Copyright (C) 2016 ARM Limited, All Rights Reserved.
0004  * Author: Marc Zyngier <marc.zyngier@arm.com>
0005  */
0006 
0007 #include <linux/bitops.h>
0008 #include <linux/interrupt.h>
0009 #include <linux/irqchip.h>
0010 #include <linux/irqchip/chained_irq.h>
0011 #include <linux/irqchip/irq-partition-percpu.h>
0012 #include <linux/irqdomain.h>
0013 #include <linux/seq_file.h>
0014 #include <linux/slab.h>
0015 
0016 struct partition_desc {
0017     int             nr_parts;
0018     struct partition_affinity   *parts;
0019     struct irq_domain       *domain;
0020     struct irq_desc         *chained_desc;
0021     unsigned long           *bitmap;
0022     struct irq_domain_ops       ops;
0023 };
0024 
0025 static bool partition_check_cpu(struct partition_desc *part,
0026                 unsigned int cpu, unsigned int hwirq)
0027 {
0028     return cpumask_test_cpu(cpu, &part->parts[hwirq].mask);
0029 }
0030 
0031 static void partition_irq_mask(struct irq_data *d)
0032 {
0033     struct partition_desc *part = irq_data_get_irq_chip_data(d);
0034     struct irq_chip *chip = irq_desc_get_chip(part->chained_desc);
0035     struct irq_data *data = irq_desc_get_irq_data(part->chained_desc);
0036 
0037     if (partition_check_cpu(part, smp_processor_id(), d->hwirq) &&
0038         chip->irq_mask)
0039         chip->irq_mask(data);
0040 }
0041 
0042 static void partition_irq_unmask(struct irq_data *d)
0043 {
0044     struct partition_desc *part = irq_data_get_irq_chip_data(d);
0045     struct irq_chip *chip = irq_desc_get_chip(part->chained_desc);
0046     struct irq_data *data = irq_desc_get_irq_data(part->chained_desc);
0047 
0048     if (partition_check_cpu(part, smp_processor_id(), d->hwirq) &&
0049         chip->irq_unmask)
0050         chip->irq_unmask(data);
0051 }
0052 
0053 static int partition_irq_set_irqchip_state(struct irq_data *d,
0054                        enum irqchip_irq_state which,
0055                        bool val)
0056 {
0057     struct partition_desc *part = irq_data_get_irq_chip_data(d);
0058     struct irq_chip *chip = irq_desc_get_chip(part->chained_desc);
0059     struct irq_data *data = irq_desc_get_irq_data(part->chained_desc);
0060 
0061     if (partition_check_cpu(part, smp_processor_id(), d->hwirq) &&
0062         chip->irq_set_irqchip_state)
0063         return chip->irq_set_irqchip_state(data, which, val);
0064 
0065     return -EINVAL;
0066 }
0067 
0068 static int partition_irq_get_irqchip_state(struct irq_data *d,
0069                        enum irqchip_irq_state which,
0070                        bool *val)
0071 {
0072     struct partition_desc *part = irq_data_get_irq_chip_data(d);
0073     struct irq_chip *chip = irq_desc_get_chip(part->chained_desc);
0074     struct irq_data *data = irq_desc_get_irq_data(part->chained_desc);
0075 
0076     if (partition_check_cpu(part, smp_processor_id(), d->hwirq) &&
0077         chip->irq_get_irqchip_state)
0078         return chip->irq_get_irqchip_state(data, which, val);
0079 
0080     return -EINVAL;
0081 }
0082 
0083 static int partition_irq_set_type(struct irq_data *d, unsigned int type)
0084 {
0085     struct partition_desc *part = irq_data_get_irq_chip_data(d);
0086     struct irq_chip *chip = irq_desc_get_chip(part->chained_desc);
0087     struct irq_data *data = irq_desc_get_irq_data(part->chained_desc);
0088 
0089     if (chip->irq_set_type)
0090         return chip->irq_set_type(data, type);
0091 
0092     return -EINVAL;
0093 }
0094 
0095 static void partition_irq_print_chip(struct irq_data *d, struct seq_file *p)
0096 {
0097     struct partition_desc *part = irq_data_get_irq_chip_data(d);
0098     struct irq_chip *chip = irq_desc_get_chip(part->chained_desc);
0099     struct irq_data *data = irq_desc_get_irq_data(part->chained_desc);
0100 
0101     seq_printf(p, " %5s-%lu", chip->name, data->hwirq);
0102 }
0103 
0104 static struct irq_chip partition_irq_chip = {
0105     .irq_mask       = partition_irq_mask,
0106     .irq_unmask     = partition_irq_unmask,
0107     .irq_set_type       = partition_irq_set_type,
0108     .irq_get_irqchip_state  = partition_irq_get_irqchip_state,
0109     .irq_set_irqchip_state  = partition_irq_set_irqchip_state,
0110     .irq_print_chip     = partition_irq_print_chip,
0111 };
0112 
0113 static void partition_handle_irq(struct irq_desc *desc)
0114 {
0115     struct partition_desc *part = irq_desc_get_handler_data(desc);
0116     struct irq_chip *chip = irq_desc_get_chip(desc);
0117     int cpu = smp_processor_id();
0118     int hwirq;
0119 
0120     chained_irq_enter(chip, desc);
0121 
0122     for_each_set_bit(hwirq, part->bitmap, part->nr_parts) {
0123         if (partition_check_cpu(part, cpu, hwirq))
0124             break;
0125     }
0126 
0127     if (unlikely(hwirq == part->nr_parts))
0128         handle_bad_irq(desc);
0129     else
0130         generic_handle_domain_irq(part->domain, hwirq);
0131 
0132     chained_irq_exit(chip, desc);
0133 }
0134 
0135 static int partition_domain_alloc(struct irq_domain *domain, unsigned int virq,
0136                   unsigned int nr_irqs, void *arg)
0137 {
0138     int ret;
0139     irq_hw_number_t hwirq;
0140     unsigned int type;
0141     struct irq_fwspec *fwspec = arg;
0142     struct partition_desc *part;
0143 
0144     BUG_ON(nr_irqs != 1);
0145     ret = domain->ops->translate(domain, fwspec, &hwirq, &type);
0146     if (ret)
0147         return ret;
0148 
0149     part = domain->host_data;
0150 
0151     set_bit(hwirq, part->bitmap);
0152     irq_set_chained_handler_and_data(irq_desc_get_irq(part->chained_desc),
0153                      partition_handle_irq, part);
0154     irq_set_percpu_devid_partition(virq, &part->parts[hwirq].mask);
0155     irq_domain_set_info(domain, virq, hwirq, &partition_irq_chip, part,
0156                 handle_percpu_devid_irq, NULL, NULL);
0157     irq_set_status_flags(virq, IRQ_NOAUTOEN);
0158 
0159     return 0;
0160 }
0161 
0162 static void partition_domain_free(struct irq_domain *domain, unsigned int virq,
0163                   unsigned int nr_irqs)
0164 {
0165     struct irq_data *d;
0166 
0167     BUG_ON(nr_irqs != 1);
0168 
0169     d = irq_domain_get_irq_data(domain, virq);
0170     irq_set_handler(virq, NULL);
0171     irq_domain_reset_irq_data(d);
0172 }
0173 
0174 int partition_translate_id(struct partition_desc *desc, void *partition_id)
0175 {
0176     struct partition_affinity *part = NULL;
0177     int i;
0178 
0179     for (i = 0; i < desc->nr_parts; i++) {
0180         if (desc->parts[i].partition_id == partition_id) {
0181             part = &desc->parts[i];
0182             break;
0183         }
0184     }
0185 
0186     if (WARN_ON(!part)) {
0187         pr_err("Failed to find partition\n");
0188         return -EINVAL;
0189     }
0190 
0191     return i;
0192 }
0193 
0194 struct partition_desc *partition_create_desc(struct fwnode_handle *fwnode,
0195                          struct partition_affinity *parts,
0196                          int nr_parts,
0197                          int chained_irq,
0198                          const struct irq_domain_ops *ops)
0199 {
0200     struct partition_desc *desc;
0201     struct irq_domain *d;
0202 
0203     BUG_ON(!ops->select || !ops->translate);
0204 
0205     desc = kzalloc(sizeof(*desc), GFP_KERNEL);
0206     if (!desc)
0207         return NULL;
0208 
0209     desc->ops = *ops;
0210     desc->ops.free = partition_domain_free;
0211     desc->ops.alloc = partition_domain_alloc;
0212 
0213     d = irq_domain_create_linear(fwnode, nr_parts, &desc->ops, desc);
0214     if (!d)
0215         goto out;
0216     desc->domain = d;
0217 
0218     desc->bitmap = bitmap_zalloc(nr_parts, GFP_KERNEL);
0219     if (WARN_ON(!desc->bitmap))
0220         goto out;
0221 
0222     desc->chained_desc = irq_to_desc(chained_irq);
0223     desc->nr_parts = nr_parts;
0224     desc->parts = parts;
0225 
0226     return desc;
0227 out:
0228     if (d)
0229         irq_domain_remove(d);
0230     kfree(desc);
0231 
0232     return NULL;
0233 }
0234 
0235 struct irq_domain *partition_get_domain(struct partition_desc *dsc)
0236 {
0237     if (dsc)
0238         return dsc->domain;
0239 
0240     return NULL;
0241 }