diff mbox

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

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

Commit Message

Bill Huang Dec. 19, 2013, 11:18 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 v4:

- Remove module driver keyword since we'll not be built as a module driver
- Read SoC compatible Id from DT root instead of statically define it.
- Rebased on linux-next branch of git://git.kernel.org/pub/scm/linux/kernel/git/rafael/linux-pm.git

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                    |  283 +++-----------------
 .../cpufreq/{tegra-cpufreq.c => tegra20-cpufreq.c} |   96 +++----
 include/linux/tegra-cpufreq.h                      |   23 ++
 6 files changed, 119 insertions(+), 298 deletions(-)
 copy drivers/cpufreq/{tegra-cpufreq.c => tegra20-cpufreq.c} (66%)
 create mode 100644 include/linux/tegra-cpufreq.h

Comments

Stephen Warren Dec. 19, 2013, 7:33 p.m. UTC | #1
On 12/19/2013 04:18 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.

Tested-by: Stephen Warren <swarren@nvidia.com>
Acked-by: Stephen Warren <swarren@nvidia.com>

It should be fine to merge the arch/arm/mach-tegra/ changes in this
patch through the cpufreq tree, since I don't think they'll conflict at
all with anything in other trees this kernel cycle.

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

> -#define NUM_CPUS	2
> +#define MAX_CPUS	2

Nit: Given this driver is explicitly for a 2-CPU SoC, I don't think
there's any need to s/NUM/MAX/ here. But I don't care that much.

--
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
Viresh Kumar Dec. 20, 2013, 9:31 a.m. UTC | #2
On 19 December 2013 16:48, Bill Huang <bilhuang@nvidia.com> wrote:
> diff --git a/drivers/cpufreq/Kconfig.arm b/drivers/cpufreq/Kconfig.arm
> index ce52ed9..22dfc43 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

Probably just in case you agree to my next comment:

            depends on 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_TEGRA_CPUFREQ.
> +
> +         If in doubt, say N.


> diff --git a/drivers/cpufreq/tegra-cpufreq.c b/drivers/cpufreq/tegra-cpufreq.c
>  /*
> + * 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.
>   */
>
>  #include <linux/kernel.h>
> +#include <linux/platform_device.h>
> +#include <linux/of.h>
> +#include <linux/tegra-cpufreq.h>
> +
> +int __init tegra_cpufreq_init(void)
> +{
> +       struct device_node *root;
> +
> +       root = of_find_node_by_path("/");
> +       if (root) {
> +               struct platform_device_info devinfo;
> +               const char *compat;
> +               int i;
> +
> +               memset(&devinfo, 0, sizeof(devinfo));
> +               i = of_property_count_strings(root, "compatible");
> +               if (i > 0) {
> +                       of_property_read_string_index(
> +                               root, "compatible", i - 1, &compat);
> +                       devinfo.name = kasprintf(
> +                                       GFP_KERNEL, "%s-cpufreq", compat);
> +                       platform_device_register_full(&devinfo);
> +               }
>         }
> +EXPORT_SYMBOL(tegra_cpufreq_init);

Probably above is all that is present in this file. This is just about adding
the right cpufreq device so that right driver can get probed.

I don't think this code is present at the right place. We don't need a file
in cpufreq/ which is there just to add a device :) ..

Probably move this piece of code to arch/mach-tegra where you are calling
init.

And so remove ARM_TEGRA_CPUFREQ config option completely.
--
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. 20, 2013, 10:25 a.m. UTC | #3
On 12/20/2013 05:31 PM, Viresh Kumar wrote:
> On 19 December 2013 16:48, Bill Huang <bilhuang@nvidia.com> wrote:
>> diff --git a/drivers/cpufreq/Kconfig.arm b/drivers/cpufreq/Kconfig.arm
>> index ce52ed9..22dfc43 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
>
> Probably just in case you agree to my next comment:
>
>              depends on 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_TEGRA_CPUFREQ.
>> +
>> +         If in doubt, say N.
>
>
>> diff --git a/drivers/cpufreq/tegra-cpufreq.c b/drivers/cpufreq/tegra-cpufreq.c
>>   /*
>> + * 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.
>>    */
>>
>>   #include <linux/kernel.h>
>> +#include <linux/platform_device.h>
>> +#include <linux/of.h>
>> +#include <linux/tegra-cpufreq.h>
>> +
>> +int __init tegra_cpufreq_init(void)
>> +{
>> +       struct device_node *root;
>> +
>> +       root = of_find_node_by_path("/");
>> +       if (root) {
>> +               struct platform_device_info devinfo;
>> +               const char *compat;
>> +               int i;
>> +
>> +               memset(&devinfo, 0, sizeof(devinfo));
>> +               i = of_property_count_strings(root, "compatible");
>> +               if (i > 0) {
>> +                       of_property_read_string_index(
>> +                               root, "compatible", i - 1, &compat);
>> +                       devinfo.name = kasprintf(
>> +                                       GFP_KERNEL, "%s-cpufreq", compat);
>> +                       platform_device_register_full(&devinfo);
>> +               }
>>          }
>> +EXPORT_SYMBOL(tegra_cpufreq_init);
>
> Probably above is all that is present in this file. This is just about adding
> the right cpufreq device so that right driver can get probed.
>
> I don't think this code is present at the right place. We don't need a file
> in cpufreq/ which is there just to add a device :) ..
>
> Probably move this piece of code to arch/mach-tegra where you are calling
> init.
>
> And so remove ARM_TEGRA_CPUFREQ config option completely.
>
Don't you think it worth creating a file here so this can be shared to 
arm64?
--
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
Viresh Kumar Dec. 20, 2013, 10:33 a.m. UTC | #4
On 20 December 2013 15:55, bilhuang <bilhuang@nvidia.com> wrote:
> Don't you think it worth creating a file here so this can be shared to
> arm64?

We will see how to handle virtual devices when we will start getting
arm64 SoCs. Probably we might end up writing a single file in cpufreq,
if required, that will create virtual devices for every arm64 platform..

So, some people might use it and others wouldn't.. But no platform
specific files for such stuff. So, the best we can do for now is to move
these to platform code as we are talking about arm32 SoC's for now
which do have a mach-* directory..
--
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. 20, 2013, 10:42 a.m. UTC | #5
On 12/20/2013 06:33 PM, Viresh Kumar wrote:
> On 20 December 2013 15:55, bilhuang <bilhuang@nvidia.com> wrote:
>> Don't you think it worth creating a file here so this can be shared to
>> arm64?
>
> We will see how to handle virtual devices when we will start getting
> arm64 SoCs. Probably we might end up writing a single file in cpufreq,
> if required, that will create virtual devices for every arm64 platform..
>
> So, some people might use it and others wouldn't.. But no platform
> specific files for such stuff. So, the best we can do for now is to move
> these to platform code as we are talking about arm32 SoC's for now
> which do have a mach-* directory..
>
OK thanks, this is suggested by Stephen earlier, I'll let him comment in 
case he might think otherwise.
--
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. 20, 2013, 4:29 p.m. UTC | #6
On 12/20/2013 03:42 AM, bilhuang wrote:
> On 12/20/2013 06:33 PM, Viresh Kumar wrote:
>> On 20 December 2013 15:55, bilhuang <bilhuang@nvidia.com> wrote:
>>> Don't you think it worth creating a file here so this can be shared to
>>> arm64?
>>
>> We will see how to handle virtual devices when we will start getting
>> arm64 SoCs. Probably we might end up writing a single file in cpufreq,
>> if required, that will create virtual devices for every arm64 platform..
>>
>> So, some people might use it and others wouldn't.. But no platform
>> specific files for such stuff. So, the best we can do for now is to move
>> these to platform code as we are talking about arm32 SoC's for now
>> which do have a mach-* directory..
>>
> OK thanks, this is suggested by Stephen earlier, I'll let him comment in
> case he might think otherwise.

No, I definitely don't agree here. The rules for arch/arm64 are: no
platform-specific code. We should immediately start planning for that.
If this means renaming the file that creates the virtual device from
tegra-cpufreq.c to something else, so be it, but we shouldn't go
backwards and push stuff into the arch directories.
--
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
Viresh Kumar Dec. 23, 2013, 5:06 a.m. UTC | #7
Ccc'ing Grant and Rob as well.

On 20 December 2013 21:59, Stephen Warren <swarren@wwwdotorg.org> wrote:
> No, I definitely don't agree here. The rules for arch/arm64 are: no
> platform-specific code. We should immediately start planning for that.
> If this means renaming the file that creates the virtual device from
> tegra-cpufreq.c to something else, so be it, but we shouldn't go
> backwards and push stuff into the arch directories.

I don't mind doing this now as well if it is generic enough. I wasn't sure
if you guys wanted to take it on now..

@Bill: So, please create a separate commit for creating such file which
would create a virtual device for probing cpufreq drivers with name picked
from root-node. Compilation of such a file should be configurable but if
it is compiled, then it shouldn't cause any problems if that device isn't
used, for multiplatform kernels specially..

Probably then you can widen the scope of your patchset by modifying
some of the existing drivers which require a device to get cpufreq
driver probed. Currently they are all making such a device from
their arch/ stuff.

I am not sure about the location of such file. Should this be placed in DT
code somewhere or kept in cpufreq? Rob/Grant ??

--
viresh
--
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 Jan. 2, 2014, 11:08 a.m. UTC | #8
On 12/23/2013 01:06 PM, Viresh Kumar wrote:
> Ccc'ing Grant and Rob as well.
>
> On 20 December 2013 21:59, Stephen Warren <swarren@wwwdotorg.org> wrote:
>> No, I definitely don't agree here. The rules for arch/arm64 are: no
>> platform-specific code. We should immediately start planning for that.
>> If this means renaming the file that creates the virtual device from
>> tegra-cpufreq.c to something else, so be it, but we shouldn't go
>> backwards and push stuff into the arch directories.
>
> I don't mind doing this now as well if it is generic enough. I wasn't sure
> if you guys wanted to take it on now..
>
> @Bill: So, please create a separate commit for creating such file which
> would create a virtual device for probing cpufreq drivers with name picked
> from root-node. Compilation of such a file should be configurable but if
> it is compiled, then it shouldn't cause any problems if that device isn't
> used, for multiplatform kernels specially..
>
> Probably then you can widen the scope of your patchset by modifying
> some of the existing drivers which require a device to get cpufreq
> driver probed. Currently they are all making such a device from
> their arch/ stuff.
Actually, I don't have plan or resource on doing this, would it be 
better that you help to do that instead? Thanks.
>
> I am not sure about the location of such file. Should this be placed in DT
> code somewhere or kept in cpufreq? Rob/Grant ??
>
Do we have consensus on where to create such file?
> --
> viresh
>

--
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
Viresh Kumar Jan. 3, 2014, 5:47 a.m. UTC | #9
On 2 January 2014 16:38, bilhuang <bilhuang@nvidia.com> wrote:
> Actually, I don't have plan or resource on doing this, would it be better
> that you help to do that instead? Thanks.

Point taken. I am there to help if required. So, initially you can just make
Tegra work according to the new file we were talking about. I will fix
others later.

>> I am not sure about the location of such file. Should this be placed in DT
>> code somewhere or kept in cpufreq? Rob/Grant ??
>>
> Do we have consensus on where to create such file?

Not yet, probably people were on leaves.

@Grant/Rob: Any inputs here?
--
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..22dfc43 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_TEGRA_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 b7309c3..1ff8025 100644
--- a/drivers/cpufreq/tegra-cpufreq.c
+++ b/drivers/cpufreq/tegra-cpufreq.c
@@ -1,261 +1,42 @@ 
 /*
- * 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>
-#include <linux/suspend.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 },
-};
-
-#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 unsigned long target_cpu_speed[NUM_CPUS];
-static DEFINE_MUTEX(tegra_cpu_lock);
-static bool is_suspended;
-
-static unsigned int tegra_getspeed(unsigned int cpu)
-{
-	unsigned long rate;
-
-	if (cpu >= NUM_CPUS)
-		return 0;
-
-	rate = clk_get_rate(cpu_clk) / 1000;
-	return rate;
-}
-
-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;
-
-	if (tegra_getspeed(0) == rate)
-		return ret;
-
-	/*
-	 * 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 unsigned long tegra_cpu_highest_speed(void)
-{
-	unsigned long rate = 0;
-	int i;
-
-	for_each_online_cpu(i)
-		rate = max(rate, target_cpu_speed[i]);
-	return rate;
-}
-
-static int tegra_target(struct cpufreq_policy *policy, unsigned int index)
-{
-	unsigned int freq;
-	int ret = 0;
-
-	mutex_lock(&tegra_cpu_lock);
-
-	if (is_suspended)
-		goto out;
-
-	freq = freq_table[index].frequency;
-
-	target_cpu_speed[policy->cpu] = freq;
-
-	ret = tegra_update_cpu_speed(policy, tegra_cpu_highest_speed());
-
-out:
-	mutex_unlock(&tegra_cpu_lock);
-	return ret;
-}
-
-static int tegra_pm_notify(struct notifier_block *nb, unsigned long event,
-	void *dummy)
-{
-	mutex_lock(&tegra_cpu_lock);
-	if (event == PM_SUSPEND_PREPARE) {
-		struct cpufreq_policy *policy = cpufreq_cpu_get(0);
-		is_suspended = true;
-		pr_info("Tegra cpufreq suspend: setting frequency to %d kHz\n",
-			freq_table[0].frequency);
-		tegra_update_cpu_speed(policy, freq_table[0].frequency);
-		cpufreq_cpu_put(policy);
-	} else if (event == PM_POST_SUSPEND) {
-		is_suspended = false;
+#include <linux/platform_device.h>
+#include <linux/of.h>
+#include <linux/tegra-cpufreq.h>
+
+int __init tegra_cpufreq_init(void)
+{
+	struct device_node *root;
+
+	root = of_find_node_by_path("/");
+	if (root) {
+		struct platform_device_info devinfo;
+		const char *compat;
+		int i;
+
+		memset(&devinfo, 0, sizeof(devinfo));
+		i = of_property_count_strings(root, "compatible");
+		if (i > 0) {
+			of_property_read_string_index(
+				root, "compatible", i - 1, &compat);
+			devinfo.name = kasprintf(
+					GFP_KERNEL, "%s-cpufreq", compat);
+			platform_device_register_full(&devinfo);
+		}
 	}
-	mutex_unlock(&tegra_cpu_lock);
-
-	return NOTIFY_OK;
-}
-
-static struct notifier_block tegra_cpu_pm_notifier = {
-	.notifier_call = tegra_pm_notify,
-};
-
-static int tegra_cpu_init(struct cpufreq_policy *policy)
-{
-	int ret;
-
-	if (policy->cpu >= NUM_CPUS)
-		return -EINVAL;
-
-	clk_prepare_enable(emc_clk);
-	clk_prepare_enable(cpu_clk);
-
-	target_cpu_speed[policy->cpu] = tegra_getspeed(policy->cpu);
-
-	/* 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;
-	}
-
-	if (policy->cpu == 0)
-		register_pm_notifier(&tegra_cpu_pm_notifier);
 
 	return 0;
 }
-
-static int tegra_cpu_exit(struct cpufreq_policy *policy)
-{
-	cpufreq_frequency_table_put_attr(policy->cpu);
-	clk_disable_unprepare(cpu_clk);
-	clk_disable_unprepare(emc_clk);
-	return 0;
-}
-
-static struct cpufreq_driver tegra_cpufreq_driver = {
-	.verify		= cpufreq_generic_frequency_table_verify,
-	.target_index	= tegra_target,
-	.get		= tegra_getspeed,
-	.init		= tegra_cpu_init,
-	.exit		= tegra_cpu_exit,
-	.name		= "tegra",
-	.attr		= cpufreq_generic_attr,
-};
-
-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/tegra-cpufreq.c b/drivers/cpufreq/tegra20-cpufreq.c
similarity index 66%
copy from drivers/cpufreq/tegra-cpufreq.c
copy to drivers/cpufreq/tegra20-cpufreq.c
index b7309c3..dc8279d 100644
--- a/drivers/cpufreq/tegra-cpufreq.c
+++ b/drivers/cpufreq/tegra20-cpufreq.c
@@ -1,5 +1,6 @@ 
 /*
  * Copyright (C) 2010 Google, Inc.
+ * Copyright (c) 2013, NVIDIA Corporation. All rights reserved.
  *
  * Author:
  *	Colin Cross <ccross@google.com>
@@ -17,16 +18,14 @@ 
  */
 
 #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>
 #include <linux/suspend.h>
+#include <linux/cpu.h>
+#include <linux/platform_device.h>
 
 static struct cpufreq_frequency_table freq_table[] = {
 	{ .frequency = 216000 },
@@ -40,29 +39,26 @@  static struct cpufreq_frequency_table freq_table[] = {
 	{ .frequency = CPUFREQ_TABLE_END },
 };
 
-#define NUM_CPUS	2
+#define MAX_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 unsigned long target_cpu_speed[NUM_CPUS];
-static DEFINE_MUTEX(tegra_cpu_lock);
+static unsigned long target_cpu_speed[MAX_CPUS];
+static DEFINE_MUTEX(tegra20_cpu_lock);
 static bool is_suspended;
 
-static unsigned int tegra_getspeed(unsigned int cpu)
+static unsigned int tegra20_getspeed(unsigned int cpu)
 {
 	unsigned long rate;
 
-	if (cpu >= NUM_CPUS)
-		return 0;
-
 	rate = clk_get_rate(cpu_clk) / 1000;
 	return rate;
 }
 
-static int tegra_cpu_clk_set_rate(unsigned long rate)
+static int tegra20_cpu_clk_set_rate(unsigned long rate)
 {
 	int ret;
 
@@ -98,12 +94,12 @@  out:
 	return ret;
 }
 
-static int tegra_update_cpu_speed(struct cpufreq_policy *policy,
+static int tegra20_update_cpu_speed(struct cpufreq_policy *policy,
 		unsigned long rate)
 {
 	int ret = 0;
 
-	if (tegra_getspeed(0) == rate)
+	if (tegra20_getspeed(0) == rate)
 		return ret;
 
 	/*
@@ -117,7 +113,7 @@  static int tegra_update_cpu_speed(struct cpufreq_policy *policy,
 	else
 		clk_set_rate(emc_clk, 100000000);  /* emc 50Mhz */
 
-	ret = tegra_cpu_clk_set_rate(rate * 1000);
+	ret = tegra20_cpu_clk_set_rate(rate * 1000);
 	if (ret)
 		pr_err("cpu-tegra: Failed to set cpu frequency to %lu kHz\n",
 			rate);
@@ -125,7 +121,7 @@  static int tegra_update_cpu_speed(struct cpufreq_policy *policy,
 	return ret;
 }
 
-static unsigned long tegra_cpu_highest_speed(void)
+static unsigned long tegra20_cpu_highest_speed(void)
 {
 	unsigned long rate = 0;
 	int i;
@@ -135,12 +131,12 @@  static unsigned long tegra_cpu_highest_speed(void)
 	return rate;
 }
 
-static int tegra_target(struct cpufreq_policy *policy, unsigned int index)
+static int tegra20_target(struct cpufreq_policy *policy, unsigned int index)
 {
 	unsigned int freq;
 	int ret = 0;
 
-	mutex_lock(&tegra_cpu_lock);
+	mutex_lock(&tegra20_cpu_lock);
 
 	if (is_suspended)
 		goto out;
@@ -149,47 +145,44 @@  static int tegra_target(struct cpufreq_policy *policy, unsigned int index)
 
 	target_cpu_speed[policy->cpu] = freq;
 
-	ret = tegra_update_cpu_speed(policy, tegra_cpu_highest_speed());
+	ret = tegra20_update_cpu_speed(policy, tegra20_cpu_highest_speed());
 
 out:
-	mutex_unlock(&tegra_cpu_lock);
+	mutex_unlock(&tegra20_cpu_lock);
 	return ret;
 }
 
-static int tegra_pm_notify(struct notifier_block *nb, unsigned long event,
+static int tegra20_pm_notify(struct notifier_block *nb, unsigned long event,
 	void *dummy)
 {
-	mutex_lock(&tegra_cpu_lock);
+	mutex_lock(&tegra20_cpu_lock);
 	if (event == PM_SUSPEND_PREPARE) {
 		struct cpufreq_policy *policy = cpufreq_cpu_get(0);
 		is_suspended = true;
 		pr_info("Tegra cpufreq suspend: setting frequency to %d kHz\n",
 			freq_table[0].frequency);
-		tegra_update_cpu_speed(policy, freq_table[0].frequency);
+		tegra20_update_cpu_speed(policy, freq_table[0].frequency);
 		cpufreq_cpu_put(policy);
 	} else if (event == PM_POST_SUSPEND) {
 		is_suspended = false;
 	}
-	mutex_unlock(&tegra_cpu_lock);
+	mutex_unlock(&tegra20_cpu_lock);
 
 	return NOTIFY_OK;
 }
 
-static struct notifier_block tegra_cpu_pm_notifier = {
-	.notifier_call = tegra_pm_notify,
+static struct notifier_block tegra20_cpu_pm_notifier = {
+	.notifier_call = tegra20_pm_notify,
 };
 
-static int tegra_cpu_init(struct cpufreq_policy *policy)
+static int tegra20_cpu_init(struct cpufreq_policy *policy)
 {
 	int ret;
 
-	if (policy->cpu >= NUM_CPUS)
-		return -EINVAL;
-
 	clk_prepare_enable(emc_clk);
 	clk_prepare_enable(cpu_clk);
 
-	target_cpu_speed[policy->cpu] = tegra_getspeed(policy->cpu);
+	target_cpu_speed[policy->cpu] = tegra20_getspeed(policy->cpu);
 
 	/* FIXME: what's the actual transition time? */
 	ret = cpufreq_generic_init(policy, freq_table, 300 * 1000);
@@ -200,12 +193,12 @@  static int tegra_cpu_init(struct cpufreq_policy *policy)
 	}
 
 	if (policy->cpu == 0)
-		register_pm_notifier(&tegra_cpu_pm_notifier);
+		register_pm_notifier(&tegra20_cpu_pm_notifier);
 
 	return 0;
 }
 
-static int tegra_cpu_exit(struct cpufreq_policy *policy)
+static int tegra20_cpu_exit(struct cpufreq_policy *policy)
 {
 	cpufreq_frequency_table_put_attr(policy->cpu);
 	clk_disable_unprepare(cpu_clk);
@@ -213,17 +206,17 @@  static int tegra_cpu_exit(struct cpufreq_policy *policy)
 	return 0;
 }
 
-static struct cpufreq_driver tegra_cpufreq_driver = {
+static struct cpufreq_driver tegra20_cpufreq_driver = {
 	.verify		= cpufreq_generic_frequency_table_verify,
-	.target_index	= tegra_target,
-	.get		= tegra_getspeed,
-	.init		= tegra_cpu_init,
-	.exit		= tegra_cpu_exit,
+	.target_index	= tegra20_target,
+	.get		= tegra20_getspeed,
+	.init		= tegra20_cpu_init,
+	.exit		= tegra20_cpu_exit,
 	.name		= "tegra",
 	.attr		= cpufreq_generic_attr,
 };
 
-static int __init tegra_cpufreq_init(void)
+static int tegra20_cpufreq_probe(struct platform_device *pdev)
 {
 	cpu_clk = clk_get_sys(NULL, "cclk");
 	if (IS_ERR(cpu_clk))
@@ -243,19 +236,28 @@  static int __init tegra_cpufreq_init(void)
 		return PTR_ERR(emc_clk);
 	}
 
-	return cpufreq_register_driver(&tegra_cpufreq_driver);
+	return cpufreq_register_driver(&tegra20_cpufreq_driver);
 }
 
-static void __exit tegra_cpufreq_exit(void)
+static int tegra20_cpufreq_remove(struct platform_device *pdev)
 {
-        cpufreq_unregister_driver(&tegra_cpufreq_driver);
+	cpufreq_unregister_driver(&tegra20_cpufreq_driver);
 	clk_put(emc_clk);
 	clk_put(cpu_clk);
+	return 0;
 }
 
+static struct platform_driver tegra20_cpufreq_platdrv = {
+	.driver = {
+		.name	= "nvidia,tegra20-cpufreq",
+		.owner	= THIS_MODULE,
+	},
+	.probe		= tegra20_cpufreq_probe,
+	.remove		= tegra20_cpufreq_remove,
+};
 
-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);
+static int __init tegra20_cpufreq_register(void)
+{
+	return platform_driver_register(&tegra20_cpufreq_platdrv);
+}
+device_initcall(tegra20_cpufreq_register);
diff --git a/include/linux/tegra-cpufreq.h b/include/linux/tegra-cpufreq.h
new file mode 100644
index 0000000..2373d10
--- /dev/null
+++ b/include/linux/tegra-cpufreq.h
@@ -0,0 +1,23 @@ 
+/*
+ * 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 0; }
+#endif
+
+#endif /* __LINUX_TEGRA_CPUFREQ_H_ */