|
@@ -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, ¶ms);
|
|
|
+ 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
|
|
|
*
|