Browse Source

drm/radeon: implement apci perf request

These functions use acpi methods to adjust the pcie
gen speed.  Used by DPM.

Signed-off-by: Alex Deucher <alexander.deucher@amd.com>
Alex Deucher 12 years ago
parent
commit
e37e6a0e4f
2 changed files with 143 additions and 21 deletions
  1. 1 1
      drivers/gpu/drm/radeon/radeon.h
  2. 142 20
      drivers/gpu/drm/radeon/radeon_acpi.c

+ 1 - 1
drivers/gpu/drm/radeon/radeon.h

@@ -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; }

+ 142 - 20
drivers/gpu/drm/radeon/radeon_acpi.c

@@ -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
  *