base-hw: use global timer on Cortex A9

genodelabs/genode#4360
This commit is contained in:
Johannes Schlatow 2023-01-11 15:25:25 +01:00 committed by Christian Helmuth
parent 777b093cad
commit 4d0cb175da
11 changed files with 195 additions and 191 deletions

View File

@ -7,7 +7,7 @@
# add C++ sources
SRC_CC += spec/cortex_a9/board.cc
SRC_CC += spec/cortex_a9/cpu.cc
SRC_CC += spec/arm/cortex_a9_private_timer.cc
SRC_CC += spec/arm/cortex_a9_global_timer.cc
SRC_CC += spec/arm/gicv2.cc
SRC_CC += spec/arm/kernel/lock.cc
SRC_CC += kernel/vm_thread_off.cc

View File

@ -19,7 +19,7 @@
#include <hw/spec/arm/imx6q_sabrelite_board.h>
/* base-hw Core includes */
#include <spec/arm/cortex_a9_private_timer.h>
#include <spec/arm/cortex_a9_global_timer.h>
#include <spec/cortex_a9/cpu.h>
namespace Board {
@ -34,8 +34,8 @@ namespace Board {
L2_cache & l2_cache();
enum {
CORTEX_A9_PRIVATE_TIMER_CLK = 396000000, /* timer clk runs half the CPU freq */
CORTEX_A9_PRIVATE_TIMER_DIV = 100,
CORTEX_A9_GLOBAL_TIMER_CLK = 396000000, /* timer clk runs half the CPU freq */
CORTEX_A9_GLOBAL_TIMER_DIV = 100,
};
}

View File

@ -19,7 +19,7 @@
#include <hw/spec/arm/nit6_solox_board.h>
/* base-hw Core includes */
#include <spec/arm/cortex_a9_private_timer.h>
#include <spec/arm/cortex_a9_global_timer.h>
#include <spec/cortex_a9/cpu.h>
namespace Board {
@ -34,8 +34,8 @@ namespace Board {
L2_cache & l2_cache();
enum {
CORTEX_A9_PRIVATE_TIMER_CLK = 500000000, /* timer clk runs half the CPU freq */
CORTEX_A9_PRIVATE_TIMER_DIV = 100,
CORTEX_A9_GLOBAL_TIMER_CLK = 500000000, /* timer clk runs half the CPU freq */
CORTEX_A9_GLOBAL_TIMER_DIV = 100,
};
}

View File

@ -19,7 +19,7 @@
#include <hw/spec/arm/pbxa9_board.h>
/* base-hw Core includes */
#include <spec/arm/cortex_a9_private_timer.h>
#include <spec/arm/cortex_a9_global_timer.h>
#include <spec/cortex_a9/cpu.h>
namespace Board {

View File

@ -20,7 +20,7 @@
#include <hw/spec/arm/wand_quad_board.h>
/* base-hw Core includes */
#include <spec/arm/cortex_a9_private_timer.h>
#include <spec/arm/cortex_a9_global_timer.h>
#include <spec/cortex_a9/cpu.h>
namespace Board {
@ -35,8 +35,8 @@ namespace Board {
L2_cache & l2_cache();
enum {
CORTEX_A9_PRIVATE_TIMER_CLK = 500000000, /* timer clk runs half the CPU freq */
CORTEX_A9_PRIVATE_TIMER_DIV = 100,
CORTEX_A9_GLOBAL_TIMER_CLK = 500000000, /* timer clk runs half the CPU freq */
CORTEX_A9_GLOBAL_TIMER_DIV = 100,
};
}

View File

@ -0,0 +1,113 @@
/*
* \brief Global timer implementation specific to Cortex A9
* \author Johannes Schlatow
* \date 2023-01-11
*/
/*
* Copyright (C) 2023 Genode Labs GmbH
*
* This file is part of the Genode OS framework, which is distributed
* under the terms of the GNU Affero General Public License version 3.
*/
/* Genode includes */
#include <drivers/timer/util.h>
/* core includes */
#include <kernel/cpu.h>
#include <kernel/timer.h>
#include <platform.h>
using namespace Genode;
using namespace Kernel;
using Device = Board::Timer;
using counter_t = Board::Timer::Counter::access_t;
enum {
TICS_PER_MS =
Board::CORTEX_A9_GLOBAL_TIMER_CLK /
Board::CORTEX_A9_GLOBAL_TIMER_DIV / 1000,
};
Board::Timer::Timer(unsigned cpu_id)
:
Mmio(Platform::mmio_to_virt(Board::Cpu_mmio::GLOBAL_TIMER_MMIO_BASE))
{
enum { PRESCALER = Board::CORTEX_A9_GLOBAL_TIMER_DIV - 1 };
static_assert((TICS_PER_MS >= 1000),
"Bad TICS_PER_US value");
/* primary CPU sets initial timer value */
if (cpu_id == 0) {
write<Control::Timer_enable>(0);
write<Counter>(0, 0);
write<Counter>(0, 1);
}
Control::access_t control = 0;
Control::Irq_enable::set(control, 1);
Control::Prescaler::set(control, PRESCALER);
Control::Timer_enable::set(control, 1);
write<Control>(control);
}
time_t Board::Timer::current_ticks() const
{
uint32_t upper = read<Counter>(1);
uint32_t lower = read<Counter>(0);
uint32_t upper_new = read<Counter>(1);
while (upper != upper_new) {
upper = upper_new;
lower = read<Counter>(0);
upper_new = read<Counter>(1);
}
return (time_t)upper << 32 | (time_t)lower;
}
void Timer::_start_one_shot(time_t const ticks)
{
/*
* First unset the interrupt flag,
* otherwise if the tick is small enough, we loose an interrupt
*/
_device.write<Device::Interrupt_status::Event>(1);
/* Disable comparator before setting a new value */
_device.write<Device::Control::Comp_enable>(0);
time_t end_ticks = _device.current_ticks() + ticks;
_device.write<Device::Comparator>(end_ticks & 0xFFFFFFFF, 0);
_device.write<Device::Comparator>(end_ticks >> 32 , 1);
/* Enable comparator before setting a new value */
_device.write<Device::Control::Comp_enable>(1);
}
time_t Timer::ticks_to_us(time_t const ticks) const {
return timer_ticks_to_us(ticks, TICS_PER_MS); }
unsigned Timer::interrupt_id() const {
return Board::Cpu_mmio::GLOBAL_TIMER_IRQ; }
time_t Timer::us_to_ticks(time_t const us) const {
return (us / 1000) * TICS_PER_MS; }
time_t Timer::_duration() const {
return _device.current_ticks() - _time; }
time_t Timer::_max_value() const {
return TICS_PER_MS * 5000; }

View File

@ -0,0 +1,66 @@
/*
* \brief Global timer implementation specific to Cortex A9
* \author Johannes Schlatow
* \date 2023-01-11
*/
/*
* Copyright (C) 2023 Genode Labs GmbH
*
* This file is part of the Genode OS framework, which is distributed
* under the terms of the GNU Affero General Public License version 3.
*/
#ifndef _SRC__CORE__SPEC__ARM__CORTEX_A9_GLOBAL_TIMER_H_
#define _SRC__CORE__SPEC__ARM__CORTEX_A9_GLOBAL_TIMER_H_
/* Genode includes */
#include <util/mmio.h>
/* base-hw includes */
#include <kernel/types.h>
namespace Board { class Timer; }
/**
* Timer driver for core
*/
struct Board::Timer : Genode::Mmio
{
/**
* Counter value registers
*/
struct Counter : Register_array<0x0, 32, 2, 32> { };
/**
* Timer control register
*/
struct Control : Register<0x8, 32>
{
struct Timer_enable : Bitfield<0,1> { }; /* enable counting */
struct Comp_enable : Bitfield<1,1> { };
struct Irq_enable : Bitfield<2,1> { }; /* unmask interrupt */
struct Auto_increment : Bitfield<3,1> { };
struct Prescaler : Bitfield<8,8> { };
};
/**
* Timer interrupt status register
*/
struct Interrupt_status : Register<0xc, 32>
{
struct Event : Bitfield<0,1> { }; /* if counter hit zero */
};
/**
* Comparator registers
*/
struct Comparator : Register_array<0x10, 32, 2, 32> { };
Kernel::time_t current_ticks() const;
Timer(unsigned);
};
#endif /* _SRC__CORE__SPEC__ARM__CORTEX_A9_GLOBAL_TIMER_H_ */

View File

@ -1,115 +0,0 @@
/*
* \brief Timer implementation specific to Cortex A9
* \author Stefan Kalkowski
* \author Martin Stein
* \date 2016-01-07
*/
/*
* Copyright (C) 2016-2017 Genode Labs GmbH
*
* This file is part of the Genode OS framework, which is distributed
* under the terms of the GNU Affero General Public License version 3.
*/
/* Genode includes */
#include <drivers/timer/util.h>
/* core includes */
#include <kernel/cpu.h>
#include <kernel/timer.h>
#include <platform.h>
using namespace Genode;
using namespace Kernel;
using Device = Board::Timer;
using counter_t = Board::Timer::Counter::access_t;
enum {
TICS_PER_MS =
Board::CORTEX_A9_PRIVATE_TIMER_CLK /
Board::CORTEX_A9_PRIVATE_TIMER_DIV / 1000,
MAX_COUNTER_VAL = ~(counter_t)0
};
Board::Timer::Timer(unsigned)
:
Mmio(Platform::mmio_to_virt(Board::Cpu_mmio::PRIVATE_TIMER_MMIO_BASE))
{
enum { PRESCALER = Board::CORTEX_A9_PRIVATE_TIMER_DIV - 1 };
static_assert((TICS_PER_MS >= 1000) /*&&
(TICS_PER_US * 1000000 *
Board::CORTEX_A9_PRIVATE_TIMER_DIV) ==
Board::CORTEX_A9_PRIVATE_TIMER_CLK*/,
"Bad TICS_PER_US value");
write<Load>(0xffffffff);
Control::access_t control = 0;
Control::Irq_enable::set(control, 1);
Control::Prescaler::set(control, PRESCALER);
Control::Auto_reload::set(control, 1);
Control::Timer_enable::set(control, 1);
write<Control>(control);
}
void Timer::_start_one_shot(time_t const ticks)
{
/*
* First unset the interrupt flag,
* otherwise if the tick is small enough, we loose an interrupt
*/
_device.write<Device::Interrupt_status::Event>(1);
_device.write<Device::Counter>(ticks);
}
time_t Timer::ticks_to_us(time_t const ticks) const {
return timer_ticks_to_us(ticks, TICS_PER_MS); }
unsigned Timer::interrupt_id() const {
return Board::Cpu_mmio::PRIVATE_TIMER_IRQ; }
time_t Timer::us_to_ticks(time_t const us) const {
return (us / 1000) * TICS_PER_MS; }
time_t Timer::_duration() const
{
counter_t const start_counter_val { (counter_t)_last_timeout_duration };
counter_t const curr_counter_val { _device.read<Device::Counter>() };
/*
* Calculate result depending on whether the counter already wrapped or
* not. See the comment in the implementation of '_max_value' for an
* explanation why this comparison is done instead of checking the IRQ
* status and why it is sufficient.
*/
if (curr_counter_val > start_counter_val)
return start_counter_val + (MAX_COUNTER_VAL - curr_counter_val);
return start_counter_val - curr_counter_val;
}
time_t Timer::_max_value() const
{
/*
* We propagate a max timeout value far lower than the one required
* by the hardware. This is because on some platforms (Qemu 4.2.1 PBXA9),
* the IRQ status register is not reliable. Sometimes, it indicates an IRQ
* too early, i.e., shortly before the counter wraps. Therefore we have to
* accomplish wrap detection via counter comparison only. Therefore, we
* have to make sure that we always read out the counter before it hits
* the max timout value again. And, therefore, the max timeout value has
* to be far away from the first value the counter has after wrapping.
*/
return MAX_COUNTER_VAL >> 1;
}

View File

@ -1,60 +0,0 @@
/*
* \brief Private Timer implementation specific to Cortex A9
* \author Martin stein
* \date 2011-12-13
*/
/*
* Copyright (C) 2011-2017 Genode Labs GmbH
*
* This file is part of the Genode OS framework, which is distributed
* under the terms of the GNU Affero General Public License version 3.
*/
#ifndef _SRC__CORE__SPEC__ARM__CORTEX_A9_PRIVATE_TIMER_H_
#define _SRC__CORE__SPEC__ARM__CORTEX_A9_PRIVATE_TIMER_H_
/* Genode includes */
#include <util/mmio.h>
namespace Board { class Timer; }
/**
* Timer driver for core
*/
struct Board::Timer : Genode::Mmio
{
/**
* Load value register
*/
struct Load : Register<0x0, 32> { };
/**
* Counter value register
*/
struct Counter : Register<0x4, 32> { };
/**
* Timer control register
*/
struct Control : Register<0x8, 32>
{
struct Timer_enable : Bitfield<0,1> { }; /* enable counting */
struct Auto_reload : Bitfield<1,1> { };
struct Irq_enable : Bitfield<2,1> { }; /* unmask interrupt */
struct Prescaler : Bitfield<8,8> { };
};
/**
* Timer interrupt status register
*/
struct Interrupt_status : Register<0xc, 32>
{
struct Event : Bitfield<0,1> { }; /* if counter hit zero */
};
Timer(unsigned);
};
#endif /* _SRC__CORE__SPEC__ARM__CORTEX_A9_PRIVATE_TIMER_H_ */

View File

@ -30,9 +30,9 @@ struct Hw::Cortex_a9_mmio
IRQ_CONTROLLER_CPU_BASE = BASE + 0x100,
IRQ_CONTROLLER_CPU_SIZE = 0x100,
PRIVATE_TIMER_MMIO_BASE = BASE + 0x600,
PRIVATE_TIMER_MMIO_SIZE = 0x10,
PRIVATE_TIMER_IRQ = 29,
GLOBAL_TIMER_MMIO_BASE = BASE + 0x200,
GLOBAL_TIMER_MMIO_SIZE = 0x18,
GLOBAL_TIMER_IRQ = 27,
};
};

View File

@ -43,8 +43,8 @@ namespace Pbxa9 {
SYSTEM_CONTROL_MMIO_BASE = 0x10000000,
/* CPU */
CORTEX_A9_PRIVATE_TIMER_CLK = 100000000,
CORTEX_A9_PRIVATE_TIMER_DIV = 100,
CORTEX_A9_GLOBAL_TIMER_CLK = 100000000,
CORTEX_A9_GLOBAL_TIMER_DIV = 100,
CORTEX_A9_PRIVATE_MEM_BASE = 0x1f000000,
CORTEX_A9_PRIVATE_MEM_SIZE = 0x2000,