[PATCH 1/2] ACPI / CPPC: read all perf caps in a single cppc read command

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



CPPC performance capabilities do not change during runtime, so we can
read perf caps for all CPUs in a single CPPC read command and cache
them locally.

Signed-off-by: Prashanth Prakash <pprakash@xxxxxxxxxxxxxx>
---
 drivers/acpi/cppc_acpi.c | 103 +++++++++++++++++++++++++++++++++--------------
 include/acpi/cppc_acpi.h |   1 +
 2 files changed, 73 insertions(+), 31 deletions(-)

diff --git a/drivers/acpi/cppc_acpi.c b/drivers/acpi/cppc_acpi.c
index 3ca0729..45f021a 100644
--- a/drivers/acpi/cppc_acpi.c
+++ b/drivers/acpi/cppc_acpi.c
@@ -92,6 +92,12 @@ struct cppc_pcc_data {
  */
 static DEFINE_PER_CPU(struct cpc_desc *, cpc_desc_ptr);
 
+/*
+ * The cppc_perf_caps structure contains the performance capabilities
+ * as described in section 8.4.7.1 of ACPI 6.1 spec
+ */
+static DEFINE_PER_CPU(struct cppc_perf_caps, cpc_perf_caps);
+
 /* pcc mapped address + header size + offset within PCC subspace */
 #define GET_PCC_VADDR(offs) (pcc_data.pcc_comm_addr + 0x8 + (offs))
 
@@ -971,49 +977,84 @@ static int cpc_write(int cpu, struct cpc_register_resource *reg_res, u64 val)
  */
 int cppc_get_perf_caps(int cpunum, struct cppc_perf_caps *perf_caps)
 {
-	struct cpc_desc *cpc_desc = per_cpu(cpc_desc_ptr, cpunum);
-	struct cpc_register_resource *highest_reg, *lowest_reg, *ref_perf,
-								 *nom_perf;
-	u64 high, low, nom;
-	int ret = 0, regs_in_pcc = 0;
+	static bool initialized;
+	int ret = 0, i;
+	bool regs_in_pcc = false;
+	struct cppc_perf_caps *caps;
 
-	if (!cpc_desc) {
-		pr_debug("No CPC descriptor for CPU:%d\n", cpunum);
-		return -ENODEV;
+	if (initialized) {
+		caps = &per_cpu(cpc_perf_caps, cpunum);
+
+		if (!caps->highest_perf || !caps->lowest_perf ||
+			!caps->nominal_perf || !caps->lowest_non_linear_perf)
+			return -EFAULT;
+
+		memcpy(perf_caps, caps, sizeof(struct cppc_perf_caps));
+		return 0;
 	}
 
-	highest_reg = &cpc_desc->cpc_regs[HIGHEST_PERF];
-	lowest_reg = &cpc_desc->cpc_regs[LOWEST_PERF];
-	ref_perf = &cpc_desc->cpc_regs[REFERENCE_PERF];
-	nom_perf = &cpc_desc->cpc_regs[NOMINAL_PERF];
+	/* Perf caps don't change over time, so read all of them at once */
+	for_each_possible_cpu(i) {
+		struct cpc_desc *cpc_desc = per_cpu(cpc_desc_ptr, i);
+		struct cpc_register_resource *highest_reg, *lowest_reg,
+			*nom_reg, *lowest_non_linear_reg;
+		u64 perf;
 
-	/* Are any of the regs PCC ?*/
-	if (CPC_IN_PCC(highest_reg) || CPC_IN_PCC(lowest_reg) ||
-		CPC_IN_PCC(ref_perf) || CPC_IN_PCC(nom_perf)) {
-		regs_in_pcc = 1;
-		down_write(&pcc_data.pcc_lock);
-		/* Ring doorbell once to update PCC subspace */
-		if (send_pcc_cmd(CMD_READ) < 0) {
-			ret = -EIO;
-			goto out_err;
+		if (!cpc_desc) {
+			pr_debug("No CPC descriptor for CPU:%d\n", i);
+			continue;
+		}
+
+		highest_reg = &cpc_desc->cpc_regs[HIGHEST_PERF];
+		lowest_reg = &cpc_desc->cpc_regs[LOWEST_PERF];
+		lowest_non_linear_reg = &cpc_desc->cpc_regs[LOW_NON_LINEAR_PERF];
+		nom_reg = &cpc_desc->cpc_regs[NOMINAL_PERF];
+
+		/* Are any of the regs in PCC ?*/
+		if (!regs_in_pcc) {
+			regs_in_pcc = CPC_IN_PCC(highest_reg) ||
+				CPC_IN_PCC(lowest_reg) ||
+				CPC_IN_PCC(lowest_non_linear_reg) ||
+				CPC_IN_PCC(nom_reg);
+			if (regs_in_pcc) {
+				down_write(&pcc_data.pcc_lock);
+				/* Skip if another CPU has completed init */
+				if (initialized)
+					goto out;
+
+				/* Ring doorbell once to update PCC subspace */
+				if (send_pcc_cmd(CMD_READ) < 0) {
+					ret = -EIO;
+					goto out;
+				}
+			}
 		}
-	}
 
-	cpc_read(cpunum, highest_reg, &high);
-	perf_caps->highest_perf = high;
+		caps = &per_cpu(cpc_perf_caps, i);
 
-	cpc_read(cpunum, lowest_reg, &low);
-	perf_caps->lowest_perf = low;
+		cpc_read(i, highest_reg, &perf);
+		caps->highest_perf = perf;
 
-	cpc_read(cpunum, nom_perf, &nom);
-	perf_caps->nominal_perf = nom;
+		cpc_read(i, lowest_reg, &perf);
+		caps->lowest_perf = perf;
 
-	if (!high || !low || !nom)
-		ret = -EFAULT;
+		cpc_read(i, nom_reg, &perf);
+		caps->nominal_perf = perf;
 
-out_err:
+		cpc_read(i, lowest_non_linear_reg, &perf);
+		caps->lowest_non_linear_perf = perf;
+	}
+
+	initialized = true;
+
+out:
 	if (regs_in_pcc)
 		up_write(&pcc_data.pcc_lock);
+
+	/* Copy over the per cpu data to perf_caps if init ws successful */
+	if (!ret)
+		ret = cppc_get_perf_caps(cpunum, perf_caps);
+
 	return ret;
 }
 EXPORT_SYMBOL_GPL(cppc_get_perf_caps);
diff --git a/include/acpi/cppc_acpi.h b/include/acpi/cppc_acpi.h
index 427a7c3..3f64660 100644
--- a/include/acpi/cppc_acpi.h
+++ b/include/acpi/cppc_acpi.h
@@ -103,6 +103,7 @@ struct cppc_perf_caps {
 	u32 highest_perf;
 	u32 nominal_perf;
 	u32 lowest_perf;
+	u32 lowest_non_linear_perf;
 };
 
 struct cppc_perf_ctrls {
-- 
Qualcomm Datacenter Technologies on behalf of Qualcomm Technologies, Inc.
Qualcomm Technologies, Inc. is a member of the
Code Aurora Forum, a Linux Foundation Collaborative Project.

--
To unsubscribe from this list: send the line "unsubscribe linux-acpi" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html



[Index of Archives]     [Linux IBM ACPI]     [Linux Power Management]     [Linux Kernel]     [Linux Laptop]     [Kernel Newbies]     [Share Photos]     [Security]     [Netfilter]     [Bugtraq]     [Yosemite News]     [MIPS Linux]     [ARM Linux]     [Linux Security]     [Linux RAID]     [Samba]     [Video 4 Linux]     [Device Mapper]     [Linux Resources]

  Powered by Linux