interrupt on arm9
Pavel Pisa
ppisa4lists at pikron.com
Fri Oct 26 14:13:45 UTC 2007
Hello All,
I typically use constructs described bellow to connect interrupt
in the driver like RTEMS code on ARM920T based PiMX1 board.
The example is for SPI interface which events are processed by driver
worker thread.
#define SPI_EVENT_ISR RTEMS_EVENT_26
#define SPI_EVENT_WAKE_RQ RTEMS_EVENT_28
static rtems_isr spi_mx1_isr(rtems_irq_hdl_param data);
static int spi_mx1_ctrl_fnc(struct spi_ifc *ifc, int ctrl, void *p);
/* Some structure describing concrete hardware driver instance context */
typedef struct spi_mx1_ifc {
spi_ifc_t spi_ifc;
rtems_id task;
volatile struct mx1_cspi_regs *cspi_regs;
rtems_irq_connect_data *irq_data;
int gpio_port;
} spi_mx1_ifc_t;
/* Structure used for interrupt registration, defined by RTEMS. */
rtems_irq_connect_data spi1_mx1_isr_data = {
.name = BSP_INT_SPI1, /* SPI1 interrupt vector number */
.hdl = spi_mx1_isr, /* SPI interrupt service routine */
.on = drvsup_mx1_isr_on,
.off = drvsup_mx1_isr_off,
.isOn = drvsup_mx1_isr_is_on,
#if 0
.irqLevel = 3, /* unused for ARM */
.irqTrigger = 0 /* unused for ARM */
#endif
};
/* MX1 SPI driver instance 1 */
spi_mx1_ifc_t spi1_mx1_ifc = {
.spi_ifc = { /* Common/target HW independent SPI driver context */
.flags=0,
.self_addr=0,
.ctrl_fnc=spi_mx1_ctrl_fnc,
},
.task=(rtems_id)0, /* Worker thread/task ID to allow send events from ISR */
.cspi_regs=&_reg_CSPI1, /* Pointer to SPI1 controller registers base */
.irq_data=&spi1_mx1_isr_data, /* Pointer to the RTEMS IRQ connect data */
.gpio_port=1 /* SPI driver instance number */
};
/* Interrupt service routine */
static rtems_isr
spi_mx1_isr(rtems_irq_hdl_param data)
{
spi_mx1_ifc_t *mx1_ifc = &spi1_mx1_ifc;
/* The context can be passed through "data" parameter for 4.6.99+ RTEMS versions */
mx1_ifc->cspi_regs->INT &= ~MX1_CSPI_INT_ENABLE_m;
rtems_event_send(mx1_ifc->task, SPI_EVENT_ISR);
}
/* Function called from driver worker thread to wait for IRQ */
static inline rtems_status_code
spi_mx1_wait_irq(spi_ifc_t *ifc)
{
rtems_status_code status;
rtems_event_set events;
rtems_interval ticks;
ticks = SPI_ISR_TIMEOUT;
status=rtems_event_receive(SPI_EVENT_ISR ,
RTEMS_EVENT_ANY|RTEMS_WAIT, ticks, &events);
if (status != RTEMS_SUCCESSFUL){
DEB_LOG("spi_mx1_wait_irq: rtems_event_receive %s\n",rtems_status_text(stat
}
return status;
}
/* Part of SPI driver initialization sequence */
status=rtems_task_create (rtems_build_name('S','P','I','0'+(mx1_ifc->gpio_por
/*priority*/ SPI_MX1_KWT_PRIORITY,
/*stacksize*/ RTEMS_MINIMUM_STACK_SIZE+4096,
RTEMS_DEFAULT_MODES /*RTEMS_PREEMPT|RTEMS_NO_TIMESLICE|RTEMS_NO_ASR|R
RTEMS_DEFAULT_ATTRIBUTES /*RTEMS_NO_FLOATING_POINT|RTEMS_LOCAL*/,
&mx1_ifc->task);
if (status != RTEMS_SUCCESSFUL){
printf("spi_mx1_init: rtems_task_create %s\n",rtems_status_text(status));
return status;
}
/* Install the interrupt handler */
if (!BSP_install_rtems_irq_handler(mx1_ifc->irq_data)){
printf("spi_mx1_init: BSP_install_rtems_irq_handler failed\n");
return RTEMS_UNSATISFIED;
}
This utilizes relatively low level RTEMS code, but I think,
that it is OK for "drivers". It requires some support routines
listed bellow. These are our target (M9328 MX1) specific.
It would be better, if RTEMS can assign some default routines
for given IRQ controller if the fields "on", "off" or "isOn"
are not assigned.
---------------------------------------------------------------------------
/* Enables spi_mx1 interrupts. */
void
drvsup_mx1_isr_on(const rtems_irq_connect_data *isr_data)
{
/* Enable interrupts */
MC9328MXL_AITC_INTENNUM = isr_data->name;
return;
}
/* Disables spi_mx1 interrupts */
void
drvsup_mx1_isr_off(const rtems_irq_connect_data *isr_data)
{
/* disable all various TX/RX interrupts */
MC9328MXL_AITC_INTDISNUM = isr_data->name;
return;
}
/* Tests to see if spi_mx1 interrupts are enabled, and returns non-0 if so.
* If interrupt is not enabled, returns 0.
*/
int
drvsup_mx1_isr_is_on(const rtems_irq_connect_data *isr_data)
{
if(isr_data->name<32)
return MC9328MXL_AITC_INTENABLEL & (1 << ((isr_data->name)&0x1f
else
return MC9328MXL_AITC_INTENABLEH & (1 << ((isr_data->name - 32)
}
---------------------------------------------------------------------------
The newer RTEMS versions (4.6.99+) allows to pass "data" parameter
to ISR routine. Then generic code to register interrupt handler (with
same syntax same as is used in Linux kernel) can be implemented for
ARMxxx as listed bellow. Again, it would be great, if it is implemented
as generic RTEMS service. It could help people to alternate between
Linux and RTEMS development as well, because Linux folks are used
for this usage and it contains parameters required for multi instance
drivers.
---------------------------------------------------------------------------
/* Set new interrupt handler simple and clear way.
*/
int
drvsup_request_irq(rtems_irq_number irq_num, rtems_irq_hdl irq_handler,
unsigned options, char *name, rtems_irq_hdl_param data)
{
rtems_irq_connect_data *irq_connect_data;
if(!irq_handler)
return -1;
irq_connect_data=malloc(sizeof(rtems_irq_connect_data));
if(!irq_connect_data)
return -1;
memset(irq_connect_data, 0, sizeof(rtems_irq_connect_data));
irq_connect_data->name = irq_num;
irq_connect_data->hdl = irq_handler;
irq_connect_data->handle= data;
irq_connect_data->on = drvsup_mx1_isr_on;
irq_connect_data->off = drvsup_mx1_isr_off;
irq_connect_data->isOn = drvsup_mx1_isr_is_on;
#if 0
irq_connect_data->irqLevel = 3; /* unused for ARM */
irq_connect_data->irqTrigger = 0; /* unused for ARM */
#endif
if (!BSP_install_rtems_irq_handler(irq_connect_data)){
free(irq_connect_data);
return -1;
}
return 0;
}
---------------------------------------------------------------------------
If you have interrest in the code, I can send more full driver examples.
Generalization and definition of rtems_request_irq() according to above
example would be nice to have as well.
Best wishes
Pavel Pisa
More information about the users
mailing list