/* * \brief Regulator driver for clock management unit of Exynos5250 SoC * \author Stefan Kalkowski * \date 2013-06-13 */ /* * Copyright (C) 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 _DRIVERS__PLATFORM__SPEC__ARNDALE__CMU_H_ #define _DRIVERS__PLATFORM__SPEC__ARNDALE__CMU_H_ #include #include #include #include using namespace Regulator; using Genode::warning; class Cmu : public Regulator::Driver, private Genode::Attached_mmio { private: enum { CMU_MMIO_BASE = 0x10010000, CMU_MMIO_SIZE = 0x24000, }; 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<0> Apll_lock; typedef Pll_con0<0x100> Apll_con0; 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)); } }; /************************ ** CMU CORE registers ** ************************/ typedef Pll_lock<0x4000> Mpll_lock; typedef Pll_con0<0x4100> Mpll_con0; struct Clk_src_core1 : Register<0x4204, 32> { struct Mux_mpll_sel : Bitfield<8, 1> { enum { XXTI, MPLL_FOUT_RGT }; }; }; struct Clk_gate_ip_acp : Register<0x8800, 32> { }; struct Clk_gate_ip_isp0 : Register<0xc800, 32> { }; struct Clk_gate_ip_isp1 : Register<0xc804, 32> { }; struct Clk_gate_sclk_isp : Register<0xc900, 32> { }; /*********************** ** CMU TOP registers ** ***********************/ typedef Pll_lock<0x10020> Cpll_lock; typedef Pll_lock<0x10030> Epll_lock; typedef Pll_lock<0x10040> Vpll_lock; typedef Pll_lock<0x10050> Gpll_lock; typedef Pll_con0<0x10120> Cpll_con0; typedef Pll_con0<0x10130> Epll_con0; typedef Pll_con0<0x10140> Vpll_con0; typedef Pll_con0<0x10150> Gpll_con0; struct Clk_src_top2 : Register<0x10218, 32> { struct Mux_mpll_user_sel : Bitfield<20, 1> { enum { XXTI, MOUT_MPLL}; }; }; struct Clk_src_fsys : Register<0x10244, 32> { struct Sata_sel : Bitfield<24, 1> { enum { SCLK_MPLL_USER, SCLK_BPLL_USER }; }; struct Usbdrd30_sel : Bitfield<28, 1> { enum { SCLK_MPLL_USER, SCLK_CPLL }; }; }; struct Clk_src_mask_fsys : Register<0x10340, 32> { struct Mmc0_mask : Bitfield<0, 1> { enum { MASK, UNMASK }; }; struct Sata_mask : Bitfield<24, 1> { enum { MASK, UNMASK }; }; struct Usbdrd30_mask : Bitfield<28, 1> { enum { MASK, UNMASK }; }; }; struct Clk_div_fsys0 : Register<0x10548, 32> { struct Sata_ratio : Bitfield<20, 4> { }; struct Usbdrd30_ratio : Bitfield<24, 4> { }; }; struct Clk_div_stat_fsys0 : Register<0x10648, 32> { struct Div_sata : Bitfield<20, 1> {}; struct Div_usbdrd30 : Bitfield<24, 1> {}; }; struct Clk_gate_ip_gscl : Register<0x10920, 32> { }; struct Clk_gate_ip_disp1 : Register<0x10928, 32> { struct Clk_mixer : Bitfield<5, 1> { }; struct Clk_hdmi : Bitfield<6, 1> { }; }; struct Clk_gate_ip_mfc : Register<0x1092c, 32> { }; struct Clk_gate_ip_g3d : Register<0x10930, 32> { }; struct Clk_gate_ip_gen : Register<0x10934, 32> { }; struct Clk_gate_ip_fsys : Register<0x10944, 32> { struct Pdma0 : Bitfield<1, 1> { }; struct Pdma1 : Bitfield<2, 1> { }; struct Sata : Bitfield<6, 1> { }; struct Sdmmc0 : Bitfield<12, 1> { }; struct Usbhost20 : Bitfield<18, 1> { }; struct Usbdrd30 : Bitfield<19, 1> { }; struct Sata_phy_ctrl : Bitfield<24, 1> { }; struct Sata_phy_i2c : Bitfield<25, 1> { }; }; struct Clk_src_disp1_0 : Register<0x1022c, 32> { struct Hdmi_sel : Bitfield<20, 1> { }; }; struct Clk_src_mask_disp1_0 : Register<0x1032c, 32> { struct Hdmi_mask : Bitfield<20, 1> { }; }; struct Clk_gate_ip_peric : Register<0x10950, 32> { struct Clk_uart2 : Bitfield<2, 1> { }; struct Clk_i2chdmi : Bitfield<14, 1> { }; struct Clk_pwm : Bitfield<24, 1> { }; }; struct Clk_gate_block : Register<0x10980, 32> { struct Clk_disp1 : Bitfield<5, 1> { }; struct Clk_gen : Bitfield<2, 1> { }; }; /************************* ** CMU CDREX registers ** *************************/ typedef Pll_lock<0x20010> Bpll_lock; typedef Pll_con0<0x20110> Bpll_con0; struct Pll_div2_sel : Register<0x20a24, 32> { struct Mpll_fout_sel : Bitfield<4, 1> { enum { MPLL_FOUT_HALF, MPLL_FOUT }; }; }; /******************* ** CPU functions ** *******************/ Cpu_clock_freq _cpu_freq; void _cpu_clk_freq(unsigned long 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; case CPU_FREQ_1600: freq = 7; break; case CPU_FREQ_1700: freq = 8; break; default: warning("Unsupported CPU frequency level ", level); warning("Supported values are 200, 400, 600, 800 MHz"); warning("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_cpu_sel::SCLK_MPLL); while (read() != Clk_mux_stat_cpu::Cpu_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_cpu_sel::MOUT_APLL); while (read() != Clk_mux_stat_cpu::Cpu_sel::MOUT_APLL) ; _cpu_freq = static_cast(level); } /********************** ** Device functions ** **********************/ void _hdmi_enable() { write(1); Clk_gate_ip_disp1::access_t gd1 = read(); Clk_gate_ip_disp1::Clk_mixer::set(gd1, 1); Clk_gate_ip_disp1::Clk_hdmi::set(gd1, 1); write(gd1); write(1); write(1); write(1); } void _sata_enable() { /* enable I2C for SATA */ write(1); /** * set SATA clock to 66 MHz (nothing else supported) * assuming 800 MHz from sclk_mpll_user, formula: sclk / (divider + 1) */ write(11); /* */ while (read()) ; /* enable SATA and SATA Phy */ write(1); write(1); write(1); } void _usb30_enable() { /** * set USBDRD30 clock to 66 MHz * assuming 800 MHz from sclk_mpll_user, formula: sclk / (divider + 1) */ write(11); while (read()) ; /* enable USBDRD30 clock */ write(1); write(1); } void _enable(Regulator_id id) { switch (id) { case CLK_SATA: _sata_enable(); break; case CLK_HDMI: _hdmi_enable(); break; case CLK_USB30: _usb30_enable(); break; case CLK_USB20: return write(1); case CLK_MMC0: write(1); write(1); break; default: warning("Unsupported for ", names[id].name); } } void _disable(Regulator_id id) { switch (id) { case CLK_SATA: write(0); write(0); write(0); write(0); break; case CLK_USB30: write(0); write(0); break; case CLK_USB20: return write(0); case CLK_MMC0: write(0); write(0); break; default: warning("Unsupported for ", names[id].name); } } public: /** * Constructor */ Cmu(Genode::Env &env) : Genode::Attached_mmio(env, CMU_MMIO_BASE, CMU_MMIO_SIZE), _cpu_freq(CPU_FREQ_1600) { /** * Close certain clock gates by default (~ 0.7 Watt reduction) */ write(0); write(0); write(0); write(0); write(0); write(0); write(0); write(0); write(0); write(0); write(Clk_gate_ip_peric::Clk_uart2::bits(1) | Clk_gate_ip_peric::Clk_pwm::bits(1)); write(Clk_gate_block::Clk_gen::bits(1)); /** * Set default CPU frequency */ _cpu_clk_freq(_cpu_freq); /** * Hard wiring of certain reference clocks */ write(Pll_div2_sel::Mpll_fout_sel::MPLL_FOUT_HALF); write(Clk_src_core1::Mux_mpll_sel::MPLL_FOUT_RGT); write(Clk_src_top2::Mux_mpll_user_sel::MOUT_MPLL); write(Clk_src_fsys::Sata_sel::SCLK_MPLL_USER); write(Clk_src_fsys::Usbdrd30_sel::SCLK_MPLL_USER); } /******************************** ** Regulator driver interface ** ********************************/ void level(Regulator_id id, unsigned long level) override { switch (id) { case CLK_CPU: _cpu_clk_freq(level); break; default: warning("Unsupported for ", names[id].name); } } unsigned long level(Regulator_id id) override { switch (id) { case CLK_CPU: return _cpu_freq; case CLK_USB30: case CLK_SATA: return 66666666; /* 66 MHz */ default: warning("Unsupported for ", names[id].name); } return 0; } void state(Regulator_id id, bool enable) override { if (enable) _enable(id); else _disable(id); } bool state(Regulator_id id) override { switch (id) { case CLK_SATA: return read() && read() && read(); case CLK_USB30: return read() && read(); case CLK_USB20: return read(); case CLK_MMC0: return read() && read(); default: warning("Unsupported for ", names[id].name); } 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, 0x4177730, 0x5377730 }; #endif /* _DRIVERS__PLATFORM__SPEC__ARNDALE__CMU_H_ */