1 | // SPDX-License-Identifier: GPL-2.0-only |
2 | /* |
3 | * Amstrad E3 FIQ handling |
4 | * |
5 | * Copyright (C) 2009 Janusz Krzysztofik |
6 | * Copyright (c) 2006 Matt Callow |
7 | * Copyright (c) 2004 Amstrad Plc |
8 | * Copyright (C) 2001 RidgeRun, Inc. |
9 | * |
10 | * Parts of this code are taken from linux/arch/arm/mach-omap/irq.c |
11 | * in the MontaVista 2.4 kernel (and the Amstrad changes therein) |
12 | */ |
13 | #include <linux/gpio/consumer.h> |
14 | #include <linux/gpio/machine.h> |
15 | #include <linux/gpio/driver.h> |
16 | #include <linux/interrupt.h> |
17 | #include <linux/irq.h> |
18 | #include <linux/module.h> |
19 | #include <linux/io.h> |
20 | #include <linux/platform_data/ams-delta-fiq.h> |
21 | #include <linux/platform_device.h> |
22 | |
23 | #include <asm/fiq.h> |
24 | #include <linux/soc/ti/omap1-io.h> |
25 | |
26 | #include "hardware.h" |
27 | #include "ams-delta-fiq.h" |
28 | #include "board-ams-delta.h" |
29 | |
30 | static struct fiq_handler fh = { |
31 | .name = "ams-delta-fiq" |
32 | }; |
33 | |
34 | /* |
35 | * This buffer is shared between FIQ and IRQ contexts. |
36 | * The FIQ and IRQ isrs can both read and write it. |
37 | * It is structured as a header section several 32bit slots, |
38 | * followed by the circular buffer where the FIQ isr stores |
39 | * keystrokes received from the qwerty keyboard. See |
40 | * <linux/platform_data/ams-delta-fiq.h> for details of offsets. |
41 | */ |
42 | static unsigned int fiq_buffer[1024]; |
43 | |
44 | static struct irq_chip *irq_chip; |
45 | static struct irq_data *irq_data[16]; |
46 | static unsigned int irq_counter[16]; |
47 | |
48 | static const char *pin_name[16] __initconst = { |
49 | [AMS_DELTA_GPIO_PIN_KEYBRD_DATA] = "keybrd_data" , |
50 | [AMS_DELTA_GPIO_PIN_KEYBRD_CLK] = "keybrd_clk" , |
51 | }; |
52 | |
53 | static irqreturn_t deferred_fiq(int irq, void *dev_id) |
54 | { |
55 | struct irq_data *d; |
56 | int gpio, irq_num, fiq_count; |
57 | |
58 | /* |
59 | * For each handled GPIO interrupt, keep calling its interrupt handler |
60 | * until the IRQ counter catches the FIQ incremented interrupt counter. |
61 | */ |
62 | for (gpio = AMS_DELTA_GPIO_PIN_KEYBRD_CLK; |
63 | gpio <= AMS_DELTA_GPIO_PIN_HOOK_SWITCH; gpio++) { |
64 | d = irq_data[gpio]; |
65 | irq_num = d->irq; |
66 | fiq_count = fiq_buffer[FIQ_CNT_INT_00 + gpio]; |
67 | |
68 | if (irq_counter[gpio] < fiq_count && |
69 | gpio != AMS_DELTA_GPIO_PIN_KEYBRD_CLK) { |
70 | /* |
71 | * handle_simple_irq() that OMAP GPIO edge |
72 | * interrupts default to since commit 80ac93c27441 |
73 | * requires interrupt already acked and unmasked. |
74 | */ |
75 | if (!WARN_ON_ONCE(!irq_chip->irq_unmask)) |
76 | irq_chip->irq_unmask(d); |
77 | } |
78 | for (; irq_counter[gpio] < fiq_count; irq_counter[gpio]++) |
79 | generic_handle_irq(irq: irq_num); |
80 | } |
81 | return IRQ_HANDLED; |
82 | } |
83 | |
84 | void __init ams_delta_init_fiq(struct gpio_chip *chip, |
85 | struct platform_device *serio) |
86 | { |
87 | struct gpio_desc *gpiod, *data = NULL, *clk = NULL; |
88 | void *fiqhandler_start; |
89 | unsigned int fiqhandler_length; |
90 | struct pt_regs FIQ_regs; |
91 | unsigned long val, offset; |
92 | int i, retval; |
93 | |
94 | /* Store irq_chip location for IRQ handler use */ |
95 | irq_chip = chip->irq.chip; |
96 | if (!irq_chip) { |
97 | pr_err("%s: GPIO chip %s is missing IRQ function\n" , __func__, |
98 | chip->label); |
99 | return; |
100 | } |
101 | |
102 | for (i = 0; i < ARRAY_SIZE(irq_data); i++) { |
103 | gpiod = gpiochip_request_own_desc(gc: chip, hwnum: i, label: pin_name[i], |
104 | lflags: GPIO_ACTIVE_HIGH, dflags: GPIOD_IN); |
105 | if (IS_ERR(ptr: gpiod)) { |
106 | pr_err("%s: failed to get GPIO pin %d (%ld)\n" , |
107 | __func__, i, PTR_ERR(gpiod)); |
108 | return; |
109 | } |
110 | /* Store irq_data location for IRQ handler use */ |
111 | irq_data[i] = irq_get_irq_data(irq: gpiod_to_irq(desc: gpiod)); |
112 | |
113 | /* |
114 | * FIQ handler takes full control over serio data and clk GPIO |
115 | * pins. Initialize them and keep requested so nobody can |
116 | * interfere. Fail if any of those two couldn't be requested. |
117 | */ |
118 | switch (i) { |
119 | case AMS_DELTA_GPIO_PIN_KEYBRD_DATA: |
120 | data = gpiod; |
121 | gpiod_direction_input(desc: data); |
122 | break; |
123 | case AMS_DELTA_GPIO_PIN_KEYBRD_CLK: |
124 | clk = gpiod; |
125 | gpiod_direction_input(desc: clk); |
126 | break; |
127 | default: |
128 | gpiochip_free_own_desc(desc: gpiod); |
129 | break; |
130 | } |
131 | } |
132 | if (!data || !clk) |
133 | goto out_gpio; |
134 | |
135 | fiqhandler_start = &qwerty_fiqin_start; |
136 | fiqhandler_length = &qwerty_fiqin_end - &qwerty_fiqin_start; |
137 | pr_info("Installing fiq handler from %p, length 0x%x\n" , |
138 | fiqhandler_start, fiqhandler_length); |
139 | |
140 | retval = claim_fiq(&fh); |
141 | if (retval) { |
142 | pr_err("ams_delta_init_fiq(): couldn't claim FIQ, ret=%d\n" , |
143 | retval); |
144 | goto out_gpio; |
145 | } |
146 | |
147 | retval = request_irq(INT_DEFERRED_FIQ, handler: deferred_fiq, |
148 | flags: IRQ_TYPE_EDGE_RISING, name: "deferred_fiq" , NULL); |
149 | if (retval < 0) { |
150 | pr_err("Failed to get deferred_fiq IRQ, ret=%d\n" , retval); |
151 | release_fiq(&fh); |
152 | goto out_gpio; |
153 | } |
154 | /* |
155 | * Since no set_type() method is provided by OMAP irq chip, |
156 | * switch to edge triggered interrupt type manually. |
157 | */ |
158 | offset = IRQ_ILR0_REG_OFFSET + |
159 | ((INT_DEFERRED_FIQ - NR_IRQS_LEGACY) & 0x1f) * 0x4; |
160 | val = omap_readl(DEFERRED_FIQ_IH_BASE + offset) & ~(1 << 1); |
161 | omap_writel(v: val, DEFERRED_FIQ_IH_BASE + offset); |
162 | |
163 | set_fiq_handler(fiqhandler_start, fiqhandler_length); |
164 | |
165 | /* |
166 | * Initialise the buffer which is shared |
167 | * between FIQ mode and IRQ mode |
168 | */ |
169 | fiq_buffer[FIQ_GPIO_INT_MASK] = 0; |
170 | fiq_buffer[FIQ_MASK] = 0; |
171 | fiq_buffer[FIQ_STATE] = 0; |
172 | fiq_buffer[FIQ_KEY] = 0; |
173 | fiq_buffer[FIQ_KEYS_CNT] = 0; |
174 | fiq_buffer[FIQ_KEYS_HICNT] = 0; |
175 | fiq_buffer[FIQ_TAIL_OFFSET] = 0; |
176 | fiq_buffer[FIQ_HEAD_OFFSET] = 0; |
177 | fiq_buffer[FIQ_BUF_LEN] = 256; |
178 | fiq_buffer[FIQ_MISSED_KEYS] = 0; |
179 | fiq_buffer[FIQ_BUFFER_START] = |
180 | (unsigned int) &fiq_buffer[FIQ_CIRC_BUFF]; |
181 | |
182 | for (i = FIQ_CNT_INT_00; i <= FIQ_CNT_INT_15; i++) |
183 | fiq_buffer[i] = 0; |
184 | |
185 | /* |
186 | * FIQ mode r9 always points to the fiq_buffer, because the FIQ isr |
187 | * will run in an unpredictable context. The fiq_buffer is the FIQ isr's |
188 | * only means of communication with the IRQ level and other kernel |
189 | * context code. |
190 | */ |
191 | FIQ_regs.ARM_r9 = (unsigned int)fiq_buffer; |
192 | set_fiq_regs(&FIQ_regs); |
193 | |
194 | pr_info("request_fiq(): fiq_buffer = %p\n" , fiq_buffer); |
195 | |
196 | /* |
197 | * Redirect GPIO interrupts to FIQ |
198 | */ |
199 | offset = IRQ_ILR0_REG_OFFSET + (INT_GPIO_BANK1 - NR_IRQS_LEGACY) * 0x4; |
200 | val = omap_readl(pa: OMAP_IH1_BASE + offset) | 1; |
201 | omap_writel(v: val, pa: OMAP_IH1_BASE + offset); |
202 | |
203 | /* Initialize serio device IRQ resource and platform_data */ |
204 | serio->resource[0].start = gpiod_to_irq(desc: clk); |
205 | serio->resource[0].end = serio->resource[0].start; |
206 | serio->dev.platform_data = fiq_buffer; |
207 | |
208 | /* |
209 | * Since FIQ handler performs handling of GPIO registers for |
210 | * "keybrd_clk" IRQ pin, ams_delta_serio driver used to set |
211 | * handle_simple_irq() as active IRQ handler for that pin to avoid |
212 | * bad interaction with gpio-omap driver. This is no longer needed |
213 | * as handle_simple_irq() is now the default handler for OMAP GPIO |
214 | * edge interrupts. |
215 | * This comment replaces the obsolete code which has been removed |
216 | * from the ams_delta_serio driver and stands here only as a reminder |
217 | * of that dependency on gpio-omap driver behavior. |
218 | */ |
219 | |
220 | return; |
221 | |
222 | out_gpio: |
223 | if (data) |
224 | gpiochip_free_own_desc(desc: data); |
225 | if (clk) |
226 | gpiochip_free_own_desc(desc: clk); |
227 | } |
228 | |