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!! */
|
||||
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;
|
||||
|
17
src/dbg.s
17
src/dbg.s
@ -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
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 <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");
|
||||
|
@ -24,6 +24,11 @@ SECTIONS
|
||||
.text : ALIGN(0x4) {
|
||||
*(.text)
|
||||
} > m_ram
|
||||
.init_array : ALIGN(0x4) {
|
||||
_init_array_start = .;
|
||||
*(.init_array)
|
||||
_init_array_end = .;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
@ -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
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:
|
||||
.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:
|
||||
|
Loading…
Reference in New Issue
Block a user