362 lines
		
	
	
		
			9.2 KiB
		
	
	
	
		
			Diff
		
	
	
	
	
	
			
		
		
	
	
			362 lines
		
	
	
		
			9.2 KiB
		
	
	
	
		
			Diff
		
	
	
	
	
	
| From a9ebdf9fa18fd317a4e97f46e8c5263898094864 Mon Sep 17 00:00:00 2001
 | |
| From: Yangbo Lu <yangbo.lu@nxp.com>
 | |
| Date: Mon, 25 Sep 2017 12:20:10 +0800
 | |
| Subject: [PATCH] cpufreq: support layerscape
 | |
| 
 | |
| This is a 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 ++-
 | |
|  3 files changed, 77 insertions(+), 113 deletions(-)
 | |
| 
 | |
| --- a/drivers/cpufreq/Kconfig
 | |
| +++ b/drivers/cpufreq/Kconfig
 | |
| @@ -332,7 +332,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
 | |
| @@ -418,8 +418,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)
 | |
| @@ -439,6 +443,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)
 | |
| @@ -516,6 +522,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);
 | |
|  }
 | |
|  
 | |
|  /*
 | 
