Back to home page

OSCL-LXR

 
 

    


0001 /*
0002  * J-Core SoC AIC driver
0003  *
0004  * Copyright (C) 2015-2016 Smart Energy Instruments, Inc.
0005  *
0006  * This file is subject to the terms and conditions of the GNU General Public
0007  * License.  See the file "COPYING" in the main directory of this archive
0008  * for more details.
0009  */
0010 
0011 #include <linux/irq.h>
0012 #include <linux/io.h>
0013 #include <linux/irqchip.h>
0014 #include <linux/irqdomain.h>
0015 #include <linux/cpu.h>
0016 #include <linux/of.h>
0017 #include <linux/of_address.h>
0018 #include <linux/of_irq.h>
0019 
0020 #define JCORE_AIC_MAX_HWIRQ 127
0021 #define JCORE_AIC1_MIN_HWIRQ    16
0022 #define JCORE_AIC2_MIN_HWIRQ    64
0023 
0024 #define JCORE_AIC1_INTPRI_REG   8
0025 
0026 static struct irq_chip jcore_aic;
0027 
0028 /*
0029  * The J-Core AIC1 and AIC2 are cpu-local interrupt controllers and do
0030  * not distinguish or use distinct irq number ranges for per-cpu event
0031  * interrupts (timer, IPI). Since information to determine whether a
0032  * particular irq number should be treated as per-cpu is not available
0033  * at mapping time, we use a wrapper handler function which chooses
0034  * the right handler at runtime based on whether IRQF_PERCPU was used
0035  * when requesting the irq.
0036  */
0037 
0038 static void handle_jcore_irq(struct irq_desc *desc)
0039 {
0040     if (irqd_is_per_cpu(irq_desc_get_irq_data(desc)))
0041         handle_percpu_irq(desc);
0042     else
0043         handle_simple_irq(desc);
0044 }
0045 
0046 static int jcore_aic_irqdomain_map(struct irq_domain *d, unsigned int irq,
0047                    irq_hw_number_t hwirq)
0048 {
0049     struct irq_chip *aic = d->host_data;
0050 
0051     irq_set_chip_and_handler(irq, aic, handle_jcore_irq);
0052 
0053     return 0;
0054 }
0055 
0056 static const struct irq_domain_ops jcore_aic_irqdomain_ops = {
0057     .map = jcore_aic_irqdomain_map,
0058     .xlate = irq_domain_xlate_onecell,
0059 };
0060 
0061 static void noop(struct irq_data *data)
0062 {
0063 }
0064 
0065 static int __init aic_irq_of_init(struct device_node *node,
0066                   struct device_node *parent)
0067 {
0068     unsigned min_irq = JCORE_AIC2_MIN_HWIRQ;
0069     unsigned dom_sz = JCORE_AIC_MAX_HWIRQ+1;
0070     struct irq_domain *domain;
0071 
0072     pr_info("Initializing J-Core AIC\n");
0073 
0074     /* AIC1 needs priority initialization to receive interrupts. */
0075     if (of_device_is_compatible(node, "jcore,aic1")) {
0076         unsigned cpu;
0077 
0078         for_each_present_cpu(cpu) {
0079             void __iomem *base = of_iomap(node, cpu);
0080 
0081             if (!base) {
0082                 pr_err("Unable to map AIC for cpu %u\n", cpu);
0083                 return -ENOMEM;
0084             }
0085             __raw_writel(0xffffffff, base + JCORE_AIC1_INTPRI_REG);
0086             iounmap(base);
0087         }
0088         min_irq = JCORE_AIC1_MIN_HWIRQ;
0089     }
0090 
0091     /*
0092      * The irq chip framework requires either mask/unmask or enable/disable
0093      * function pointers to be provided, but the hardware does not have any
0094      * such mechanism; the only interrupt masking is at the cpu level and
0095      * it affects all interrupts. We provide dummy mask/unmask. The hardware
0096      * handles all interrupt control and clears pending status when the cpu
0097      * accepts the interrupt.
0098      */
0099     jcore_aic.irq_mask = noop;
0100     jcore_aic.irq_unmask = noop;
0101     jcore_aic.name = "AIC";
0102 
0103     domain = irq_domain_add_legacy(node, dom_sz - min_irq, min_irq, min_irq,
0104                        &jcore_aic_irqdomain_ops,
0105                        &jcore_aic);
0106     if (!domain)
0107         return -ENOMEM;
0108 
0109     return 0;
0110 }
0111 
0112 IRQCHIP_DECLARE(jcore_aic2, "jcore,aic2", aic_irq_of_init);
0113 IRQCHIP_DECLARE(jcore_aic1, "jcore,aic1", aic_irq_of_init);