diff mbox

[v4,1/1] cpufreq: tegra: Re-model Tegra20 cpufreq driver

Message ID 1386840835-28751-1-git-send-email-bilhuang@nvidia.com
State Not Applicable, archived
Headers show

Commit Message

Bill Huang Dec. 12, 2013, 9:33 a.m. UTC
Re-model Tegra20 cpufreq driver as below.

* Rename tegra-cpufreq.c to tegra20-cpufreq.c since this file supports
  only Tegra20.
* Add probe function so defer probe can be used when we're going to
  support DVFS.
* Create a fake cpufreq platform device with its name being
  "${root_compatible}-cpufreq" so SoC cpufreq driver can bind to it
  accordingly.

Signed-off-by: Bill Huang <bilhuang@nvidia.com>
---

This patch remodel Tegra cpufreq driver to make it more easy to add new
SoC support, in addition to that, adding probe function in the driver to
let probe defer can be used to control init sequence when we are going
to support DVFS.

Changes since v3:

- Create separate driver for each SoCs instead of a central driver indirect
  call to different SoC functions.

Changes since v2:

- Fix Kconfig.
- Rebase on top of branch 'cpufreq-next' on git://git.linaro.org/people/vireshk/linux.git.

Changes since v1:

- Split up patches.
- Split configuration-time data out of structure "tegra_cpufreq_data".
- Bug fixes.

---
 arch/arm/mach-tegra/tegra.c       |    2 +
 drivers/cpufreq/Kconfig.arm       |   12 +++
 drivers/cpufreq/Makefile          |    1 +
 drivers/cpufreq/tegra-cpufreq.c   |  214 ++++++-------------------------------
 drivers/cpufreq/tegra20-cpufreq.c |  192 +++++++++++++++++++++++++++++++++
 include/linux/tegra-cpufreq.h     |   24 +++++
 6 files changed, 265 insertions(+), 180 deletions(-)
 create mode 100644 drivers/cpufreq/tegra20-cpufreq.c
 create mode 100644 include/linux/tegra-cpufreq.h

Comments

Stephen Warren Dec. 13, 2013, 11:21 p.m. UTC | #1
On 12/12/2013 02:33 AM, Bill Huang wrote:
> Re-model Tegra20 cpufreq driver as below.
> 
> * Rename tegra-cpufreq.c to tegra20-cpufreq.c since this file supports
>   only Tegra20.
> * Add probe function so defer probe can be used when we're going to
>   support DVFS.
> * Create a fake cpufreq platform device with its name being
>   "${root_compatible}-cpufreq" so SoC cpufreq driver can bind to it
>   accordingly.

> diff --git a/drivers/cpufreq/Kconfig.arm b/drivers/cpufreq/Kconfig.arm

> +config ARM_TEGRA20_CPUFREQ
> +	bool "NVIDIA TEGRA20"
> +	depends on ARM_TEGRA_CPUFREQ && ARCH_TEGRA_2x_SOC
> +	default y
> +	help
> +	  This enables Tegra20 cpufreq functionality, it adds
> +	  Tegra20 CPU frequency ladder and the call back functions
> +	  to set CPU rate. All the non-SoC dependant codes are
> +	  controlled by the config ARM_TEGRA20_CPUFREQ.

I think that last sentence is no longer true in this patch version. Or,
did you mean to write ARM_TEGRA_CPUFREQ rather than ARM_TEGRA20_CPUFREQ?

> diff --git a/drivers/cpufreq/tegra-cpufreq.c b/drivers/cpufreq/tegra-cpufreq.c

> +static const char * const tegra_soc_compat[] = {
> +	"nvidia,tegra124",
> +	"nvidia,tegra114",
> +	"nvidia,tegra30",
> +	"nvidia,tegra20",
> +	NULL
>  };

That table will need editing for each chip. I wonder if you can do
something like always use the very last entry in /compatible. That would
assume a particular ordering of the compatible entries, but they should
be in the order $board, $soc anyway...

> +int __init tegra_cpufreq_init(void)
> +	int i;
> +
> +	for (i = 0; i < ARRAY_SIZE(tegra_soc_compat); i++) {
> +		if (of_machine_is_compatible(tegra_soc_compat[i])) {
> +			struct platform_device_info devinfo;
> +			char buf[40];
> +
> +			memset(&devinfo, 0, sizeof(devinfo));
> +			strcpy(buf, tegra_soc_compat[i]);
> +			strcat(buf, "-cpufreq");

kasprintf() might be simpler, and would avoid the arbitrary 39-character
string limit and possibility of overflow.

> +			devinfo.name = buf;
> +			platform_device_register_full(&devinfo);

Does the devinfo struct need to stick around, i.e. does
platform_device_register_full keep the pointer, or take a copy of the
struct? If it keeps the pointer, it'd be best to make devinfo a static
global variable.

> diff --git a/drivers/cpufreq/tegra20-cpufreq.c b/drivers/cpufreq/tegra20-cpufreq.c

Please pass the "-C" option to "git format-patch"; I assume that almost
all the code in this file is simply cut/paste verbatim from
tegra-cpufreq.c where it was deleted.

> + * Copyright (C) 2010 Google, Inc.

It's worth adding NV (c) here too.

> +static int tegra20_cpufreq_remove(struct platform_device *pdev)
> +{
> +	cpufreq_unregister_driver(&tegra20_cpufreq_driver);
> +	return 0;
> +}

That leaks all the clk_get_sys() calls. Does building this as a module
work OK?

> +MODULE_LICENSE("GPL");

That should be "GPL v2".

> diff --git a/include/linux/tegra-cpufreq.h b/include/linux/tegra-cpufreq.h

> +#ifdef CONFIG_ARM_TEGRA_CPUFREQ
> +int tegra_cpufreq_init(void);
> +#else
> +static inline int tegra_cpufreq_init(void)
> +{ return; }
> +#endif

If you're going to wrap the { } onto one line, then I think it'd be best
to wrap the whole thing (prototype and body) onto one line. Otherwise,
write:

{
	return;
}

Oh, and you need "return 0" not just "return".
--
To unsubscribe from this list: send the line "unsubscribe linux-tegra" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Bill Huang Dec. 16, 2013, 10:52 a.m. UTC | #2
On 12/14/2013 07:21 AM, Stephen Warren wrote:
> On 12/12/2013 02:33 AM, Bill Huang wrote:
>> Re-model Tegra20 cpufreq driver as below.
>>
>> * Rename tegra-cpufreq.c to tegra20-cpufreq.c since this file supports
>>    only Tegra20.
>> * Add probe function so defer probe can be used when we're going to
>>    support DVFS.
>> * Create a fake cpufreq platform device with its name being
>>    "${root_compatible}-cpufreq" so SoC cpufreq driver can bind to it
>>    accordingly.
>
>> diff --git a/drivers/cpufreq/Kconfig.arm b/drivers/cpufreq/Kconfig.arm
>
>> +config ARM_TEGRA20_CPUFREQ
>> +	bool "NVIDIA TEGRA20"
>> +	depends on ARM_TEGRA_CPUFREQ && ARCH_TEGRA_2x_SOC
>> +	default y
>> +	help
>> +	  This enables Tegra20 cpufreq functionality, it adds
>> +	  Tegra20 CPU frequency ladder and the call back functions
>> +	  to set CPU rate. All the non-SoC dependant codes are
>> +	  controlled by the config ARM_TEGRA20_CPUFREQ.
>
> I think that last sentence is no longer true in this patch version. Or,
> did you mean to write ARM_TEGRA_CPUFREQ rather than ARM_TEGRA20_CPUFREQ?

Right, should be ARM_TEGRA_CPUFREQ, thanks for catching this.
>
>> diff --git a/drivers/cpufreq/tegra-cpufreq.c b/drivers/cpufreq/tegra-cpufreq.c
>
>> +static const char * const tegra_soc_compat[] = {
>> +	"nvidia,tegra124",
>> +	"nvidia,tegra114",
>> +	"nvidia,tegra30",
>> +	"nvidia,tegra20",
>> +	NULL
>>   };
>
> That table will need editing for each chip. I wonder if you can do
> something like always use the very last entry in /compatible. That would
> assume a particular ordering of the compatible entries, but they should
> be in the order $board, $soc anyway...

How do we get subset of a string and making sure it is the last? There 
must be some assumptions here (though they will possibly be true) I 
guess, for example, they should be in the order $board, $soc... and the 
last "nvidia" should be the start of the last compatible id we would 
like to get.
>
>> +int __init tegra_cpufreq_init(void)
>> +	int i;
>> +
>> +	for (i = 0; i < ARRAY_SIZE(tegra_soc_compat); i++) {
>> +		if (of_machine_is_compatible(tegra_soc_compat[i])) {
>> +			struct platform_device_info devinfo;
>> +			char buf[40];
>> +
>> +			memset(&devinfo, 0, sizeof(devinfo));
>> +			strcpy(buf, tegra_soc_compat[i]);
>> +			strcat(buf, "-cpufreq");
>
> kasprintf() might be simpler, and would avoid the arbitrary 39-character
> string limit and possibility of overflow.

Ah yeah, thanks.
>
>> +			devinfo.name = buf;
>> +			platform_device_register_full(&devinfo);
>
> Does the devinfo struct need to stick around, i.e. does
> platform_device_register_full keep the pointer, or take a copy of the
> struct? If it keeps the pointer, it'd be best to make devinfo a static
> global variable.

devinfo is used to provide dev info to create platform device structure, 
so I think it is OK here.
>
>> diff --git a/drivers/cpufreq/tegra20-cpufreq.c b/drivers/cpufreq/tegra20-cpufreq.c
>
> Please pass the "-C" option to "git format-patch"; I assume that almost
> all the code in this file is simply cut/paste verbatim from
> tegra-cpufreq.c where it was deleted.

OK, thanks.
>
>> + * Copyright (C) 2010 Google, Inc.
>
> It's worth adding NV (c) here too.

OK.
>
>> +static int tegra20_cpufreq_remove(struct platform_device *pdev)
>> +{
>> +	cpufreq_unregister_driver(&tegra20_cpufreq_driver);
>> +	return 0;
>> +}
>
> That leaks all the clk_get_sys() calls. Does building this as a module
> work OK?

I should add back those clk_put here.
>
>> +MODULE_LICENSE("GPL");
>
> That should be "GPL v2".

OK.
>
>> diff --git a/include/linux/tegra-cpufreq.h b/include/linux/tegra-cpufreq.h
>
>> +#ifdef CONFIG_ARM_TEGRA_CPUFREQ
>> +int tegra_cpufreq_init(void);
>> +#else
>> +static inline int tegra_cpufreq_init(void)
>> +{ return; }
>> +#endif
>
> If you're going to wrap the { } onto one line, then I think it'd be best
> to wrap the whole thing (prototype and body) onto one line. Otherwise,
> write:
>
> {
> 	return;
> }
>
> Oh, and you need "return 0" not just "return".

Thanks for catching.
>

--
To unsubscribe from this list: send the line "unsubscribe linux-tegra" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Stephen Warren Dec. 16, 2013, 4:58 p.m. UTC | #3
On 12/16/2013 03:52 AM, bilhuang wrote:
> On 12/14/2013 07:21 AM, Stephen Warren wrote:
>> On 12/12/2013 02:33 AM, Bill Huang wrote:
>>> Re-model Tegra20 cpufreq driver as below.
>>>
>>> * Rename tegra-cpufreq.c to tegra20-cpufreq.c since this file supports
>>>    only Tegra20.
>>> * Add probe function so defer probe can be used when we're going to
>>>    support DVFS.
>>> * Create a fake cpufreq platform device with its name being
>>>    "${root_compatible}-cpufreq" so SoC cpufreq driver can bind to it
>>>    accordingly.
>>
>>> diff --git a/drivers/cpufreq/Kconfig.arm b/drivers/cpufreq/Kconfig.arm
>>
>>> +config ARM_TEGRA20_CPUFREQ
>>> +    bool "NVIDIA TEGRA20"
>>> +    depends on ARM_TEGRA_CPUFREQ && ARCH_TEGRA_2x_SOC
>>> +    default y
>>> +    help
>>> +      This enables Tegra20 cpufreq functionality, it adds
>>> +      Tegra20 CPU frequency ladder and the call back functions
>>> +      to set CPU rate. All the non-SoC dependant codes are
>>> +      controlled by the config ARM_TEGRA20_CPUFREQ.
>>
>> I think that last sentence is no longer true in this patch version. Or,
>> did you mean to write ARM_TEGRA_CPUFREQ rather than ARM_TEGRA20_CPUFREQ?
> 
> Right, should be ARM_TEGRA_CPUFREQ, thanks for catching this.
>>
>>> diff --git a/drivers/cpufreq/tegra-cpufreq.c
>>> b/drivers/cpufreq/tegra-cpufreq.c
>>
>>> +static const char * const tegra_soc_compat[] = {
>>> +    "nvidia,tegra124",
>>> +    "nvidia,tegra114",
>>> +    "nvidia,tegra30",
>>> +    "nvidia,tegra20",
>>> +    NULL
>>>   };
>>
>> That table will need editing for each chip. I wonder if you can do
>> something like always use the very last entry in /compatible. That would
>> assume a particular ordering of the compatible entries, but they should
>> be in the order $board, $soc anyway...
> 
> How do we get subset of a string and making sure it is the last? There
> must be some assumptions here (though they will possibly be true) I
> guess, for example, they should be in the order $board, $soc... and the
> last "nvidia" should be the start of the last compatible id we would
> like to get.

The compatible property is an array of strings, rather than one big
string, so you can just keep getting index 0, 1, 2, ... until you find
there aren't any more entries. I think it's reasonable to assume that
the SoC compatible value must be last, and it should be in practice,
although perhaps that is fragile. If you want, we can leave this as-is
for now, and modify it later if it becomes a problem.
--
To unsubscribe from this list: send the line "unsubscribe linux-tegra" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Bill Huang Dec. 17, 2013, 10:46 a.m. UTC | #4
On 12/17/2013 12:58 AM, Stephen Warren wrote:
> On 12/16/2013 03:52 AM, bilhuang wrote:
>> On 12/14/2013 07:21 AM, Stephen Warren wrote:
>>> On 12/12/2013 02:33 AM, Bill Huang wrote:
>>>> Re-model Tegra20 cpufreq driver as below.
>>>>
>>>> * Rename tegra-cpufreq.c to tegra20-cpufreq.c since this file supports
>>>>     only Tegra20.
>>>> * Add probe function so defer probe can be used when we're going to
>>>>     support DVFS.
>>>> * Create a fake cpufreq platform device with its name being
>>>>     "${root_compatible}-cpufreq" so SoC cpufreq driver can bind to it
>>>>     accordingly.
>>>
>>>> diff --git a/drivers/cpufreq/Kconfig.arm b/drivers/cpufreq/Kconfig.arm
>>>
>>>> +config ARM_TEGRA20_CPUFREQ
>>>> +    bool "NVIDIA TEGRA20"
>>>> +    depends on ARM_TEGRA_CPUFREQ && ARCH_TEGRA_2x_SOC
>>>> +    default y
>>>> +    help
>>>> +      This enables Tegra20 cpufreq functionality, it adds
>>>> +      Tegra20 CPU frequency ladder and the call back functions
>>>> +      to set CPU rate. All the non-SoC dependant codes are
>>>> +      controlled by the config ARM_TEGRA20_CPUFREQ.
>>>
>>> I think that last sentence is no longer true in this patch version. Or,
>>> did you mean to write ARM_TEGRA_CPUFREQ rather than ARM_TEGRA20_CPUFREQ?
>>
>> Right, should be ARM_TEGRA_CPUFREQ, thanks for catching this.
>>>
>>>> diff --git a/drivers/cpufreq/tegra-cpufreq.c
>>>> b/drivers/cpufreq/tegra-cpufreq.c
>>>
>>>> +static const char * const tegra_soc_compat[] = {
>>>> +    "nvidia,tegra124",
>>>> +    "nvidia,tegra114",
>>>> +    "nvidia,tegra30",
>>>> +    "nvidia,tegra20",
>>>> +    NULL
>>>>    };
>>>
>>> That table will need editing for each chip. I wonder if you can do
>>> something like always use the very last entry in /compatible. That would
>>> assume a particular ordering of the compatible entries, but they should
>>> be in the order $board, $soc anyway...
>>
>> How do we get subset of a string and making sure it is the last? There
>> must be some assumptions here (though they will possibly be true) I
>> guess, for example, they should be in the order $board, $soc... and the
>> last "nvidia" should be the start of the last compatible id we would
>> like to get.
>
> The compatible property is an array of strings, rather than one big
> string, so you can just keep getting index 0, 1, 2, ... until you find
> there aren't any more entries. I think it's reasonable to assume that
> the SoC compatible value must be last, and it should be in practice,
> although perhaps that is fragile. If you want, we can leave this as-is
> for now, and modify it later if it becomes a problem.
>
Right, thanks.
--
To unsubscribe from this list: send the line "unsubscribe linux-tegra" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
diff mbox

Patch

diff --git a/arch/arm/mach-tegra/tegra.c b/arch/arm/mach-tegra/tegra.c
index 7336817..a9b23e9 100644
--- a/arch/arm/mach-tegra/tegra.c
+++ b/arch/arm/mach-tegra/tegra.c
@@ -34,6 +34,7 @@ 
 #include <linux/usb/tegra_usb_phy.h>
 #include <linux/clk/tegra.h>
 #include <linux/irqchip.h>
+#include <linux/tegra-cpufreq.h>
 
 #include <asm/hardware/cache-l2x0.h>
 #include <asm/mach-types.h>
@@ -160,6 +161,7 @@  static void __init tegra_dt_init_late(void)
 {
 	int i;
 
+	tegra_cpufreq_init();
 	tegra_init_suspend();
 	tegra_cpuidle_init();
 	tegra_powergate_debugfs_init();
diff --git a/drivers/cpufreq/Kconfig.arm b/drivers/cpufreq/Kconfig.arm
index ce52ed9..1cc9213 100644
--- a/drivers/cpufreq/Kconfig.arm
+++ b/drivers/cpufreq/Kconfig.arm
@@ -225,6 +225,18 @@  config ARM_TEGRA_CPUFREQ
 	help
 	  This adds the CPUFreq driver support for TEGRA SOCs.
 
+config ARM_TEGRA20_CPUFREQ
+	bool "NVIDIA TEGRA20"
+	depends on ARM_TEGRA_CPUFREQ && ARCH_TEGRA_2x_SOC
+	default y
+	help
+	  This enables Tegra20 cpufreq functionality, it adds
+	  Tegra20 CPU frequency ladder and the call back functions
+	  to set CPU rate. All the non-SoC dependant codes are
+	  controlled by the config ARM_TEGRA20_CPUFREQ.
+
+	  If in doubt, say N.
+
 config ARM_VEXPRESS_SPC_CPUFREQ
         tristate "Versatile Express SPC based CPUfreq driver"
         select ARM_BIG_LITTLE_CPUFREQ
diff --git a/drivers/cpufreq/Makefile b/drivers/cpufreq/Makefile
index 7494565..331964b 100644
--- a/drivers/cpufreq/Makefile
+++ b/drivers/cpufreq/Makefile
@@ -74,6 +74,7 @@  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_VEXPRESS_SPC_CPUFREQ)	+= vexpress-spc-cpufreq.o
 
 ##################################################################################
diff --git a/drivers/cpufreq/tegra-cpufreq.c b/drivers/cpufreq/tegra-cpufreq.c
index 63f0059..a4be7c4 100644
--- a/drivers/cpufreq/tegra-cpufreq.c
+++ b/drivers/cpufreq/tegra-cpufreq.c
@@ -1,193 +1,47 @@ 
 /*
- * Copyright (C) 2010 Google, Inc.
+ * Copyright (c) 2013, NVIDIA Corporation. All rights reserved.
  *
- * Author:
- *	Colin Cross <ccross@google.com>
- *	Based on arch/arm/plat-omap/cpu-omap.c, (C) 2005 Nokia Corporation
- *
- * 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.
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
  *
+ * This program is distributed in the hope 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.
  */
 
 #include <linux/kernel.h>
-#include <linux/module.h>
-#include <linux/types.h>
-#include <linux/sched.h>
-#include <linux/cpufreq.h>
-#include <linux/delay.h>
-#include <linux/init.h>
-#include <linux/err.h>
-#include <linux/clk.h>
-#include <linux/io.h>
-
-static struct cpufreq_frequency_table freq_table[] = {
-	{ .frequency = 216000 },
-	{ .frequency = 312000 },
-	{ .frequency = 456000 },
-	{ .frequency = 608000 },
-	{ .frequency = 760000 },
-	{ .frequency = 816000 },
-	{ .frequency = 912000 },
-	{ .frequency = 1000000 },
-	{ .frequency = CPUFREQ_TABLE_END },
+#include <linux/platform_device.h>
+#include <linux/of.h>
+#include <linux/tegra-cpufreq.h>
+
+static const char * const tegra_soc_compat[] = {
+	"nvidia,tegra124",
+	"nvidia,tegra114",
+	"nvidia,tegra30",
+	"nvidia,tegra20",
+	NULL
 };
 
-#define NUM_CPUS	2
-
-static struct clk *cpu_clk;
-static struct clk *pll_x_clk;
-static struct clk *pll_p_clk;
-static struct clk *emc_clk;
-
-static int tegra_cpu_clk_set_rate(unsigned long rate)
-{
-	int ret;
-
-	/*
-	 * Take an extra reference to the main pll so it doesn't turn
-	 * off when we move the cpu off of it
-	 */
-	clk_prepare_enable(pll_x_clk);
-
-	ret = clk_set_parent(cpu_clk, pll_p_clk);
-	if (ret) {
-		pr_err("Failed to switch cpu to clock pll_p\n");
-		goto out;
-	}
-
-	if (rate == clk_get_rate(pll_p_clk))
-		goto out;
-
-	ret = clk_set_rate(pll_x_clk, rate);
-	if (ret) {
-		pr_err("Failed to change pll_x to %lu\n", rate);
-		goto out;
-	}
-
-	ret = clk_set_parent(cpu_clk, pll_x_clk);
-	if (ret) {
-		pr_err("Failed to switch cpu to clock pll_x\n");
-		goto out;
-	}
-
-out:
-	clk_disable_unprepare(pll_x_clk);
-	return ret;
-}
-
-static int tegra_update_cpu_speed(struct cpufreq_policy *policy,
-		unsigned long rate)
-{
-	int ret = 0;
-
-	/*
-	 * Vote on memory bus frequency based on cpu frequency
-	 * This sets the minimum frequency, display or avp may request higher
-	 */
-	if (rate >= 816000)
-		clk_set_rate(emc_clk, 600000000); /* cpu 816 MHz, emc max */
-	else if (rate >= 456000)
-		clk_set_rate(emc_clk, 300000000); /* cpu 456 MHz, emc 150Mhz */
-	else
-		clk_set_rate(emc_clk, 100000000);  /* emc 50Mhz */
-
-	ret = tegra_cpu_clk_set_rate(rate * 1000);
-	if (ret)
-		pr_err("cpu-tegra: Failed to set cpu frequency to %lu kHz\n",
-			rate);
-
-	return ret;
-}
-
-static int tegra_target(struct cpufreq_policy *policy, unsigned int index)
-{
-	return tegra_update_cpu_speed(policy, freq_table[index].frequency);
-}
-
-static int tegra_cpu_init(struct cpufreq_policy *policy)
+int __init tegra_cpufreq_init(void)
 {
-	int ret;
-
-	if (policy->cpu >= NUM_CPUS)
-		return -EINVAL;
-
-	clk_prepare_enable(emc_clk);
-	clk_prepare_enable(cpu_clk);
-
-	/* FIXME: what's the actual transition time? */
-	ret = cpufreq_generic_init(policy, freq_table, 300 * 1000);
-	if (ret) {
-		clk_disable_unprepare(cpu_clk);
-		clk_disable_unprepare(emc_clk);
-		return ret;
+	int i;
+
+	for (i = 0; i < ARRAY_SIZE(tegra_soc_compat); i++) {
+		if (of_machine_is_compatible(tegra_soc_compat[i])) {
+			struct platform_device_info devinfo;
+			char buf[40];
+
+			memset(&devinfo, 0, sizeof(devinfo));
+			strcpy(buf, tegra_soc_compat[i]);
+			strcat(buf, "-cpufreq");
+			devinfo.name = buf;
+			platform_device_register_full(&devinfo);
+			break;
+		}
 	}
 
-	policy->clk = cpu_clk;
-	policy->suspend_freq = freq_table[0].frequency;
-	return 0;
-}
-
-static int tegra_cpu_exit(struct cpufreq_policy *policy)
-{
-	clk_disable_unprepare(cpu_clk);
-	clk_disable_unprepare(emc_clk);
 	return 0;
 }
-
-static struct cpufreq_driver tegra_cpufreq_driver = {
-	.flags		= CPUFREQ_NEED_INITIAL_FREQ_CHECK,
-	.verify		= cpufreq_generic_frequency_table_verify,
-	.target_index	= tegra_target,
-	.get		= cpufreq_generic_get,
-	.init		= tegra_cpu_init,
-	.exit		= tegra_cpu_exit,
-	.name		= "tegra",
-	.attr		= cpufreq_generic_attr,
-#ifdef CONFIG_PM
-	.suspend	= cpufreq_generic_suspend,
-#endif
-};
-
-static int __init tegra_cpufreq_init(void)
-{
-	cpu_clk = clk_get_sys(NULL, "cclk");
-	if (IS_ERR(cpu_clk))
-		return PTR_ERR(cpu_clk);
-
-	pll_x_clk = clk_get_sys(NULL, "pll_x");
-	if (IS_ERR(pll_x_clk))
-		return PTR_ERR(pll_x_clk);
-
-	pll_p_clk = clk_get_sys(NULL, "pll_p");
-	if (IS_ERR(pll_p_clk))
-		return PTR_ERR(pll_p_clk);
-
-	emc_clk = clk_get_sys("cpu", "emc");
-	if (IS_ERR(emc_clk)) {
-		clk_put(cpu_clk);
-		return PTR_ERR(emc_clk);
-	}
-
-	return cpufreq_register_driver(&tegra_cpufreq_driver);
-}
-
-static void __exit tegra_cpufreq_exit(void)
-{
-        cpufreq_unregister_driver(&tegra_cpufreq_driver);
-	clk_put(emc_clk);
-	clk_put(cpu_clk);
-}
-
-
-MODULE_AUTHOR("Colin Cross <ccross@android.com>");
-MODULE_DESCRIPTION("cpufreq driver for Nvidia Tegra2");
-MODULE_LICENSE("GPL");
-module_init(tegra_cpufreq_init);
-module_exit(tegra_cpufreq_exit);
+EXPORT_SYMBOL(tegra_cpufreq_init);
diff --git a/drivers/cpufreq/tegra20-cpufreq.c b/drivers/cpufreq/tegra20-cpufreq.c
new file mode 100644
index 0000000..a430701
--- /dev/null
+++ b/drivers/cpufreq/tegra20-cpufreq.c
@@ -0,0 +1,192 @@ 
+/*
+ * Copyright (C) 2010 Google, Inc.
+ *
+ * Author:
+ *	Colin Cross <ccross@google.com>
+ *	Based on arch/arm/plat-omap/cpu-omap.c, (C) 2005 Nokia Corporation
+ *
+ * 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.
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/cpufreq.h>
+#include <linux/err.h>
+#include <linux/clk.h>
+#include <linux/io.h>
+#include <linux/cpu.h>
+#include <linux/platform_device.h>
+
+static struct cpufreq_frequency_table freq_table[] = {
+	{ .frequency = 216000 },
+	{ .frequency = 312000 },
+	{ .frequency = 456000 },
+	{ .frequency = 608000 },
+	{ .frequency = 760000 },
+	{ .frequency = 816000 },
+	{ .frequency = 912000 },
+	{ .frequency = 1000000 },
+	{ .frequency = CPUFREQ_TABLE_END },
+};
+
+static struct clk *cpu_clk;
+static struct clk *pll_x_clk;
+static struct clk *pll_p_clk;
+static struct clk *emc_clk;
+
+static int tegra20_cpu_clk_set_rate(unsigned long rate)
+{
+	int ret;
+
+	/*
+	 * Take an extra reference to the main pll so it doesn't turn
+	 * off when we move the cpu off of it
+	 */
+	clk_prepare_enable(pll_x_clk);
+
+	ret = clk_set_parent(cpu_clk, pll_p_clk);
+	if (ret) {
+		pr_err("Failed to switch cpu to clock pll_p\n");
+		goto out;
+	}
+
+	if (rate == clk_get_rate(pll_p_clk))
+		goto out;
+
+	ret = clk_set_rate(pll_x_clk, rate);
+	if (ret) {
+		pr_err("Failed to change pll_x to %lu\n", rate);
+		goto out;
+	}
+
+	ret = clk_set_parent(cpu_clk, pll_x_clk);
+	if (ret) {
+		pr_err("Failed to switch cpu to clock pll_x\n");
+		goto out;
+	}
+
+out:
+	clk_disable_unprepare(pll_x_clk);
+	return ret;
+}
+
+static int tegra20_update_cpu_speed(struct cpufreq_policy *policy,
+		unsigned long rate)
+{
+	int ret = 0;
+
+	/*
+	 * Vote on memory bus frequency based on cpu frequency
+	 * This sets the minimum frequency, display or avp may request higher
+	 */
+	if (rate >= 816000)
+		clk_set_rate(emc_clk, 600000000); /* cpu 816 MHz, emc max */
+	else if (rate >= 456000)
+		clk_set_rate(emc_clk, 300000000); /* cpu 456 MHz, emc 150Mhz */
+	else
+		clk_set_rate(emc_clk, 100000000);  /* emc 50Mhz */
+
+	ret = tegra20_cpu_clk_set_rate(rate * 1000);
+	if (ret)
+		pr_err("cpu-tegra20: Failed to set cpu frequency to %lu kHz\n",
+			rate);
+
+	return ret;
+}
+
+static int tegra20_target(struct cpufreq_policy *policy, unsigned int index)
+{
+	return tegra20_update_cpu_speed(policy, freq_table[index].frequency);
+}
+
+static int tegra20_cpu_init(struct cpufreq_policy *policy)
+{
+	int ret;
+
+	clk_prepare_enable(emc_clk);
+	clk_prepare_enable(cpu_clk);
+
+	/* FIXME: what's the actual transition time? */
+	ret = cpufreq_generic_init(policy, freq_table, 300 * 1000);
+	if (ret) {
+		clk_disable_unprepare(cpu_clk);
+		clk_disable_unprepare(emc_clk);
+		return ret;
+	}
+
+	policy->clk = cpu_clk;
+	policy->suspend_freq = freq_table[0].frequency;
+	return 0;
+}
+
+static int tegra20_cpu_exit(struct cpufreq_policy *policy)
+{
+	clk_disable_unprepare(cpu_clk);
+	clk_disable_unprepare(emc_clk);
+	return 0;
+}
+
+static struct cpufreq_driver tegra20_cpufreq_driver = {
+	.flags		= CPUFREQ_NEED_INITIAL_FREQ_CHECK,
+	.verify		= cpufreq_generic_frequency_table_verify,
+	.target_index	= tegra20_target,
+	.get		= cpufreq_generic_get,
+	.init		= tegra20_cpu_init,
+	.exit		= tegra20_cpu_exit,
+	.name		= "tegra",
+	.attr		= cpufreq_generic_attr,
+#ifdef CONFIG_PM
+	.suspend	= cpufreq_generic_suspend,
+#endif
+};
+
+static int tegra20_cpufreq_probe(struct platform_device *pdev)
+{
+	cpu_clk = clk_get_sys(NULL, "cclk");
+	if (IS_ERR(cpu_clk))
+		return PTR_ERR(cpu_clk);
+
+	pll_x_clk = clk_get_sys(NULL, "pll_x");
+	if (IS_ERR(pll_x_clk))
+		return PTR_ERR(pll_x_clk);
+
+	pll_p_clk = clk_get_sys(NULL, "pll_p");
+	if (IS_ERR(pll_p_clk))
+		return PTR_ERR(pll_p_clk);
+
+	emc_clk = clk_get_sys("cpu", "emc");
+	if (IS_ERR(emc_clk)) {
+		clk_put(cpu_clk);
+		return PTR_ERR(emc_clk);
+	}
+
+	return cpufreq_register_driver(&tegra20_cpufreq_driver);
+}
+
+static int tegra20_cpufreq_remove(struct platform_device *pdev)
+{
+	cpufreq_unregister_driver(&tegra20_cpufreq_driver);
+	return 0;
+}
+
+static struct platform_driver tegra_cpufreq_platdrv = {
+	.driver = {
+		.name	= "nvidia,tegra20-cpufreq",
+		.owner	= THIS_MODULE,
+	},
+	.probe		= tegra20_cpufreq_probe,
+	.remove		= tegra20_cpufreq_remove,
+};
+module_platform_driver(tegra_cpufreq_platdrv);
+
+MODULE_AUTHOR("Colin Cross <ccross@android.com>");
+MODULE_DESCRIPTION("cpufreq driver for Nvidia Tegra2");
+MODULE_LICENSE("GPL");
diff --git a/include/linux/tegra-cpufreq.h b/include/linux/tegra-cpufreq.h
new file mode 100644
index 0000000..d7643e0
--- /dev/null
+++ b/include/linux/tegra-cpufreq.h
@@ -0,0 +1,24 @@ 
+/*
+ * Copyright (c) 2013, NVIDIA Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope 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.
+ */
+
+#ifndef __LINUX_TEGRA_CPUFREQ_H
+#define __LINUX_TEGRA_CPUFREQ_H
+
+#ifdef CONFIG_ARM_TEGRA_CPUFREQ
+int tegra_cpufreq_init(void);
+#else
+static inline int tegra_cpufreq_init(void)
+{ return; }
+#endif
+
+#endif /* __LINUX_TEGRA_CPUFREQ_H_ */