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