Interrupts and Timers
This commit is contained in:
parent
52c288a056
commit
06f000e19a
45
include/gic.h
Normal file
45
include/gic.h
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
#ifndef GIC_H
|
||||||
|
#define GIC_H
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
namespace GIC {
|
||||||
|
|
||||||
|
/* GIC Distributor and CPU Interface base addresses */
|
||||||
|
uint32_t* const distributor = (uint32_t*)(0x01c81000);
|
||||||
|
uint32_t* const cpuInterface = (uint32_t*)(0x01c82000);
|
||||||
|
|
||||||
|
/* GIC Distributor Registers Offsets */
|
||||||
|
const uint16_t GICD_CTLR = 0x000 / 4;
|
||||||
|
const uint16_t GICD_ISENABLER = 0x100 / 4;
|
||||||
|
const uint16_t GICD_ICENABLER = 0x180 / 4;
|
||||||
|
const uint16_t GICD_ITARGETSR = 0x800 / 4;
|
||||||
|
const uint16_t GICD_ICFGR = 0xc00 / 4;
|
||||||
|
|
||||||
|
/* GIC CPU Interface Registers Offsets */
|
||||||
|
const uint16_t GICC_CTLR = 0x0000 / 4;
|
||||||
|
const uint16_t GICC_PMR = 0x0004 / 4;
|
||||||
|
const uint16_t GICC_IAR = 0x000c / 4;
|
||||||
|
const uint16_t GICC_EOIR = 0x0010 / 4;
|
||||||
|
|
||||||
|
enum Sensitivity {
|
||||||
|
LEVEL = 0,
|
||||||
|
EDGE = 1
|
||||||
|
};
|
||||||
|
enum TargetCPU {
|
||||||
|
CPU0T = 1,
|
||||||
|
CPU1T = 2,
|
||||||
|
CPU2T = 4,
|
||||||
|
CPU3T = 8,
|
||||||
|
CPU4T = 16,
|
||||||
|
CPU5T = 32,
|
||||||
|
CPU6T = 64,
|
||||||
|
CPU7T = 128,
|
||||||
|
CPUALLT = 255
|
||||||
|
};
|
||||||
|
|
||||||
|
void enable(void);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
66
include/timer.h
Normal file
66
include/timer.h
Normal file
@ -0,0 +1,66 @@
|
|||||||
|
#ifndef TIMER_H
|
||||||
|
#define TIMER_H
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
namespace Timer {
|
||||||
|
/* Timer peripheral (with common registers) */
|
||||||
|
uint32_t* const TMR_PERIPHERAL_BASE = (uint32_t*)(0x01c20c00);
|
||||||
|
struct Peripheral {
|
||||||
|
uint32_t TMR_IRQ_EN_REG;
|
||||||
|
uint32_t TMR_IRQ_STA_REG;
|
||||||
|
};
|
||||||
|
|
||||||
|
/* Timer modules array */
|
||||||
|
uint32_t* const TMR_MODULE_BASE = (uint32_t*)(0x01c20c10);
|
||||||
|
struct TimerModule {
|
||||||
|
uint32_t TMR_CTLR_REG;
|
||||||
|
uint32_t TMR_INTV_VALUE_REG;
|
||||||
|
uint32_t TMR_CUR_VALUE_REG;
|
||||||
|
};
|
||||||
|
enum Mode {
|
||||||
|
CONTINUOUS = 0,
|
||||||
|
SINGLE = 1
|
||||||
|
};
|
||||||
|
enum Prescaler { /* maybe PRESCALERx = log2(x) */
|
||||||
|
PRESCALER_1 = 0,
|
||||||
|
PRESCALER_2 = 1,
|
||||||
|
PRESCALER_4 = 2,
|
||||||
|
PRESCALER_8 = 3,
|
||||||
|
PRESCALER_16 = 4,
|
||||||
|
PRESCALER_32 = 5,
|
||||||
|
PRESCALER_64 = 6,
|
||||||
|
PRESCALER_128 = 7
|
||||||
|
|
||||||
|
};
|
||||||
|
enum ClockSource {
|
||||||
|
LS_OSC = 0, /* Low speed OSC */
|
||||||
|
OSC24M = 1, /* OSC24M */
|
||||||
|
PLL66 = 2 /* PLL6/6 */
|
||||||
|
};
|
||||||
|
|
||||||
|
Peripheral* const peripheral = (Peripheral* const)(TMR_PERIPHERAL_BASE);
|
||||||
|
|
||||||
|
class Timer {
|
||||||
|
private:
|
||||||
|
uint8_t n;
|
||||||
|
TimerModule* module;
|
||||||
|
public:
|
||||||
|
/* Initialize class instance as timer n
|
||||||
|
* where n is found on Allwinner datasheet */
|
||||||
|
Timer(uint8_t n);
|
||||||
|
/* Prepare timer */
|
||||||
|
void set(uint32_t interval, Mode mode, Prescaler prescaler, ClockSource source);
|
||||||
|
/* Reload and start timer */
|
||||||
|
void start(void);
|
||||||
|
/* Stop timer */
|
||||||
|
void stop(void);
|
||||||
|
};
|
||||||
|
|
||||||
|
/* Vector of system's timers */
|
||||||
|
extern Timer timer[];
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
@ -4,9 +4,7 @@
|
|||||||
/* WARNING: use of static variable is not thread safe!! */
|
/* WARNING: use of static variable is not thread safe!! */
|
||||||
static char string[32];
|
static char string[32];
|
||||||
|
|
||||||
/* Never call this function directly: always use the printk,
|
extern "C" int printk(const char* msg) {
|
||||||
* which disables and reenables interrupts */
|
|
||||||
extern "C" int c_printk(const char* msg) {
|
|
||||||
while (*msg != '\0') {
|
while (*msg != '\0') {
|
||||||
while ((*dbg::UART_LSR & 0x20) == 0); /* Wait for transmission */
|
while ((*dbg::UART_LSR & 0x20) == 0); /* Wait for transmission */
|
||||||
*dbg::UART_THR = *msg;
|
*dbg::UART_THR = *msg;
|
||||||
|
17
src/dbg.s
17
src/dbg.s
@ -3,23 +3,6 @@ panic_msg:
|
|||||||
.string "=== PANIC : I'm afraid I have lost my towel"
|
.string "=== PANIC : I'm afraid I have lost my towel"
|
||||||
|
|
||||||
.text
|
.text
|
||||||
|
|
||||||
.extern c_printk
|
|
||||||
.global printk
|
|
||||||
printk:
|
|
||||||
push {lr}
|
|
||||||
|
|
||||||
/* disables IRQ and FIQ in CPU */
|
|
||||||
cpsid if
|
|
||||||
|
|
||||||
/* C-Implementation */
|
|
||||||
bl c_printk
|
|
||||||
|
|
||||||
/* enables IRQ and FIQ in CPU */
|
|
||||||
cpsie if
|
|
||||||
|
|
||||||
pop {pc}
|
|
||||||
|
|
||||||
/* TODO: to be implemented in a more useful way */
|
/* TODO: to be implemented in a more useful way */
|
||||||
.global panic
|
.global panic
|
||||||
panic:
|
panic:
|
||||||
|
57
src/gic.cpp
Normal file
57
src/gic.cpp
Normal file
@ -0,0 +1,57 @@
|
|||||||
|
#include <gic.h>
|
||||||
|
#include <dbg.h>
|
||||||
|
|
||||||
|
namespace GIC {
|
||||||
|
|
||||||
|
/* This code (correctly) assumes that initial registers' values are 0x0,
|
||||||
|
* thus does not work (read: correctly overwrite) with subsequent changes,
|
||||||
|
* but since GIC is prepared at system startup and then never touched again,
|
||||||
|
* this should not be a problem */
|
||||||
|
void enableInterrupt(const uint16_t m, const TargetCPU target, const Sensitivity sensitivity) {
|
||||||
|
/* register offset (used in an uint32_t array, so no need to multiply by 4) */
|
||||||
|
uint16_t n;
|
||||||
|
|
||||||
|
/* Enable interrupt m */
|
||||||
|
n = m / 32;
|
||||||
|
distributor[GICD_ISENABLER + n] = distributor[GICD_ISENABLER + n] | (1 << (m % 32));
|
||||||
|
|
||||||
|
/* Set target CPU */
|
||||||
|
n = m / 4;
|
||||||
|
distributor[GICD_ITARGETSR + n] = distributor[GICD_ITARGETSR + n] | (target << ((m % 4) * 8));
|
||||||
|
|
||||||
|
/* Edge or level triggered? */
|
||||||
|
n = m / 16;
|
||||||
|
distributor[GICD_ICFGR + n] = distributor[GICD_ICFGR + n] | (sensitivity << (2 * (m % 16) + 1));
|
||||||
|
|
||||||
|
/* Enable all priorities */
|
||||||
|
cpuInterface[1] = 0xff;
|
||||||
|
}
|
||||||
|
|
||||||
|
void printDistributorStatus(void) {
|
||||||
|
for (short i = 0; i < 1024; ++i) {
|
||||||
|
printk("Distributor "); printk(itoa(i * 4)); printk(" : ");
|
||||||
|
printkl(itoa(distributor[i]));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
void printCPUInterfaceStatus(void) {
|
||||||
|
for (short i = 0; i < 64; ++i) {
|
||||||
|
printk("CPUInterface "); printk(itoa(i * 4)); printk(" : ");
|
||||||
|
printkl(itoa(cpuInterface[i]));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void enable(void) {
|
||||||
|
|
||||||
|
enableInterrupt(54, CPU0T, EDGE); /* Timer0 */
|
||||||
|
|
||||||
|
/* Enable distributor and CPU interfaces
|
||||||
|
* Do this always at the end,
|
||||||
|
* since writing on GIC registers is not allowed when it is enabled */
|
||||||
|
distributor[0] = distributor[GICD_CTLR] | 0x1;
|
||||||
|
cpuInterface[0] = cpuInterface[GICC_CTLR] | 0x1;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
@ -1,6 +1,8 @@
|
|||||||
#include <dbg.h>
|
#include <dbg.h>
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
|
#include <gic.h>
|
||||||
#include <interrupt.h>
|
#include <interrupt.h>
|
||||||
|
#include <timer.h>
|
||||||
|
|
||||||
extern "C" void go_usr(void);
|
extern "C" void go_usr(void);
|
||||||
|
|
||||||
@ -13,7 +15,14 @@ extern "C" int main(int argc, char** argv) {
|
|||||||
printkl("Now firing test software interrupt...");
|
printkl("Now firing test software interrupt...");
|
||||||
fireswi();
|
fireswi();
|
||||||
|
|
||||||
printkl("Go user...");
|
printkl("Now enabling GIC interrupts...");
|
||||||
|
GIC::enable();
|
||||||
|
|
||||||
|
printkl("Now enabling timer...");
|
||||||
|
Timer::timer[0].set(0x80, Timer::CONTINUOUS, Timer::PRESCALER_128, Timer::LS_OSC);
|
||||||
|
Timer::timer[0].start();
|
||||||
|
|
||||||
|
printkl("Now going user...");
|
||||||
go_usr();
|
go_usr();
|
||||||
|
|
||||||
printkl("EOK -- End of Kernel -- This point can not be reached");
|
printkl("EOK -- End of Kernel -- This point can not be reached");
|
||||||
|
@ -24,6 +24,11 @@ SECTIONS
|
|||||||
.text : ALIGN(0x4) {
|
.text : ALIGN(0x4) {
|
||||||
*(.text)
|
*(.text)
|
||||||
} > m_ram
|
} > m_ram
|
||||||
|
.init_array : ALIGN(0x4) {
|
||||||
|
_init_array_start = .;
|
||||||
|
*(.init_array)
|
||||||
|
_init_array_end = .;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -27,6 +27,22 @@ _start:
|
|||||||
/* Enter SuperVisor Mode */
|
/* Enter SuperVisor Mode */
|
||||||
cps #0x13
|
cps #0x13
|
||||||
|
|
||||||
|
/* Call constructors */
|
||||||
|
ldr r10, =_init_array_start
|
||||||
|
ldr r11, =_init_array_end
|
||||||
|
1:
|
||||||
|
cmp r10, r11 // if every constructor has been called
|
||||||
|
beq 3f
|
||||||
|
|
||||||
|
// "resta di stucco è un barbatrucco"
|
||||||
|
ldr r0, [r10] // load constructor address in r0
|
||||||
|
ldr lr, =2f // store return address in lr
|
||||||
|
mov pc, r0 // jump to address in r0
|
||||||
|
2:
|
||||||
|
add r10, #4 // next constructor
|
||||||
|
b 1b
|
||||||
|
3:
|
||||||
|
|
||||||
/* Enable Vector Table BAR remapping (clears bit 13 of SCTLR) */
|
/* Enable Vector Table BAR remapping (clears bit 13 of SCTLR) */
|
||||||
mrc p15, 0, r0, c1, c0, 0
|
mrc p15, 0, r0, c1, c0, 0
|
||||||
and r0, #0xffffdfff
|
and r0, #0xffffdfff
|
||||||
|
39
src/timer.cpp
Normal file
39
src/timer.cpp
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
#include <timer.h>
|
||||||
|
#include <dbg.h>
|
||||||
|
|
||||||
|
namespace Timer {
|
||||||
|
|
||||||
|
Timer timer[3] = { Timer(0), Timer(1), Timer(2) };
|
||||||
|
|
||||||
|
Timer::Timer(uint8_t n) {
|
||||||
|
this->n = n;
|
||||||
|
module = (TimerModule*)(TMR_MODULE_BASE + n);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Timer::set(uint32_t interval, Mode mode, Prescaler prescaler, ClockSource source) {
|
||||||
|
/* zero fill control registers */
|
||||||
|
module->TMR_CTLR_REG = 0;
|
||||||
|
module->TMR_CUR_VALUE_REG = 0;
|
||||||
|
|
||||||
|
/* Set controls: mode, prescaler and source */
|
||||||
|
module->TMR_CTLR_REG = (mode << 7) | (prescaler << 4) | (source << 2);
|
||||||
|
|
||||||
|
/* Set timer value */
|
||||||
|
module->TMR_INTV_VALUE_REG = interval;
|
||||||
|
|
||||||
|
/* Enable interrupt */
|
||||||
|
peripheral->TMR_IRQ_EN_REG = peripheral->TMR_IRQ_EN_REG | (1 << n);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void Timer::start(void) {
|
||||||
|
/* Reload interval value and start */
|
||||||
|
module->TMR_CTLR_REG = module->TMR_CTLR_REG | 0x3;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Timer::stop(void) {
|
||||||
|
/* Clear stop/pause bit */
|
||||||
|
module->TMR_CTLR_REG = module->TMR_CTLR_REG & 0xfffffffe;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
32
src/vectab.cpp
Normal file
32
src/vectab.cpp
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
#include <dbg.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
extern "C" uint32_t c_irq_handler(uint32_t iar) {
|
||||||
|
uint8_t cpuID = (iar >> 10) & 0x7;
|
||||||
|
uint16_t interruptID = iar & 0x3ff;
|
||||||
|
|
||||||
|
if (cpuID == 0) {
|
||||||
|
/* for
|
||||||
|
* SPI - Shared Peripheral Interrupt
|
||||||
|
* PPI - Private Peripheral Interrupt
|
||||||
|
*/
|
||||||
|
switch (interruptID) {
|
||||||
|
case 54: /* timer 0 */ /* TODO: improve this ugliness */
|
||||||
|
uint32_t* const TMR_IRQ_STA_REG = (uint32_t* const)(0x01c20c04);
|
||||||
|
*TMR_IRQ_STA_REG = *TMR_IRQ_STA_REG | 0x1;
|
||||||
|
printk("Serving IRQ #");
|
||||||
|
printkl(itoa(interruptID));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
/* for
|
||||||
|
* SGI - Software Generated Interrupts
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* to be done -- multiprocessor systems */
|
||||||
|
}
|
||||||
|
|
||||||
|
return iar;
|
||||||
|
}
|
||||||
|
|
31
src/vectab.s
31
src/vectab.s
@ -9,7 +9,7 @@ swi_msg:
|
|||||||
prefetch_abt_msg:
|
prefetch_abt_msg:
|
||||||
.string "Prefetch Abort"
|
.string "Prefetch Abort"
|
||||||
data_abt_msg:
|
data_abt_msg:
|
||||||
.string "Data Abort"
|
.string "Data Abort @ "
|
||||||
irq_msg:
|
irq_msg:
|
||||||
.string "IRQ"
|
.string "IRQ"
|
||||||
fiq_msg:
|
fiq_msg:
|
||||||
@ -69,20 +69,39 @@ data_abt_handler:
|
|||||||
stmfd sp!, {lr}
|
stmfd sp!, {lr}
|
||||||
|
|
||||||
ldr r0, =data_abt_msg
|
ldr r0, =data_abt_msg
|
||||||
|
bl printk
|
||||||
|
ldmfd sp!, {r0}
|
||||||
|
bl itoa
|
||||||
bl printkl
|
bl printkl
|
||||||
|
|
||||||
b panic
|
b panic
|
||||||
|
|
||||||
ldmfd sp!, {pc}^
|
ldmfd sp!, {pc}^
|
||||||
|
|
||||||
// TODO: untested
|
|
||||||
irq_handler:
|
irq_handler:
|
||||||
stmfd sp!, {lr}
|
// mask FIQ (IRQ already masked by hardware when entering IRQ mode)
|
||||||
|
cpsid f
|
||||||
|
|
||||||
ldr r0, =irq_msg
|
// Prepare return address and store context on stack
|
||||||
bl printkl
|
sub lr, #0x4
|
||||||
|
stmfd sp!, {r0-r12, lr}
|
||||||
|
|
||||||
ldmfd sp!, {pc}^
|
// Acknoweledge interrupt
|
||||||
|
ldr r10, =0x01c8200c // IAR
|
||||||
|
ldr r0, [r10]
|
||||||
|
push {r0}
|
||||||
|
|
||||||
|
// C complex handler
|
||||||
|
bl c_irq_handler
|
||||||
|
|
||||||
|
// Send End of Interrupt
|
||||||
|
pop {r0}
|
||||||
|
ldr r10, =0x01c82010 // EOIR
|
||||||
|
str r0, [r10]
|
||||||
|
|
||||||
|
// Reload context from stack
|
||||||
|
// and also restore CPSR from SPSR (that caret)
|
||||||
|
ldmfd sp!, {r0-r12, pc}^
|
||||||
|
|
||||||
// TODO: untested
|
// TODO: untested
|
||||||
fiq_handler:
|
fiq_handler:
|
||||||
|
Loading…
Reference in New Issue
Block a user