From e5cbf602e04970e3df3127d7f4b87b3bdc5918a6 Mon Sep 17 00:00:00 2001 From: Sebastian Sumpf Date: Fri, 14 Mar 2025 21:53:35 +0100 Subject: [PATCH] gpu/intel: RC6 support * add RC6 support for GPU GEN9 (Skylake+) and GEN12 (Tiger Lake+), RC6 is entered by software after CHECK_INACTIVEus of inactivity. When a new Vgpu is scheduled we trigger a resume from RC6. * increase VGPU-watchdog timeout to 300ms issue #5504 --- repos/os/src/driver/gpu/intel/device_info.h | 34 ++++ repos/os/src/driver/gpu/intel/main.cc | 49 ++--- repos/os/src/driver/gpu/intel/mmio.h | 145 +++++++++++--- repos/os/src/driver/gpu/intel/rc6.h | 203 ++++++++++++++++++++ 4 files changed, 382 insertions(+), 49 deletions(-) create mode 100644 repos/os/src/driver/gpu/intel/device_info.h create mode 100644 repos/os/src/driver/gpu/intel/rc6.h diff --git a/repos/os/src/driver/gpu/intel/device_info.h b/repos/os/src/driver/gpu/intel/device_info.h new file mode 100644 index 0000000000..2ba434f189 --- /dev/null +++ b/repos/os/src/driver/gpu/intel/device_info.h @@ -0,0 +1,34 @@ +/* + * \brief Device_info stores GPU information + * \author Sebastian Sumpf + * \date 2025-03-11 + */ + +/* + * Copyright (C) 2025 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 _DEVICE_INFO_ +#define _DEVICE_INFO_ + +#include + +namespace Igd { + struct Device_info; +} + +struct Igd::Device_info +{ + enum Platform { UNKNOWN, BROADWELL, SKYLAKE, KABYLAKE, WHISKEYLAKE, TIGERLAKE }; + enum Stepping { A0, B0, C0, D0, D1, E0, F0, G0 }; + + uint16_t id; + uint8_t generation; + Platform platform; + uint64_t features; +}; + +#endif /* _DEVICE_INFO_ */ diff --git a/repos/os/src/driver/gpu/intel/main.cc b/repos/os/src/driver/gpu/intel/main.cc index 4f58dbf096..3f36ea16fe 100644 --- a/repos/os/src/driver/gpu/intel/main.cc +++ b/repos/os/src/driver/gpu/intel/main.cc @@ -1,11 +1,13 @@ /* - * \brief Intel GPU multiplexer for Broadwell generation and newer + * \brief Intel GPU multiplexer for GEN8-12 + * \author Alexander Boettcher * \author Josef Soentgen - * \data 2017-03-15 + * \author Sebastian Sumpf + * \date 2017-03-15 */ /* - * Copyright (C) 2017-2024 Genode Labs GmbH + * Copyright (C) 2017-2025 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. @@ -41,6 +43,7 @@ #include #include #include +#include #include #include @@ -55,19 +58,6 @@ namespace Igd { } -struct Igd::Device_info -{ - enum Platform { UNKNOWN, BROADWELL, SKYLAKE, KABYLAKE, WHISKEYLAKE, TIGERLAKE }; - enum Stepping { A0, B0, C0, D0, D1, E0, F0, G0 }; - - uint16_t id; - uint8_t generation; - Platform platform; - uint64_t features; -}; - - - struct Igd::Device { struct Unsupported_device : Genode::Exception { }; @@ -75,14 +65,15 @@ struct Igd::Device struct Out_of_ram : Genode::Exception { }; struct Could_not_map_vram : Genode::Exception { }; - /* 200 ms */ - enum { WATCHDOG_TIMEOUT = 200*1000 }; + /* 300 ms */ + enum { WATCHDOG_TIMEOUT = 300*1000 }; Env & _env; Allocator & _md_alloc; Platform::Resources & _resources; Rm_connection & _rm; - Timer::Connection _timer { _env }; + Timer::Connection _timer { _env }; + Constructible _rc6 { }; /********* ** PCI ** @@ -198,12 +189,14 @@ struct Igd::Device if (info.platform == Igd::Device_info::Platform::UNKNOWN) return; - /* set generation for device IO as early as possible */ - mmio.generation(generation); if (info.id == dev_id) { _info = info; _revision.value = rev_id; + + /* set generation for device IO as early as possible */ + mmio.device_info(_info); + _clock_frequency.value = mmio.clock_frequency(); found = true; @@ -1086,6 +1079,9 @@ struct Igd::Device return; } + /* signal alive to RC6 watchdog */ + _rc6->progress(); + Engine &rcs = gpu->rcs; mmio.flush_gfx_tlb(); @@ -1228,7 +1224,7 @@ struct Igd::Device _resources.with_mmio([&](auto &mmio) { - mmio.generation(_info.generation); + mmio.device_info(_info); reinit(mmio); _schedule_stop = false; @@ -1267,6 +1263,11 @@ struct Igd::Device mmio.reset(); mmio.clear_errors(); mmio.init(); + + /* try to enable RC6-sleep-state support */ + _rc6.construct(_env, mmio); + _rc6->enable(); + mmio.enable_intr(); } @@ -1473,8 +1474,12 @@ struct Igd::Device vgpu_unschedule(*_active_vgpu); } + /* disable hw handling of RC6 */ + _rc6->clear(); /* reset */ Igd::Reset(mmio).execute(); + /* re-init RC6 */ + _rc6->enable(); /* set address of global hardware status page */ if (_hw_status_ctx.constructed()) { diff --git a/repos/os/src/driver/gpu/intel/mmio.h b/repos/os/src/driver/gpu/intel/mmio.h index b36bb20757..1c10628496 100644 --- a/repos/os/src/driver/gpu/intel/mmio.h +++ b/repos/os/src/driver/gpu/intel/mmio.h @@ -20,7 +20,7 @@ #include /* local includes */ -#include +#include namespace Igd { @@ -35,19 +35,28 @@ class Igd::Mmio : public Platform::Device::Mmio { public: - unsigned _generation { 0 }; + /* Device info related */ - void generation(unsigned gen) { _generation = gen; } + Device_info _info { }; + + void device_info(Device_info const info) { _info = info; } unsigned generation() const { - if (!_generation) { - Genode::error("Unsupported generation: ", _generation); + if (!_info.generation) { + Genode::error("Unsupported generation: ", _info.generation); } - return _generation; + return _info.generation; } + /* expand here as needed */ + bool skylake() const { + return _info.platform == Device_info::Platform::SKYLAKE; } + + + /* Register definitions */ + enum { /* * XXX IDs are taken from Linux, still looking @@ -668,20 +677,6 @@ class Igd::Mmio : public Platform::Device::Mmio struct Arbiter_mode_control_1 : Bitfield<1, 1> { }; }; - - /* - * IHD-OS-BDW-Vol 2c-11.15 p. 1315 ff. - */ - struct RC_CTRL0 : Register<0x0A090, 32> { }; - - /* - * IHD-OS-BDW-Vol 2c-11.15 p. 1317 ff. - */ - struct RC_CTRL1 : Register<0x0A094, 32> - { - struct Rc_state : Bitfield<18, 1> { }; - }; - /* * IHD-OS-BDW-Vol 2c-11.15 p. 1095 */ @@ -1140,6 +1135,109 @@ class Igd::Mmio : public Platform::Device::Mmio struct MI_RDRET_STATE : Register<0x20FC, 32> { }; struct ECOSKPD : Register<0x21D0, 32> { }; + + /********* + ** RC6 ** + *********/ + + /* + * IHD-OS-BDW-Vol 2c-11.15 p. 1315 ff. + * + * GEN6_RC_CONTROL in Linux + */ + struct RC_CTRL0 : Register<0x0A090, 32> + { + struct Rc6_enable : Bitfield<18, 1> { }; + struct Ei_hw : Bitfield<27, 1> { }; + struct Hw_control_enable : Bitfield<31, 1> { }; + }; + + /* + * IHD-OS-BDW-Vol 2c-11.15 p. 1317 ff. + * + * GEN6_RC_STATE in Linux + */ + struct RC_CTRL1 : Register<0x0A094, 32> + { + struct Target : Bitfield<16, 3> { }; + }; + + /* + * IHD-OS-BDW-Vol 2c-11.15 p. 1311 ff. + */ + struct RC_WAKE_RATE_LIMIT : Register<0xA09C, 32> + { + struct Rc6_deeper : Bitfield<0, 16> { }; + struct Rc6 : Bitfield<16, 16> { }; + }; + + + /* + * IHD-OS-BDW-Vol 2c-11.15 p. 1296 ff. + */ + struct RC_EVALUATION_INTERVAL : Register<0xA0A8, 32> + { + struct Render_standby : Bitfield<0, 24> { }; + }; + + /* + * IHD-OS-BDW-Vol 2c-11.15 p. 1296 ff. + */ + struct RC_IDLE_HYSTERSIS : Register<0xA0AC, 32> + { + struct Detection : Bitfield<0, 24> { }; + }; + + /* + * IHD-OS-BDW-Vol 2c-11.15 p. 1557 ff. + * + * GEN6_RC_SLEEP in Linux + */ + struct RC_WAKE_HYSTERSIS : Register<0xA0B0, 32> + { + /* minimum amount of time in RC0 */ + struct Msg_to_busy_timer : Bitfield<0, 24> { }; + }; + + /* + * IHD-OS-BDW-Vol 2c-11.15 p. 1300 ff. + * + * GEN6_RC6_THRESHOLD in Linux + */ + struct RC_PROMO_TIME : Register<0xA0B8, 32> + { + /* absolute time starting from post-hyst idle */ + struct Promotion_timer : Bitfield<0, 24> { }; + }; + + /* + * Undocumented (PG stands for "power gating") + */ + struct GEN9_PG_ENABLE : Register<0xA210, 32> + { + struct Gen9_render_pg_enable : Bitfield<0, 1> { }; + struct Gen9_media_pg_enable : Bitfield<1, 1> { }; + struct Gen11_media_sampler_pg_enable : Bitfield<2, 1> { }; + /* missing HCP/MFX power gates */ + }; + + /* + * Undocumented + */ + struct GEN9_MEDIA_PG_IDLE_HYSTERESIS : Register<0xA0C4, 32> { }; + struct GEN9_RENDER_PG_IDLE_HYSTERESIS : Register<0xA0C8, 32> { }; + /* + * Undocumented + */ + struct RING_MAX_IDLE : Register<0x2054, 32> { }; + + /* + * Bootmsg - Boot Vector in IHD-OS-BDW-Vol 2c-11.15 p. 202 + * + * Does not seem to correlate but is used to check a Wa + */ + struct GEN8_RC6_CTX_INFO : Register<0x8504, 32> { }; + private: struct Timer_delayer : Genode::Mmio::Delayer @@ -1441,13 +1539,6 @@ class Igd::Mmio : public Platform::Device::Mmio */ void _disable_rps() { - /* - * Set RC0 state -- does not matter at this point b/c - * we disable RC states entirely. - */ - write_post(0); - - write(0); write(1); write(0); } diff --git a/repos/os/src/driver/gpu/intel/rc6.h b/repos/os/src/driver/gpu/intel/rc6.h new file mode 100644 index 0000000000..3f09743692 --- /dev/null +++ b/repos/os/src/driver/gpu/intel/rc6.h @@ -0,0 +1,203 @@ +/* + * \brief RC6 power stage based on the Linux driver + * \author Sebastian Sumpf + * \date 2025-03-11 + * + * Enable RC6, there is no support for deep and deep deep. When enabled low + * voltage mode is entered when the GPU goes idle for CHECK_INACTIVEus while + * power is restored when new workloads are received via 'progress'. + * + * Note: Bspec = Behavioral Specification + */ + +/* + * Copyright (C) 2025 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 _RC6_H_ +#define _RC6_H_ + +#include + +namespace Igd { + class Rc6; +} + + +class Igd::Rc6 +{ + private: + + /* every 2s */ + enum { CHECK_INACTIVE = 2'000'000 }; + + Genode::Env &_env; + Mmio &_mmio; + + Timer::Connection _rc6_watchdog { _env }; + + Mmio::RC_CTRL0::access_t _ctrl { 0 }; + + Genode::Signal_handler _timer_sigh { _env.ep(), *this, + &Rc6::_handle_timer }; + + bool _progress { false }; + bool _suspended { false }; + + /* NEEDS_RC6_CTX_CORRUPTION_WA(i915)) */ + bool _pctx_corrupted() + { + if (_mmio.generation() != 9) return false; + if (_mmio.read()) return false; + + Genode::warning("RC6 context corruption, disabling RC6"); + return true; + } + + /* handles render engine only */ + void _gen9_enable() + { + if (_mmio.skylake()) { + /* + * WaRsDoubleRc6WrlWithCoarsePowerGating:skl Doubling WRL only + * when CPG is enabled + */ + _mmio.write(108); + } else + _mmio.write(54); + + _mmio.write(125000); /* 12500 * 1280ns */ + _mmio.write(25); /* 25 * 1280ns */ + _mmio.write(10); + _mmio.write(0); + _mmio.write(37500); /* 37.5/125ms per EI */ + + + /* 2c: Program Coarse Power Gating Policies */ + _mmio.write(250); + + /* 3a: Enable RC6 */ + /* + * WaRsDisableCoarsePowerGating:skl,cnl + * - Render/Media PG need to be disabled with RC6. + * + * actually just for gt3 and gt4 not for gt2, but we cannot distinguish + * that right now + * if (!_mmio.skylake()) + * _mmio.write(1); + */ + + Mmio::RC_CTRL0::Ei_hw::set(_ctrl, 1); + Mmio::RC_CTRL0::Rc6_enable::set(_ctrl, 1); + Mmio::RC_CTRL0::Hw_control_enable::set(_ctrl, 1); + } + + /* handles render engine only */ + void _gen11_enable() + { + /* 2b: Program RC6 thresholds.*/ + _mmio.write(54); + _mmio.write(125000); /* 12500 * 1280ns */ + _mmio.write(25); /* 25 * 1280ns */ + _mmio.write(10); + _mmio.write(0); + _mmio.write(50000); /* 50/125ms per EI */ + + /* 2c: Program Coarse Power Gating Policies */ + _mmio.write(60); + + /* 3a: Enable RC6 */ + /* + * power-gating, special case for Meteor Lake omitted, power-gating for + * VCS omitted + */ + _mmio.write(1); + + /* + * Rc6 + */ + _mmio.write(4); /* target RC6 */ + Mmio::RC_CTRL0::Ei_hw::set(_ctrl, 1); + Mmio::RC_CTRL0::Rc6_enable::set(_ctrl, 1); + Mmio::RC_CTRL0::Hw_control_enable::set(_ctrl, 1); + } + + void _handle_timer() + { + if (!_progress) { + _enter_rc6(); + return; + } + + _progress = false; + _rc6_watchdog.trigger_once(CHECK_INACTIVE); + } + + void _enter_rc6() + { + /* disable HW timers and enter RC6 */ + _mmio.write(0); + _mmio.write(1); + _mmio.write(4); /* target RC6 */ + + _suspended = true; + } + + void _resume() + { + _mmio.write(_ctrl); + + if (_mmio.generation() == 9) + _mmio.write(0); + + _rc6_watchdog.trigger_once(CHECK_INACTIVE); + + _suspended = false; + } + + public: + + Rc6(Genode::Env &env, Mmio &mmio) + : _env(env), _mmio(mmio) + { + _rc6_watchdog.sigh(_timer_sigh); + } + + void clear() + { + if (_mmio.generation() >= 9) + _mmio.write(0); + + _mmio.write(0); + _mmio.write(0); + _mmio.write(0); + _mmio.write_post(0); + } + + void enable() + { + clear(); + + if (_mmio.generation() >= 11) { + _gen11_enable(); + } + else if (_mmio.generation() >= 9) { + _gen9_enable(); + if (_pctx_corrupted()) return; + } + else return; + + _resume(); + } + + void progress() + { + if (_suspended) _resume(); + if (!_progress) _progress = true; + } +}; + +#endif /* _RC6_H_ */