Not for upstreaming. Hacked for experiments on the the Thinkpad X240. The pcc_send_data() function is modified to read certain MSRs and update a shared memory region. This enables the PCC client (CPPC in this case) to read from the buffer as though it were getting data from a remote processor.
Signed-off-by: Ashwin Chaugule ashwin.chaugule@linaro.org --- drivers/mailbox/pcc.c | 125 +++++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 109 insertions(+), 16 deletions(-)
diff --git a/drivers/mailbox/pcc.c b/drivers/mailbox/pcc.c index a16991e..27d0e61 100644 --- a/drivers/mailbox/pcc.c +++ b/drivers/mailbox/pcc.c @@ -98,22 +98,22 @@ static bool pcc_tx_done(struct mbox_chan *chan) u16 cmd_delay = pcct_ss->min_turnaround_time; unsigned int retries = 0;
- /* Try a few times while waiting for platform to consume */ - while (!(readw_relaxed(&generic_comm_base->status) - & PCC_CMD_COMPLETE)) { - - if (retries++ < 5) - udelay(cmd_delay); - else { - /* - * If the remote is dead, this will cause the Mbox - * controller to timeout after mbox client.tx_tout - * msecs. - */ - pr_err("PCC platform did not respond.\n"); - return false; - } - } +// /* Try a few times while waiting for platform to consume */ +// while (!(readw_relaxed(&generic_comm_base->status) +// & PCC_CMD_COMPLETE)) { +// +// if (retries++ < 5) +// udelay(cmd_delay); +// else { +// /* +// * If the remote is dead, this will cause the Mbox +// * controller to timeout after mbox client.tx_tout +// * msecs. +// */ +// pr_err("PCC platform did not respond.\n"); +// return false; +// } +// } return true; }
@@ -127,6 +127,97 @@ static int get_subspace_id(struct mbox_chan *chan) return id; }
+#define PCC_HACK + +#ifdef PCC_HACK + +#include <asm/msr.h> + +/* These offsets are from the SSDT9.asl table on the Thinkpad X240 */ + +/* These are offsets per CPU from which its CPC table begins. */ +int cpu_base[] = {0, 0x64, 0xC8, 0x12C, 0x190, 0x1F4, 0x258, 0x2BC}; + +/* These are offsets of the registers in each CPC table. */ +#define HIGHEST_PERF_OFFSET 0x0 +#define LOWEST_PERF_OFFSET 0xc +#define DESIRED_PERF_OFFSET 0x14 + +static int core_get_min(void) +{ + u64 val; + rdmsrl(MSR_PLATFORM_INFO, val); + return (val >> 40) & 0xff; +} + +static int core_get_max(void) +{ + u64 val; + rdmsrl(MSR_PLATFORM_INFO, val); + return (val >> 8) & 0xff; +} + +static int core_get_turbo(void) +{ + u64 value; + int nont, ret; + + rdmsrl(MSR_NHM_TURBO_RATIO_LIMIT, value); + nont = core_get_max(); + ret = ((value) & 255); + if (ret <= nont) + ret = nont; + return ret; +} + +static int pcc_send_data(struct mbox_chan *chan, void *data) +{ + struct acpi_pcct_subspace *pcct_ss = chan->con_priv; + u64 pcc_comm_addr = pcct_ss->base_address; + unsigned int cpu; + u16 cmd = *(u16 *) data; + u64 desired_val; + + /*XXX: Instead of waiting for platform to consume the cmd, + * just do what the platform would've done. + */ + switch (cmd) { + case 0: //PCC_CMD_READ + + /* XXX: Normally the Platform would need to update all the other CPPC registers as well. + * But for this experiment, since we're not really using all of them, we'll only update + * what we use. + */ + for_each_possible_cpu(cpu) { + *(char*)(pcc_comm_addr + cpu_base[cpu] + HIGHEST_PERF_OFFSET) = core_get_turbo(); + *(char*)(pcc_comm_addr + cpu_base[cpu] + LOWEST_PERF_OFFSET) = core_get_min(); + } + break; + case 1: //PCC_CMD_WRITE + + /* XXX: All this hackery is very X86 Thinkpad X240 specific. + * Normally, the cpc_write64() would have all the info on + * how, where and what to write. + */ + for_each_possible_cpu(cpu) { + desired_val = *(u64*)(pcc_comm_addr + cpu_base[cpu] + DESIRED_PERF_OFFSET); + + if (desired_val) { + wrmsrl_on_cpu(cpu, MSR_IA32_PERF_CTL, desired_val << 8); + *(u64*)(pcc_comm_addr + cpu_base[cpu] + DESIRED_PERF_OFFSET) = 0; + } + } + break; + default: + pr_err("Unknown PCC cmd from the OS\n"); + return 0; + } + + return 0; +} + +#else + /* Channel lock is already held by mbox controller code. */ static int pcc_send_data(struct mbox_chan *chan, void *data) { @@ -171,6 +262,8 @@ out_err: return ret; }
+#endif + static struct mbox_chan_ops pcc_chan_ops = { .send_data = pcc_send_data, .last_tx_done = pcc_tx_done,