Skip to content

Commit

Permalink
Merge tag 'tegra-for-4.3-cpufreq' of git://git.kernel.org/pub/scm/lin…
Browse files Browse the repository at this point in the history
…ux/kernel/git/tegra/linux into next/drivers

ARM: tegra: CPU frequency scaling for v4.3-rc1

This adds CPU frequency scaling support for Tegra124.

* tag 'tegra-for-4.3-cpufreq' of git://git.kernel.org/pub/scm/linux/kernel/git/tegra/linux:
  cpufreq: Add cpufreq driver for Tegra124
  cpufreq: tegra: Rename tegra-cpufreq to tegra20-cpufreq
  cpufreq: tegra124: Add device tree bindings

Signed-off-by: Olof Johansson <olof@lixom.net>
  • Loading branch information
Olof Johansson committed Aug 21, 2015
2 parents 5378e46 + 9eb15db commit 420f262
Show file tree
Hide file tree
Showing 5 changed files with 270 additions and 4 deletions.
44 changes: 44 additions & 0 deletions Documentation/devicetree/bindings/cpufreq/tegra124-cpufreq.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
Tegra124 CPU frequency scaling driver bindings
----------------------------------------------

Both required and optional properties listed below must be defined
under node /cpus/cpu@0.

Required properties:
- clocks: Must contain an entry for each entry in clock-names.
See ../clocks/clock-bindings.txt for details.
- clock-names: Must include the following entries:
- cpu_g: Clock mux for the fast CPU cluster.
- cpu_lp: Clock mux for the low-power CPU cluster.
- pll_x: Fast PLL clocksource.
- pll_p: Auxiliary PLL used during fast PLL rate changes.
- dfll: Fast DFLL clocksource that also automatically scales CPU voltage.
- vdd-cpu-supply: Regulator for CPU voltage

Optional properties:
- clock-latency: Specify the possible maximum transition latency for clock,
in unit of nanoseconds.

Example:
--------
cpus {
#address-cells = <1>;
#size-cells = <0>;

cpu@0 {
device_type = "cpu";
compatible = "arm,cortex-a15";
reg = <0>;

clocks = <&tegra_car TEGRA124_CLK_CCLK_G>,
<&tegra_car TEGRA124_CLK_CCLK_LP>,
<&tegra_car TEGRA124_CLK_PLL_X>,
<&tegra_car TEGRA124_CLK_PLL_P>,
<&dfll>;
clock-names = "cpu_g", "cpu_lp", "pll_x", "pll_p", "dfll";
clock-latency = <300000>;
vdd-cpu-supply: <&vdd_cpu>;
};

<...>
};
13 changes: 10 additions & 3 deletions drivers/cpufreq/Kconfig.arm
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down
3 changes: 2 additions & 1 deletion drivers/cpufreq/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -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

##################################################################################
Expand Down
214 changes: 214 additions & 0 deletions drivers/cpufreq/tegra124-cpufreq.c
Original file line number Diff line number Diff line change
@@ -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");
File renamed without changes.

0 comments on commit 420f262

Please sign in to comment.