[PATCH 1/1] amdgpu/pm: restructure reporting of clock values by smu

Alex Deucher alexdeucher at gmail.com
Wed Nov 17 19:14:38 UTC 2021


On Wed, Nov 17, 2021 at 2:12 AM Lazar, Lijo <lijo.lazar at amd.com> wrote:
>
>
>
> On 11/17/2021 11:50 AM, Darren Powell wrote:
> >   Use of sysfs_emit by each of the specific device implementations is problematic.
> >   To remove this back to a higher level, this patch adds a new function "get_clock_levels"
> >   to the power-management API (amd_pm_funcs) and smu powerplay API (pptable_funcs). The
> >   function returns an array of values to the power management module, which can then
> >   interpret the array and print the values at the top layer.
> >   This patch is only implemented for navi10 for some clocks to seek comment on the
> >   implementation before extending the implementation to other clock values and devices.
> >
> >   == Changes ==
> >    - new power management function get_clock_levels implemented by smu_get_ppclk_levels()
> >    - new power management function amdgpu_dpm_print_clock_array to print most common clock
> >         list, which can be later extended to use different units (currently only MHz), or
> >         other functions added to handle more complex structure (eg pcie clocks)
> >    - new powerplay function get_clk_levels implemented by navi10_get_clk_levels()
> >    - new helper function smu_convert_to_smuclk called by smu_print_ppclk_levels and
> >         smu_get_ppclk_levels
> >    - new error return value -EOPNOTSUPP for clock levels not recognized, allowing fallback
> >         to print_clock_levels. (NOTE: If pm or dpm not enabled current implementation will
> >         try and fail with both methods)
> >    - new error return value -EINVAL for unsupported clock level
> >    - new error messages output for error conditions encountered at the device specific layer
> >         which can be removed if suggested
> >
> >   == Test ==
> >   LOGFILE=sysfs_emit.test.log
> >   cd ~/workspace/linux
> >   cp ${LOGFILE}{,.old}
> >
> >   AMDGPU_PCI_ADDR=`lspci -nn | grep "VGA\|Display" | cut -d " " -f 1`
> >   AMDGPU_HWMON=`ls -la /sys/class/hwmon | grep $AMDGPU_PCI_ADDR | awk '{print $9}'`
> >   HWMON_DIR=/sys/class/hwmon/${AMDGPU_HWMON}
> >
> >   lspci -nn | grep "VGA\|Display"  > $LOGFILE
> >   FILES="pp_dpm_sclk
> >   pp_dpm_pcie
> >   "
> >
>
> There hasn't been any problem with sysfs_emit for these nodes.
>
> The problem is with this kind of usage -
>
>                  size = amdgpu_dpm_print_clock_levels(adev, OD_SCLK, buf);
>                  size += amdgpu_dpm_print_clock_levels(adev, OD_MCLK,
> buf+size);
>                  size += amdgpu_dpm_print_clock_levels(adev,
> OD_VDDC_CURVE, buf+size);
>                  size += amdgpu_dpm_print_clock_levels(adev,
> OD_VDDGFX_OFFSET, buf+size);
>                  size += amdgpu_dpm_print_clock_levels(adev, OD_RANGE,
> buf+size);
>                  size += amdgpu_dpm_print_clock_levels(adev, OD_CCLK,
> buf+size);
>
>
> It's mixing a set of data in a particular node. Then the node needs to
> pass buf and offset separately to use sysfs_emit_at
>
> Even then, if you notice, not every data is a clock level data. Some
> like VDDC_CURVE is ASIC specific and have a different format than clock
> DPM level data.
>
> The max number of DPM levels are ASIC specific. In the below
> implementation a max of 8 levels is assumed for all ASICs, but there are
> ASICs which support 16 levels for a particualr clock. In fact the
> implementation needs to pass max+1 as in arg for list_length.
>
> Sometimes, the current frequency may not match the exact DPM level, the
> approximation of what is the current level is ASIC specific. So instead
> of current freq, ASIC implementation needs to pass levels and the
> current level.
>
> In general, the problem with sysfs_emit_at is not related to a common
> print format. pp_od_clk_voltage node is a representation of the problem
> - a node which is an aggregator of different data formats. Also, it is
> difficult to narrow down to a single one across ASICs given that there
> could be changes from one to next.

Perhaps we should just switch the swsmu and powerplay code back to
sprintf for these functions. sysfs_emit doesn't really buy us anything
at this point.

Alex

>
> Thanks,
> Lijo
>
>
>
> >   for f in $FILES
> >   do
> >     echo === $f === >> $LOGFILE
> >     cat $HWMON_DIR/device/$f >> $LOGFILE
> >   done
> >   cat $LOGFILE
> >
> > Signed-off-by: Darren Powell <darren.powell at amd.com>
> > ---
> >   .../gpu/drm/amd/include/kgd_pp_interface.h    |  2 +
> >   drivers/gpu/drm/amd/pm/amdgpu_pm.c            | 41 +++++++-
> >   drivers/gpu/drm/amd/pm/inc/amdgpu_smu.h       |  9 ++
> >   drivers/gpu/drm/amd/pm/swsmu/amdgpu_smu.c     | 53 +++++++++-
> >   .../gpu/drm/amd/pm/swsmu/smu11/navi10_ppt.c   | 96 +++++++++++++++++++
> >   5 files changed, 192 insertions(+), 9 deletions(-)
> >
> > diff --git a/drivers/gpu/drm/amd/include/kgd_pp_interface.h b/drivers/gpu/drm/amd/include/kgd_pp_interface.h
> > index bac15c466733..fbe045811e60 100644
> > --- a/drivers/gpu/drm/amd/include/kgd_pp_interface.h
> > +++ b/drivers/gpu/drm/amd/include/kgd_pp_interface.h
> > @@ -310,6 +310,8 @@ struct amd_pm_funcs {
> >       int (*get_fan_speed_pwm)(void *handle, u32 *speed);
> >       int (*force_clock_level)(void *handle, enum pp_clock_type type, uint32_t mask);
> >       int (*print_clock_levels)(void *handle, enum pp_clock_type type, char *buf);
> > +     int (*get_clock_levels)(void *handle, enum pp_clock_type type,
> > +                             uint32_t *clock_list, uint max_list_length);
> >       int (*force_performance_level)(void *handle, enum amd_dpm_forced_level level);
> >       int (*get_sclk_od)(void *handle);
> >       int (*set_sclk_od)(void *handle, uint32_t value);
> > diff --git a/drivers/gpu/drm/amd/pm/amdgpu_pm.c b/drivers/gpu/drm/amd/pm/amdgpu_pm.c
> > index 41472ed99253..ca7a6779e59b 100644
> > --- a/drivers/gpu/drm/amd/pm/amdgpu_pm.c
> > +++ b/drivers/gpu/drm/amd/pm/amdgpu_pm.c
> > @@ -1020,6 +1020,23 @@ static ssize_t amdgpu_get_pp_features(struct device *dev,
> >       return size;
> >   }
> >
> > +static int amdgpu_dpm_print_clock_array(enum pp_clock_type type,
> > +                                     char *buf,
> > +                                     uint32_t *clock_list, uint list_length) {
> > +
> > +     uint i;
> > +     int size = 0;
> > +
> > +     for (i = 1; i < list_length; i++) {
> > +             size += sysfs_emit_at(buf, size, "%d: %uMhz %s\n", i, clock_list[i],
> > +                             clock_list[i] == clock_list[0] ? "*" : "");
> > +     }
> > +
> > +
> > +     return size;
> > +}
> > +
> > +
> >   /**
> >    * DOC: pp_dpm_sclk pp_dpm_mclk pp_dpm_socclk pp_dpm_fclk pp_dpm_dcefclk pp_dpm_pcie
> >    *
> > @@ -1058,6 +1075,7 @@ static ssize_t amdgpu_get_pp_dpm_clock(struct device *dev,
> >       struct amdgpu_device *adev = drm_to_adev(ddev);
> >       ssize_t size;
> >       int ret;
> > +     uint32_t clock_list[8];
> >
> >       if (amdgpu_in_reset(adev))
> >               return -EPERM;
> > @@ -1070,10 +1088,24 @@ static ssize_t amdgpu_get_pp_dpm_clock(struct device *dev,
> >               return ret;
> >       }
> >
> > -     if (adev->powerplay.pp_funcs->print_clock_levels)
> > -             size = amdgpu_dpm_print_clock_levels(adev, type, buf);
> > -     else
> > -             size = sysfs_emit(buf, "\n");
> > +     if (adev->powerplay.pp_funcs->get_clock_levels) {
> > +             ret = adev->powerplay.pp_funcs->get_clock_levels(
> > +                                     adev->powerplay.pp_handle, type, clock_list, 8);
> > +
> > +             if (ret > 0)
> > +                     size = amdgpu_dpm_print_clock_array(type, buf, clock_list, ret);
> > +     }
> > +     else {
> > +             ret = -EOPNOTSUPP;
> > +     }
> > +
> > +     if  (ret == -EOPNOTSUPP)  {
> > +             if (adev->powerplay.pp_funcs->print_clock_levels) {
> > +                     size = amdgpu_dpm_print_clock_levels(adev, type, buf);
> > +             }
> > +             else
> > +                     size = sysfs_emit(buf, "\n");
> > +     }
> >
> >       pm_runtime_mark_last_busy(ddev->dev);
> >       pm_runtime_put_autosuspend(ddev->dev);
> > @@ -1159,6 +1191,7 @@ static ssize_t amdgpu_get_pp_dpm_sclk(struct device *dev,
> >               struct device_attribute *attr,
> >               char *buf)
> >   {
> > +     dev_warn(dev, " %s ENTRY", __func__);
> >       return amdgpu_get_pp_dpm_clock(dev, PP_SCLK, buf);
> >   }
> >
> > diff --git a/drivers/gpu/drm/amd/pm/inc/amdgpu_smu.h b/drivers/gpu/drm/amd/pm/inc/amdgpu_smu.h
> > index 3557f4e7fc30..d8c20a134475 100644
> > --- a/drivers/gpu/drm/amd/pm/inc/amdgpu_smu.h
> > +++ b/drivers/gpu/drm/amd/pm/inc/amdgpu_smu.h
> > @@ -610,6 +610,15 @@ struct pptable_funcs {
> >        */
> >       int (*print_clk_levels)(struct smu_context *smu, enum smu_clk_type clk_type, char *buf);
> >
> > +     /**
> > +      * @get_clk_levels: Get array DPM clock levels for a clock domain
> > +      *                    to array clock_list. current level is first element.
> > +      *
> > +      * Used for sysfs interfaces.
> > +      */
> > +     int (*get_clk_levels)(struct smu_context *smu, enum smu_clk_type clk_type,
> > +                           uint32_t *clock_list, uint max_list_length);
> > +
> >       /**
> >        * @force_clk_levels: Set a range of allowed DPM levels for a clock
> >        *                    domain.
> > diff --git a/drivers/gpu/drm/amd/pm/swsmu/amdgpu_smu.c b/drivers/gpu/drm/amd/pm/swsmu/amdgpu_smu.c
> > index 01168b8955bf..2de566118422 100644
> > --- a/drivers/gpu/drm/amd/pm/swsmu/amdgpu_smu.c
> > +++ b/drivers/gpu/drm/amd/pm/swsmu/amdgpu_smu.c
> > @@ -2395,11 +2395,8 @@ static int smu_print_smuclk_levels(struct smu_context *smu, enum smu_clk_type cl
> >       return ret;
> >   }
> >
> > -static int smu_print_ppclk_levels(void *handle,
> > -                               enum pp_clock_type type,
> > -                               char *buf)
> > +static enum smu_clk_type smu_convert_to_smuclk(enum pp_clock_type type)
> >   {
> > -     struct smu_context *smu = handle;
> >       enum smu_clk_type clk_type;
> >
> >       switch (type) {
> > @@ -2432,12 +2429,57 @@ static int smu_print_ppclk_levels(void *handle,
> >       case OD_CCLK:
> >               clk_type = SMU_OD_CCLK; break;
> >       default:
> > -             return -EINVAL;
> > +             clk_type = SMU_CLK_COUNT; break;
> >       }
> >
> > +     return clk_type;
> > +}
> > +
> > +static int smu_print_ppclk_levels(void *handle,
> > +                               enum pp_clock_type type,
> > +                               char *buf)
> > +{
> > +     struct smu_context *smu = handle;
> > +     enum smu_clk_type clk_type;
> > +
> > +     clk_type = smu_convert_to_smuclk(type);
> > +     if (clk_type == SMU_CLK_COUNT)
> > +             return -EINVAL;
> > +
> >       return smu_print_smuclk_levels(smu, clk_type, buf);
> >   }
> >
> > +static int smu_get_ppclk_levels(void *handle,
> > +                             enum pp_clock_type type,
> > +                             uint32_t *clock_list, uint max_list_length)
> > +{
> > +     struct smu_context *smu = handle;
> > +     enum smu_clk_type clk_type;
> > +     int ret = 0;
> > +
> > +     clk_type = smu_convert_to_smuclk(type);
> > +     if (clk_type == SMU_CLK_COUNT)
> > +             return -EINVAL;
> > +
> > +
> > +     if (!smu->pm_enabled || !smu->adev->pm.dpm_enabled)
> > +             return -EOPNOTSUPP;
> > +
> > +
> > +     if (smu->ppt_funcs->get_clk_levels) {
> > +
> > +             mutex_lock(&smu->mutex);
> > +             ret = smu->ppt_funcs->get_clk_levels(smu, clk_type, clock_list, max_list_length);
> > +             mutex_unlock(&smu->mutex);
> > +     }
> > +     else {
> > +             ret = -EOPNOTSUPP;
> > +     }
> > +
> > +
> > +     return ret;
> > +}
> > +
> >   static int smu_od_edit_dpm_table(void *handle,
> >                                enum PP_OD_DPM_TABLE_COMMAND type,
> >                                long *input, uint32_t size)
> > @@ -3100,6 +3142,7 @@ static const struct amd_pm_funcs swsmu_pm_funcs = {
> >       .set_fan_speed_pwm   = smu_set_fan_speed_pwm,
> >       .get_fan_speed_pwm   = smu_get_fan_speed_pwm,
> >       .force_clock_level       = smu_force_ppclk_levels,
> > +     .get_clock_levels        = smu_get_ppclk_levels,
> >       .print_clock_levels      = smu_print_ppclk_levels,
> >       .force_performance_level = smu_force_performance_level,
> >       .read_sensor             = smu_read_sensor,
> > diff --git a/drivers/gpu/drm/amd/pm/swsmu/smu11/navi10_ppt.c b/drivers/gpu/drm/amd/pm/swsmu/smu11/navi10_ppt.c
> > index 71161f6b78fe..55e6880c2964 100644
> > --- a/drivers/gpu/drm/amd/pm/swsmu/smu11/navi10_ppt.c
> > +++ b/drivers/gpu/drm/amd/pm/swsmu/smu11/navi10_ppt.c
> > @@ -1261,6 +1261,101 @@ static void navi10_od_setting_get_range(struct smu_11_0_overdrive_table *od_tabl
> >               *max = od_table->max[setting];
> >   }
> >
> > +static int navi10_get_clk_levels(struct smu_context *smu,
> > +                              enum smu_clk_type clk_type,
> > +                              uint32_t *clock_list, uint max_list_length)
> > +{
> > +     struct amdgpu_device *adev = smu->adev;
> > +     int size = 0, ret = 0;
> > +     uint32_t cur_value = 0, value = 0, count = 0, i;
> > +     uint32_t freq_values[3] = {0};
> > +     uint32_t mark_index = 0;
> > +
> > +     switch (clk_type) {
> > +     case SMU_GFXCLK:
> > +     case SMU_SCLK:
> > +     case SMU_SOCCLK:
> > +     case SMU_MCLK:
> > +     case SMU_UCLK:
> > +     case SMU_FCLK:
> > +     case SMU_VCLK:
> > +     case SMU_DCEFCLK:
> > +     case SMU_DCLK:
> > +             ret = navi10_get_current_clk_freq_by_table(smu, clk_type, &cur_value);
> > +             if (ret) {
> > +                     dev_err(adev->dev, " %s Unable to retrieve clk freq : %u", __func__, clk_type);
> > +                     return -EIO;
> > +             }
> > +
> > +             ret = smu_v11_0_get_dpm_level_count(smu, clk_type, &count);
> > +             if (ret) {
> > +                     dev_err(adev->dev, " %s Unable to retrieve clk count : %u", __func__, clk_type);
> > +                     return -EIO;
> > +             }
> > +
> > +             if (count > max_list_length-1) {
> > +                     dev_err(adev->dev, " %s Too many clock values", __func__);
> > +                     return -EPERM;
> > +             }
> > +
> > +
> > +             clock_list[0] = cur_value;
> > +             if (!navi10_is_support_fine_grained_dpm(smu, clk_type)) {
> > +                     for (i = 0; i < count; i++) {
> > +                             ret = smu_v11_0_get_dpm_freq_by_index(smu, clk_type, i, &value);
> > +                             if (ret) {
> > +                                     dev_err(adev->dev, " %s Unable to retrieve clk freq : %u", __func__, clk_type);
> > +                                     return -EIO;
> > +                             }
> > +
> > +                             clock_list[i+1] = value;
> > +                     }
> > +                     size = count+1;
> > +             } else {
> > +                     // for fine-grained dpm, display cur_value as average of max and min
> > +                     ret = smu_v11_0_get_dpm_freq_by_index(smu, clk_type, 0, &freq_values[0]);
> > +                     if (ret) {
> > +                             dev_err(adev->dev, " %s Unable to retrieve clk freq : %u", __func__, clk_type);
> > +                             return -EIO;
> > +                     }
> > +                     ret = smu_v11_0_get_dpm_freq_by_index(smu, clk_type, count - 1, &freq_values[2]);
> > +                     if (ret) {
> > +                             dev_err(adev->dev, " %s Unable to retrieve clk freq : %u", __func__, clk_type);
> > +                             return -EIO;
> > +                     }
> > +
> > +                     freq_values[1] = cur_value;
> > +                     mark_index = cur_value == freq_values[0] ? 0 :
> > +                                  cur_value == freq_values[2] ? 2 : 1;
> > +                     if (mark_index != 1)
> > +                             freq_values[1] = (freq_values[0] + freq_values[2]) / 2;
> > +
> > +                     clock_list[0] = freq_values[1];
> > +                     for (i = 0; i < 3; i++) {
> > +                             clock_list[i+1] = freq_values[i];
> > +                     }
> > +
> > +                     size = 3+1;
> > +             }
> > +             break;
> > +     case SMU_PCIE:
> > +     case SMU_OD_SCLK:
> > +     case SMU_OD_MCLK:
> > +     case SMU_OD_VDDC_CURVE:
> > +     case SMU_OD_RANGE:
> > +             dev_warn(adev->dev, " %s Unhandled clk_type : %u", __func__, clk_type);
> > +             size = -EOPNOTSUPP;
> > +             break;
> > +
> > +     default:
> > +             dev_err(adev->dev, " %s Unknown clk_type : %u", __func__, clk_type);
> > +             size = -EINVAL;
> > +             break;
> > +     }
> > +
> > +     return size;
> > +}
> > +
> >   static int navi10_print_clk_levels(struct smu_context *smu,
> >                       enum smu_clk_type clk_type, char *buf)
> >   {
> > @@ -3241,6 +3336,7 @@ static const struct pptable_funcs navi10_ppt_funcs = {
> >       .dpm_set_jpeg_enable = navi10_dpm_set_jpeg_enable,
> >       .i2c_init = navi10_i2c_control_init,
> >       .i2c_fini = navi10_i2c_control_fini,
> > +     .get_clk_levels = navi10_get_clk_levels,
> >       .print_clk_levels = navi10_print_clk_levels,
> >       .force_clk_levels = navi10_force_clk_levels,
> >       .populate_umd_state_clk = navi10_populate_umd_state_clk,
> >
> > base-commit: eabeb4f20a0786188fba07a2dd1b0a614c4e15f6
> >


More information about the amd-gfx mailing list