/* * \brief Regulator driver for clock management unit of Exynos4412 SoC * \author Alexy Gallardo Segura * \author Humberto Lopez Leon * \author Reinier Millo Sanchez * \date 2015-04-30 */ /* * Copyright (C) 2015 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_mmio { private: static const Genode::uint16_t m_values[]; /* M values for frequencies */ static const Genode::uint8_t p_values[]; /* P values for frequencies */ static const Genode::uint8_t s_values[]; /* S values for frequencies */ template struct Pll_lock : Register { struct Pll_locktime : Register::template Bitfield<0, 20> { }; static Genode::uint32_t max_lock_time(Genode::uint8_t pdiv) { return pdiv * 250; }; }; template struct Pll_con0 : Register { struct S : Register::template Bitfield < 0, 3> { }; struct P : Register::template Bitfield < 8, 6> { }; struct M : Register::template Bitfield <16, 10> { }; struct Locked : Register::template Bitfield <29, 1> { }; struct Enable : Register::template Bitfield <31, 1> { }; }; /*********************** ** CMU CPU registers ** ***********************/ typedef Pll_lock<4000> Apll_lock; typedef Pll_con0<0x4100> Apll_con0; struct Clk_src_cpu : Register<0x4200, 32> { struct Mux_core_sel : Bitfield<16, 1> { enum { MOUT_APLL, SCLK_MPLL}; }; }; struct Clk_mux_stat_cpu : Register<0x4400, 32> { struct Core_sel : Bitfield<16, 3> { enum { MOUT_APLL = 0b1, SCLK_MPLL = 0b10 }; }; }; struct Clk_div_cpu0 : Register<0x4500, 32> { /* Cpu0 divider values for frequencies 200 - 1400 */ static const Genode::uint32_t values[]; }; struct Clk_div_cpu1 : Register<0x4504, 32> { /* Divider for cpu1 doesn't change */ enum { FIX_VALUE = 32 }; }; struct Clk_div_stat_cpu0 : Register<0x4600, 32> { struct Div_core : Bitfield< 0, 1> {}; struct Div_corem0 : Bitfield< 4, 1> {}; struct Div_corem1 : 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_core2 : Bitfield<28, 1> {}; static bool in_progress(access_t stat_word) { return stat_word & (Div_core::bits(1) | Div_corem0::bits(1) | Div_corem1::bits(1) | Div_pheriph::bits(1) | Div_atb::bits(1) | Div_pclk_dbg::bits(1) | Div_apll::bits(1) | Div_core2::bits(1)); } }; struct Clk_div_stat_cpu1 : Register<0x4604, 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)); } }; /************************ ** CMU CORE registers ** ************************/ typedef Pll_lock<0x0008> Mpll_lock; typedef Pll_con0<0x0108> Mpll_con0; struct Clk_src_dmc : Register<0x4200, 32> { struct Mux_mpll_sel : Bitfield<12, 1> { enum { XXTI, MPLL_FOUT_RGT }; }; }; struct Clk_gate_ip_dmc : Register<0x0900, 32> { }; struct Clk_gate_ip_isp0 : Register<0x8800, 32> { }; struct Clk_gate_ip_isp1 : Register<0x8804, 32> { }; /******************* ** CPU functions ** *******************/ Cpu_clock_freq _cpu_freq; void _cpu_clk_freq(unsigned long level) { PINF("Setting CPU frequency %lu\n",level); unsigned freq; switch (level) { case CPU_FREQ_200: freq = 0; break; case CPU_FREQ_400: freq = 1; break; case CPU_FREQ_600: freq = 2; break; case CPU_FREQ_800: freq = 3; break; case CPU_FREQ_1000: freq = 4; break; case CPU_FREQ_1200: freq = 5; break; case CPU_FREQ_1400: freq = 6; break; default: PWRN("Unsupported CPU frequency level %ld", level); PWRN("Supported values are 200, 400, 600, 800, 1000, 1200, 14000 MHz"); PWRN("and 1, 1.2, 1.4, 1.6, 1.7 GHz"); return; }; /** * 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_core_sel::SCLK_MPLL); while (read() != Clk_mux_stat_cpu::Core_sel::SCLK_MPLL) ; /* set lock time */ unsigned pdiv = p_values[freq]; write(Apll_lock::max_lock_time(pdiv)); /* change P, M, S values of APLL */ write(p_values[freq]); write(m_values[freq]); write(s_values[freq]); while (!read()) ; /* change reference clock back to APLL */ write(Clk_src_cpu::Mux_core_sel::MOUT_APLL); while (read() != Clk_mux_stat_cpu::Core_sel::MOUT_APLL) ; _cpu_freq = static_cast(level); } public: /** * Constructor */ Cmu() : Genode::Attached_mmio(Genode::Board_base::CMU_MMIO_BASE, Genode::Board_base::CMU_MMIO_SIZE), _cpu_freq(CPU_FREQ_1400) { /** * Set default CPU frequency */ _cpu_clk_freq(_cpu_freq); } /******************************** ** Regulator driver interface ** ********************************/ void level(Regulator_id id, unsigned long level) { switch (id) { case CLK_CPU: _cpu_clk_freq(level); break; default: PWRN("Unsupported for %s", names[id].name); } } unsigned long level(Regulator_id id) { switch (id) { case CLK_CPU: return _cpu_freq; default: PWRN("Unsupported for %s", names[id].name); } return 0; } void state(Regulator_id id, bool enable) { } bool state(Regulator_id id) { return true; } }; const Genode::uint8_t Cmu::s_values[] = { 2, 1, 1, 0, 0, 0, 0, 0, 0 }; const Genode::uint16_t Cmu::m_values[] = { 100, 100, 200, 100, 125, 150, 175, 200, 425 }; const Genode::uint8_t Cmu::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}; #endif /* _CMU_H_ */