0001
0002
0003
0004
0005
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
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
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