usb: use hardware timer to update jiffies

This takes a lot of load away because we don't need to call the time server on
each IRQ.

Fixes #1999
This commit is contained in:
Sebastian Sumpf 2016-06-06 10:58:48 +02:00 committed by Christian Helmuth
parent 5e6c3a979e
commit 597cdc846c
3 changed files with 140 additions and 1 deletions

View File

@ -36,6 +36,9 @@ namespace Lx {
unsigned long *jiffies_ptr = nullptr);
void timer_update_jiffies();
typedef unsigned long (*jiffies_update_func)(void);
void register_jiffies_func(jiffies_update_func func);
}

View File

@ -12,14 +12,18 @@
*/
/* Genode includes */
#include <base/attached_io_mem_dataspace.h>
#include <io_mem_session/connection.h>
#include <irq_session/connection.h>
#include <util/mmio.h>
#include <platform_session/connection.h>
/* emulation */
#include <platform.h>
#include <lx_emul.h>
#include <lx_kit/timer.h>
/* dwc-otg */
#define new new_
@ -31,6 +35,122 @@
using namespace Genode;
namespace Genode { template <unsigned long> class Sp804_base; }
/**
* Basic driver for the ARM SP804 timer
*/
template <unsigned long CLK>
class Genode::Sp804_base : public Mmio
{
enum {
TICS_PER_MS = CLK / 1000,
};
/**
* Holds value that shall be loaded to the timer value register
*/
struct Load : Register<0x0, 32> { };
/**
* Timer value register
*/
struct Value : Register<0x4, 32> { enum { MAX_VALUE = 0xffffffff }; };
/**
* Timer control register
*/
struct Control : Register<0x8, 8>
{
struct Oneshot : Bitfield<0,1> { };
struct Size : Bitfield<1,1> { };
struct Pre : Bitfield<2,2> { };
struct Int_en : Bitfield<5,1> { };
struct Mode : Bitfield<6,1> { };
struct Timer_en : Bitfield<7,1> { };
};
/**
* Clears the timer interrupt
*/
struct Int_clr : Register<0xc, 1> { };
unsigned _last_tick = Value::MAX_VALUE;
public:
/**
* Constructor, clears interrupt output
*/
Sp804_base(addr_t const mmio_base) : Mmio(mmio_base)
{
/* clear irq */
write<Int_clr>(1);
/* disabble and configure */
write<typename Control::Timer_en>(0);
write<Control>(Control::Mode::bits(0) | /* free running */
Control::Int_en::bits(0) | /* IRQ disabled */
Control::Pre::bits(0) | /* clk divider 1 */
Control::Size::bits(1) | /* 32 bit */
Control::Oneshot::bits(0)); /* one-shot off */
/* start */
write<Load>(Value::MAX_VALUE);
write<typename Control::Timer_en>(1);
}
unsigned long update_jiffies()
{
unsigned tick = read<Value>();
/* timer runs backwards */
unsigned delta = ticks_to_ms(_last_tick - tick);
/* delta < 1 jiffie */
if (!msecs_to_jiffies(delta))
return jiffies;
jiffies += msecs_to_jiffies(delta);
_last_tick = tick;
return jiffies;
}
/**
* Translate native timer value to milliseconds
*/
static unsigned long ticks_to_ms(unsigned long const tics) {
return tics / TICS_PER_MS; }
};
enum { TIMER_MMIO_BASE = 0x2000b400,
TIMER_MMIO_SIZE = 0x100,
TIMER_CLOCK = 992*1000 };
struct Platform_timer : Attached_io_mem_dataspace,
Sp804_base<TIMER_CLOCK>
{
Platform_timer() :
Attached_io_mem_dataspace(TIMER_MMIO_BASE, TIMER_MMIO_SIZE),
Sp804_base((Genode::addr_t)local_addr<void>())
{ }
static Platform_timer &instance()
{
static Platform_timer _timer;
return _timer;
}
};
static unsigned long jiffies_update_func()
{
return Platform_timer::instance().update_jiffies();
}
/************************************************
** Resource info passed to the dwc_otg driver **
@ -165,6 +285,12 @@ extern "C" int module_smsc95xx_driver_init();
void platform_hcd_init(Services *services)
{
/* enable timer */
Platform_timer::instance();
/* use costum jiffies update function and the Sp804 timer */
Lx::register_jiffies_func(jiffies_update_func);
/* enable USB power */
Platform::Connection platform;
platform.power_state(Platform::Session::POWER_USB_HCD, true);

View File

@ -86,6 +86,7 @@ class Lx_kit::Timer : public Lx::Timer
Lx::Task _timer_task;
Genode::Signal_rpc_member<Lx_kit::Timer> _dispatcher;
Genode::Tslab<Context, 32 * sizeof(Context)> _timer_alloc;
Lx::jiffies_update_func _jiffies_func = nullptr;
/**
* Lookup local timer
@ -289,7 +290,10 @@ class Lx_kit::Timer : public Lx::Timer
}
void update_jiffies() {
_jiffies = msecs_to_jiffies(_timer_conn.elapsed_ms()); }
_jiffies = _jiffies_func ? _jiffies_func() : msecs_to_jiffies(_timer_conn.elapsed_ms()); }
void register_jiffies_func(Lx::jiffies_update_func func) {
_jiffies_func = func; }
};
@ -308,3 +312,9 @@ void Lx::timer_update_jiffies()
{
timer().update_jiffies();
}
void Lx::register_jiffies_func(jiffies_update_func func)
{
dynamic_cast<Lx_kit::Timer &>(timer()).register_jiffies_func(func);
}