Merge tag 'cpufreq-arm-updates-6.4' of git://git.kernel.org/pub/scm/linux/kernel...
authorRafael J. Wysocki <rafael.j.wysocki@intel.com>
Thu, 20 Apr 2023 17:30:01 +0000 (19:30 +0200)
committerRafael J. Wysocki <rafael.j.wysocki@intel.com>
Thu, 20 Apr 2023 17:30:01 +0000 (19:30 +0200)
Pull cpufreq/ARM updates for 6.4 from Viresh Kumar:

"- Add opp and bandwidth support to tegra194 cpufreq driver (Sumit
   Gupta).

 - Use of_property_present() for testing DT property presence (Rob
   Herring).

 - Remove MODULE_LICENSE in non-modules (Nick Alcock).

 - Add SM7225 to cpufreq-dt-platdev blocklist (Luca Weiss).

 - Optimizations and fixes for qcom-cpufreq-hw driver (Krzysztof
   Kozlowski, Konrad Dybcio, and Bjorn Andersson).

 - DT binding updates for qcom-cpufreq-hw driver (Konrad Dybcio and
   Bartosz Golaszewski).

 - Updates and fixes for mediatek driver (Jia-Wei Chang and
   AngeloGioacchino Del Regno)."

Documentation/devicetree/bindings/cpufreq/cpufreq-qcom-hw.yaml
drivers/cpufreq/cpufreq-dt-platdev.c
drivers/cpufreq/freq_table.c
drivers/cpufreq/imx-cpufreq-dt.c
drivers/cpufreq/imx6q-cpufreq.c
drivers/cpufreq/mediatek-cpufreq.c
drivers/cpufreq/qcom-cpufreq-hw.c
drivers/cpufreq/scmi-cpufreq.c
drivers/cpufreq/tegra124-cpufreq.c
drivers/cpufreq/tegra194-cpufreq.c
drivers/cpufreq/tegra20-cpufreq.c

index e4aa8c6..a6b3bb8 100644 (file)
@@ -20,12 +20,20 @@ properties:
     oneOf:
       - description: v1 of CPUFREQ HW
         items:
+          - enum:
+              - qcom,qcm2290-cpufreq-hw
+              - qcom,sc7180-cpufreq-hw
+              - qcom,sdm845-cpufreq-hw
+              - qcom,sm6115-cpufreq-hw
+              - qcom,sm6350-cpufreq-hw
+              - qcom,sm8150-cpufreq-hw
           - const: qcom,cpufreq-hw
 
       - description: v2 of CPUFREQ HW (EPSS)
         items:
           - enum:
               - qcom,qdu1000-cpufreq-epss
+              - qcom,sa8775p-cpufreq-epss
               - qcom,sc7280-cpufreq-epss
               - qcom,sc8280xp-cpufreq-epss
               - qcom,sm6375-cpufreq-epss
@@ -36,14 +44,14 @@ properties:
           - const: qcom,cpufreq-epss
 
   reg:
-    minItems: 2
+    minItems: 1
     items:
       - description: Frequency domain 0 register region
       - description: Frequency domain 1 register region
       - description: Frequency domain 2 register region
 
   reg-names:
-    minItems: 2
+    minItems: 1
     items:
       - const: freq-domain0
       - const: freq-domain1
@@ -85,6 +93,111 @@ required:
 
 additionalProperties: false
 
+allOf:
+  - if:
+      properties:
+        compatible:
+          contains:
+            enum:
+              - qcom,qcm2290-cpufreq-hw
+    then:
+      properties:
+        reg:
+          minItems: 1
+          maxItems: 1
+
+        reg-names:
+          minItems: 1
+          maxItems: 1
+
+        interrupts:
+          minItems: 1
+          maxItems: 1
+
+        interrupt-names:
+          minItems: 1
+
+  - if:
+      properties:
+        compatible:
+          contains:
+            enum:
+              - qcom,qdu1000-cpufreq-epss
+              - qcom,sc7180-cpufreq-hw
+              - qcom,sc8280xp-cpufreq-epss
+              - qcom,sdm845-cpufreq-hw
+              - qcom,sm6115-cpufreq-hw
+              - qcom,sm6350-cpufreq-hw
+              - qcom,sm6375-cpufreq-epss
+    then:
+      properties:
+        reg:
+          minItems: 2
+          maxItems: 2
+
+        reg-names:
+          minItems: 2
+          maxItems: 2
+
+        interrupts:
+          minItems: 2
+          maxItems: 2
+
+        interrupt-names:
+          minItems: 2
+
+  - if:
+      properties:
+        compatible:
+          contains:
+            enum:
+              - qcom,sc7280-cpufreq-epss
+              - qcom,sm8250-cpufreq-epss
+              - qcom,sm8350-cpufreq-epss
+              - qcom,sm8450-cpufreq-epss
+              - qcom,sm8550-cpufreq-epss
+    then:
+      properties:
+        reg:
+          minItems: 3
+          maxItems: 3
+
+        reg-names:
+          minItems: 3
+          maxItems: 3
+
+        interrupts:
+          minItems: 3
+          maxItems: 3
+
+        interrupt-names:
+          minItems: 3
+
+  - if:
+      properties:
+        compatible:
+          contains:
+            enum:
+              - qcom,sm8150-cpufreq-hw
+    then:
+      properties:
+        reg:
+          minItems: 3
+          maxItems: 3
+
+        reg-names:
+          minItems: 3
+          maxItems: 3
+
+        # On some SoCs the Prime core shares the LMH irq with Big cores
+        interrupts:
+          minItems: 2
+          maxItems: 2
+
+        interrupt-names:
+          minItems: 2
+
+
 examples:
   - |
     #include <dt-bindings/clock/qcom,gcc-sdm845.h>
@@ -235,7 +348,7 @@ examples:
       #size-cells = <1>;
 
       cpufreq@17d43000 {
-        compatible = "qcom,cpufreq-hw";
+        compatible = "qcom,sdm845-cpufreq-hw", "qcom,cpufreq-hw";
         reg = <0x17d43000 0x1400>, <0x17d45800 0x1400>;
         reg-names = "freq-domain0", "freq-domain1";
 
index e857036..4521814 100644 (file)
@@ -152,6 +152,7 @@ static const struct of_device_id blocklist[] __initconst = {
        { .compatible = "qcom,sm6115", },
        { .compatible = "qcom,sm6350", },
        { .compatible = "qcom,sm6375", },
+       { .compatible = "qcom,sm7225", },
        { .compatible = "qcom,sm8150", },
        { .compatible = "qcom,sm8250", },
        { .compatible = "qcom,sm8350", },
@@ -179,7 +180,7 @@ static bool __init cpu0_node_has_opp_v2_prop(void)
        struct device_node *np = of_cpu_device_node_get(0);
        bool ret = false;
 
-       if (of_get_property(np, "operating-points-v2", NULL))
+       if (of_property_present(np, "operating-points-v2"))
                ret = true;
 
        of_node_put(np);
index ddd4832..c4d4643 100644 (file)
@@ -372,4 +372,3 @@ int cpufreq_table_validate_and_sort(struct cpufreq_policy *policy)
 
 MODULE_AUTHOR("Dominik Brodowski <linux@brodo.de>");
 MODULE_DESCRIPTION("CPUfreq frequency table helpers");
-MODULE_LICENSE("GPL");
index 76e553a..535867a 100644 (file)
@@ -89,7 +89,7 @@ static int imx_cpufreq_dt_probe(struct platform_device *pdev)
 
        cpu_dev = get_cpu_device(0);
 
-       if (!of_find_property(cpu_dev->of_node, "cpu-supply", NULL))
+       if (!of_property_present(cpu_dev->of_node, "cpu-supply"))
                return -ENODEV;
 
        if (of_machine_is_compatible("fsl,imx7ulp")) {
index ad4ce84..48e1772 100644 (file)
@@ -222,7 +222,7 @@ static int imx6q_opp_check_speed_grading(struct device *dev)
        u32 val;
        int ret;
 
-       if (of_find_property(dev->of_node, "nvmem-cells", NULL)) {
+       if (of_property_present(dev->of_node, "nvmem-cells")) {
                ret = nvmem_cell_read_u32(dev, "speed_grade", &val);
                if (ret)
                        return ret;
@@ -279,7 +279,7 @@ static int imx6ul_opp_check_speed_grading(struct device *dev)
        u32 val;
        int ret = 0;
 
-       if (of_find_property(dev->of_node, "nvmem-cells", NULL)) {
+       if (of_property_present(dev->of_node, "nvmem-cells")) {
                ret = nvmem_cell_read_u32(dev, "speed_grade", &val);
                if (ret)
                        return ret;
index 7f2680b..9a39a7c 100644 (file)
@@ -373,13 +373,13 @@ static struct device *of_get_cci(struct device *cpu_dev)
        struct platform_device *pdev;
 
        np = of_parse_phandle(cpu_dev->of_node, "mediatek,cci", 0);
-       if (IS_ERR_OR_NULL(np))
-               return NULL;
+       if (!np)
+               return ERR_PTR(-ENODEV);
 
        pdev = of_find_device_by_node(np);
        of_node_put(np);
-       if (IS_ERR_OR_NULL(pdev))
-               return NULL;
+       if (!pdev)
+               return ERR_PTR(-ENODEV);
 
        return &pdev->dev;
 }
@@ -401,7 +401,7 @@ static int mtk_cpu_dvfs_info_init(struct mtk_cpu_dvfs_info *info, int cpu)
        info->ccifreq_bound = false;
        if (info->soc_data->ccifreq_supported) {
                info->cci_dev = of_get_cci(info->cpu_dev);
-               if (IS_ERR_OR_NULL(info->cci_dev)) {
+               if (IS_ERR(info->cci_dev)) {
                        ret = PTR_ERR(info->cci_dev);
                        dev_err(cpu_dev, "cpu%d: failed to get cci device\n", cpu);
                        return -ENODEV;
@@ -420,7 +420,7 @@ static int mtk_cpu_dvfs_info_init(struct mtk_cpu_dvfs_info *info, int cpu)
                ret = PTR_ERR(info->inter_clk);
                dev_err_probe(cpu_dev, ret,
                              "cpu%d: failed to get intermediate clk\n", cpu);
-               goto out_free_resources;
+               goto out_free_mux_clock;
        }
 
        info->proc_reg = regulator_get_optional(cpu_dev, "proc");
@@ -428,13 +428,13 @@ static int mtk_cpu_dvfs_info_init(struct mtk_cpu_dvfs_info *info, int cpu)
                ret = PTR_ERR(info->proc_reg);
                dev_err_probe(cpu_dev, ret,
                              "cpu%d: failed to get proc regulator\n", cpu);
-               goto out_free_resources;
+               goto out_free_inter_clock;
        }
 
        ret = regulator_enable(info->proc_reg);
        if (ret) {
                dev_warn(cpu_dev, "cpu%d: failed to enable vproc\n", cpu);
-               goto out_free_resources;
+               goto out_free_proc_reg;
        }
 
        /* Both presence and absence of sram regulator are valid cases. */
@@ -442,14 +442,14 @@ static int mtk_cpu_dvfs_info_init(struct mtk_cpu_dvfs_info *info, int cpu)
        if (IS_ERR(info->sram_reg)) {
                ret = PTR_ERR(info->sram_reg);
                if (ret == -EPROBE_DEFER)
-                       goto out_free_resources;
+                       goto out_disable_proc_reg;
 
                info->sram_reg = NULL;
        } else {
                ret = regulator_enable(info->sram_reg);
                if (ret) {
                        dev_warn(cpu_dev, "cpu%d: failed to enable vsram\n", cpu);
-                       goto out_free_resources;
+                       goto out_free_sram_reg;
                }
        }
 
@@ -458,13 +458,13 @@ static int mtk_cpu_dvfs_info_init(struct mtk_cpu_dvfs_info *info, int cpu)
        if (ret) {
                dev_err(cpu_dev,
                        "cpu%d: failed to get OPP-sharing information\n", cpu);
-               goto out_free_resources;
+               goto out_disable_sram_reg;
        }
 
        ret = dev_pm_opp_of_cpumask_add_table(&info->cpus);
        if (ret) {
                dev_warn(cpu_dev, "cpu%d: no OPP table\n", cpu);
-               goto out_free_resources;
+               goto out_disable_sram_reg;
        }
 
        ret = clk_prepare_enable(info->cpu_clk);
@@ -533,43 +533,41 @@ out_disable_mux_clock:
 out_free_opp_table:
        dev_pm_opp_of_cpumask_remove_table(&info->cpus);
 
-out_free_resources:
-       if (regulator_is_enabled(info->proc_reg))
-               regulator_disable(info->proc_reg);
-       if (info->sram_reg && regulator_is_enabled(info->sram_reg))
+out_disable_sram_reg:
+       if (info->sram_reg)
                regulator_disable(info->sram_reg);
 
-       if (!IS_ERR(info->proc_reg))
-               regulator_put(info->proc_reg);
-       if (!IS_ERR(info->sram_reg))
+out_free_sram_reg:
+       if (info->sram_reg)
                regulator_put(info->sram_reg);
-       if (!IS_ERR(info->cpu_clk))
-               clk_put(info->cpu_clk);
-       if (!IS_ERR(info->inter_clk))
-               clk_put(info->inter_clk);
+
+out_disable_proc_reg:
+       regulator_disable(info->proc_reg);
+
+out_free_proc_reg:
+       regulator_put(info->proc_reg);
+
+out_free_inter_clock:
+       clk_put(info->inter_clk);
+
+out_free_mux_clock:
+       clk_put(info->cpu_clk);
 
        return ret;
 }
 
 static void mtk_cpu_dvfs_info_release(struct mtk_cpu_dvfs_info *info)
 {
-       if (!IS_ERR(info->proc_reg)) {
-               regulator_disable(info->proc_reg);
-               regulator_put(info->proc_reg);
-       }
-       if (!IS_ERR(info->sram_reg)) {
+       regulator_disable(info->proc_reg);
+       regulator_put(info->proc_reg);
+       if (info->sram_reg) {
                regulator_disable(info->sram_reg);
                regulator_put(info->sram_reg);
        }
-       if (!IS_ERR(info->cpu_clk)) {
-               clk_disable_unprepare(info->cpu_clk);
-               clk_put(info->cpu_clk);
-       }
-       if (!IS_ERR(info->inter_clk)) {
-               clk_disable_unprepare(info->inter_clk);
-               clk_put(info->inter_clk);
-       }
-
+       clk_disable_unprepare(info->cpu_clk);
+       clk_put(info->cpu_clk);
+       clk_disable_unprepare(info->inter_clk);
+       clk_put(info->inter_clk);
        dev_pm_opp_of_cpumask_remove_table(&info->cpus);
        dev_pm_opp_unregister_notifier(info->cpu_dev, &info->opp_nb);
 }
@@ -695,6 +693,15 @@ static const struct mtk_cpufreq_platform_data mt2701_platform_data = {
        .ccifreq_supported = false,
 };
 
+static const struct mtk_cpufreq_platform_data mt7622_platform_data = {
+       .min_volt_shift = 100000,
+       .max_volt_shift = 200000,
+       .proc_max_volt = 1360000,
+       .sram_min_volt = 0,
+       .sram_max_volt = 1360000,
+       .ccifreq_supported = false,
+};
+
 static const struct mtk_cpufreq_platform_data mt8183_platform_data = {
        .min_volt_shift = 100000,
        .max_volt_shift = 200000,
@@ -713,20 +720,29 @@ static const struct mtk_cpufreq_platform_data mt8186_platform_data = {
        .ccifreq_supported = true,
 };
 
+static const struct mtk_cpufreq_platform_data mt8516_platform_data = {
+       .min_volt_shift = 100000,
+       .max_volt_shift = 200000,
+       .proc_max_volt = 1310000,
+       .sram_min_volt = 0,
+       .sram_max_volt = 1310000,
+       .ccifreq_supported = false,
+};
+
 /* List of machines supported by this driver */
 static const struct of_device_id mtk_cpufreq_machines[] __initconst = {
        { .compatible = "mediatek,mt2701", .data = &mt2701_platform_data },
        { .compatible = "mediatek,mt2712", .data = &mt2701_platform_data },
-       { .compatible = "mediatek,mt7622", .data = &mt2701_platform_data },
-       { .compatible = "mediatek,mt7623", .data = &mt2701_platform_data },
-       { .compatible = "mediatek,mt8167", .data = &mt2701_platform_data },
+       { .compatible = "mediatek,mt7622", .data = &mt7622_platform_data },
+       { .compatible = "mediatek,mt7623", .data = &mt7622_platform_data },
+       { .compatible = "mediatek,mt8167", .data = &mt8516_platform_data },
        { .compatible = "mediatek,mt817x", .data = &mt2701_platform_data },
        { .compatible = "mediatek,mt8173", .data = &mt2701_platform_data },
        { .compatible = "mediatek,mt8176", .data = &mt2701_platform_data },
        { .compatible = "mediatek,mt8183", .data = &mt8183_platform_data },
        { .compatible = "mediatek,mt8186", .data = &mt8186_platform_data },
        { .compatible = "mediatek,mt8365", .data = &mt2701_platform_data },
-       { .compatible = "mediatek,mt8516", .data = &mt2701_platform_data },
+       { .compatible = "mediatek,mt8516", .data = &mt8516_platform_data },
        { }
 };
 MODULE_DEVICE_TABLE(of, mtk_cpufreq_machines);
index 2f581d2..eb54f7f 100644 (file)
@@ -14,7 +14,6 @@
 #include <linux/of_address.h>
 #include <linux/of_platform.h>
 #include <linux/pm_opp.h>
-#include <linux/pm_qos.h>
 #include <linux/slab.h>
 #include <linux/spinlock.h>
 #include <linux/units.h>
@@ -29,6 +28,8 @@
 
 #define GT_IRQ_STATUS                  BIT(2)
 
+#define MAX_FREQ_DOMAINS               3
+
 struct qcom_cpufreq_soc_data {
        u32 reg_enable;
        u32 reg_domain_state;
@@ -43,7 +44,6 @@ struct qcom_cpufreq_soc_data {
 
 struct qcom_cpufreq_data {
        void __iomem *base;
-       struct resource *res;
 
        /*
         * Mutex to synchronize between de-init sequence and re-starting LMh
@@ -58,8 +58,6 @@ struct qcom_cpufreq_data {
        struct clk_hw cpu_clk;
 
        bool per_core_dcvs;
-
-       struct freq_qos_request throttle_freq_req;
 };
 
 static struct {
@@ -349,8 +347,6 @@ static void qcom_lmh_dcvs_notify(struct qcom_cpufreq_data *data)
 
        throttled_freq = freq_hz / HZ_PER_KHZ;
 
-       freq_qos_update_request(&data->throttle_freq_req, throttled_freq);
-
        /* Update thermal pressure (the boost frequencies are accepted) */
        arch_update_thermal_pressure(policy->related_cpus, throttled_freq);
 
@@ -443,14 +439,6 @@ static int qcom_cpufreq_hw_lmh_init(struct cpufreq_policy *policy, int index)
        if (data->throttle_irq < 0)
                return data->throttle_irq;
 
-       ret = freq_qos_add_request(&policy->constraints,
-                                  &data->throttle_freq_req, FREQ_QOS_MAX,
-                                  FREQ_QOS_MAX_DEFAULT_VALUE);
-       if (ret < 0) {
-               dev_err(&pdev->dev, "Failed to add freq constraint (%d)\n", ret);
-               return ret;
-       }
-
        data->cancel_throttle = false;
        data->policy = policy;
 
@@ -517,7 +505,6 @@ static void qcom_cpufreq_hw_lmh_exit(struct qcom_cpufreq_data *data)
        if (data->throttle_irq <= 0)
                return;
 
-       freq_qos_remove_request(&data->throttle_freq_req);
        free_irq(data->throttle_irq, data);
 }
 
@@ -590,16 +577,12 @@ static int qcom_cpufreq_hw_cpu_exit(struct cpufreq_policy *policy)
 {
        struct device *cpu_dev = get_cpu_device(policy->cpu);
        struct qcom_cpufreq_data *data = policy->driver_data;
-       struct resource *res = data->res;
-       void __iomem *base = data->base;
 
        dev_pm_opp_remove_all_dynamic(cpu_dev);
        dev_pm_opp_of_cpumask_remove_table(policy->related_cpus);
        qcom_cpufreq_hw_lmh_exit(data);
        kfree(policy->freq_table);
        kfree(data);
-       iounmap(base);
-       release_mem_region(res->start, resource_size(res));
 
        return 0;
 }
@@ -651,10 +634,9 @@ static int qcom_cpufreq_hw_driver_probe(struct platform_device *pdev)
 {
        struct clk_hw_onecell_data *clk_data;
        struct device *dev = &pdev->dev;
-       struct device_node *soc_node;
        struct device *cpu_dev;
        struct clk *clk;
-       int ret, i, num_domains, reg_sz;
+       int ret, i, num_domains;
 
        clk = clk_get(dev, "xo");
        if (IS_ERR(clk))
@@ -681,24 +663,9 @@ static int qcom_cpufreq_hw_driver_probe(struct platform_device *pdev)
        if (ret)
                return ret;
 
-       /* Allocate qcom_cpufreq_data based on the available frequency domains in DT */
-       soc_node = of_get_parent(dev->of_node);
-       if (!soc_node)
-               return -EINVAL;
-
-       ret = of_property_read_u32(soc_node, "#address-cells", &reg_sz);
-       if (ret)
-               goto of_exit;
-
-       ret = of_property_read_u32(soc_node, "#size-cells", &i);
-       if (ret)
-               goto of_exit;
-
-       reg_sz += i;
-
-       num_domains = of_property_count_elems_of_size(dev->of_node, "reg", sizeof(u32) * reg_sz);
-       if (num_domains <= 0)
-               return num_domains;
+       for (num_domains = 0; num_domains < MAX_FREQ_DOMAINS; num_domains++)
+               if (!platform_get_resource(pdev, IORESOURCE_MEM, num_domains))
+                       break;
 
        qcom_cpufreq.data = devm_kzalloc(dev, sizeof(struct qcom_cpufreq_data) * num_domains,
                                         GFP_KERNEL);
@@ -718,17 +685,15 @@ static int qcom_cpufreq_hw_driver_probe(struct platform_device *pdev)
        for (i = 0; i < num_domains; i++) {
                struct qcom_cpufreq_data *data = &qcom_cpufreq.data[i];
                struct clk_init_data clk_init = {};
-               struct resource *res;
                void __iomem *base;
 
-               base = devm_platform_get_and_ioremap_resource(pdev, i, &res);
+               base = devm_platform_ioremap_resource(pdev, i);
                if (IS_ERR(base)) {
-                       dev_err(dev, "Failed to map resource %pR\n", res);
+                       dev_err(dev, "Failed to map resource index %d\n", i);
                        return PTR_ERR(base);
                }
 
                data->base = base;
-               data->res = res;
 
                /* Register CPU clock for each frequency domain */
                clk_init.name = kasprintf(GFP_KERNEL, "qcom_cpufreq%d", i);
@@ -762,9 +727,6 @@ static int qcom_cpufreq_hw_driver_probe(struct platform_device *pdev)
        else
                dev_dbg(dev, "QCOM CPUFreq HW driver initialized\n");
 
-of_exit:
-       of_node_put(soc_node);
-
        return ret;
 }
 
index 513a071..f34e638 100644 (file)
@@ -310,7 +310,7 @@ static int scmi_cpufreq_probe(struct scmi_device *sdev)
 
 #ifdef CONFIG_COMMON_CLK
        /* dummy clock provider as needed by OPP if clocks property is used */
-       if (of_find_property(dev->of_node, "#clock-cells", NULL))
+       if (of_property_present(dev->of_node, "#clock-cells"))
                devm_of_clk_add_hw_provider(dev, of_clk_hw_simple_get, NULL);
 #endif
 
index 7a1ea6f..788672c 100644 (file)
@@ -221,4 +221,3 @@ module_init(tegra_cpufreq_init);
 
 MODULE_AUTHOR("Tuomas Tynkkynen <ttynkkynen@nvidia.com>");
 MODULE_DESCRIPTION("cpufreq driver for NVIDIA Tegra124");
-MODULE_LICENSE("GPL v2");
index 5890e25..c8d0334 100644 (file)
@@ -12,6 +12,7 @@
 #include <linux/of_platform.h>
 #include <linux/platform_device.h>
 #include <linux/slab.h>
+#include <linux/units.h>
 
 #include <asm/smp_plat.h>
 
@@ -65,12 +66,36 @@ struct tegra_cpufreq_soc {
 
 struct tegra194_cpufreq_data {
        void __iomem *regs;
-       struct cpufreq_frequency_table **tables;
+       struct cpufreq_frequency_table **bpmp_luts;
        const struct tegra_cpufreq_soc *soc;
+       bool icc_dram_bw_scaling;
 };
 
 static struct workqueue_struct *read_counters_wq;
 
+static int tegra_cpufreq_set_bw(struct cpufreq_policy *policy, unsigned long freq_khz)
+{
+       struct tegra194_cpufreq_data *data = cpufreq_get_driver_data();
+       struct dev_pm_opp *opp;
+       struct device *dev;
+       int ret;
+
+       dev = get_cpu_device(policy->cpu);
+       if (!dev)
+               return -ENODEV;
+
+       opp = dev_pm_opp_find_freq_exact(dev, freq_khz * KHZ, true);
+       if (IS_ERR(opp))
+               return PTR_ERR(opp);
+
+       ret = dev_pm_opp_set_opp(dev, opp);
+       if (ret)
+               data->icc_dram_bw_scaling = false;
+
+       dev_pm_opp_put(opp);
+       return ret;
+}
+
 static void tegra_get_cpu_mpidr(void *mpidr)
 {
        *((u64 *)mpidr) = read_cpuid_mpidr() & MPIDR_HWID_BITMASK;
@@ -354,7 +379,7 @@ static unsigned int tegra194_get_speed(u32 cpu)
         * to the last written ndiv value from freq_table. This is
         * done to return consistent value.
         */
-       cpufreq_for_each_valid_entry(pos, data->tables[clusterid]) {
+       cpufreq_for_each_valid_entry(pos, data->bpmp_luts[clusterid]) {
                if (pos->driver_data != ndiv)
                        continue;
 
@@ -369,16 +394,93 @@ static unsigned int tegra194_get_speed(u32 cpu)
        return rate;
 }
 
+static int tegra_cpufreq_init_cpufreq_table(struct cpufreq_policy *policy,
+                                           struct cpufreq_frequency_table *bpmp_lut,
+                                           struct cpufreq_frequency_table **opp_table)
+{
+       struct tegra194_cpufreq_data *data = cpufreq_get_driver_data();
+       struct cpufreq_frequency_table *freq_table = NULL;
+       struct cpufreq_frequency_table *pos;
+       struct device *cpu_dev;
+       struct dev_pm_opp *opp;
+       unsigned long rate;
+       int ret, max_opps;
+       int j = 0;
+
+       cpu_dev = get_cpu_device(policy->cpu);
+       if (!cpu_dev) {
+               pr_err("%s: failed to get cpu%d device\n", __func__, policy->cpu);
+               return -ENODEV;
+       }
+
+       /* Initialize OPP table mentioned in operating-points-v2 property in DT */
+       ret = dev_pm_opp_of_add_table_indexed(cpu_dev, 0);
+       if (!ret) {
+               max_opps = dev_pm_opp_get_opp_count(cpu_dev);
+               if (max_opps <= 0) {
+                       dev_err(cpu_dev, "Failed to add OPPs\n");
+                       return max_opps;
+               }
+
+               /* Disable all opps and cross-validate against LUT later */
+               for (rate = 0; ; rate++) {
+                       opp = dev_pm_opp_find_freq_ceil(cpu_dev, &rate);
+                       if (IS_ERR(opp))
+                               break;
+
+                       dev_pm_opp_put(opp);
+                       dev_pm_opp_disable(cpu_dev, rate);
+               }
+       } else {
+               dev_err(cpu_dev, "Invalid or empty opp table in device tree\n");
+               data->icc_dram_bw_scaling = false;
+               return ret;
+       }
+
+       freq_table = kcalloc((max_opps + 1), sizeof(*freq_table), GFP_KERNEL);
+       if (!freq_table)
+               return -ENOMEM;
+
+       /*
+        * Cross check the frequencies from BPMP-FW LUT against the OPP's present in DT.
+        * Enable only those DT OPP's which are present in LUT also.
+        */
+       cpufreq_for_each_valid_entry(pos, bpmp_lut) {
+               opp = dev_pm_opp_find_freq_exact(cpu_dev, pos->frequency * KHZ, false);
+               if (IS_ERR(opp))
+                       continue;
+
+               ret = dev_pm_opp_enable(cpu_dev, pos->frequency * KHZ);
+               if (ret < 0)
+                       return ret;
+
+               freq_table[j].driver_data = pos->driver_data;
+               freq_table[j].frequency = pos->frequency;
+               j++;
+       }
+
+       freq_table[j].driver_data = pos->driver_data;
+       freq_table[j].frequency = CPUFREQ_TABLE_END;
+
+       *opp_table = &freq_table[0];
+
+       dev_pm_opp_set_sharing_cpus(cpu_dev, policy->cpus);
+
+       return ret;
+}
+
 static int tegra194_cpufreq_init(struct cpufreq_policy *policy)
 {
        struct tegra194_cpufreq_data *data = cpufreq_get_driver_data();
        int maxcpus_per_cluster = data->soc->maxcpus_per_cluster;
+       struct cpufreq_frequency_table *freq_table;
+       struct cpufreq_frequency_table *bpmp_lut;
        u32 start_cpu, cpu;
        u32 clusterid;
+       int ret;
 
        data->soc->ops->get_cpu_cluster_id(policy->cpu, NULL, &clusterid);
-
-       if (clusterid >= data->soc->num_clusters || !data->tables[clusterid])
+       if (clusterid >= data->soc->num_clusters || !data->bpmp_luts[clusterid])
                return -EINVAL;
 
        start_cpu = rounddown(policy->cpu, maxcpus_per_cluster);
@@ -387,9 +489,22 @@ static int tegra194_cpufreq_init(struct cpufreq_policy *policy)
                if (cpu_possible(cpu))
                        cpumask_set_cpu(cpu, policy->cpus);
        }
-       policy->freq_table = data->tables[clusterid];
        policy->cpuinfo.transition_latency = TEGRA_CPUFREQ_TRANSITION_LATENCY;
 
+       bpmp_lut = data->bpmp_luts[clusterid];
+
+       if (data->icc_dram_bw_scaling) {
+               ret = tegra_cpufreq_init_cpufreq_table(policy, bpmp_lut, &freq_table);
+               if (!ret) {
+                       policy->freq_table = freq_table;
+                       return 0;
+               }
+       }
+
+       data->icc_dram_bw_scaling = false;
+       policy->freq_table = bpmp_lut;
+       pr_info("OPP tables missing from DT, EMC frequency scaling disabled\n");
+
        return 0;
 }
 
@@ -406,6 +521,9 @@ static int tegra194_cpufreq_set_target(struct cpufreq_policy *policy,
         */
        data->soc->ops->set_cpu_ndiv(policy, (u64)tbl->driver_data);
 
+       if (data->icc_dram_bw_scaling)
+               tegra_cpufreq_set_bw(policy, tbl->frequency);
+
        return 0;
 }
 
@@ -439,8 +557,8 @@ static void tegra194_cpufreq_free_resources(void)
 }
 
 static struct cpufreq_frequency_table *
-init_freq_table(struct platform_device *pdev, struct tegra_bpmp *bpmp,
-               unsigned int cluster_id)
+tegra_cpufreq_bpmp_read_lut(struct platform_device *pdev, struct tegra_bpmp *bpmp,
+                           unsigned int cluster_id)
 {
        struct cpufreq_frequency_table *freq_table;
        struct mrq_cpu_ndiv_limits_response resp;
@@ -515,6 +633,7 @@ static int tegra194_cpufreq_probe(struct platform_device *pdev)
        const struct tegra_cpufreq_soc *soc;
        struct tegra194_cpufreq_data *data;
        struct tegra_bpmp *bpmp;
+       struct device *cpu_dev;
        int err, i;
 
        data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL);
@@ -530,9 +649,9 @@ static int tegra194_cpufreq_probe(struct platform_device *pdev)
                return -EINVAL;
        }
 
-       data->tables = devm_kcalloc(&pdev->dev, data->soc->num_clusters,
-                                   sizeof(*data->tables), GFP_KERNEL);
-       if (!data->tables)
+       data->bpmp_luts = devm_kcalloc(&pdev->dev, data->soc->num_clusters,
+                                      sizeof(*data->bpmp_luts), GFP_KERNEL);
+       if (!data->bpmp_luts)
                return -ENOMEM;
 
        if (soc->actmon_cntr_base) {
@@ -556,15 +675,26 @@ static int tegra194_cpufreq_probe(struct platform_device *pdev)
        }
 
        for (i = 0; i < data->soc->num_clusters; i++) {
-               data->tables[i] = init_freq_table(pdev, bpmp, i);
-               if (IS_ERR(data->tables[i])) {
-                       err = PTR_ERR(data->tables[i]);
+               data->bpmp_luts[i] = tegra_cpufreq_bpmp_read_lut(pdev, bpmp, i);
+               if (IS_ERR(data->bpmp_luts[i])) {
+                       err = PTR_ERR(data->bpmp_luts[i]);
                        goto err_free_res;
                }
        }
 
        tegra194_cpufreq_driver.driver_data = data;
 
+       /* Check for optional OPPv2 and interconnect paths on CPU0 to enable ICC scaling */
+       cpu_dev = get_cpu_device(0);
+       if (!cpu_dev)
+               return -EPROBE_DEFER;
+
+       if (dev_pm_opp_of_get_opp_desc_node(cpu_dev)) {
+               err = dev_pm_opp_of_find_icc_paths(cpu_dev, NULL);
+               if (!err)
+                       data->icc_dram_bw_scaling = true;
+       }
+
        err = cpufreq_register_driver(&tegra194_cpufreq_driver);
        if (!err)
                goto put_bpmp;
index ab7ac7d..dfd2de4 100644 (file)
@@ -25,7 +25,7 @@ static bool cpu0_node_has_opp_v2_prop(void)
        struct device_node *np = of_cpu_device_node_get(0);
        bool ret = false;
 
-       if (of_get_property(np, "operating-points-v2", NULL))
+       if (of_property_present(np, "operating-points-v2"))
                ret = true;
 
        of_node_put(np);