diff --git a/cpu/mips32r2_common/include/eic.h b/cpu/mips32r2_common/include/eic.h new file mode 100644 index 0000000000..c3a3d4f4fc --- /dev/null +++ b/cpu/mips32r2_common/include/eic.h @@ -0,0 +1,72 @@ +/* + * Copyright 2016, Imagination Technologies Limited and/or its + * affiliated group companies. + * This file is subject to the terms and conditions of the GNU Lesser + * General Public License v2.1. See the file LICENSE in the top level + * directory for more details. + */ + +/** + * @ingroup cpu + * @brief Imagination Technologies MIPS32R2 Common implementation + * @{ + * + * @file + * @brief API for supporting External Interrupt Controllers (EIC mode) + * + * @author Neil Jones + */ + +#ifndef EIC_H +#define EIC_H + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief External ISR callback + */ +typedef void (*external_isr_ptr_t)(void); + +/** + * @brief Set External ISR callback + */ +void set_external_isr_cb(int vecNum, external_isr_ptr_t cbFunc); + +/** + * @brief Configure interrupt priority + * + * @param[in] vecNum + * @param[in] priority + * @param[in] subpriority + */ +void eic_configure_priority(int vecNum, int priority, int subpriority); + +/** + * @brief Enable interrupt + * + * @param[in] vecNum + */ +void eic_enable(int vecNum); + +/** + * @brief Disable interrupt + * + * @param[in] vecNum + */ +void eic_disable(int vecNum); + +/** + * @brief Clear interrupt flag + * + * @param[in] vecNum + */ +void eic_clear_flag(int vecNum); + +#ifdef __cplusplus +} +#endif + +#endif /* EIC_H */ +/** @} */ diff --git a/cpu/mips32r2_common/include/eic_irq.h b/cpu/mips32r2_common/include/eic_irq.h deleted file mode 100644 index 8c9832391b..0000000000 --- a/cpu/mips32r2_common/include/eic_irq.h +++ /dev/null @@ -1,69 +0,0 @@ -/* - * Copyright 2016, Imagination Technologies Limited and/or its - * affiliated group companies. - * This file is subject to the terms and conditions of the GNU Lesser - * General Public License v2.1. See the file LICENSE in the top level - * directory for more details. - */ - -/** - * @ingroup cpu_mips32r2_common - * @brief Imagination Technologies MIPS32R2 Common implementation - * @{ - * - * @file - * @brief API for supporting External Interrupt Controllers (EIC mode) - * - * @author Neil Jones - */ - -#ifndef EIC_IRQ_H -#define EIC_IRQ_H - -#ifdef __cplusplus -extern "C" { -#endif - -/** - * @ brief Internal Interrupt numbers - * - * MIPS cores have a few internally generated interrupts from the Timer, - * Performance Counters and Fast Debug Channel hardware, in EIC mode these - * become outputs from the core and are connected to the external controller, - * the external control then loops these back at whichever IPL it decides - * - * We use negative numbers to represent these, leaving positive numbers free for - * the SoC specific interrupts - * @{ - */ -#define EIC_IRQ_TIMER (-1) -#define EIC_IRQ_FDC (-2) -#define EIC_IRQ_PC (-3) -/** @} */ - -/** - * @brief Configure and route the interrupt - */ -void eic_irq_configure(int irq_num); - -/** - * @brief Enable an interrupt - */ -void eic_irq_enable(int irq_num); - -/** - * @brief Disable an interrupt - */ -void eic_irq_disable(int irq_num); - -/** - * @brief Acknowledge an interrupt - */ -void eic_irq_ack(int irq_num); - -#ifdef __cplusplus -} -#endif - -#endif /* EIC_IRQ_H */ -/** @} */ diff --git a/cpu/mips_pic32_common/Makefile.dep b/cpu/mips_pic32_common/Makefile.dep index f3fd6729ba..005e02ec4a 100644 --- a/cpu/mips_pic32_common/Makefile.dep +++ b/cpu/mips_pic32_common/Makefile.dep @@ -1,4 +1,7 @@ USEMODULE += mips_pic32_common USEMODULE += mips_pic32_common_periph +# mips32 needs periph_timer for its gettimeofday() implementation +USEMODULE += periph_timer + include $(RIOTCPU)/mips32r2_common/Makefile.dep diff --git a/cpu/mips_pic32_common/eic.c b/cpu/mips_pic32_common/eic.c new file mode 100644 index 0000000000..6c2b016935 --- /dev/null +++ b/cpu/mips_pic32_common/eic.c @@ -0,0 +1,95 @@ +/* + * Copyright (C) 2019 Francois Berder + * + * This file is subject to the terms and conditions of the GNU Lesser + * General Public License v2.1. See the file LICENSE in the top level + * directory for more details. + * + */ + +#include "cpu_conf.h" +#include "eic.h" +#include + +#if defined(CPU_FAM_PIC32MX) +#define VEC_NUMOF (64) +#elif defined (CPU_FAM_PIC32MZ) +#define VEC_NUMOF (256) +#endif + +#define IECSET(V) *(volatile uint32_t *)((uintptr_t)&IEC0SET + 0x10 * ((V) / 32)) +#define IECCLR(V) *(volatile uint32_t *)((uintptr_t)&IEC0CLR + 0x10 * ((V) / 32)) +#define IFSCLR(V) *(volatile uint32_t *)((uintptr_t)&IFS0CLR + 0x10 * ((V) / 32)) +#define IPC(V) *(volatile uint32_t *)((uintptr_t)&IPC0 + 0x10 * ((V) / 4)) + +static external_isr_ptr_t vectors[VEC_NUMOF]; + +void set_external_isr_cb(int vecNum, external_isr_ptr_t cbFunc) +{ + if (vecNum < VEC_NUMOF) + vectors[vecNum] = cbFunc; +} + +/* note Compiler inserts GP context save + restore code (to current stack). */ +/* + * This is a hack - currently the toolchain does not support correct placement + * of EIC mode vectors (it is coming though) But we can support non-vectored EIC + * mode and note the default PIC32 interrupt controller (which uses EIC + + * MCU-ASE) defaults to non vectored mode anyway with all interrupts coming via + * vector 0 which is equivalent to 'sw0' in 'VI' mode. + * + * Thus all EIC interrupts should be decoded here. + * + * When toolchain support is available we could move to full vector mode but + * this does take up significant space (MCU-ASE provides 256 vectors at 32B + * spacing (the default) that's 8KB of vector space!), So a single entry point + * may be better anyway. + * + */ +void __attribute__ ((interrupt("vector=sw0"), keep_interrupts_masked)) _mips_isr_sw0(void) +{ +#if defined(CPU_FAM_PIC32MX) + int vecNum = INTSTAT & _INTSTAT_VEC_MASK; +#elif defined (CPU_FAM_PIC32MZ) + int vecNum = INTSTAT & _INTSTAT_SIRQ_MASK; +#endif + + if (vectors[vecNum]) + vectors[vecNum](); +} + +void eic_configure_priority(int vecNum, int priority, int subpriority) +{ + unsigned int offset; + + if (vecNum >= VEC_NUMOF) + return; + + offset = 8 * (vecNum & 0x3); + IPC(vecNum) &= ~(0x1F << offset); + IPC(vecNum) |= ((priority << 2) | (subpriority)) << offset; +} + +void eic_enable(int vecNum) +{ + if (vecNum >= VEC_NUMOF) + return; + + IECSET(vecNum) = 1U << (vecNum & 0x1F); +} + +void eic_disable(int vecNum) +{ + if (vecNum >= VEC_NUMOF) + return; + + IECCLR(vecNum) = 1U << (vecNum & 0x1F); +} + +void eic_clear_flag(int vecNum) +{ + if (vecNum >= VEC_NUMOF) + return; + + IFSCLR(vecNum) = 1U << (vecNum & 0x1F); +} diff --git a/cpu/mips32r2_common/periph/timer.c b/cpu/mips_pic32_common/periph/timer.c similarity index 60% rename from cpu/mips32r2_common/periph/timer.c rename to cpu/mips_pic32_common/periph/timer.c index 5a88fa8a6d..818793478b 100644 --- a/cpu/mips32r2_common/periph/timer.c +++ b/cpu/mips_pic32_common/periph/timer.c @@ -34,9 +34,7 @@ #include "div.h" #include -#ifdef EIC_IRQ -#include "eic_irq.h" -#endif +#include "eic.h" /* * setting TIMER_ACCURACY_SHIFT lower will improve accuracy @@ -70,7 +68,48 @@ static timer_isr_ctx_t timer_isr_ctx; volatile unsigned int counter; volatile unsigned int compares[CHANNELS]; -static volatile int spurious_int; + +static void timer_isr(void) +{ + IFS0CLR =_IFS0_CTIF_MASK; + + uint32_t status = irq_disable(); + counter += TIMER_ACCURACY; + irq_restore(status); + + if (counter == compares[0]) { + /* + * The Xtimer code expects the ISR to take some time + * but our counter is a fake software one, so bump it a + * bit to give the impression some time elapsed in the ISR. + * Without this the callback ( _shoot(timer) on xtimer_core.c ) + * never fires. + */ + counter += TIMER_ACCURACY; + timer_isr_ctx.cb(timer_isr_ctx.arg, 0); + + if (sched_context_switch_request) { + thread_yield(); + } + } + if (counter == compares[1]) { + timer_isr_ctx.cb(timer_isr_ctx.arg, 1); + + if (sched_context_switch_request) { + thread_yield(); + } + } + if (counter == compares[2]) { + timer_isr_ctx.cb(timer_isr_ctx.arg, 2); + + if (sched_context_switch_request) { + thread_yield(); + } + } + + mips_setcompare(mips_getcount() + TICKS_PER_US * TIMER_ACCURACY); +} + /* * The mips toolchain C library does not implement gettimeofday() @@ -111,12 +150,9 @@ int timer_init(tim_t dev, unsigned long freq, timer_cb_t cb, void *arg) mips32_bc_c0(C0_CAUSE, CR_DC); /* Enable Timer Interrupts */ -#ifdef EIC_IRQ - eic_irq_configure(EIC_IRQ_TIMER); -#else - mips32_bs_c0(C0_STATUS, SR_HINT5); -#endif - + set_external_isr_cb(_CORE_TIMER_VECTOR, timer_isr); + eic_configure_priority(_CORE_TIMER_VECTOR, 1, 0); + eic_enable(_CORE_TIMER_VECTOR); return 0; } @@ -189,95 +225,3 @@ void timer_stop(tim_t dev) (void)dev; mips32_bs_c0(C0_CAUSE, CR_DC); } - -void timer_irq_enable(tim_t dev) -{ - (void)dev; -#ifdef EIC_IRQ - eic_irq_enable(EIC_IRQ_TIMER); -#else - mips32_bs_c0(C0_STATUS, SR_HINT5); -#endif - -} - -void timer_irq_disable(tim_t dev) -{ - (void)dev; -#ifdef EIC_IRQ - eic_irq_disable(EIC_IRQ_TIMER); -#else - mips32_bc_c0(C0_STATUS, SR_HINT5); -#endif -} - -/* note Compiler inserts GP context save + restore code (to current stack). */ -#ifdef EIC_IRQ -/* - * This is a hack - currently the toolchain does not support correct placement - * of EIC mode vectors (it is coming though) But we can support non-vectored EIC - * mode and note the default PIC32 interrupt controller (which uses EIC + - * MCU-ASE) defaults to non vectored mode anyway with all interrupts coming via - * vector 0 which is equivalent to 'sw0' in 'VI' mode. - * - * Thus all EIC interrupts should be decoded here (currently only Timer is - * used) - * - * When toolchain support is available we could move to full vector mode but - * this does take up significant space (MCU-ASE provides 256 vectors at 32B - * spacing (the default) that's 8KB of vector space!), So a single entry point - * may be better anyway. - * - */ -void __attribute__ ((interrupt("vector=sw0"), keep_interrupts_masked)) _mips_isr_sw0(void) -#else -void __attribute__ ((interrupt("vector=hw5"))) _mips_isr_hw5(void) -#endif -{ - register int cr = mips_getcr(); - - if (cr & CR_TI) { -#ifdef EIC_IRQ - eic_irq_ack(EIC_IRQ_TIMER); -#endif - uint32_t status = irq_disable(); - counter += TIMER_ACCURACY; - irq_restore(status); - - if (counter == compares[0]) { - /* - * The Xtimer code expects the ISR to take some time - * but our counter is a fake software one, so bump it a - * bit to give the impression some time elapsed in the ISR. - * Without this the callback ( _shoot(timer) on xtimer_core.c ) - * never fires. - */ - counter += TIMER_ACCURACY; - timer_isr_ctx.cb(timer_isr_ctx.arg, 0); - - if (sched_context_switch_request) { - thread_yield(); - } - } - if (counter == compares[1]) { - timer_isr_ctx.cb(timer_isr_ctx.arg, 1); - - if (sched_context_switch_request) { - thread_yield(); - } - } - if (counter == compares[2]) { - timer_isr_ctx.cb(timer_isr_ctx.arg, 2); - - if (sched_context_switch_request) { - thread_yield(); - } - } - - mips_setcompare(mips_getcount() + TICKS_PER_US * TIMER_ACCURACY); - - } - else { - spurious_int++; - } -} diff --git a/cpu/mips_pic32mx/eic_pic32mx.c b/cpu/mips_pic32mx/eic_pic32mx.c deleted file mode 100644 index 08ed5607fc..0000000000 --- a/cpu/mips_pic32mx/eic_pic32mx.c +++ /dev/null @@ -1,56 +0,0 @@ -/* - * Copyright (C) 2016,2017, Imagination Technologies Limited and/or its - * affiliated group companies. - * - * This file is subject to the terms and conditions of the GNU Lesser - * General Public License v2.1. See the file LICENSE in the top level - * directory for more details. - * - */ -#include - -#include "board.h" -#include "eic_irq.h" - -void eic_irq_configure(int irq_num) -{ - (void)irq_num; - /* Only timer interrupt supported currently */ - assert(irq_num == EIC_IRQ_TIMER); - - /* Enable IRQ0 CPU Timer Interrupt */ - IEC0SET = _IEC0_CTIE_MASK; - - /* Set IRQ 0 to priority 1.0 */ - IPC0SET = 1 << _IPC0_CTIP_POSITION | 0 << _IPC0_CTIS_POSITION; -} - -void eic_irq_enable(int irq_num) -{ - (void)irq_num; - /* Only timer interrupt supported currently */ - assert(irq_num == EIC_IRQ_TIMER); - - /* Enable IRQ0 CPU Timer Interrupt */ - IEC0SET = _IEC0_CTIE_MASK; -} - -void eic_irq_disable(int irq_num) -{ - (void)irq_num; - /* Only timer interrupt supported currently */ - assert(irq_num == EIC_IRQ_TIMER); - - /* Disable IRQ0 CPU Timer Interrupt */ - IEC0CLR = _IEC0_CTIE_MASK; -} - -void eic_irq_ack(int irq_num) -{ - (void)irq_num; - /* Only timer interrupt supported currently */ - assert(irq_num == EIC_IRQ_TIMER); - - /* Ack the timer interrupt */ - IFS0CLR =_IFS0_CTIF_MASK; -} diff --git a/cpu/mips_pic32mz/eic_pic32mz.c b/cpu/mips_pic32mz/eic_pic32mz.c deleted file mode 100644 index ffa017da73..0000000000 --- a/cpu/mips_pic32mz/eic_pic32mz.c +++ /dev/null @@ -1,56 +0,0 @@ -/* - * Copyright(C) 2016,2017, Imagination Technologies Limited and/or its - * affiliated group companies. - * - * This file is subject to the terms and conditions of the GNU Lesser - * General Public License v2.1. See the file LICENSE in the top level - * directory for more details. - * - */ -#include - -#include "board.h" -#include "eic_irq.h" - -void eic_irq_configure(int irq_num) -{ - (void)irq_num; - /* Only timer interrupt supported currently */ - assert(irq_num == EIC_IRQ_TIMER); - - /* Enable IRQ0 CPU Timer Interrupt */ - IEC0SET = _IEC0_CTIE_MASK; - - /* Set IRQ 0 to priority 1.0 */ - IPC0SET = 1 << _IPC0_CTIP_POSITION | 0 << _IPC0_CTIS_POSITION; -} - -void eic_irq_enable(int irq_num) -{ - (void)irq_num; - /* Only timer interrupt supported currently */ - assert(irq_num == EIC_IRQ_TIMER); - - /* Enable IRQ0 CPU Timer Interrupt */ - IEC0SET = _IEC0_CTIE_MASK; -} - -void eic_irq_disable(int irq_num) -{ - (void)irq_num; - /* Only timer interrupt supported currently */ - assert(irq_num == EIC_IRQ_TIMER); - - /* Disable IRQ0 CPU Timer Interrupt */ - IEC0CLR = _IEC0_CTIE_MASK; -} - -void eic_irq_ack(int irq_num) -{ - (void)irq_num; - /* Only timer interrupt supported currently */ - assert(irq_num == EIC_IRQ_TIMER); - - /* Ack the timer interrupt */ - IFS0CLR =_IFS0_CTIF_MASK; -}