Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0-or-later
0002 /*
0003  *  CLPS711X IRQ driver
0004  *
0005  *  Copyright (C) 2013 Alexander Shiyan <shc_work@mail.ru>
0006  */
0007 
0008 #include <linux/io.h>
0009 #include <linux/irq.h>
0010 #include <linux/irqchip.h>
0011 #include <linux/irqdomain.h>
0012 #include <linux/of_address.h>
0013 #include <linux/of_irq.h>
0014 #include <linux/slab.h>
0015 
0016 #include <asm/exception.h>
0017 #include <asm/mach/irq.h>
0018 
0019 #define CLPS711X_INTSR1 (0x0240)
0020 #define CLPS711X_INTMR1 (0x0280)
0021 #define CLPS711X_BLEOI  (0x0600)
0022 #define CLPS711X_MCEOI  (0x0640)
0023 #define CLPS711X_TEOI   (0x0680)
0024 #define CLPS711X_TC1EOI (0x06c0)
0025 #define CLPS711X_TC2EOI (0x0700)
0026 #define CLPS711X_RTCEOI (0x0740)
0027 #define CLPS711X_UMSEOI (0x0780)
0028 #define CLPS711X_COEOI  (0x07c0)
0029 #define CLPS711X_INTSR2 (0x1240)
0030 #define CLPS711X_INTMR2 (0x1280)
0031 #define CLPS711X_SRXEOF (0x1600)
0032 #define CLPS711X_KBDEOI (0x1700)
0033 #define CLPS711X_INTSR3 (0x2240)
0034 #define CLPS711X_INTMR3 (0x2280)
0035 
0036 static const struct {
0037 #define CLPS711X_FLAG_EN    (1 << 0)
0038 #define CLPS711X_FLAG_FIQ   (1 << 1)
0039     unsigned int    flags;
0040     phys_addr_t eoi;
0041 } clps711x_irqs[] = {
0042     [1] = { CLPS711X_FLAG_FIQ, CLPS711X_BLEOI, },
0043     [3] = { CLPS711X_FLAG_FIQ, CLPS711X_MCEOI, },
0044     [4] = { CLPS711X_FLAG_EN, CLPS711X_COEOI, },
0045     [5] = { CLPS711X_FLAG_EN, },
0046     [6] = { CLPS711X_FLAG_EN, },
0047     [7] = { CLPS711X_FLAG_EN, },
0048     [8] = { CLPS711X_FLAG_EN, CLPS711X_TC1EOI, },
0049     [9] = { CLPS711X_FLAG_EN, CLPS711X_TC2EOI, },
0050     [10]    = { CLPS711X_FLAG_EN, CLPS711X_RTCEOI, },
0051     [11]    = { CLPS711X_FLAG_EN, CLPS711X_TEOI, },
0052     [12]    = { CLPS711X_FLAG_EN, },
0053     [13]    = { CLPS711X_FLAG_EN, },
0054     [14]    = { CLPS711X_FLAG_EN, CLPS711X_UMSEOI, },
0055     [15]    = { CLPS711X_FLAG_EN, CLPS711X_SRXEOF, },
0056     [16]    = { CLPS711X_FLAG_EN, CLPS711X_KBDEOI, },
0057     [17]    = { CLPS711X_FLAG_EN, },
0058     [18]    = { CLPS711X_FLAG_EN, },
0059     [28]    = { CLPS711X_FLAG_EN, },
0060     [29]    = { CLPS711X_FLAG_EN, },
0061     [32]    = { CLPS711X_FLAG_FIQ, },
0062 };
0063 
0064 static struct {
0065     void __iomem        *base;
0066     void __iomem        *intmr[3];
0067     void __iomem        *intsr[3];
0068     struct irq_domain   *domain;
0069     struct irq_domain_ops   ops;
0070 } *clps711x_intc;
0071 
0072 static asmlinkage void __exception_irq_entry clps711x_irqh(struct pt_regs *regs)
0073 {
0074     u32 irqstat;
0075 
0076     do {
0077         irqstat = readw_relaxed(clps711x_intc->intmr[0]) &
0078               readw_relaxed(clps711x_intc->intsr[0]);
0079         if (irqstat)
0080             generic_handle_domain_irq(clps711x_intc->domain,
0081                           fls(irqstat) - 1);
0082 
0083         irqstat = readw_relaxed(clps711x_intc->intmr[1]) &
0084               readw_relaxed(clps711x_intc->intsr[1]);
0085         if (irqstat)
0086             generic_handle_domain_irq(clps711x_intc->domain,
0087                           fls(irqstat) - 1 + 16);
0088     } while (irqstat);
0089 }
0090 
0091 static void clps711x_intc_eoi(struct irq_data *d)
0092 {
0093     irq_hw_number_t hwirq = irqd_to_hwirq(d);
0094 
0095     writel_relaxed(0, clps711x_intc->base + clps711x_irqs[hwirq].eoi);
0096 }
0097 
0098 static void clps711x_intc_mask(struct irq_data *d)
0099 {
0100     irq_hw_number_t hwirq = irqd_to_hwirq(d);
0101     void __iomem *intmr = clps711x_intc->intmr[hwirq / 16];
0102     u32 tmp;
0103 
0104     tmp = readl_relaxed(intmr);
0105     tmp &= ~(1 << (hwirq % 16));
0106     writel_relaxed(tmp, intmr);
0107 }
0108 
0109 static void clps711x_intc_unmask(struct irq_data *d)
0110 {
0111     irq_hw_number_t hwirq = irqd_to_hwirq(d);
0112     void __iomem *intmr = clps711x_intc->intmr[hwirq / 16];
0113     u32 tmp;
0114 
0115     tmp = readl_relaxed(intmr);
0116     tmp |= 1 << (hwirq % 16);
0117     writel_relaxed(tmp, intmr);
0118 }
0119 
0120 static struct irq_chip clps711x_intc_chip = {
0121     .name       = "clps711x-intc",
0122     .irq_eoi    = clps711x_intc_eoi,
0123     .irq_mask   = clps711x_intc_mask,
0124     .irq_unmask = clps711x_intc_unmask,
0125 };
0126 
0127 static int __init clps711x_intc_irq_map(struct irq_domain *h, unsigned int virq,
0128                     irq_hw_number_t hw)
0129 {
0130     irq_flow_handler_t handler = handle_level_irq;
0131     unsigned int flags = 0;
0132 
0133     if (!clps711x_irqs[hw].flags)
0134         return 0;
0135 
0136     if (clps711x_irqs[hw].flags & CLPS711X_FLAG_FIQ) {
0137         handler = handle_bad_irq;
0138         flags |= IRQ_NOAUTOEN;
0139     } else if (clps711x_irqs[hw].eoi) {
0140         handler = handle_fasteoi_irq;
0141     }
0142 
0143     /* Clear down pending interrupt */
0144     if (clps711x_irqs[hw].eoi)
0145         writel_relaxed(0, clps711x_intc->base + clps711x_irqs[hw].eoi);
0146 
0147     irq_set_chip_and_handler(virq, &clps711x_intc_chip, handler);
0148     irq_modify_status(virq, IRQ_NOPROBE, flags);
0149 
0150     return 0;
0151 }
0152 
0153 static int __init _clps711x_intc_init(struct device_node *np,
0154                       phys_addr_t base, resource_size_t size)
0155 {
0156     int err;
0157 
0158     clps711x_intc = kzalloc(sizeof(*clps711x_intc), GFP_KERNEL);
0159     if (!clps711x_intc)
0160         return -ENOMEM;
0161 
0162     clps711x_intc->base = ioremap(base, size);
0163     if (!clps711x_intc->base) {
0164         err = -ENOMEM;
0165         goto out_kfree;
0166     }
0167 
0168     clps711x_intc->intsr[0] = clps711x_intc->base + CLPS711X_INTSR1;
0169     clps711x_intc->intmr[0] = clps711x_intc->base + CLPS711X_INTMR1;
0170     clps711x_intc->intsr[1] = clps711x_intc->base + CLPS711X_INTSR2;
0171     clps711x_intc->intmr[1] = clps711x_intc->base + CLPS711X_INTMR2;
0172     clps711x_intc->intsr[2] = clps711x_intc->base + CLPS711X_INTSR3;
0173     clps711x_intc->intmr[2] = clps711x_intc->base + CLPS711X_INTMR3;
0174 
0175     /* Mask all interrupts */
0176     writel_relaxed(0, clps711x_intc->intmr[0]);
0177     writel_relaxed(0, clps711x_intc->intmr[1]);
0178     writel_relaxed(0, clps711x_intc->intmr[2]);
0179 
0180     err = irq_alloc_descs(-1, 0, ARRAY_SIZE(clps711x_irqs), numa_node_id());
0181     if (err < 0)
0182         goto out_iounmap;
0183 
0184     clps711x_intc->ops.map = clps711x_intc_irq_map;
0185     clps711x_intc->ops.xlate = irq_domain_xlate_onecell;
0186     clps711x_intc->domain =
0187         irq_domain_add_legacy(np, ARRAY_SIZE(clps711x_irqs),
0188                       0, 0, &clps711x_intc->ops, NULL);
0189     if (!clps711x_intc->domain) {
0190         err = -ENOMEM;
0191         goto out_irqfree;
0192     }
0193 
0194     irq_set_default_host(clps711x_intc->domain);
0195     set_handle_irq(clps711x_irqh);
0196 
0197 #ifdef CONFIG_FIQ
0198     init_FIQ(0);
0199 #endif
0200 
0201     return 0;
0202 
0203 out_irqfree:
0204     irq_free_descs(0, ARRAY_SIZE(clps711x_irqs));
0205 
0206 out_iounmap:
0207     iounmap(clps711x_intc->base);
0208 
0209 out_kfree:
0210     kfree(clps711x_intc);
0211 
0212     return err;
0213 }
0214 
0215 void __init clps711x_intc_init(phys_addr_t base, resource_size_t size)
0216 {
0217     BUG_ON(_clps711x_intc_init(NULL, base, size));
0218 }
0219 
0220 #ifdef CONFIG_IRQCHIP
0221 static int __init clps711x_intc_init_dt(struct device_node *np,
0222                     struct device_node *parent)
0223 {
0224     struct resource res;
0225     int err;
0226 
0227     err = of_address_to_resource(np, 0, &res);
0228     if (err)
0229         return err;
0230 
0231     return _clps711x_intc_init(np, res.start, resource_size(&res));
0232 }
0233 IRQCHIP_DECLARE(clps711x, "cirrus,ep7209-intc", clps711x_intc_init_dt);
0234 #endif