/* PIO IRQ Implementation for OpenPCD * (C) 2006 by Harald Welte <hwelte@hmw-consulting.de> * (C) 2007 Henryk Plötz <henryk@ploetzli.ch> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * */ #include "FreeRTOS.h" #include <errno.h> #include <sys/types.h> #include <lib_AT91SAM7.h> #include <string.h> #include "pio_irq.h" #include "dbgu.h" //#include <os/req_ctx.h> #include "openpicc.h" #include "board.h" #include "led.h" #include <FreeRTOS.h> #include <task.h> struct pioirq_state { irq_handler_t *handlers[NR_PIO]; u_int32_t usbmask; u_int32_t usb_throttled; /* atomic? */ }; static struct pioirq_state pirqs; static unsigned long count = 0; /* This FIQ implementation of pio data change works in close cooperation with function fiq_handler * in os/boot/boot.s * This code uses fast forcing for the PIOA irq so that each PIOA data change triggers * a FIQ. The FreeRTOS code has been modified to not mask FIQ ever. This means that the FIQ * code will run asynchronously with regards to the other code and especially might invade critical * sections. The actual FIQ code must therefore be as short as possible and may not call into the * FreeRTOS API (or parts of the application that call into the FreeRTOS API). * Instead a trick will be attempted: The PIOA IRQ will be set to fast forcing with my_fiq_handler * as handler and the FIQ handler then does the absolutely time critical tasks without calling any * other code. Additionally a second, normal IRQ handler is set up for a reserved IRQ on the AIC * that is not connected to any peripherals (see #define of PIO_SECONDARY_IRQ). After handling * the time critical tasks the FIQ handler will then manually assert this IRQ which will then * be handled by the AIC and priority controller and also execute synchronized with regards to * critical sections. * Potential problem: look for race conditions between PIO data change FIQ and handling * of PIO_SECONDARY_IRQ. * * Note: Originally I wanted to use 15 for the PIO_SECONDARY_IRQ but it turns out that that * won't work (the identifier is marked as reserved). Use 31 (external IRQ1) instead. Another * candidate would be 7 (USART1). */ #define USE_FIQ #define PIO_SECONDARY_IRQ 31 extern void fiq_handler(void); /* Will be used in pio_irq_demux_secondary below and contains the PIO_ISR value * from when the FIQ was raised */ volatile u_int32_t pio_irq_isr_value; /* low-level handler, used by Cstartup_app.S PIOA fast forcing and * by regular interrupt handler below */ portBASE_TYPE __ramfunc __pio_irq_demux(u_int32_t pio, portBASE_TYPE xTaskWoken) { u_int8_t send_usb = 0; int i; count++; DEBUGPCRF("PIO_ISR_STATUS = 0x%08x", pio); for (i = 0; i < NR_PIO; i++) { if (pio & (1 << i) && pirqs.handlers[i]) xTaskWoken = pirqs.handlers[i](i, xTaskWoken); if (pirqs.usbmask & (1 << i)) send_usb = 1; } AT91F_AIC_AcknowledgeIt(); //AT91F_AIC_ClearIt(AT91C_ID_PIOA); return xTaskWoken; } /* regular interrupt handler, in case fast forcing for PIOA disabled */ static void pio_irq_demux(void) __attribute__ ((naked)); static void pio_irq_demux(void) { portENTER_SWITCHING_ISR(); portBASE_TYPE xTaskWoken = pdFALSE; u_int32_t pio = AT91F_PIO_GetInterruptStatus(AT91C_BASE_PIOA); xTaskWoken = __pio_irq_demux(pio, xTaskWoken); portEXIT_SWITCHING_ISR(xTaskWoken); } /* nearly regular interrupt handler, in case fast forcing for PIOA is enabled and the secondary irq hack used */ static void pio_irq_demux_secondary(void) __attribute__ ((naked)); static void pio_irq_demux_secondary(void) { portENTER_SWITCHING_ISR(); portBASE_TYPE xTaskWoken = pdFALSE; xTaskWoken = __pio_irq_demux(pio_irq_isr_value, xTaskWoken); AT91F_AIC_ClearIt(PIO_SECONDARY_IRQ); portEXIT_SWITCHING_ISR(xTaskWoken); } void pio_irq_enable(u_int32_t pio) { AT91F_PIO_InterruptEnable(AT91C_BASE_PIOA, pio); } void pio_irq_disable(u_int32_t pio) { AT91F_PIO_InterruptDisable(AT91C_BASE_PIOA, pio); } /* Return the number of PIO IRQs received */ long pio_irq_get_count(void) { return count; } int pio_irq_register(u_int32_t pio, irq_handler_t *handler) { u_int8_t num = ffs(pio); if (num == 0) return -EINVAL; num--; if (pirqs.handlers[num]) return -EBUSY; pio_irq_disable(pio); AT91F_PIO_CfgInput(AT91C_BASE_PIOA, pio); pirqs.handlers[num] = handler; DEBUGPCRF("registering handler %p for PIOA %u", handler, num); return 0; } void pio_irq_unregister(u_int32_t pio) { u_int8_t num = ffs(pio); if (num == 0) return; num--; pio_irq_disable(pio); pirqs.handlers[num] = NULL; } static int initialized = 0; void pio_irq_init_once(void) { if(!initialized) pio_irq_init(); } void pio_irq_init(void) { initialized = 1; AT91F_PIOA_CfgPMC(); #ifdef USE_FIQ /* This code is not necessary anymore, because fiq_handler is directly part of the vector table, so no jump will happen. AT91F_AIC_ConfigureIt(AT91C_ID_FIQ, //0, AT91C_AIC_SRCTYPE_INT_HIGH_LEVEL, &cdsync_cb); 0, AT91C_AIC_SRCTYPE_INT_HIGH_LEVEL, &fiq_handler); */ /* enable fast forcing for PIOA interrupt */ *AT91C_AIC_FFER = (1 << AT91C_ID_PIOA); /* Set up a regular IRQ handler to be triggered from within the FIQ */ AT91F_AIC_ConfigureIt(PIO_SECONDARY_IRQ, OPENPICC_IRQ_PRIO_PIO, AT91C_AIC_SRCTYPE_INT_POSITIVE_EDGE, &pio_irq_demux_secondary); AT91F_AIC_ClearIt(PIO_SECONDARY_IRQ); AT91F_AIC_EnableIt(PIO_SECONDARY_IRQ); #else AT91F_AIC_ConfigureIt(AT91C_ID_PIOA, OPENPICC_IRQ_PRIO_PIO, AT91C_AIC_SRCTYPE_INT_HIGH_LEVEL, &pio_irq_demux); #endif AT91F_AIC_EnableIt(AT91C_ID_PIOA); (void)pio_irq_demux; // FIXME NO IRQ }