All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH V3 0/7] CPPC enhancements
@ 2016-08-16 20:39 Prashanth Prakash
  2016-08-16 20:39 ` [PATCH V3 1/7] ACPI/CPPC: restructure read/writes for efficient sys mapped reg ops Prashanth Prakash
                   ` (8 more replies)
  0 siblings, 9 replies; 12+ messages in thread
From: Prashanth Prakash @ 2016-08-16 20:39 UTC (permalink / raw)
  To: linux-acpi; +Cc: rjw, alexey.klimov, hotran, cov, Prashanth Prakash

Hi,

This series has patches related to cppc that either improves the performance
or adds some features.

v3:
  * Fixed the error path in send_pcc_cmd in CPPC batching patch
  * Included a patch to consolidate all the global variables
  * Included a patch to check for PCC error bit (Alexey)
v2:
  * Sysfs entry for counter wraparound time(Alexey)
  * Updated latency calculation to sum of Nominal and MRTT(Alexey)
  * Moved refernce_perf from perf_caps to fb_ctrs
  * Addressed review comments from Hoan and few minor cleanups
v1:
  * Pulled in the CPPC batching path to this series
  * Addressed POSTCHANGE issue(Alexey) and switch to RW semaphore(Hoan)

Thanks,
Prashanth
Ashwin Chaugule (2):
  ACPI/CPPC: restructure read/writes for efficient sys mapped reg ops
  ACPI/CPPC: add sysfs support to compute delivered performance

Prashanth Prakash (5):
  ACPI/CPPC: acquire pcc_lock only while accessing PCC subspace
  ACPI/CPPC: support for batching CPPC requests
  ACPI/CPPC: set a non-zero value for transition_latency
  ACPI/CPPC: move all PCC related information into pcc_data
  ACPI/CPPC: check for error bit in PCC status field

 drivers/acpi/cppc_acpi.c       | 600 +++++++++++++++++++++++++++++++----------
 drivers/cpufreq/cppc_cpufreq.c |   1 +
 include/acpi/cppc_acpi.h       |  14 +-
 3 files changed, 466 insertions(+), 149 deletions(-)

-- 
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.


^ permalink raw reply	[flat|nested] 12+ messages in thread

* [PATCH V3 1/7] ACPI/CPPC: restructure read/writes for efficient sys mapped reg ops
  2016-08-16 20:39 [PATCH V3 0/7] CPPC enhancements Prashanth Prakash
@ 2016-08-16 20:39 ` Prashanth Prakash
  2016-08-16 20:39 ` [PATCH V3 2/7] ACPI/CPPC: acquire pcc_lock only while accessing PCC subspace Prashanth Prakash
                   ` (7 subsequent siblings)
  8 siblings, 0 replies; 12+ messages in thread
From: Prashanth Prakash @ 2016-08-16 20:39 UTC (permalink / raw)
  To: linux-acpi
  Cc: rjw, alexey.klimov, hotran, cov, Ashwin Chaugule, Prashanth Prakash

From: Ashwin Chaugule <ashwin.chaugule@linaro.org>

For cases where sys mapped CPC registers need to be accessed
frequently, it helps immensly to pre-map them rather than map
and unmap for each operation. e.g. case where feedback counters
are sys mem map registers.

Restructure cpc_read/write and the cpc_regs structure to allow
pre-mapping the system addresses and unmap them when the CPU exits.

Cc: "Rafael J. Wysocki" <rjw@rjwysocki.net>
Signed-off-by: Ashwin Chaugule <ashwin.chaugule@linaro.org>
Signed-off-by: Prashanth Prakash <pprakash@codeaurora.org>
---
 drivers/acpi/cppc_acpi.c | 108 +++++++++++++++++++++++++++++++----------------
 include/acpi/cppc_acpi.h |   1 +
 2 files changed, 72 insertions(+), 37 deletions(-)

diff --git a/drivers/acpi/cppc_acpi.c b/drivers/acpi/cppc_acpi.c
index 2e98173..fea58e2 100644
--- a/drivers/acpi/cppc_acpi.c
+++ b/drivers/acpi/cppc_acpi.c
@@ -62,7 +62,6 @@ static DEFINE_PER_CPU(struct cpc_desc *, cpc_desc_ptr);
 /* This layer handles all the PCC specifics for CPPC. */
 static struct mbox_chan *pcc_channel;
 static void __iomem *pcc_comm_addr;
-static u64 comm_base_addr;
 static int pcc_subspace_idx = -1;
 static bool pcc_channel_acquired;
 static ktime_t deadline;
@@ -394,7 +393,6 @@ EXPORT_SYMBOL_GPL(acpi_get_psd_map);
 static int register_pcc_channel(int pcc_subspace_idx)
 {
 	struct acpi_pcct_hw_reduced *cppc_ss;
-	unsigned int len;
 	u64 usecs_lat;
 
 	if (pcc_subspace_idx >= 0) {
@@ -419,12 +417,6 @@ static int register_pcc_channel(int pcc_subspace_idx)
 			return -ENODEV;
 		}
 
-		/*
-		 * This is the shared communication region
-		 * for the OS and Platform to communicate over.
-		 */
-		comm_base_addr = cppc_ss->base_address;
-		len = cppc_ss->length;
 
 		/*
 		 * cppc_ss->latency is just a Nominal value. In reality
@@ -436,7 +428,7 @@ static int register_pcc_channel(int pcc_subspace_idx)
 		pcc_mrtt = cppc_ss->min_turnaround_time;
 		pcc_mpar = cppc_ss->max_access_rate;
 
-		pcc_comm_addr = acpi_os_ioremap(comm_base_addr, len);
+		pcc_comm_addr = acpi_os_ioremap(cppc_ss->base_address, cppc_ss->length);
 		if (!pcc_comm_addr) {
 			pr_err("Failed to ioremap PCC comm region mem\n");
 			return -ENOMEM;
@@ -545,6 +537,8 @@ int acpi_cppc_processor_probe(struct acpi_processor *pr)
 		goto out_free;
 	}
 
+	cpc_ptr->num_entries = num_ent;
+
 	/* Second entry should be revision. */
 	cpc_obj = &out_obj->package.elements[1];
 	if (cpc_obj->type == ACPI_TYPE_INTEGER)	{
@@ -585,7 +579,16 @@ int acpi_cppc_processor_probe(struct acpi_processor *pr)
 					pr_debug("Mismatched PCC ids.\n");
 					goto out_free;
 				}
-			} else if (gas_t->space_id != ACPI_ADR_SPACE_SYSTEM_MEMORY) {
+			} else if (gas_t->space_id == ACPI_ADR_SPACE_SYSTEM_MEMORY) {
+				if (gas_t->address) {
+					void __iomem *addr;
+
+					addr = ioremap(gas_t->address, gas_t->bit_width/8);
+					if (!addr)
+						goto out_free;
+					cpc_ptr->cpc_regs[i-2].sys_mem_vaddr = addr;
+				}
+			} else {
 				/* Support only PCC and SYS MEM type regs */
 				pr_debug("Unsupported register type: %d\n", gas_t->space_id);
 				goto out_free;
@@ -623,6 +626,13 @@ int acpi_cppc_processor_probe(struct acpi_processor *pr)
 	return 0;
 
 out_free:
+	/* Free all the mapped sys mem areas for this CPU */
+	for (i = 2; i < cpc_ptr->num_entries; i++) {
+		void __iomem *addr = cpc_ptr->cpc_regs[i-2].sys_mem_vaddr;
+
+		if (addr)
+			iounmap(addr);
+	}
 	kfree(cpc_ptr);
 
 out_buf_free:
@@ -640,7 +650,17 @@ EXPORT_SYMBOL_GPL(acpi_cppc_processor_probe);
 void acpi_cppc_processor_exit(struct acpi_processor *pr)
 {
 	struct cpc_desc *cpc_ptr;
+	unsigned int i;
+	void __iomem *addr;
 	cpc_ptr = per_cpu(cpc_desc_ptr, pr->id);
+
+	/* Free all the mapped sys mem areas for this CPU */
+	for (i = 2; i < cpc_ptr->num_entries; i++) {
+		addr = cpc_ptr->cpc_regs[i-2].sys_mem_vaddr;
+		if (addr)
+			iounmap(addr);
+	}
+
 	kfree(cpc_ptr);
 }
 EXPORT_SYMBOL_GPL(acpi_cppc_processor_exit);
@@ -651,15 +671,27 @@ EXPORT_SYMBOL_GPL(acpi_cppc_processor_exit);
  * we can directly write to it.
  */
 
-static int cpc_read(struct cpc_reg *reg, u64 *val)
+static int cpc_read(struct cpc_register_resource *reg_res, u64 *val)
 {
 	int ret_val = 0;
+	void __iomem *vaddr = 0;
+	struct cpc_reg *reg = &reg_res->cpc_entry.reg;
+
+	if (reg_res->type == ACPI_TYPE_INTEGER) {
+		*val = reg_res->cpc_entry.int_value;
+		return ret_val;
+	}
 
 	*val = 0;
-	if (reg->space_id == ACPI_ADR_SPACE_PLATFORM_COMM) {
-		void __iomem *vaddr = GET_PCC_VADDR(reg->address);
+	if (reg->space_id == ACPI_ADR_SPACE_PLATFORM_COMM)
+		vaddr = GET_PCC_VADDR(reg->address);
+	else if (reg->space_id == ACPI_ADR_SPACE_SYSTEM_MEMORY)
+		vaddr = reg_res->sys_mem_vaddr;
+	else
+		return acpi_os_read_memory((acpi_physical_address)reg->address,
+				val, reg->bit_width);
 
-		switch (reg->bit_width) {
+	switch (reg->bit_width) {
 		case 8:
 			*val = readb_relaxed(vaddr);
 			break;
@@ -674,23 +706,28 @@ static int cpc_read(struct cpc_reg *reg, u64 *val)
 			break;
 		default:
 			pr_debug("Error: Cannot read %u bit width from PCC\n",
-				reg->bit_width);
+					reg->bit_width);
 			ret_val = -EFAULT;
-		}
-	} else
-		ret_val = acpi_os_read_memory((acpi_physical_address)reg->address,
-					val, reg->bit_width);
+	}
+
 	return ret_val;
 }
 
-static int cpc_write(struct cpc_reg *reg, u64 val)
+static int cpc_write(struct cpc_register_resource *reg_res, u64 val)
 {
 	int ret_val = 0;
+	void __iomem *vaddr = 0;
+	struct cpc_reg *reg = &reg_res->cpc_entry.reg;
 
-	if (reg->space_id == ACPI_ADR_SPACE_PLATFORM_COMM) {
-		void __iomem *vaddr = GET_PCC_VADDR(reg->address);
+	if (reg->space_id == ACPI_ADR_SPACE_PLATFORM_COMM)
+		vaddr = GET_PCC_VADDR(reg->address);
+	else if (reg->space_id == ACPI_ADR_SPACE_SYSTEM_MEMORY)
+		vaddr = reg_res->sys_mem_vaddr;
+	else
+		return acpi_os_write_memory((acpi_physical_address)reg->address,
+				val, reg->bit_width);
 
-		switch (reg->bit_width) {
+	switch (reg->bit_width) {
 		case 8:
 			writeb_relaxed(val, vaddr);
 			break;
@@ -705,13 +742,11 @@ static int cpc_write(struct cpc_reg *reg, u64 val)
 			break;
 		default:
 			pr_debug("Error: Cannot write %u bit width to PCC\n",
-				reg->bit_width);
+					reg->bit_width);
 			ret_val = -EFAULT;
 			break;
-		}
-	} else
-		ret_val = acpi_os_write_memory((acpi_physical_address)reg->address,
-				val, reg->bit_width);
+	}
+
 	return ret_val;
 }
 
@@ -754,16 +789,16 @@ int cppc_get_perf_caps(int cpunum, struct cppc_perf_caps *perf_caps)
 		}
 	}
 
-	cpc_read(&highest_reg->cpc_entry.reg, &high);
+	cpc_read(highest_reg, &high);
 	perf_caps->highest_perf = high;
 
-	cpc_read(&lowest_reg->cpc_entry.reg, &low);
+	cpc_read(lowest_reg, &low);
 	perf_caps->lowest_perf = low;
 
-	cpc_read(&ref_perf->cpc_entry.reg, &ref);
+	cpc_read(ref_perf, &ref);
 	perf_caps->reference_perf = ref;
 
-	cpc_read(&nom_perf->cpc_entry.reg, &nom);
+	cpc_read(nom_perf, &nom);
 	perf_caps->nominal_perf = nom;
 
 	if (!ref)
@@ -804,7 +839,7 @@ int cppc_get_perf_ctrs(int cpunum, struct cppc_perf_fb_ctrs *perf_fb_ctrs)
 
 	/* Are any of the regs PCC ?*/
 	if ((delivered_reg->cpc_entry.reg.space_id == ACPI_ADR_SPACE_PLATFORM_COMM) ||
-			(reference_reg->cpc_entry.reg.space_id == ACPI_ADR_SPACE_PLATFORM_COMM)) {
+		(reference_reg->cpc_entry.reg.space_id == ACPI_ADR_SPACE_PLATFORM_COMM)) {
 		/* Ring doorbell once to update PCC subspace */
 		if (send_pcc_cmd(CMD_READ) < 0) {
 			ret = -EIO;
@@ -812,8 +847,8 @@ int cppc_get_perf_ctrs(int cpunum, struct cppc_perf_fb_ctrs *perf_fb_ctrs)
 		}
 	}
 
-	cpc_read(&delivered_reg->cpc_entry.reg, &delivered);
-	cpc_read(&reference_reg->cpc_entry.reg, &reference);
+	cpc_read(delivered_reg, &delivered);
+	cpc_read(reference_reg, &reference);
 
 	if (!delivered || !reference) {
 		ret = -EFAULT;
@@ -868,7 +903,7 @@ int cppc_set_perf(int cpu, struct cppc_perf_ctrls *perf_ctrls)
 	 * Skip writing MIN/MAX until Linux knows how to come up with
 	 * useful values.
 	 */
-	cpc_write(&desired_reg->cpc_entry.reg, perf_ctrls->desired_perf);
+	cpc_write(desired_reg, perf_ctrls->desired_perf);
 
 	/* Is this a PCC reg ?*/
 	if (desired_reg->cpc_entry.reg.space_id == ACPI_ADR_SPACE_PLATFORM_COMM) {
@@ -878,7 +913,6 @@ int cppc_set_perf(int cpu, struct cppc_perf_ctrls *perf_ctrls)
 	}
 busy_channel:
 	spin_unlock(&pcc_lock);
-
 	return ret;
 }
 EXPORT_SYMBOL_GPL(cppc_set_perf);
diff --git a/include/acpi/cppc_acpi.h b/include/acpi/cppc_acpi.h
index 284965c..36ff5c6 100644
--- a/include/acpi/cppc_acpi.h
+++ b/include/acpi/cppc_acpi.h
@@ -49,6 +49,7 @@ struct cpc_reg {
  */
 struct cpc_register_resource {
 	acpi_object_type type;
+	u64 __iomem *sys_mem_vaddr;
 	union {
 		struct cpc_reg reg;
 		u64 int_value;
-- 
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.


^ permalink raw reply related	[flat|nested] 12+ messages in thread

* [PATCH V3 2/7] ACPI/CPPC: acquire pcc_lock only while accessing PCC subspace
  2016-08-16 20:39 [PATCH V3 0/7] CPPC enhancements Prashanth Prakash
  2016-08-16 20:39 ` [PATCH V3 1/7] ACPI/CPPC: restructure read/writes for efficient sys mapped reg ops Prashanth Prakash
@ 2016-08-16 20:39 ` Prashanth Prakash
  2016-08-16 20:39 ` [PATCH V3 3/7] ACPI/CPPC: support for batching CPPC requests Prashanth Prakash
                   ` (6 subsequent siblings)
  8 siblings, 0 replies; 12+ messages in thread
From: Prashanth Prakash @ 2016-08-16 20:39 UTC (permalink / raw)
  To: linux-acpi; +Cc: rjw, alexey.klimov, hotran, cov, Prashanth Prakash

We need to acquire pcc_lock only when we are accessing registers
that are in the PCC subspsace.

Cc: "Rafael J. Wysocki" <rjw@rjwysocki.net>
Signed-off-by: Prashanth Prakash <pprakash@codeaurora.org>
---
 drivers/acpi/cppc_acpi.c | 30 ++++++++++++++++--------------
 1 file changed, 16 insertions(+), 14 deletions(-)

diff --git a/drivers/acpi/cppc_acpi.c b/drivers/acpi/cppc_acpi.c
index fea58e2..93826c7 100644
--- a/drivers/acpi/cppc_acpi.c
+++ b/drivers/acpi/cppc_acpi.c
@@ -763,7 +763,7 @@ int cppc_get_perf_caps(int cpunum, struct cppc_perf_caps *perf_caps)
 	struct cpc_register_resource *highest_reg, *lowest_reg, *ref_perf,
 								 *nom_perf;
 	u64 high, low, ref, nom;
-	int ret = 0;
+	int ret = 0, regs_in_pcc = 0;
 
 	if (!cpc_desc) {
 		pr_debug("No CPC descriptor for CPU:%d\n", cpunum);
@@ -775,13 +775,13 @@ int cppc_get_perf_caps(int cpunum, struct cppc_perf_caps *perf_caps)
 	ref_perf = &cpc_desc->cpc_regs[REFERENCE_PERF];
 	nom_perf = &cpc_desc->cpc_regs[NOMINAL_PERF];
 
-	spin_lock(&pcc_lock);
-
 	/* Are any of the regs PCC ?*/
 	if ((highest_reg->cpc_entry.reg.space_id == ACPI_ADR_SPACE_PLATFORM_COMM) ||
-			(lowest_reg->cpc_entry.reg.space_id == ACPI_ADR_SPACE_PLATFORM_COMM) ||
-			(ref_perf->cpc_entry.reg.space_id == ACPI_ADR_SPACE_PLATFORM_COMM) ||
-			(nom_perf->cpc_entry.reg.space_id == ACPI_ADR_SPACE_PLATFORM_COMM)) {
+		(lowest_reg->cpc_entry.reg.space_id == ACPI_ADR_SPACE_PLATFORM_COMM) ||
+		(ref_perf->cpc_entry.reg.space_id == ACPI_ADR_SPACE_PLATFORM_COMM) ||
+		(nom_perf->cpc_entry.reg.space_id == ACPI_ADR_SPACE_PLATFORM_COMM)) {
+		spin_lock(&pcc_lock);
+		regs_in_pcc = 1;
 		/* Ring doorbell once to update PCC subspace */
 		if (send_pcc_cmd(CMD_READ) < 0) {
 			ret = -EIO;
@@ -808,7 +808,8 @@ int cppc_get_perf_caps(int cpunum, struct cppc_perf_caps *perf_caps)
 		ret = -EFAULT;
 
 out_err:
-	spin_unlock(&pcc_lock);
+	if (regs_in_pcc)
+		spin_unlock(&pcc_lock);
 	return ret;
 }
 EXPORT_SYMBOL_GPL(cppc_get_perf_caps);
@@ -825,7 +826,7 @@ int cppc_get_perf_ctrs(int cpunum, struct cppc_perf_fb_ctrs *perf_fb_ctrs)
 	struct cpc_desc *cpc_desc = per_cpu(cpc_desc_ptr, cpunum);
 	struct cpc_register_resource *delivered_reg, *reference_reg;
 	u64 delivered, reference;
-	int ret = 0;
+	int ret = 0, regs_in_pcc = 0;
 
 	if (!cpc_desc) {
 		pr_debug("No CPC descriptor for CPU:%d\n", cpunum);
@@ -835,11 +836,11 @@ int cppc_get_perf_ctrs(int cpunum, struct cppc_perf_fb_ctrs *perf_fb_ctrs)
 	delivered_reg = &cpc_desc->cpc_regs[DELIVERED_CTR];
 	reference_reg = &cpc_desc->cpc_regs[REFERENCE_CTR];
 
-	spin_lock(&pcc_lock);
-
 	/* Are any of the regs PCC ?*/
 	if ((delivered_reg->cpc_entry.reg.space_id == ACPI_ADR_SPACE_PLATFORM_COMM) ||
 		(reference_reg->cpc_entry.reg.space_id == ACPI_ADR_SPACE_PLATFORM_COMM)) {
+		spin_lock(&pcc_lock);
+		regs_in_pcc = 1;
 		/* Ring doorbell once to update PCC subspace */
 		if (send_pcc_cmd(CMD_READ) < 0) {
 			ret = -EIO;
@@ -865,7 +866,8 @@ int cppc_get_perf_ctrs(int cpunum, struct cppc_perf_fb_ctrs *perf_fb_ctrs)
 	perf_fb_ctrs->prev_reference = reference;
 
 out_err:
-	spin_unlock(&pcc_lock);
+	if (regs_in_pcc)
+		spin_unlock(&pcc_lock);
 	return ret;
 }
 EXPORT_SYMBOL_GPL(cppc_get_perf_ctrs);
@@ -890,10 +892,9 @@ int cppc_set_perf(int cpu, struct cppc_perf_ctrls *perf_ctrls)
 
 	desired_reg = &cpc_desc->cpc_regs[DESIRED_PERF];
 
-	spin_lock(&pcc_lock);
-
 	/* If this is PCC reg, check if channel is free before writing */
 	if (desired_reg->cpc_entry.reg.space_id == ACPI_ADR_SPACE_PLATFORM_COMM) {
+		spin_lock(&pcc_lock);
 		ret = check_pcc_chan();
 		if (ret)
 			goto busy_channel;
@@ -912,7 +913,8 @@ int cppc_set_perf(int cpu, struct cppc_perf_ctrls *perf_ctrls)
 			ret = -EIO;
 	}
 busy_channel:
-	spin_unlock(&pcc_lock);
+	if (desired_reg->cpc_entry.reg.space_id == ACPI_ADR_SPACE_PLATFORM_COMM)
+		spin_unlock(&pcc_lock);
 	return ret;
 }
 EXPORT_SYMBOL_GPL(cppc_set_perf);
-- 
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.


^ permalink raw reply related	[flat|nested] 12+ messages in thread

* [PATCH V3 3/7] ACPI/CPPC: support for batching CPPC requests
  2016-08-16 20:39 [PATCH V3 0/7] CPPC enhancements Prashanth Prakash
  2016-08-16 20:39 ` [PATCH V3 1/7] ACPI/CPPC: restructure read/writes for efficient sys mapped reg ops Prashanth Prakash
  2016-08-16 20:39 ` [PATCH V3 2/7] ACPI/CPPC: acquire pcc_lock only while accessing PCC subspace Prashanth Prakash
@ 2016-08-16 20:39 ` Prashanth Prakash
  2016-08-16 20:39 ` [PATCH V3 4/7] ACPI/CPPC: set a non-zero value for transition_latency Prashanth Prakash
                   ` (5 subsequent siblings)
  8 siblings, 0 replies; 12+ messages in thread
From: Prashanth Prakash @ 2016-08-16 20:39 UTC (permalink / raw)
  To: linux-acpi; +Cc: rjw, alexey.klimov, hotran, cov, Prashanth Prakash

CPPC defined in section 8.4.7 of ACPI 6.0 specification suggests
"To amortize the cost of PCC transactions, OSPM should read or write
all PCC registers via a single read or write command when possible"
This patch enables opportunistic batching of frequency transition
requests whenever the request happen to overlap in time.

Currently the access to pcc is serialized by a spin lock which does
not scale well as we increase the number of cores in the system. This
patch improves the scalability by allowing the differnt CPU cores to
update PCC subspace in parallel and by batching requests which will
reduce the certain types of operation(checking command completion bit,
ringing doorbell) by a significant margin.

Profiling shows significant improvement in the overall effeciency
to service freq. transition requests. With this patch we observe close
to 30% of the frequency transition requests being batched with other
requests while running apache bench on a ARM platform with 6
independent domains(or sets of related cpus).

Cc: "Rafael J. Wysocki" <rjw@rjwysocki.net>
Signed-off-by: Prashanth Prakash <pprakash@codeaurora.org>
---
 drivers/acpi/cppc_acpi.c | 198 +++++++++++++++++++++++++++++++++++++++--------
 include/acpi/cppc_acpi.h |   2 +
 2 files changed, 166 insertions(+), 34 deletions(-)

diff --git a/drivers/acpi/cppc_acpi.c b/drivers/acpi/cppc_acpi.c
index 93826c7..5623fca 100644
--- a/drivers/acpi/cppc_acpi.c
+++ b/drivers/acpi/cppc_acpi.c
@@ -40,15 +40,35 @@
 #include <linux/cpufreq.h>
 #include <linux/delay.h>
 #include <linux/ktime.h>
+#include <linux/rwsem.h>
+#include <linux/wait.h>
 
 #include <acpi/cppc_acpi.h>
+
 /*
- * Lock to provide mutually exclusive access to the PCC
- * channel. e.g. When the remote updates the shared region
- * with new data, the reader needs to be protected from
- * other CPUs activity on the same channel.
+ * Lock to provide controlled access to the PCC channel.
+ *
+ * For performance critical usecases(currently cppc_set_perf)
+ *	We need to take read_lock and check if channel belongs to OSPM before
+ * reading or writing to PCC subspace
+ *	We need to take write_lock before transferring the channel ownership to
+ * the platform via a Doorbell
+ *	This allows us to batch a number of CPPC requests if they happen to
+ * originate in about the same time
+ *
+ * For non-performance critical usecases(init)
+ *	Take write_lock for all purposes which gives exclusive access
  */
-static DEFINE_SPINLOCK(pcc_lock);
+static DECLARE_RWSEM(pcc_lock);
+
+/* Indicates if there are any pending/batched PCC write commands */
+static bool pending_pcc_write_cmd;
+
+/* Wait queue for CPUs whose requests were batched */
+static DECLARE_WAIT_QUEUE_HEAD(pcc_write_wait_q);
+
+/* Used to identify if a batched request is delivered to platform */
+static unsigned int pcc_write_cnt;
 
 /*
  * The cpc_desc structure contains the ACPI register details
@@ -70,6 +90,11 @@ 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))
 
+/* Check if a CPC regsiter is in PCC */
+#define CPC_IN_PCC(cpc) ((cpc)->type == ACPI_TYPE_BUFFER &&		\
+				(cpc)->cpc_entry.reg.space_id ==	\
+				ACPI_ADR_SPACE_PLATFORM_COMM)
+
 /*
  * Arbitrary Retries in case the remote processor is slow to respond
  * to PCC commands. Keeping it high enough to cover emulators where
@@ -104,9 +129,13 @@ static int check_pcc_chan(void)
 	return ret;
 }
 
+/*
+ * This function transfers the ownership of the PCC to the platform
+ * So it must be called while holding write_lock(pcc_lock)
+ */
 static int send_pcc_cmd(u16 cmd)
 {
-	int ret = -EIO;
+	int ret = -EIO, i;
 	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;
@@ -118,10 +147,19 @@ static int send_pcc_cmd(u16 cmd)
 	 * the channel before writing to PCC space
 	 */
 	if (cmd == CMD_READ) {
+		/*
+		 * If there are pending cpc_writes, then we stole the channel
+		 * before write completion, so first send a WRITE command to
+		 * platform
+		 */
+		if (pending_pcc_write_cmd)
+			send_pcc_cmd(CMD_WRITE);
+
 		ret = check_pcc_chan();
 		if (ret)
-			return ret;
-	}
+			goto end;
+	} else /* CMD_WRITE */
+		pending_pcc_write_cmd = FALSE;
 
 	/*
 	 * Handle the Minimum Request Turnaround Time(MRTT)
@@ -150,7 +188,8 @@ static int send_pcc_cmd(u16 cmd)
 			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;
+				ret = -EIO;
+				goto end;
 			}
 			last_mpar_reset = ktime_get();
 			mpar_count = pcc_mpar;
@@ -169,7 +208,7 @@ static int send_pcc_cmd(u16 cmd)
 	if (ret < 0) {
 		pr_err("Err sending PCC mbox message. cmd:%d, ret:%d\n",
 				cmd, ret);
-		return ret;
+		goto end;
 	}
 
 	/*
@@ -191,6 +230,23 @@ static int send_pcc_cmd(u16 cmd)
 	}
 
 	mbox_client_txdone(pcc_channel, ret);
+
+end:
+	if (cmd == CMD_WRITE) {
+		if (unlikely(ret)) {
+			for_each_possible_cpu(i) {
+				struct cpc_desc *desc = per_cpu(cpc_desc_ptr, i);
+				if (!desc)
+					continue;
+
+				if (desc->write_cmd_id == pcc_write_cnt)
+					desc->write_cmd_status = ret;
+			}
+		}
+		pcc_write_cnt++;
+		wake_up_all(&pcc_write_wait_q);
+	}
+
 	return ret;
 }
 
@@ -776,12 +832,10 @@ int cppc_get_perf_caps(int cpunum, struct cppc_perf_caps *perf_caps)
 	nom_perf = &cpc_desc->cpc_regs[NOMINAL_PERF];
 
 	/* Are any of the regs PCC ?*/
-	if ((highest_reg->cpc_entry.reg.space_id == ACPI_ADR_SPACE_PLATFORM_COMM) ||
-		(lowest_reg->cpc_entry.reg.space_id == ACPI_ADR_SPACE_PLATFORM_COMM) ||
-		(ref_perf->cpc_entry.reg.space_id == ACPI_ADR_SPACE_PLATFORM_COMM) ||
-		(nom_perf->cpc_entry.reg.space_id == ACPI_ADR_SPACE_PLATFORM_COMM)) {
-		spin_lock(&pcc_lock);
+	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_lock);
 		/* Ring doorbell once to update PCC subspace */
 		if (send_pcc_cmd(CMD_READ) < 0) {
 			ret = -EIO;
@@ -809,7 +863,7 @@ int cppc_get_perf_caps(int cpunum, struct cppc_perf_caps *perf_caps)
 
 out_err:
 	if (regs_in_pcc)
-		spin_unlock(&pcc_lock);
+		up_write(&pcc_lock);
 	return ret;
 }
 EXPORT_SYMBOL_GPL(cppc_get_perf_caps);
@@ -837,9 +891,8 @@ int cppc_get_perf_ctrs(int cpunum, struct cppc_perf_fb_ctrs *perf_fb_ctrs)
 	reference_reg = &cpc_desc->cpc_regs[REFERENCE_CTR];
 
 	/* Are any of the regs PCC ?*/
-	if ((delivered_reg->cpc_entry.reg.space_id == ACPI_ADR_SPACE_PLATFORM_COMM) ||
-		(reference_reg->cpc_entry.reg.space_id == ACPI_ADR_SPACE_PLATFORM_COMM)) {
-		spin_lock(&pcc_lock);
+	if (CPC_IN_PCC(delivered_reg) || CPC_IN_PCC(reference_reg)) {
+		down_write(&pcc_lock);
 		regs_in_pcc = 1;
 		/* Ring doorbell once to update PCC subspace */
 		if (send_pcc_cmd(CMD_READ) < 0) {
@@ -867,7 +920,7 @@ int cppc_get_perf_ctrs(int cpunum, struct cppc_perf_fb_ctrs *perf_fb_ctrs)
 
 out_err:
 	if (regs_in_pcc)
-		spin_unlock(&pcc_lock);
+		up_write(&pcc_lock);
 	return ret;
 }
 EXPORT_SYMBOL_GPL(cppc_get_perf_ctrs);
@@ -892,12 +945,36 @@ int cppc_set_perf(int cpu, struct cppc_perf_ctrls *perf_ctrls)
 
 	desired_reg = &cpc_desc->cpc_regs[DESIRED_PERF];
 
-	/* If this is PCC reg, check if channel is free before writing */
-	if (desired_reg->cpc_entry.reg.space_id == ACPI_ADR_SPACE_PLATFORM_COMM) {
-		spin_lock(&pcc_lock);
-		ret = check_pcc_chan();
-		if (ret)
-			goto busy_channel;
+	/*
+	 * This is Phase-I where we want to write to CPC registers
+	 * -> We want all CPUs to be able to execute this phase in parallel
+	 *
+	 * Since read_lock can be acquired by multiple CPUs simultaneously we
+	 * achieve that goal here
+	 */
+	if (CPC_IN_PCC(desired_reg)) {
+		down_read(&pcc_lock);	/* BEGIN Phase-I */
+		/*
+		 * If there are pending write commands i.e pending_pcc_write_cmd
+		 * is TRUE, then we know OSPM owns the channel as another CPU
+		 * has already checked for command completion bit and updated
+		 * the corresponding CPC registers
+		 */
+		if (!pending_pcc_write_cmd) {
+			ret = check_pcc_chan();
+			if (ret) {
+				up_read(&pcc_lock);
+				return ret;
+			}
+			/*
+			 * Update the pending_write to make sure a PCC CMD_READ
+			 * will not arrive and steal the channel during the
+			 * transition to write lock
+			 */
+			pending_pcc_write_cmd = TRUE;
+		}
+		cpc_desc->write_cmd_id = pcc_write_cnt;
+		cpc_desc->write_cmd_status = 0;
 	}
 
 	/*
@@ -906,15 +983,68 @@ int cppc_set_perf(int cpu, struct cppc_perf_ctrls *perf_ctrls)
 	 */
 	cpc_write(desired_reg, perf_ctrls->desired_perf);
 
-	/* Is this a PCC reg ?*/
-	if (desired_reg->cpc_entry.reg.space_id == ACPI_ADR_SPACE_PLATFORM_COMM) {
-		/* Ring doorbell so Remote can get our perf request. */
-		if (send_pcc_cmd(CMD_WRITE) < 0)
-			ret = -EIO;
+	if (CPC_IN_PCC(desired_reg))
+		up_read(&pcc_lock);	/* END Phase-I */
+	/*
+	 * This is Phase-II where we transfer the ownership of PCC to Platform
+	 *
+	 * Short Summary: Basically if we think of a group of cppc_set_perf
+	 * requests that happened in short overlapping interval. The last CPU to
+	 * come out of Phase-I will enter Phase-II and ring the doorbell.
+	 *
+	 * We have the following requirements for Phase-II:
+	 *     1. We want to execute Phase-II only when there are no CPUs
+	 * currently executing in Phase-I
+	 *     2. Once we start Phase-II we want to avoid all other CPUs from
+	 * entering Phase-I.
+	 *     3. We want only one CPU among all those who went through Phase-I
+	 * to run phase-II
+	 *
+	 * If write_trylock fails to get the lock and doesn't transfer the
+	 * PCC ownership to the platform, then one of the following will be TRUE
+	 *     1. There is at-least one CPU in Phase-I which will later execute
+	 * write_trylock, so the CPUs in Phase-I will be responsible for
+	 * executing the Phase-II.
+	 *     2. Some other CPU has beaten this CPU to successfully execute the
+	 * write_trylock and has already acquired the write_lock. We know for a
+	 * fact it(other CPU acquiring the write_lock) couldn't have happened
+	 * before this CPU's Phase-I as we held the read_lock.
+	 *     3. Some other CPU executing pcc CMD_READ has stolen the
+	 * down_write, in which case, send_pcc_cmd will check for pending
+	 * CMD_WRITE commands by checking the pending_pcc_write_cmd.
+	 * So this CPU can be certain that its request will be delivered
+	 *    So in all cases, this CPU knows that its request will be delivered
+	 * by another CPU and can return
+	 *
+	 * After getting the down_write we still need to check for
+	 * pending_pcc_write_cmd to take care of the following scenario
+	 *    The thread running this code could be scheduled out between
+	 * Phase-I and Phase-II. Before it is scheduled back on, another CPU
+	 * could have delivered the request to Platform by triggering the
+	 * doorbell and transferred the ownership of PCC to platform. So this
+	 * avoids triggering an unnecessary doorbell and more importantly before
+	 * triggering the doorbell it makes sure that the PCC channel ownership
+	 * is still with OSPM.
+	 *   pending_pcc_write_cmd can also be cleared by a different CPU, if
+	 * there was a pcc CMD_READ waiting on down_write and it steals the lock
+	 * before the pcc CMD_WRITE is completed. pcc_send_cmd checks for this
+	 * case during a CMD_READ and if there are pending writes it delivers
+	 * the write command before servicing the read command
+	 */
+	if (CPC_IN_PCC(desired_reg)) {
+		if (down_write_trylock(&pcc_lock)) {		/* BEGIN Phase-II */
+			/* Update only if there are pending write commands */
+			if (pending_pcc_write_cmd)
+				send_pcc_cmd(CMD_WRITE);
+			up_write(&pcc_lock);			/* END Phase-II */
+		} else
+			/* Wait until pcc_write_cnt is updated by send_pcc_cmd */
+			wait_event(pcc_write_wait_q,
+				cpc_desc->write_cmd_id != pcc_write_cnt);
+
+		/* send_pcc_cmd updates the status in case of failure */
+		ret = cpc_desc->write_cmd_status;
 	}
-busy_channel:
-	if (desired_reg->cpc_entry.reg.space_id == ACPI_ADR_SPACE_PLATFORM_COMM)
-		spin_unlock(&pcc_lock);
 	return ret;
 }
 EXPORT_SYMBOL_GPL(cppc_set_perf);
diff --git a/include/acpi/cppc_acpi.h b/include/acpi/cppc_acpi.h
index 36ff5c6..d2101bc 100644
--- a/include/acpi/cppc_acpi.h
+++ b/include/acpi/cppc_acpi.h
@@ -61,6 +61,8 @@ struct cpc_desc {
 	int num_entries;
 	int version;
 	int cpu_id;
+	int write_cmd_status;
+	int write_cmd_id;
 	struct cpc_register_resource cpc_regs[MAX_CPC_REG_ENT];
 	struct acpi_psd_package domain_info;
 };
-- 
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.


^ permalink raw reply related	[flat|nested] 12+ messages in thread

* [PATCH V3 4/7] ACPI/CPPC: set a non-zero value for transition_latency
  2016-08-16 20:39 [PATCH V3 0/7] CPPC enhancements Prashanth Prakash
                   ` (2 preceding siblings ...)
  2016-08-16 20:39 ` [PATCH V3 3/7] ACPI/CPPC: support for batching CPPC requests Prashanth Prakash
@ 2016-08-16 20:39 ` Prashanth Prakash
  2016-08-16 20:39 ` [PATCH V3 5/7] ACPI/CPPC: add sysfs support to compute delivered performance Prashanth Prakash
                   ` (4 subsequent siblings)
  8 siblings, 0 replies; 12+ messages in thread
From: Prashanth Prakash @ 2016-08-16 20:39 UTC (permalink / raw)
  To: linux-acpi; +Cc: rjw, alexey.klimov, hotran, cov, Prashanth Prakash

Compute the expected transition latency for frequency transitions
using the values from the PCCT tables when the desired perf
register is in PCC.

Cc: "Rafael J. Wysocki" <rjw@rjwysocki.net>
Signed-off-by: Prashanth Prakash <pprakash@codeaurora.org>
Reviewed-by: Alexey Klimov <alexey.klimov@arm.com>
---
 drivers/acpi/cppc_acpi.c       | 46 ++++++++++++++++++++++++++++++++++++++++--
 drivers/cpufreq/cppc_cpufreq.c |  1 +
 include/acpi/cppc_acpi.h       |  1 +
 3 files changed, 46 insertions(+), 2 deletions(-)

diff --git a/drivers/acpi/cppc_acpi.c b/drivers/acpi/cppc_acpi.c
index 5623fca..6c54a8f 100644
--- a/drivers/acpi/cppc_acpi.c
+++ b/drivers/acpi/cppc_acpi.c
@@ -85,7 +85,7 @@ static void __iomem *pcc_comm_addr;
 static int pcc_subspace_idx = -1;
 static bool pcc_channel_acquired;
 static ktime_t deadline;
-static unsigned int pcc_mpar, pcc_mrtt;
+static unsigned int pcc_mpar, pcc_mrtt, pcc_nominal;
 
 /* pcc mapped address + header size + offset within PCC subspace */
 #define GET_PCC_VADDR(offs) (pcc_comm_addr + 0x8 + (offs))
@@ -473,7 +473,6 @@ static int register_pcc_channel(int pcc_subspace_idx)
 			return -ENODEV;
 		}
 
-
 		/*
 		 * cppc_ss->latency is just a Nominal value. In reality
 		 * the remote processor could be much slower to reply.
@@ -483,6 +482,7 @@ static int register_pcc_channel(int pcc_subspace_idx)
 		deadline = ns_to_ktime(usecs_lat * NSEC_PER_USEC);
 		pcc_mrtt = cppc_ss->min_turnaround_time;
 		pcc_mpar = cppc_ss->max_access_rate;
+		pcc_nominal = cppc_ss->latency;
 
 		pcc_comm_addr = acpi_os_ioremap(cppc_ss->base_address, cppc_ss->length);
 		if (!pcc_comm_addr) {
@@ -1048,3 +1048,45 @@ int cppc_set_perf(int cpu, struct cppc_perf_ctrls *perf_ctrls)
 	return ret;
 }
 EXPORT_SYMBOL_GPL(cppc_set_perf);
+
+/**
+ * cppc_get_transition_latency - returns frequency transition latency in ns
+ *
+ * ACPI CPPC does not explicitly specifiy how a platform can specify the
+ * transition latency for perfromance change requests. The closest we have
+ * is the timing information from the PCCT tables which provides the info
+ * on the number and frequency of PCC commands the platform can handle.
+ */
+unsigned int cppc_get_transition_latency(int cpu_num)
+{
+	/*
+	 * Expected transition latency is based on the PCCT timing values
+	 * Below are definition from ACPI spec:
+	 * pcc_nominal- Expected latency to process a command, in microseconds
+	 * pcc_mpar   - The maximum number of periodic requests that the subspace
+	 *              channel can support, reported in commands per minute. 0
+	 *              indicates no limitation.
+	 * pcc_mrtt   - The minimum amount of time that OSPM must wait after the
+	 *              completion of a command before issuing the next command,
+	 *              in microseconds.
+	 */
+	unsigned int latency_ns = 0;
+	struct cpc_desc *cpc_desc;
+	struct cpc_register_resource *desired_reg;
+
+	cpc_desc = per_cpu(cpc_desc_ptr, cpu_num);
+	if (!cpc_desc)
+		return CPUFREQ_ETERNAL;
+
+	desired_reg = &cpc_desc->cpc_regs[DESIRED_PERF];
+	if (!CPC_IN_PCC(desired_reg))
+		return CPUFREQ_ETERNAL;
+
+	if (pcc_mpar)
+		latency_ns = 60 * (1000 * 1000 * 1000 / pcc_mpar);
+
+	latency_ns = max(latency_ns, (pcc_nominal + pcc_mrtt) * 1000);
+
+	return latency_ns;
+}
+EXPORT_SYMBOL_GPL(cppc_get_transition_latency);
diff --git a/drivers/cpufreq/cppc_cpufreq.c b/drivers/cpufreq/cppc_cpufreq.c
index 8882b8e..e6a3359 100644
--- a/drivers/cpufreq/cppc_cpufreq.c
+++ b/drivers/cpufreq/cppc_cpufreq.c
@@ -98,6 +98,7 @@ static int cppc_cpufreq_cpu_init(struct cpufreq_policy *policy)
 	policy->max = cpu->perf_caps.highest_perf;
 	policy->cpuinfo.min_freq = policy->min;
 	policy->cpuinfo.max_freq = policy->max;
+	policy->cpuinfo.transition_latency = cppc_get_transition_latency(cpu_num);
 	policy->shared_type = cpu->shared_type;
 
 	if (policy->shared_type == CPUFREQ_SHARED_TYPE_ANY)
diff --git a/include/acpi/cppc_acpi.h b/include/acpi/cppc_acpi.h
index d2101bc..42cbeb9 100644
--- a/include/acpi/cppc_acpi.h
+++ b/include/acpi/cppc_acpi.h
@@ -131,5 +131,6 @@ extern int cppc_get_perf_ctrs(int cpu, struct cppc_perf_fb_ctrs *perf_fb_ctrs);
 extern int cppc_set_perf(int cpu, struct cppc_perf_ctrls *perf_ctrls);
 extern int cppc_get_perf_caps(int cpu, struct cppc_perf_caps *caps);
 extern int acpi_get_psd_map(struct cpudata **);
+extern unsigned int cppc_get_transition_latency(int cpu);
 
 #endif /* _CPPC_ACPI_H*/
-- 
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.


^ permalink raw reply related	[flat|nested] 12+ messages in thread

* [PATCH V3 5/7] ACPI/CPPC: add sysfs support to compute delivered performance
  2016-08-16 20:39 [PATCH V3 0/7] CPPC enhancements Prashanth Prakash
                   ` (3 preceding siblings ...)
  2016-08-16 20:39 ` [PATCH V3 4/7] ACPI/CPPC: set a non-zero value for transition_latency Prashanth Prakash
@ 2016-08-16 20:39 ` Prashanth Prakash
  2016-08-16 20:39 ` [PATCH V3 6/7] ACPI/CPPC: move all PCC related information into pcc_data Prashanth Prakash
                   ` (3 subsequent siblings)
  8 siblings, 0 replies; 12+ messages in thread
From: Prashanth Prakash @ 2016-08-16 20:39 UTC (permalink / raw)
  To: linux-acpi
  Cc: rjw, alexey.klimov, hotran, cov, Ashwin Chaugule, Prashanth Prakash

From: Ashwin Chaugule <ashwin.chaugule@linaro.org>

The CPPC tables contain entries for per CPU feedback counters which
allows us to compute the delivered performance over a given interval
of time.

The math for delivered performance per the CPPCv5.0+ spec is:
  reference perf * delta(delivered perf ctr)/delta(ref perf ctr)

Maintaining deltas of the counters in the kernel is messy, as it
depends on when the reads are triggered. (e.g. via the cpufreq
->get() interface). Also the ->get() interace only returns one
value, so cant return raw values. So instead, leave it to userspace
to keep track of raw values and do its math for CPUs it cares about.

delivered and reference perf counters are exposed via the same
sysfs file to avoid the potential "skid", if these values are read
individually from userspace.

Cc: "Rafael J. Wysocki" <rjw@rjwysocki.net>
Signed-off-by: Prashanth Prakash <pprakash@codeaurora.org>
Signed-off-by: Ashwin Chaugule <ashwin.chaugule@linaro.org>
---
 drivers/acpi/cppc_acpi.c | 135 ++++++++++++++++++++++++++++++++++++++++-------
 include/acpi/cppc_acpi.h |   6 +--
 2 files changed, 120 insertions(+), 21 deletions(-)

diff --git a/drivers/acpi/cppc_acpi.c b/drivers/acpi/cppc_acpi.c
index 6c54a8f..f00fac3 100644
--- a/drivers/acpi/cppc_acpi.c
+++ b/drivers/acpi/cppc_acpi.c
@@ -95,6 +95,17 @@ static unsigned int pcc_mpar, pcc_mrtt, pcc_nominal;
 				(cpc)->cpc_entry.reg.space_id ==	\
 				ACPI_ADR_SPACE_PLATFORM_COMM)
 
+/* Evalutes to True if reg is a NULL register descriptor */
+#define IS_NULL_REG(reg) ((reg)->space_id ==  ACPI_ADR_SPACE_SYSTEM_MEMORY && \
+				(reg)->address == 0 &&			\
+				(reg)->bit_width == 0 &&		\
+				(reg)->bit_offset == 0 &&		\
+				(reg)->access_width == 0)
+
+/* Evalutes to True if an optional cpc field is supported */
+#define CPC_SUPPORTED(cpc) ((cpc)->type == ACPI_TYPE_INTEGER ?		\
+				!!(cpc)->cpc_entry.int_value :		\
+				!IS_NULL_REG(&(cpc)->cpc_entry.reg))
 /*
  * Arbitrary Retries in case the remote processor is slow to respond
  * to PCC commands. Keeping it high enough to cover emulators where
@@ -102,6 +113,71 @@ static unsigned int pcc_mpar, pcc_mrtt, pcc_nominal;
  */
 #define NUM_RETRIES 500
 
+struct cppc_attr {
+	struct attribute attr;
+	ssize_t (*show)(struct kobject *kobj,
+			struct attribute *attr, char *buf);
+	ssize_t (*store)(struct kobject *kobj,
+			struct attribute *attr, const char *c, ssize_t count);
+};
+
+#define define_one_cppc_ro(_name)		\
+static struct cppc_attr _name =			\
+__ATTR(_name, 0444, show_##_name, NULL)
+
+#define to_cpc_desc(a) container_of(a, struct cpc_desc, kobj)
+
+static ssize_t show_feedback_ctrs(struct kobject *kobj,
+		struct attribute *attr, char *buf)
+{
+	struct cpc_desc *cpc_ptr = to_cpc_desc(kobj);
+	struct cppc_perf_fb_ctrs fb_ctrs = {0};
+
+	cppc_get_perf_ctrs(cpc_ptr->cpu_id, &fb_ctrs);
+
+	return scnprintf(buf, PAGE_SIZE, "ref:%llu del:%llu\n",
+			fb_ctrs.reference, fb_ctrs.delivered);
+}
+define_one_cppc_ro(feedback_ctrs);
+
+static ssize_t show_reference_perf(struct kobject *kobj,
+		struct attribute *attr, char *buf)
+{
+	struct cpc_desc *cpc_ptr = to_cpc_desc(kobj);
+	struct cppc_perf_fb_ctrs fb_ctrs = {0};
+
+	cppc_get_perf_ctrs(cpc_ptr->cpu_id, &fb_ctrs);
+
+	return scnprintf(buf, PAGE_SIZE, "%llu\n",
+			fb_ctrs.reference_perf);
+}
+define_one_cppc_ro(reference_perf);
+
+static ssize_t show_wraparound_time(struct kobject *kobj,
+				struct attribute *attr, char *buf)
+{
+	struct cpc_desc *cpc_ptr = to_cpc_desc(kobj);
+	struct cppc_perf_fb_ctrs fb_ctrs = {0};
+
+	cppc_get_perf_ctrs(cpc_ptr->cpu_id, &fb_ctrs);
+
+	return scnprintf(buf, PAGE_SIZE, "%llu\n", fb_ctrs.ctr_wrap_time);
+
+}
+define_one_cppc_ro(wraparound_time);
+
+static struct attribute *cppc_attrs[] = {
+	&feedback_ctrs.attr,
+	&reference_perf.attr,
+	&wraparound_time.attr,
+	NULL
+};
+
+static struct kobj_type cppc_ktype = {
+	.sysfs_ops = &kobj_sysfs_ops,
+	.default_attrs = cppc_attrs,
+};
+
 static int check_pcc_chan(void)
 {
 	int ret = -EIO;
@@ -555,6 +631,7 @@ int acpi_cppc_processor_probe(struct acpi_processor *pr)
 	union acpi_object *out_obj, *cpc_obj;
 	struct cpc_desc *cpc_ptr;
 	struct cpc_reg *gas_t;
+	struct device *cpu_dev;
 	acpi_handle handle = pr->handle;
 	unsigned int num_ent, i, cpc_rev;
 	acpi_status status;
@@ -678,6 +755,16 @@ int acpi_cppc_processor_probe(struct acpi_processor *pr)
 	/* Everything looks okay */
 	pr_debug("Parsed CPC struct for CPU: %d\n", pr->id);
 
+	/* Add per logical CPU nodes for reading its feedback counters. */
+	cpu_dev = get_cpu_device(pr->id);
+	if (!cpu_dev)
+		goto out_free;
+
+	ret = kobject_init_and_add(&cpc_ptr->kobj, &cppc_ktype, &cpu_dev->kobj,
+			"acpi_cppc");
+	if (ret)
+		goto out_free;
+
 	kfree(output.pointer);
 	return 0;
 
@@ -708,6 +795,7 @@ void acpi_cppc_processor_exit(struct acpi_processor *pr)
 	struct cpc_desc *cpc_ptr;
 	unsigned int i;
 	void __iomem *addr;
+
 	cpc_ptr = per_cpu(cpc_desc_ptr, pr->id);
 
 	/* Free all the mapped sys mem areas for this CPU */
@@ -717,6 +805,7 @@ void acpi_cppc_processor_exit(struct acpi_processor *pr)
 			iounmap(addr);
 	}
 
+	kobject_put(&cpc_ptr->kobj);
 	kfree(cpc_ptr);
 }
 EXPORT_SYMBOL_GPL(acpi_cppc_processor_exit);
@@ -818,7 +907,7 @@ 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, ref, nom;
+	u64 high, low, nom;
 	int ret = 0, regs_in_pcc = 0;
 
 	if (!cpc_desc) {
@@ -849,15 +938,9 @@ int cppc_get_perf_caps(int cpunum, struct cppc_perf_caps *perf_caps)
 	cpc_read(lowest_reg, &low);
 	perf_caps->lowest_perf = low;
 
-	cpc_read(ref_perf, &ref);
-	perf_caps->reference_perf = ref;
-
 	cpc_read(nom_perf, &nom);
 	perf_caps->nominal_perf = nom;
 
-	if (!ref)
-		perf_caps->reference_perf = perf_caps->nominal_perf;
-
 	if (!high || !low || !nom)
 		ret = -EFAULT;
 
@@ -878,8 +961,9 @@ EXPORT_SYMBOL_GPL(cppc_get_perf_caps);
 int cppc_get_perf_ctrs(int cpunum, struct cppc_perf_fb_ctrs *perf_fb_ctrs)
 {
 	struct cpc_desc *cpc_desc = per_cpu(cpc_desc_ptr, cpunum);
-	struct cpc_register_resource *delivered_reg, *reference_reg;
-	u64 delivered, reference;
+	struct cpc_register_resource *delivered_reg, *reference_reg,
+		*ref_perf_reg, *ctr_wrap_reg;
+	u64 delivered, reference, ref_perf, ctr_wrap_time;
 	int ret = 0, regs_in_pcc = 0;
 
 	if (!cpc_desc) {
@@ -889,9 +973,19 @@ int cppc_get_perf_ctrs(int cpunum, struct cppc_perf_fb_ctrs *perf_fb_ctrs)
 
 	delivered_reg = &cpc_desc->cpc_regs[DELIVERED_CTR];
 	reference_reg = &cpc_desc->cpc_regs[REFERENCE_CTR];
+	ref_perf_reg = &cpc_desc->cpc_regs[REFERENCE_PERF];
+	ctr_wrap_reg = &cpc_desc->cpc_regs[CTR_WRAP_TIME];
+
+	/*
+	 * If refernce perf register is not supported then we should
+	 * use the nominal perf value
+	 */
+	if (!CPC_SUPPORTED(ref_perf_reg))
+		ref_perf_reg = &cpc_desc->cpc_regs[NOMINAL_PERF];
 
 	/* Are any of the regs PCC ?*/
-	if (CPC_IN_PCC(delivered_reg) || CPC_IN_PCC(reference_reg)) {
+	if (CPC_IN_PCC(delivered_reg) || CPC_IN_PCC(reference_reg) ||
+		CPC_IN_PCC(ctr_wrap_reg) || CPC_IN_PCC(ref_perf_reg)) {
 		down_write(&pcc_lock);
 		regs_in_pcc = 1;
 		/* Ring doorbell once to update PCC subspace */
@@ -903,21 +997,26 @@ int cppc_get_perf_ctrs(int cpunum, struct cppc_perf_fb_ctrs *perf_fb_ctrs)
 
 	cpc_read(delivered_reg, &delivered);
 	cpc_read(reference_reg, &reference);
+	cpc_read(ref_perf_reg, &ref_perf);
+
+	/*
+	 * Per spec, if ctr_wrap_time optional register is unsupported, then the
+	 * performance counters are assumed to never wrap during the lifetime of
+	 * platform
+	 */
+	ctr_wrap_time = (u64)(~((u64)0));
+	if (CPC_SUPPORTED(ctr_wrap_reg))
+		cpc_read(ctr_wrap_reg, &ctr_wrap_time);
 
-	if (!delivered || !reference) {
+	if (!delivered || !reference ||	!ref_perf) {
 		ret = -EFAULT;
 		goto out_err;
 	}
 
 	perf_fb_ctrs->delivered = delivered;
 	perf_fb_ctrs->reference = reference;
-
-	perf_fb_ctrs->delivered -= perf_fb_ctrs->prev_delivered;
-	perf_fb_ctrs->reference -= perf_fb_ctrs->prev_reference;
-
-	perf_fb_ctrs->prev_delivered = delivered;
-	perf_fb_ctrs->prev_reference = reference;
-
+	perf_fb_ctrs->reference_perf = ref_perf;
+	perf_fb_ctrs->ctr_wrap_time = ctr_wrap_time;
 out_err:
 	if (regs_in_pcc)
 		up_write(&pcc_lock);
diff --git a/include/acpi/cppc_acpi.h b/include/acpi/cppc_acpi.h
index 42cbeb9..f50b533 100644
--- a/include/acpi/cppc_acpi.h
+++ b/include/acpi/cppc_acpi.h
@@ -65,6 +65,7 @@ struct cpc_desc {
 	int write_cmd_id;
 	struct cpc_register_resource cpc_regs[MAX_CPC_REG_ENT];
 	struct acpi_psd_package domain_info;
+	struct kobject kobj;
 };
 
 /* These are indexes into the per-cpu cpc_regs[]. Order is important. */
@@ -99,7 +100,6 @@ enum cppc_regs {
 struct cppc_perf_caps {
 	u32 highest_perf;
 	u32 nominal_perf;
-	u32 reference_perf;
 	u32 lowest_perf;
 };
 
@@ -111,9 +111,9 @@ struct cppc_perf_ctrls {
 
 struct cppc_perf_fb_ctrs {
 	u64 reference;
-	u64 prev_reference;
 	u64 delivered;
-	u64 prev_delivered;
+	u64 reference_perf;
+	u64 ctr_wrap_time;
 };
 
 /* Per CPU container for runtime CPPC management. */
-- 
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.


^ permalink raw reply related	[flat|nested] 12+ messages in thread

* [PATCH V3 6/7] ACPI/CPPC: move all PCC related information into pcc_data
  2016-08-16 20:39 [PATCH V3 0/7] CPPC enhancements Prashanth Prakash
                   ` (4 preceding siblings ...)
  2016-08-16 20:39 ` [PATCH V3 5/7] ACPI/CPPC: add sysfs support to compute delivered performance Prashanth Prakash
@ 2016-08-16 20:39 ` Prashanth Prakash
  2016-08-16 20:39 ` [PATCH V3 7/7] ACPI/CPPC: check for error bit in PCC status field Prashanth Prakash
                   ` (2 subsequent siblings)
  8 siblings, 0 replies; 12+ messages in thread
From: Prashanth Prakash @ 2016-08-16 20:39 UTC (permalink / raw)
  To: linux-acpi; +Cc: rjw, alexey.klimov, hotran, cov, Prashanth Prakash

There are several global variables in cppc driver that are related
to PCC channel used for CPPC. This patch collects all such
information into a single consolidated structure(cppc_pcc_data).

Cc: "Rafael J. Wysocki" <rjw@rjwysocki.net>
Signed-off-by: Prashanth Prakash <pprakash@codeaurora.org>
---
 drivers/acpi/cppc_acpi.c | 167 ++++++++++++++++++++++++-----------------------
 1 file changed, 87 insertions(+), 80 deletions(-)

diff --git a/drivers/acpi/cppc_acpi.c b/drivers/acpi/cppc_acpi.c
index f00fac3..80c123f 100644
--- a/drivers/acpi/cppc_acpi.c
+++ b/drivers/acpi/cppc_acpi.c
@@ -45,30 +45,41 @@
 
 #include <acpi/cppc_acpi.h>
 
-/*
- * Lock to provide controlled access to the PCC channel.
- *
- * For performance critical usecases(currently cppc_set_perf)
- *	We need to take read_lock and check if channel belongs to OSPM before
- * reading or writing to PCC subspace
- *	We need to take write_lock before transferring the channel ownership to
- * the platform via a Doorbell
- *	This allows us to batch a number of CPPC requests if they happen to
- * originate in about the same time
- *
- * For non-performance critical usecases(init)
- *	Take write_lock for all purposes which gives exclusive access
- */
-static DECLARE_RWSEM(pcc_lock);
+struct cppc_pcc_data {
+	struct mbox_chan *pcc_channel;
+	void __iomem *pcc_comm_addr;
+	int pcc_subspace_idx;
+	bool pcc_channel_acquired;
+	ktime_t deadline;
+	unsigned int pcc_mpar, pcc_mrtt, pcc_nominal;
 
-/* Indicates if there are any pending/batched PCC write commands */
-static bool pending_pcc_write_cmd;
+	bool pending_pcc_write_cmd;	/* Any pending/batched PCC write cmds? */
+	unsigned int pcc_write_cnt;	/* Running count of PCC write commands */
 
-/* Wait queue for CPUs whose requests were batched */
-static DECLARE_WAIT_QUEUE_HEAD(pcc_write_wait_q);
+	/*
+	 * Lock to provide controlled access to the PCC channel.
+	 *
+	 * For performance critical usecases(currently cppc_set_perf)
+	 *	We need to take read_lock and check if channel belongs to OSPM
+	 * before reading or writing to PCC subspace
+	 *	We need to take write_lock before transferring the channel
+	 * ownership to the platform via a Doorbell
+	 *	This allows us to batch a number of CPPC requests if they happen
+	 * to originate in about the same time
+	 *
+	 * For non-performance critical usecases(init)
+	 *	Take write_lock for all purposes which gives exclusive access
+	 */
+	struct rw_semaphore pcc_lock;
+
+	/* Wait queue for CPUs whose requests were batched */
+	wait_queue_head_t pcc_write_wait_q;
+};
 
-/* Used to identify if a batched request is delivered to platform */
-static unsigned int pcc_write_cnt;
+/* Structure to represent the single PCC channel */
+static struct cppc_pcc_data pcc_data = {
+	.pcc_subspace_idx = -1,
+};
 
 /*
  * The cpc_desc structure contains the ACPI register details
@@ -79,16 +90,8 @@ static unsigned int pcc_write_cnt;
  */
 static DEFINE_PER_CPU(struct cpc_desc *, cpc_desc_ptr);
 
-/* This layer handles all the PCC specifics for CPPC. */
-static struct mbox_chan *pcc_channel;
-static void __iomem *pcc_comm_addr;
-static int pcc_subspace_idx = -1;
-static bool pcc_channel_acquired;
-static ktime_t deadline;
-static unsigned int pcc_mpar, pcc_mrtt, pcc_nominal;
-
 /* pcc mapped address + header size + offset within PCC subspace */
-#define GET_PCC_VADDR(offs) (pcc_comm_addr + 0x8 + (offs))
+#define GET_PCC_VADDR(offs) (pcc_data.pcc_comm_addr + 0x8 + (offs))
 
 /* Check if a CPC regsiter is in PCC */
 #define CPC_IN_PCC(cpc) ((cpc)->type == ACPI_TYPE_BUFFER &&		\
@@ -181,8 +184,8 @@ static struct kobj_type cppc_ktype = {
 static int check_pcc_chan(void)
 {
 	int ret = -EIO;
-	struct acpi_pcct_shared_memory __iomem *generic_comm_base = pcc_comm_addr;
-	ktime_t next_deadline = ktime_add(ktime_get(), deadline);
+	struct acpi_pcct_shared_memory __iomem *generic_comm_base = pcc_data.pcc_comm_addr;
+	ktime_t next_deadline = ktime_add(ktime_get(), pcc_data.deadline);
 
 	/* Retry in case the remote processor was too slow to catch up. */
 	while (!ktime_after(ktime_get(), next_deadline)) {
@@ -213,7 +216,7 @@ static int send_pcc_cmd(u16 cmd)
 {
 	int ret = -EIO, i;
 	struct acpi_pcct_shared_memory *generic_comm_base =
-		(struct acpi_pcct_shared_memory *) pcc_comm_addr;
+		(struct acpi_pcct_shared_memory *) pcc_data.pcc_comm_addr;
 	static ktime_t last_cmd_cmpl_time, last_mpar_reset;
 	static int mpar_count;
 	unsigned int time_delta;
@@ -228,24 +231,24 @@ static int send_pcc_cmd(u16 cmd)
 		 * before write completion, so first send a WRITE command to
 		 * platform
 		 */
-		if (pending_pcc_write_cmd)
+		if (pcc_data.pending_pcc_write_cmd)
 			send_pcc_cmd(CMD_WRITE);
 
 		ret = check_pcc_chan();
 		if (ret)
 			goto end;
 	} else /* CMD_WRITE */
-		pending_pcc_write_cmd = FALSE;
+		pcc_data.pending_pcc_write_cmd = FALSE;
 
 	/*
 	 * 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) {
+	if (pcc_data.pcc_mrtt) {
 		time_delta = ktime_us_delta(ktime_get(), last_cmd_cmpl_time);
-		if (pcc_mrtt > time_delta)
-			udelay(pcc_mrtt - time_delta);
+		if (pcc_data.pcc_mrtt > time_delta)
+			udelay(pcc_data.pcc_mrtt - time_delta);
 	}
 
 	/*
@@ -259,7 +262,7 @@ static int send_pcc_cmd(u16 cmd)
 	 * not send the request to the platform after hitting the MPAR limit in
 	 * any 60s window
 	 */
-	if (pcc_mpar) {
+	if (pcc_data.pcc_mpar) {
 		if (mpar_count == 0) {
 			time_delta = ktime_ms_delta(ktime_get(), last_mpar_reset);
 			if (time_delta < 60 * MSEC_PER_SEC) {
@@ -268,7 +271,7 @@ static int send_pcc_cmd(u16 cmd)
 				goto end;
 			}
 			last_mpar_reset = ktime_get();
-			mpar_count = pcc_mpar;
+			mpar_count = pcc_data.pcc_mpar;
 		}
 		mpar_count--;
 	}
@@ -280,7 +283,7 @@ static int send_pcc_cmd(u16 cmd)
 	writew_relaxed(0, &generic_comm_base->status);
 
 	/* Ring doorbell */
-	ret = mbox_send_message(pcc_channel, &cmd);
+	ret = mbox_send_message(pcc_data.pcc_channel, &cmd);
 	if (ret < 0) {
 		pr_err("Err sending PCC mbox message. cmd:%d, ret:%d\n",
 				cmd, ret);
@@ -299,13 +302,13 @@ static int send_pcc_cmd(u16 cmd)
 	 * command for proper handling of MRTT, so we need to check
 	 * for pcc_mrtt in addition to CMD_READ
 	 */
-	if (cmd == CMD_READ || pcc_mrtt) {
+	if (cmd == CMD_READ || pcc_data.pcc_mrtt) {
 		ret = check_pcc_chan();
-		if (pcc_mrtt)
+		if (pcc_data.pcc_mrtt)
 			last_cmd_cmpl_time = ktime_get();
 	}
 
-	mbox_client_txdone(pcc_channel, ret);
+	mbox_client_txdone(pcc_data.pcc_channel, ret);
 
 end:
 	if (cmd == CMD_WRITE) {
@@ -315,12 +318,12 @@ end:
 				if (!desc)
 					continue;
 
-				if (desc->write_cmd_id == pcc_write_cnt)
+				if (desc->write_cmd_id == pcc_data.pcc_write_cnt)
 					desc->write_cmd_status = ret;
 			}
 		}
-		pcc_write_cnt++;
-		wake_up_all(&pcc_write_wait_q);
+		pcc_data.pcc_write_cnt++;
+		wake_up_all(&pcc_data.pcc_write_wait_q);
 	}
 
 	return ret;
@@ -528,10 +531,10 @@ static int register_pcc_channel(int pcc_subspace_idx)
 	u64 usecs_lat;
 
 	if (pcc_subspace_idx >= 0) {
-		pcc_channel = pcc_mbox_request_channel(&cppc_mbox_cl,
+		pcc_data.pcc_channel = pcc_mbox_request_channel(&cppc_mbox_cl,
 				pcc_subspace_idx);
 
-		if (IS_ERR(pcc_channel)) {
+		if (IS_ERR(pcc_data.pcc_channel)) {
 			pr_err("Failed to find PCC communication channel\n");
 			return -ENODEV;
 		}
@@ -542,7 +545,7 @@ static int register_pcc_channel(int pcc_subspace_idx)
 		 * PCC channels) and stored pointers to the
 		 * subspace communication region in con_priv.
 		 */
-		cppc_ss = pcc_channel->con_priv;
+		cppc_ss = (pcc_data.pcc_channel)->con_priv;
 
 		if (!cppc_ss) {
 			pr_err("No PCC subspace found for CPPC\n");
@@ -555,19 +558,19 @@ static int register_pcc_channel(int pcc_subspace_idx)
 		 * So add an arbitrary amount of wait on top of Nominal.
 		 */
 		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_nominal = cppc_ss->latency;
+		pcc_data.deadline = ns_to_ktime(usecs_lat * NSEC_PER_USEC);
+		pcc_data.pcc_mrtt = cppc_ss->min_turnaround_time;
+		pcc_data.pcc_mpar = cppc_ss->max_access_rate;
+		pcc_data.pcc_nominal = cppc_ss->latency;
 
-		pcc_comm_addr = acpi_os_ioremap(cppc_ss->base_address, cppc_ss->length);
-		if (!pcc_comm_addr) {
+		pcc_data.pcc_comm_addr = acpi_os_ioremap(cppc_ss->base_address, cppc_ss->length);
+		if (!pcc_data.pcc_comm_addr) {
 			pr_err("Failed to ioremap PCC comm region mem\n");
 			return -ENOMEM;
 		}
 
 		/* Set flag so that we dont come here for each CPU. */
-		pcc_channel_acquired = true;
+		pcc_data.pcc_channel_acquired = true;
 	}
 
 	return 0;
@@ -706,9 +709,9 @@ int acpi_cppc_processor_probe(struct acpi_processor *pr)
 			 * so extract it only once.
 			 */
 			if (gas_t->space_id == ACPI_ADR_SPACE_PLATFORM_COMM) {
-				if (pcc_subspace_idx < 0)
-					pcc_subspace_idx = gas_t->access_width;
-				else if (pcc_subspace_idx != gas_t->access_width) {
+				if (pcc_data.pcc_subspace_idx < 0)
+					pcc_data.pcc_subspace_idx = gas_t->access_width;
+				else if (pcc_data.pcc_subspace_idx != gas_t->access_width) {
 					pr_debug("Mismatched PCC ids.\n");
 					goto out_free;
 				}
@@ -743,10 +746,13 @@ int acpi_cppc_processor_probe(struct acpi_processor *pr)
 		goto out_free;
 
 	/* Register PCC channel once for all CPUs. */
-	if (!pcc_channel_acquired) {
-		ret = register_pcc_channel(pcc_subspace_idx);
+	if (!pcc_data.pcc_channel_acquired) {
+		ret = register_pcc_channel(pcc_data.pcc_subspace_idx);
 		if (ret)
 			goto out_free;
+
+		init_rwsem(&pcc_data.pcc_lock);
+		init_waitqueue_head(&pcc_data.pcc_write_wait_q);
 	}
 
 	/* Plug PSD data into this CPUs CPC descriptor. */
@@ -924,7 +930,7 @@ int cppc_get_perf_caps(int cpunum, struct cppc_perf_caps *perf_caps)
 	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_lock);
+		down_write(&pcc_data.pcc_lock);
 		/* Ring doorbell once to update PCC subspace */
 		if (send_pcc_cmd(CMD_READ) < 0) {
 			ret = -EIO;
@@ -946,7 +952,7 @@ int cppc_get_perf_caps(int cpunum, struct cppc_perf_caps *perf_caps)
 
 out_err:
 	if (regs_in_pcc)
-		up_write(&pcc_lock);
+		up_write(&pcc_data.pcc_lock);
 	return ret;
 }
 EXPORT_SYMBOL_GPL(cppc_get_perf_caps);
@@ -986,7 +992,7 @@ int cppc_get_perf_ctrs(int cpunum, struct cppc_perf_fb_ctrs *perf_fb_ctrs)
 	/* Are any of the regs PCC ?*/
 	if (CPC_IN_PCC(delivered_reg) || CPC_IN_PCC(reference_reg) ||
 		CPC_IN_PCC(ctr_wrap_reg) || CPC_IN_PCC(ref_perf_reg)) {
-		down_write(&pcc_lock);
+		down_write(&pcc_data.pcc_lock);
 		regs_in_pcc = 1;
 		/* Ring doorbell once to update PCC subspace */
 		if (send_pcc_cmd(CMD_READ) < 0) {
@@ -1019,7 +1025,7 @@ int cppc_get_perf_ctrs(int cpunum, struct cppc_perf_fb_ctrs *perf_fb_ctrs)
 	perf_fb_ctrs->ctr_wrap_time = ctr_wrap_time;
 out_err:
 	if (regs_in_pcc)
-		up_write(&pcc_lock);
+		up_write(&pcc_data.pcc_lock);
 	return ret;
 }
 EXPORT_SYMBOL_GPL(cppc_get_perf_ctrs);
@@ -1052,17 +1058,17 @@ int cppc_set_perf(int cpu, struct cppc_perf_ctrls *perf_ctrls)
 	 * achieve that goal here
 	 */
 	if (CPC_IN_PCC(desired_reg)) {
-		down_read(&pcc_lock);	/* BEGIN Phase-I */
+		down_read(&pcc_data.pcc_lock);	/* BEGIN Phase-I */
 		/*
 		 * If there are pending write commands i.e pending_pcc_write_cmd
 		 * is TRUE, then we know OSPM owns the channel as another CPU
 		 * has already checked for command completion bit and updated
 		 * the corresponding CPC registers
 		 */
-		if (!pending_pcc_write_cmd) {
+		if (!pcc_data.pending_pcc_write_cmd) {
 			ret = check_pcc_chan();
 			if (ret) {
-				up_read(&pcc_lock);
+				up_read(&pcc_data.pcc_lock);
 				return ret;
 			}
 			/*
@@ -1070,9 +1076,9 @@ int cppc_set_perf(int cpu, struct cppc_perf_ctrls *perf_ctrls)
 			 * will not arrive and steal the channel during the
 			 * transition to write lock
 			 */
-			pending_pcc_write_cmd = TRUE;
+			pcc_data.pending_pcc_write_cmd = TRUE;
 		}
-		cpc_desc->write_cmd_id = pcc_write_cnt;
+		cpc_desc->write_cmd_id = pcc_data.pcc_write_cnt;
 		cpc_desc->write_cmd_status = 0;
 	}
 
@@ -1083,7 +1089,7 @@ int cppc_set_perf(int cpu, struct cppc_perf_ctrls *perf_ctrls)
 	cpc_write(desired_reg, perf_ctrls->desired_perf);
 
 	if (CPC_IN_PCC(desired_reg))
-		up_read(&pcc_lock);	/* END Phase-I */
+		up_read(&pcc_data.pcc_lock);	/* END Phase-I */
 	/*
 	 * This is Phase-II where we transfer the ownership of PCC to Platform
 	 *
@@ -1131,15 +1137,15 @@ int cppc_set_perf(int cpu, struct cppc_perf_ctrls *perf_ctrls)
 	 * the write command before servicing the read command
 	 */
 	if (CPC_IN_PCC(desired_reg)) {
-		if (down_write_trylock(&pcc_lock)) {		/* BEGIN Phase-II */
+		if (down_write_trylock(&pcc_data.pcc_lock)) {	/* BEGIN Phase-II */
 			/* Update only if there are pending write commands */
-			if (pending_pcc_write_cmd)
+			if (pcc_data.pending_pcc_write_cmd)
 				send_pcc_cmd(CMD_WRITE);
-			up_write(&pcc_lock);			/* END Phase-II */
+			up_write(&pcc_data.pcc_lock);		/* END Phase-II */
 		} else
 			/* Wait until pcc_write_cnt is updated by send_pcc_cmd */
-			wait_event(pcc_write_wait_q,
-				cpc_desc->write_cmd_id != pcc_write_cnt);
+			wait_event(pcc_data.pcc_write_wait_q,
+				cpc_desc->write_cmd_id != pcc_data.pcc_write_cnt);
 
 		/* send_pcc_cmd updates the status in case of failure */
 		ret = cpc_desc->write_cmd_status;
@@ -1181,10 +1187,11 @@ unsigned int cppc_get_transition_latency(int cpu_num)
 	if (!CPC_IN_PCC(desired_reg))
 		return CPUFREQ_ETERNAL;
 
-	if (pcc_mpar)
-		latency_ns = 60 * (1000 * 1000 * 1000 / pcc_mpar);
+	if (pcc_data.pcc_mpar)
+		latency_ns = 60 * (1000 * 1000 * 1000 / pcc_data.pcc_mpar);
 
-	latency_ns = max(latency_ns, (pcc_nominal + pcc_mrtt) * 1000);
+	latency_ns = max(latency_ns, pcc_data.pcc_nominal * 1000);
+	latency_ns = max(latency_ns, pcc_data.pcc_mrtt * 1000);
 
 	return latency_ns;
 }
-- 
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.


^ permalink raw reply related	[flat|nested] 12+ messages in thread

* [PATCH V3 7/7] ACPI/CPPC: check for error bit in PCC status field
  2016-08-16 20:39 [PATCH V3 0/7] CPPC enhancements Prashanth Prakash
                   ` (5 preceding siblings ...)
  2016-08-16 20:39 ` [PATCH V3 6/7] ACPI/CPPC: move all PCC related information into pcc_data Prashanth Prakash
@ 2016-08-16 20:39 ` Prashanth Prakash
  2016-08-26 23:43 ` [PATCH V3 0/7] CPPC enhancements Prakash, Prashanth
  2016-09-12 22:07 ` Rafael J. Wysocki
  8 siblings, 0 replies; 12+ messages in thread
From: Prashanth Prakash @ 2016-08-16 20:39 UTC (permalink / raw)
  To: linux-acpi; +Cc: rjw, alexey.klimov, hotran, cov, Prashanth Prakash

PCC status field exposes an error bit(2) to indicate any errors during
the execution of last comamnd. This patch checks the error bit before
notifying success/failure to the cpufreq driver.

Cc: "Rafael J. Wysocki" <rjw@rjwysocki.net>
Signed-off-by: Prashanth Prakash <pprakash@codeaurora.org>
---
 drivers/acpi/cppc_acpi.c | 66 +++++++++++++++++++++++-------------------------
 include/acpi/cppc_acpi.h |  4 ++-
 2 files changed, 34 insertions(+), 36 deletions(-)

diff --git a/drivers/acpi/cppc_acpi.c b/drivers/acpi/cppc_acpi.c
index 80c123f..ed58883 100644
--- a/drivers/acpi/cppc_acpi.c
+++ b/drivers/acpi/cppc_acpi.c
@@ -54,6 +54,7 @@ struct cppc_pcc_data {
 	unsigned int pcc_mpar, pcc_mrtt, pcc_nominal;
 
 	bool pending_pcc_write_cmd;	/* Any pending/batched PCC write cmds? */
+	bool platform_owns_pcc;		/* Ownership of PCC subspace */
 	unsigned int pcc_write_cnt;	/* Running count of PCC write commands */
 
 	/*
@@ -79,6 +80,7 @@ struct cppc_pcc_data {
 /* Structure to represent the single PCC channel */
 static struct cppc_pcc_data pcc_data = {
 	.pcc_subspace_idx = -1,
+	.platform_owns_pcc = true,
 };
 
 /*
@@ -181,12 +183,15 @@ static struct kobj_type cppc_ktype = {
 	.default_attrs = cppc_attrs,
 };
 
-static int check_pcc_chan(void)
+static int check_pcc_chan(bool chk_err_bit)
 {
-	int ret = -EIO;
+	int ret = -EIO, status = 0;
 	struct acpi_pcct_shared_memory __iomem *generic_comm_base = pcc_data.pcc_comm_addr;
 	ktime_t next_deadline = ktime_add(ktime_get(), pcc_data.deadline);
 
+	if (!pcc_data.platform_owns_pcc)
+		return 0;
+
 	/* Retry in case the remote processor was too slow to catch up. */
 	while (!ktime_after(ktime_get(), next_deadline)) {
 		/*
@@ -194,8 +199,11 @@ static int check_pcc_chan(void)
 		 * 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) {
+		status = readw_relaxed(&generic_comm_base->status);
+		if (status & PCC_CMD_COMPLETE_MASK) {
 			ret = 0;
+			if (chk_err_bit && (status & PCC_ERROR_MASK))
+				ret = -EIO;
 			break;
 		}
 		/*
@@ -205,6 +213,11 @@ static int check_pcc_chan(void)
 		udelay(3);
 	}
 
+	if (likely(!ret))
+		pcc_data.platform_owns_pcc = false;
+	else
+		pr_err("PCC check channel failed. Status=%x\n", status);
+
 	return ret;
 }
 
@@ -234,7 +247,7 @@ static int send_pcc_cmd(u16 cmd)
 		if (pcc_data.pending_pcc_write_cmd)
 			send_pcc_cmd(CMD_WRITE);
 
-		ret = check_pcc_chan();
+		ret = check_pcc_chan(false);
 		if (ret)
 			goto end;
 	} else /* CMD_WRITE */
@@ -282,6 +295,8 @@ static int send_pcc_cmd(u16 cmd)
 	/* Flip CMD COMPLETE bit */
 	writew_relaxed(0, &generic_comm_base->status);
 
+	pcc_data.platform_owns_pcc = true;
+
 	/* Ring doorbell */
 	ret = mbox_send_message(pcc_data.pcc_channel, &cmd);
 	if (ret < 0) {
@@ -290,23 +305,11 @@ static int send_pcc_cmd(u16 cmd)
 		goto end;
 	}
 
-	/*
-	 * For READs we need to ensure the cmd completed to ensure
-	 * the ensuing read()s can proceed. For WRITEs we dont care
-	 * 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 || pcc_data.pcc_mrtt) {
-		ret = check_pcc_chan();
-		if (pcc_data.pcc_mrtt)
-			last_cmd_cmpl_time = ktime_get();
-	}
+	/* wait for completion and check for PCC errro bit */
+	ret = check_pcc_chan(true);
+
+	if (pcc_data.pcc_mrtt)
+		last_cmd_cmpl_time = ktime_get();
 
 	mbox_client_txdone(pcc_data.pcc_channel, ret);
 
@@ -1059,25 +1062,18 @@ int cppc_set_perf(int cpu, struct cppc_perf_ctrls *perf_ctrls)
 	 */
 	if (CPC_IN_PCC(desired_reg)) {
 		down_read(&pcc_data.pcc_lock);	/* BEGIN Phase-I */
-		/*
-		 * If there are pending write commands i.e pending_pcc_write_cmd
-		 * is TRUE, then we know OSPM owns the channel as another CPU
-		 * has already checked for command completion bit and updated
-		 * the corresponding CPC registers
-		 */
-		if (!pcc_data.pending_pcc_write_cmd) {
-			ret = check_pcc_chan();
+		if (pcc_data.platform_owns_pcc) {
+			ret = check_pcc_chan(false);
 			if (ret) {
 				up_read(&pcc_data.pcc_lock);
 				return ret;
 			}
-			/*
-			 * Update the pending_write to make sure a PCC CMD_READ
-			 * will not arrive and steal the channel during the
-			 * transition to write lock
-			 */
-			pcc_data.pending_pcc_write_cmd = TRUE;
 		}
+		/*
+		 * Update the pending_write to make sure a PCC CMD_READ will not
+		 * arrive and steal the channel during the switch to write lock
+		 */
+		pcc_data.pending_pcc_write_cmd = true;
 		cpc_desc->write_cmd_id = pcc_data.pcc_write_cnt;
 		cpc_desc->write_cmd_status = 0;
 	}
diff --git a/include/acpi/cppc_acpi.h b/include/acpi/cppc_acpi.h
index f50b533..0e83cc3 100644
--- a/include/acpi/cppc_acpi.h
+++ b/include/acpi/cppc_acpi.h
@@ -24,7 +24,9 @@
 #define CPPC_NUM_ENT	21
 #define CPPC_REV	2
 
-#define PCC_CMD_COMPLETE 1
+#define PCC_CMD_COMPLETE_MASK	(1 << 0)
+#define PCC_ERROR_MASK		(1 << 2)
+
 #define MAX_CPC_REG_ENT 19
 
 /* CPPC specific PCC commands. */
-- 
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.


^ permalink raw reply related	[flat|nested] 12+ messages in thread

* Re: [PATCH V3 0/7] CPPC enhancements
  2016-08-16 20:39 [PATCH V3 0/7] CPPC enhancements Prashanth Prakash
                   ` (6 preceding siblings ...)
  2016-08-16 20:39 ` [PATCH V3 7/7] ACPI/CPPC: check for error bit in PCC status field Prashanth Prakash
@ 2016-08-26 23:43 ` Prakash, Prashanth
  2016-09-12 11:15   ` Alexey Klimov
  2016-09-12 22:07 ` Rafael J. Wysocki
  8 siblings, 1 reply; 12+ messages in thread
From: Prakash, Prashanth @ 2016-08-26 23:43 UTC (permalink / raw)
  To: linux-acpi; +Cc: rjw, alexey.klimov, hotran, cov, ashwinch

Hi

On 8/16/2016 2:39 PM, Prashanth Prakash wrote:
> Hi,
>
> This series has patches related to cppc that either improves the performance
> or adds some features.
Any feedback on this V3 patch set?

Thanks in advance for your inputs.

--
Thanks,
Prashanth


^ permalink raw reply	[flat|nested] 12+ messages in thread

* Re: [PATCH V3 0/7] CPPC enhancements
  2016-08-26 23:43 ` [PATCH V3 0/7] CPPC enhancements Prakash, Prashanth
@ 2016-09-12 11:15   ` Alexey Klimov
  0 siblings, 0 replies; 12+ messages in thread
From: Alexey Klimov @ 2016-09-12 11:15 UTC (permalink / raw)
  To: Prakash, Prashanth; +Cc: linux-acpi, rjw, hotran, cov, ashwinch

On Fri, Aug 26, 2016 at 05:43:00PM -0600, Prakash, Prashanth wrote:
> Hi
> 
> On 8/16/2016 2:39 PM, Prashanth Prakash wrote:
> > Hi,
> >
> > This series has patches related to cppc that either improves the performance
> > or adds some features.
> Any feedback on this V3 patch set?
> 
> Thanks in advance for your inputs.

Hi Prashanth,

I will catch up in next few days.

Best regards,
Alexey

^ permalink raw reply	[flat|nested] 12+ messages in thread

* Re: [PATCH V3 0/7] CPPC enhancements
  2016-08-16 20:39 [PATCH V3 0/7] CPPC enhancements Prashanth Prakash
                   ` (7 preceding siblings ...)
  2016-08-26 23:43 ` [PATCH V3 0/7] CPPC enhancements Prakash, Prashanth
@ 2016-09-12 22:07 ` Rafael J. Wysocki
  2016-09-14 15:33   ` Prakash, Prashanth
  8 siblings, 1 reply; 12+ messages in thread
From: Rafael J. Wysocki @ 2016-09-12 22:07 UTC (permalink / raw)
  To: Prashanth Prakash; +Cc: linux-acpi, alexey.klimov, hotran, cov

On Tuesday, August 16, 2016 02:39:37 PM Prashanth Prakash wrote:
> Hi,
> 
> This series has patches related to cppc that either improves the performance
> or adds some features.
> 
> v3:
>   * Fixed the error path in send_pcc_cmd in CPPC batching patch
>   * Included a patch to consolidate all the global variables
>   * Included a patch to check for PCC error bit (Alexey)
> v2:
>   * Sysfs entry for counter wraparound time(Alexey)
>   * Updated latency calculation to sum of Nominal and MRTT(Alexey)
>   * Moved refernce_perf from perf_caps to fb_ctrs
>   * Addressed review comments from Hoan and few minor cleanups
> v1:
>   * Pulled in the CPPC batching path to this series
>   * Addressed POSTCHANGE issue(Alexey) and switch to RW semaphore(Hoan)
> 
> Thanks,
> Prashanth
> Ashwin Chaugule (2):
>   ACPI/CPPC: restructure read/writes for efficient sys mapped reg ops
>   ACPI/CPPC: add sysfs support to compute delivered performance
> 
> Prashanth Prakash (5):
>   ACPI/CPPC: acquire pcc_lock only while accessing PCC subspace
>   ACPI/CPPC: support for batching CPPC requests
>   ACPI/CPPC: set a non-zero value for transition_latency
>   ACPI/CPPC: move all PCC related information into pcc_data
>   ACPI/CPPC: check for error bit in PCC status field
> 
>  drivers/acpi/cppc_acpi.c       | 600 +++++++++++++++++++++++++++++++----------
>  drivers/cpufreq/cppc_cpufreq.c |   1 +
>  include/acpi/cppc_acpi.h       |  14 +-
>  3 files changed, 466 insertions(+), 149 deletions(-)

All applied.

Thanks,
Rafael


^ permalink raw reply	[flat|nested] 12+ messages in thread

* Re: [PATCH V3 0/7] CPPC enhancements
  2016-09-12 22:07 ` Rafael J. Wysocki
@ 2016-09-14 15:33   ` Prakash, Prashanth
  0 siblings, 0 replies; 12+ messages in thread
From: Prakash, Prashanth @ 2016-09-14 15:33 UTC (permalink / raw)
  To: Rafael J. Wysocki; +Cc: linux-acpi, alexey.klimov, hotran, cov



On 9/12/2016 4:07 PM, Rafael J. Wysocki wrote:
> On Tuesday, August 16, 2016 02:39:37 PM Prashanth Prakash wrote:
>> Hi,
>>
>> This series has patches related to cppc that either improves the performance
>> or adds some features.
>>
>> v3:
>>   * Fixed the error path in send_pcc_cmd in CPPC batching patch
>>   * Included a patch to consolidate all the global variables
>>   * Included a patch to check for PCC error bit (Alexey)
>> v2:
>>   * Sysfs entry for counter wraparound time(Alexey)
>>   * Updated latency calculation to sum of Nominal and MRTT(Alexey)
>>   * Moved refernce_perf from perf_caps to fb_ctrs
>>   * Addressed review comments from Hoan and few minor cleanups
>> v1:
>>   * Pulled in the CPPC batching path to this series
>>   * Addressed POSTCHANGE issue(Alexey) and switch to RW semaphore(Hoan)
>>
>> Thanks,
>> Prashanth
>> Ashwin Chaugule (2):
>>   ACPI/CPPC: restructure read/writes for efficient sys mapped reg ops
>>   ACPI/CPPC: add sysfs support to compute delivered performance
>>
>> Prashanth Prakash (5):
>>   ACPI/CPPC: acquire pcc_lock only while accessing PCC subspace
>>   ACPI/CPPC: support for batching CPPC requests
>>   ACPI/CPPC: set a non-zero value for transition_latency
>>   ACPI/CPPC: move all PCC related information into pcc_data
>>   ACPI/CPPC: check for error bit in PCC status field
>>
>>  drivers/acpi/cppc_acpi.c       | 600 +++++++++++++++++++++++++++++++----------
>>  drivers/cpufreq/cppc_cpufreq.c |   1 +
>>  include/acpi/cppc_acpi.h       |  14 +-
>>  3 files changed, 466 insertions(+), 149 deletions(-)
> All applied.

Thanks Rafael!

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


^ permalink raw reply	[flat|nested] 12+ messages in thread

end of thread, other threads:[~2016-09-14 15:33 UTC | newest]

Thread overview: 12+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2016-08-16 20:39 [PATCH V3 0/7] CPPC enhancements Prashanth Prakash
2016-08-16 20:39 ` [PATCH V3 1/7] ACPI/CPPC: restructure read/writes for efficient sys mapped reg ops Prashanth Prakash
2016-08-16 20:39 ` [PATCH V3 2/7] ACPI/CPPC: acquire pcc_lock only while accessing PCC subspace Prashanth Prakash
2016-08-16 20:39 ` [PATCH V3 3/7] ACPI/CPPC: support for batching CPPC requests Prashanth Prakash
2016-08-16 20:39 ` [PATCH V3 4/7] ACPI/CPPC: set a non-zero value for transition_latency Prashanth Prakash
2016-08-16 20:39 ` [PATCH V3 5/7] ACPI/CPPC: add sysfs support to compute delivered performance Prashanth Prakash
2016-08-16 20:39 ` [PATCH V3 6/7] ACPI/CPPC: move all PCC related information into pcc_data Prashanth Prakash
2016-08-16 20:39 ` [PATCH V3 7/7] ACPI/CPPC: check for error bit in PCC status field Prashanth Prakash
2016-08-26 23:43 ` [PATCH V3 0/7] CPPC enhancements Prakash, Prashanth
2016-09-12 11:15   ` Alexey Klimov
2016-09-12 22:07 ` Rafael J. Wysocki
2016-09-14 15:33   ` Prakash, Prashanth

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.