/*1* linux/arch/arm/mach-omap1/fpga.c2*3* Interrupt handler for OMAP-1510 Innovator FPGA4*5* Copyright (C) 2001 RidgeRun, Inc.6* Author: Greg Lonnon <[email protected]>7*8* Copyright (C) 2002 MontaVista Software, Inc.9*10* Separated FPGA interrupts from innovator1510.c and cleaned up for 2.611* Copyright (C) 2004 Nokia Corporation by Tony Lindrgen <[email protected]>12*13* This program is free software; you can redistribute it and/or modify14* it under the terms of the GNU General Public License version 2 as15* published by the Free Software Foundation.16*/1718#include <linux/types.h>19#include <linux/init.h>20#include <linux/kernel.h>21#include <linux/device.h>22#include <linux/errno.h>23#include <linux/io.h>2425#include <mach/hardware.h>26#include <asm/irq.h>27#include <asm/mach/irq.h>2829#include <plat/fpga.h>30#include <mach/gpio.h>3132static void fpga_mask_irq(struct irq_data *d)33{34unsigned int irq = d->irq - OMAP_FPGA_IRQ_BASE;3536if (irq < 8)37__raw_writeb((__raw_readb(OMAP1510_FPGA_IMR_LO)38& ~(1 << irq)), OMAP1510_FPGA_IMR_LO);39else if (irq < 16)40__raw_writeb((__raw_readb(OMAP1510_FPGA_IMR_HI)41& ~(1 << (irq - 8))), OMAP1510_FPGA_IMR_HI);42else43__raw_writeb((__raw_readb(INNOVATOR_FPGA_IMR2)44& ~(1 << (irq - 16))), INNOVATOR_FPGA_IMR2);45}464748static inline u32 get_fpga_unmasked_irqs(void)49{50return51((__raw_readb(OMAP1510_FPGA_ISR_LO) &52__raw_readb(OMAP1510_FPGA_IMR_LO))) |53((__raw_readb(OMAP1510_FPGA_ISR_HI) &54__raw_readb(OMAP1510_FPGA_IMR_HI)) << 8) |55((__raw_readb(INNOVATOR_FPGA_ISR2) &56__raw_readb(INNOVATOR_FPGA_IMR2)) << 16);57}585960static void fpga_ack_irq(struct irq_data *d)61{62/* Don't need to explicitly ACK FPGA interrupts */63}6465static void fpga_unmask_irq(struct irq_data *d)66{67unsigned int irq = d->irq - OMAP_FPGA_IRQ_BASE;6869if (irq < 8)70__raw_writeb((__raw_readb(OMAP1510_FPGA_IMR_LO) | (1 << irq)),71OMAP1510_FPGA_IMR_LO);72else if (irq < 16)73__raw_writeb((__raw_readb(OMAP1510_FPGA_IMR_HI)74| (1 << (irq - 8))), OMAP1510_FPGA_IMR_HI);75else76__raw_writeb((__raw_readb(INNOVATOR_FPGA_IMR2)77| (1 << (irq - 16))), INNOVATOR_FPGA_IMR2);78}7980static void fpga_mask_ack_irq(struct irq_data *d)81{82fpga_mask_irq(d);83fpga_ack_irq(d);84}8586void innovator_fpga_IRQ_demux(unsigned int irq, struct irq_desc *desc)87{88u32 stat;89int fpga_irq;9091stat = get_fpga_unmasked_irqs();9293if (!stat)94return;9596for (fpga_irq = OMAP_FPGA_IRQ_BASE;97(fpga_irq < OMAP_FPGA_IRQ_END) && stat;98fpga_irq++, stat >>= 1) {99if (stat & 1) {100generic_handle_irq(fpga_irq);101}102}103}104105static struct irq_chip omap_fpga_irq_ack = {106.name = "FPGA-ack",107.irq_ack = fpga_mask_ack_irq,108.irq_mask = fpga_mask_irq,109.irq_unmask = fpga_unmask_irq,110};111112113static struct irq_chip omap_fpga_irq = {114.name = "FPGA",115.irq_ack = fpga_ack_irq,116.irq_mask = fpga_mask_irq,117.irq_unmask = fpga_unmask_irq,118};119120/*121* All of the FPGA interrupt request inputs except for the touchscreen are122* edge-sensitive; the touchscreen is level-sensitive. The edge-sensitive123* interrupts are acknowledged as a side-effect of reading the interrupt124* status register from the FPGA. The edge-sensitive interrupt inputs125* cause a problem with level interrupt requests, such as Ethernet. The126* problem occurs when a level interrupt request is asserted while its127* interrupt input is masked in the FPGA, which results in a missed128* interrupt.129*130* In an attempt to workaround the problem with missed interrupts, the131* mask_ack routine for all of the FPGA interrupts has been changed from132* fpga_mask_ack_irq() to fpga_ack_irq() so that the specific FPGA interrupt133* being serviced is left unmasked. We can do this because the FPGA cascade134* interrupt is installed with the IRQF_DISABLED flag, which leaves all135* interrupts masked at the CPU while an FPGA interrupt handler executes.136*137* Limited testing indicates that this workaround appears to be effective138* for the smc9194 Ethernet driver used on the Innovator. It should work139* on other FPGA interrupts as well, but any drivers that explicitly mask140* interrupts at the interrupt controller via disable_irq/enable_irq141* could pose a problem.142*/143void omap1510_fpga_init_irq(void)144{145int i, res;146147__raw_writeb(0, OMAP1510_FPGA_IMR_LO);148__raw_writeb(0, OMAP1510_FPGA_IMR_HI);149__raw_writeb(0, INNOVATOR_FPGA_IMR2);150151for (i = OMAP_FPGA_IRQ_BASE; i < OMAP_FPGA_IRQ_END; i++) {152153if (i == OMAP1510_INT_FPGA_TS) {154/*155* The touchscreen interrupt is level-sensitive, so156* we'll use the regular mask_ack routine for it.157*/158irq_set_chip(i, &omap_fpga_irq_ack);159}160else {161/*162* All FPGA interrupts except the touchscreen are163* edge-sensitive, so we won't mask them.164*/165irq_set_chip(i, &omap_fpga_irq);166}167168irq_set_handler(i, handle_edge_irq);169set_irq_flags(i, IRQF_VALID);170}171172/*173* The FPGA interrupt line is connected to GPIO13. Claim this pin for174* the ARM.175*176* NOTE: For general GPIO/MPUIO access and interrupts, please see177* gpio.[ch]178*/179res = gpio_request(13, "FPGA irq");180if (res) {181pr_err("%s failed to get gpio\n", __func__);182return;183}184gpio_direction_input(13);185irq_set_irq_type(gpio_to_irq(13), IRQ_TYPE_EDGE_RISING);186irq_set_chained_handler(OMAP1510_INT_FPGA, innovator_fpga_IRQ_demux);187}188189190