summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDavid Collins <collinsd@codeaurora.org>2015-12-01 16:49:41 -0800
committerJeevan Shriram <jshriram@codeaurora.org>2016-04-22 11:57:45 -0700
commitd9a90463d37b29bcba02c16d61fdae645f884bee (patch)
tree4eed8dad33477c13ef4b8c6609a70058de414a88
parent666151fd60acabfe8498884d54f9b9a71c45e6dd (diff)
regulator: cpr3-regulator: add support for configuring CPR IRQ affinity
Add support to configure the CPR interrupt affinity via a CPR3 controller device tree node. This can be used to avoid servicing CPR interrupts triggered by a CPU enterring power collapse on the CPU that just power collapsed. Change-Id: I4c04a2c255a6bd249c888c0dd0dbda19b8436be2 CRs-Fixed: 949650 Signed-off-by: David Collins <collinsd@codeaurora.org>
-rw-r--r--Documentation/devicetree/bindings/regulator/cpr3-hmss-regulator.txt1
-rw-r--r--Documentation/devicetree/bindings/regulator/cpr3-regulator.txt6
-rw-r--r--drivers/regulator/cpr3-regulator.c33
-rw-r--r--drivers/regulator/cpr3-regulator.h6
-rw-r--r--drivers/regulator/cpr3-util.c45
5 files changed, 91 insertions, 0 deletions
diff --git a/Documentation/devicetree/bindings/regulator/cpr3-hmss-regulator.txt b/Documentation/devicetree/bindings/regulator/cpr3-hmss-regulator.txt
index 45a27d14c249..2af3cbfd0584 100644
--- a/Documentation/devicetree/bindings/regulator/cpr3-hmss-regulator.txt
+++ b/Documentation/devicetree/bindings/regulator/cpr3-hmss-regulator.txt
@@ -313,6 +313,7 @@ apcc_cpr: cpr3-ctrl@99e8000 {
clock-names = "core_clk";
interrupts = <0 48 0>, <0 47 0>;
interrupt-names = "cpr", "ceiling";
+ qcom,cpr-interrupt-affinity = <&CPU0 &CPU1>;
qcom,cpr-ctrl-name = "apcc";
qcom,cpr-sensor-time = <1000>;
diff --git a/Documentation/devicetree/bindings/regulator/cpr3-regulator.txt b/Documentation/devicetree/bindings/regulator/cpr3-regulator.txt
index 58bee1c2cba8..0a5ad543a2be 100644
--- a/Documentation/devicetree/bindings/regulator/cpr3-regulator.txt
+++ b/Documentation/devicetree/bindings/regulator/cpr3-regulator.txt
@@ -104,6 +104,12 @@ Platform independent properties:
must be specified. "ceiling" may be specified for some
platforms.
+- qcom,cpr-interrupt-affinity
+ Usage: optional
+ Value type: <prop-encoded-array>
+ Definition: A list of CPU phandles which correspond to the cores that
+ the "cpr" interrupt should have affinity for.
+
- qcom,cpr-sensor-time
Usage: required
Value type: <u32>
diff --git a/drivers/regulator/cpr3-regulator.c b/drivers/regulator/cpr3-regulator.c
index 4fa26334bdea..c93b71c4bc37 100644
--- a/drivers/regulator/cpr3-regulator.c
+++ b/drivers/regulator/cpr3-regulator.c
@@ -5779,6 +5779,31 @@ int cpr3_regulator_resume(struct cpr3_controller *ctrl)
}
/**
+ * cpr3_regulator_cpu_hotplug_callback() - reset CPR IRQ affinity when a CPU is
+ * brought online via hotplug
+ * @nb: Pointer to the notifier block
+ * @action: hotplug action
+ * @hcpu: long value corresponding to the CPU number
+ *
+ * Return: NOTIFY_OK
+ */
+static int cpr3_regulator_cpu_hotplug_callback(struct notifier_block *nb,
+ unsigned long action, void *hcpu)
+{
+ struct cpr3_controller *ctrl = container_of(nb, struct cpr3_controller,
+ cpu_hotplug_notifier);
+ int cpu = (long)hcpu;
+
+ action &= ~CPU_TASKS_FROZEN;
+
+ if (action == CPU_ONLINE
+ && cpumask_test_cpu(cpu, &ctrl->irq_affinity_mask))
+ irq_set_affinity(ctrl->irq, &ctrl->irq_affinity_mask);
+
+ return NOTIFY_OK;
+}
+
+/**
* cpr3_regulator_validate_controller() - verify the data passed in via the
* cpr3_controller data structure
* @ctrl: Pointer to the CPR3 controller
@@ -5975,6 +6000,14 @@ int cpr3_regulator_register(struct platform_device *pdev,
}
}
+ if (ctrl->irq && !cpumask_empty(&ctrl->irq_affinity_mask)) {
+ irq_set_affinity(ctrl->irq, &ctrl->irq_affinity_mask);
+
+ ctrl->cpu_hotplug_notifier.notifier_call
+ = cpr3_regulator_cpu_hotplug_callback;
+ register_hotcpu_notifier(&ctrl->cpu_hotplug_notifier);
+ }
+
mutex_lock(&cpr3_controller_list_mutex);
cpr3_regulator_debugfs_ctrl_add(ctrl);
list_add(&ctrl->list, &cpr3_controller_list);
diff --git a/drivers/regulator/cpr3-regulator.h b/drivers/regulator/cpr3-regulator.h
index 111d362a9384..0c4b6cb66805 100644
--- a/drivers/regulator/cpr3-regulator.h
+++ b/drivers/regulator/cpr3-regulator.h
@@ -535,6 +535,10 @@ struct cpr3_aging_sensor_info {
* @iface_clk: Pointer to the CPR3 interface clock (platform specific)
* @bus_clk: Pointer to the CPR3 bus clock (platform specific)
* @irq: CPR interrupt number
+ * @irq_affinity_mask: The cpumask for the CPUs which the CPR interrupt should
+ * have affinity for
+ * @cpu_hotplug_notifier: CPU hotplug notifier used to reset IRQ affinity when a
+ * CPU is brought back online
* @ceiling_irq: Interrupt number for the interrupt that is triggered
* when hardware closed-loop attempts to exceed the ceiling
* voltage
@@ -695,6 +699,8 @@ struct cpr3_controller {
struct clk *iface_clk;
struct clk *bus_clk;
int irq;
+ struct cpumask irq_affinity_mask;
+ struct notifier_block cpu_hotplug_notifier;
int ceiling_irq;
struct msm_apm_ctrl_dev *apm;
int apm_threshold_volt;
diff --git a/drivers/regulator/cpr3-util.c b/drivers/regulator/cpr3-util.c
index 255e585f02a3..164579344c81 100644
--- a/drivers/regulator/cpr3-util.c
+++ b/drivers/regulator/cpr3-util.c
@@ -18,6 +18,7 @@
#define pr_fmt(fmt) "%s: " fmt, __func__
+#include <linux/cpumask.h>
#include <linux/device.h>
#include <linux/io.h>
#include <linux/kernel.h>
@@ -980,6 +981,46 @@ int cpr3_parse_common_thread_data(struct cpr3_thread *thread)
}
/**
+ * cpr3_parse_irq_affinity() - parse CPR IRQ affinity information
+ * @ctrl: Pointer to the CPR3 controller
+ *
+ * Return: 0 on success, errno on failure
+ */
+static int cpr3_parse_irq_affinity(struct cpr3_controller *ctrl)
+{
+ struct device_node *cpu_node;
+ int i, cpu;
+ int len = 0;
+
+ if (!of_find_property(ctrl->dev->of_node, "qcom,cpr-interrupt-affinity",
+ &len)) {
+ /* No IRQ affinity required */
+ return 0;
+ }
+
+ len /= sizeof(u32);
+
+ for (i = 0; i < len; i++) {
+ cpu_node = of_parse_phandle(ctrl->dev->of_node,
+ "qcom,cpr-interrupt-affinity", i);
+ if (!cpu_node) {
+ cpr3_err(ctrl, "could not find CPU node %d\n", i);
+ return -EINVAL;
+ }
+
+ for_each_possible_cpu(cpu) {
+ if (of_get_cpu_node(cpu, NULL) == cpu_node) {
+ cpumask_set_cpu(cpu, &ctrl->irq_affinity_mask);
+ break;
+ }
+ }
+ of_node_put(cpu_node);
+ }
+
+ return 0;
+}
+
+/**
* cpr3_parse_common_ctrl_data() - parse common CPR3 controller properties from
* device tree
* @ctrl: Pointer to the CPR3 controller
@@ -1045,6 +1086,10 @@ int cpr3_parse_common_ctrl_data(struct cpr3_controller *ctrl)
ctrl->cpr_allowed_sw = of_property_read_bool(ctrl->dev->of_node,
"qcom,cpr-enable");
+ rc = cpr3_parse_irq_affinity(ctrl);
+ if (rc)
+ return rc;
+
/* Aging reference voltage is optional */
ctrl->aging_ref_volt = 0;
of_property_read_u32(ctrl->dev->of_node, "qcom,cpr-aging-ref-voltage",