OpenWrt – Rev 4

Subversion Repositories:
Rev:
From b018e44a68dc2f4df819ae194e39e07313841dad Mon Sep 17 00:00:00 2001
From: Yangbo Lu <yangbo.lu@nxp.com>
Date: Wed, 17 Jan 2018 15:27:58 +0800
Subject: [PATCH 15/30] cpufreq: support layerscape

This is an integrated patch for layerscape pm support.

Signed-off-by: Tang Yuantian <Yuantian.Tang@nxp.com>
Signed-off-by: Yangbo Lu <yangbo.lu@nxp.com>
---
 drivers/cpufreq/Kconfig         |   2 +-
 drivers/cpufreq/qoriq-cpufreq.c | 176 +++++++++++++++-------------------------
 drivers/firmware/psci.c         |  12 ++-
 drivers/soc/fsl/rcpm.c          | 158 ++++++++++++++++++++++++++++++++++++
 4 files changed, 235 insertions(+), 113 deletions(-)
 create mode 100644 drivers/soc/fsl/rcpm.c

--- a/drivers/cpufreq/Kconfig
+++ b/drivers/cpufreq/Kconfig
@@ -334,7 +334,7 @@ endif
 
 config QORIQ_CPUFREQ
        tristate "CPU frequency scaling driver for Freescale QorIQ SoCs"
-       depends on OF && COMMON_CLK && (PPC_E500MC || ARM)
+       depends on OF && COMMON_CLK && (PPC_E500MC || ARM || ARM64)
        depends on !CPU_THERMAL || THERMAL
        select CLK_QORIQ
        help
--- a/drivers/cpufreq/qoriq-cpufreq.c
+++ b/drivers/cpufreq/qoriq-cpufreq.c
@@ -11,6 +11,7 @@
 #define pr_fmt(fmt)    KBUILD_MODNAME ": " fmt
 
 #include <linux/clk.h>
+#include <linux/clk-provider.h>
 #include <linux/cpufreq.h>
 #include <linux/cpu_cooling.h>
 #include <linux/errno.h>
@@ -22,10 +23,6 @@
 #include <linux/slab.h>
 #include <linux/smp.h>
 
-#if !defined(CONFIG_ARM)
-#include <asm/smp.h>   /* for get_hard_smp_processor_id() in UP configs */
-#endif
-
 /**
  * struct cpu_data
  * @pclk: the parent clock of cpu
@@ -37,73 +34,51 @@ struct cpu_data {
        struct thermal_cooling_device *cdev;
 };
 
+/*
+ * Don't use cpufreq on this SoC -- used when the SoC would have otherwise
+ * matched a more generic compatible.
+ */
+#define SOC_BLACKLIST          1
+
 /**
  * struct soc_data - SoC specific data
- * @freq_mask: mask the disallowed frequencies
- * @flag: unique flags
+ * @flags: SOC_xxx
  */
 struct soc_data {
-       u32 freq_mask[4];
-       u32 flag;
-};
-
-#define FREQ_MASK      1
-/* see hardware specification for the allowed frqeuencies */
-static const struct soc_data sdata[] = {
-       { /* used by p2041 and p3041 */
-               .freq_mask = {0x8, 0x8, 0x2, 0x2},
-               .flag = FREQ_MASK,
-       },
-       { /* used by p5020 */
-               .freq_mask = {0x8, 0x2},
-               .flag = FREQ_MASK,
-       },
-       { /* used by p4080, p5040 */
-               .freq_mask = {0},
-               .flag = 0,
-       },
+       u32 flags;
 };
 
-/*
- * the minimum allowed core frequency, in Hz
- * for chassis v1.0, >= platform frequency
- * for chassis v2.0, >= platform frequency / 2
- */
-static u32 min_cpufreq;
-static const u32 *fmask;
-
-#if defined(CONFIG_ARM)
-static int get_cpu_physical_id(int cpu)
-{
-       return topology_core_id(cpu);
-}
-#else
-static int get_cpu_physical_id(int cpu)
-{
-       return get_hard_smp_processor_id(cpu);
-}
-#endif
-
 static u32 get_bus_freq(void)
 {
        struct device_node *soc;
        u32 sysfreq;
+       struct clk *pltclk;
+       int ret;
 
+       /* get platform freq by searching bus-frequency property */
        soc = of_find_node_by_type(NULL, "soc");
-       if (!soc)
-               return 0;
-
-       if (of_property_read_u32(soc, "bus-frequency", &sysfreq))
-               sysfreq = 0;
+       if (soc) {
+               ret = of_property_read_u32(soc, "bus-frequency", &sysfreq);
+               of_node_put(soc);
+               if (!ret)
+                       return sysfreq;
+       }
 
-       of_node_put(soc);
+       /* get platform freq by its clock name */
+       pltclk = clk_get(NULL, "cg-pll0-div1");
+       if (IS_ERR(pltclk)) {
+               pr_err("%s: can't get bus frequency %ld\n",
+                      __func__, PTR_ERR(pltclk));
+               return PTR_ERR(pltclk);
+       }
 
-       return sysfreq;
+       return clk_get_rate(pltclk);
 }
 
-static struct device_node *cpu_to_clk_node(int cpu)
+static struct clk *cpu_to_clk(int cpu)
 {
-       struct device_node *np, *clk_np;
+       struct device_node *np;
+       struct clk *clk;
 
        if (!cpu_present(cpu))
                return NULL;
@@ -112,37 +87,28 @@ static struct device_node *cpu_to_clk_no
        if (!np)
                return NULL;
 
-       clk_np = of_parse_phandle(np, "clocks", 0);
-       if (!clk_np)
-               return NULL;
-
+       clk = of_clk_get(np, 0);
        of_node_put(np);
-
-       return clk_np;
+       return clk;
 }
 
 /* traverse cpu nodes to get cpu mask of sharing clock wire */
 static void set_affected_cpus(struct cpufreq_policy *policy)
 {
-       struct device_node *np, *clk_np;
        struct cpumask *dstp = policy->cpus;
+       struct clk *clk;
        int i;
 
-       np = cpu_to_clk_node(policy->cpu);
-       if (!np)
-               return;
-
        for_each_present_cpu(i) {
-               clk_np = cpu_to_clk_node(i);
-               if (!clk_np)
+               clk = cpu_to_clk(i);
+               if (IS_ERR(clk)) {
+                       pr_err("%s: no clock for cpu %d\n", __func__, i);
                        continue;
+               }
 
-               if (clk_np == np)
+               if (clk_is_match(policy->clk, clk))
                        cpumask_set_cpu(i, dstp);
-
-               of_node_put(clk_np);
        }
-       of_node_put(np);
 }
 
 /* reduce the duplicated frequencies in frequency table */
@@ -198,10 +164,11 @@ static void freq_table_sort(struct cpufr
 
 static int qoriq_cpufreq_cpu_init(struct cpufreq_policy *policy)
 {
-       struct device_node *np, *pnode;
+       struct device_node *np;
        int i, count, ret;
-       u32 freq, mask;
+       u32 freq;
        struct clk *clk;
+       const struct clk_hw *hwclk;
        struct cpufreq_frequency_table *table;
        struct cpu_data *data;
        unsigned int cpu = policy->cpu;
@@ -221,17 +188,13 @@ static int qoriq_cpufreq_cpu_init(struct
                goto err_nomem2;
        }
 
-       pnode = of_parse_phandle(np, "clocks", 0);
-       if (!pnode) {
-               pr_err("%s: could not get clock information\n", __func__);
-               goto err_nomem2;
-       }
+       hwclk = __clk_get_hw(policy->clk);
+       count = clk_hw_get_num_parents(hwclk);
 
-       count = of_property_count_strings(pnode, "clock-names");
        data->pclk = kcalloc(count, sizeof(struct clk *), GFP_KERNEL);
        if (!data->pclk) {
                pr_err("%s: no memory\n", __func__);
-               goto err_node;
+               goto err_nomem2;
        }
 
        table = kcalloc(count + 1, sizeof(*table), GFP_KERNEL);
@@ -240,23 +203,11 @@ static int qoriq_cpufreq_cpu_init(struct
                goto err_pclk;
        }
 
-       if (fmask)
-               mask = fmask[get_cpu_physical_id(cpu)];
-       else
-               mask = 0x0;
-
        for (i = 0; i < count; i++) {
-               clk = of_clk_get(pnode, i);
+               clk = clk_hw_get_parent_by_index(hwclk, i)->clk;
                data->pclk[i] = clk;
                freq = clk_get_rate(clk);
-               /*
-                * the clock is valid if its frequency is not masked
-                * and large than minimum allowed frequency.
-                */
-               if (freq < min_cpufreq || (mask & (1 << i)))
-                       table[i].frequency = CPUFREQ_ENTRY_INVALID;
-               else
-                       table[i].frequency = freq / 1000;
+               table[i].frequency = freq / 1000;
                table[i].driver_data = i;
        }
        freq_table_redup(table, count);
@@ -282,7 +233,6 @@ static int qoriq_cpufreq_cpu_init(struct
        policy->cpuinfo.transition_latency = u64temp + 1;
 
        of_node_put(np);
-       of_node_put(pnode);
 
        return 0;
 
@@ -290,10 +240,7 @@ err_nomem1:
        kfree(table);
 err_pclk:
        kfree(data->pclk);
-err_node:
-       of_node_put(pnode);
 err_nomem2:
-       policy->driver_data = NULL;
        kfree(data);
 err_np:
        of_node_put(np);
@@ -357,12 +304,25 @@ static struct cpufreq_driver qoriq_cpufr
        .attr           = cpufreq_generic_attr,
 };
 
+static const struct soc_data blacklist = {
+       .flags = SOC_BLACKLIST,
+};
+
 static const struct of_device_id node_matches[] __initconst = {
-       { .compatible = "fsl,p2041-clockgen", .data = &sdata[0], },
-       { .compatible = "fsl,p3041-clockgen", .data = &sdata[0], },
-       { .compatible = "fsl,p5020-clockgen", .data = &sdata[1], },
-       { .compatible = "fsl,p4080-clockgen", .data = &sdata[2], },
-       { .compatible = "fsl,p5040-clockgen", .data = &sdata[2], },
+       /* e6500 cannot use cpufreq due to erratum A-008083 */
+       { .compatible = "fsl,b4420-clockgen", &blacklist },
+       { .compatible = "fsl,b4860-clockgen", &blacklist },
+       { .compatible = "fsl,t2080-clockgen", &blacklist },
+       { .compatible = "fsl,t4240-clockgen", &blacklist },
+
+       { .compatible = "fsl,ls1012a-clockgen", },
+       { .compatible = "fsl,ls1021a-clockgen", },
+       { .compatible = "fsl,ls1043a-clockgen", },
+       { .compatible = "fsl,ls1046a-clockgen", },
+       { .compatible = "fsl,ls1088a-clockgen", },
+       { .compatible = "fsl,ls2080a-clockgen", },
+       { .compatible = "fsl,p4080-clockgen", },
+       { .compatible = "fsl,qoriq-clockgen-1.0", },
        { .compatible = "fsl,qoriq-clockgen-2.0", },
        {}
 };
@@ -380,16 +340,12 @@ static int __init qoriq_cpufreq_init(voi
 
        match = of_match_node(node_matches, np);
        data = match->data;
-       if (data) {
-               if (data->flag)
-                       fmask = data->freq_mask;
-               min_cpufreq = get_bus_freq();
-       } else {
-               min_cpufreq = get_bus_freq() / 2;
-       }
 
        of_node_put(np);
 
+       if (data && data->flags & SOC_BLACKLIST)
+               return -ENODEV;
+
        ret = cpufreq_register_driver(&qoriq_cpufreq_driver);
        if (!ret)
                pr_info("Freescale QorIQ CPU frequency scaling driver\n");
--- a/drivers/firmware/psci.c
+++ b/drivers/firmware/psci.c
@@ -437,8 +437,12 @@ CPUIDLE_METHOD_OF_DECLARE(psci, "psci",
 
 static int psci_system_suspend(unsigned long unused)
 {
-       return invoke_psci_fn(PSCI_FN_NATIVE(1_0, SYSTEM_SUSPEND),
-                             virt_to_phys(cpu_resume), 0, 0);
+       u32 state;
+
+       state = ( 2 << PSCI_0_2_POWER_STATE_AFFL_SHIFT) |
+               (1 << PSCI_0_2_POWER_STATE_TYPE_SHIFT);
+
+       return psci_cpu_suspend(state, virt_to_phys(cpu_resume));
 }
 
 static int psci_system_suspend_enter(suspend_state_t state)
@@ -458,6 +462,8 @@ static void __init psci_init_system_susp
        if (!IS_ENABLED(CONFIG_SUSPEND))
                return;
 
+       suspend_set_ops(&psci_suspend_ops);
+
        ret = psci_features(PSCI_FN_NATIVE(1_0, SYSTEM_SUSPEND));
 
        if (ret != PSCI_RET_NOT_SUPPORTED)
@@ -562,6 +568,8 @@ static void __init psci_0_2_set_function
        arm_pm_restart = psci_sys_reset;
 
        pm_power_off = psci_sys_poweroff;
+       psci_init_system_suspend();
+       suspend_set_ops(&psci_suspend_ops);
 }
 
 /*
--- /dev/null
+++ b/drivers/soc/fsl/rcpm.c
@@ -0,0 +1,158 @@
+/*
+ * Run Control and Power Management (RCPM) driver
+ *
+ * Copyright 2016 NXP
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+#define pr_fmt(fmt) "RCPM: %s: " fmt, __func__
+
+#include <linux/kernel.h>
+#include <linux/io.h>
+#include <linux/of_platform.h>
+#include <linux/of_address.h>
+#include <linux/suspend.h>
+
+/* RCPM register offset */
+#define RCPM_IPPDEXPCR0                        0x140
+
+#define RCPM_WAKEUP_CELL_SIZE  2
+
+struct rcpm_config {
+       int ipp_num;
+       int ippdexpcr_offset;
+       u32 ippdexpcr[2];
+       void *rcpm_reg_base;
+};
+
+static struct rcpm_config *rcpm;
+
+static inline void rcpm_reg_write(u32 offset, u32 value)
+{
+       iowrite32be(value, rcpm->rcpm_reg_base + offset);
+}
+
+static inline u32 rcpm_reg_read(u32 offset)
+{
+       return ioread32be(rcpm->rcpm_reg_base + offset);
+}
+
+static void rcpm_wakeup_fixup(struct device *dev, void *data)
+{
+       struct device_node *node = dev ? dev->of_node : NULL;
+       u32 value[RCPM_WAKEUP_CELL_SIZE];
+       int ret, i;
+
+       if (!dev || !node || !device_may_wakeup(dev))
+               return;
+
+       /*
+        * Get the values in the "rcpm-wakeup" property.
+        * Three values are:
+        * The first is a pointer to the RCPM node.
+        * The second is the value of the ippdexpcr0 register.
+        * The third is the value of the ippdexpcr1 register.
+        */
+       ret = of_property_read_u32_array(node, "fsl,rcpm-wakeup",
+                                        value, RCPM_WAKEUP_CELL_SIZE);
+       if (ret)
+               return;
+
+       pr_debug("wakeup source: the device %s\n", node->full_name);
+
+       for (i = 0; i < rcpm->ipp_num; i++)
+               rcpm->ippdexpcr[i] |= value[i + 1];
+}
+
+static int rcpm_suspend_prepare(void)
+{
+       int i;
+       u32 val;
+
+       BUG_ON(!rcpm);
+
+       for (i = 0; i < rcpm->ipp_num; i++)
+               rcpm->ippdexpcr[i] = 0;
+
+       dpm_for_each_dev(NULL, rcpm_wakeup_fixup);
+
+       for (i = 0; i < rcpm->ipp_num; i++) {
+               if (rcpm->ippdexpcr[i]) {
+                       val = rcpm_reg_read(rcpm->ippdexpcr_offset + 4 * i);
+                       rcpm_reg_write(rcpm->ippdexpcr_offset + 4 * i,
+                                              val | rcpm->ippdexpcr[i]);
+                       pr_debug("ippdexpcr%d = 0x%x\n", i, rcpm->ippdexpcr[i]);
+               }
+       }
+
+       return 0;
+}
+
+static int rcpm_suspend_notifier_call(struct notifier_block *bl,
+                                     unsigned long state,
+                                     void *unused)
+{
+       switch (state) {
+       case PM_SUSPEND_PREPARE:
+               rcpm_suspend_prepare();
+               break;
+       }
+
+       return NOTIFY_DONE;
+}
+
+static struct rcpm_config rcpm_default_config = {
+       .ipp_num = 1,
+       .ippdexpcr_offset = RCPM_IPPDEXPCR0,
+};
+
+static const struct of_device_id rcpm_matches[] = {
+       {
+               .compatible = "fsl,qoriq-rcpm-2.1",
+               .data = &rcpm_default_config,
+       },
+       {}
+};
+
+static struct notifier_block rcpm_suspend_notifier = {
+       .notifier_call = rcpm_suspend_notifier_call,
+};
+
+static int __init layerscape_rcpm_init(void)
+{
+       const struct of_device_id *match;
+       struct device_node *np;
+
+       np = of_find_matching_node_and_match(NULL, rcpm_matches, &match);
+       if (!np) {
+               pr_err("Can't find the RCPM node.\n");
+               return -EINVAL;
+       }
+
+       if (match->data)
+               rcpm = (struct rcpm_config *)match->data;
+       else
+               return -EINVAL;
+
+       rcpm->rcpm_reg_base = of_iomap(np, 0);
+       of_node_put(np);
+       if (!rcpm->rcpm_reg_base)
+               return -ENOMEM;
+
+       register_pm_notifier(&rcpm_suspend_notifier);
+
+       pr_info("The RCPM driver initialized.\n");
+
+       return 0;
+}
+
+subsys_initcall(layerscape_rcpm_init);

Generated by GNU Enscript 1.6.5.90.