clk: qcom: clk-cpu-osm: Rework the OPP table filling logic
Add support to allow filling the L3 clock OPP tables for more
than one device.
Additionally, merge the l3-dev0 and l3-dev4 properties to
l3_devs.
Change-Id: Id6ef4164f1f9efb399c43222c1126718f5358e61
Signed-off-by: Deepak Katragadda <dkatraga@codeaurora.org>
diff --git a/Documentation/devicetree/bindings/arm/msm/qcom,osm.txt b/Documentation/devicetree/bindings/arm/msm/qcom,osm.txt
index 06b1ed9..ea749a6 100644
--- a/Documentation/devicetree/bindings/arm/msm/qcom,osm.txt
+++ b/Documentation/devicetree/bindings/arm/msm/qcom,osm.txt
@@ -23,6 +23,13 @@
"osm_perfcl_base".
Must be specified in the same order as the corresponding
addresses are specified in the reg property.
+
+- l3-devs
+ Usage: optional
+ Value type: <phandle>
+ Definition: List of phandles to devices that the OPP tables with the L3
+ frequency and voltage mappings are loaded for.
+
- clock-names
Usage: required
Value type: <string>
@@ -41,6 +48,8 @@
<0x17d45800 0x1400>;
reg-names = "osm_l3_base", "osm_pwrcl_base", "osm_perfcl_base";
+ l3-devs = <&phandle0 &phandle1 &phandle2>;
+
clock-names = "xo_ao";
clocks = <&clock_rpmh RPMH_CXO_CLK_A>;
#clock-cells = <1>;
diff --git a/arch/arm64/boot/dts/qcom/sdm845.dtsi b/arch/arm64/boot/dts/qcom/sdm845.dtsi
index 007f231..a69580d 100644
--- a/arch/arm64/boot/dts/qcom/sdm845.dtsi
+++ b/arch/arm64/boot/dts/qcom/sdm845.dtsi
@@ -1210,8 +1210,7 @@
<0x17d45800 0x1400>;
reg-names = "osm_l3_base", "osm_pwrcl_base", "osm_perfcl_base";
- l3-dev0 = <&l3_cpu0>;
- l3-dev4 = <&l3_cpu4>;
+ l3-devs = <&l3_cpu0 &l3_cpu4>;
clock-names = "xo_ao";
clocks = <&clock_rpmh RPMH_CXO_CLK_A>;
diff --git a/drivers/clk/qcom/clk-cpu-osm.c b/drivers/clk/qcom/clk-cpu-osm.c
index 767b880..cabf0c2 100644
--- a/drivers/clk/qcom/clk-cpu-osm.c
+++ b/drivers/clk/qcom/clk-cpu-osm.c
@@ -637,12 +637,12 @@
return -EINVAL;
}
-static int add_opp(struct clk_osm *c, struct device *dev)
+static int add_opp(struct clk_osm *c, struct device **device_list, int count)
{
unsigned long rate = 0;
u32 uv;
long rc;
- int j = 0;
+ int i, j = 0;
unsigned long min_rate = c->hw.init->rate_max[0];
unsigned long max_rate =
c->hw.init->rate_max[c->hw.init->num_rate_max - 1];
@@ -655,10 +655,12 @@
return -EINVAL;
}
- rc = dev_pm_opp_add(dev, rate, uv);
- if (rc) {
- pr_warn("failed to add OPP for %lu\n", rate);
- return rc;
+ for (i = 0; i < count; i++) {
+ rc = dev_pm_opp_add(device_list[i], rate, uv);
+ if (rc) {
+ pr_warn("failed to add OPP for %lu\n", rate);
+ return rc;
+ }
}
/*
@@ -667,13 +669,18 @@
* this information will be used by thermal mitigation and the
* scheduler.
*/
- if (rate == min_rate)
- pr_info("Set OPP pair (%lu Hz, %d uv) on %s\n",
- rate, uv, dev_name(dev));
+ if (rate == min_rate) {
+ for (i = 0; i < count; i++) {
+ pr_info("Set OPP pair (%lu Hz, %d uv) on %s\n",
+ rate, uv, dev_name(device_list[i]));
+ }
+ }
if (rate == max_rate && max_rate != min_rate) {
- pr_info("Set OPP pair (%lu Hz, %d uv) on %s\n",
- rate, uv, dev_name(dev));
+ for (i = 0; i < count; i++) {
+ pr_info("Set OPP pair (%lu Hz, %d uv) on %s\n",
+ rate, uv, dev_name(device_list[i]));
+ }
break;
}
@@ -683,14 +690,70 @@
return 0;
}
+static int derive_device_list(struct device **device_list,
+ struct device_node *np,
+ char *phandle_name, int count)
+{
+ int i;
+ struct platform_device *pdev;
+ struct device_node *dev_node;
+
+ for (i = 0; i < count; i++) {
+ dev_node = of_parse_phandle(np, phandle_name, i);
+ if (!dev_node) {
+ pr_err("Unable to get device_node pointer for opp-handle (%s)\n",
+ phandle_name);
+ return -ENODEV;
+ }
+
+ pdev = of_find_device_by_node(dev_node);
+ if (!pdev) {
+ pr_err("Unable to find platform_device node for opp-handle (%s)\n",
+ phandle_name);
+ return -ENODEV;
+ }
+ device_list[i] = &pdev->dev;
+ }
+ return 0;
+}
+
+static void populate_l3_opp_table(struct device_node *np, char *phandle_name)
+{
+ struct device **device_list;
+ int len, count, ret = 0;
+
+ if (of_find_property(np, phandle_name, &len)) {
+ count = len / sizeof(u32);
+
+ device_list = kcalloc(count, sizeof(struct device *),
+ GFP_KERNEL);
+ if (!device_list)
+ return;
+
+ ret = derive_device_list(device_list, np, phandle_name, count);
+ if (ret < 0) {
+ pr_err("Failed to fill device_list for %s\n",
+ phandle_name);
+ return;
+ }
+ } else {
+ pr_debug("Unable to find %s\n", phandle_name);
+ return;
+ }
+
+ if (add_opp(&l3_clk, device_list, count))
+ pr_err("Failed to add OPP levels for %s\n", phandle_name);
+
+ kfree(device_list);
+}
+
static void populate_opp_table(struct platform_device *pdev)
{
int cpu;
struct device *cpu_dev;
struct clk_osm *c, *parent;
struct clk_hw *hw_parent;
- struct device_node *l3_node_0, *l3_node_4;
- struct platform_device *l3_dev_0, *l3_dev_4;
+ struct device_node *np = pdev->dev.of_node;
for_each_possible_cpu(cpu) {
c = logical_cpu_to_clk(cpu);
@@ -703,40 +766,12 @@
parent = to_clk_osm(hw_parent);
cpu_dev = get_cpu_device(cpu);
if (cpu_dev)
- if (add_opp(parent, cpu_dev))
+ if (add_opp(parent, &cpu_dev, 1))
pr_err("Failed to add OPP levels for %s\n",
dev_name(cpu_dev));
}
- l3_node_0 = of_parse_phandle(pdev->dev.of_node, "l3-dev0", 0);
- if (!l3_node_0) {
- pr_err("can't find the L3 cluster 0 dt node\n");
- return;
- }
-
- l3_dev_0 = of_find_device_by_node(l3_node_0);
- if (!l3_dev_0) {
- pr_err("can't find the L3 cluster 0 dt device\n");
- return;
- }
-
- if (add_opp(&l3_clk, &l3_dev_0->dev))
- pr_err("Failed to add OPP levels for L3 cluster 0\n");
-
- l3_node_4 = of_parse_phandle(pdev->dev.of_node, "l3-dev4", 0);
- if (!l3_node_4) {
- pr_err("can't find the L3 cluster 1 dt node\n");
- return;
- }
-
- l3_dev_4 = of_find_device_by_node(l3_node_4);
- if (!l3_dev_4) {
- pr_err("can't find the L3 cluster 1 dt device\n");
- return;
- }
-
- if (add_opp(&l3_clk, &l3_dev_4->dev))
- pr_err("Failed to add OPP levels for L3 cluster 1\n");
+ populate_l3_opp_table(np, "l3-devs");
}
static u64 clk_osm_get_cpu_cycle_counter(int cpu)