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>
This commit is contained in:
Alex Deucher 2013-02-13 15:47:24 -05:00
parent 9d45ad5aff
commit e37e6a0e4f
2 changed files with 143 additions and 21 deletions

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

View 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 */
#define ATCS_VALID_FLAGS_MASK 0x3
return false;
}
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;
int radeon_acpi_pcie_notify_device_ready(struct radeon_device *rdev)
{
/* XXX: call appropriate ATIF method */
return -EINVAL;
}
int radeon_acpi_pcie_performance_request(struct radeon_device *rdev,
u8 ref_req, bool advertise)
{
/* XXX: call appropriate ATIF method */
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 @@ static int radeon_atcs_verify_interface(acpi_handle handle,
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
*