blob: 495224c743ee7c63ef2e209a928a120f78303376 [file] [log] [blame]
Linus Walleijb4d30532017-01-30 20:28:49 +01001/*
2 * irqchip for the Cortina Systems Gemini Copyright (C) 2017 Linus
3 * Walleij <linus.walleij@linaro.org>
4 *
5 * Based on arch/arm/mach-gemini/irq.c
6 * Copyright (C) 2001-2006 Storlink, Corp.
7 * Copyright (C) 2008-2009 Paulius Zaleckas <paulius.zaleckas@teltonika.lt>
8 */
9#include <linux/bitops.h>
10#include <linux/irq.h>
11#include <linux/io.h>
12#include <linux/irqchip.h>
13#include <linux/irqchip/versatile-fpga.h>
14#include <linux/irqdomain.h>
15#include <linux/module.h>
16#include <linux/of.h>
17#include <linux/of_address.h>
18#include <linux/of_irq.h>
19#include <linux/cpu.h>
20
21#include <asm/exception.h>
22#include <asm/mach/irq.h>
23
24#define GEMINI_NUM_IRQS 32
25
26#define GEMINI_IRQ_SOURCE(base_addr) (base_addr + 0x00)
27#define GEMINI_IRQ_MASK(base_addr) (base_addr + 0x04)
28#define GEMINI_IRQ_CLEAR(base_addr) (base_addr + 0x08)
29#define GEMINI_IRQ_MODE(base_addr) (base_addr + 0x0C)
30#define GEMINI_IRQ_POLARITY(base_addr) (base_addr + 0x10)
31#define GEMINI_IRQ_STATUS(base_addr) (base_addr + 0x14)
32#define GEMINI_FIQ_SOURCE(base_addr) (base_addr + 0x20)
33#define GEMINI_FIQ_MASK(base_addr) (base_addr + 0x24)
34#define GEMINI_FIQ_CLEAR(base_addr) (base_addr + 0x28)
35#define GEMINI_FIQ_MODE(base_addr) (base_addr + 0x2C)
36#define GEMINI_FIQ_POLARITY(base_addr) (base_addr + 0x30)
37#define GEMINI_FIQ_STATUS(base_addr) (base_addr + 0x34)
38
39/**
40 * struct gemini_irq_data - irq data container for the Gemini IRQ controller
41 * @base: memory offset in virtual memory
42 * @chip: chip container for this instance
43 * @domain: IRQ domain for this instance
44 */
45struct gemini_irq_data {
46 void __iomem *base;
47 struct irq_chip chip;
48 struct irq_domain *domain;
49};
50
51static void gemini_irq_mask(struct irq_data *d)
52{
53 struct gemini_irq_data *g = irq_data_get_irq_chip_data(d);
54 unsigned int mask;
55
56 mask = readl(GEMINI_IRQ_MASK(g->base));
57 mask &= ~BIT(irqd_to_hwirq(d));
58 writel(mask, GEMINI_IRQ_MASK(g->base));
59}
60
61static void gemini_irq_unmask(struct irq_data *d)
62{
63 struct gemini_irq_data *g = irq_data_get_irq_chip_data(d);
64 unsigned int mask;
65
66 mask = readl(GEMINI_IRQ_MASK(g->base));
67 mask |= BIT(irqd_to_hwirq(d));
68 writel(mask, GEMINI_IRQ_MASK(g->base));
69}
70
71static void gemini_irq_ack(struct irq_data *d)
72{
73 struct gemini_irq_data *g = irq_data_get_irq_chip_data(d);
74
75 writel(BIT(irqd_to_hwirq(d)), GEMINI_IRQ_CLEAR(g->base));
76}
77
78static int gemini_irq_set_type(struct irq_data *d, unsigned int trigger)
79{
80 struct gemini_irq_data *g = irq_data_get_irq_chip_data(d);
81 int offset = irqd_to_hwirq(d);
82 u32 mode, polarity;
83
84 mode = readl(GEMINI_IRQ_MODE(g->base));
85 polarity = readl(GEMINI_IRQ_POLARITY(g->base));
86
87 if (trigger & (IRQ_TYPE_LEVEL_HIGH)) {
88 irq_set_handler_locked(d, handle_level_irq);
89 /* Disable edge detection */
90 mode &= ~BIT(offset);
91 polarity &= ~BIT(offset);
92 } else if (trigger & IRQ_TYPE_EDGE_RISING) {
93 irq_set_handler_locked(d, handle_edge_irq);
94 mode |= BIT(offset);
95 polarity |= BIT(offset);
96 } else if (trigger & IRQ_TYPE_EDGE_FALLING) {
97 irq_set_handler_locked(d, handle_edge_irq);
98 mode |= BIT(offset);
99 polarity &= ~BIT(offset);
100 } else {
101 irq_set_handler_locked(d, handle_bad_irq);
102 pr_warn("GEMINI IRQ: no supported trigger selected for line %d\n",
103 offset);
104 }
105
106 writel(mode, GEMINI_IRQ_MODE(g->base));
107 writel(polarity, GEMINI_IRQ_POLARITY(g->base));
108
109 return 0;
110}
111
112static struct irq_chip gemini_irq_chip = {
113 .name = "GEMINI",
114 .irq_ack = gemini_irq_ack,
115 .irq_mask = gemini_irq_mask,
116 .irq_unmask = gemini_irq_unmask,
117 .irq_set_type = gemini_irq_set_type,
118};
119
120/* Local static for the IRQ entry call */
121static struct gemini_irq_data girq;
122
123asmlinkage void __exception_irq_entry gemini_irqchip_handle_irq(struct pt_regs *regs)
124{
125 struct gemini_irq_data *g = &girq;
126 int irq;
127 u32 status;
128
129 while ((status = readl(GEMINI_IRQ_STATUS(g->base)))) {
130 irq = ffs(status) - 1;
131 handle_domain_irq(g->domain, irq, regs);
132 }
133}
134
135static int gemini_irqdomain_map(struct irq_domain *d, unsigned int irq,
136 irq_hw_number_t hwirq)
137{
138 struct gemini_irq_data *g = d->host_data;
139
140 irq_set_chip_data(irq, g);
141 /* All IRQs should set up their type, flags as bad by default */
142 irq_set_chip_and_handler(irq, &gemini_irq_chip, handle_bad_irq);
143 irq_set_probe(irq);
144
145 return 0;
146}
147
148static void gemini_irqdomain_unmap(struct irq_domain *d, unsigned int irq)
149{
150 irq_set_chip_and_handler(irq, NULL, NULL);
151 irq_set_chip_data(irq, NULL);
152}
153
154static const struct irq_domain_ops gemini_irqdomain_ops = {
155 .map = gemini_irqdomain_map,
156 .unmap = gemini_irqdomain_unmap,
157 .xlate = irq_domain_xlate_onetwocell,
158};
159
160int __init gemini_of_init_irq(struct device_node *node,
161 struct device_node *parent)
162{
163 struct gemini_irq_data *g = &girq;
164
165 /*
166 * Disable the idle handler by default since it is buggy
167 * For more info see arch/arm/mach-gemini/idle.c
168 */
169 cpu_idle_poll_ctrl(true);
170
171 g->base = of_iomap(node, 0);
172 WARN(!g->base, "unable to map gemini irq registers\n");
173
174 /* Disable all interrupts */
175 writel(0, GEMINI_IRQ_MASK(g->base));
176 writel(0, GEMINI_FIQ_MASK(g->base));
177
178 g->domain = irq_domain_add_simple(node, GEMINI_NUM_IRQS, 0,
179 &gemini_irqdomain_ops, g);
180 set_handle_irq(gemini_irqchip_handle_irq);
181
182 return 0;
183}
184IRQCHIP_DECLARE(gemini, "cortina,gemini-interrupt-controller",
185 gemini_of_init_irq);