Support for getting power1_cap_min value on smu13 and smu11.
For other Asics, we still use 0 as the default value.
Signed-off-by: Ma Jun <Jun.Ma2@amd.com>
Reviewed-by: Kenneth Feng <kenneth.feng@amd.com>
Signed-off-by: Alex Deucher <alexander.deucher@amd.com>
        return sysfs_emit(buf, "%zd\n", val);
 }
 
-static ssize_t amdgpu_hwmon_show_power_cap_min(struct device *dev,
-                                        struct device_attribute *attr,
-                                        char *buf)
-{
-       return sysfs_emit(buf, "%i\n", 0);
-}
-
-
 static ssize_t amdgpu_hwmon_show_power_cap_generic(struct device *dev,
                                        struct device_attribute *attr,
                                        char *buf,
        return size;
 }
 
+static ssize_t amdgpu_hwmon_show_power_cap_min(struct device *dev,
+                                        struct device_attribute *attr,
+                                        char *buf)
+{
+       return amdgpu_hwmon_show_power_cap_generic(dev, attr, buf, PP_PWR_LIMIT_MIN);
+}
 
 static ssize_t amdgpu_hwmon_show_power_cap_max(struct device *dev,
                                         struct device_attribute *attr,
 
        ret = smu_get_asic_power_limits(smu,
                                        &smu->current_power_limit,
                                        &smu->default_power_limit,
-                                       &smu->max_power_limit);
+                                       &smu->max_power_limit,
+                                       &smu->min_power_limit);
        if (ret) {
                dev_err(adev->dev, "Failed to get asic power limits!\n");
                return ret;
                limit_level = SMU_PPT_LIMIT_MAX;
                break;
        case PP_PWR_LIMIT_MIN:
+               limit_level = SMU_PPT_LIMIT_MIN;
+               break;
        default:
                return -EOPNOTSUPP;
                break;
                        case IP_VERSION(11, 0, 13):
                                ret = smu_get_asic_power_limits(smu,
                                                                &smu->current_power_limit,
-                                                               NULL,
-                                                               NULL);
+                                                               NULL, NULL, NULL);
                                break;
                        default:
                                break;
                case SMU_PPT_LIMIT_MAX:
                        *limit = smu->max_power_limit;
                        break;
+               case SMU_PPT_LIMIT_MIN:
+                       *limit = smu->min_power_limit;
+                       break;
                default:
                        break;
                }
                if (smu->ppt_funcs->set_power_limit)
                        return smu->ppt_funcs->set_power_limit(smu, limit_type, limit);
 
-       if (limit > smu->max_power_limit) {
+       if ((limit > smu->max_power_limit) || (limit < smu->min_power_limit)) {
                dev_err(smu->adev->dev,
-                       "New power limit (%d) is over the max allowed %d\n",
-                       limit, smu->max_power_limit);
+                       "New power limit (%d) is out of range [%d,%d]\n",
+                       limit, smu->min_power_limit, smu->max_power_limit);
                return -EINVAL;
        }
 
 
        uint32_t current_power_limit;
        uint32_t default_power_limit;
        uint32_t max_power_limit;
+       uint32_t min_power_limit;
 
        /* soft pptable */
        uint32_t ppt_offset_bytes;
         * @get_power_limit: Get the device's power limits.
         */
        int (*get_power_limit)(struct smu_context *smu,
-                              uint32_t *current_power_limit,
-                              uint32_t *default_power_limit,
-                              uint32_t *max_power_limit);
+                                       uint32_t *current_power_limit,
+                                       uint32_t *default_power_limit,
+                                       uint32_t *max_power_limit,
+                                       uint32_t *min_power_limit);
 
        /**
         * @get_ppt_limit: Get the device's ppt limits.
 
 }
 
 static int arcturus_get_power_limit(struct smu_context *smu,
-                                   uint32_t *current_power_limit,
-                                   uint32_t *default_power_limit,
-                                   uint32_t *max_power_limit)
+                                       uint32_t *current_power_limit,
+                                       uint32_t *default_power_limit,
+                                       uint32_t *max_power_limit,
+                                       uint32_t *min_power_limit)
 {
        struct smu_11_0_powerplay_table *powerplay_table =
                (struct smu_11_0_powerplay_table *)smu->smu_table.power_play_table;
        PPTable_t *pptable = smu->smu_table.driver_pptable;
-       uint32_t power_limit, od_percent;
+       uint32_t power_limit, od_percent_upper, od_percent_lower;
 
        if (smu_v11_0_get_current_power_limit(smu, &power_limit)) {
                /* the last hope to figure out the ppt limit */
        if (default_power_limit)
                *default_power_limit = power_limit;
 
-       if (max_power_limit) {
-               if (smu->od_enabled) {
-                       od_percent = le32_to_cpu(powerplay_table->overdrive_table.max[SMU_11_0_ODSETTING_POWERPERCENTAGE]);
+       if (smu->od_enabled) {
+               od_percent_upper = le32_to_cpu(powerplay_table->overdrive_table.max[SMU_11_0_ODSETTING_POWERPERCENTAGE]);
+               od_percent_lower = le32_to_cpu(powerplay_table->overdrive_table.min[SMU_11_0_ODSETTING_POWERPERCENTAGE]);
+       } else {
+               od_percent_upper = 0;
+               od_percent_lower = 100;
+       }
 
-                       dev_dbg(smu->adev->dev, "ODSETTING_POWERPERCENTAGE: %d (default: %d)\n", od_percent, power_limit);
+       dev_dbg(smu->adev->dev, "od percent upper:%d, od percent lower:%d (default power: %d)\n",
+                                                       od_percent_upper, od_percent_lower, power_limit);
 
-                       power_limit *= (100 + od_percent);
-                       power_limit /= 100;
-               }
+       if (max_power_limit) {
+               *max_power_limit = power_limit * (100 + od_percent_upper);
+               *max_power_limit /= 100;
+       }
 
-               *max_power_limit = power_limit;
+       if (min_power_limit) {
+               *min_power_limit = power_limit * (100 - od_percent_lower);
+               *min_power_limit /= 100;
        }
 
        return 0;
 
 }
 
 static int navi10_get_power_limit(struct smu_context *smu,
-                                 uint32_t *current_power_limit,
-                                 uint32_t *default_power_limit,
-                                 uint32_t *max_power_limit)
+                                       uint32_t *current_power_limit,
+                                       uint32_t *default_power_limit,
+                                       uint32_t *max_power_limit,
+                                       uint32_t *min_power_limit)
 {
        struct smu_11_0_powerplay_table *powerplay_table =
                (struct smu_11_0_powerplay_table *)smu->smu_table.power_play_table;
        struct smu_11_0_overdrive_table *od_settings = smu->od_settings;
        PPTable_t *pptable = smu->smu_table.driver_pptable;
-       uint32_t power_limit, od_percent;
+       uint32_t power_limit, od_percent_upper, od_percent_lower;
 
        if (smu_v11_0_get_current_power_limit(smu, &power_limit)) {
                /* the last hope to figure out the ppt limit */
        if (default_power_limit)
                *default_power_limit = power_limit;
 
-       if (max_power_limit) {
-               if (smu->od_enabled &&
+       if (smu->od_enabled &&
                    navi10_od_feature_is_supported(od_settings, SMU_11_0_ODCAP_POWER_LIMIT)) {
-                       od_percent = le32_to_cpu(powerplay_table->overdrive_table.max[SMU_11_0_ODSETTING_POWERPERCENTAGE]);
+               od_percent_upper = le32_to_cpu(powerplay_table->overdrive_table.max[SMU_11_0_ODSETTING_POWERPERCENTAGE]);
+               od_percent_lower = le32_to_cpu(powerplay_table->overdrive_table.min[SMU_11_0_ODSETTING_POWERPERCENTAGE]);
+       } else {
+               od_percent_upper = 0;
+               od_percent_lower = 100;
+       }
 
-                       dev_dbg(smu->adev->dev, "ODSETTING_POWERPERCENTAGE: %d (default: %d)\n", od_percent, power_limit);
+       dev_dbg(smu->adev->dev, "od percent upper:%d, od percent lower:%d (default power: %d)\n",
+                                       od_percent_upper, od_percent_lower, power_limit);
 
-                       power_limit *= (100 + od_percent);
-                       power_limit /= 100;
-               }
+       if (max_power_limit) {
+               *max_power_limit = power_limit * (100 + od_percent_upper);
+               *max_power_limit /= 100;
+       }
 
-               *max_power_limit = power_limit;
+       if (min_power_limit) {
+               *min_power_limit = power_limit * (100 - od_percent_lower);
+               *min_power_limit /= 100;
        }
 
        return 0;
 
 static int sienna_cichlid_get_power_limit(struct smu_context *smu,
                                          uint32_t *current_power_limit,
                                          uint32_t *default_power_limit,
-                                         uint32_t *max_power_limit)
+                                         uint32_t *max_power_limit,
+                                         uint32_t *min_power_limit)
 {
        struct smu_11_0_7_powerplay_table *powerplay_table =
                (struct smu_11_0_7_powerplay_table *)smu->smu_table.power_play_table;
-       uint32_t power_limit, od_percent;
+       uint32_t power_limit, od_percent_upper, od_percent_lower;
        uint16_t *table_member;
 
        GET_PPTABLE_MEMBER(SocketPowerLimitAc, &table_member);
        if (default_power_limit)
                *default_power_limit = power_limit;
 
-       if (max_power_limit) {
-               if (smu->od_enabled) {
-                       od_percent =
-                               le32_to_cpu(powerplay_table->overdrive_table.max[
-                                                       SMU_11_0_7_ODSETTING_POWERPERCENTAGE]);
+       if (smu->od_enabled) {
+               od_percent_upper = le32_to_cpu(powerplay_table->overdrive_table.max[SMU_11_0_7_ODSETTING_POWERPERCENTAGE]);
+               od_percent_lower = le32_to_cpu(powerplay_table->overdrive_table.min[SMU_11_0_7_ODSETTING_POWERPERCENTAGE]);
+       } else {
+               od_percent_upper = 0;
+               od_percent_lower = 100;
+       }
 
-                       dev_dbg(smu->adev->dev, "ODSETTING_POWERPERCENTAGE: %d (default: %d)\n",
-                                       od_percent, power_limit);
+       dev_dbg(smu->adev->dev, "od percent upper:%d, od percent lower:%d (default power: %d)\n",
+                                       od_percent_upper, od_percent_lower, power_limit);
 
-                       power_limit *= (100 + od_percent);
-                       power_limit /= 100;
-               }
-               *max_power_limit = power_limit;
+       if (max_power_limit) {
+               *max_power_limit = power_limit * (100 + od_percent_upper);
+               *max_power_limit /= 100;
        }
 
+       if (min_power_limit) {
+               *min_power_limit = power_limit * (100 - od_percent_lower);
+               *min_power_limit /= 100;
+       }
        return 0;
 }
 
        uint32_t cur_power_limit;
 
        if (metrics_v4->ApuSTAPMSmartShiftLimit != 0) {
-               sienna_cichlid_get_power_limit(smu, &cur_power_limit, NULL, NULL);
+               sienna_cichlid_get_power_limit(smu, &cur_power_limit, NULL, NULL, NULL);
                apu_power_limit = metrics_v4->ApuSTAPMLimit;
                dgpu_power_limit = cur_power_limit;
                powerRatio = (((apu_power_limit +
 
 static int vangogh_get_power_limit(struct smu_context *smu,
                                   uint32_t *current_power_limit,
                                   uint32_t *default_power_limit,
-                                  uint32_t *max_power_limit)
+                                  uint32_t *max_power_limit,
+                                  uint32_t *min_power_limit)
 {
        struct smu_11_5_power_context *power_context =
                                                                smu->smu_power.power_context;
                *default_power_limit = ppt_limit / 1000;
        if (max_power_limit)
                *max_power_limit = 29;
+       if (min_power_limit)
+               *min_power_limit = 0;
 
        ret = smu_cmn_send_smc_msg(smu, SMU_MSG_GetFastPPTLimit, &ppt_limit);
        if (ret) {
 
 }
 
 static int aldebaran_get_power_limit(struct smu_context *smu,
-                                    uint32_t *current_power_limit,
-                                    uint32_t *default_power_limit,
-                                    uint32_t *max_power_limit)
+                                               uint32_t *current_power_limit,
+                                               uint32_t *default_power_limit,
+                                               uint32_t *max_power_limit,
+                                               uint32_t *min_power_limit)
 {
        PPTable_t *pptable = smu->smu_table.driver_pptable;
        uint32_t power_limit = 0;
                        *default_power_limit = 0;
                if (max_power_limit)
                        *max_power_limit = 0;
-
+               if (min_power_limit)
+                       *min_power_limit = 0;
                dev_warn(smu->adev->dev,
                        "PPT feature is not enabled, power values can't be fetched.");
 
                        *max_power_limit = pptable->PptLimit;
        }
 
+       if (min_power_limit)
+               *min_power_limit = 0;
+
        return 0;
 }
 
 
 }
 
 static int smu_v13_0_0_get_power_limit(struct smu_context *smu,
-                                      uint32_t *current_power_limit,
-                                      uint32_t *default_power_limit,
-                                      uint32_t *max_power_limit)
+                                               uint32_t *current_power_limit,
+                                               uint32_t *default_power_limit,
+                                               uint32_t *max_power_limit,
+                                               uint32_t *min_power_limit)
 {
        struct smu_table_context *table_context = &smu->smu_table;
        struct smu_13_0_0_powerplay_table *powerplay_table =
                (struct smu_13_0_0_powerplay_table *)table_context->power_play_table;
        PPTable_t *pptable = table_context->driver_pptable;
        SkuTable_t *skutable = &pptable->SkuTable;
-       uint32_t power_limit, od_percent;
+       uint32_t power_limit, od_percent_upper, od_percent_lower;
 
        if (smu_v13_0_get_current_power_limit(smu, &power_limit))
                power_limit = smu->adev->pm.ac_power ?
        if (default_power_limit)
                *default_power_limit = power_limit;
 
-       if (max_power_limit) {
-               if (smu->od_enabled) {
-                       od_percent = le32_to_cpu(powerplay_table->overdrive_table.max[SMU_13_0_0_ODSETTING_POWERPERCENTAGE]);
+       if (smu->od_enabled) {
+               od_percent_upper = le32_to_cpu(powerplay_table->overdrive_table.max[SMU_13_0_0_ODSETTING_POWERPERCENTAGE]);
+               od_percent_lower = le32_to_cpu(powerplay_table->overdrive_table.min[SMU_13_0_0_ODSETTING_POWERPERCENTAGE]);
+       } else {
+               od_percent_upper = 0;
+               od_percent_lower = 100;
+       }
 
-                       dev_dbg(smu->adev->dev, "ODSETTING_POWERPERCENTAGE: %d (default: %d)\n", od_percent, power_limit);
+       dev_dbg(smu->adev->dev, "od percent upper:%d, od percent lower:%d (default power: %d)\n",
+                                       od_percent_upper, od_percent_lower, power_limit);
 
-                       power_limit *= (100 + od_percent);
-                       power_limit /= 100;
-               }
-               *max_power_limit = power_limit;
+       if (max_power_limit) {
+               *max_power_limit = power_limit * (100 + od_percent_upper);
+               *max_power_limit /= 100;
+       }
+
+       if (min_power_limit) {
+               *min_power_limit = power_limit * (100 - od_percent_lower);
+               *min_power_limit /= 100;
        }
 
        return 0;
 
 }
 
 static int smu_v13_0_6_get_power_limit(struct smu_context *smu,
-                                      uint32_t *current_power_limit,
-                                      uint32_t *default_power_limit,
-                                      uint32_t *max_power_limit)
+                                               uint32_t *current_power_limit,
+                                               uint32_t *default_power_limit,
+                                               uint32_t *max_power_limit,
+                                               uint32_t *min_power_limit)
 {
        struct smu_table_context *smu_table = &smu->smu_table;
        struct PPTable_t *pptable =
                *max_power_limit = pptable->MaxSocketPowerLimit;
        }
 
+       if (min_power_limit)
+               *min_power_limit = 0;
        return 0;
 }
 
 
 }
 
 static int smu_v13_0_7_get_power_limit(struct smu_context *smu,
-                                      uint32_t *current_power_limit,
-                                      uint32_t *default_power_limit,
-                                      uint32_t *max_power_limit)
+                                               uint32_t *current_power_limit,
+                                               uint32_t *default_power_limit,
+                                               uint32_t *max_power_limit,
+                                               uint32_t *min_power_limit)
 {
        struct smu_table_context *table_context = &smu->smu_table;
        struct smu_13_0_7_powerplay_table *powerplay_table =
                (struct smu_13_0_7_powerplay_table *)table_context->power_play_table;
        PPTable_t *pptable = table_context->driver_pptable;
        SkuTable_t *skutable = &pptable->SkuTable;
-       uint32_t power_limit, od_percent;
+       uint32_t power_limit, od_percent_upper, od_percent_lower;
 
        if (smu_v13_0_get_current_power_limit(smu, &power_limit))
                power_limit = smu->adev->pm.ac_power ?
        if (default_power_limit)
                *default_power_limit = power_limit;
 
-       if (max_power_limit) {
-               if (smu->od_enabled) {
-                       od_percent = le32_to_cpu(powerplay_table->overdrive_table.max[SMU_13_0_7_ODSETTING_POWERPERCENTAGE]);
+       if (smu->od_enabled) {
+               od_percent_upper = le32_to_cpu(powerplay_table->overdrive_table.max[SMU_13_0_7_ODSETTING_POWERPERCENTAGE]);
+               od_percent_lower = le32_to_cpu(powerplay_table->overdrive_table.min[SMU_13_0_7_ODSETTING_POWERPERCENTAGE]);
+       } else {
+               od_percent_upper = 0;
+               od_percent_lower = 100;
+       }
 
-                       dev_dbg(smu->adev->dev, "ODSETTING_POWERPERCENTAGE: %d (default: %d)\n", od_percent, power_limit);
+       dev_dbg(smu->adev->dev, "od percent upper:%d, od percent lower:%d (default power: %d)\n",
+                                       od_percent_upper, od_percent_lower, power_limit);
 
-                       power_limit *= (100 + od_percent);
-                       power_limit /= 100;
-               }
-               *max_power_limit = power_limit;
+       if (max_power_limit) {
+               *max_power_limit = power_limit * (100 + od_percent_upper);
+               *max_power_limit /= 100;
+       }
+
+       if (min_power_limit) {
+               *min_power_limit = power_limit * (100 - od_percent_lower);
+               *min_power_limit /= 100;
        }
 
        return 0;
 
 #define smu_i2c_fini(smu)                                               smu_ppt_funcs(i2c_fini, 0, smu)
 #define smu_get_unique_id(smu)                                         smu_ppt_funcs(get_unique_id, 0, smu)
 #define smu_log_thermal_throttling(smu)                                        smu_ppt_funcs(log_thermal_throttling_event, 0, smu)
-#define smu_get_asic_power_limits(smu, current, default, max)          smu_ppt_funcs(get_power_limit, 0, smu, current, default, max)
+#define smu_get_asic_power_limits(smu, current, default, max, min)             smu_ppt_funcs(get_power_limit, 0, smu, current, default, max, min)
 #define smu_get_pp_feature_mask(smu, buf)                              smu_ppt_funcs(get_pp_feature_mask, 0, smu, buf)
 #define smu_set_pp_feature_mask(smu, new_mask)                         smu_ppt_funcs(set_pp_feature_mask, 0, smu, new_mask)
 #define smu_gfx_ulv_control(smu, enablement)                           smu_ppt_funcs(gfx_ulv_control, 0, smu, enablement)