Janusz Krzysztofik | 11f9562 | 2010-04-28 01:03:59 +0000 | [diff] [blame] | 1 | /* |
| 2 | * Amstrad E3 FIQ handling |
| 3 | * |
| 4 | * Copyright (C) 2009 Janusz Krzysztofik |
| 5 | * Copyright (c) 2006 Matt Callow |
| 6 | * Copyright (c) 2004 Amstrad Plc |
| 7 | * Copyright (C) 2001 RidgeRun, Inc. |
| 8 | * |
| 9 | * Parts of this code are taken from linux/arch/arm/mach-omap/irq.c |
| 10 | * in the MontaVista 2.4 kernel (and the Amstrad changes therein) |
| 11 | * |
| 12 | * This program is free software; you can redistribute it and/or modify it |
| 13 | * under the terms of the GNU General Public License version 2 as published by |
| 14 | * the Free Software Foundation. |
| 15 | */ |
| 16 | #include <linux/gpio.h> |
| 17 | #include <linux/interrupt.h> |
| 18 | #include <linux/irq.h> |
| 19 | #include <linux/module.h> |
| 20 | #include <linux/io.h> |
| 21 | |
Tony Lindgren | e27e35e | 2012-09-19 10:33:43 -0700 | [diff] [blame] | 22 | #include <mach/board-ams-delta.h> |
Janusz Krzysztofik | 11f9562 | 2010-04-28 01:03:59 +0000 | [diff] [blame] | 23 | |
| 24 | #include <asm/fiq.h> |
Tony Lindgren | 2e3ee9f | 2012-02-24 10:34:34 -0800 | [diff] [blame] | 25 | |
Janusz Krzysztofik | 11f9562 | 2010-04-28 01:03:59 +0000 | [diff] [blame] | 26 | #include <mach/ams-delta-fiq.h> |
| 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 |
| 37 | * keystrokes received from the qwerty keyboard. |
| 38 | * See ams-delta-fiq.h for details of offsets. |
| 39 | */ |
| 40 | unsigned int fiq_buffer[1024]; |
| 41 | EXPORT_SYMBOL(fiq_buffer); |
| 42 | |
| 43 | static unsigned int irq_counter[16]; |
| 44 | |
| 45 | static irqreturn_t deferred_fiq(int irq, void *dev_id) |
| 46 | { |
| 47 | struct irq_desc *irq_desc; |
| 48 | struct irq_chip *irq_chip = NULL; |
| 49 | int gpio, irq_num, fiq_count; |
| 50 | |
Janusz Krzysztofik | b6392bd | 2012-04-30 19:26:09 +0200 | [diff] [blame] | 51 | irq_desc = irq_to_desc(gpio_to_irq(AMS_DELTA_GPIO_PIN_KEYBRD_CLK)); |
Janusz Krzysztofik | 11f9562 | 2010-04-28 01:03:59 +0000 | [diff] [blame] | 52 | if (irq_desc) |
Lennert Buytenhek | a51eef7 | 2010-11-29 10:39:27 +0100 | [diff] [blame] | 53 | irq_chip = irq_desc->irq_data.chip; |
Janusz Krzysztofik | 11f9562 | 2010-04-28 01:03:59 +0000 | [diff] [blame] | 54 | |
| 55 | /* |
| 56 | * For each handled GPIO interrupt, keep calling its interrupt handler |
| 57 | * until the IRQ counter catches the FIQ incremented interrupt counter. |
| 58 | */ |
| 59 | for (gpio = AMS_DELTA_GPIO_PIN_KEYBRD_CLK; |
| 60 | gpio <= AMS_DELTA_GPIO_PIN_HOOK_SWITCH; gpio++) { |
| 61 | irq_num = gpio_to_irq(gpio); |
| 62 | fiq_count = fiq_buffer[FIQ_CNT_INT_00 + gpio]; |
| 63 | |
| 64 | while (irq_counter[gpio] < fiq_count) { |
| 65 | if (gpio != AMS_DELTA_GPIO_PIN_KEYBRD_CLK) { |
Lennert Buytenhek | a51eef7 | 2010-11-29 10:39:27 +0100 | [diff] [blame] | 66 | struct irq_data *d = irq_get_irq_data(irq_num); |
| 67 | |
Janusz Krzysztofik | 11f9562 | 2010-04-28 01:03:59 +0000 | [diff] [blame] | 68 | /* |
| 69 | * It looks like handle_edge_irq() that |
| 70 | * OMAP GPIO edge interrupts default to, |
| 71 | * expects interrupt already unmasked. |
| 72 | */ |
Lennert Buytenhek | a51eef7 | 2010-11-29 10:39:27 +0100 | [diff] [blame] | 73 | if (irq_chip && irq_chip->irq_unmask) |
| 74 | irq_chip->irq_unmask(d); |
Janusz Krzysztofik | 11f9562 | 2010-04-28 01:03:59 +0000 | [diff] [blame] | 75 | } |
| 76 | generic_handle_irq(irq_num); |
| 77 | |
| 78 | irq_counter[gpio]++; |
| 79 | } |
| 80 | } |
| 81 | return IRQ_HANDLED; |
| 82 | } |
| 83 | |
| 84 | void __init ams_delta_init_fiq(void) |
| 85 | { |
| 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 | |
| 92 | fiqhandler_start = &qwerty_fiqin_start; |
| 93 | fiqhandler_length = &qwerty_fiqin_end - &qwerty_fiqin_start; |
| 94 | pr_info("Installing fiq handler from %p, length 0x%x\n", |
| 95 | fiqhandler_start, fiqhandler_length); |
| 96 | |
| 97 | retval = claim_fiq(&fh); |
| 98 | if (retval) { |
| 99 | pr_err("ams_delta_init_fiq(): couldn't claim FIQ, ret=%d\n", |
| 100 | retval); |
| 101 | return; |
| 102 | } |
| 103 | |
| 104 | retval = request_irq(INT_DEFERRED_FIQ, deferred_fiq, |
Paul Walmsley | a7022d6 | 2012-04-13 06:34:28 -0600 | [diff] [blame] | 105 | IRQ_TYPE_EDGE_RISING, "deferred_fiq", NULL); |
Janusz Krzysztofik | 11f9562 | 2010-04-28 01:03:59 +0000 | [diff] [blame] | 106 | if (retval < 0) { |
| 107 | pr_err("Failed to get deferred_fiq IRQ, ret=%d\n", retval); |
| 108 | release_fiq(&fh); |
| 109 | return; |
| 110 | } |
| 111 | /* |
| 112 | * Since no set_type() method is provided by OMAP irq chip, |
| 113 | * switch to edge triggered interrupt type manually. |
| 114 | */ |
| 115 | offset = IRQ_ILR0_REG_OFFSET + INT_DEFERRED_FIQ * 0x4; |
| 116 | val = omap_readl(DEFERRED_FIQ_IH_BASE + offset) & ~(1 << 1); |
| 117 | omap_writel(val, DEFERRED_FIQ_IH_BASE + offset); |
| 118 | |
| 119 | set_fiq_handler(fiqhandler_start, fiqhandler_length); |
| 120 | |
| 121 | /* |
| 122 | * Initialise the buffer which is shared |
| 123 | * between FIQ mode and IRQ mode |
| 124 | */ |
| 125 | fiq_buffer[FIQ_GPIO_INT_MASK] = 0; |
| 126 | fiq_buffer[FIQ_MASK] = 0; |
| 127 | fiq_buffer[FIQ_STATE] = 0; |
| 128 | fiq_buffer[FIQ_KEY] = 0; |
| 129 | fiq_buffer[FIQ_KEYS_CNT] = 0; |
| 130 | fiq_buffer[FIQ_KEYS_HICNT] = 0; |
| 131 | fiq_buffer[FIQ_TAIL_OFFSET] = 0; |
| 132 | fiq_buffer[FIQ_HEAD_OFFSET] = 0; |
| 133 | fiq_buffer[FIQ_BUF_LEN] = 256; |
| 134 | fiq_buffer[FIQ_MISSED_KEYS] = 0; |
| 135 | fiq_buffer[FIQ_BUFFER_START] = |
| 136 | (unsigned int) &fiq_buffer[FIQ_CIRC_BUFF]; |
| 137 | |
| 138 | for (i = FIQ_CNT_INT_00; i <= FIQ_CNT_INT_15; i++) |
| 139 | fiq_buffer[i] = 0; |
| 140 | |
| 141 | /* |
| 142 | * FIQ mode r9 always points to the fiq_buffer, becauses the FIQ isr |
| 143 | * will run in an unpredictable context. The fiq_buffer is the FIQ isr's |
| 144 | * only means of communication with the IRQ level and other kernel |
| 145 | * context code. |
| 146 | */ |
| 147 | FIQ_regs.ARM_r9 = (unsigned int)fiq_buffer; |
| 148 | set_fiq_regs(&FIQ_regs); |
| 149 | |
| 150 | pr_info("request_fiq(): fiq_buffer = %p\n", fiq_buffer); |
| 151 | |
| 152 | /* |
| 153 | * Redirect GPIO interrupts to FIQ |
| 154 | */ |
| 155 | offset = IRQ_ILR0_REG_OFFSET + INT_GPIO_BANK1 * 0x4; |
| 156 | val = omap_readl(OMAP_IH1_BASE + offset) | 1; |
| 157 | omap_writel(val, OMAP_IH1_BASE + offset); |
| 158 | } |