1/* 2 * linux/arch/arm/mach-omap1/fpga.c 3 * 4 * Interrupt handler for OMAP-1510 Innovator FPGA 5 * 6 * Copyright (C) 2001 RidgeRun, Inc. 7 * Author: Greg Lonnon <glonnon@ridgerun.com> 8 * 9 * Copyright (C) 2002 MontaVista Software, Inc. 10 * 11 * Separated FPGA interrupts from innovator1510.c and cleaned up for 2.6 12 * Copyright (C) 2004 Nokia Corporation by Tony Lindrgen <tony@atomide.com> 13 * 14 * This program is free software; you can redistribute it and/or modify 15 * it under the terms of the GNU General Public License version 2 as 16 * published by the Free Software Foundation. 17 */ 18 19#include <linux/types.h> 20#include <linux/gpio.h> 21#include <linux/init.h> 22#include <linux/kernel.h> 23#include <linux/device.h> 24#include <linux/errno.h> 25#include <linux/io.h> 26 27#include <asm/irq.h> 28#include <asm/mach/irq.h> 29 30#include <mach/hardware.h> 31 32#include "iomap.h" 33#include "common.h" 34#include "fpga.h" 35 36static void fpga_mask_irq(struct irq_data *d) 37{ 38 unsigned int irq = d->irq - OMAP_FPGA_IRQ_BASE; 39 40 if (irq < 8) 41 __raw_writeb((__raw_readb(OMAP1510_FPGA_IMR_LO) 42 & ~(1 << irq)), OMAP1510_FPGA_IMR_LO); 43 else if (irq < 16) 44 __raw_writeb((__raw_readb(OMAP1510_FPGA_IMR_HI) 45 & ~(1 << (irq - 8))), OMAP1510_FPGA_IMR_HI); 46 else 47 __raw_writeb((__raw_readb(INNOVATOR_FPGA_IMR2) 48 & ~(1 << (irq - 16))), INNOVATOR_FPGA_IMR2); 49} 50 51 52static inline u32 get_fpga_unmasked_irqs(void) 53{ 54 return 55 ((__raw_readb(OMAP1510_FPGA_ISR_LO) & 56 __raw_readb(OMAP1510_FPGA_IMR_LO))) | 57 ((__raw_readb(OMAP1510_FPGA_ISR_HI) & 58 __raw_readb(OMAP1510_FPGA_IMR_HI)) << 8) | 59 ((__raw_readb(INNOVATOR_FPGA_ISR2) & 60 __raw_readb(INNOVATOR_FPGA_IMR2)) << 16); 61} 62 63 64static void fpga_ack_irq(struct irq_data *d) 65{ 66 /* Don't need to explicitly ACK FPGA interrupts */ 67} 68 69static void fpga_unmask_irq(struct irq_data *d) 70{ 71 unsigned int irq = d->irq - OMAP_FPGA_IRQ_BASE; 72 73 if (irq < 8) 74 __raw_writeb((__raw_readb(OMAP1510_FPGA_IMR_LO) | (1 << irq)), 75 OMAP1510_FPGA_IMR_LO); 76 else if (irq < 16) 77 __raw_writeb((__raw_readb(OMAP1510_FPGA_IMR_HI) 78 | (1 << (irq - 8))), OMAP1510_FPGA_IMR_HI); 79 else 80 __raw_writeb((__raw_readb(INNOVATOR_FPGA_IMR2) 81 | (1 << (irq - 16))), INNOVATOR_FPGA_IMR2); 82} 83 84static void fpga_mask_ack_irq(struct irq_data *d) 85{ 86 fpga_mask_irq(d); 87 fpga_ack_irq(d); 88} 89 90static void innovator_fpga_IRQ_demux(unsigned int irq, struct irq_desc *desc) 91{ 92 u32 stat; 93 int fpga_irq; 94 95 stat = get_fpga_unmasked_irqs(); 96 97 if (!stat) 98 return; 99 100 for (fpga_irq = OMAP_FPGA_IRQ_BASE; 101 (fpga_irq < OMAP_FPGA_IRQ_END) && stat; 102 fpga_irq++, stat >>= 1) { 103 if (stat & 1) { 104 generic_handle_irq(fpga_irq); 105 } 106 } 107} 108 109static struct irq_chip omap_fpga_irq_ack = { 110 .name = "FPGA-ack", 111 .irq_ack = fpga_mask_ack_irq, 112 .irq_mask = fpga_mask_irq, 113 .irq_unmask = fpga_unmask_irq, 114}; 115 116 117static struct irq_chip omap_fpga_irq = { 118 .name = "FPGA", 119 .irq_ack = fpga_ack_irq, 120 .irq_mask = fpga_mask_irq, 121 .irq_unmask = fpga_unmask_irq, 122}; 123 124/* 125 * All of the FPGA interrupt request inputs except for the touchscreen are 126 * edge-sensitive; the touchscreen is level-sensitive. The edge-sensitive 127 * interrupts are acknowledged as a side-effect of reading the interrupt 128 * status register from the FPGA. The edge-sensitive interrupt inputs 129 * cause a problem with level interrupt requests, such as Ethernet. The 130 * problem occurs when a level interrupt request is asserted while its 131 * interrupt input is masked in the FPGA, which results in a missed 132 * interrupt. 133 * 134 * In an attempt to workaround the problem with missed interrupts, the 135 * mask_ack routine for all of the FPGA interrupts has been changed from 136 * fpga_mask_ack_irq() to fpga_ack_irq() so that the specific FPGA interrupt 137 * being serviced is left unmasked. We can do this because the FPGA cascade 138 * interrupt is run with all interrupts masked. 139 * 140 * Limited testing indicates that this workaround appears to be effective 141 * for the smc9194 Ethernet driver used on the Innovator. It should work 142 * on other FPGA interrupts as well, but any drivers that explicitly mask 143 * interrupts at the interrupt controller via disable_irq/enable_irq 144 * could pose a problem. 145 */ 146void omap1510_fpga_init_irq(void) 147{ 148 int i, res; 149 150 __raw_writeb(0, OMAP1510_FPGA_IMR_LO); 151 __raw_writeb(0, OMAP1510_FPGA_IMR_HI); 152 __raw_writeb(0, INNOVATOR_FPGA_IMR2); 153 154 for (i = OMAP_FPGA_IRQ_BASE; i < OMAP_FPGA_IRQ_END; i++) { 155 156 if (i == OMAP1510_INT_FPGA_TS) { 157 /* 158 * The touchscreen interrupt is level-sensitive, so 159 * we'll use the regular mask_ack routine for it. 160 */ 161 irq_set_chip(i, &omap_fpga_irq_ack); 162 } 163 else { 164 /* 165 * All FPGA interrupts except the touchscreen are 166 * edge-sensitive, so we won't mask them. 167 */ 168 irq_set_chip(i, &omap_fpga_irq); 169 } 170 171 irq_set_handler(i, handle_edge_irq); 172 set_irq_flags(i, IRQF_VALID); 173 } 174 175 /* 176 * The FPGA interrupt line is connected to GPIO13. Claim this pin for 177 * the ARM. 178 * 179 * NOTE: For general GPIO/MPUIO access and interrupts, please see 180 * gpio.[ch] 181 */ 182 res = gpio_request(13, "FPGA irq"); 183 if (res) { 184 pr_err("%s failed to get gpio\n", __func__); 185 return; 186 } 187 gpio_direction_input(13); 188 irq_set_irq_type(gpio_to_irq(13), IRQ_TYPE_EDGE_RISING); 189 irq_set_chained_handler(OMAP1510_INT_FPGA, innovator_fpga_IRQ_demux); 190} 191