[PATCH v2 08/10] drm/amd/pp: Implement auto wattman feature on Smu7
Alex Deucher
alexdeucher at gmail.com
Thu Feb 8 15:18:32 UTC 2018
On Thu, Feb 8, 2018 at 6:18 AM, Rex Zhu <Rex.Zhu at amd.com> wrote:
> v2: refine work queue name.
>
> Change-Id: I2521d83cbea9b3418bed63de86cf93deafaab3fb
> Signed-off-by: Rex Zhu <Rex.Zhu at amd.com>
Please provide a patch description. With that fixed:
Reviewed-by: Alex Deucher <alexander.deucher at amd.com>
> ---
> drivers/gpu/drm/amd/powerplay/hwmgr/hwmgr.c | 35 ++++
> drivers/gpu/drm/amd/powerplay/hwmgr/smu7_hwmgr.c | 200 ++++++++++++++++++++++-
> drivers/gpu/drm/amd/powerplay/hwmgr/smu7_hwmgr.h | 26 +++
> drivers/gpu/drm/amd/powerplay/inc/hwmgr.h | 2 +
> 4 files changed, 262 insertions(+), 1 deletion(-)
>
> diff --git a/drivers/gpu/drm/amd/powerplay/hwmgr/hwmgr.c b/drivers/gpu/drm/amd/powerplay/hwmgr/hwmgr.c
> index 33eabc1..b7f4a31 100644
> --- a/drivers/gpu/drm/amd/powerplay/hwmgr/hwmgr.c
> +++ b/drivers/gpu/drm/amd/powerplay/hwmgr/hwmgr.c
> @@ -236,6 +236,20 @@ int hwmgr_early_init(struct pp_instance *handle)
> return 0;
> }
>
> +static void wattman_sample_handler(struct work_struct *work)
> +{
> + struct pp_hwmgr *hwmgr =
> + container_of(work, struct pp_hwmgr, wattman_update_work.work);
> +
> + if (hwmgr->autowattman_enabled) {
> + if (hwmgr->hwmgr_func->update_auto_wattman != NULL)
> + hwmgr->hwmgr_func->update_auto_wattman(hwmgr);
> +
> + if (hwmgr->hwmgr_func->start_auto_wattman != NULL)
> + hwmgr->hwmgr_func->start_auto_wattman(hwmgr, true);
> + }
> +}
> +
> int hwmgr_hw_init(struct pp_instance *handle)
> {
> struct pp_hwmgr *hwmgr;
> @@ -246,6 +260,8 @@ int hwmgr_hw_init(struct pp_instance *handle)
>
> hwmgr = handle->hwmgr;
>
> + INIT_DELAYED_WORK(&hwmgr->wattman_update_work, wattman_sample_handler);
> +
> if (hwmgr->pptable_func == NULL ||
> hwmgr->pptable_func->pptable_init == NULL ||
> hwmgr->hwmgr_func->backend_init == NULL)
> @@ -279,6 +295,11 @@ int hwmgr_hw_init(struct pp_instance *handle)
> if (ret)
> goto err2;
>
> + if (hwmgr->autowattman_enabled) {
> + if (hwmgr->hwmgr_func->start_auto_wattman != NULL)
> + hwmgr->hwmgr_func->start_auto_wattman(hwmgr, true);
> + }
> +
> return 0;
> err2:
> if (hwmgr->hwmgr_func->backend_fini)
> @@ -300,6 +321,10 @@ int hwmgr_hw_fini(struct pp_instance *handle)
>
> hwmgr = handle->hwmgr;
>
> + if (hwmgr->autowattman_enabled) {
> + if (hwmgr->hwmgr_func->start_auto_wattman != NULL)
> + hwmgr->hwmgr_func->start_auto_wattman(hwmgr, false);
> + }
> phm_stop_thermal_controller(hwmgr);
> psm_set_boot_states(hwmgr);
> psm_adjust_power_state_dynamic(hwmgr, false, NULL);
> @@ -322,6 +347,11 @@ int hwmgr_hw_suspend(struct pp_instance *handle)
> return -EINVAL;
>
> hwmgr = handle->hwmgr;
> +
> + if (hwmgr->autowattman_enabled) {
> + if (hwmgr->hwmgr_func->start_auto_wattman != NULL)
> + hwmgr->hwmgr_func->start_auto_wattman(hwmgr, false);
> + }
> phm_disable_smc_firmware_ctf(hwmgr);
> ret = psm_set_boot_states(hwmgr);
> if (ret)
> @@ -360,6 +390,11 @@ int hwmgr_hw_resume(struct pp_instance *handle)
>
> ret = psm_adjust_power_state_dynamic(hwmgr, false, NULL);
>
> + if (hwmgr->autowattman_enabled) {
> + if (hwmgr->hwmgr_func->start_auto_wattman != NULL)
> + hwmgr->hwmgr_func->start_auto_wattman(hwmgr, true);
> + }
> +
> return ret;
> }
>
> diff --git a/drivers/gpu/drm/amd/powerplay/hwmgr/smu7_hwmgr.c b/drivers/gpu/drm/amd/powerplay/hwmgr/smu7_hwmgr.c
> index 08e9e44..b89b530 100644
> --- a/drivers/gpu/drm/amd/powerplay/hwmgr/smu7_hwmgr.c
> +++ b/drivers/gpu/drm/amd/powerplay/hwmgr/smu7_hwmgr.c
> @@ -80,6 +80,7 @@
>
> #define PCIE_BUS_CLK 10000
> #define TCLK (PCIE_BUS_CLK / 10)
> +#define WATTMAM_SAMPLE_PERIOD msecs_to_jiffies(1000)
>
> static const struct profile_mode_setting smu7_profiling[5] =
> {{1, 0, 100, 30, 1, 0, 100, 10},
> @@ -2540,9 +2541,10 @@ static int smu7_hwmgr_backend_init(struct pp_hwmgr *hwmgr)
> data->pcie_gen_cap = AMDGPU_DEFAULT_PCIE_GEN_MASK;
> else
> data->pcie_gen_cap = (uint32_t)sys_info.value;
> +
> if (data->pcie_gen_cap & CAIL_PCIE_LINK_SPEED_SUPPORT_GEN3)
> data->pcie_spc_cap = 20;
> - sys_info.size = sizeof(struct cgs_system_info);
> +
> sys_info.info_id = CGS_SYSTEM_INFO_PCIE_MLW;
> result = cgs_query_system_info(hwmgr->device, &sys_info);
> if (result)
> @@ -2550,6 +2552,11 @@ static int smu7_hwmgr_backend_init(struct pp_hwmgr *hwmgr)
> else
> data->pcie_lane_cap = (uint32_t)sys_info.value;
>
> + sys_info.info_id = CGS_SYSTEM_INFO_VRAM_WIDTH;
> + result = cgs_query_system_info(hwmgr->device, &sys_info);
> + if (!result)
> + data->memory_bit_width = (uint32_t)sys_info.value;
> +
> hwmgr->platform_descriptor.vbiosInterruptId = 0x20000400; /* IRQ_SOURCE1_SW_INT */
> /* The true clock step depends on the frequency, typically 4.5 or 9 MHz. Here we use 5. */
> hwmgr->platform_descriptor.clockStep.engineClock = 500;
> @@ -5070,6 +5077,195 @@ static int smu7_set_power_profile_mode(struct pp_hwmgr *hwmgr, long *input, uint
> return 0;
> }
>
> +static void smu7_start_auto_wattman(struct pp_hwmgr *hwmgr, bool en)
> +{
> + if (en) {
> + smum_send_msg_to_smc(hwmgr, PPSMC_MSG_PmStatusLogStart);
> + schedule_delayed_work(&hwmgr->wattman_update_work, WATTMAM_SAMPLE_PERIOD);
> + } else {
> + cancel_delayed_work_sync(&hwmgr->wattman_update_work);
> + }
> +}
> +
> +static void smu7_auto_wattman_get_data(struct pp_hwmgr *hwmgr)
> +{
> + struct smu7_hwmgr *data = (struct smu7_hwmgr *)(hwmgr->backend);
> +
> + data->wattman_data.average_sclk_busy = cgs_read_ind_register(
> + hwmgr->device,
> + CGS_IND_REG__SMC,
> + ixSMU_PM_STATUS_91);
> + data->wattman_data.average_sclk_busy >>= 8;
> +
> + data->wattman_data.average_mclk_busy = cgs_read_ind_register(
> + hwmgr->device,
> + CGS_IND_REG__SMC,
> + ixSMU_PM_STATUS_92);
> +
> + data->wattman_data.average_mclk_busy >>= 8;
> +
> + data->wattman_data.effective_sclk = cgs_read_ind_register(
> + hwmgr->device,
> + CGS_IND_REG__SMC,
> + ixSMU_PM_STATUS_77);
> +
> + data->wattman_data.effective_mclk = cgs_read_ind_register(
> + hwmgr->device,
> + CGS_IND_REG__SMC,
> + ixSMU_PM_STATUS_111);
> +
> + pr_debug("effective sclk: %x average sclk busy: %x\n",
> + data->wattman_data.effective_sclk,
> + data->wattman_data.average_sclk_busy);
> + pr_debug("effective mclk: %x average mclk busy: %x\n",
> + data->wattman_data.effective_mclk,
> + data->wattman_data.average_mclk_busy);
> +
> + return;
> +}
> +
> +static int smu7_auto_wattman_update_clk_setting(struct pp_hwmgr *hwmgr,
> + struct smu7_auto_wattman_adjust_settings *setting,
> + uint32_t clk_busy, uint32_t threshold)
> +{
> + uint32_t adjust_factor = 0;
> + uint32_t divide_factor = 0;
> +
> + if (setting == NULL)
> + return -EINVAL;
> +
> + if (clk_busy < threshold) {
> + divide_factor = threshold / AutoWattmanAlgorithmMaxAdjustFactor;
> + adjust_factor = AutoWattmanAlgorithmMaxAdjustFactor - (clk_busy / divide_factor);
> + setting->uphyst_adjust = adjust_factor;
> + setting->uphyst_polarity = 1;
> + setting->downhyst_adjust = 6 * adjust_factor;
> + setting->downhyst_polarity = 0;
> + setting->activity_adjust = 2 * adjust_factor;
> + setting->activity_polarity = 1;
> + } else {
> + divide_factor = (100 - threshold) / AutoWattmanAlgorithmMaxAdjustFactor;
> + adjust_factor = (clk_busy - threshold + divide_factor - 1) / divide_factor;
> + setting->uphyst_adjust = adjust_factor;
> + setting->uphyst_polarity = 0;
> + setting->downhyst_adjust = 6 * adjust_factor;
> + setting->downhyst_polarity = 1;
> + setting->activity_adjust = 2 * adjust_factor;
> + setting->activity_polarity = 0;
> + }
> +
> + return 0;
> +}
> +
> +static void smu7_auto_wattman_algorithm(struct pp_hwmgr *hwmgr)
> +{
> + struct smu7_hwmgr *data = (struct smu7_hwmgr *)(hwmgr->backend);
> + uint32_t threshold = 0x32;
> +
> + smu7_auto_wattman_update_clk_setting(hwmgr, &data->sclk_settings,
> + data->wattman_data.average_sclk_busy, threshold);
> +
> + if (data->memory_bit_width == 64)
> + threshold = 0xA;
> + else if (hwmgr->chip_id == CHIP_POLARIS12)
> + threshold = 0xF;
> + else
> + threshold = 0x14;
> +
> + smu7_auto_wattman_update_clk_setting(hwmgr, &data->mclk_settings,
> + data->wattman_data.average_mclk_busy, threshold);
> +}
> +
> +static int smu7_auto_wattman_get_adjust_setting(uint32_t low_limit, uint32_t high_limit,
> + uint32_t polarity, uint32_t adjust_value,
> + uint32_t original_setting)
> +{
> + if (!polarity) {
> + if (original_setting < low_limit + adjust_value)
> + original_setting = low_limit;
> + else
> + original_setting -= adjust_value;
> + } else {
> + if ((high_limit < adjust_value) || (high_limit - adjust_value < original_setting))
> + original_setting = high_limit;
> + else
> + original_setting += adjust_value;
> + }
> +
> + return original_setting;
> +}
> +
> +static int smu7_update_auto_wattman(struct pp_hwmgr *hwmgr)
> +{
> + struct smu7_hwmgr *data = (struct smu7_hwmgr *)(hwmgr->backend);
> + struct profile_mode_setting tmp;
> + uint32_t high_limit, low_limit;
> +
> + high_limit = AutoWattmanSCLKHighLimits;
> + low_limit = AutoWattmanSCLKLowLimits;
> +
> + smu7_auto_wattman_get_data(hwmgr);
> + smu7_auto_wattman_algorithm(hwmgr);
> +
> + tmp.sclk_up_hyst = smu7_auto_wattman_get_adjust_setting(low_limit & 0xff,
> + high_limit & 0xff,
> + data->sclk_settings.uphyst_polarity,
> + data->sclk_settings.uphyst_adjust,
> + data->current_profile_setting.sclk_up_hyst);
> +
> + tmp.sclk_down_hyst = smu7_auto_wattman_get_adjust_setting((low_limit >> 8) & 0xff,
> + (high_limit >> 8) & 0xff,
> + data->sclk_settings.downhyst_polarity,
> + data->sclk_settings.downhyst_adjust,
> + data->current_profile_setting.sclk_down_hyst);
> +
> + tmp.sclk_activity = smu7_auto_wattman_get_adjust_setting((low_limit >> 16) & 0xffff,
> + (high_limit >> 16) & 0xffff,
> + data->sclk_settings.activity_polarity,
> + data->sclk_settings.activity_adjust,
> + data->current_profile_setting.sclk_activity);
> +
> + if ((tmp.sclk_up_hyst == data->current_profile_setting.sclk_up_hyst) &&
> + (tmp.sclk_down_hyst == data->current_profile_setting.sclk_down_hyst) &&
> + (tmp.sclk_activity == data->current_profile_setting.sclk_activity))
> + tmp.bupdate_sclk = false;
> + else
> + tmp.bupdate_sclk = true;
> +
> + high_limit = AutoWattmanMCLKHighLimits;
> + low_limit = AutoWattmanMCLKLowLimits;
> +
> + tmp.mclk_up_hyst = smu7_auto_wattman_get_adjust_setting(low_limit && 0xff,
> + high_limit && 0xff,
> + data->mclk_settings.uphyst_polarity,
> + data->mclk_settings.uphyst_adjust,
> + data->current_profile_setting.mclk_up_hyst);
> +
> + tmp.mclk_down_hyst = smu7_auto_wattman_get_adjust_setting((low_limit >> 8) && 0xff,
> + (high_limit >> 8) && 0xff,
> + data->mclk_settings.downhyst_polarity,
> + data->mclk_settings.downhyst_adjust,
> + data->current_profile_setting.mclk_down_hyst);
> +
> + tmp.mclk_activity = smu7_auto_wattman_get_adjust_setting((low_limit >> 16) && 0xffff,
> + (high_limit >> 16) && 0xffff,
> + data->mclk_settings.activity_polarity,
> + data->mclk_settings.activity_adjust,
> + data->current_profile_setting.mclk_activity);
> +
> + if ((tmp.mclk_up_hyst == data->current_profile_setting.mclk_up_hyst) &&
> + (tmp.mclk_down_hyst == data->current_profile_setting.mclk_down_hyst) &&
> + (tmp.mclk_activity == data->current_profile_setting.mclk_activity))
> + tmp.bupdate_mclk = false;
> + else
> + tmp.bupdate_mclk = true;
> +
> + if (!smum_update_dpm_settings(hwmgr, &tmp) && ((tmp.bupdate_mclk) || tmp.bupdate_mclk))
> + memcpy(&data->current_profile_setting, &tmp, sizeof(struct profile_mode_setting));
> +
> + return 0;
> +}
> +
> static const struct pp_hwmgr_func smu7_hwmgr_funcs = {
> .backend_init = &smu7_hwmgr_backend_init,
> .backend_fini = &smu7_hwmgr_backend_fini,
> @@ -5127,6 +5323,8 @@ static int smu7_set_power_profile_mode(struct pp_hwmgr *hwmgr, long *input, uint
> .set_power_limit = smu7_set_power_limit,
> .get_power_profile_mode = smu7_get_power_profile_mode,
> .set_power_profile_mode = smu7_set_power_profile_mode,
> + .update_auto_wattman = smu7_update_auto_wattman,
> + .start_auto_wattman = smu7_start_auto_wattman,
> };
>
> uint8_t smu7_get_sleep_divider_id_from_clock(uint32_t clock,
> diff --git a/drivers/gpu/drm/amd/powerplay/hwmgr/smu7_hwmgr.h b/drivers/gpu/drm/amd/powerplay/hwmgr/smu7_hwmgr.h
> index 3bcfc61..9f61507 100644
> --- a/drivers/gpu/drm/amd/powerplay/hwmgr/smu7_hwmgr.h
> +++ b/drivers/gpu/drm/amd/powerplay/hwmgr/smu7_hwmgr.h
> @@ -186,6 +186,21 @@ struct smu7_odn_dpm_table {
> uint32_t odn_mclk_min_limit;
> };
>
> +#define AutoWattmanSCLKHighLimits 0x002D3C0A
> +#define AutoWattmanSCLKLowLimits 0x00190000
> +#define AutoWattmanMCLKHighLimits 0x002D3C0A
> +#define AutoWattmanMCLKLowLimits 0x000A1000
> +#define AutoWattmanAlgorithmMaxAdjustFactor 5
> +
> +struct smu7_auto_wattman_adjust_settings {
> + uint32_t uphyst_adjust;
> + uint32_t uphyst_polarity;
> + uint32_t downhyst_adjust;
> + uint32_t downhyst_polarity;
> + uint32_t activity_adjust;
> + uint32_t activity_polarity;
> +};
> +
> struct profile_mode_setting {
> uint8_t bupdate_sclk;
> uint8_t sclk_up_hyst;
> @@ -197,6 +212,13 @@ struct profile_mode_setting {
> uint16_t mclk_activity;
> };
>
> +struct smu7_auto_wattman_data {
> + uint32_t effective_sclk;
> + uint32_t effective_mclk;
> + uint32_t average_sclk_busy;
> + uint32_t average_mclk_busy;
> +};
> +
> struct smu7_hwmgr {
> struct smu7_dpm_table dpm_table;
> struct smu7_dpm_table golden_dpm_table;
> @@ -327,6 +349,10 @@ struct smu7_hwmgr {
> uint32_t vr_config;
> struct profile_mode_setting custom_profile_setting;
> struct profile_mode_setting current_profile_setting;
> + struct smu7_auto_wattman_data wattman_data;
> + uint32_t memory_bit_width;
> + struct smu7_auto_wattman_adjust_settings sclk_settings;
> + struct smu7_auto_wattman_adjust_settings mclk_settings;
> };
>
> /* To convert to Q8.8 format for firmware */
> diff --git a/drivers/gpu/drm/amd/powerplay/inc/hwmgr.h b/drivers/gpu/drm/amd/powerplay/inc/hwmgr.h
> index d809d96..b68ab71 100644
> --- a/drivers/gpu/drm/amd/powerplay/inc/hwmgr.h
> +++ b/drivers/gpu/drm/amd/powerplay/inc/hwmgr.h
> @@ -767,6 +767,8 @@ struct pp_hwmgr {
> bool od_enabled;
> uint32_t power_limit;
> uint32_t default_power_limit;
> + bool autowattman_enabled;
> + struct delayed_work wattman_update_work;
> };
>
> struct cgs_irq_src_funcs {
> --
> 1.9.1
>
> _______________________________________________
> amd-gfx mailing list
> amd-gfx at lists.freedesktop.org
> https://lists.freedesktop.org/mailman/listinfo/amd-gfx
More information about the amd-gfx
mailing list