forked from Imagelibrary/rtems
bsp/ss555: Move libcpu content to bsps
This patch is a part of the BSP source reorganization. Update #3285.
This commit is contained in:
144
bsps/powerpc/ss555/dev/clock.c
Normal file
144
bsps/powerpc/ss555/dev/clock.c
Normal file
@@ -0,0 +1,144 @@
|
||||
/*
|
||||
* This routine initializes the PIT on the MPC5xx.
|
||||
* The tick frequency is specified by the BSP.
|
||||
*/
|
||||
|
||||
/*
|
||||
* MPC5xx port sponsored by Defence Research and Development Canada - Suffield
|
||||
* Copyright (C) 2004, Real-Time Systems Inc. (querbach@realtime.bc.ca)
|
||||
*
|
||||
* Derived from c/src/lib/libcpu/powerpc/mpc8xx/clock/clock.c:
|
||||
*
|
||||
* Author: Jay Monkman (jmonkman@frasca.com)
|
||||
* Copyright (C) 1998 by Frasca International, Inc.
|
||||
*
|
||||
* Derived from c/src/lib/libcpu/ppc/ppc403/clock/clock.c:
|
||||
*
|
||||
* Author: Andrew Bray <andy@i-cubed.co.uk>
|
||||
*
|
||||
* COPYRIGHT (c) 1995 by i-cubed ltd.
|
||||
*
|
||||
* To anyone who acknowledges that this file is provided "AS IS"
|
||||
* without any express or implied warranty:
|
||||
* permission to use, copy, modify, and distribute this file
|
||||
* for any purpose is hereby granted without fee, provided that
|
||||
* the above copyright notice and this notice appears in all
|
||||
* copies, and that the name of i-cubed limited not be used in
|
||||
* advertising or publicity pertaining to distribution of the
|
||||
* software without specific, written prior permission.
|
||||
* i-cubed limited makes no representations about the suitability
|
||||
* of this software for any purpose.
|
||||
*
|
||||
* Derived from c/src/lib/libcpu/hppa1_1/clock/clock.c:
|
||||
*
|
||||
* COPYRIGHT (c) 1989-2007.
|
||||
* On-Line Applications Research Corporation (OAR).
|
||||
*
|
||||
* The license and distribution terms for this file may be
|
||||
* found in the file LICENSE in this distribution or at
|
||||
* http://www.rtems.org/license/LICENSE.
|
||||
*/
|
||||
|
||||
#include <rtems.h>
|
||||
#include <rtems/clockdrv.h>
|
||||
#include <rtems/libio.h>
|
||||
#include <libcpu/irq.h>
|
||||
|
||||
#include <stdlib.h> /* for atexit() */
|
||||
#include <mpc5xx.h>
|
||||
|
||||
volatile uint32_t Clock_driver_ticks;
|
||||
extern int BSP_connect_clock_handler(rtems_isr_entry);
|
||||
extern int BSP_disconnect_clock_handler(void);
|
||||
extern uint32_t bsp_clicks_per_usec;
|
||||
|
||||
void Clock_exit( void );
|
||||
|
||||
/*
|
||||
* ISR Handler
|
||||
*/
|
||||
rtems_isr Clock_isr(rtems_vector_number vector)
|
||||
{
|
||||
usiu.piscrk = USIU_UNLOCK_KEY;
|
||||
usiu.piscr |= USIU_PISCR_PS; /* acknowledge interrupt */
|
||||
usiu.piscrk = 0;
|
||||
|
||||
Clock_driver_ticks++;
|
||||
rtems_clock_tick();
|
||||
}
|
||||
|
||||
void clockOn(void* unused)
|
||||
{
|
||||
unsigned desiredLevel;
|
||||
uint32_t pit_value;
|
||||
|
||||
/* calculate and set modulus */
|
||||
pit_value = (rtems_configuration_get_microseconds_per_tick() *
|
||||
bsp_clicks_per_usec) - 1 ;
|
||||
|
||||
if (pit_value > 0xffff) { /* pit is only 16 bits long */
|
||||
rtems_fatal_error_occurred(-1);
|
||||
}
|
||||
usiu.sccrk = USIU_UNLOCK_KEY;
|
||||
usiu.sccr &= ~USIU_SCCR_RTDIV; /* RTC and PIT clock is divided by 4 */
|
||||
usiu.sccrk = 0;
|
||||
|
||||
usiu.pitck = USIU_UNLOCK_KEY;
|
||||
usiu.pitc = pit_value;
|
||||
usiu.pitck = 0;
|
||||
|
||||
/* set PIT irq level, enable PIT, PIT interrupts */
|
||||
/* and clear int. status */
|
||||
desiredLevel = CPU_irq_level_from_symbolic_name(CPU_PERIODIC_TIMER);
|
||||
|
||||
usiu.piscrk = USIU_UNLOCK_KEY;
|
||||
usiu.piscr = USIU_PISCR_PIRQ(desiredLevel) /* set interrupt priority */
|
||||
| USIU_PISCR_PS /* acknowledge interrupt */
|
||||
| USIU_PISCR_PIE /* enable interrupt */
|
||||
| USIU_PISCR_PITF /* freeze during debug */
|
||||
| USIU_PISCR_PTE; /* enable timer */
|
||||
usiu.piscrk = 0;
|
||||
}
|
||||
|
||||
void clockOff(void* unused)
|
||||
{
|
||||
/* disable PIT and PIT interrupts */
|
||||
usiu.piscrk = USIU_UNLOCK_KEY;
|
||||
usiu.piscr &= ~(USIU_PISCR_PTE | USIU_PISCR_PIE);
|
||||
usiu.piscrk = 0;
|
||||
}
|
||||
|
||||
int clockIsOn(void* unused)
|
||||
{
|
||||
if (usiu.piscr & USIU_PISCR_PIE)
|
||||
return 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Called via atexit()
|
||||
* Remove the clock interrupt handler by setting handler to NULL
|
||||
*/
|
||||
void Clock_exit(void)
|
||||
{
|
||||
(void) BSP_disconnect_clock_handler ();
|
||||
}
|
||||
|
||||
static void Install_clock(rtems_isr_entry clock_isr)
|
||||
{
|
||||
Clock_driver_ticks = 0;
|
||||
|
||||
BSP_connect_clock_handler (clock_isr);
|
||||
atexit(Clock_exit);
|
||||
}
|
||||
|
||||
rtems_device_driver Clock_initialize(
|
||||
rtems_device_major_number major,
|
||||
rtems_device_minor_number minor,
|
||||
void *pargp
|
||||
)
|
||||
{
|
||||
Install_clock( Clock_isr );
|
||||
|
||||
return RTEMS_SUCCESSFUL;
|
||||
}
|
||||
317
bsps/powerpc/ss555/dev/console-generic.c
Normal file
317
bsps/powerpc/ss555/dev/console-generic.c
Normal file
@@ -0,0 +1,317 @@
|
||||
/*
|
||||
* General Serial I/O functions.
|
||||
*
|
||||
* This file contains the functions for performing serial I/O. The actual
|
||||
* system calls (console_*) should be in the BSP part of the source tree.
|
||||
* That way different BSPs can use whichever SCI they wish for /dev/console.
|
||||
*
|
||||
* On-chip resources used:
|
||||
* resource minor note
|
||||
* SCI1 0
|
||||
* SCI2 1
|
||||
*/
|
||||
|
||||
/*
|
||||
* MPC5xx port sponsored by Defence Research and Development Canada - Suffield
|
||||
* Copyright (C) 2004, Real-Time Systems Inc. (querbach@realtime.bc.ca)
|
||||
*
|
||||
* Derived from
|
||||
* c/src/lib/libcpu/powerpc/mpc8xx/console_generic/console_generic.c:
|
||||
* Author: Jay Monkman (jmonkman@frasca.com)
|
||||
* Copyright (C) 1998 by Frasca International, Inc.
|
||||
*
|
||||
* Derived from c/src/lib/libbsp/m68k/gen360/console/console.c written by:
|
||||
* W. Eric Norum
|
||||
* Saskatchewan Accelerator Laboratory
|
||||
* University of Saskatchewan
|
||||
* Saskatoon, Saskatchewan, CANADA
|
||||
* eric@skatter.usask.ca
|
||||
*
|
||||
* COPYRIGHT (c) 1989-1998.
|
||||
* On-Line Applications Research Corporation (OAR).
|
||||
*
|
||||
* Modifications by Darlene Stewart <Darlene.Stewart@iit.nrc.ca>
|
||||
* and Charles-Antoine Gauthier <charles.gauthier@iit.nrc.ca>
|
||||
* Copyright (c) 1999, National Research Council of Canada
|
||||
*
|
||||
* The license and distribution terms for this file may be
|
||||
* found in the file LICENSE in this distribution or at
|
||||
* http://www.rtems.org/license/LICENSE.
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <termios.h>
|
||||
#include <rtems.h>
|
||||
#include <rtems/libio.h>
|
||||
#include <rtems/bspIo.h> /* for printk */
|
||||
#include <mpc5xx.h>
|
||||
#include <mpc5xx/console.h>
|
||||
#include <bsp/irq.h>
|
||||
|
||||
|
||||
/*
|
||||
* SCI port descriptor table.
|
||||
*/
|
||||
typedef struct
|
||||
{
|
||||
volatile m5xxSCIRegisters_t *regs; /* hardware registers */
|
||||
struct rtems_termios_tty *ttyp; /* termios data for this port */
|
||||
} sci_desc;
|
||||
|
||||
static sci_desc sci_descs[] = {
|
||||
{ &imb.qsmcm.sci1, 0 }, /* SCI 1 */
|
||||
{ &imb.qsmcm.sci2, 0 }, /* SCI 2 */
|
||||
};
|
||||
|
||||
/*
|
||||
* Number of SCI port initialization calls made so far. Used to avoid
|
||||
* installing the common interrupt handler more than once.
|
||||
*/
|
||||
int init_calls = 0;
|
||||
|
||||
/*
|
||||
* Default configuration.
|
||||
*/
|
||||
static struct termios default_termios = {
|
||||
0, /* input mode flags */
|
||||
0, /* output mode flags */
|
||||
0, /* local mode flags */
|
||||
0, /* line discipline */
|
||||
{ 0 }, /* control characters */
|
||||
CS8 | CREAD | CLOCAL | B9600, /* control mode flags */
|
||||
};
|
||||
|
||||
|
||||
extern uint32_t bsp_clock_speed;
|
||||
|
||||
/*
|
||||
* Termios callback functions
|
||||
*/
|
||||
|
||||
int
|
||||
m5xx_uart_firstOpen(
|
||||
int major,
|
||||
int minor,
|
||||
void *arg
|
||||
)
|
||||
{
|
||||
rtems_libio_open_close_args_t *args = arg;
|
||||
sci_desc* desc = &sci_descs[minor];
|
||||
struct rtems_termios_tty *tty = args->iop->data1;
|
||||
|
||||
desc->ttyp = tty; /* connect tty */
|
||||
if ( tty->device.outputUsesInterrupts == TERMIOS_IRQ_DRIVEN)
|
||||
desc->regs->sccr1 |= QSMCM_SCI_RIE; /* enable rx interrupt */
|
||||
|
||||
return RTEMS_SUCCESSFUL;
|
||||
}
|
||||
|
||||
int
|
||||
m5xx_uart_lastClose(
|
||||
int major,
|
||||
int minor,
|
||||
void* arg
|
||||
)
|
||||
{
|
||||
sci_desc* desc = &sci_descs[minor];
|
||||
|
||||
desc->regs->sccr1 &= ~(QSMCM_SCI_RIE | QSMCM_SCI_TIE); /* disable all */
|
||||
desc->ttyp = NULL; /* disconnect tty */
|
||||
|
||||
return RTEMS_SUCCESSFUL;
|
||||
}
|
||||
|
||||
int
|
||||
m5xx_uart_pollRead(
|
||||
int minor
|
||||
)
|
||||
{
|
||||
volatile m5xxSCIRegisters_t *regs = sci_descs[minor].regs;
|
||||
int c = -1;
|
||||
|
||||
if ( regs ) {
|
||||
while ( (regs->scsr & QSMCM_SCI_RDRF) == 0 )
|
||||
;
|
||||
c = regs->scdr;
|
||||
}
|
||||
|
||||
return c;
|
||||
}
|
||||
|
||||
ssize_t m5xx_uart_write(
|
||||
int minor,
|
||||
const char *buf,
|
||||
size_t len
|
||||
)
|
||||
{
|
||||
if (len > 0) {
|
||||
volatile m5xxSCIRegisters_t *regs = sci_descs[minor].regs;
|
||||
|
||||
regs->scdr = *buf; /* start transmission */
|
||||
regs->sccr1 |= QSMCM_SCI_TIE; /* enable interrupt */
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
ssize_t m5xx_uart_pollWrite(
|
||||
int minor,
|
||||
const char *buf,
|
||||
size_t len
|
||||
)
|
||||
{
|
||||
volatile m5xxSCIRegisters_t *regs = sci_descs[minor].regs;
|
||||
size_t retval = len;
|
||||
|
||||
while ( len-- ) {
|
||||
while ( (regs->scsr & QSMCM_SCI_TDRE) == 0 )
|
||||
;
|
||||
regs->scdr = *buf++;
|
||||
}
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
int
|
||||
m5xx_uart_setAttributes(
|
||||
int minor,
|
||||
const struct termios *t
|
||||
)
|
||||
{
|
||||
uint16_t sccr0 = sci_descs[minor].regs->sccr0;
|
||||
uint16_t sccr1 = sci_descs[minor].regs->sccr1;
|
||||
int baud;
|
||||
|
||||
/*
|
||||
* Check that port number is valid
|
||||
*/
|
||||
if ( (minor < SCI1_MINOR) || (minor > SCI2_MINOR) )
|
||||
return RTEMS_INVALID_NUMBER;
|
||||
|
||||
/* Baud rate */
|
||||
baud = rtems_termios_baud_to_number( t->c_ospeed );
|
||||
if (baud > 0) {
|
||||
sccr0 &= ~QSMCM_SCI_BAUD(-1);
|
||||
sccr0 |= QSMCM_SCI_BAUD((bsp_clock_speed + (16 * baud)) / (32 * baud));
|
||||
}
|
||||
|
||||
/* Number of data bits -- not available with MPC5xx SCI */
|
||||
switch ( t->c_cflag & CSIZE ) {
|
||||
case CS5: break;
|
||||
case CS6: break;
|
||||
case CS7: break;
|
||||
case CS8: break;
|
||||
}
|
||||
|
||||
/* Stop bits -- not easily available with MPC5xx SCI */
|
||||
if ( t->c_cflag & CSTOPB ) {
|
||||
/* Two stop bits */
|
||||
} else {
|
||||
/* One stop bit */
|
||||
}
|
||||
|
||||
/* Parity */
|
||||
if ( t->c_cflag & PARENB )
|
||||
sccr1 |= QSMCM_SCI_PE;
|
||||
else
|
||||
sccr1 &= ~QSMCM_SCI_PE;
|
||||
|
||||
if ( t->c_cflag & PARODD )
|
||||
sccr1 |= QSMCM_SCI_PT;
|
||||
else
|
||||
sccr1 &= ~QSMCM_SCI_PT;
|
||||
|
||||
/* Transmitter and receiver enable */
|
||||
sccr1 |= QSMCM_SCI_TE;
|
||||
if ( t->c_cflag & CREAD )
|
||||
sccr1 |= QSMCM_SCI_RE;
|
||||
else
|
||||
sccr1 &= ~QSMCM_SCI_RE;
|
||||
|
||||
/* Write hardware registers */
|
||||
sci_descs[minor].regs->sccr0 = sccr0;
|
||||
sci_descs[minor].regs->sccr1 = sccr1;
|
||||
|
||||
return RTEMS_SUCCESSFUL;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Interrupt handling.
|
||||
*/
|
||||
static void
|
||||
m5xx_sci_interrupt_handler (rtems_irq_hdl_param unused)
|
||||
{
|
||||
int minor;
|
||||
|
||||
for ( minor = 0; minor < NUM_PORTS; minor++ ) {
|
||||
sci_desc *desc = &sci_descs[minor];
|
||||
int sccr1 = desc->regs->sccr1;
|
||||
int scsr = desc->regs->scsr;
|
||||
|
||||
/*
|
||||
* Character received?
|
||||
*/
|
||||
if ((sccr1 & QSMCM_SCI_RIE) && (scsr & QSMCM_SCI_RDRF)) {
|
||||
char c = desc->regs->scdr;
|
||||
rtems_termios_enqueue_raw_characters(desc->ttyp, &c, 1);
|
||||
}
|
||||
/*
|
||||
* Transmitter empty?
|
||||
*/
|
||||
if ((sccr1 & QSMCM_SCI_TIE) && (scsr & QSMCM_SCI_TDRE)) {
|
||||
desc->regs->sccr1 &= ~QSMCM_SCI_TIE;
|
||||
rtems_termios_dequeue_characters (desc->ttyp, 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void m5xx_sci_nop(const rtems_irq_connect_data* ptr)
|
||||
{
|
||||
}
|
||||
|
||||
static int m5xx_sci_isOn(const rtems_irq_connect_data* ptr)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
/*
|
||||
* Basic initialization.
|
||||
*/
|
||||
|
||||
void
|
||||
m5xx_uart_initialize (int minor)
|
||||
{
|
||||
/*
|
||||
* Check that minor number is valid.
|
||||
*/
|
||||
if ( (minor < SCI1_MINOR) || (minor > SCI2_MINOR) )
|
||||
return;
|
||||
|
||||
/*
|
||||
* Configure and enable receiver and transmitter.
|
||||
*/
|
||||
m5xx_uart_setAttributes(minor, &default_termios);
|
||||
|
||||
/*
|
||||
* Connect interrupt if not yet done.
|
||||
*/
|
||||
if ( init_calls++ == 0 ) {
|
||||
rtems_irq_connect_data irq_data;
|
||||
|
||||
irq_data.name = CPU_IRQ_SCI;
|
||||
irq_data.hdl = m5xx_sci_interrupt_handler;
|
||||
irq_data.on = m5xx_sci_nop; /* can't enable both channels here */
|
||||
irq_data.off = m5xx_sci_nop; /* can't disable both channels here */
|
||||
irq_data.isOn = m5xx_sci_isOn;
|
||||
|
||||
if (!CPU_install_rtems_irq_handler (&irq_data)) {
|
||||
printk("Unable to connect SCI Irq handler\n");
|
||||
rtems_fatal_error_occurred(1);
|
||||
}
|
||||
|
||||
imb.qsmcm.qdsci_il = /* set interrupt level in port */
|
||||
QSMCM_ILDSCI(CPU_irq_level_from_symbolic_name(CPU_IRQ_SCI));
|
||||
}
|
||||
}
|
||||
104
bsps/powerpc/ss555/dev/timer.c
Normal file
104
bsps/powerpc/ss555/dev/timer.c
Normal file
@@ -0,0 +1,104 @@
|
||||
/**
|
||||
* @file
|
||||
* @brief Timer Driver for the PowerPC MPC5xx.
|
||||
*
|
||||
* This file manages the interval timer on the PowerPC MPC5xx.
|
||||
* @noe This is not the PIT, but rather the RTEMS interval timer.
|
||||
* We shall use the bottom 32 bits of the timebase register,
|
||||
*/
|
||||
|
||||
/*
|
||||
* MPC5xx port sponsored by Defence Research and Development Canada - Suffield
|
||||
* Copyright (C) 2004, Real-Time Systems Inc. (querbach@realtime.bc.ca)
|
||||
*
|
||||
* Derived from c/src/lib/libcpu/powerpc/mpc8xx/timer/timer.c:
|
||||
*
|
||||
* Author: Jay Monkman (jmonkman@frasca.com)
|
||||
* Copywright (C) 1998 by Frasca International, Inc.
|
||||
*
|
||||
* Derived from c/src/lib/libcpu/ppc/ppc403/timer/timer.c:
|
||||
*
|
||||
* Author: Andrew Bray <andy@i-cubed.co.uk>
|
||||
*
|
||||
* COPYRIGHT (c) 1995 by i-cubed ltd.
|
||||
*
|
||||
* To anyone who acknowledges that this file is provided "AS IS"
|
||||
* without any express or implied warranty:
|
||||
* permission to use, copy, modify, and distribute this file
|
||||
* for any purpose is hereby granted without fee, provided that
|
||||
* the above copyright notice and this notice appears in all
|
||||
* copies, and that the name of i-cubed limited not be used in
|
||||
* advertising or publicity pertaining to distribution of the
|
||||
* software without specific, written prior permission.
|
||||
* i-cubed limited makes no representations about the suitability
|
||||
* of this software for any purpose.
|
||||
*
|
||||
* Derived from c/src/lib/libcpu/hppa1_1/timer/timer.c:
|
||||
*
|
||||
* COPYRIGHT (c) 1989-2007.
|
||||
* On-Line Applications Research Corporation (OAR).
|
||||
*
|
||||
* The license and distribution terms for this file may be
|
||||
* found in the file LICENSE in this distribution or at
|
||||
* http://www.rtems.org/license/LICENSE.
|
||||
*/
|
||||
|
||||
#include <rtems.h>
|
||||
#include <rtems/btimer.h>
|
||||
#include <mpc5xx.h>
|
||||
|
||||
static volatile uint32_t Timer_starting;
|
||||
static bool benchmark_timer_find_average_overhead;
|
||||
|
||||
extern uint32_t bsp_timer_least_valid;
|
||||
extern uint32_t bsp_timer_average_overhead;
|
||||
|
||||
/*
|
||||
* This is so small that this code will be reproduced where needed.
|
||||
*/
|
||||
static inline uint32_t get_itimer(void)
|
||||
{
|
||||
uint32_t ret;
|
||||
|
||||
__asm__ volatile ("mftb %0" : "=r" ((ret))); /* TBLO */
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
void benchmark_timer_initialize(void)
|
||||
{
|
||||
/* set interrupt level and enable timebase. This should never */
|
||||
/* generate an interrupt however. */
|
||||
usiu.tbscrk = USIU_UNLOCK_KEY;
|
||||
usiu.tbscr |= USIU_TBSCR_TBIRQ(4) /* interrupt priority level */
|
||||
| USIU_TBSCR_TBF /* freeze timebase during debug */
|
||||
| USIU_TBSCR_TBE; /* enable timebase */
|
||||
usiu.tbscrk = 0;
|
||||
|
||||
Timer_starting = get_itimer();
|
||||
}
|
||||
|
||||
benchmark_timer_t benchmark_timer_read(void)
|
||||
{
|
||||
uint32_t clicks;
|
||||
uint32_t total;
|
||||
|
||||
clicks = get_itimer();
|
||||
|
||||
total = clicks - Timer_starting;
|
||||
|
||||
if ( benchmark_timer_find_average_overhead == 1 )
|
||||
return total; /* in XXX microsecond units */
|
||||
|
||||
else {
|
||||
if ( total < bsp_timer_least_valid ) {
|
||||
return 0; /* below timer resolution */
|
||||
}
|
||||
return (total - bsp_timer_average_overhead);
|
||||
}
|
||||
}
|
||||
|
||||
void benchmark_timer_disable_subtracting_average_overhead(bool find_flag)
|
||||
{
|
||||
benchmark_timer_find_average_overhead = find_flag;
|
||||
}
|
||||
463
bsps/powerpc/ss555/start/irq.c
Normal file
463
bsps/powerpc/ss555/start/irq.c
Normal file
@@ -0,0 +1,463 @@
|
||||
/*
|
||||
* This file contains the implementation of the function described in irq.h
|
||||
*/
|
||||
|
||||
/*
|
||||
* MPC5xx port sponsored by Defence Research and Development Canada - Suffield
|
||||
* Copyright (C) 2004, Real-Time Systems Inc. (querbach@realtime.bc.ca)
|
||||
*
|
||||
* Derived from libbsp/powerpc/mbx8xx/irq/irq.c:
|
||||
*
|
||||
* Copyright (C) 1998, 1999 valette@crf.canon.fr
|
||||
*
|
||||
* The license and distribution terms for this file may be
|
||||
* found in the file LICENSE in this distribution or at
|
||||
* http://www.rtems.org/license/LICENSE.
|
||||
*/
|
||||
|
||||
#include <rtems.h>
|
||||
#include <mpc5xx.h>
|
||||
#include <libcpu/vectors.h>
|
||||
#include <libcpu/raw_exception.h>
|
||||
#include <libcpu/irq.h>
|
||||
#include <bsp/irq.h>
|
||||
|
||||
/*
|
||||
* Convert an rtems_irq_number constant to an interrupt level
|
||||
* suitable for programming into an I/O device's interrupt level field.
|
||||
*/
|
||||
int CPU_irq_level_from_symbolic_name(const rtems_irq_number name)
|
||||
{
|
||||
if (CPU_USIU_EXT_IRQ_0 <= name && name <= CPU_USIU_INT_IRQ_7)
|
||||
return (name - CPU_USIU_EXT_IRQ_0) / 2;
|
||||
|
||||
if (CPU_UIMB_IRQ_8 <= name && name <= CPU_UIMB_IRQ_31)
|
||||
return 8 + (name - CPU_UIMB_IRQ_8);
|
||||
|
||||
return 31; /* reasonable default */
|
||||
}
|
||||
|
||||
/*
|
||||
* default handler connected on each irq after bsp initialization
|
||||
*/
|
||||
static rtems_irq_connect_data default_rtems_entry;
|
||||
|
||||
/*
|
||||
* location used to store initial tables used for interrupt
|
||||
* management.
|
||||
*/
|
||||
static rtems_irq_global_settings* internal_config;
|
||||
static rtems_irq_connect_data* rtems_hdl_tbl;
|
||||
|
||||
/*
|
||||
* Check if symbolic IRQ name is an USIU IRQ
|
||||
*/
|
||||
static inline int is_usiu_irq(const rtems_irq_number irqLine)
|
||||
{
|
||||
return (((int) irqLine <= CPU_USIU_IRQ_MAX_OFFSET) &&
|
||||
((int) irqLine >= CPU_USIU_IRQ_MIN_OFFSET)
|
||||
);
|
||||
}
|
||||
|
||||
/*
|
||||
* Check if symbolic IRQ name is an UIMB IRQ
|
||||
*/
|
||||
static inline int is_uimb_irq(const rtems_irq_number irqLine)
|
||||
{
|
||||
return (((int) irqLine <= CPU_UIMB_IRQ_MAX_OFFSET) &&
|
||||
((int) irqLine >= CPU_UIMB_IRQ_MIN_OFFSET)
|
||||
);
|
||||
}
|
||||
|
||||
/*
|
||||
* Check if symbolic IRQ name is a Processor IRQ
|
||||
*/
|
||||
static inline int is_proc_irq(const rtems_irq_number irqLine)
|
||||
{
|
||||
return (((int) irqLine <= CPU_PROC_IRQ_MAX_OFFSET) &&
|
||||
((int) irqLine >= CPU_PROC_IRQ_MIN_OFFSET)
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Masks used to mask off the interrupts. For exmaple, for ILVL2, the
|
||||
* mask is used to mask off interrupts ILVL2, IRQ3, ILVL3, ... IRQ7
|
||||
* and ILVL7.
|
||||
*
|
||||
*/
|
||||
const static unsigned int USIU_IvectMask[CPU_USIU_IRQ_COUNT] =
|
||||
{
|
||||
0, /* external IRQ 0 */
|
||||
0xFFFFFFFF << 31, /* internal level 0 */
|
||||
0xFFFFFFFF << 30, /* external IRQ 1 */
|
||||
0xFFFFFFFF << 29, /* internal level 1 */
|
||||
0xFFFFFFFF << 28, /* external IRQ 2 */
|
||||
0xFFFFFFFF << 27, /* internal level 2 */
|
||||
0xFFFFFFFF << 26, /* external IRQ 3 */
|
||||
0xFFFFFFFF << 25, /* internal level 3 */
|
||||
0xFFFFFFFF << 24, /* external IRQ 4 */
|
||||
0xFFFFFFFF << 23, /* internal level 4 */
|
||||
0xFFFFFFFF << 22, /* external IRQ 5 */
|
||||
0xFFFFFFFF << 21, /* internal level 5 */
|
||||
0xFFFFFFFF << 20, /* external IRQ 6 */
|
||||
0xFFFFFFFF << 19, /* internal level 6 */
|
||||
0xFFFFFFFF << 18, /* external IRQ 7 */
|
||||
0xFFFFFFFF << 17 /* internal level 7 */
|
||||
};
|
||||
|
||||
|
||||
/*
|
||||
* ------------------------ RTEMS Irq helper functions ----------------
|
||||
*/
|
||||
|
||||
/*
|
||||
* Caution : this function assumes the variable "internal_config"
|
||||
* is already set and that the tables it contains are still valid
|
||||
* and accessible.
|
||||
*/
|
||||
static void compute_USIU_IvectMask_from_prio (void)
|
||||
{
|
||||
/*
|
||||
* In theory this is feasible. No time to code it yet. See i386/shared/irq.c
|
||||
* for an example based on 8259 controller mask. The actual masks defined
|
||||
* correspond to the priorities defined for the USIU in irq_init.c.
|
||||
*/
|
||||
}
|
||||
|
||||
/*
|
||||
* This function check that the value given for the irq line
|
||||
* is valid.
|
||||
*/
|
||||
static int isValidInterrupt(int irq)
|
||||
{
|
||||
if ( (irq < CPU_MIN_OFFSET) || (irq > CPU_MAX_OFFSET)
|
||||
|| (irq == CPU_UIMB_INTERRUPT) )
|
||||
return 0;
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int CPU_irq_enable_at_uimb(const rtems_irq_number irqLine)
|
||||
{
|
||||
if (!is_uimb_irq(irqLine))
|
||||
return 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int CPU_irq_disable_at_uimb(const rtems_irq_number irqLine)
|
||||
{
|
||||
if (!is_uimb_irq(irqLine))
|
||||
return 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int CPU_irq_enable_at_usiu(const rtems_irq_number irqLine)
|
||||
{
|
||||
int usiu_irq_index;
|
||||
|
||||
if (!is_usiu_irq(irqLine))
|
||||
return 1;
|
||||
|
||||
usiu_irq_index = ((int) (irqLine) - CPU_USIU_IRQ_MIN_OFFSET);
|
||||
ppc_cached_irq_mask |= (1 << (31-usiu_irq_index));
|
||||
usiu.simask = ppc_cached_irq_mask;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int CPU_irq_disable_at_usiu(const rtems_irq_number irqLine)
|
||||
{
|
||||
int usiu_irq_index;
|
||||
|
||||
if (!is_usiu_irq(irqLine))
|
||||
return 1;
|
||||
|
||||
usiu_irq_index = ((int) (irqLine) - CPU_USIU_IRQ_MIN_OFFSET);
|
||||
ppc_cached_irq_mask &= ~(1 << (31-usiu_irq_index));
|
||||
usiu.simask = ppc_cached_irq_mask;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* --------------- RTEMS Single Irq Handler Mngt Routines ----------------
|
||||
*/
|
||||
|
||||
int CPU_install_rtems_irq_handler (const rtems_irq_connect_data* irq)
|
||||
{
|
||||
rtems_interrupt_level level;
|
||||
|
||||
if (!isValidInterrupt(irq->name)) {
|
||||
return 0;
|
||||
}
|
||||
/*
|
||||
* Check if default handler is actually connected. If not issue an error.
|
||||
* You must first get the current handler via CPU_get_current_idt_entry
|
||||
* and then disconnect it using CPU_delete_idt_entry.
|
||||
* RATIONALE : to always have the same transition by forcing the user
|
||||
* to get the previous handler before accepting to disconnect.
|
||||
*/
|
||||
if (rtems_hdl_tbl[irq->name].hdl != default_rtems_entry.hdl) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
rtems_interrupt_disable(level);
|
||||
|
||||
/*
|
||||
* store the data provided by user
|
||||
*/
|
||||
rtems_hdl_tbl[irq->name] = *irq;
|
||||
|
||||
if (is_uimb_irq(irq->name)) {
|
||||
/*
|
||||
* Enable interrupt at UIMB level
|
||||
*/
|
||||
CPU_irq_enable_at_uimb (irq->name);
|
||||
}
|
||||
|
||||
if (is_usiu_irq(irq->name)) {
|
||||
/*
|
||||
* Enable interrupt at USIU level
|
||||
*/
|
||||
CPU_irq_enable_at_usiu (irq->name);
|
||||
}
|
||||
|
||||
if (is_proc_irq(irq->name)) {
|
||||
/*
|
||||
* Should Enable exception at processor level but not needed. Will restore
|
||||
* EE flags at the end of the routine anyway.
|
||||
*/
|
||||
}
|
||||
/*
|
||||
* Enable interrupt on device
|
||||
*/
|
||||
if (irq->on)
|
||||
irq->on(irq);
|
||||
|
||||
rtems_interrupt_enable(level);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
int CPU_get_current_rtems_irq_handler (rtems_irq_connect_data* irq)
|
||||
{
|
||||
if (!isValidInterrupt(irq->name)) {
|
||||
return 0;
|
||||
}
|
||||
*irq = rtems_hdl_tbl[irq->name];
|
||||
return 1;
|
||||
}
|
||||
|
||||
int CPU_remove_rtems_irq_handler (const rtems_irq_connect_data* irq)
|
||||
{
|
||||
rtems_interrupt_level level;
|
||||
|
||||
if (!isValidInterrupt(irq->name)) {
|
||||
return 0;
|
||||
}
|
||||
/*
|
||||
* Check if default handler is actually connected. If not issue an error.
|
||||
* You must first get the current handler via CPU_get_current_idt_entry
|
||||
* and then disconnect it using CPU_delete_idt_entry.
|
||||
* RATIONALE : to always have the same transition by forcing the user
|
||||
* to get the previous handler before accepting to disconnect.
|
||||
*/
|
||||
if (rtems_hdl_tbl[irq->name].hdl != irq->hdl) {
|
||||
return 0;
|
||||
}
|
||||
rtems_interrupt_disable(level);
|
||||
|
||||
/*
|
||||
* Disable interrupt on device
|
||||
*/
|
||||
if (irq->off)
|
||||
irq->off(irq);
|
||||
|
||||
if (is_uimb_irq(irq->name)) {
|
||||
/*
|
||||
* disable interrupt at UIMB level
|
||||
*/
|
||||
CPU_irq_disable_at_uimb (irq->name);
|
||||
}
|
||||
if (is_usiu_irq(irq->name)) {
|
||||
/*
|
||||
* disable interrupt at USIU level
|
||||
*/
|
||||
CPU_irq_disable_at_usiu (irq->name);
|
||||
}
|
||||
if (is_proc_irq(irq->name)) {
|
||||
/*
|
||||
* disable exception at processor level
|
||||
*/
|
||||
}
|
||||
|
||||
/*
|
||||
* restore the default irq value
|
||||
*/
|
||||
rtems_hdl_tbl[irq->name] = default_rtems_entry;
|
||||
|
||||
rtems_interrupt_enable(level);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
/*
|
||||
* ---------------- RTEMS Global Irq Handler Mngt Routines ----------------
|
||||
*/
|
||||
|
||||
int CPU_rtems_irq_mngt_set (rtems_irq_global_settings* config)
|
||||
{
|
||||
int i;
|
||||
rtems_interrupt_level level;
|
||||
|
||||
/*
|
||||
* Store various code accelerators
|
||||
*/
|
||||
internal_config = config;
|
||||
default_rtems_entry = config->defaultEntry;
|
||||
rtems_hdl_tbl = config->irqHdlTbl;
|
||||
|
||||
rtems_interrupt_disable(level);
|
||||
|
||||
/*
|
||||
* Start with UIMB IRQ
|
||||
*/
|
||||
for (i = CPU_UIMB_IRQ_MIN_OFFSET; i <= CPU_UIMB_IRQ_MAX_OFFSET ; i++) {
|
||||
if (rtems_hdl_tbl[i].hdl != default_rtems_entry.hdl) {
|
||||
CPU_irq_enable_at_uimb (i);
|
||||
if (rtems_hdl_tbl[i].on)
|
||||
rtems_hdl_tbl[i].on(&rtems_hdl_tbl[i]);
|
||||
}
|
||||
else {
|
||||
if (rtems_hdl_tbl[i].off)
|
||||
rtems_hdl_tbl[i].off(&rtems_hdl_tbl[i]);
|
||||
CPU_irq_disable_at_uimb (i);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Continue with USIU IRQ
|
||||
* Set up internal tables used by rtems interrupt prologue
|
||||
*/
|
||||
compute_USIU_IvectMask_from_prio ();
|
||||
|
||||
for (i = CPU_USIU_IRQ_MIN_OFFSET; i <= CPU_USIU_IRQ_MAX_OFFSET ; i++) {
|
||||
if (rtems_hdl_tbl[i].hdl != default_rtems_entry.hdl) {
|
||||
CPU_irq_enable_at_usiu (i);
|
||||
if (rtems_hdl_tbl[i].on)
|
||||
rtems_hdl_tbl[i].on(&rtems_hdl_tbl[i]);
|
||||
}
|
||||
else {
|
||||
if (rtems_hdl_tbl[i].off)
|
||||
rtems_hdl_tbl[i].off(&rtems_hdl_tbl[i]);
|
||||
CPU_irq_disable_at_usiu (i);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Enable all UIMB interrupt lines, then enable at USIU.
|
||||
*/
|
||||
imb.uimb.umcr |= UIMB_UMCR_IRQMUX(3);
|
||||
CPU_irq_enable_at_usiu (CPU_UIMB_INTERRUPT);
|
||||
|
||||
/*
|
||||
* finish with Processor exceptions handled like IRQ
|
||||
*/
|
||||
for (i = CPU_PROC_IRQ_MIN_OFFSET; i <= CPU_PROC_IRQ_MAX_OFFSET; i++) {
|
||||
if (rtems_hdl_tbl[i].hdl != default_rtems_entry.hdl) {
|
||||
if (rtems_hdl_tbl[i].on)
|
||||
rtems_hdl_tbl[i].on(&rtems_hdl_tbl[i]);
|
||||
}
|
||||
else {
|
||||
if (rtems_hdl_tbl[i].off)
|
||||
rtems_hdl_tbl[i].off(&rtems_hdl_tbl[i]);
|
||||
}
|
||||
}
|
||||
rtems_interrupt_enable(level);
|
||||
return 1;
|
||||
}
|
||||
|
||||
int CPU_rtems_irq_mngt_get(rtems_irq_global_settings** config)
|
||||
{
|
||||
*config = internal_config;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* High level IRQ handler called from shared_raw_irq_code_entry
|
||||
*/
|
||||
void C_dispatch_irq_handler (MPC5XX_Interrupt_frame *frame, unsigned int excNum)
|
||||
{
|
||||
register unsigned int irq;
|
||||
register unsigned uimbIntr; /* boolean */
|
||||
register unsigned oldMask; /* old siu pic masks */
|
||||
register unsigned msr;
|
||||
register unsigned new_msr;
|
||||
|
||||
/*
|
||||
* Handle decrementer interrupt
|
||||
*/
|
||||
if (excNum == ASM_DEC_VECTOR) {
|
||||
_CPU_MSR_GET(msr);
|
||||
new_msr = msr | MSR_EE;
|
||||
_CPU_MSR_SET(new_msr);
|
||||
|
||||
rtems_hdl_tbl[CPU_DECREMENTER].hdl(rtems_hdl_tbl[CPU_DECREMENTER].handle);
|
||||
|
||||
_CPU_MSR_SET(msr);
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* Handle external interrupt generated by USIU on PPC core
|
||||
*/
|
||||
while ((ppc_cached_irq_mask & usiu.sipend) != 0) {
|
||||
irq = (usiu.sivec >> 26);
|
||||
uimbIntr = (irq == CPU_UIMB_INTERRUPT);
|
||||
/*
|
||||
* Disable the interrupt of the same and lower priority.
|
||||
*/
|
||||
oldMask = ppc_cached_irq_mask;
|
||||
ppc_cached_irq_mask = oldMask & USIU_IvectMask[irq];
|
||||
usiu.simask = ppc_cached_irq_mask;
|
||||
/*
|
||||
* Acknowledge current interrupt. This has no effect on internal level
|
||||
* interrupts.
|
||||
*/
|
||||
usiu.sipend = (1 << (31 - irq));
|
||||
|
||||
if (uimbIntr) {
|
||||
/*
|
||||
* Look at the bits set in the UIMB interrupt-pending register. The
|
||||
* highest-order set bit indicates the handler we will run.
|
||||
*
|
||||
* Unfortunately, we can't easily mask individual UIMB interrupts
|
||||
* unless they use USIU levels 0 to 6, so we must mask all low-level
|
||||
* (level > 7) UIMB interrupts while we service any interrupt.
|
||||
*/
|
||||
int uipend = imb.uimb.uipend << 8;
|
||||
|
||||
if (uipend == 0) { /* spurious interrupt? use last vector */
|
||||
irq = CPU_UIMB_IRQ_MAX_OFFSET;
|
||||
}
|
||||
else {
|
||||
irq = CPU_UIMB_IRQ_MIN_OFFSET;
|
||||
for ( ; (uipend & 0x8000000) == 0; uipend <<= 1) {
|
||||
irq++;
|
||||
}
|
||||
}
|
||||
}
|
||||
_CPU_MSR_GET(msr);
|
||||
new_msr = msr | MSR_EE;
|
||||
_CPU_MSR_SET(new_msr);
|
||||
|
||||
rtems_hdl_tbl[irq].hdl(rtems_hdl_tbl[irq].handle);
|
||||
|
||||
_CPU_MSR_SET(msr);
|
||||
|
||||
ppc_cached_irq_mask = oldMask;
|
||||
usiu.simask = ppc_cached_irq_mask;
|
||||
}
|
||||
}
|
||||
312
bsps/powerpc/ss555/start/irq_asm.S
Normal file
312
bsps/powerpc/ss555/start/irq_asm.S
Normal file
@@ -0,0 +1,312 @@
|
||||
/*
|
||||
* irq_asm.S
|
||||
*
|
||||
* This file contains the assembly code for the PowerPC
|
||||
* IRQ veneers for RTEMS.
|
||||
*
|
||||
* The license and distribution terms for this file may be
|
||||
* found in the file LICENSE in this distribution or at
|
||||
* http://www.rtems.org/license/LICENSE.
|
||||
*
|
||||
*
|
||||
* MPC5xx port sponsored by Defence Research and Development Canada - Suffield
|
||||
* Copyright (C) 2004, Real-Time Systems Inc. (querbach@realtime.bc.ca)
|
||||
*
|
||||
* Derived from libbsp/powerpc/mbx8xx/irq/irq_asm.S:
|
||||
*
|
||||
* Modified to support the MCP750.
|
||||
* Modifications Copyright (C) 1999 Eric Valette. valette@crf.canon.fr
|
||||
*
|
||||
* Till Straumann <strauman@slac.stanford.edu>, 2003/7:
|
||||
* - store isr nesting level in _ISR_Nest_level rather than
|
||||
* SPRG0 - RTEMS relies on that variable.
|
||||
*/
|
||||
|
||||
#include <rtems/asm.h>
|
||||
#include <rtems/score/cpu.h>
|
||||
#include <rtems/score/percpu.h>
|
||||
#include <libcpu/vectors.h>
|
||||
#include <libcpu/raw_exception.h>
|
||||
|
||||
|
||||
#define SYNC \
|
||||
sync; \
|
||||
isync
|
||||
|
||||
/*
|
||||
* Common handler for interrupt exceptions.
|
||||
*
|
||||
* The function CPU_rtems_irq_mng_init() initializes the decrementer and
|
||||
* external interrupt entries in the exception handler table with pointers
|
||||
* to this routine, which saves the remainder of the interrupted code's
|
||||
* state, then calls C_dispatch_irq_handler().
|
||||
*
|
||||
* On entry, R1 points to a new exception stack frame in which R3, R4, and
|
||||
* LR have been saved. R4 holds the exception number.
|
||||
*/
|
||||
PUBLIC_VAR(C_dispatch_irq_handler)
|
||||
|
||||
PUBLIC_VAR(dispatch_irq_handler)
|
||||
SYM (dispatch_irq_handler):
|
||||
/*
|
||||
* Save SRR0/SRR1 As soon As possible as it is the minimal needed
|
||||
* to re-enable exception processing.
|
||||
*
|
||||
* Note that R2 should never change (it's the EABI pointer to
|
||||
* .sdata2), but we save it just in case.
|
||||
*/
|
||||
stw r0, GPR0_OFFSET(r1)
|
||||
stw r2, GPR2_OFFSET(r1)
|
||||
|
||||
mfsrr0 r0
|
||||
mfsrr1 r3
|
||||
|
||||
stw r0, SRR0_FRAME_OFFSET(r1)
|
||||
stw r3, SRR1_FRAME_OFFSET(r1)
|
||||
|
||||
/*
|
||||
* Enable exception recovery. Also enable FP so that FP context
|
||||
* can be saved and restored (using FP instructions).
|
||||
*/
|
||||
mfmsr r3
|
||||
ori r3, r3, MSR_RI | MSR_FP
|
||||
mtmsr r3
|
||||
SYNC
|
||||
|
||||
/*
|
||||
* Push C scratch registers on the current stack. It may actually be
|
||||
* the thread stack or the interrupt stack. Anyway we have to make
|
||||
* it in order to be able to call C/C++ functions. Depending on the
|
||||
* nesting interrupt level, we will switch to the right stack later.
|
||||
*/
|
||||
stw r5, GPR5_OFFSET(r1)
|
||||
stw r6, GPR6_OFFSET(r1)
|
||||
stw r7, GPR7_OFFSET(r1)
|
||||
stw r8, GPR8_OFFSET(r1)
|
||||
stw r9, GPR9_OFFSET(r1)
|
||||
stw r10, GPR10_OFFSET(r1)
|
||||
stw r11, GPR11_OFFSET(r1)
|
||||
stw r12, GPR12_OFFSET(r1)
|
||||
stw r13, GPR13_OFFSET(r1)
|
||||
|
||||
mfcr r5
|
||||
mfctr r6
|
||||
mfxer r7
|
||||
|
||||
stw r5, EXC_CR_OFFSET(r1)
|
||||
stw r6, EXC_CTR_OFFSET(r1)
|
||||
stw r7, EXC_XER_OFFSET(r1)
|
||||
|
||||
/*
|
||||
* Add some non volatile registers to store information that will be
|
||||
* used when returning from C handler.
|
||||
*/
|
||||
stw r14, GPR14_OFFSET(r1)
|
||||
stw r15, GPR15_OFFSET(r1)
|
||||
|
||||
/*
|
||||
* Save current stack pointer location in R14.
|
||||
*/
|
||||
addi r14, r1, 0
|
||||
|
||||
/*
|
||||
* store part of THREAD_DISPATCH_DISABLE_LEVEL address in R15
|
||||
*/
|
||||
addis r15, 0, THREAD_DISPATCH_DISABLE_LEVEL@ha
|
||||
|
||||
/*
|
||||
* Retrieve current nesting level from _ISR_Nest_level
|
||||
*/
|
||||
lis r7, ISR_NEST_LEVEL@ha
|
||||
lwz r3, ISR_NEST_LEVEL@l(r7)
|
||||
|
||||
/*
|
||||
* Check if stack switch is necessary
|
||||
*/
|
||||
cmpwi r3, 0
|
||||
bne nested
|
||||
|
||||
mfspr r1, SPRG1 /* switch to interrupt stack */
|
||||
nested:
|
||||
|
||||
/*
|
||||
* Start Incrementing nesting level in R3
|
||||
*/
|
||||
addi r3, r3, 1
|
||||
|
||||
/*
|
||||
* Start Incrementing THREAD_DISPATCH_DISABLE_LEVEL R4 = THREAD_DISPATCH_DISABLE_LEVEL
|
||||
*/
|
||||
lwz r6, THREAD_DISPATCH_DISABLE_LEVEL@l(r15)
|
||||
|
||||
/* store new nesting level in _ISR_Nest_level */
|
||||
stw r3, ISR_NEST_LEVEL@l(r7)
|
||||
|
||||
addi r6, r6, 1
|
||||
|
||||
/*
|
||||
* store new THREAD_DISPATCH_DISABLE_LEVEL value
|
||||
*/
|
||||
stw r6, THREAD_DISPATCH_DISABLE_LEVEL@l(r15)
|
||||
|
||||
/*
|
||||
* We are now running on the interrupt stack. External and decrementer
|
||||
* exceptions are still disabled. I see no purpose trying to optimize
|
||||
* further assembler code.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Call C exception handler for decrementer or external interrupt.
|
||||
* Pass frame along just in case..
|
||||
*
|
||||
* C_dispatch_irq_handler(cpu_interrupt_frame* r3, vector r4)
|
||||
*/
|
||||
addi r3, r14, 0x8
|
||||
bl C_dispatch_irq_handler
|
||||
|
||||
/*
|
||||
* start decrementing nesting level. Note : do not test result against 0
|
||||
* value as an easy exit condition because if interrupt nesting level > 1
|
||||
* then THREAD_DISPATCH_DISABLE_LEVEL > 1
|
||||
*/
|
||||
lis r7, ISR_NEST_LEVEL@ha
|
||||
lwz r4, ISR_NEST_LEVEL@l(r7)
|
||||
|
||||
/*
|
||||
* start decrementing THREAD_DISPATCH_DISABLE_LEVEL
|
||||
*/
|
||||
lwz r3,THREAD_DISPATCH_DISABLE_LEVEL@l(r15)
|
||||
|
||||
addi r4, r4, -1 /* Continue decrementing nesting level */
|
||||
addi r3, r3, -1 /* Continue decrementing THREAD_DISPATCH_DISABLE_LEVEL */
|
||||
|
||||
stw r4, ISR_NEST_LEVEL@l(r7) /* End decrementing nesting level */
|
||||
stw r3,THREAD_DISPATCH_DISABLE_LEVEL@l(r15) /* End decrementing THREAD_DISPATCH_DISABLE_LEVEL */
|
||||
|
||||
cmpwi r3, 0
|
||||
|
||||
/*
|
||||
* switch back to original stack (done here just optimize registers
|
||||
* contention. Could have been done before...)
|
||||
*/
|
||||
addi r1, r14, 0
|
||||
bne easy_exit /* if (THREAD_DISPATCH_DISABLE_LEVEL != 0) goto easy_exit */
|
||||
|
||||
/*
|
||||
* Here we are running again on the thread system stack.
|
||||
* We have interrupt nesting level = THREAD_DISPATCH_DISABLE_LEVEL = 0.
|
||||
* Interrupt are still disabled. Time to check if scheduler request to
|
||||
* do something with the current thread...
|
||||
*/
|
||||
addis r4, 0, DISPATCH_NEEDED@ha
|
||||
lbz r5, DISPATCH_NEEDED@l(r4)
|
||||
cmpwi r5, 0
|
||||
beq easy_exit
|
||||
|
||||
/*
|
||||
* going to call _Thread_Dispatch
|
||||
* Push a complete exception like frame...
|
||||
*/
|
||||
stmw r16, GPR16_OFFSET(r1)
|
||||
addi r3, r1, 0x8
|
||||
|
||||
/*
|
||||
* compute SP at exception entry
|
||||
*/
|
||||
addi r4, r1, EXCEPTION_FRAME_END
|
||||
|
||||
/*
|
||||
* store it at the right place
|
||||
*/
|
||||
stw r4, GPR1_OFFSET(r1)
|
||||
|
||||
/*
|
||||
* Call High Level signal handling code
|
||||
*/
|
||||
bl _Thread_Dispatch
|
||||
|
||||
/*
|
||||
* start restoring exception like frame
|
||||
*/
|
||||
lwz r31, EXC_CTR_OFFSET(r1)
|
||||
lwz r30, EXC_XER_OFFSET(r1)
|
||||
lwz r29, EXC_CR_OFFSET(r1)
|
||||
lwz r28, EXC_LR_OFFSET(r1)
|
||||
|
||||
mtctr r31
|
||||
mtxer r30
|
||||
mtcr r29
|
||||
mtlr r28
|
||||
|
||||
lmw r4, GPR4_OFFSET(r1)
|
||||
lwz r2, GPR2_OFFSET(r1)
|
||||
lwz r0, GPR0_OFFSET(r1)
|
||||
|
||||
/*
|
||||
* Make path non recoverable...
|
||||
*/
|
||||
mtspr nri, r0
|
||||
SYNC
|
||||
|
||||
/*
|
||||
* Restore rfi related settings
|
||||
*/
|
||||
|
||||
lwz r3, SRR1_FRAME_OFFSET(r1)
|
||||
mtsrr1 r3
|
||||
lwz r3, SRR0_FRAME_OFFSET(r1)
|
||||
mtsrr0 r3
|
||||
|
||||
lwz r3, GPR3_OFFSET(r1)
|
||||
addi r1,r1, EXCEPTION_FRAME_END
|
||||
SYNC
|
||||
rfi
|
||||
|
||||
|
||||
easy_exit:
|
||||
/*
|
||||
* start restoring interrupt frame
|
||||
*/
|
||||
lwz r3, EXC_CTR_OFFSET(r1)
|
||||
lwz r4, EXC_XER_OFFSET(r1)
|
||||
lwz r5, EXC_CR_OFFSET(r1)
|
||||
lwz r6, EXC_LR_OFFSET(r1)
|
||||
|
||||
mtctr r3
|
||||
mtxer r4
|
||||
mtcr r5
|
||||
mtlr r6
|
||||
|
||||
lwz r15, GPR15_OFFSET(r1)
|
||||
lwz r14, GPR14_OFFSET(r1)
|
||||
lwz r13, GPR13_OFFSET(r1)
|
||||
lwz r12, GPR12_OFFSET(r1)
|
||||
lwz r11, GPR11_OFFSET(r1)
|
||||
lwz r10, GPR10_OFFSET(r1)
|
||||
lwz r9, GPR9_OFFSET(r1)
|
||||
lwz r8, GPR8_OFFSET(r1)
|
||||
lwz r7, GPR7_OFFSET(r1)
|
||||
lwz r6, GPR6_OFFSET(r1)
|
||||
lwz r5, GPR5_OFFSET(r1)
|
||||
|
||||
/*
|
||||
* Disable nested exception processing.
|
||||
*/
|
||||
mtspr nri, r0
|
||||
SYNC
|
||||
|
||||
/*
|
||||
* Restore rfi related settings
|
||||
*/
|
||||
lwz r4, SRR1_FRAME_OFFSET(r1)
|
||||
lwz r3, SRR0_FRAME_OFFSET(r1)
|
||||
lwz r2, GPR2_OFFSET(r1)
|
||||
lwz r0, GPR0_OFFSET(r1)
|
||||
|
||||
mtsrr1 r4
|
||||
mtsrr0 r3
|
||||
lwz r4, GPR4_OFFSET(r1)
|
||||
lwz r3, GPR3_OFFSET(r1)
|
||||
addi r1,r1, EXCEPTION_FRAME_END
|
||||
SYNC
|
||||
rfi
|
||||
165
bsps/powerpc/ss555/start/irq_init.c
Normal file
165
bsps/powerpc/ss555/start/irq_init.c
Normal file
@@ -0,0 +1,165 @@
|
||||
/*
|
||||
* irq_init.c
|
||||
*
|
||||
* This file contains the implementation of rtems initialization
|
||||
* related to interrupt handling.
|
||||
*/
|
||||
|
||||
/*
|
||||
* MPC5xx port sponsored by Defence Research and Development Canada - Suffield
|
||||
* Copyright (C) 2004, Real-Time Systems Inc. (querbach@realtime.bc.ca)
|
||||
*
|
||||
* Derived from libbsp/powerpc/mbx8xx/irq/irq_init.c:
|
||||
*
|
||||
* CopyRight (C) 2001 valette@crf.canon.fr
|
||||
*
|
||||
* The license and distribution terms for this file may be
|
||||
* found in the file LICENSE in this distribution or at
|
||||
* http://www.rtems.org/license/LICENSE.
|
||||
*/
|
||||
|
||||
#include <rtems.h>
|
||||
#include <mpc5xx.h>
|
||||
#include <libcpu/vectors.h>
|
||||
#include <libcpu/raw_exception.h>
|
||||
#include <bsp/irq.h>
|
||||
|
||||
extern rtems_exception_handler_t dispatch_irq_handler;
|
||||
|
||||
volatile unsigned int ppc_cached_irq_mask;
|
||||
|
||||
/*
|
||||
* default methods
|
||||
*/
|
||||
static void nop_hdl(rtems_irq_hdl_param ignored)
|
||||
{
|
||||
}
|
||||
|
||||
static void nop_irq_enable(const struct __rtems_irq_connect_data__*ignored)
|
||||
{
|
||||
}
|
||||
|
||||
static void nop_raw_enable(
|
||||
const struct __rtems_raw_except_connect_data__*ignored
|
||||
)
|
||||
{
|
||||
}
|
||||
|
||||
static int irq_is_connected(const struct __rtems_irq_connect_data__*ignored)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int raw_is_connected(const struct __rtems_raw_except_connect_data__*ignored)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static rtems_irq_connect_data rtemsIrq[CPU_IRQ_COUNT];
|
||||
static rtems_irq_global_settings initial_config;
|
||||
static rtems_irq_connect_data defaultIrq = {
|
||||
0, /* vector */
|
||||
nop_hdl, /* hdl */
|
||||
NULL, /* handle */
|
||||
nop_irq_enable, /* on */
|
||||
nop_irq_enable, /* off */
|
||||
irq_is_connected /* isOn */
|
||||
};
|
||||
|
||||
static rtems_irq_prio irqPrioTable[CPU_IRQ_COUNT]={
|
||||
/*
|
||||
* actual priorities for interrupt :
|
||||
* 0 means that only current interrupt is masked
|
||||
* 255 means all other interrupts are masked
|
||||
*/
|
||||
/*
|
||||
* USIU interrupts.
|
||||
*/
|
||||
7,7, 6,6, 5,5, 4,4, 3,3, 2,2, 1,1, 0,0,
|
||||
/*
|
||||
* UIMB Interrupts
|
||||
*
|
||||
* Note that the first 8 UIMB interrupts overlap the 8 external USIU
|
||||
* interrupts.
|
||||
*/
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
/*
|
||||
* Processor exceptions handled as interrupts
|
||||
*/
|
||||
0
|
||||
};
|
||||
|
||||
static void CPU_USIU_irq_init(void)
|
||||
{
|
||||
/*
|
||||
* In theory we should initialize two registers at least : SIMASK and
|
||||
* SIEL. SIMASK is reset at 0 value meaning no interrupts. If someone
|
||||
* find a reasonnable value for SIEL, and the need to change it, please
|
||||
* feel free to add it here.
|
||||
*/
|
||||
ppc_cached_irq_mask = 0;
|
||||
usiu.simask = ppc_cached_irq_mask;
|
||||
usiu.sipend = 0xffff0000;
|
||||
usiu.siel = usiu.siel;
|
||||
}
|
||||
|
||||
/*
|
||||
* Initialize UIMB interrupt management
|
||||
*/
|
||||
static void CPU_UIMB_irq_init(void)
|
||||
{
|
||||
}
|
||||
|
||||
void CPU_rtems_irq_mng_init(unsigned cpuId)
|
||||
{
|
||||
rtems_raw_except_connect_data vectorDesc;
|
||||
int i;
|
||||
|
||||
CPU_USIU_irq_init();
|
||||
CPU_UIMB_irq_init();
|
||||
/*
|
||||
* Initialize Rtems management interrupt table
|
||||
*/
|
||||
/*
|
||||
* re-init the rtemsIrq table
|
||||
*/
|
||||
for (i = 0; i < CPU_IRQ_COUNT; i++) {
|
||||
rtemsIrq[i] = defaultIrq;
|
||||
rtemsIrq[i].name = i;
|
||||
}
|
||||
/*
|
||||
* Init initial Interrupt management config
|
||||
*/
|
||||
initial_config.irqNb = CPU_IRQ_COUNT;
|
||||
initial_config.defaultEntry = defaultIrq;
|
||||
initial_config.irqHdlTbl = rtemsIrq;
|
||||
initial_config.irqBase = CPU_ASM_IRQ_VECTOR_BASE;
|
||||
initial_config.irqPrioTbl = irqPrioTable;
|
||||
|
||||
if (!CPU_rtems_irq_mngt_set(&initial_config)) {
|
||||
/*
|
||||
* put something here that will show the failure...
|
||||
*/
|
||||
rtems_panic("Unable to initialize RTEMS interrupt Management\n");
|
||||
}
|
||||
|
||||
/*
|
||||
* We must connect the raw irq handler for the two
|
||||
* expected interrupt sources : decrementer and external interrupts.
|
||||
*/
|
||||
vectorDesc.exceptIndex = ASM_DEC_VECTOR;
|
||||
vectorDesc.hdl.vector = ASM_DEC_VECTOR;
|
||||
vectorDesc.hdl.raw_hdl = dispatch_irq_handler;
|
||||
vectorDesc.on = nop_raw_enable;
|
||||
vectorDesc.off = nop_raw_enable;
|
||||
vectorDesc.isOn = raw_is_connected;
|
||||
if (!mpc5xx_set_exception (&vectorDesc)) {
|
||||
rtems_panic("Unable to initialize RTEMS decrementer raw exception\n");
|
||||
}
|
||||
vectorDesc.exceptIndex = ASM_EXT_VECTOR;
|
||||
vectorDesc.hdl.vector = ASM_EXT_VECTOR;
|
||||
if (!mpc5xx_set_exception (&vectorDesc)) {
|
||||
rtems_panic("Unable to initialize RTEMS external raw exception\n");
|
||||
}
|
||||
}
|
||||
190
bsps/powerpc/ss555/start/raw_exception.c
Normal file
190
bsps/powerpc/ss555/start/raw_exception.c
Normal file
@@ -0,0 +1,190 @@
|
||||
/*
|
||||
* raw_exception.c - This file contains implementation of C functions to
|
||||
* Instantiate mpc5xx primary exception entries.
|
||||
* More detailled information can be found on the Motorola
|
||||
* site and more precisely in the following book:
|
||||
*
|
||||
* MPC555/MPC556 User's Manual
|
||||
* Motorola REF : MPC555UM/D Rev. 3, 2000 October 15
|
||||
*
|
||||
*
|
||||
* MPC5xx port sponsored by Defence Research and Development Canada - Suffield
|
||||
* Copyright (C) 2004, Real-Time Systems Inc. (querbach@realtime.bc.ca)
|
||||
*
|
||||
* Derived from libcpu/powerpc/mpc8xx/exceptions/raw_exception.c:
|
||||
*
|
||||
* Copyright (C) 1999 Eric Valette (valette@crf.canon.fr)
|
||||
* Canon Centre Recherche France.
|
||||
*
|
||||
* The license and distribution terms for this file may be
|
||||
* found in the file LICENSE in this distribution or at
|
||||
* http://www.rtems.org/license/LICENSE.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <rtems.h>
|
||||
#include <rtems/score/powerpc.h>
|
||||
#include <libcpu/raw_exception.h>
|
||||
#include <libcpu/cpuIdent.h>
|
||||
#include <rtems/bspIo.h> /* for printk */
|
||||
#include <string.h>
|
||||
|
||||
static rtems_raw_except_connect_data* raw_except_table;
|
||||
static rtems_raw_except_connect_data default_raw_except_entry;
|
||||
static rtems_raw_except_global_settings* local_settings;
|
||||
|
||||
int mpc5xx_vector_is_valid(rtems_vector vector)
|
||||
{
|
||||
switch (current_ppc_cpu) {
|
||||
case PPC_5XX:
|
||||
switch(vector) {
|
||||
case ASM_RESET_VECTOR:
|
||||
case ASM_MACH_VECTOR:
|
||||
|
||||
case ASM_EXT_VECTOR:
|
||||
case ASM_ALIGN_VECTOR:
|
||||
case ASM_PROG_VECTOR:
|
||||
case ASM_FLOAT_VECTOR:
|
||||
case ASM_DEC_VECTOR:
|
||||
|
||||
case ASM_SYS_VECTOR:
|
||||
case ASM_TRACE_VECTOR:
|
||||
case ASM_FLOATASSIST_VECTOR:
|
||||
|
||||
case ASM_SOFTEMUL_VECTOR:
|
||||
|
||||
case ASM_IPROT_VECTOR:
|
||||
case ASM_DPROT_VECTOR:
|
||||
|
||||
case ASM_DBREAK_VECTOR:
|
||||
case ASM_IBREAK_VECTOR:
|
||||
case ASM_MEBREAK_VECTOR:
|
||||
case ASM_NMEBREAK_VECTOR:
|
||||
return 1;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
default:
|
||||
printk("Please complete libcpu/powerpc/mpc5xx/exceptions/raw_exception.c\n");
|
||||
printk("current_ppc_cpu = %x\n", current_ppc_cpu);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
int mpc5xx_set_exception (const rtems_raw_except_connect_data* except)
|
||||
{
|
||||
rtems_interrupt_level level;
|
||||
|
||||
if (!mpc5xx_vector_is_valid(except->exceptIndex)) {
|
||||
return 0;
|
||||
}
|
||||
/*
|
||||
* Check if default handler is actually connected. If not issue an error.
|
||||
* You must first get the current handler via mpc5xx_get_current_exception
|
||||
* and then disconnect it using mpc5xx_delete_exception.
|
||||
* RATIONALE : to always have the same transition by forcing the user
|
||||
* to get the previous handler before accepting to disconnect.
|
||||
*/
|
||||
if (exception_handler_table[except->exceptIndex] !=
|
||||
default_raw_except_entry.hdl.raw_hdl) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
rtems_interrupt_disable(level);
|
||||
|
||||
raw_except_table[except->exceptIndex] = *except;
|
||||
|
||||
exception_handler_table[except->exceptIndex] = except->hdl.raw_hdl;
|
||||
if (except->on)
|
||||
except->on(except);
|
||||
|
||||
rtems_interrupt_enable(level);
|
||||
return 1;
|
||||
}
|
||||
|
||||
int mpc5xx_get_current_exception (rtems_raw_except_connect_data* except)
|
||||
{
|
||||
if (!mpc5xx_vector_is_valid(except->exceptIndex)){
|
||||
return 0;
|
||||
}
|
||||
|
||||
*except = raw_except_table[except->exceptIndex];
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
int mpc5xx_delete_exception (const rtems_raw_except_connect_data* except)
|
||||
{
|
||||
rtems_interrupt_level level;
|
||||
|
||||
if (!mpc5xx_vector_is_valid(except->exceptIndex)){
|
||||
return 0;
|
||||
}
|
||||
/*
|
||||
* Check if handler passed is actually connected. If not issue an error.
|
||||
* You must first get the current handler via mpc5xx_get_current_exception
|
||||
* and then disconnect it using mpc5xx_delete_exception.
|
||||
* RATIONALE : to always have the same transition by forcing the user
|
||||
* to get the previous handler before accepting to disconnect.
|
||||
*/
|
||||
if (exception_handler_table[except->exceptIndex] != except->hdl.raw_hdl) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
rtems_interrupt_disable(level);
|
||||
|
||||
if (except->off)
|
||||
except->off(except);
|
||||
exception_handler_table[except->exceptIndex] =
|
||||
default_raw_except_entry.hdl.raw_hdl;
|
||||
|
||||
raw_except_table[except->exceptIndex] = default_raw_except_entry;
|
||||
raw_except_table[except->exceptIndex].exceptIndex = except->exceptIndex;
|
||||
|
||||
rtems_interrupt_enable(level);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
/*
|
||||
* Exception global init.
|
||||
*
|
||||
* Install exception handler pointers from the raw exception table into the
|
||||
* exception handler table.
|
||||
*/
|
||||
int mpc5xx_init_exceptions (rtems_raw_except_global_settings* config)
|
||||
{
|
||||
unsigned i;
|
||||
rtems_interrupt_level level;
|
||||
|
||||
/*
|
||||
* store various accelerators
|
||||
*/
|
||||
raw_except_table = config->rawExceptHdlTbl;
|
||||
local_settings = config;
|
||||
default_raw_except_entry = config->defaultRawEntry;
|
||||
|
||||
rtems_interrupt_disable(level);
|
||||
|
||||
for (i = 0; i < NUM_EXCEPTIONS; i++) {
|
||||
exception_handler_table[i] = raw_except_table[i].hdl.raw_hdl;
|
||||
|
||||
if (raw_except_table[i].hdl.raw_hdl != default_raw_except_entry.hdl.raw_hdl) {
|
||||
if (raw_except_table[i].on)
|
||||
raw_except_table[i].on(&raw_except_table[i]);
|
||||
}
|
||||
else {
|
||||
if (raw_except_table[i].off)
|
||||
raw_except_table[i].off(&raw_except_table[i]);
|
||||
}
|
||||
}
|
||||
rtems_interrupt_enable(level);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
int mpc5xx_get_exception_config (rtems_raw_except_global_settings** config)
|
||||
{
|
||||
*config = local_settings;
|
||||
return 1;
|
||||
}
|
||||
Reference in New Issue
Block a user