[PATCH V4 5/5] acpi/cppc: use MRTT/MPAR to decide if/when a req can be sent

Prashanth Prakash
2016-02-17T20:21:27Z

The ACPI spec defines Minimum Request Turnaround Time(MRTT) and
Maximum Periodic Access Rate(MPAR) to prevent the OSPM from sending
too many requests than the platform can handle. For further details
on these parameters please refer to section 14.1.3 of ACPI 6.0 spec.

This patch includes MRTT/MPAR in deciding if or when a CPPC request
can be sent to the platform to make sure CPPC implementation is
compliant to the spec.

Signed-off-by: Prashanth Prakash <pprakash@codeaurora.org>
---
drivers/acpi/cppc_acpi.c | 56 +++++++++++++++++++++++++++++++++++++++++++++++-
1 file changed, 55 insertions(+), 1 deletion(-)

diff --git a/drivers/acpi/cppc_acpi.c b/drivers/acpi/cppc_acpi.c
index 8a0911a..f00dc7e 100644
--- a/drivers/acpi/cppc_acpi.c
+++ b/drivers/acpi/cppc_acpi.c
@@ -66,6 +66,7 @@ static u64 comm_base_addr;
static int pcc_subspace_idx = -1;
static bool pcc_channel_acquired;
static ktime_t deadline;
+static unsigned int pcc_mpar, pcc_mrtt;

/* pcc mapped address + header size + offset within PCC subspace */
#define GET_PCC_VADDR(offs) (pcc_comm_addr + 0x8 + (offs))
@@ -85,6 +86,11 @@ static int check_pcc_chan(void)

/* Retry in case the remote processor was too slow to catch up. */
while (!ktime_after(ktime_get(), next_deadline)) {
+ /*
+ * Per spec, prior to boot the PCC space wil be initialized by
+ * platform and should have set the command completion bit when
+ * PCC can be used by OSPM
+ */
if (readw_relaxed(&generic_comm_base->status) & PCC_CMD_COMPLETE) {
ret = 0;
break;
@@ -104,6 +110,9 @@ static int send_pcc_cmd(u16 cmd)
int ret = -EIO;
struct acpi_pcct_shared_memory *generic_comm_base =
(struct acpi_pcct_shared_memory *) pcc_comm_addr;
+ static ktime_t last_cmd_cmpl_time, last_mpar_reset;
+ static int mpar_count;
+ unsigned int time_delta;

/*
* For CMD_WRITE we know for a fact the caller should have checked
@@ -115,6 +124,41 @@ static int send_pcc_cmd(u16 cmd)
return ret;
}

+ /*
+ * Handle the Minimum Request Turnaround Time(MRTT)
+ * "The minimum amount of time that OSPM must wait after the completion
+ * of a command before issuing the next command, in microseconds"
+ */
+ if (pcc_mrtt) {
+ time_delta = ktime_us_delta(ktime_get(), last_cmd_cmpl_time);
+ if (pcc_mrtt > time_delta)
+ udelay(pcc_mrtt - time_delta);
+ }
+
+ /*
+ * Handle the non-zero Maximum Periodic Access Rate(MPAR)
+ * "The maximum number of periodic requests that the subspace channel can
+ * support, reported in commands per minute. 0 indicates no limitation."
+ *
+ * This parameter should be ideally zero or large enough so that it can
+ * handle maximum number of requests that all the cores in the system can
+ * collectively generate. If it is not, we will follow the spec and just
+ * not send the request to the platform after hitting the MPAR limit in
+ * any 60s window
+ */
+ if (pcc_mpar) {
+ if (mpar_count == 0) {
+ time_delta = ktime_ms_delta(ktime_get(), last_mpar_reset);
+ if (time_delta < 60 * MSEC_PER_SEC) {
+ pr_debug("PCC cmd not sent due to MPAR limit");
+ return -EIO;
+ }
+ last_mpar_reset = ktime_get();
+ mpar_count = pcc_mpar;
+ }
+ mpar_count--;
+ }
+
/* Write to the shared comm region. */
writew_relaxed(cmd, &generic_comm_base->command);

@@ -135,9 +179,17 @@ static int send_pcc_cmd(u16 cmd)
* because the actual write()s are done before coming here
* and the next READ or WRITE will check if the channel
* is busy/free at the entry of this call.
+ *
+ * If Minimum Request Turnaround Time is non-zero, we need
+ * to record the completion time of both READ and WRITE
+ * command for proper handling of MRTT, so we need to check
+ * for pcc_mrtt in addition to CMD_READ
*/
- if (cmd == CMD_READ)
+ if (cmd == CMD_READ || pcc_mrtt) {
ret = check_pcc_chan();
+ if (pcc_mrtt)
+ last_cmd_cmpl_time = ktime_get();
+ }

mbox_client_txdone(pcc_channel, ret);
return ret;
@@ -375,6 +427,8 @@ static int register_pcc_channel(int pcc_subspace_idx)
*/
usecs_lat = NUM_RETRIES * cppc_ss->latency;
deadline = ns_to_ktime(usecs_lat * NSEC_PER_USEC);
+ pcc_mrtt = cppc_ss->min_turnaround_time;
+ pcc_mpar = cppc_ss->max_access_rate;

pcc_comm_addr = acpi_os_ioremap(comm_base_addr, len);
if (!pcc_comm_addr) {