Interrupts and Timers

This commit is contained in:
giomba 2019-01-06 17:33:02 +01:00
parent 52c288a056
commit 06f000e19a
11 changed files with 296 additions and 27 deletions

45
include/gic.h Normal file
View 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
View 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

View File

@ -4,9 +4,7 @@
/* WARNING: use of static variable is not thread safe!! */
static char string[32];
/* Never call this function directly: always use the printk,
* which disables and reenables interrupts */
extern "C" int c_printk(const char* msg) {
extern "C" int printk(const char* msg) {
while (*msg != '\0') {
while ((*dbg::UART_LSR & 0x20) == 0); /* Wait for transmission */
*dbg::UART_THR = *msg;

View File

@ -3,23 +3,6 @@ panic_msg:
.string "=== PANIC : I'm afraid I have lost my towel"
.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 */
.global panic
panic:

57
src/gic.cpp Normal file
View 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;
}
}

View File

@ -1,6 +1,8 @@
#include <dbg.h>
#include <stdint.h>
#include <gic.h>
#include <interrupt.h>
#include <timer.h>
extern "C" void go_usr(void);
@ -13,7 +15,14 @@ extern "C" int main(int argc, char** argv) {
printkl("Now firing test software interrupt...");
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();
printkl("EOK -- End of Kernel -- This point can not be reached");

View File

@ -24,6 +24,11 @@ SECTIONS
.text : ALIGN(0x4) {
*(.text)
} > m_ram
.init_array : ALIGN(0x4) {
_init_array_start = .;
*(.init_array)
_init_array_end = .;
}
}

View File

@ -27,6 +27,22 @@ _start:
/* Enter SuperVisor Mode */
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) */
mrc p15, 0, r0, c1, c0, 0
and r0, #0xffffdfff

39
src/timer.cpp Normal file
View 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
View 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;
}

View File

@ -9,7 +9,7 @@ swi_msg:
prefetch_abt_msg:
.string "Prefetch Abort"
data_abt_msg:
.string "Data Abort"
.string "Data Abort @ "
irq_msg:
.string "IRQ"
fiq_msg:
@ -69,20 +69,39 @@ data_abt_handler:
stmfd sp!, {lr}
ldr r0, =data_abt_msg
bl printk
ldmfd sp!, {r0}
bl itoa
bl printkl
b panic
ldmfd sp!, {pc}^
// TODO: untested
irq_handler:
stmfd sp!, {lr}
// mask FIQ (IRQ already masked by hardware when entering IRQ mode)
cpsid f
ldr r0, =irq_msg
bl printkl
// Prepare return address and store context on stack
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
fiq_handler: