From d9a90463d37b29bcba02c16d61fdae645f884bee Mon Sep 17 00:00:00 2001 From: David Collins Date: Tue, 1 Dec 2015 16:49:41 -0800 Subject: [PATCH] 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 --- .../regulator/cpr3-hmss-regulator.txt | 1 + .../bindings/regulator/cpr3-regulator.txt | 6 +++ drivers/regulator/cpr3-regulator.c | 33 ++++++++++++++ drivers/regulator/cpr3-regulator.h | 6 +++ drivers/regulator/cpr3-util.c | 45 +++++++++++++++++++ 5 files changed, 91 insertions(+) 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: + 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: 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 @@ -5778,6 +5778,31 @@ int cpr3_regulator_resume(struct cpr3_controller *ctrl) return 0; } +/** + * 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 @@ -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 #include #include #include @@ -979,6 +980,46 @@ int cpr3_parse_common_thread_data(struct cpr3_thread *thread) return rc; } +/** + * 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 @@ -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",