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
This commit is contained in:
Sebastian Sumpf 2025-03-14 21:53:35 +01:00 committed by Norman Feske
parent 721e2c634a
commit e5cbf602e0
4 changed files with 382 additions and 49 deletions

View File

@ -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 <types.h>
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_ */

View File

@ -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 <context.h>
#include <context_descriptor.h>
#include <platform_session.h>
#include <rc6.h>
#include <reset.h>
#include <ring_buffer.h>
@ -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> _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_context> &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()) {

View File

@ -20,7 +20,7 @@
#include <timer_session/connection.h>
/* local includes */
#include <types.h>
#include <device_info.h>
namespace Igd {
@ -35,19 +35,28 @@ class Igd::Mmio : public Platform::Device::Mmio<MMIO_SIZE>
{
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<MMIO_SIZE>
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<MMIO_SIZE>
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<MMIO_SIZE>::Delayer
@ -1441,13 +1539,6 @@ class Igd::Mmio : public Platform::Device::Mmio<MMIO_SIZE>
*/
void _disable_rps()
{
/*
* Set RC0 state -- does not matter at this point b/c
* we disable RC states entirely.
*/
write_post<RC_CTRL1::Rc_state>(0);
write<RC_CTRL0>(0);
write<RP_FREQ_NORMAL::Turbo_disable>(1);
write<RP_CTRL>(0);
}

View File

@ -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 <mmio.h>
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<Rc6> _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<Mmio::GEN8_RC6_CTX_INFO>()) 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<Mmio::RC_WAKE_RATE_LIMIT::Rc6>(108);
} else
_mmio.write<Mmio::RC_WAKE_RATE_LIMIT::Rc6>(54);
_mmio.write<Mmio::RC_EVALUATION_INTERVAL>(125000); /* 12500 * 1280ns */
_mmio.write<Mmio::RC_IDLE_HYSTERSIS>(25); /* 25 * 1280ns */
_mmio.write<Mmio::RING_MAX_IDLE>(10);
_mmio.write<Mmio::RC_WAKE_HYSTERSIS>(0);
_mmio.write<Mmio::RC_PROMO_TIME>(37500); /* 37.5/125ms per EI */
/* 2c: Program Coarse Power Gating Policies */
_mmio.write<Mmio::GEN9_RENDER_PG_IDLE_HYSTERESIS>(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<Mmio::GEN9_PG_ENABLE::Gen9_render_pg_enable>(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<Mmio::RC_WAKE_RATE_LIMIT::Rc6>(54);
_mmio.write<Mmio::RC_EVALUATION_INTERVAL>(125000); /* 12500 * 1280ns */
_mmio.write<Mmio::RC_IDLE_HYSTERSIS>(25); /* 25 * 1280ns */
_mmio.write<Mmio::RING_MAX_IDLE>(10);
_mmio.write<Mmio::RC_WAKE_HYSTERSIS>(0);
_mmio.write<Mmio::RC_PROMO_TIME>(50000); /* 50/125ms per EI */
/* 2c: Program Coarse Power Gating Policies */
_mmio.write<Mmio::GEN9_RENDER_PG_IDLE_HYSTERESIS>(60);
/* 3a: Enable RC6 */
/*
* power-gating, special case for Meteor Lake omitted, power-gating for
* VCS omitted
*/
_mmio.write<Mmio::GEN9_PG_ENABLE::Gen9_render_pg_enable>(1);
/*
* Rc6
*/
_mmio.write<Mmio::RC_CTRL1::Target>(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<Mmio::RC_CTRL0>(0);
_mmio.write<Mmio::RC_CTRL0::Rc6_enable>(1);
_mmio.write<Mmio::RC_CTRL1::Target>(4); /* target RC6 */
_suspended = true;
}
void _resume()
{
_mmio.write<Mmio::RC_CTRL0>(_ctrl);
if (_mmio.generation() == 9)
_mmio.write<Mmio::RC_CTRL1::Target>(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<Mmio::GEN9_PG_ENABLE>(0);
_mmio.write<Mmio::RC_CTRL0::Rc6_enable>(0);
_mmio.write<Mmio::RC_CTRL0>(0);
_mmio.write<Mmio::RP_CTRL>(0);
_mmio.write_post<Mmio::RC_CTRL1::Target>(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_ */