diff --git a/base/include/platform/arndale/drivers/board_base.h b/base/include/platform/arndale/drivers/board_base.h index dd8bf53f5..b128ad1c6 100644 --- a/base/include/platform/arndale/drivers/board_base.h +++ b/base/include/platform/arndale/drivers/board_base.h @@ -31,6 +31,9 @@ namespace Genode MMIO_0_BASE = 0x10000000, MMIO_0_SIZE = 0x10000000, + CMU_MMIO_BASE = 0x10010000, + CMU_MMIO_SIZE = 0x21000, + /* interrupt controller */ GIC_CPU_MMIO_BASE = 0x10480000, GIC_CPU_MMIO_SIZE = 0x00010000, diff --git a/os/include/platform/arndale/regulator/consts.h b/os/include/platform/arndale/regulator/consts.h new file mode 100644 index 000000000..fddb89382 --- /dev/null +++ b/os/include/platform/arndale/regulator/consts.h @@ -0,0 +1,68 @@ +/* + * \brief Regulator definitions for Arndale + * \author Stefan Kalkowski + * \date 2013-06-13 + */ + +/* + * Copyright (C) 2013 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#ifndef _INCLUDE__PLATFORM__ARNDALE__REGULATOR__CONSTS_H_ +#define _INCLUDE__PLATFORM__ARNDALE__REGULATOR__CONSTS_H_ + +#include + +namespace Regulator { + + enum Regulator_id { + CLK_CPU, + MAX, + INVALID + }; + + struct Regulator_name { + Regulator_id id; + const char * name; + }; + + Regulator_name names[] = { + { CLK_CPU, "clock-cpu" }, + }; + + Regulator_id regulator_id_by_name(const char * name) + { + for (unsigned i = 0; i < MAX; i++) + if (Genode::strcmp(names[i].name, name) == 0) + return names[i].id; + return INVALID; + } + + const char * regulator_name_by_id(Regulator_id id) { + return (id < MAX) ? names[id].name : 0; } + + + /*************************************** + ** Device specific level definitions ** + ***************************************/ + + enum Cpu_clock_freq { + CPU_FREQ_200, + CPU_FREQ_400, + CPU_FREQ_600, + CPU_FREQ_800, + CPU_FREQ_1000, + CPU_FREQ_1200, + CPU_FREQ_1400, + CPU_FREQ_1600, + CPU_FREQ_1700, /* warning: 1700 not recommended by the reference manual + we just insert this for performance measurement against + Linux, which uses this overclocking */ + CPU_FREQ_MAX + }; +} + +#endif /* _INCLUDE__PLATFORM__ARNDALE__REGULATOR__CONSTS_H_ */ diff --git a/os/run/cpufreq.run b/os/run/cpufreq.run new file mode 100644 index 000000000..c7499fcd9 --- /dev/null +++ b/os/run/cpufreq.run @@ -0,0 +1,78 @@ +# +# Build +# + +# generic components +set build_components { + core + init + drivers/platform + drivers/timer + test/affinity + test/cpufreq +} + +build $build_components +create_boot_directory + + +# +# Config +# + +set config { + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +} + +install_config $config + + +# +# Boot modules +# + +# generic modules +set boot_modules { + core + init + platform_drv + timer + test-affinity + test-cpufreq +} + +build_boot_image $boot_modules +run_genode_until forever + diff --git a/os/src/drivers/platform/arndale/cmu.h b/os/src/drivers/platform/arndale/cmu.h new file mode 100644 index 000000000..6a3951b38 --- /dev/null +++ b/os/src/drivers/platform/arndale/cmu.h @@ -0,0 +1,244 @@ +/* + * \brief Regulator driver for clock management unit of Exynos5250 SoC + * \author Stefan Kalkowski + * \date 2013-06-13 + */ + +/* + * Copyright (C) 2013 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#ifndef _CMU_H_ +#define _CMU_H_ + +#include +#include +#include +#include +#include + +using namespace Regulator; + + +class Cmu : public Regulator::Driver, + public Genode::Attached_io_mem_dataspace, + public Genode::Mmio +{ + private: + + + /*********************** + ** CMU CPU registers ** + ***********************/ + + struct Apll_lock : Register<0x000, 32> + { + struct Pll_locktime : Bitfield <0, 20> { }; + + static access_t max_lock_time(access_t pdiv) { return pdiv * 250; }; + }; + + struct Apll_con0 : Register<0x100, 32> + { + struct S : Bitfield <0, 3> + { + /* S values for frequencies 200 - 1700 */ + static const Genode::uint8_t values[]; + }; + + struct P : Bitfield <8, 6> + { + /* P values for frequencies 200 - 1700 */ + static const Genode::uint8_t values[]; + }; + + struct M : Bitfield <16, 10> + { + /* M values for frequencies 200 - 1700 */ + static const Genode::uint16_t values[]; + }; + + struct Locked : Bitfield <29, 1> { }; + }; + + struct Clk_src_cpu : Register<0x200, 32> + { + struct Mux_cpu_sel : Bitfield<16, 1> + { + enum { MOUT_APLL, SCLK_MPLL}; + }; + }; + + struct Clk_mux_stat_cpu : Register<0x400, 32> + { + struct Cpu_sel : Bitfield<16, 3> + { + enum { MOUT_APLL = 0b1, SCLK_MPLL = 0b10 }; + }; + }; + + struct Clk_div_cpu0 : Register<0x500, 32> + { + /* Cpu0 divider values for frequencies 200 - 1700 */ + static const Genode::uint32_t values[]; + }; + + struct Clk_div_cpu1 : Register<0x504, 32> + { + /* Divider for cpu1 doesn't change */ + enum { FIX_VALUE = 32 }; + }; + + struct Clk_div_stat_cpu0 : Register<0x600, 32> + { + struct Div_arm : Bitfield< 0, 1> {}; + struct Div_cpud : Bitfield< 4, 1> {}; + struct Div_acp : Bitfield< 8, 1> {}; + struct Div_pheriph : Bitfield<12, 1> {}; + struct Div_atb : Bitfield<16, 1> {}; + struct Div_pclk_dbg : Bitfield<20, 1> {}; + struct Div_apll : Bitfield<24, 1> {}; + struct Div_arm2 : Bitfield<28, 1> {}; + + static bool in_progress(access_t stat_word) + { + return stat_word & (Div_arm::bits(1) | + Div_cpud::bits(1) | + Div_acp::bits(1) | + Div_pheriph::bits(1) | + Div_atb::bits(1) | + Div_pclk_dbg::bits(1) | + Div_apll::bits(1) | + Div_arm2::bits(1)); + } + }; + + struct Clk_div_stat_cpu1 : Register<0x604, 32> + { + struct Div_copy : Bitfield<0, 1> { }; + struct Div_hpm : Bitfield<4, 1> { }; + + static bool in_progress(access_t stat_word) + { + return stat_word & (Div_copy::bits(1) | + Div_hpm::bits(1)); + } + }; + + + /** + * CPU frequency scaling function + */ + void _cpu_clk_freq(Cpu_clock_freq freq) + { + /** + * change clock divider values + */ + + /* cpu0 divider */ + write(Clk_div_cpu0::values[freq]); + while (Clk_div_stat_cpu0::in_progress(read())) ; + + /* cpu1 divider */ + write(Clk_div_cpu1::FIX_VALUE); + while (Clk_div_stat_cpu1::in_progress(read())) ; + + + /** + * change APLL frequency + */ + + /* change reference clock to MPLL */ + write(Clk_src_cpu::Mux_cpu_sel::SCLK_MPLL); + while (read() + != Clk_mux_stat_cpu::Cpu_sel::SCLK_MPLL) ; + + /* set lock time */ + unsigned pdiv = Apll_con0::P::values[freq]; + write(Apll_lock::max_lock_time(pdiv)); + + /* change P, M, S values of APLL */ + write(Apll_con0::P::values[freq]); + write(Apll_con0::M::values[freq]); + write(Apll_con0::S::values[freq]); + + while (!read()) ; + + /* change reference clock back to APLL */ + write(Clk_src_cpu::Mux_cpu_sel::MOUT_APLL); + while (read() + != Clk_mux_stat_cpu::Cpu_sel::MOUT_APLL) ; + } + + public: + + /** + * Constructor + */ + Cmu() + : Genode::Attached_io_mem_dataspace(Genode::Board_base::CMU_MMIO_BASE, + Genode::Board_base::CMU_MMIO_SIZE), + Mmio((Genode::addr_t)local_addr()) + { + /* set CPU to full speed */ + _cpu_clk_freq(CPU_FREQ_1600); + } + + + /******************************** + ** Regulator driver interface ** + ********************************/ + + void set_level(Regulator_id id, unsigned long level) + { + switch (id) { + case CLK_CPU: + if (level >= CPU_FREQ_MAX) { + PWRN("level=%ld not supported", level); + return; + } + _cpu_clk_freq(static_cast(level)); + default: + PWRN("Unsupported for %s", names[id].name); + } + } + + unsigned long level(Regulator_id id) + { + switch (id) { + default: + PWRN("Unsupported for %s", names[id].name); + } + return 0; + } + + void set_state(Regulator_id id, bool enable) + { + switch (id) { + default: + PWRN("Unsupported for %s", names[id].name); + } + } + + bool state(Regulator_id id) + { + switch (id) { + default: + PWRN("Unsupported for %s", names[id].name); + } + return true; + } +}; + + +const Genode::uint8_t Cmu::Apll_con0::S::values[] = { 2, 1, 1, 0, 0, 0, 0, 0, 0 }; +const Genode::uint16_t Cmu::Apll_con0::M::values[] = { 100, 100, 200, 100, 125, + 150, 175, 200, 425 }; +const Genode::uint8_t Cmu::Apll_con0::P::values[] = { 3, 3, 4, 3, 3, 3, 3, 3, 6 }; +const Genode::uint32_t Cmu::Clk_div_cpu0::values[] = { 0x1117710, 0x1127710, 0x1137710, + 0x2147710, 0x2147710, 0x3157720, + 0x4167720, 0x4177730, 0x5377730 }; +#endif /* _CMU_H_ */ diff --git a/os/src/drivers/platform/arndale/main.cc b/os/src/drivers/platform/arndale/main.cc new file mode 100644 index 000000000..ba7e4960b --- /dev/null +++ b/os/src/drivers/platform/arndale/main.cc @@ -0,0 +1,55 @@ +/* + * \brief Driver for Arndale specific platform devices (clocks, power, etc.) + * \author Stefan Kalkowski + * \date 2013-06-13 + */ + +/* + * Copyright (C) 2013 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#include +#include +#include +#include +#include + +#include + + +struct Driver_factory : Regulator::Driver_factory +{ + Cmu _cmu; + + Regulator::Driver &create(Regulator::Regulator_id id) { + switch (id) { + case Regulator::CLK_CPU: + return _cmu; + default: + throw Root::Invalid_args(); /* invalid regulator */ + }; + } + + void destroy(Regulator::Driver &driver) { } + +}; + + +int main(int, char **) +{ + using namespace Genode; + + PINF("--- Arndale platform driver ---\n"); + + static Cap_connection cap; + static Rpc_entrypoint ep(&cap, 4096, "arndale_plat_ep"); + static ::Driver_factory driver_factory; + static Regulator::Root reg_root(&ep, env()->heap(), driver_factory); + env()->parent()->announce(ep.manage(®_root)); + + sleep_forever(); + return 0; +} diff --git a/os/src/drivers/platform/arndale/target.mk b/os/src/drivers/platform/arndale/target.mk new file mode 100644 index 000000000..c427ae6fb --- /dev/null +++ b/os/src/drivers/platform/arndale/target.mk @@ -0,0 +1,5 @@ +TARGET = platform_drv +REQUIRES = platform_arndale +SRC_CC = main.cc +INC_DIR += ${PRG_DIR} +LIBS = base diff --git a/os/src/test/cpufreq/main.cc b/os/src/test/cpufreq/main.cc new file mode 100644 index 000000000..c6b35e994 --- /dev/null +++ b/os/src/test/cpufreq/main.cc @@ -0,0 +1,42 @@ +/* + * \brief Test for changing the CPU frequency + * \author Stefan Kalkowski + * \date 2013-06-14 + */ + +/* + * Copyright (C) 2013 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +/* Genode includes */ +#include +#include +#include + +#include +#include +#include + +int main(int argc, char **argv) +{ + using namespace Genode; + + printf("--- test-cpufreq started ---\n"); + + static Timer::Connection timer; + static Regulator::Connection cpu_regulator(Regulator::CLK_CPU); + bool high = true; + + while (true) { + timer.msleep(10000); + PINF("Setting CPU frequency %s", high ? "low" : "high"); + cpu_regulator.set_level(high ? Regulator::CPU_FREQ_200 + : Regulator::CPU_FREQ_1600); + high = !high; + } + + return 1; +} diff --git a/os/src/test/cpufreq/target.mk b/os/src/test/cpufreq/target.mk new file mode 100644 index 000000000..5da63596a --- /dev/null +++ b/os/src/test/cpufreq/target.mk @@ -0,0 +1,4 @@ +TARGET = test-cpufreq +REQUIRES = platform_arndale +SRC_CC = main.cc +LIBS = base