diff options
Diffstat (limited to 'drivers/cpufreq')
-rw-r--r-- | drivers/cpufreq/Kconfig.arm | 13 | ||||
-rw-r--r-- | drivers/cpufreq/Makefile | 3 | ||||
-rw-r--r-- | drivers/cpufreq/cpufreq.c | 110 | ||||
-rw-r--r-- | drivers/cpufreq/exynos-cpufreq.c | 6 | ||||
-rw-r--r-- | drivers/cpufreq/intel_pstate.c | 3 | ||||
-rw-r--r-- | drivers/cpufreq/loongson2_cpufreq.c | 2 | ||||
-rw-r--r-- | drivers/cpufreq/tegra124-cpufreq.c | 214 | ||||
-rw-r--r-- | drivers/cpufreq/tegra20-cpufreq.c (renamed from drivers/cpufreq/tegra-cpufreq.c) | 0 |
8 files changed, 288 insertions, 63 deletions
diff --git a/drivers/cpufreq/Kconfig.arm b/drivers/cpufreq/Kconfig.arm index cc8a71c267b8..24e5c664683f 100644 --- a/drivers/cpufreq/Kconfig.arm +++ b/drivers/cpufreq/Kconfig.arm @@ -247,12 +247,19 @@ config ARM_SPEAR_CPUFREQ help This adds the CPUFreq driver support for SPEAr SOCs. -config ARM_TEGRA_CPUFREQ - bool "TEGRA CPUFreq support" +config ARM_TEGRA20_CPUFREQ + bool "Tegra20 CPUFreq support" depends on ARCH_TEGRA default y help - This adds the CPUFreq driver support for TEGRA SOCs. + This adds the CPUFreq driver support for Tegra20 SOCs. + +config ARM_TEGRA124_CPUFREQ + tristate "Tegra124 CPUFreq support" + depends on ARCH_TEGRA && CPUFREQ_DT + default y + help + This adds the CPUFreq driver support for Tegra124 SOCs. config ARM_PXA2xx_CPUFREQ tristate "Intel PXA2xx CPUfreq driver" diff --git a/drivers/cpufreq/Makefile b/drivers/cpufreq/Makefile index 2169bf792db7..032745de8fcc 100644 --- a/drivers/cpufreq/Makefile +++ b/drivers/cpufreq/Makefile @@ -76,7 +76,8 @@ obj-$(CONFIG_ARM_S5PV210_CPUFREQ) += s5pv210-cpufreq.o obj-$(CONFIG_ARM_SA1100_CPUFREQ) += sa1100-cpufreq.o obj-$(CONFIG_ARM_SA1110_CPUFREQ) += sa1110-cpufreq.o obj-$(CONFIG_ARM_SPEAR_CPUFREQ) += spear-cpufreq.o -obj-$(CONFIG_ARM_TEGRA_CPUFREQ) += tegra-cpufreq.o +obj-$(CONFIG_ARM_TEGRA20_CPUFREQ) += tegra20-cpufreq.o +obj-$(CONFIG_ARM_TEGRA124_CPUFREQ) += tegra124-cpufreq.o obj-$(CONFIG_ARM_VEXPRESS_SPC_CPUFREQ) += vexpress-spc-cpufreq.o ################################################################################## diff --git a/drivers/cpufreq/cpufreq.c b/drivers/cpufreq/cpufreq.c index 26063afb3eba..9bb09ce98d04 100644 --- a/drivers/cpufreq/cpufreq.c +++ b/drivers/cpufreq/cpufreq.c @@ -1002,7 +1002,7 @@ static int cpufreq_add_dev_symlink(struct cpufreq_policy *policy) int ret = 0; /* Some related CPUs might not be present (physically hotplugged) */ - for_each_cpu_and(j, policy->related_cpus, cpu_present_mask) { + for_each_cpu(j, policy->real_cpus) { if (j == policy->kobj_cpu) continue; @@ -1019,7 +1019,7 @@ static void cpufreq_remove_dev_symlink(struct cpufreq_policy *policy) unsigned int j; /* Some related CPUs might not be present (physically hotplugged) */ - for_each_cpu_and(j, policy->related_cpus, cpu_present_mask) { + for_each_cpu(j, policy->real_cpus) { if (j == policy->kobj_cpu) continue; @@ -1163,11 +1163,14 @@ static struct cpufreq_policy *cpufreq_policy_alloc(struct device *dev) if (!zalloc_cpumask_var(&policy->related_cpus, GFP_KERNEL)) goto err_free_cpumask; + if (!zalloc_cpumask_var(&policy->real_cpus, GFP_KERNEL)) + goto err_free_rcpumask; + ret = kobject_init_and_add(&policy->kobj, &ktype_cpufreq, &dev->kobj, "cpufreq"); if (ret) { pr_err("%s: failed to init policy->kobj: %d\n", __func__, ret); - goto err_free_rcpumask; + goto err_free_real_cpus; } INIT_LIST_HEAD(&policy->policy_list); @@ -1184,6 +1187,8 @@ static struct cpufreq_policy *cpufreq_policy_alloc(struct device *dev) return policy; +err_free_real_cpus: + free_cpumask_var(policy->real_cpus); err_free_rcpumask: free_cpumask_var(policy->related_cpus); err_free_cpumask: @@ -1234,6 +1239,7 @@ static void cpufreq_policy_free(struct cpufreq_policy *policy, bool notify) write_unlock_irqrestore(&cpufreq_driver_lock, flags); cpufreq_policy_put_kobj(policy, notify); + free_cpumask_var(policy->real_cpus); free_cpumask_var(policy->related_cpus); free_cpumask_var(policy->cpus); kfree(policy); @@ -1258,14 +1264,17 @@ static int cpufreq_add_dev(struct device *dev, struct subsys_interface *sif) pr_debug("adding CPU %u\n", cpu); - /* - * Only possible if 'cpu' wasn't physically present earlier and we are - * here from subsys_interface add callback. A hotplug notifier will - * follow and we will handle it like logical CPU hotplug then. For now, - * just create the sysfs link. - */ - if (cpu_is_offline(cpu)) - return add_cpu_dev_symlink(per_cpu(cpufreq_cpu_data, cpu), cpu); + if (cpu_is_offline(cpu)) { + /* + * Only possible if we are here from the subsys_interface add + * callback. A hotplug notifier will follow and we will handle + * it as CPU online then. For now, just create the sysfs link, + * unless there is no policy or the link is already present. + */ + policy = per_cpu(cpufreq_cpu_data, cpu); + return policy && !cpumask_test_and_set_cpu(cpu, policy->real_cpus) + ? add_cpu_dev_symlink(policy, cpu) : 0; + } if (!down_read_trylock(&cpufreq_rwsem)) return 0; @@ -1307,6 +1316,10 @@ static int cpufreq_add_dev(struct device *dev, struct subsys_interface *sif) /* related cpus should atleast have policy->cpus */ cpumask_or(policy->related_cpus, policy->related_cpus, policy->cpus); + /* Remember which CPUs have been present at the policy creation time. */ + if (!recover_policy) + cpumask_and(policy->real_cpus, policy->cpus, cpu_present_mask); + /* * affected cpus must always be the one, which are online. We aren't * managing offline cpus here. @@ -1420,8 +1433,7 @@ nomem_out: return ret; } -static int __cpufreq_remove_dev_prepare(struct device *dev, - struct subsys_interface *sif) +static int __cpufreq_remove_dev_prepare(struct device *dev) { unsigned int cpu = dev->id; int ret = 0; @@ -1437,10 +1449,8 @@ static int __cpufreq_remove_dev_prepare(struct device *dev, if (has_target()) { ret = __cpufreq_governor(policy, CPUFREQ_GOV_STOP); - if (ret) { + if (ret) pr_err("%s: Failed to stop governor\n", __func__); - return ret; - } } down_write(&policy->rwsem); @@ -1473,8 +1483,7 @@ static int __cpufreq_remove_dev_prepare(struct device *dev, return ret; } -static int __cpufreq_remove_dev_finish(struct device *dev, - struct subsys_interface *sif) +static int __cpufreq_remove_dev_finish(struct device *dev) { unsigned int cpu = dev->id; int ret; @@ -1492,10 +1501,8 @@ static int __cpufreq_remove_dev_finish(struct device *dev, /* If cpu is last user of policy, free policy */ if (has_target()) { ret = __cpufreq_governor(policy, CPUFREQ_GOV_POLICY_EXIT); - if (ret) { + if (ret) pr_err("%s: Failed to exit governor\n", __func__); - return ret; - } } /* @@ -1506,10 +1513,6 @@ static int __cpufreq_remove_dev_finish(struct device *dev, if (cpufreq_driver->exit) cpufreq_driver->exit(policy); - /* Free the policy only if the driver is getting removed. */ - if (sif) - cpufreq_policy_free(policy, true); - return 0; } @@ -1518,45 +1521,42 @@ static int __cpufreq_remove_dev_finish(struct device *dev, * * Removes the cpufreq interface for a CPU device. */ -static int cpufreq_remove_dev(struct device *dev, struct subsys_interface *sif) +static void cpufreq_remove_dev(struct device *dev, struct subsys_interface *sif) { unsigned int cpu = dev->id; - int ret; - - /* - * Only possible if 'cpu' is getting physically removed now. A hotplug - * notifier should have already been called and we just need to remove - * link or free policy here. - */ - if (cpu_is_offline(cpu)) { - struct cpufreq_policy *policy = per_cpu(cpufreq_cpu_data, cpu); - struct cpumask mask; + struct cpufreq_policy *policy = per_cpu(cpufreq_cpu_data, cpu); - if (!policy) - return 0; + if (!policy) + return; - cpumask_copy(&mask, policy->related_cpus); - cpumask_clear_cpu(cpu, &mask); + if (cpu_online(cpu)) { + __cpufreq_remove_dev_prepare(dev); + __cpufreq_remove_dev_finish(dev); + } - /* - * Free policy only if all policy->related_cpus are removed - * physically. - */ - if (cpumask_intersects(&mask, cpu_present_mask)) { - remove_cpu_dev_symlink(policy, cpu); - return 0; - } + cpumask_clear_cpu(cpu, policy->real_cpus); + if (cpumask_empty(policy->real_cpus)) { cpufreq_policy_free(policy, true); - return 0; + return; } - ret = __cpufreq_remove_dev_prepare(dev, sif); + if (cpu != policy->kobj_cpu) { + remove_cpu_dev_symlink(policy, cpu); + } else { + /* + * The CPU owning the policy object is going away. Move it to + * another suitable CPU. + */ + unsigned int new_cpu = cpumask_first(policy->real_cpus); + struct device *new_dev = get_cpu_device(new_cpu); - if (!ret) - ret = __cpufreq_remove_dev_finish(dev, sif); + dev_dbg(dev, "%s: Moving policy object to CPU%u\n", __func__, new_cpu); - return ret; + sysfs_remove_link(&new_dev->kobj, "cpufreq"); + policy->kobj_cpu = new_cpu; + WARN_ON(kobject_move(&policy->kobj, &new_dev->kobj)); + } } static void handle_update(struct work_struct *work) @@ -2395,11 +2395,11 @@ static int cpufreq_cpu_callback(struct notifier_block *nfb, break; case CPU_DOWN_PREPARE: - __cpufreq_remove_dev_prepare(dev, NULL); + __cpufreq_remove_dev_prepare(dev); break; case CPU_POST_DEAD: - __cpufreq_remove_dev_finish(dev, NULL); + __cpufreq_remove_dev_finish(dev); break; case CPU_DOWN_FAILED: diff --git a/drivers/cpufreq/exynos-cpufreq.c b/drivers/cpufreq/exynos-cpufreq.c index ae5b2bd3a978..fa3dd840a837 100644 --- a/drivers/cpufreq/exynos-cpufreq.c +++ b/drivers/cpufreq/exynos-cpufreq.c @@ -180,7 +180,7 @@ static int exynos_cpufreq_probe(struct platform_device *pdev) ret = exynos5250_cpufreq_init(exynos_info); } else { pr_err("%s: Unknown SoC type\n", __func__); - return -ENODEV; + ret = -ENODEV; } if (ret) @@ -188,12 +188,14 @@ static int exynos_cpufreq_probe(struct platform_device *pdev) if (exynos_info->set_freq == NULL) { dev_err(&pdev->dev, "No set_freq function (ERR)\n"); + ret = -EINVAL; goto err_vdd_arm; } arm_regulator = regulator_get(NULL, "vdd_arm"); if (IS_ERR(arm_regulator)) { dev_err(&pdev->dev, "failed to get resource vdd_arm\n"); + ret = -EINVAL; goto err_vdd_arm; } @@ -225,7 +227,7 @@ err_cpufreq_reg: regulator_put(arm_regulator); err_vdd_arm: kfree(exynos_info); - return -EINVAL; + return ret; } static struct platform_driver exynos_cpufreq_platdrv = { diff --git a/drivers/cpufreq/intel_pstate.c b/drivers/cpufreq/intel_pstate.c index 15ada47bb720..7898de054f4e 100644 --- a/drivers/cpufreq/intel_pstate.c +++ b/drivers/cpufreq/intel_pstate.c @@ -681,6 +681,7 @@ static struct cpu_defaults knl_params = { .get_max = core_get_max_pstate, .get_min = core_get_min_pstate, .get_turbo = knl_get_turbo_pstate, + .get_scaling = core_get_scaling, .set = core_set_pstate, }, }; @@ -765,7 +766,7 @@ static inline void intel_pstate_sample(struct cpudata *cpu) local_irq_save(flags); rdmsrl(MSR_IA32_APERF, aperf); rdmsrl(MSR_IA32_MPERF, mperf); - tsc = native_read_tsc(); + tsc = rdtsc(); local_irq_restore(flags); cpu->last_sample_time = cpu->sample.time; diff --git a/drivers/cpufreq/loongson2_cpufreq.c b/drivers/cpufreq/loongson2_cpufreq.c index e362860c2b50..cd593c1f66dc 100644 --- a/drivers/cpufreq/loongson2_cpufreq.c +++ b/drivers/cpufreq/loongson2_cpufreq.c @@ -20,7 +20,7 @@ #include <asm/clock.h> #include <asm/idle.h> -#include <asm/mach-loongson/loongson.h> +#include <asm/mach-loongson64/loongson.h> static uint nowait; diff --git a/drivers/cpufreq/tegra124-cpufreq.c b/drivers/cpufreq/tegra124-cpufreq.c new file mode 100644 index 000000000000..20bcceb58ccc --- /dev/null +++ b/drivers/cpufreq/tegra124-cpufreq.c @@ -0,0 +1,214 @@ +/* + * Tegra 124 cpufreq driver + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * 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) KBUILD_MODNAME ": " fmt + +#include <linux/clk.h> +#include <linux/cpufreq-dt.h> +#include <linux/err.h> +#include <linux/init.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/of_device.h> +#include <linux/of.h> +#include <linux/platform_device.h> +#include <linux/pm_opp.h> +#include <linux/regulator/consumer.h> +#include <linux/types.h> + +struct tegra124_cpufreq_priv { + struct regulator *vdd_cpu_reg; + struct clk *cpu_clk; + struct clk *pllp_clk; + struct clk *pllx_clk; + struct clk *dfll_clk; + struct platform_device *cpufreq_dt_pdev; +}; + +static int tegra124_cpu_switch_to_dfll(struct tegra124_cpufreq_priv *priv) +{ + struct clk *orig_parent; + int ret; + + ret = clk_set_rate(priv->dfll_clk, clk_get_rate(priv->cpu_clk)); + if (ret) + return ret; + + orig_parent = clk_get_parent(priv->cpu_clk); + clk_set_parent(priv->cpu_clk, priv->pllp_clk); + + ret = clk_prepare_enable(priv->dfll_clk); + if (ret) + goto out; + + clk_set_parent(priv->cpu_clk, priv->dfll_clk); + + return 0; + +out: + clk_set_parent(priv->cpu_clk, orig_parent); + + return ret; +} + +static void tegra124_cpu_switch_to_pllx(struct tegra124_cpufreq_priv *priv) +{ + clk_set_parent(priv->cpu_clk, priv->pllp_clk); + clk_disable_unprepare(priv->dfll_clk); + regulator_sync_voltage(priv->vdd_cpu_reg); + clk_set_parent(priv->cpu_clk, priv->pllx_clk); +} + +static struct cpufreq_dt_platform_data cpufreq_dt_pd = { + .independent_clocks = false, +}; + +static int tegra124_cpufreq_probe(struct platform_device *pdev) +{ + struct tegra124_cpufreq_priv *priv; + struct device_node *np; + struct device *cpu_dev; + struct platform_device_info cpufreq_dt_devinfo = {}; + int ret; + + priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + cpu_dev = get_cpu_device(0); + if (!cpu_dev) + return -ENODEV; + + np = of_cpu_device_node_get(0); + if (!np) + return -ENODEV; + + priv->vdd_cpu_reg = regulator_get(cpu_dev, "vdd-cpu"); + if (IS_ERR(priv->vdd_cpu_reg)) { + ret = PTR_ERR(priv->vdd_cpu_reg); + goto out_put_np; + } + + priv->cpu_clk = of_clk_get_by_name(np, "cpu_g"); + if (IS_ERR(priv->cpu_clk)) { + ret = PTR_ERR(priv->cpu_clk); + goto out_put_vdd_cpu_reg; + } + + priv->dfll_clk = of_clk_get_by_name(np, "dfll"); + if (IS_ERR(priv->dfll_clk)) { + ret = PTR_ERR(priv->dfll_clk); + goto out_put_cpu_clk; + } + + priv->pllx_clk = of_clk_get_by_name(np, "pll_x"); + if (IS_ERR(priv->pllx_clk)) { + ret = PTR_ERR(priv->pllx_clk); + goto out_put_dfll_clk; + } + + priv->pllp_clk = of_clk_get_by_name(np, "pll_p"); + if (IS_ERR(priv->pllp_clk)) { + ret = PTR_ERR(priv->pllp_clk); + goto out_put_pllx_clk; + } + + ret = tegra124_cpu_switch_to_dfll(priv); + if (ret) + goto out_put_pllp_clk; + + cpufreq_dt_devinfo.name = "cpufreq-dt"; + cpufreq_dt_devinfo.parent = &pdev->dev; + cpufreq_dt_devinfo.data = &cpufreq_dt_pd; + cpufreq_dt_devinfo.size_data = sizeof(cpufreq_dt_pd); + + priv->cpufreq_dt_pdev = + platform_device_register_full(&cpufreq_dt_devinfo); + if (IS_ERR(priv->cpufreq_dt_pdev)) { + ret = PTR_ERR(priv->cpufreq_dt_pdev); + goto out_switch_to_pllx; + } + + platform_set_drvdata(pdev, priv); + + return 0; + +out_switch_to_pllx: + tegra124_cpu_switch_to_pllx(priv); +out_put_pllp_clk: + clk_put(priv->pllp_clk); +out_put_pllx_clk: + clk_put(priv->pllx_clk); +out_put_dfll_clk: + clk_put(priv->dfll_clk); +out_put_cpu_clk: + clk_put(priv->cpu_clk); +out_put_vdd_cpu_reg: + regulator_put(priv->vdd_cpu_reg); +out_put_np: + of_node_put(np); + + return ret; +} + +static int tegra124_cpufreq_remove(struct platform_device *pdev) +{ + struct tegra124_cpufreq_priv *priv = platform_get_drvdata(pdev); + + platform_device_unregister(priv->cpufreq_dt_pdev); + tegra124_cpu_switch_to_pllx(priv); + + clk_put(priv->pllp_clk); + clk_put(priv->pllx_clk); + clk_put(priv->dfll_clk); + clk_put(priv->cpu_clk); + regulator_put(priv->vdd_cpu_reg); + + return 0; +} + +static struct platform_driver tegra124_cpufreq_platdrv = { + .driver.name = "cpufreq-tegra124", + .probe = tegra124_cpufreq_probe, + .remove = tegra124_cpufreq_remove, +}; + +static int __init tegra_cpufreq_init(void) +{ + int ret; + struct platform_device *pdev; + + if (!of_machine_is_compatible("nvidia,tegra124")) + return -ENODEV; + + /* + * Platform driver+device required for handling EPROBE_DEFER with + * the regulator and the DFLL clock + */ + ret = platform_driver_register(&tegra124_cpufreq_platdrv); + if (ret) + return ret; + + pdev = platform_device_register_simple("cpufreq-tegra124", -1, NULL, 0); + if (IS_ERR(pdev)) { + platform_driver_unregister(&tegra124_cpufreq_platdrv); + return PTR_ERR(pdev); + } + + return 0; +} +module_init(tegra_cpufreq_init); + +MODULE_AUTHOR("Tuomas Tynkkynen <ttynkkynen@nvidia.com>"); +MODULE_DESCRIPTION("cpufreq driver for NVIDIA Tegra124"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/cpufreq/tegra-cpufreq.c b/drivers/cpufreq/tegra20-cpufreq.c index 8084c7f7e206..8084c7f7e206 100644 --- a/drivers/cpufreq/tegra-cpufreq.c +++ b/drivers/cpufreq/tegra20-cpufreq.c |