drm/radeon: implement apci perf request
authorAlex Deucher <alexander.deucher@amd.com>
Wed, 13 Feb 2013 20:47:24 +0000 (15:47 -0500)
committerAlex Deucher <alexander.deucher@amd.com>
Thu, 27 Jun 2013 23:16:23 +0000 (19:16 -0400)
These functions use acpi methods to adjust the pcie
gen speed.  Used by DPM.

Signed-off-by: Alex Deucher <alexander.deucher@amd.com>
drivers/gpu/drm/radeon/radeon.h
drivers/gpu/drm/radeon/radeon_acpi.c

index d41c3843d3db4ece871d051c237bb297de3a4a76..f57e36d6fb4aa7358a114ae60a07b220db5a749c 100644 (file)
@@ -2458,7 +2458,7 @@ extern int radeon_acpi_init(struct radeon_device *rdev);
 extern void radeon_acpi_fini(struct radeon_device *rdev);
 extern bool radeon_acpi_is_pcie_performance_request_supported(struct radeon_device *rdev);
 extern int radeon_acpi_pcie_performance_request(struct radeon_device *rdev,
-                                               u8 ref_req, bool advertise);
+                                               u8 perf_req, bool advertise);
 extern int radeon_acpi_pcie_notify_device_ready(struct radeon_device *rdev);
 #else
 static inline int radeon_acpi_init(struct radeon_device *rdev) { return 0; }
index 87419a4c25acc77fae69206511be4228dd164a65..10f98c7742d8c87289e8bbf677857c781fcebbee 100644 (file)
@@ -78,28 +78,21 @@ struct atcs_verify_interface {
        u32 function_bits;      /* supported functions bit vector */
 } __packed;
 
-bool radeon_acpi_is_pcie_performance_request_supported(struct radeon_device *rdev)
-{
-       /* XXX: query ATIF */
-
-       return false;
-}
-
-int radeon_acpi_pcie_notify_device_ready(struct radeon_device *rdev)
-{
-       /* XXX: call appropriate ATIF method */
-
-       return -EINVAL;
-
-}
+#define ATCS_VALID_FLAGS_MASK  0x3
 
-int radeon_acpi_pcie_performance_request(struct radeon_device *rdev,
-                                        u8 ref_req, bool advertise)
-{
-       /* XXX: call appropriate ATIF method */
+struct atcs_pref_req_input {
+       u16 size;               /* structure size in bytes (includes size field) */
+       u16 client_id;          /* client id (bit 2-0: func num, 7-3: dev num, 15-8: bus num) */
+       u16 valid_flags_mask;   /* valid flags mask */
+       u16 flags;              /* flags */
+       u8 req_type;            /* request type */
+       u8 perf_req;            /* performance request */
+} __packed;
 
-       return -EINVAL;
-}
+struct atcs_pref_req_output {
+       u16 size;               /* structure size in bytes (includes size field) */
+       u8 ret_val;             /* return value */
+} __packed;
 
 /* Call the ATIF method
  */
@@ -528,6 +521,135 @@ out:
        return err;
 }
 
+/**
+ * radeon_acpi_is_pcie_performance_request_supported
+ *
+ * @rdev: radeon_device pointer
+ *
+ * Check if the ATCS pcie_perf_req and pcie_dev_rdy methods
+ * are supported (all asics).
+ * returns true if supported, false if not.
+ */
+bool radeon_acpi_is_pcie_performance_request_supported(struct radeon_device *rdev)
+{
+       struct radeon_atcs *atcs = &rdev->atcs;
+
+       if (atcs->functions.pcie_perf_req && atcs->functions.pcie_dev_rdy)
+               return true;
+
+       return false;
+}
+
+/**
+ * radeon_acpi_pcie_notify_device_ready
+ *
+ * @rdev: radeon_device pointer
+ *
+ * Executes the PCIE_DEVICE_READY_NOTIFICATION method
+ * (all asics).
+ * returns 0 on success, error on failure.
+ */
+int radeon_acpi_pcie_notify_device_ready(struct radeon_device *rdev)
+{
+       acpi_handle handle;
+       union acpi_object *info;
+       struct radeon_atcs *atcs = &rdev->atcs;
+
+       /* Get the device handle */
+       handle = DEVICE_ACPI_HANDLE(&rdev->pdev->dev);
+       if (!handle)
+               return -EINVAL;
+
+       if (!atcs->functions.pcie_dev_rdy)
+               return -EINVAL;
+
+       info = radeon_atcs_call(handle, ATCS_FUNCTION_PCIE_DEVICE_READY_NOTIFICATION, NULL);
+       if (!info)
+               return -EIO;
+
+       kfree(info);
+
+       return 0;
+}
+
+/**
+ * radeon_acpi_pcie_performance_request
+ *
+ * @rdev: radeon_device pointer
+ * @perf_req: requested perf level (pcie gen speed)
+ * @advertise: set advertise caps flag if set
+ *
+ * Executes the PCIE_PERFORMANCE_REQUEST method to
+ * change the pcie gen speed (all asics).
+ * returns 0 on success, error on failure.
+ */
+int radeon_acpi_pcie_performance_request(struct radeon_device *rdev,
+                                        u8 perf_req, bool advertise)
+{
+       acpi_handle handle;
+       union acpi_object *info;
+       struct radeon_atcs *atcs = &rdev->atcs;
+       struct atcs_pref_req_input atcs_input;
+       struct atcs_pref_req_output atcs_output;
+       struct acpi_buffer params;
+       size_t size;
+       u32 retry = 3;
+
+       /* Get the device handle */
+       handle = DEVICE_ACPI_HANDLE(&rdev->pdev->dev);
+       if (!handle)
+               return -EINVAL;
+
+       if (!atcs->functions.pcie_perf_req)
+               return -EINVAL;
+
+       atcs_input.size = sizeof(struct atcs_pref_req_input);
+       /* client id (bit 2-0: func num, 7-3: dev num, 15-8: bus num) */
+       atcs_input.client_id = rdev->pdev->devfn | (rdev->pdev->bus->number << 8);
+       atcs_input.valid_flags_mask = ATCS_VALID_FLAGS_MASK;
+       atcs_input.flags = ATCS_WAIT_FOR_COMPLETION;
+       if (advertise)
+               atcs_input.flags |= ATCS_ADVERTISE_CAPS;
+       atcs_input.req_type = ATCS_PCIE_LINK_SPEED;
+       atcs_input.perf_req = perf_req;
+
+       params.length = sizeof(struct atcs_pref_req_input);
+       params.pointer = &atcs_input;
+
+       while (retry--) {
+               info = radeon_atcs_call(handle, ATCS_FUNCTION_PCIE_PERFORMANCE_REQUEST, &params);
+               if (!info)
+                       return -EIO;
+
+               memset(&atcs_output, 0, sizeof(atcs_output));
+
+               size = *(u16 *) info->buffer.pointer;
+               if (size < 3) {
+                       DRM_INFO("ATCS buffer is too small: %zu\n", size);
+                       kfree(info);
+                       return -EINVAL;
+               }
+               size = min(sizeof(atcs_output), size);
+
+               memcpy(&atcs_output, info->buffer.pointer, size);
+
+               kfree(info);
+
+               switch (atcs_output.ret_val) {
+               case ATCS_REQUEST_REFUSED:
+               default:
+                       return -EINVAL;
+               case ATCS_REQUEST_COMPLETE:
+                       return 0;
+               case ATCS_REQUEST_IN_PROGRESS:
+                       udelay(10);
+                       break;
+               }
+       }
+
+       return 0;
+}
+
 /**
  * radeon_acpi_event - handle notify events
  *