From 748c876634870f8e535ddebd76f409f0477d3dd4 Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Thu, 28 Aug 2014 11:22:24 +0530 Subject: cpufreq: cpu0: Update Module Author Two people are maintaining it now, Viresh and Shawn. Add Viresh's details in MODULE_AUTHOR() and copyright section. Suggested-by: Shawn Guo Tested-by: Stephen Boyd Signed-off-by: Viresh Kumar Signed-off-by: Rafael J. Wysocki --- drivers/cpufreq/cpufreq-cpu0.c | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'drivers/cpufreq') diff --git a/drivers/cpufreq/cpufreq-cpu0.c b/drivers/cpufreq/cpufreq-cpu0.c index 0d2172b07765..4cfde6340967 100644 --- a/drivers/cpufreq/cpufreq-cpu0.c +++ b/drivers/cpufreq/cpufreq-cpu0.c @@ -1,6 +1,9 @@ /* * Copyright (C) 2012 Freescale Semiconductor, Inc. * + * Copyright (C) 2014 Linaro. + * Viresh Kumar + * * The OPP code in function cpu0_set_target() is reused from * drivers/cpufreq/omap-cpufreq.c * @@ -243,6 +246,7 @@ static struct platform_driver cpu0_cpufreq_platdrv = { }; module_platform_driver(cpu0_cpufreq_platdrv); +MODULE_AUTHOR("Viresh Kumar "); MODULE_AUTHOR("Shawn Guo "); MODULE_DESCRIPTION("Generic CPU0 cpufreq driver"); MODULE_LICENSE("GPL"); -- cgit v1.2.3 From ed4b053cb864f29f57cf5a4c3f3c85cda22edaf1 Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Thu, 28 Aug 2014 11:22:25 +0530 Subject: cpufreq: cpu0: don't validate clock on clk_put() CPU clk is not optional for this driver and probe would fail if it couldn't find a suitable clock. And so, while calling clk_put() we don't need to validate clocks. Acked-by: Santosh Shilimkar Tested-by: Stephen Boyd Signed-off-by: Viresh Kumar Signed-off-by: Rafael J. Wysocki --- drivers/cpufreq/cpufreq-cpu0.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'drivers/cpufreq') diff --git a/drivers/cpufreq/cpufreq-cpu0.c b/drivers/cpufreq/cpufreq-cpu0.c index 4cfde6340967..d2dc9216a2f2 100644 --- a/drivers/cpufreq/cpufreq-cpu0.c +++ b/drivers/cpufreq/cpufreq-cpu0.c @@ -217,8 +217,7 @@ static int cpu0_cpufreq_probe(struct platform_device *pdev) out_free_table: dev_pm_opp_free_cpufreq_table(cpu_dev, &freq_table); out_put_clk: - if (!IS_ERR(cpu_clk)) - clk_put(cpu_clk); + clk_put(cpu_clk); out_put_reg: if (!IS_ERR(cpu_reg)) regulator_put(cpu_reg); -- cgit v1.2.3 From 48a8624b3abe39bd66490e3ab692a74a73b582eb Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Thu, 28 Aug 2014 11:22:26 +0530 Subject: cpufreq: cpu0: print relevant error when we defer probe Currently, we defer probe if regulator_get() returned -EPROBE_DEFER, i.e. regulator isn't registered yet. We do a dev_err() in this case. Sending a message to the log on probe defer just duplicates what the driver core is already doing. Convert it to dev_dbg() instead. We should defer in case of clk_get() as well. Current code already does it, but it wasn't intentional probably. Its just that we are returning the right error with wrong print message. Fix print message to convey right error. Tested-by: Stephen Boyd Signed-off-by: Viresh Kumar Signed-off-by: Rafael J. Wysocki --- drivers/cpufreq/cpufreq-cpu0.c | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) (limited to 'drivers/cpufreq') diff --git a/drivers/cpufreq/cpufreq-cpu0.c b/drivers/cpufreq/cpufreq-cpu0.c index d2dc9216a2f2..eb07e3f996b3 100644 --- a/drivers/cpufreq/cpufreq-cpu0.c +++ b/drivers/cpufreq/cpufreq-cpu0.c @@ -151,7 +151,16 @@ static int cpu0_cpufreq_probe(struct platform_device *pdev) cpu_clk = clk_get(cpu_dev, NULL); if (IS_ERR(cpu_clk)) { ret = PTR_ERR(cpu_clk); - pr_err("failed to get cpu0 clock: %d\n", ret); + + /* + * If cpu's clk node is present, but clock is not yet + * registered, we should try defering probe. + */ + if (ret == -EPROBE_DEFER) + dev_dbg(cpu_dev, "cpu0 clock not ready, retry\n"); + else + dev_err(cpu_dev, "failed to get cpu0 clock: %d\n", ret); + goto out_put_reg; } -- cgit v1.2.3 From fbd48ca5911b3cd70da57c3313d13004e40aea54 Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Thu, 28 Aug 2014 11:22:27 +0530 Subject: cpufreq: cpu0: use dev_{err|warn|dbg} instead of pr_{err|warn|debug} We already have cpu_dev and is used at multiple places for printing errors using dev_*(). But some prints are still using pr_*(). Lets make it consistent and replace those pr_*() macros with dev_*() macros. Acked-by: Santosh Shilimkar Tested-by: Stephen Boyd Signed-off-by: Viresh Kumar Signed-off-by: Rafael J. Wysocki --- drivers/cpufreq/cpufreq-cpu0.c | 32 ++++++++++++++++++-------------- 1 file changed, 18 insertions(+), 14 deletions(-) (limited to 'drivers/cpufreq') diff --git a/drivers/cpufreq/cpufreq-cpu0.c b/drivers/cpufreq/cpufreq-cpu0.c index eb07e3f996b3..741ff220f9bf 100644 --- a/drivers/cpufreq/cpufreq-cpu0.c +++ b/drivers/cpufreq/cpufreq-cpu0.c @@ -58,7 +58,8 @@ static int cpu0_set_target(struct cpufreq_policy *policy, unsigned int index) opp = dev_pm_opp_find_freq_ceil(cpu_dev, &freq_Hz); if (IS_ERR(opp)) { rcu_read_unlock(); - pr_err("failed to find OPP for %ld\n", freq_Hz); + dev_err(cpu_dev, "failed to find OPP for %ld\n", + freq_Hz); return PTR_ERR(opp); } volt = dev_pm_opp_get_voltage(opp); @@ -67,22 +68,23 @@ static int cpu0_set_target(struct cpufreq_policy *policy, unsigned int index) volt_old = regulator_get_voltage(cpu_reg); } - pr_debug("%u MHz, %ld mV --> %u MHz, %ld mV\n", - old_freq / 1000, volt_old ? volt_old / 1000 : -1, - new_freq / 1000, volt ? volt / 1000 : -1); + dev_dbg(cpu_dev, "%u MHz, %ld mV --> %u MHz, %ld mV\n", + old_freq / 1000, volt_old ? volt_old / 1000 : -1, + new_freq / 1000, volt ? volt / 1000 : -1); /* scaling up? scale voltage before frequency */ if (!IS_ERR(cpu_reg) && new_freq > old_freq) { ret = regulator_set_voltage_tol(cpu_reg, volt, tol); if (ret) { - pr_err("failed to scale voltage up: %d\n", ret); + dev_err(cpu_dev, "failed to scale voltage up: %d\n", + ret); return ret; } } ret = clk_set_rate(cpu_clk, freq_exact); if (ret) { - pr_err("failed to set clock rate: %d\n", ret); + dev_err(cpu_dev, "failed to set clock rate: %d\n", ret); if (!IS_ERR(cpu_reg)) regulator_set_voltage_tol(cpu_reg, volt_old, tol); return ret; @@ -92,7 +94,8 @@ static int cpu0_set_target(struct cpufreq_policy *policy, unsigned int index) if (!IS_ERR(cpu_reg) && new_freq < old_freq) { ret = regulator_set_voltage_tol(cpu_reg, volt, tol); if (ret) { - pr_err("failed to scale voltage down: %d\n", ret); + dev_err(cpu_dev, "failed to scale voltage down: %d\n", + ret); clk_set_rate(cpu_clk, old_freq * 1000); } } @@ -129,7 +132,7 @@ static int cpu0_cpufreq_probe(struct platform_device *pdev) np = of_node_get(cpu_dev->of_node); if (!np) { - pr_err("failed to find cpu0 node\n"); + dev_err(cpu_dev, "failed to find cpu0 node\n"); return -ENOENT; } @@ -144,8 +147,8 @@ static int cpu0_cpufreq_probe(struct platform_device *pdev) ret = -EPROBE_DEFER; goto out_put_node; } - pr_warn("failed to get cpu0 regulator: %ld\n", - PTR_ERR(cpu_reg)); + dev_warn(cpu_dev, "failed to get cpu0 regulator: %ld\n", + PTR_ERR(cpu_reg)); } cpu_clk = clk_get(cpu_dev, NULL); @@ -169,7 +172,7 @@ static int cpu0_cpufreq_probe(struct platform_device *pdev) ret = dev_pm_opp_init_cpufreq_table(cpu_dev, &freq_table); if (ret) { - pr_err("failed to init cpufreq table: %d\n", ret); + dev_err(cpu_dev, "failed to init cpufreq table: %d\n", ret); goto out_put_clk; } @@ -205,7 +208,7 @@ static int cpu0_cpufreq_probe(struct platform_device *pdev) ret = cpufreq_register_driver(&cpu0_cpufreq_driver); if (ret) { - pr_err("failed register driver: %d\n", ret); + dev_err(cpu_dev, "failed to register driver: %d\n", ret); goto out_free_table; } @@ -216,8 +219,9 @@ static int cpu0_cpufreq_probe(struct platform_device *pdev) if (of_find_property(np, "#cooling-cells", NULL)) { cdev = of_cpufreq_cooling_register(np, cpu_present_mask); if (IS_ERR(cdev)) - pr_err("running cpufreq without cooling device: %ld\n", - PTR_ERR(cdev)); + dev_err(cpu_dev, + "running cpufreq without cooling device: %ld\n", + PTR_ERR(cdev)); } of_node_put(np); -- cgit v1.2.3 From d2f31f1da54f83c4eb2738402284c49cd51798d1 Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Thu, 28 Aug 2014 11:22:28 +0530 Subject: cpufreq: cpu0: Move per-cluster initialization code to ->init() Currently this driver only support platforms on which all CPUs share clock & voltage lines and there is requirement to support platforms which have separate clock & voltage lines for CPUs, like Qualcomm's Krait and ARM's big LITTLE. Each group of CPUs sharing clock/voltage lines are represented by 'struct cpufreq_policy' in cpufreq framework. And core calls ->init() once for each policy. Currently we do all initialization/allocation from probe() which wouldn't work for above scenario. To make it work for these platforms, the first step is to move all initialization/allocation to ->init() and add ->exit() to do the reverse of it. Also, remove all global variables and allocate space for them at runtime. This patch creates 'struct private_data' for keeping all such information and a pointer to that would be stored in policy->driver_data. The changed probe() routine now tries to see if regulator/clocks are available or we need to defer probe. In case they are available, it registers cpufreq driver. Otherwise, returns with -EPROBE_DEFER. We still *don't* support platforms with separate clock/voltage lines for CPUs. This would be done in a separate patch later. Tested-by: Stephen Boyd Signed-off-by: Viresh Kumar Signed-off-by: Rafael J. Wysocki --- drivers/cpufreq/cpufreq-cpu0.c | 189 +++++++++++++++++++++++++++++------------ 1 file changed, 136 insertions(+), 53 deletions(-) (limited to 'drivers/cpufreq') diff --git a/drivers/cpufreq/cpufreq-cpu0.c b/drivers/cpufreq/cpufreq-cpu0.c index 741ff220f9bf..03e352b627dd 100644 --- a/drivers/cpufreq/cpufreq-cpu0.c +++ b/drivers/cpufreq/cpufreq-cpu0.c @@ -28,18 +28,21 @@ #include #include -static unsigned int transition_latency; -static unsigned int voltage_tolerance; /* in percentage */ - -static struct device *cpu_dev; -static struct clk *cpu_clk; -static struct regulator *cpu_reg; -static struct cpufreq_frequency_table *freq_table; -static struct thermal_cooling_device *cdev; +struct private_data { + struct device *cpu_dev; + struct regulator *cpu_reg; + struct thermal_cooling_device *cdev; + unsigned int voltage_tolerance; /* in percentage */ +}; static int cpu0_set_target(struct cpufreq_policy *policy, unsigned int index) { struct dev_pm_opp *opp; + struct cpufreq_frequency_table *freq_table = policy->freq_table; + struct clk *cpu_clk = policy->clk; + struct private_data *priv = policy->driver_data; + struct device *cpu_dev = priv->cpu_dev; + struct regulator *cpu_reg = priv->cpu_reg; unsigned long volt = 0, volt_old = 0, tol = 0; unsigned int old_freq, new_freq; long freq_Hz, freq_exact; @@ -64,7 +67,7 @@ static int cpu0_set_target(struct cpufreq_policy *policy, unsigned int index) } volt = dev_pm_opp_get_voltage(opp); rcu_read_unlock(); - tol = volt * voltage_tolerance / 100; + tol = volt * priv->voltage_tolerance / 100; volt_old = regulator_get_voltage(cpu_reg); } @@ -103,26 +106,13 @@ static int cpu0_set_target(struct cpufreq_policy *policy, unsigned int index) return ret; } -static int cpu0_cpufreq_init(struct cpufreq_policy *policy) -{ - policy->clk = cpu_clk; - return cpufreq_generic_init(policy, freq_table, transition_latency); -} - -static struct cpufreq_driver cpu0_cpufreq_driver = { - .flags = CPUFREQ_STICKY | CPUFREQ_NEED_INITIAL_FREQ_CHECK, - .verify = cpufreq_generic_frequency_table_verify, - .target_index = cpu0_set_target, - .get = cpufreq_generic_get, - .init = cpu0_cpufreq_init, - .name = "generic_cpu0", - .attr = cpufreq_generic_attr, -}; - -static int cpu0_cpufreq_probe(struct platform_device *pdev) +static int allocate_resources(struct device **cdev, + struct regulator **creg, struct clk **cclk) { - struct device_node *np; - int ret; + struct device *cpu_dev; + struct regulator *cpu_reg; + struct clk *cpu_clk; + int ret = 0; cpu_dev = get_cpu_device(0); if (!cpu_dev) { @@ -130,12 +120,6 @@ static int cpu0_cpufreq_probe(struct platform_device *pdev) return -ENODEV; } - np = of_node_get(cpu_dev->of_node); - if (!np) { - dev_err(cpu_dev, "failed to find cpu0 node\n"); - return -ENOENT; - } - cpu_reg = regulator_get_optional(cpu_dev, "cpu0"); if (IS_ERR(cpu_reg)) { /* @@ -144,8 +128,7 @@ static int cpu0_cpufreq_probe(struct platform_device *pdev) */ if (PTR_ERR(cpu_reg) == -EPROBE_DEFER) { dev_dbg(cpu_dev, "cpu0 regulator not ready, retry\n"); - ret = -EPROBE_DEFER; - goto out_put_node; + return -EPROBE_DEFER; } dev_warn(cpu_dev, "failed to get cpu0 regulator: %ld\n", PTR_ERR(cpu_reg)); @@ -153,6 +136,10 @@ static int cpu0_cpufreq_probe(struct platform_device *pdev) cpu_clk = clk_get(cpu_dev, NULL); if (IS_ERR(cpu_clk)) { + /* put regulator */ + if (!IS_ERR(cpu_reg)) + regulator_put(cpu_reg); + ret = PTR_ERR(cpu_clk); /* @@ -163,8 +150,39 @@ static int cpu0_cpufreq_probe(struct platform_device *pdev) dev_dbg(cpu_dev, "cpu0 clock not ready, retry\n"); else dev_err(cpu_dev, "failed to get cpu0 clock: %d\n", ret); + } else { + *cdev = cpu_dev; + *creg = cpu_reg; + *cclk = cpu_clk; + } + + return ret; +} + +static int cpu0_cpufreq_init(struct cpufreq_policy *policy) +{ + struct cpufreq_frequency_table *freq_table; + struct thermal_cooling_device *cdev; + struct device_node *np; + struct private_data *priv; + struct device *cpu_dev; + struct regulator *cpu_reg; + struct clk *cpu_clk; + unsigned int transition_latency; + int ret; + + /* We only support cpu0 currently */ + ret = allocate_resources(&cpu_dev, &cpu_reg, &cpu_clk); + if (ret) { + pr_err("%s: Failed to allocate resources\n: %d", __func__, ret); + return ret; + } - goto out_put_reg; + np = of_node_get(cpu_dev->of_node); + if (!np) { + dev_err(cpu_dev, "failed to find cpu%d node\n", policy->cpu); + ret = -ENOENT; + goto out_put_reg_clk; } /* OPPs might be populated at runtime, don't check for error here */ @@ -173,10 +191,16 @@ static int cpu0_cpufreq_probe(struct platform_device *pdev) ret = dev_pm_opp_init_cpufreq_table(cpu_dev, &freq_table); if (ret) { dev_err(cpu_dev, "failed to init cpufreq table: %d\n", ret); - goto out_put_clk; + goto out_put_node; + } + + priv = kzalloc(sizeof(*priv), GFP_KERNEL); + if (!priv) { + ret = -ENOMEM; + goto out_free_table; } - of_property_read_u32(np, "voltage-tolerance", &voltage_tolerance); + of_property_read_u32(np, "voltage-tolerance", &priv->voltage_tolerance); if (of_property_read_u32(np, "clock-latency", &transition_latency)) transition_latency = CPUFREQ_ETERNAL; @@ -206,12 +230,6 @@ static int cpu0_cpufreq_probe(struct platform_device *pdev) transition_latency += ret * 1000; } - ret = cpufreq_register_driver(&cpu0_cpufreq_driver); - if (ret) { - dev_err(cpu_dev, "failed to register driver: %d\n", ret); - goto out_free_table; - } - /* * For now, just loading the cooling device; * thermal DT code takes care of matching them. @@ -222,29 +240,94 @@ static int cpu0_cpufreq_probe(struct platform_device *pdev) dev_err(cpu_dev, "running cpufreq without cooling device: %ld\n", PTR_ERR(cdev)); + else + priv->cdev = cdev; } - of_node_put(np); + + priv->cpu_dev = cpu_dev; + priv->cpu_reg = cpu_reg; + policy->driver_data = priv; + + policy->clk = cpu_clk; + ret = cpufreq_generic_init(policy, freq_table, transition_latency); + if (ret) + goto out_cooling_unregister; + return 0; +out_cooling_unregister: + cpufreq_cooling_unregister(priv->cdev); + kfree(priv); out_free_table: dev_pm_opp_free_cpufreq_table(cpu_dev, &freq_table); -out_put_clk: +out_put_node: + of_node_put(np); +out_put_reg_clk: clk_put(cpu_clk); -out_put_reg: if (!IS_ERR(cpu_reg)) regulator_put(cpu_reg); -out_put_node: - of_node_put(np); + + return ret; +} + +static int cpu0_cpufreq_exit(struct cpufreq_policy *policy) +{ + struct private_data *priv = policy->driver_data; + + cpufreq_cooling_unregister(priv->cdev); + dev_pm_opp_free_cpufreq_table(priv->cpu_dev, &policy->freq_table); + clk_put(policy->clk); + if (!IS_ERR(priv->cpu_reg)) + regulator_put(priv->cpu_reg); + kfree(priv); + + return 0; +} + +static struct cpufreq_driver cpu0_cpufreq_driver = { + .flags = CPUFREQ_STICKY | CPUFREQ_NEED_INITIAL_FREQ_CHECK, + .verify = cpufreq_generic_frequency_table_verify, + .target_index = cpu0_set_target, + .get = cpufreq_generic_get, + .init = cpu0_cpufreq_init, + .exit = cpu0_cpufreq_exit, + .name = "generic_cpu0", + .attr = cpufreq_generic_attr, +}; + +static int cpu0_cpufreq_probe(struct platform_device *pdev) +{ + struct device *cpu_dev; + struct regulator *cpu_reg; + struct clk *cpu_clk; + int ret; + + /* + * All per-cluster (CPUs sharing clock/voltages) initialization is done + * from ->init(). In probe(), we just need to make sure that clk and + * regulators are available. Else defer probe and retry. + * + * FIXME: Is checking this only for CPU0 sufficient ? + */ + ret = allocate_resources(&cpu_dev, &cpu_reg, &cpu_clk); + if (ret) + return ret; + + clk_put(cpu_clk); + if (!IS_ERR(cpu_reg)) + regulator_put(cpu_reg); + + ret = cpufreq_register_driver(&cpu0_cpufreq_driver); + if (ret) + dev_err(cpu_dev, "failed register driver: %d\n", ret); + return ret; } static int cpu0_cpufreq_remove(struct platform_device *pdev) { - cpufreq_cooling_unregister(cdev); cpufreq_unregister_driver(&cpu0_cpufreq_driver); - dev_pm_opp_free_cpufreq_table(cpu_dev, &freq_table); - return 0; } -- cgit v1.2.3 From 2d2c5e0e72546c1b4375ff5820ca8016c7123cbb Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Thu, 28 Aug 2014 11:22:29 +0530 Subject: cpufreq: cpu0: try regulators with name "cpu-supply" Currently, we expect regulator name to be "cpu0", but as we are going to support multiple cpu-blocks (all CPUs in a block share clock/voltage) later, we need to pass some generic string instead of that. For backwards compatibility try for "cpu0" first and if it fails, then try for "cpu". Suggested-by: Stephen Boyd Tested-by: Stephen Boyd Signed-off-by: Viresh Kumar Signed-off-by: Rafael J. Wysocki --- drivers/cpufreq/cpufreq-cpu0.c | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) (limited to 'drivers/cpufreq') diff --git a/drivers/cpufreq/cpufreq-cpu0.c b/drivers/cpufreq/cpufreq-cpu0.c index 03e352b627dd..de38952dab1a 100644 --- a/drivers/cpufreq/cpufreq-cpu0.c +++ b/drivers/cpufreq/cpufreq-cpu0.c @@ -113,6 +113,7 @@ static int allocate_resources(struct device **cdev, struct regulator *cpu_reg; struct clk *cpu_clk; int ret = 0; + char *reg_cpu0 = "cpu0", *reg_cpu = "cpu", *reg; cpu_dev = get_cpu_device(0); if (!cpu_dev) { @@ -120,7 +121,11 @@ static int allocate_resources(struct device **cdev, return -ENODEV; } - cpu_reg = regulator_get_optional(cpu_dev, "cpu0"); + /* Try "cpu0" for older DTs */ + reg = reg_cpu0; + +try_again: + cpu_reg = regulator_get_optional(cpu_dev, reg); if (IS_ERR(cpu_reg)) { /* * If cpu0 regulator supply node is present, but regulator is @@ -130,6 +135,13 @@ static int allocate_resources(struct device **cdev, dev_dbg(cpu_dev, "cpu0 regulator not ready, retry\n"); return -EPROBE_DEFER; } + + /* Try with "cpu-supply" */ + if (reg == reg_cpu0) { + reg = reg_cpu; + goto try_again; + } + dev_warn(cpu_dev, "failed to get cpu0 regulator: %ld\n", PTR_ERR(cpu_reg)); } -- cgit v1.2.3 From 95b6105835293a910484c5bcdd1599b8588959e9 Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Thu, 28 Aug 2014 11:22:30 +0530 Subject: cpufreq: cpu0: Make allocate_resources() work for any CPU Currently allocate_resources() supports only CPU0 and it would need to allocate resources for any CPU going forward. Add another argument to it, i.e. cpu, and update code accordingly. Tested-by: Stephen Boyd Signed-off-by: Viresh Kumar Signed-off-by: Rafael J. Wysocki --- drivers/cpufreq/cpufreq-cpu0.c | 30 +++++++++++++++++------------- 1 file changed, 17 insertions(+), 13 deletions(-) (limited to 'drivers/cpufreq') diff --git a/drivers/cpufreq/cpufreq-cpu0.c b/drivers/cpufreq/cpufreq-cpu0.c index de38952dab1a..a5f8c5f5f4f4 100644 --- a/drivers/cpufreq/cpufreq-cpu0.c +++ b/drivers/cpufreq/cpufreq-cpu0.c @@ -106,7 +106,7 @@ static int cpu0_set_target(struct cpufreq_policy *policy, unsigned int index) return ret; } -static int allocate_resources(struct device **cdev, +static int allocate_resources(int cpu, struct device **cdev, struct regulator **creg, struct clk **cclk) { struct device *cpu_dev; @@ -115,24 +115,28 @@ static int allocate_resources(struct device **cdev, int ret = 0; char *reg_cpu0 = "cpu0", *reg_cpu = "cpu", *reg; - cpu_dev = get_cpu_device(0); + cpu_dev = get_cpu_device(cpu); if (!cpu_dev) { - pr_err("failed to get cpu0 device\n"); + pr_err("failed to get cpu%d device\n", cpu); return -ENODEV; } /* Try "cpu0" for older DTs */ - reg = reg_cpu0; + if (!cpu) + reg = reg_cpu0; + else + reg = reg_cpu; try_again: cpu_reg = regulator_get_optional(cpu_dev, reg); if (IS_ERR(cpu_reg)) { /* - * If cpu0 regulator supply node is present, but regulator is + * If cpu's regulator supply node is present, but regulator is * not yet registered, we should try defering probe. */ if (PTR_ERR(cpu_reg) == -EPROBE_DEFER) { - dev_dbg(cpu_dev, "cpu0 regulator not ready, retry\n"); + dev_dbg(cpu_dev, "cpu%d regulator not ready, retry\n", + cpu); return -EPROBE_DEFER; } @@ -142,8 +146,8 @@ try_again: goto try_again; } - dev_warn(cpu_dev, "failed to get cpu0 regulator: %ld\n", - PTR_ERR(cpu_reg)); + dev_warn(cpu_dev, "failed to get cpu%d regulator: %ld\n", + cpu, PTR_ERR(cpu_reg)); } cpu_clk = clk_get(cpu_dev, NULL); @@ -159,9 +163,10 @@ try_again: * registered, we should try defering probe. */ if (ret == -EPROBE_DEFER) - dev_dbg(cpu_dev, "cpu0 clock not ready, retry\n"); + dev_dbg(cpu_dev, "cpu%d clock not ready, retry\n", cpu); else - dev_err(cpu_dev, "failed to get cpu0 clock: %d\n", ret); + dev_err(cpu_dev, "failed to get cpu%d clock: %d\n", ret, + cpu); } else { *cdev = cpu_dev; *creg = cpu_reg; @@ -183,8 +188,7 @@ static int cpu0_cpufreq_init(struct cpufreq_policy *policy) unsigned int transition_latency; int ret; - /* We only support cpu0 currently */ - ret = allocate_resources(&cpu_dev, &cpu_reg, &cpu_clk); + ret = allocate_resources(policy->cpu, &cpu_dev, &cpu_reg, &cpu_clk); if (ret) { pr_err("%s: Failed to allocate resources\n: %d", __func__, ret); return ret; @@ -322,7 +326,7 @@ static int cpu0_cpufreq_probe(struct platform_device *pdev) * * FIXME: Is checking this only for CPU0 sufficient ? */ - ret = allocate_resources(&cpu_dev, &cpu_reg, &cpu_clk); + ret = allocate_resources(0, &cpu_dev, &cpu_reg, &cpu_clk); if (ret) return ret; -- cgit v1.2.3 From 789ca243740de236a39146fc3d3bbaeb4d3ae0ba Mon Sep 17 00:00:00 2001 From: Preeti U Murthy Date: Mon, 29 Sep 2014 15:47:12 +0200 Subject: cpufreq: Allow stop CPU callback to be used by all cpufreq drivers Commit 367dc4aa932bfb3 ("cpufreq: Add stop CPU callback to cpufreq_driver interface") introduced the stop CPU callback for intel_pstate drivers. During the CPU_DOWN_PREPARE stage, this callback is invoked so that drivers can take some action on the pstate of the cpu before it is taken offline. This callback was assumed to be useful only for those drivers which have implemented the set_policy CPU callback because they have no other way to take action about the cpufreq of a CPU which is being hotplugged out except in the exit callback which is called very late in the offline process. The drivers which implement the target/target_index callbacks were expected to take care of requirements like the ones that commit 367dc4aa addresses in the GOV_STOP notification event. But there are disadvantages to restricting the usage of stop CPU callback to cpufreq drivers that implement the set_policy callbacks and who want to take explicit action on the setting the cpufreq during a hotplug operation. 1.GOV_STOP gets called for every CPU offline and drivers would usually want to take action when the last cpu in the policy->cpus mask is taken offline. As long as there is more than one cpu in the policy->cpus mask, cpufreq core itself makes sure that the freq for the other cpus in this mask is set according to the maximum load. This is sensible and drivers which implement the target_index callback would mostly not want to modify that. However the cpufreq core leaves a loose end when the cpu in the policy->cpus mask is the last one to go offline; it does nothing explicit to the frequency of the core. Drivers may need a way to take some action here and stop CPU callback mechanism is the best way to do it today. 2. We cannot implement driver specific actions in the GOV_STOP mechanism. So we will need another driver callback which is invoked from here which is unnecessary. Therefore this patch extends the usage of stop CPU callback to be used by all cpufreq drivers as long as they have this callback implemented and irrespective of whether they are set_policy/target_index drivers. The assumption is if the drivers find the GOV_STOP path to be a suitable way of implementing what they want to do with the freq of the cpu going offine,they will not implement the stop CPU callback at all. Signed-off-by: Preeti U Murthy Acked-by: Viresh Kumar Signed-off-by: Rafael J. Wysocki --- drivers/cpufreq/cpufreq.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/cpufreq') diff --git a/drivers/cpufreq/cpufreq.c b/drivers/cpufreq/cpufreq.c index d9fdeddcef96..6463f35f8be8 100644 --- a/drivers/cpufreq/cpufreq.c +++ b/drivers/cpufreq/cpufreq.c @@ -1380,7 +1380,7 @@ static int __cpufreq_remove_dev_prepare(struct device *dev, if (!cpufreq_suspended) pr_debug("%s: policy Kobject moved to cpu: %d from: %d\n", __func__, new_cpu, cpu); - } else if (cpufreq_driver->stop_cpu && cpufreq_driver->setpolicy) { + } else if (cpufreq_driver->stop_cpu) { cpufreq_driver->stop_cpu(policy); } -- cgit v1.2.3 From b120339c787b243cdbe3f67401b4aa5625d88ff3 Mon Sep 17 00:00:00 2001 From: Preeti U Murthy Date: Mon, 29 Sep 2014 15:47:53 +0200 Subject: cpufreq: powernv: Set the pstate of the last hotplugged out cpu in policy->cpus to minimum Its possible today that the pstate of a core is held at a high even after the entire core is hotplugged out if a load had just run on the hotplugged cpu. This is fair, since it is assumed that the pstate does not matter to a cpu in a deep idle state, which is the expected state of a hotplugged core on powerpc. However on powerpc, the pstate at a socket level is held at the maximum of the pstates of each core. Even if the pstates of the active cores on that socket is low, the socket pstate is held high due to the pstate of the hotplugged core in the above mentioned scenario. This can cost significant amount of power loss for no good. Besides, since it is a non active core, nothing can be done from the kernel's end to set the frequency of the core right. Hence make use of the stop_cpu callback to explicitly set the pstate of the core to a minimum when the last cpu of the core gets hotplugged out. Signed-off-by: Preeti U Murthy Acked-by: Viresh Kumar Signed-off-by: Rafael J. Wysocki --- drivers/cpufreq/powernv-cpufreq.c | 9 +++++++++ 1 file changed, 9 insertions(+) (limited to 'drivers/cpufreq') diff --git a/drivers/cpufreq/powernv-cpufreq.c b/drivers/cpufreq/powernv-cpufreq.c index 379c0837f5a9..5a628f1e2cdf 100644 --- a/drivers/cpufreq/powernv-cpufreq.c +++ b/drivers/cpufreq/powernv-cpufreq.c @@ -317,6 +317,14 @@ static int powernv_cpufreq_cpu_init(struct cpufreq_policy *policy) return cpufreq_table_validate_and_show(policy, powernv_freqs); } +static void powernv_cpufreq_stop_cpu(struct cpufreq_policy *policy) +{ + struct powernv_smp_call_data freq_data; + + freq_data.pstate_id = powernv_pstate_info.min; + smp_call_function_single(policy->cpu, set_pstate, &freq_data, 1); +} + static struct cpufreq_driver powernv_cpufreq_driver = { .name = "powernv-cpufreq", .flags = CPUFREQ_CONST_LOOPS, @@ -324,6 +332,7 @@ static struct cpufreq_driver powernv_cpufreq_driver = { .verify = cpufreq_generic_frequency_table_verify, .target_index = powernv_cpufreq_target_index, .get = powernv_cpufreq_get, + .stop_cpu = powernv_cpufreq_stop_cpu, .attr = powernv_cpu_freq_attr, }; -- cgit v1.2.3 From cf30af76952b0cb20c4a2cf8db16d69ca59eb652 Mon Sep 17 00:00:00 2001 From: Shilpasri G Bhat Date: Mon, 29 Sep 2014 15:49:11 +0200 Subject: cpufreq: powernv: Set the cpus to nominal frequency during reboot/kexec This patch ensures the cpus to kexec/reboot at nominal frequency. Nominal frequency is the highest cpu frequency on PowerPC at which the cores can run without getting throttled. If the host kernel had set the cpus to a low pstate and then it kexecs/reboots to a cpufreq disabled kernel it would cause the target kernel to perform poorly. It will also increase the boot up time of the target kernel. So set the cpus to high pstate, in this case to nominal frequency before rebooting to avoid such scenarios. The reboot notifier will set the cpus to nominal frequncy. Signed-off-by: Shilpasri G Bhat Reviewed-by: Preeti U Murthy Acked-by: Viresh Kumar Signed-off-by: Rafael J. Wysocki --- drivers/cpufreq/powernv-cpufreq.c | 35 +++++++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) (limited to 'drivers/cpufreq') diff --git a/drivers/cpufreq/powernv-cpufreq.c b/drivers/cpufreq/powernv-cpufreq.c index 5a628f1e2cdf..2dfd4fdb5a52 100644 --- a/drivers/cpufreq/powernv-cpufreq.c +++ b/drivers/cpufreq/powernv-cpufreq.c @@ -26,6 +26,7 @@ #include #include #include +#include #include #include @@ -35,6 +36,7 @@ #define POWERNV_MAX_PSTATES 256 static struct cpufreq_frequency_table powernv_freqs[POWERNV_MAX_PSTATES+1]; +static bool rebooting; /* * Note: The set of pstates consists of contiguous integers, the @@ -283,6 +285,15 @@ static void set_pstate(void *freq_data) set_pmspr(SPRN_PMCR, val); } +/* + * get_nominal_index: Returns the index corresponding to the nominal + * pstate in the cpufreq table + */ +static inline unsigned int get_nominal_index(void) +{ + return powernv_pstate_info.max - powernv_pstate_info.nominal; +} + /* * powernv_cpufreq_target_index: Sets the frequency corresponding to * the cpufreq table entry indexed by new_index on the cpus in the @@ -293,6 +304,9 @@ static int powernv_cpufreq_target_index(struct cpufreq_policy *policy, { struct powernv_smp_call_data freq_data; + if (unlikely(rebooting) && new_index != get_nominal_index()) + return 0; + freq_data.pstate_id = powernv_freqs[new_index].driver_data; /* @@ -317,6 +331,25 @@ static int powernv_cpufreq_cpu_init(struct cpufreq_policy *policy) return cpufreq_table_validate_and_show(policy, powernv_freqs); } +static int powernv_cpufreq_reboot_notifier(struct notifier_block *nb, + unsigned long action, void *unused) +{ + int cpu; + struct cpufreq_policy cpu_policy; + + rebooting = true; + for_each_online_cpu(cpu) { + cpufreq_get_policy(&cpu_policy, cpu); + powernv_cpufreq_target_index(&cpu_policy, get_nominal_index()); + } + + return NOTIFY_DONE; +} + +static struct notifier_block powernv_cpufreq_reboot_nb = { + .notifier_call = powernv_cpufreq_reboot_notifier, +}; + static void powernv_cpufreq_stop_cpu(struct cpufreq_policy *policy) { struct powernv_smp_call_data freq_data; @@ -351,12 +384,14 @@ static int __init powernv_cpufreq_init(void) return rc; } + register_reboot_notifier(&powernv_cpufreq_reboot_nb); return cpufreq_register_driver(&powernv_cpufreq_driver); } module_init(powernv_cpufreq_init); static void __exit powernv_cpufreq_exit(void) { + unregister_reboot_notifier(&powernv_cpufreq_reboot_nb); cpufreq_unregister_driver(&powernv_cpufreq_driver); } module_exit(powernv_cpufreq_exit); -- cgit v1.2.3 From 7c4f45397057506415d1cbf3e30b90674f2e0ec0 Mon Sep 17 00:00:00 2001 From: Rasmus Villemoes Date: Mon, 29 Sep 2014 15:50:11 +0200 Subject: cpufreq: Replace strnicmp with strncasecmp The kernel used to contain two functions for length-delimited, case-insensitive string comparison, strnicmp with correct semantics and a slightly buggy strncasecmp. The latter is the POSIX name, so strnicmp was renamed to strncasecmp, and strnicmp made into a wrapper for the new strncasecmp to avoid breaking existing users. To allow the compat wrapper strnicmp to be removed at some point in the future, and to avoid the extra indirection cost, do s/strnicmp/strncasecmp/g. Signed-off-by: Rasmus Villemoes Acked-by: Viresh Kumar Signed-off-by: Rafael J. Wysocki --- drivers/cpufreq/cpufreq.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'drivers/cpufreq') diff --git a/drivers/cpufreq/cpufreq.c b/drivers/cpufreq/cpufreq.c index 6463f35f8be8..6fdd5c702258 100644 --- a/drivers/cpufreq/cpufreq.c +++ b/drivers/cpufreq/cpufreq.c @@ -437,7 +437,7 @@ static struct cpufreq_governor *__find_governor(const char *str_governor) struct cpufreq_governor *t; list_for_each_entry(t, &cpufreq_governor_list, governor_list) - if (!strnicmp(str_governor, t->name, CPUFREQ_NAME_LEN)) + if (!strncasecmp(str_governor, t->name, CPUFREQ_NAME_LEN)) return t; return NULL; @@ -455,10 +455,10 @@ static int cpufreq_parse_governor(char *str_governor, unsigned int *policy, goto out; if (cpufreq_driver->setpolicy) { - if (!strnicmp(str_governor, "performance", CPUFREQ_NAME_LEN)) { + if (!strncasecmp(str_governor, "performance", CPUFREQ_NAME_LEN)) { *policy = CPUFREQ_POLICY_PERFORMANCE; err = 0; - } else if (!strnicmp(str_governor, "powersave", + } else if (!strncasecmp(str_governor, "powersave", CPUFREQ_NAME_LEN)) { *policy = CPUFREQ_POLICY_POWERSAVE; err = 0; -- cgit v1.2.3 From 0bf18f19be4d26545d7aa3114091cffe556572be Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Tue, 30 Sep 2014 09:19:57 +0530 Subject: cpufreq: ppc-corenet: remove duplicate update of cpu_data 'cpu_data' is updated for policy->cpu first and then for all CPUs in policy->cpus. policy->cpus is guaranteed to contain policy->cpu as well and so the first write to 'cpu_data' for policy->cpu is redundant. Remove it. Acked-by: Tang Yuantian Signed-off-by: Viresh Kumar Signed-off-by: Rafael J. Wysocki --- drivers/cpufreq/ppc-corenet-cpufreq.c | 1 - 1 file changed, 1 deletion(-) (limited to 'drivers/cpufreq') diff --git a/drivers/cpufreq/ppc-corenet-cpufreq.c b/drivers/cpufreq/ppc-corenet-cpufreq.c index 3607070797af..bee5df7794d3 100644 --- a/drivers/cpufreq/ppc-corenet-cpufreq.c +++ b/drivers/cpufreq/ppc-corenet-cpufreq.c @@ -199,7 +199,6 @@ static int corenet_cpufreq_cpu_init(struct cpufreq_policy *policy) } data->table = table; - per_cpu(cpu_data, cpu) = data; /* update ->cpus if we have cluster, no harm if not */ cpumask_copy(policy->cpus, per_cpu(cpu_mask, cpu)); -- cgit v1.2.3 From bbcf071969b20f356877c8067986be0a2dcaa2aa Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Tue, 9 Sep 2014 19:58:03 +0530 Subject: cpufreq: cpu0: rename driver and internals to 'cpufreq_dt' The naming convention of this driver was always under the scanner, people complained that it should have a more generic name than cpu0, as it manages all CPUs that are sharing clock lines. Also, in future it will be modified to support any number of clusters with separate clock/voltage lines. Lets rename it to 'cpufreq_dt' from 'cpufreq_cpu0'. Tested-by: Stephen Boyd Signed-off-by: Viresh Kumar Signed-off-by: Rafael J. Wysocki --- .../devicetree/bindings/cpufreq/cpufreq-cpu0.txt | 64 ---- .../devicetree/bindings/cpufreq/cpufreq-dt.txt | 64 ++++ arch/arm/configs/mvebu_v7_defconfig | 2 +- arch/arm/mach-imx/imx27-dt.c | 2 +- arch/arm/mach-imx/mach-imx51.c | 2 +- arch/arm/mach-mvebu/pmsu.c | 2 +- arch/arm/mach-omap2/pm.c | 2 +- arch/arm/mach-shmobile/board-ape6evm-reference.c | 2 +- arch/arm/mach-shmobile/cpufreq.c | 2 +- arch/arm/mach-shmobile/setup-sh73a0.c | 4 +- arch/arm/mach-zynq/common.c | 2 +- drivers/cpufreq/Kconfig | 8 +- drivers/cpufreq/Kconfig.arm | 2 +- drivers/cpufreq/Makefile | 2 +- drivers/cpufreq/cpufreq-cpu0.c | 363 --------------------- drivers/cpufreq/cpufreq-dt.c | 363 +++++++++++++++++++++ drivers/cpufreq/exynos4210-cpufreq.c | 2 +- drivers/cpufreq/exynos4x12-cpufreq.c | 2 +- drivers/cpufreq/exynos5250-cpufreq.c | 2 +- drivers/cpufreq/highbank-cpufreq.c | 6 +- drivers/cpufreq/s5pv210-cpufreq.c | 2 +- 21 files changed, 450 insertions(+), 450 deletions(-) delete mode 100644 Documentation/devicetree/bindings/cpufreq/cpufreq-cpu0.txt create mode 100644 Documentation/devicetree/bindings/cpufreq/cpufreq-dt.txt delete mode 100644 drivers/cpufreq/cpufreq-cpu0.c create mode 100644 drivers/cpufreq/cpufreq-dt.c (limited to 'drivers/cpufreq') diff --git a/Documentation/devicetree/bindings/cpufreq/cpufreq-cpu0.txt b/Documentation/devicetree/bindings/cpufreq/cpufreq-cpu0.txt deleted file mode 100644 index 366690cb86a3..000000000000 --- a/Documentation/devicetree/bindings/cpufreq/cpufreq-cpu0.txt +++ /dev/null @@ -1,64 +0,0 @@ -Generic CPU0 cpufreq driver - -It is a generic cpufreq driver for CPU0 frequency management. It -supports both uniprocessor (UP) and symmetric multiprocessor (SMP) -systems which share clock and voltage across all CPUs. - -Both required and optional properties listed below must be defined -under node /cpus/cpu@0. - -Required properties: -- None - -Optional properties: -- operating-points: Refer to Documentation/devicetree/bindings/power/opp.txt for - details. OPPs *must* be supplied either via DT, i.e. this property, or - populated at runtime. -- clock-latency: Specify the possible maximum transition latency for clock, - in unit of nanoseconds. -- voltage-tolerance: Specify the CPU voltage tolerance in percentage. -- #cooling-cells: -- cooling-min-level: -- cooling-max-level: - Please refer to Documentation/devicetree/bindings/thermal/thermal.txt. - -Examples: - -cpus { - #address-cells = <1>; - #size-cells = <0>; - - cpu@0 { - compatible = "arm,cortex-a9"; - reg = <0>; - next-level-cache = <&L2>; - operating-points = < - /* kHz uV */ - 792000 1100000 - 396000 950000 - 198000 850000 - >; - clock-latency = <61036>; /* two CLK32 periods */ - #cooling-cells = <2>; - cooling-min-level = <0>; - cooling-max-level = <2>; - }; - - cpu@1 { - compatible = "arm,cortex-a9"; - reg = <1>; - next-level-cache = <&L2>; - }; - - cpu@2 { - compatible = "arm,cortex-a9"; - reg = <2>; - next-level-cache = <&L2>; - }; - - cpu@3 { - compatible = "arm,cortex-a9"; - reg = <3>; - next-level-cache = <&L2>; - }; -}; diff --git a/Documentation/devicetree/bindings/cpufreq/cpufreq-dt.txt b/Documentation/devicetree/bindings/cpufreq/cpufreq-dt.txt new file mode 100644 index 000000000000..e41c98ffbccb --- /dev/null +++ b/Documentation/devicetree/bindings/cpufreq/cpufreq-dt.txt @@ -0,0 +1,64 @@ +Generic cpufreq driver + +It is a generic DT based cpufreq driver for frequency management. It supports +both uniprocessor (UP) and symmetric multiprocessor (SMP) systems which share +clock and voltage across all CPUs. + +Both required and optional properties listed below must be defined +under node /cpus/cpu@0. + +Required properties: +- None + +Optional properties: +- operating-points: Refer to Documentation/devicetree/bindings/power/opp.txt for + details. OPPs *must* be supplied either via DT, i.e. this property, or + populated at runtime. +- clock-latency: Specify the possible maximum transition latency for clock, + in unit of nanoseconds. +- voltage-tolerance: Specify the CPU voltage tolerance in percentage. +- #cooling-cells: +- cooling-min-level: +- cooling-max-level: + Please refer to Documentation/devicetree/bindings/thermal/thermal.txt. + +Examples: + +cpus { + #address-cells = <1>; + #size-cells = <0>; + + cpu@0 { + compatible = "arm,cortex-a9"; + reg = <0>; + next-level-cache = <&L2>; + operating-points = < + /* kHz uV */ + 792000 1100000 + 396000 950000 + 198000 850000 + >; + clock-latency = <61036>; /* two CLK32 periods */ + #cooling-cells = <2>; + cooling-min-level = <0>; + cooling-max-level = <2>; + }; + + cpu@1 { + compatible = "arm,cortex-a9"; + reg = <1>; + next-level-cache = <&L2>; + }; + + cpu@2 { + compatible = "arm,cortex-a9"; + reg = <2>; + next-level-cache = <&L2>; + }; + + cpu@3 { + compatible = "arm,cortex-a9"; + reg = <3>; + next-level-cache = <&L2>; + }; +}; diff --git a/arch/arm/configs/mvebu_v7_defconfig b/arch/arm/configs/mvebu_v7_defconfig index fdfda1fa9521..7309988b0f1f 100644 --- a/arch/arm/configs/mvebu_v7_defconfig +++ b/arch/arm/configs/mvebu_v7_defconfig @@ -32,7 +32,7 @@ CONFIG_ARM_ATAG_DTB_COMPAT=y CONFIG_CPU_IDLE=y CONFIG_ARM_MVEBU_V7_CPUIDLE=y CONFIG_CPU_FREQ=y -CONFIG_CPUFREQ_GENERIC=y +CONFIG_CPUFREQ_DT=y CONFIG_VFP=y CONFIG_NET=y CONFIG_INET=y diff --git a/arch/arm/mach-imx/imx27-dt.c b/arch/arm/mach-imx/imx27-dt.c index 080e66c6a1d0..dc8f1a6f45f2 100644 --- a/arch/arm/mach-imx/imx27-dt.c +++ b/arch/arm/mach-imx/imx27-dt.c @@ -20,7 +20,7 @@ static void __init imx27_dt_init(void) { - struct platform_device_info devinfo = { .name = "cpufreq-cpu0", }; + struct platform_device_info devinfo = { .name = "cpufreq-dt", }; mxc_arch_reset_init_dt(); diff --git a/arch/arm/mach-imx/mach-imx51.c b/arch/arm/mach-imx/mach-imx51.c index c77deb3f0893..2c5fcaf8675b 100644 --- a/arch/arm/mach-imx/mach-imx51.c +++ b/arch/arm/mach-imx/mach-imx51.c @@ -51,7 +51,7 @@ static void __init imx51_ipu_mipi_setup(void) static void __init imx51_dt_init(void) { - struct platform_device_info devinfo = { .name = "cpufreq-cpu0", }; + struct platform_device_info devinfo = { .name = "cpufreq-dt", }; mxc_arch_reset_init_dt(); imx51_ipu_mipi_setup(); diff --git a/arch/arm/mach-mvebu/pmsu.c b/arch/arm/mach-mvebu/pmsu.c index 8a70a51533fd..bbd8664d1bac 100644 --- a/arch/arm/mach-mvebu/pmsu.c +++ b/arch/arm/mach-mvebu/pmsu.c @@ -644,7 +644,7 @@ static int __init armada_xp_pmsu_cpufreq_init(void) } } - platform_device_register_simple("cpufreq-generic", -1, NULL, 0); + platform_device_register_simple("cpufreq-dt", -1, NULL, 0); return 0; } diff --git a/arch/arm/mach-omap2/pm.c b/arch/arm/mach-omap2/pm.c index 828aee9ea6a8..58920bc8807b 100644 --- a/arch/arm/mach-omap2/pm.c +++ b/arch/arm/mach-omap2/pm.c @@ -282,7 +282,7 @@ static inline void omap_init_cpufreq(void) if (!of_have_populated_dt()) devinfo.name = "omap-cpufreq"; else - devinfo.name = "cpufreq-cpu0"; + devinfo.name = "cpufreq-dt"; platform_device_register_full(&devinfo); } diff --git a/arch/arm/mach-shmobile/board-ape6evm-reference.c b/arch/arm/mach-shmobile/board-ape6evm-reference.c index 2f7723e5fe91..0110751da511 100644 --- a/arch/arm/mach-shmobile/board-ape6evm-reference.c +++ b/arch/arm/mach-shmobile/board-ape6evm-reference.c @@ -50,7 +50,7 @@ static void __init ape6evm_add_standard_devices(void) r8a73a4_add_dt_devices(); of_platform_populate(NULL, of_default_bus_match_table, NULL, NULL); - platform_device_register_simple("cpufreq-cpu0", -1, NULL, 0); + platform_device_register_simple("cpufreq-dt", -1, NULL, 0); } static const char *ape6evm_boards_compat_dt[] __initdata = { diff --git a/arch/arm/mach-shmobile/cpufreq.c b/arch/arm/mach-shmobile/cpufreq.c index 8a24b2be46ae..57fbff024dcd 100644 --- a/arch/arm/mach-shmobile/cpufreq.c +++ b/arch/arm/mach-shmobile/cpufreq.c @@ -12,6 +12,6 @@ int __init shmobile_cpufreq_init(void) { - platform_device_register_simple("cpufreq-cpu0", -1, NULL, 0); + platform_device_register_simple("cpufreq-dt", -1, NULL, 0); return 0; } diff --git a/arch/arm/mach-shmobile/setup-sh73a0.c b/arch/arm/mach-shmobile/setup-sh73a0.c index 2c802ae9b241..15b990cd8c70 100644 --- a/arch/arm/mach-shmobile/setup-sh73a0.c +++ b/arch/arm/mach-shmobile/setup-sh73a0.c @@ -775,7 +775,7 @@ void __init sh73a0_add_early_devices(void) void __init sh73a0_add_standard_devices_dt(void) { - struct platform_device_info devinfo = { .name = "cpufreq-cpu0", .id = -1, }; + struct platform_device_info devinfo = { .name = "cpufreq-dt", .id = -1, }; /* clocks are setup late during boot in the case of DT */ sh73a0_clock_init(); @@ -784,7 +784,7 @@ void __init sh73a0_add_standard_devices_dt(void) ARRAY_SIZE(sh73a0_devices_dt)); of_platform_populate(NULL, of_default_bus_match_table, NULL, NULL); - /* Instantiate cpufreq-cpu0 */ + /* Instantiate cpufreq-dt */ platform_device_register_full(&devinfo); } diff --git a/arch/arm/mach-zynq/common.c b/arch/arm/mach-zynq/common.c index 31a6fa40ba37..ec03ec40e9c6 100644 --- a/arch/arm/mach-zynq/common.c +++ b/arch/arm/mach-zynq/common.c @@ -104,7 +104,7 @@ static int __init zynq_get_revision(void) */ static void __init zynq_init_machine(void) { - struct platform_device_info devinfo = { .name = "cpufreq-cpu0", }; + struct platform_device_info devinfo = { .name = "cpufreq-dt", }; struct soc_device_attribute *soc_dev_attr; struct soc_device *soc_dev; struct device *parent = NULL; diff --git a/drivers/cpufreq/Kconfig b/drivers/cpufreq/Kconfig index ffe350f86bca..3489f8f5fada 100644 --- a/drivers/cpufreq/Kconfig +++ b/drivers/cpufreq/Kconfig @@ -183,14 +183,14 @@ config CPU_FREQ_GOV_CONSERVATIVE If in doubt, say N. -config GENERIC_CPUFREQ_CPU0 - tristate "Generic CPU0 cpufreq driver" +config CPUFREQ_DT + tristate "Generic DT based cpufreq driver" depends on HAVE_CLK && OF - # if CPU_THERMAL is on and THERMAL=m, CPU0 cannot be =y: + # if CPU_THERMAL is on and THERMAL=m, CPUFREQ_DT cannot be =y: depends on !CPU_THERMAL || THERMAL select PM_OPP help - This adds a generic cpufreq driver for CPU0 frequency management. + This adds a generic DT based cpufreq driver for frequency management. It supports both uniprocessor (UP) and symmetric multiprocessor (SMP) systems which share clock and voltage across all CPUs. diff --git a/drivers/cpufreq/Kconfig.arm b/drivers/cpufreq/Kconfig.arm index 7364a538e056..48ed28b789f7 100644 --- a/drivers/cpufreq/Kconfig.arm +++ b/drivers/cpufreq/Kconfig.arm @@ -92,7 +92,7 @@ config ARM_EXYNOS_CPU_FREQ_BOOST_SW config ARM_HIGHBANK_CPUFREQ tristate "Calxeda Highbank-based" - depends on ARCH_HIGHBANK && GENERIC_CPUFREQ_CPU0 && REGULATOR + depends on ARCH_HIGHBANK && CPUFREQ_DT && REGULATOR default m help This adds the CPUFreq driver for Calxeda Highbank SoC diff --git a/drivers/cpufreq/Makefile b/drivers/cpufreq/Makefile index db6d9a2fea4d..40c53dc1937e 100644 --- a/drivers/cpufreq/Makefile +++ b/drivers/cpufreq/Makefile @@ -13,7 +13,7 @@ obj-$(CONFIG_CPU_FREQ_GOV_ONDEMAND) += cpufreq_ondemand.o obj-$(CONFIG_CPU_FREQ_GOV_CONSERVATIVE) += cpufreq_conservative.o obj-$(CONFIG_CPU_FREQ_GOV_COMMON) += cpufreq_governor.o -obj-$(CONFIG_GENERIC_CPUFREQ_CPU0) += cpufreq-cpu0.o +obj-$(CONFIG_CPUFREQ_DT) += cpufreq-dt.o ################################################################################## # x86 drivers. diff --git a/drivers/cpufreq/cpufreq-cpu0.c b/drivers/cpufreq/cpufreq-cpu0.c deleted file mode 100644 index a5f8c5f5f4f4..000000000000 --- a/drivers/cpufreq/cpufreq-cpu0.c +++ /dev/null @@ -1,363 +0,0 @@ -/* - * Copyright (C) 2012 Freescale Semiconductor, Inc. - * - * Copyright (C) 2014 Linaro. - * Viresh Kumar - * - * The OPP code in function cpu0_set_target() is reused from - * drivers/cpufreq/omap-cpufreq.c - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - */ - -#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -struct private_data { - struct device *cpu_dev; - struct regulator *cpu_reg; - struct thermal_cooling_device *cdev; - unsigned int voltage_tolerance; /* in percentage */ -}; - -static int cpu0_set_target(struct cpufreq_policy *policy, unsigned int index) -{ - struct dev_pm_opp *opp; - struct cpufreq_frequency_table *freq_table = policy->freq_table; - struct clk *cpu_clk = policy->clk; - struct private_data *priv = policy->driver_data; - struct device *cpu_dev = priv->cpu_dev; - struct regulator *cpu_reg = priv->cpu_reg; - unsigned long volt = 0, volt_old = 0, tol = 0; - unsigned int old_freq, new_freq; - long freq_Hz, freq_exact; - int ret; - - freq_Hz = clk_round_rate(cpu_clk, freq_table[index].frequency * 1000); - if (freq_Hz <= 0) - freq_Hz = freq_table[index].frequency * 1000; - - freq_exact = freq_Hz; - new_freq = freq_Hz / 1000; - old_freq = clk_get_rate(cpu_clk) / 1000; - - if (!IS_ERR(cpu_reg)) { - rcu_read_lock(); - opp = dev_pm_opp_find_freq_ceil(cpu_dev, &freq_Hz); - if (IS_ERR(opp)) { - rcu_read_unlock(); - dev_err(cpu_dev, "failed to find OPP for %ld\n", - freq_Hz); - return PTR_ERR(opp); - } - volt = dev_pm_opp_get_voltage(opp); - rcu_read_unlock(); - tol = volt * priv->voltage_tolerance / 100; - volt_old = regulator_get_voltage(cpu_reg); - } - - dev_dbg(cpu_dev, "%u MHz, %ld mV --> %u MHz, %ld mV\n", - old_freq / 1000, volt_old ? volt_old / 1000 : -1, - new_freq / 1000, volt ? volt / 1000 : -1); - - /* scaling up? scale voltage before frequency */ - if (!IS_ERR(cpu_reg) && new_freq > old_freq) { - ret = regulator_set_voltage_tol(cpu_reg, volt, tol); - if (ret) { - dev_err(cpu_dev, "failed to scale voltage up: %d\n", - ret); - return ret; - } - } - - ret = clk_set_rate(cpu_clk, freq_exact); - if (ret) { - dev_err(cpu_dev, "failed to set clock rate: %d\n", ret); - if (!IS_ERR(cpu_reg)) - regulator_set_voltage_tol(cpu_reg, volt_old, tol); - return ret; - } - - /* scaling down? scale voltage after frequency */ - if (!IS_ERR(cpu_reg) && new_freq < old_freq) { - ret = regulator_set_voltage_tol(cpu_reg, volt, tol); - if (ret) { - dev_err(cpu_dev, "failed to scale voltage down: %d\n", - ret); - clk_set_rate(cpu_clk, old_freq * 1000); - } - } - - return ret; -} - -static int allocate_resources(int cpu, struct device **cdev, - struct regulator **creg, struct clk **cclk) -{ - struct device *cpu_dev; - struct regulator *cpu_reg; - struct clk *cpu_clk; - int ret = 0; - char *reg_cpu0 = "cpu0", *reg_cpu = "cpu", *reg; - - cpu_dev = get_cpu_device(cpu); - if (!cpu_dev) { - pr_err("failed to get cpu%d device\n", cpu); - return -ENODEV; - } - - /* Try "cpu0" for older DTs */ - if (!cpu) - reg = reg_cpu0; - else - reg = reg_cpu; - -try_again: - cpu_reg = regulator_get_optional(cpu_dev, reg); - if (IS_ERR(cpu_reg)) { - /* - * If cpu's regulator supply node is present, but regulator is - * not yet registered, we should try defering probe. - */ - if (PTR_ERR(cpu_reg) == -EPROBE_DEFER) { - dev_dbg(cpu_dev, "cpu%d regulator not ready, retry\n", - cpu); - return -EPROBE_DEFER; - } - - /* Try with "cpu-supply" */ - if (reg == reg_cpu0) { - reg = reg_cpu; - goto try_again; - } - - dev_warn(cpu_dev, "failed to get cpu%d regulator: %ld\n", - cpu, PTR_ERR(cpu_reg)); - } - - cpu_clk = clk_get(cpu_dev, NULL); - if (IS_ERR(cpu_clk)) { - /* put regulator */ - if (!IS_ERR(cpu_reg)) - regulator_put(cpu_reg); - - ret = PTR_ERR(cpu_clk); - - /* - * If cpu's clk node is present, but clock is not yet - * registered, we should try defering probe. - */ - if (ret == -EPROBE_DEFER) - dev_dbg(cpu_dev, "cpu%d clock not ready, retry\n", cpu); - else - dev_err(cpu_dev, "failed to get cpu%d clock: %d\n", ret, - cpu); - } else { - *cdev = cpu_dev; - *creg = cpu_reg; - *cclk = cpu_clk; - } - - return ret; -} - -static int cpu0_cpufreq_init(struct cpufreq_policy *policy) -{ - struct cpufreq_frequency_table *freq_table; - struct thermal_cooling_device *cdev; - struct device_node *np; - struct private_data *priv; - struct device *cpu_dev; - struct regulator *cpu_reg; - struct clk *cpu_clk; - unsigned int transition_latency; - int ret; - - ret = allocate_resources(policy->cpu, &cpu_dev, &cpu_reg, &cpu_clk); - if (ret) { - pr_err("%s: Failed to allocate resources\n: %d", __func__, ret); - return ret; - } - - np = of_node_get(cpu_dev->of_node); - if (!np) { - dev_err(cpu_dev, "failed to find cpu%d node\n", policy->cpu); - ret = -ENOENT; - goto out_put_reg_clk; - } - - /* OPPs might be populated at runtime, don't check for error here */ - of_init_opp_table(cpu_dev); - - ret = dev_pm_opp_init_cpufreq_table(cpu_dev, &freq_table); - if (ret) { - dev_err(cpu_dev, "failed to init cpufreq table: %d\n", ret); - goto out_put_node; - } - - priv = kzalloc(sizeof(*priv), GFP_KERNEL); - if (!priv) { - ret = -ENOMEM; - goto out_free_table; - } - - of_property_read_u32(np, "voltage-tolerance", &priv->voltage_tolerance); - - if (of_property_read_u32(np, "clock-latency", &transition_latency)) - transition_latency = CPUFREQ_ETERNAL; - - if (!IS_ERR(cpu_reg)) { - struct dev_pm_opp *opp; - unsigned long min_uV, max_uV; - int i; - - /* - * OPP is maintained in order of increasing frequency, and - * freq_table initialised from OPP is therefore sorted in the - * same order. - */ - for (i = 0; freq_table[i].frequency != CPUFREQ_TABLE_END; i++) - ; - rcu_read_lock(); - opp = dev_pm_opp_find_freq_exact(cpu_dev, - freq_table[0].frequency * 1000, true); - min_uV = dev_pm_opp_get_voltage(opp); - opp = dev_pm_opp_find_freq_exact(cpu_dev, - freq_table[i-1].frequency * 1000, true); - max_uV = dev_pm_opp_get_voltage(opp); - rcu_read_unlock(); - ret = regulator_set_voltage_time(cpu_reg, min_uV, max_uV); - if (ret > 0) - transition_latency += ret * 1000; - } - - /* - * For now, just loading the cooling device; - * thermal DT code takes care of matching them. - */ - if (of_find_property(np, "#cooling-cells", NULL)) { - cdev = of_cpufreq_cooling_register(np, cpu_present_mask); - if (IS_ERR(cdev)) - dev_err(cpu_dev, - "running cpufreq without cooling device: %ld\n", - PTR_ERR(cdev)); - else - priv->cdev = cdev; - } - of_node_put(np); - - priv->cpu_dev = cpu_dev; - priv->cpu_reg = cpu_reg; - policy->driver_data = priv; - - policy->clk = cpu_clk; - ret = cpufreq_generic_init(policy, freq_table, transition_latency); - if (ret) - goto out_cooling_unregister; - - return 0; - -out_cooling_unregister: - cpufreq_cooling_unregister(priv->cdev); - kfree(priv); -out_free_table: - dev_pm_opp_free_cpufreq_table(cpu_dev, &freq_table); -out_put_node: - of_node_put(np); -out_put_reg_clk: - clk_put(cpu_clk); - if (!IS_ERR(cpu_reg)) - regulator_put(cpu_reg); - - return ret; -} - -static int cpu0_cpufreq_exit(struct cpufreq_policy *policy) -{ - struct private_data *priv = policy->driver_data; - - cpufreq_cooling_unregister(priv->cdev); - dev_pm_opp_free_cpufreq_table(priv->cpu_dev, &policy->freq_table); - clk_put(policy->clk); - if (!IS_ERR(priv->cpu_reg)) - regulator_put(priv->cpu_reg); - kfree(priv); - - return 0; -} - -static struct cpufreq_driver cpu0_cpufreq_driver = { - .flags = CPUFREQ_STICKY | CPUFREQ_NEED_INITIAL_FREQ_CHECK, - .verify = cpufreq_generic_frequency_table_verify, - .target_index = cpu0_set_target, - .get = cpufreq_generic_get, - .init = cpu0_cpufreq_init, - .exit = cpu0_cpufreq_exit, - .name = "generic_cpu0", - .attr = cpufreq_generic_attr, -}; - -static int cpu0_cpufreq_probe(struct platform_device *pdev) -{ - struct device *cpu_dev; - struct regulator *cpu_reg; - struct clk *cpu_clk; - int ret; - - /* - * All per-cluster (CPUs sharing clock/voltages) initialization is done - * from ->init(). In probe(), we just need to make sure that clk and - * regulators are available. Else defer probe and retry. - * - * FIXME: Is checking this only for CPU0 sufficient ? - */ - ret = allocate_resources(0, &cpu_dev, &cpu_reg, &cpu_clk); - if (ret) - return ret; - - clk_put(cpu_clk); - if (!IS_ERR(cpu_reg)) - regulator_put(cpu_reg); - - ret = cpufreq_register_driver(&cpu0_cpufreq_driver); - if (ret) - dev_err(cpu_dev, "failed register driver: %d\n", ret); - - return ret; -} - -static int cpu0_cpufreq_remove(struct platform_device *pdev) -{ - cpufreq_unregister_driver(&cpu0_cpufreq_driver); - return 0; -} - -static struct platform_driver cpu0_cpufreq_platdrv = { - .driver = { - .name = "cpufreq-cpu0", - .owner = THIS_MODULE, - }, - .probe = cpu0_cpufreq_probe, - .remove = cpu0_cpufreq_remove, -}; -module_platform_driver(cpu0_cpufreq_platdrv); - -MODULE_AUTHOR("Viresh Kumar "); -MODULE_AUTHOR("Shawn Guo "); -MODULE_DESCRIPTION("Generic CPU0 cpufreq driver"); -MODULE_LICENSE("GPL"); diff --git a/drivers/cpufreq/cpufreq-dt.c b/drivers/cpufreq/cpufreq-dt.c new file mode 100644 index 000000000000..e00265066a75 --- /dev/null +++ b/drivers/cpufreq/cpufreq-dt.c @@ -0,0 +1,363 @@ +/* + * Copyright (C) 2012 Freescale Semiconductor, Inc. + * + * Copyright (C) 2014 Linaro. + * Viresh Kumar + * + * The OPP code in function set_target() is reused from + * drivers/cpufreq/omap-cpufreq.c + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +struct private_data { + struct device *cpu_dev; + struct regulator *cpu_reg; + struct thermal_cooling_device *cdev; + unsigned int voltage_tolerance; /* in percentage */ +}; + +static int set_target(struct cpufreq_policy *policy, unsigned int index) +{ + struct dev_pm_opp *opp; + struct cpufreq_frequency_table *freq_table = policy->freq_table; + struct clk *cpu_clk = policy->clk; + struct private_data *priv = policy->driver_data; + struct device *cpu_dev = priv->cpu_dev; + struct regulator *cpu_reg = priv->cpu_reg; + unsigned long volt = 0, volt_old = 0, tol = 0; + unsigned int old_freq, new_freq; + long freq_Hz, freq_exact; + int ret; + + freq_Hz = clk_round_rate(cpu_clk, freq_table[index].frequency * 1000); + if (freq_Hz <= 0) + freq_Hz = freq_table[index].frequency * 1000; + + freq_exact = freq_Hz; + new_freq = freq_Hz / 1000; + old_freq = clk_get_rate(cpu_clk) / 1000; + + if (!IS_ERR(cpu_reg)) { + rcu_read_lock(); + opp = dev_pm_opp_find_freq_ceil(cpu_dev, &freq_Hz); + if (IS_ERR(opp)) { + rcu_read_unlock(); + dev_err(cpu_dev, "failed to find OPP for %ld\n", + freq_Hz); + return PTR_ERR(opp); + } + volt = dev_pm_opp_get_voltage(opp); + rcu_read_unlock(); + tol = volt * priv->voltage_tolerance / 100; + volt_old = regulator_get_voltage(cpu_reg); + } + + dev_dbg(cpu_dev, "%u MHz, %ld mV --> %u MHz, %ld mV\n", + old_freq / 1000, volt_old ? volt_old / 1000 : -1, + new_freq / 1000, volt ? volt / 1000 : -1); + + /* scaling up? scale voltage before frequency */ + if (!IS_ERR(cpu_reg) && new_freq > old_freq) { + ret = regulator_set_voltage_tol(cpu_reg, volt, tol); + if (ret) { + dev_err(cpu_dev, "failed to scale voltage up: %d\n", + ret); + return ret; + } + } + + ret = clk_set_rate(cpu_clk, freq_exact); + if (ret) { + dev_err(cpu_dev, "failed to set clock rate: %d\n", ret); + if (!IS_ERR(cpu_reg)) + regulator_set_voltage_tol(cpu_reg, volt_old, tol); + return ret; + } + + /* scaling down? scale voltage after frequency */ + if (!IS_ERR(cpu_reg) && new_freq < old_freq) { + ret = regulator_set_voltage_tol(cpu_reg, volt, tol); + if (ret) { + dev_err(cpu_dev, "failed to scale voltage down: %d\n", + ret); + clk_set_rate(cpu_clk, old_freq * 1000); + } + } + + return ret; +} + +static int allocate_resources(int cpu, struct device **cdev, + struct regulator **creg, struct clk **cclk) +{ + struct device *cpu_dev; + struct regulator *cpu_reg; + struct clk *cpu_clk; + int ret = 0; + char *reg_cpu0 = "cpu0", *reg_cpu = "cpu", *reg; + + cpu_dev = get_cpu_device(cpu); + if (!cpu_dev) { + pr_err("failed to get cpu%d device\n", cpu); + return -ENODEV; + } + + /* Try "cpu0" for older DTs */ + if (!cpu) + reg = reg_cpu0; + else + reg = reg_cpu; + +try_again: + cpu_reg = regulator_get_optional(cpu_dev, reg); + if (IS_ERR(cpu_reg)) { + /* + * If cpu's regulator supply node is present, but regulator is + * not yet registered, we should try defering probe. + */ + if (PTR_ERR(cpu_reg) == -EPROBE_DEFER) { + dev_dbg(cpu_dev, "cpu%d regulator not ready, retry\n", + cpu); + return -EPROBE_DEFER; + } + + /* Try with "cpu-supply" */ + if (reg == reg_cpu0) { + reg = reg_cpu; + goto try_again; + } + + dev_warn(cpu_dev, "failed to get cpu%d regulator: %ld\n", + cpu, PTR_ERR(cpu_reg)); + } + + cpu_clk = clk_get(cpu_dev, NULL); + if (IS_ERR(cpu_clk)) { + /* put regulator */ + if (!IS_ERR(cpu_reg)) + regulator_put(cpu_reg); + + ret = PTR_ERR(cpu_clk); + + /* + * If cpu's clk node is present, but clock is not yet + * registered, we should try defering probe. + */ + if (ret == -EPROBE_DEFER) + dev_dbg(cpu_dev, "cpu%d clock not ready, retry\n", cpu); + else + dev_err(cpu_dev, "failed to get cpu%d clock: %d\n", ret, + cpu); + } else { + *cdev = cpu_dev; + *creg = cpu_reg; + *cclk = cpu_clk; + } + + return ret; +} + +static int cpufreq_init(struct cpufreq_policy *policy) +{ + struct cpufreq_frequency_table *freq_table; + struct thermal_cooling_device *cdev; + struct device_node *np; + struct private_data *priv; + struct device *cpu_dev; + struct regulator *cpu_reg; + struct clk *cpu_clk; + unsigned int transition_latency; + int ret; + + ret = allocate_resources(policy->cpu, &cpu_dev, &cpu_reg, &cpu_clk); + if (ret) { + pr_err("%s: Failed to allocate resources\n: %d", __func__, ret); + return ret; + } + + np = of_node_get(cpu_dev->of_node); + if (!np) { + dev_err(cpu_dev, "failed to find cpu%d node\n", policy->cpu); + ret = -ENOENT; + goto out_put_reg_clk; + } + + /* OPPs might be populated at runtime, don't check for error here */ + of_init_opp_table(cpu_dev); + + ret = dev_pm_opp_init_cpufreq_table(cpu_dev, &freq_table); + if (ret) { + dev_err(cpu_dev, "failed to init cpufreq table: %d\n", ret); + goto out_put_node; + } + + priv = kzalloc(sizeof(*priv), GFP_KERNEL); + if (!priv) { + ret = -ENOMEM; + goto out_free_table; + } + + of_property_read_u32(np, "voltage-tolerance", &priv->voltage_tolerance); + + if (of_property_read_u32(np, "clock-latency", &transition_latency)) + transition_latency = CPUFREQ_ETERNAL; + + if (!IS_ERR(cpu_reg)) { + struct dev_pm_opp *opp; + unsigned long min_uV, max_uV; + int i; + + /* + * OPP is maintained in order of increasing frequency, and + * freq_table initialised from OPP is therefore sorted in the + * same order. + */ + for (i = 0; freq_table[i].frequency != CPUFREQ_TABLE_END; i++) + ; + rcu_read_lock(); + opp = dev_pm_opp_find_freq_exact(cpu_dev, + freq_table[0].frequency * 1000, true); + min_uV = dev_pm_opp_get_voltage(opp); + opp = dev_pm_opp_find_freq_exact(cpu_dev, + freq_table[i-1].frequency * 1000, true); + max_uV = dev_pm_opp_get_voltage(opp); + rcu_read_unlock(); + ret = regulator_set_voltage_time(cpu_reg, min_uV, max_uV); + if (ret > 0) + transition_latency += ret * 1000; + } + + /* + * For now, just loading the cooling device; + * thermal DT code takes care of matching them. + */ + if (of_find_property(np, "#cooling-cells", NULL)) { + cdev = of_cpufreq_cooling_register(np, cpu_present_mask); + if (IS_ERR(cdev)) + dev_err(cpu_dev, + "running cpufreq without cooling device: %ld\n", + PTR_ERR(cdev)); + else + priv->cdev = cdev; + } + of_node_put(np); + + priv->cpu_dev = cpu_dev; + priv->cpu_reg = cpu_reg; + policy->driver_data = priv; + + policy->clk = cpu_clk; + ret = cpufreq_generic_init(policy, freq_table, transition_latency); + if (ret) + goto out_cooling_unregister; + + return 0; + +out_cooling_unregister: + cpufreq_cooling_unregister(priv->cdev); + kfree(priv); +out_free_table: + dev_pm_opp_free_cpufreq_table(cpu_dev, &freq_table); +out_put_node: + of_node_put(np); +out_put_reg_clk: + clk_put(cpu_clk); + if (!IS_ERR(cpu_reg)) + regulator_put(cpu_reg); + + return ret; +} + +static int cpufreq_exit(struct cpufreq_policy *policy) +{ + struct private_data *priv = policy->driver_data; + + cpufreq_cooling_unregister(priv->cdev); + dev_pm_opp_free_cpufreq_table(priv->cpu_dev, &policy->freq_table); + clk_put(policy->clk); + if (!IS_ERR(priv->cpu_reg)) + regulator_put(priv->cpu_reg); + kfree(priv); + + return 0; +} + +static struct cpufreq_driver dt_cpufreq_driver = { + .flags = CPUFREQ_STICKY | CPUFREQ_NEED_INITIAL_FREQ_CHECK, + .verify = cpufreq_generic_frequency_table_verify, + .target_index = set_target, + .get = cpufreq_generic_get, + .init = cpufreq_init, + .exit = cpufreq_exit, + .name = "cpufreq-dt", + .attr = cpufreq_generic_attr, +}; + +static int dt_cpufreq_probe(struct platform_device *pdev) +{ + struct device *cpu_dev; + struct regulator *cpu_reg; + struct clk *cpu_clk; + int ret; + + /* + * All per-cluster (CPUs sharing clock/voltages) initialization is done + * from ->init(). In probe(), we just need to make sure that clk and + * regulators are available. Else defer probe and retry. + * + * FIXME: Is checking this only for CPU0 sufficient ? + */ + ret = allocate_resources(0, &cpu_dev, &cpu_reg, &cpu_clk); + if (ret) + return ret; + + clk_put(cpu_clk); + if (!IS_ERR(cpu_reg)) + regulator_put(cpu_reg); + + ret = cpufreq_register_driver(&dt_cpufreq_driver); + if (ret) + dev_err(cpu_dev, "failed register driver: %d\n", ret); + + return ret; +} + +static int dt_cpufreq_remove(struct platform_device *pdev) +{ + cpufreq_unregister_driver(&dt_cpufreq_driver); + return 0; +} + +static struct platform_driver dt_cpufreq_platdrv = { + .driver = { + .name = "cpufreq-dt", + .owner = THIS_MODULE, + }, + .probe = dt_cpufreq_probe, + .remove = dt_cpufreq_remove, +}; +module_platform_driver(dt_cpufreq_platdrv); + +MODULE_AUTHOR("Viresh Kumar "); +MODULE_AUTHOR("Shawn Guo "); +MODULE_DESCRIPTION("Generic cpufreq driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/cpufreq/exynos4210-cpufreq.c b/drivers/cpufreq/exynos4210-cpufreq.c index 61a54310a1b9..843ec824fd91 100644 --- a/drivers/cpufreq/exynos4210-cpufreq.c +++ b/drivers/cpufreq/exynos4210-cpufreq.c @@ -127,7 +127,7 @@ int exynos4210_cpufreq_init(struct exynos_dvfs_info *info) * dependencies on platform headers. It is necessary to enable * Exynos multi-platform support and will be removed together with * this whole driver as soon as Exynos gets migrated to use - * cpufreq-cpu0 driver. + * cpufreq-dt driver. */ np = of_find_compatible_node(NULL, NULL, "samsung,exynos4210-clock"); if (!np) { diff --git a/drivers/cpufreq/exynos4x12-cpufreq.c b/drivers/cpufreq/exynos4x12-cpufreq.c index 351a2074cfea..9e78a850e29f 100644 --- a/drivers/cpufreq/exynos4x12-cpufreq.c +++ b/drivers/cpufreq/exynos4x12-cpufreq.c @@ -174,7 +174,7 @@ int exynos4x12_cpufreq_init(struct exynos_dvfs_info *info) * dependencies on platform headers. It is necessary to enable * Exynos multi-platform support and will be removed together with * this whole driver as soon as Exynos gets migrated to use - * cpufreq-cpu0 driver. + * cpufreq-dt driver. */ np = of_find_compatible_node(NULL, NULL, "samsung,exynos4412-clock"); if (!np) { diff --git a/drivers/cpufreq/exynos5250-cpufreq.c b/drivers/cpufreq/exynos5250-cpufreq.c index c91ce69dc631..3eafdc7ba787 100644 --- a/drivers/cpufreq/exynos5250-cpufreq.c +++ b/drivers/cpufreq/exynos5250-cpufreq.c @@ -153,7 +153,7 @@ int exynos5250_cpufreq_init(struct exynos_dvfs_info *info) * dependencies on platform headers. It is necessary to enable * Exynos multi-platform support and will be removed together with * this whole driver as soon as Exynos gets migrated to use - * cpufreq-cpu0 driver. + * cpufreq-dt driver. */ np = of_find_compatible_node(NULL, NULL, "samsung,exynos5250-clock"); if (!np) { diff --git a/drivers/cpufreq/highbank-cpufreq.c b/drivers/cpufreq/highbank-cpufreq.c index bf8902a0866d..ec399ad2f059 100644 --- a/drivers/cpufreq/highbank-cpufreq.c +++ b/drivers/cpufreq/highbank-cpufreq.c @@ -6,7 +6,7 @@ * published by the Free Software Foundation. * * This driver provides the clk notifier callbacks that are used when - * the cpufreq-cpu0 driver changes to frequency to alert the highbank + * the cpufreq-dt driver changes to frequency to alert the highbank * EnergyCore Management Engine (ECME) about the need to change * voltage. The ECME interfaces with the actual voltage regulators. */ @@ -60,7 +60,7 @@ static struct notifier_block hb_cpufreq_clk_nb = { static int hb_cpufreq_driver_init(void) { - struct platform_device_info devinfo = { .name = "cpufreq-cpu0", }; + struct platform_device_info devinfo = { .name = "cpufreq-dt", }; struct device *cpu_dev; struct clk *cpu_clk; struct device_node *np; @@ -95,7 +95,7 @@ static int hb_cpufreq_driver_init(void) goto out_put_node; } - /* Instantiate cpufreq-cpu0 */ + /* Instantiate cpufreq-dt */ platform_device_register_full(&devinfo); out_put_node: diff --git a/drivers/cpufreq/s5pv210-cpufreq.c b/drivers/cpufreq/s5pv210-cpufreq.c index 3f9791f07b8e..567caa6313ff 100644 --- a/drivers/cpufreq/s5pv210-cpufreq.c +++ b/drivers/cpufreq/s5pv210-cpufreq.c @@ -597,7 +597,7 @@ static int s5pv210_cpufreq_probe(struct platform_device *pdev) * and dependencies on platform headers. It is necessary to enable * S5PV210 multi-platform support and will be removed together with * this whole driver as soon as S5PV210 gets migrated to use - * cpufreq-cpu0 driver. + * cpufreq-dt driver. */ np = of_find_compatible_node(NULL, NULL, "samsung,s5pv210-clock"); if (!np) { -- cgit v1.2.3 From f9739d27059d8fd7b64096ea7251608628b5bd30 Mon Sep 17 00:00:00 2001 From: Lucas Stach Date: Fri, 26 Sep 2014 15:33:46 +0200 Subject: cpufreq: cpufreq-dt: fix potential double put of cpu OF node If cpufreq_generic_init() fails we jump into the resource cleanup path which contains a of_node_put() call. Another instance of this has already been called at that time resulting a double decrement of the refcount. Fix this by calling of_node_put() only after we are sure that nothing has gone wrong. Fixes: d2f31f1da54f "cpufreq: cpu0: Move per-cluster initialization code to ->init()" Signed-off-by: Lucas Stach Signed-off-by: Rafael J. Wysocki --- drivers/cpufreq/cpufreq-dt.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'drivers/cpufreq') diff --git a/drivers/cpufreq/cpufreq-dt.c b/drivers/cpufreq/cpufreq-dt.c index e00265066a75..6bbb8b913446 100644 --- a/drivers/cpufreq/cpufreq-dt.c +++ b/drivers/cpufreq/cpufreq-dt.c @@ -259,7 +259,6 @@ static int cpufreq_init(struct cpufreq_policy *policy) else priv->cdev = cdev; } - of_node_put(np); priv->cpu_dev = cpu_dev; priv->cpu_reg = cpu_reg; @@ -270,6 +269,8 @@ static int cpufreq_init(struct cpufreq_policy *policy) if (ret) goto out_cooling_unregister; + of_node_put(np); + return 0; out_cooling_unregister: -- cgit v1.2.3