drm/amdgpu: attr to control SS2.0 bias level (v2)
[linux-2.6-microblaze.git] / drivers / gpu / drm / amd / pm / amdgpu_pm.c
index 9a54066..2e1286f 100644 (file)
@@ -735,6 +735,23 @@ static ssize_t amdgpu_set_pp_table(struct device *dev,
  * - a list of valid ranges for sclk, mclk, and voltage curve points
  *   labeled OD_RANGE
  *
+ * < For APUs >
+ *
+ * Reading the file will display:
+ *
+ * - minimum and maximum engine clock labeled OD_SCLK
+ *
+ * - a list of valid ranges for sclk labeled OD_RANGE
+ *
+ * < For VanGogh >
+ *
+ * Reading the file will display:
+ *
+ * - minimum and maximum engine clock labeled OD_SCLK
+ * - minimum and maximum core clocks labeled OD_CCLK
+ *
+ * - a list of valid ranges for sclk and cclk labeled OD_RANGE
+ *
  * To manually adjust these settings:
  *
  * - First select manual using power_dpm_force_performance_level
@@ -743,7 +760,10 @@ static ssize_t amdgpu_set_pp_table(struct device *dev,
  *   string that contains "s/m index clock" to the file. The index
  *   should be 0 if to set minimum clock. And 1 if to set maximum
  *   clock. E.g., "s 0 500" will update minimum sclk to be 500 MHz.
- *   "m 1 800" will update maximum mclk to be 800Mhz.
+ *   "m 1 800" will update maximum mclk to be 800Mhz. For core
+ *   clocks on VanGogh, the string contains "p core index clock".
+ *   E.g., "p 2 0 800" would set the minimum core clock on core
+ *   2 to 800Mhz.
  *
  *   For sclk voltage curve, enter the new values by writing a
  *   string that contains "vc point clock voltage" to the file. The
@@ -1797,6 +1817,190 @@ out:
        return size;
 }
 
+/**
+ * DOC: smartshift_apu_power
+ *
+ * The amdgpu driver provides a sysfs API for reporting APU power
+ * share if it supports smartshift. The value is expressed as
+ * the proportion of stapm limit where stapm limit is the total APU
+ * power limit. The result is in percentage. If APU power is 130% of
+ * STAPM, then APU is using 30% of the dGPU's headroom.
+ */
+
+static ssize_t amdgpu_get_smartshift_apu_power(struct device *dev, struct device_attribute *attr,
+                                              char *buf)
+{
+       struct drm_device *ddev = dev_get_drvdata(dev);
+       struct amdgpu_device *adev = drm_to_adev(ddev);
+       uint32_t ss_power, size;
+       int r = 0;
+
+       if (amdgpu_in_reset(adev))
+               return -EPERM;
+       if (adev->in_suspend && !adev->in_runpm)
+               return -EPERM;
+
+       r = pm_runtime_get_sync(ddev->dev);
+       if (r < 0) {
+               pm_runtime_put_autosuspend(ddev->dev);
+               return r;
+       }
+
+       r = amdgpu_dpm_read_sensor(adev, AMDGPU_PP_SENSOR_SS_APU_SHARE,
+                                  (void *)&ss_power, &size);
+       if (r)
+               goto out;
+
+       r = sysfs_emit(buf, "%u%%\n", ss_power);
+
+out:
+       pm_runtime_mark_last_busy(ddev->dev);
+       pm_runtime_put_autosuspend(ddev->dev);
+       return r;
+}
+
+/**
+ * DOC: smartshift_dgpu_power
+ *
+ * The amdgpu driver provides a sysfs API for reporting the dGPU power
+ * share if the device is in HG and supports smartshift. The value
+ * is expressed as the proportion of stapm limit where stapm limit
+ * is the total APU power limit. The value is in percentage. If dGPU
+ * power is 20% higher than STAPM power(120%), it's using 20% of the
+ * APU's power headroom.
+ */
+
+static ssize_t amdgpu_get_smartshift_dgpu_power(struct device *dev, struct device_attribute *attr,
+                                               char *buf)
+{
+       struct drm_device *ddev = dev_get_drvdata(dev);
+       struct amdgpu_device *adev = drm_to_adev(ddev);
+       uint32_t ss_power, size;
+       int r = 0;
+
+       if (amdgpu_in_reset(adev))
+               return -EPERM;
+       if (adev->in_suspend && !adev->in_runpm)
+               return -EPERM;
+
+       r = pm_runtime_get_sync(ddev->dev);
+       if (r < 0) {
+               pm_runtime_put_autosuspend(ddev->dev);
+               return r;
+       }
+
+       r = amdgpu_dpm_read_sensor(adev, AMDGPU_PP_SENSOR_SS_DGPU_SHARE,
+                                  (void *)&ss_power, &size);
+
+       if (r)
+               goto out;
+
+       r = sysfs_emit(buf, "%u%%\n", ss_power);
+
+out:
+       pm_runtime_mark_last_busy(ddev->dev);
+       pm_runtime_put_autosuspend(ddev->dev);
+       return r;
+}
+
+/**
+ * DOC: smartshift_bias
+ *
+ * The amdgpu driver provides a sysfs API for reporting the
+ * smartshift(SS2.0) bias level. The value ranges from -100 to 100
+ * and the default is 0. -100 sets maximum preference to APU
+ * and 100 sets max perference to dGPU.
+ */
+
+static ssize_t amdgpu_get_smartshift_bias(struct device *dev,
+                                         struct device_attribute *attr,
+                                         char *buf)
+{
+       int r = 0;
+
+       r = sysfs_emit(buf, "%d\n", amdgpu_smartshift_bias);
+
+       return r;
+}
+
+static ssize_t amdgpu_set_smartshift_bias(struct device *dev,
+                                         struct device_attribute *attr,
+                                         const char *buf, size_t count)
+{
+       struct drm_device *ddev = dev_get_drvdata(dev);
+       struct amdgpu_device *adev = drm_to_adev(ddev);
+       int r = 0;
+       int bias = 0;
+
+       if (amdgpu_in_reset(adev))
+               return -EPERM;
+       if (adev->in_suspend && !adev->in_runpm)
+               return -EPERM;
+
+       r = pm_runtime_get_sync(ddev->dev);
+       if (r < 0) {
+               pm_runtime_put_autosuspend(ddev->dev);
+               return r;
+       }
+
+       r = kstrtoint(buf, 10, &bias);
+       if (r)
+               goto out;
+
+       if (bias > AMDGPU_SMARTSHIFT_MAX_BIAS)
+               bias = AMDGPU_SMARTSHIFT_MAX_BIAS;
+       else if (bias < AMDGPU_SMARTSHIFT_MIN_BIAS)
+               bias = AMDGPU_SMARTSHIFT_MIN_BIAS;
+
+       amdgpu_smartshift_bias = bias;
+       r = count;
+
+       /* TODO: upadte bias level with SMU message */
+
+out:
+       pm_runtime_mark_last_busy(ddev->dev);
+       pm_runtime_put_autosuspend(ddev->dev);
+       return r;
+}
+
+
+static int ss_power_attr_update(struct amdgpu_device *adev, struct amdgpu_device_attr *attr,
+                               uint32_t mask, enum amdgpu_device_attr_states *states)
+{
+       uint32_t ss_power, size;
+
+       if (!amdgpu_acpi_is_power_shift_control_supported())
+               *states = ATTR_STATE_UNSUPPORTED;
+       else if ((adev->flags & AMD_IS_PX) &&
+                !amdgpu_device_supports_smart_shift(adev_to_drm(adev)))
+               *states = ATTR_STATE_UNSUPPORTED;
+       else if (amdgpu_dpm_read_sensor(adev, AMDGPU_PP_SENSOR_SS_APU_SHARE,
+                (void *)&ss_power, &size))
+               *states = ATTR_STATE_UNSUPPORTED;
+       else if (amdgpu_dpm_read_sensor(adev, AMDGPU_PP_SENSOR_SS_DGPU_SHARE,
+                (void *)&ss_power, &size))
+               *states = ATTR_STATE_UNSUPPORTED;
+
+       return 0;
+}
+
+static int ss_bias_attr_update(struct amdgpu_device *adev, struct amdgpu_device_attr *attr,
+                              uint32_t mask, enum amdgpu_device_attr_states *states)
+{
+       uint32_t ss_power, size;
+
+       if (!amdgpu_device_supports_smart_shift(adev_to_drm(adev)))
+               *states = ATTR_STATE_UNSUPPORTED;
+       else if (amdgpu_dpm_read_sensor(adev, AMDGPU_PP_SENSOR_SS_APU_SHARE,
+                (void *)&ss_power, &size))
+               *states = ATTR_STATE_UNSUPPORTED;
+       else if (amdgpu_dpm_read_sensor(adev, AMDGPU_PP_SENSOR_SS_DGPU_SHARE,
+                (void *)&ss_power, &size))
+               *states = ATTR_STATE_UNSUPPORTED;
+
+       return 0;
+}
+
 static struct amdgpu_device_attr amdgpu_device_attrs[] = {
        AMDGPU_DEVICE_ATTR_RW(power_dpm_state,                          ATTR_FLAG_BASIC|ATTR_FLAG_ONEVF),
        AMDGPU_DEVICE_ATTR_RW(power_dpm_force_performance_level,        ATTR_FLAG_BASIC|ATTR_FLAG_ONEVF),
@@ -1823,6 +2027,12 @@ static struct amdgpu_device_attr amdgpu_device_attrs[] = {
        AMDGPU_DEVICE_ATTR_RO(unique_id,                                ATTR_FLAG_BASIC),
        AMDGPU_DEVICE_ATTR_RW(thermal_throttling_logging,               ATTR_FLAG_BASIC),
        AMDGPU_DEVICE_ATTR_RO(gpu_metrics,                              ATTR_FLAG_BASIC),
+       AMDGPU_DEVICE_ATTR_RO(smartshift_apu_power,                     ATTR_FLAG_BASIC,
+                             .attr_update = ss_power_attr_update),
+       AMDGPU_DEVICE_ATTR_RO(smartshift_dgpu_power,                    ATTR_FLAG_BASIC,
+                             .attr_update = ss_power_attr_update),
+       AMDGPU_DEVICE_ATTR_RW(smartshift_bias,                          ATTR_FLAG_BASIC,
+                             .attr_update = ss_bias_attr_update),
 };
 
 static int default_attr_update(struct amdgpu_device *adev, struct amdgpu_device_attr *attr,
@@ -1922,7 +2132,7 @@ static int amdgpu_device_attr_create(struct amdgpu_device *adev,
 
        BUG_ON(!attr);
 
-       attr_update = attr->attr_update ? attr_update : default_attr_update;
+       attr_update = attr->attr_update ? attr->attr_update : default_attr_update;
 
        ret = attr_update(adev, attr, mask, &attr_states);
        if (ret) {
@@ -3534,6 +3744,45 @@ out:
 
 DEFINE_SHOW_ATTRIBUTE(amdgpu_debugfs_pm_info);
 
+/*
+ * amdgpu_pm_priv_buffer_read - Read memory region allocated to FW
+ *
+ * Reads debug memory region allocated to PMFW
+ */
+static ssize_t amdgpu_pm_prv_buffer_read(struct file *f, char __user *buf,
+                                        size_t size, loff_t *pos)
+{
+       struct amdgpu_device *adev = file_inode(f)->i_private;
+       const struct amd_pm_funcs *pp_funcs = adev->powerplay.pp_funcs;
+       void *pp_handle = adev->powerplay.pp_handle;
+       size_t smu_prv_buf_size;
+       void *smu_prv_buf;
+
+       if (amdgpu_in_reset(adev))
+               return -EPERM;
+       if (adev->in_suspend && !adev->in_runpm)
+               return -EPERM;
+
+       if (pp_funcs && pp_funcs->get_smu_prv_buf_details)
+               pp_funcs->get_smu_prv_buf_details(pp_handle, &smu_prv_buf,
+                                                 &smu_prv_buf_size);
+       else
+               return -ENOSYS;
+
+       if (!smu_prv_buf || !smu_prv_buf_size)
+               return -EINVAL;
+
+       return simple_read_from_buffer(buf, size, pos, smu_prv_buf,
+                                      smu_prv_buf_size);
+}
+
+static const struct file_operations amdgpu_debugfs_pm_prv_buffer_fops = {
+       .owner = THIS_MODULE,
+       .open = simple_open,
+       .read = amdgpu_pm_prv_buffer_read,
+       .llseek = default_llseek,
+};
+
 #endif
 
 void amdgpu_debugfs_pm_init(struct amdgpu_device *adev)
@@ -3545,5 +3794,10 @@ void amdgpu_debugfs_pm_init(struct amdgpu_device *adev)
        debugfs_create_file("amdgpu_pm_info", 0444, root, adev,
                            &amdgpu_debugfs_pm_info_fops);
 
+       if (adev->pm.smu_prv_buffer_size > 0)
+               debugfs_create_file_size("amdgpu_pm_prv_buffer", 0444, root,
+                                        adev,
+                                        &amdgpu_debugfs_pm_prv_buffer_fops,
+                                        adev->pm.smu_prv_buffer_size);
 #endif
 }