diff mbox

[4/7] powerpc/85xx: add support to JOG feature using cpufreq interface

Message ID 1320410166-14500-1-git-send-email-chenhui.zhao@freescale.com (mailing list archive)
State Superseded
Headers show

Commit Message

chenhui zhao Nov. 4, 2011, 12:36 p.m. UTC
From: Li Yang <leoli@freescale.com>

Some 85xx silicons like MPC8536 and P1022 has the JOG PM feature.

The patch adds the support to change CPU frequency using the standard
cpufreq interface. Add the all PLL ratio core support. The ratio CORE
to CCB can 1:1, 1.5, 2:1, 2.5:1, 3:1, 3.5:1 and 4:1

Signed-off-by: Dave Liu <daveliu@freescale.com>
Signed-off-by: Li Yang <leoli@freescale.com>
Signed-off-by: Jerry Huang <Chang-Ming.Huang@freescale.com>
Signed-off-by: Zhao Chenhui <chenhui.zhao@freescale.com>
---
 arch/powerpc/platforms/85xx/Makefile  |    1 +
 arch/powerpc/platforms/85xx/cpufreq.c |  255 +++++++++++++++++++++++++++++++++
 arch/powerpc/platforms/Kconfig        |    8 +
 3 files changed, 264 insertions(+), 0 deletions(-)
 create mode 100644 arch/powerpc/platforms/85xx/cpufreq.c

Comments

Scott Wood Nov. 4, 2011, 7:42 p.m. UTC | #1
On 11/04/2011 07:36 AM, Zhao Chenhui wrote:
> From: Li Yang <leoli@freescale.com>
> 
> Some 85xx silicons like MPC8536 and P1022 has the JOG PM feature.
> 
> The patch adds the support to change CPU frequency using the standard
> cpufreq interface. Add the all PLL ratio core support. The ratio CORE
> to CCB can 1:1, 1.5, 2:1, 2.5:1, 3:1, 3.5:1 and 4:1
> 
> Signed-off-by: Dave Liu <daveliu@freescale.com>
> Signed-off-by: Li Yang <leoli@freescale.com>
> Signed-off-by: Jerry Huang <Chang-Ming.Huang@freescale.com>
> Signed-off-by: Zhao Chenhui <chenhui.zhao@freescale.com>
> ---
>  arch/powerpc/platforms/85xx/Makefile  |    1 +
>  arch/powerpc/platforms/85xx/cpufreq.c |  255 +++++++++++++++++++++++++++++++++
>  arch/powerpc/platforms/Kconfig        |    8 +
>  3 files changed, 264 insertions(+), 0 deletions(-)
>  create mode 100644 arch/powerpc/platforms/85xx/cpufreq.c

Please name this something more specific, such as 85xx/cpufreq-jog.c

Other 85xx/qoriq chips, such as p4080, have different mechanisms for
updating CPU frequency.

> +static struct cpufreq_frequency_table mpc85xx_freqs[] = {
> +	{2,	0},
> +	{3,	0},
> +	{4,	0},
> +	{5,	0},
> +	{6,	0},
> +	{7,	0},
> +	{8,	0},
> +	{0,	CPUFREQ_TABLE_END},
> +};

Only p1022 can handle 1:1 (index 2).

> +static void set_pll(unsigned int pll, int cpu)
> +{
> +	int shift;
> +	u32 busfreq, corefreq, val;
> +	u32 core_spd, mask, tmp;
> +
> +	tmp = in_be32(guts + PMJCR);
> +	shift = (cpu == 1) ? CORE1_RATIO_SHIFT : CORE0_RATIO_SHIFT;
> +	busfreq = fsl_get_sys_freq();
> +	val = (pll & CORE_RATIO_MASK) << shift;
> +
> +	corefreq = ((busfreq * pll) >> 1);

Use "/ 2", not ">> 1".  Same asm code, more readable.

> +	/* must set the bit[18/19] if the requested core freq > 533 MHz */
> +	core_spd = (cpu == 1) ? PMJCR_CORE1_SPD_MASK : PMJCR_CORE0_SPD_MASK;
> +	if (corefreq > FREQ_533MHz)
> +		val |= core_spd;

this is the cutoff for p1022 -- on mpc8536 the manual says the cutoff is
800 MHz.

> +	mask = (cpu == 1) ? (PMJCR_CORE1_RATIO_MASK | PMJCR_CORE1_SPD_MASK) :
> +		(PMJCR_CORE0_RATIO_MASK | PMJCR_CORE0_SPD_MASK);
> +	tmp &= ~mask;
> +	tmp |= val;
> +	out_be32(guts + PMJCR, tmp);

clrsetbits_be32()

> +	val = in_be32(guts + PMJCR);
> +	out_be32(guts + POWMGTCSR,
> +			POWMGTCSR_LOSSLESS_MASK | POWMGTCSR_JOG_MASK);

setbits32()

> +	pr_debug("PMJCR request %08x at CPU %d\n", tmp, cpu);
> +}
> +
> +static void verify_pll(int cpu)
> +{
> +	int shift;
> +	u32 busfreq, pll, corefreq;
> +
> +	shift = (cpu == 1) ? CORE1_RATIO_SHIFT : CORE0_RATIO_SHIFT;
> +	busfreq = fsl_get_sys_freq();
> +	pll = (in_be32(guts + PORPLLSR) >> shift) & CORE_RATIO_MASK;
> +
> +	corefreq = (busfreq * pll) >> 1;
> +	corefreq /= 1000000;
> +	pr_debug("PORPLLSR core freq %dMHz at CPU %d\n", corefreq, cpu);
> +}

It looks like the entire point of this function is to make a debug
print...  #ifdef DEBUG the contents?  Or if we mark fsl_get_sys_freq()
as __pure (or better, read this once at init, since it involves
searching the device tree), will it all get optimized away?


> +	/* initialize frequency table */
> +	pr_info("core %d frequency table:\n", policy->cpu);
> +	for (i = 0; mpc85xx_freqs[i].frequency != CPUFREQ_TABLE_END; i++) {
> +		mpc85xx_freqs[i].frequency =
> +				(busfreq * mpc85xx_freqs[i].index) >> 1;
> +		pr_info("%d: %dkHz\n", i, mpc85xx_freqs[i].frequency);
> +	}

This should be pr_debug.

> +	/* the latency of a transition, the unit is ns */
> +	policy->cpuinfo.transition_latency = 2000;
> +
> +	cur_pll = get_pll(policy->cpu);
> +	pr_debug("current pll is at %d\n", cur_pll);
> +
> +	for (i = 0; mpc85xx_freqs[i].frequency != CPUFREQ_TABLE_END; i++) {
> +		if (mpc85xx_freqs[i].index == cur_pll)
> +			policy->cur = mpc85xx_freqs[i].frequency;
> +	}

You could combine these loops.

> +	/* this ensures that policy->cpuinfo_min
> +	 * and policy->cpuinfo_max are set correctly */

comment style

> +static int mpc85xx_cpufreq_target(struct cpufreq_policy *policy,
> +			      unsigned int target_freq,
> +			      unsigned int relation)
> +{
> +	struct cpufreq_freqs freqs;
> +	unsigned int new;
> +
> +	cpufreq_frequency_table_target(policy,
> +				       mpc85xx_freqs,
> +				       target_freq,
> +				       relation,
> +				       &new);
> +
> +	freqs.old = policy->cur;
> +	freqs.new = mpc85xx_freqs[new].frequency;
> +	freqs.cpu = policy->cpu;
> +
> +	mutex_lock(&mpc85xx_switch_mutex);
> +	cpufreq_notify_transition(&freqs, CPUFREQ_PRECHANGE);
> +
> +	pr_info("Setting frequency for core %d to %d kHz, " \
> +		 "PLL ratio is %d/2\n",
> +		 policy->cpu,
> +		 mpc85xx_freqs[new].frequency,
> +		 mpc85xx_freqs[new].index);
> +
> +	set_pll(mpc85xx_freqs[new].index, policy->cpu);
> +
> +	cpufreq_notify_transition(&freqs, CPUFREQ_POSTCHANGE);
> +	mutex_unlock(&mpc85xx_switch_mutex);
> +
> +	ppc_proc_freq = freqs.new * 1000ul;

ppc_proc_freq is global -- can CPUs not have their frequencies adjusted
separately?

It should be under the lock, if the lock is needed at all.

> +/*
> + * module init and destoy
> + */
> +static struct of_device_id mpc85xx_jog_ids[] __initdata = {
> +	{ .compatible = "fsl,mpc8536-guts", },
> +	{ .compatible = "fsl,p1022-guts", },
> +	{}
> +};
> +
> +static int __init mpc85xx_cpufreq_init(void)
> +{
> +	struct device_node *np;
> +
> +	pr_info("Freescale MPC85xx CPU frequency switching driver\n");

If you're going to print something here, print it after you find a node
you can work with -- not on all 85xx/qoriq that have this driver enabled.

-Scott
chenhui zhao Nov. 7, 2011, 10:27 a.m. UTC | #2
On Fri, Nov 04, 2011 at 02:42:54PM -0500, Scott Wood wrote:
> On 11/04/2011 07:36 AM, Zhao Chenhui wrote:
> > From: Li Yang <leoli@freescale.com>
> > 
> > Some 85xx silicons like MPC8536 and P1022 has the JOG PM feature.
> > 
> > The patch adds the support to change CPU frequency using the standard
> > cpufreq interface. Add the all PLL ratio core support. The ratio CORE
> > to CCB can 1:1, 1.5, 2:1, 2.5:1, 3:1, 3.5:1 and 4:1
> > 
> > Signed-off-by: Dave Liu <daveliu@freescale.com>
> > Signed-off-by: Li Yang <leoli@freescale.com>
> > Signed-off-by: Jerry Huang <Chang-Ming.Huang@freescale.com>
> > Signed-off-by: Zhao Chenhui <chenhui.zhao@freescale.com>
> > ---
> >  arch/powerpc/platforms/85xx/Makefile  |    1 +
> >  arch/powerpc/platforms/85xx/cpufreq.c |  255 +++++++++++++++++++++++++++++++++
> >  arch/powerpc/platforms/Kconfig        |    8 +
> >  3 files changed, 264 insertions(+), 0 deletions(-)
> >  create mode 100644 arch/powerpc/platforms/85xx/cpufreq.c
> 
> Please name this something more specific, such as 85xx/cpufreq-jog.c
> 
> Other 85xx/qoriq chips, such as p4080, have different mechanisms for
> updating CPU frequency.
> 
> > +static struct cpufreq_frequency_table mpc85xx_freqs[] = {
> > +	{2,	0},
> > +	{3,	0},
> > +	{4,	0},
> > +	{5,	0},
> > +	{6,	0},
> > +	{7,	0},
> > +	{8,	0},
> > +	{0,	CPUFREQ_TABLE_END},
> > +};
> 
> Only p1022 can handle 1:1 (index 2).
> 
> > +static void set_pll(unsigned int pll, int cpu)
> > +{
> > +	int shift;
> > +	u32 busfreq, corefreq, val;
> > +	u32 core_spd, mask, tmp;
> > +
> > +	tmp = in_be32(guts + PMJCR);
> > +	shift = (cpu == 1) ? CORE1_RATIO_SHIFT : CORE0_RATIO_SHIFT;
> > +	busfreq = fsl_get_sys_freq();
> > +	val = (pll & CORE_RATIO_MASK) << shift;
> > +
> > +	corefreq = ((busfreq * pll) >> 1);
> 
> Use "/ 2", not ">> 1".  Same asm code, more readable.
> 
> > +	/* must set the bit[18/19] if the requested core freq > 533 MHz */
> > +	core_spd = (cpu == 1) ? PMJCR_CORE1_SPD_MASK : PMJCR_CORE0_SPD_MASK;
> > +	if (corefreq > FREQ_533MHz)
> > +		val |= core_spd;
> 
> this is the cutoff for p1022 -- on mpc8536 the manual says the cutoff is
> 800 MHz.
> 
> > +	mask = (cpu == 1) ? (PMJCR_CORE1_RATIO_MASK | PMJCR_CORE1_SPD_MASK) :
> > +		(PMJCR_CORE0_RATIO_MASK | PMJCR_CORE0_SPD_MASK);
> > +	tmp &= ~mask;
> > +	tmp |= val;
> > +	out_be32(guts + PMJCR, tmp);
> 
> clrsetbits_be32()
> 
> > +	val = in_be32(guts + PMJCR);
> > +	out_be32(guts + POWMGTCSR,
> > +			POWMGTCSR_LOSSLESS_MASK | POWMGTCSR_JOG_MASK);
> 
> setbits32()
> 
> > +	pr_debug("PMJCR request %08x at CPU %d\n", tmp, cpu);
> > +}
> > +
> > +static void verify_pll(int cpu)
> > +{
> > +	int shift;
> > +	u32 busfreq, pll, corefreq;
> > +
> > +	shift = (cpu == 1) ? CORE1_RATIO_SHIFT : CORE0_RATIO_SHIFT;
> > +	busfreq = fsl_get_sys_freq();
> > +	pll = (in_be32(guts + PORPLLSR) >> shift) & CORE_RATIO_MASK;
> > +
> > +	corefreq = (busfreq * pll) >> 1;
> > +	corefreq /= 1000000;
> > +	pr_debug("PORPLLSR core freq %dMHz at CPU %d\n", corefreq, cpu);
> > +}
> 
> It looks like the entire point of this function is to make a debug
> print...  #ifdef DEBUG the contents?  Or if we mark fsl_get_sys_freq()
> as __pure (or better, read this once at init, since it involves
> searching the device tree), will it all get optimized away?
> 
> 
> > +	/* initialize frequency table */
> > +	pr_info("core %d frequency table:\n", policy->cpu);
> > +	for (i = 0; mpc85xx_freqs[i].frequency != CPUFREQ_TABLE_END; i++) {
> > +		mpc85xx_freqs[i].frequency =
> > +				(busfreq * mpc85xx_freqs[i].index) >> 1;
> > +		pr_info("%d: %dkHz\n", i, mpc85xx_freqs[i].frequency);
> > +	}
> 
> This should be pr_debug.
> 
> > +	/* the latency of a transition, the unit is ns */
> > +	policy->cpuinfo.transition_latency = 2000;
> > +
> > +	cur_pll = get_pll(policy->cpu);
> > +	pr_debug("current pll is at %d\n", cur_pll);
> > +
> > +	for (i = 0; mpc85xx_freqs[i].frequency != CPUFREQ_TABLE_END; i++) {
> > +		if (mpc85xx_freqs[i].index == cur_pll)
> > +			policy->cur = mpc85xx_freqs[i].frequency;
> > +	}
> 
> You could combine these loops.
> 
> > +	/* this ensures that policy->cpuinfo_min
> > +	 * and policy->cpuinfo_max are set correctly */
> 
> comment style
> 
> > +static int mpc85xx_cpufreq_target(struct cpufreq_policy *policy,
> > +			      unsigned int target_freq,
> > +			      unsigned int relation)
> > +{
> > +	struct cpufreq_freqs freqs;
> > +	unsigned int new;
> > +
> > +	cpufreq_frequency_table_target(policy,
> > +				       mpc85xx_freqs,
> > +				       target_freq,
> > +				       relation,
> > +				       &new);
> > +
> > +	freqs.old = policy->cur;
> > +	freqs.new = mpc85xx_freqs[new].frequency;
> > +	freqs.cpu = policy->cpu;
> > +
> > +	mutex_lock(&mpc85xx_switch_mutex);
> > +	cpufreq_notify_transition(&freqs, CPUFREQ_PRECHANGE);
> > +
> > +	pr_info("Setting frequency for core %d to %d kHz, " \
> > +		 "PLL ratio is %d/2\n",
> > +		 policy->cpu,
> > +		 mpc85xx_freqs[new].frequency,
> > +		 mpc85xx_freqs[new].index);
> > +
> > +	set_pll(mpc85xx_freqs[new].index, policy->cpu);
> > +
> > +	cpufreq_notify_transition(&freqs, CPUFREQ_POSTCHANGE);
> > +	mutex_unlock(&mpc85xx_switch_mutex);
> > +
> > +	ppc_proc_freq = freqs.new * 1000ul;
> 
> ppc_proc_freq is global -- can CPUs not have their frequencies adjusted
> separately?
> 
> It should be under the lock, if the lock is needed at all.
> 

There is only one ppc_proc_freq. no lock.

> > +/*
> > + * module init and destoy
> > + */
> > +static struct of_device_id mpc85xx_jog_ids[] __initdata = {
> > +	{ .compatible = "fsl,mpc8536-guts", },
> > +	{ .compatible = "fsl,p1022-guts", },
> > +	{}
> > +};
> > +
> > +static int __init mpc85xx_cpufreq_init(void)
> > +{
> > +	struct device_node *np;
> > +
> > +	pr_info("Freescale MPC85xx CPU frequency switching driver\n");
> 
> If you're going to print something here, print it after you find a node
> you can work with -- not on all 85xx/qoriq that have this driver enabled.
> 
> -Scott

Thanks. I will fix them all.

-chenhui
Scott Wood Nov. 7, 2011, 6:50 p.m. UTC | #3
On 11/07/2011 04:27 AM, Zhao Chenhui wrote:
> On Fri, Nov 04, 2011 at 02:42:54PM -0500, Scott Wood wrote:
>> On 11/04/2011 07:36 AM, Zhao Chenhui wrote:
>>> +	cpufreq_frequency_table_target(policy,
>>> +				       mpc85xx_freqs,
>>> +				       target_freq,
>>> +				       relation,
>>> +				       &new);
>>> +
>>> +	freqs.old = policy->cur;
>>> +	freqs.new = mpc85xx_freqs[new].frequency;
>>> +	freqs.cpu = policy->cpu;
>>> +
>>> +	mutex_lock(&mpc85xx_switch_mutex);
>>> +	cpufreq_notify_transition(&freqs, CPUFREQ_PRECHANGE);
>>> +
>>> +	pr_info("Setting frequency for core %d to %d kHz, " \
>>> +		 "PLL ratio is %d/2\n",
>>> +		 policy->cpu,
>>> +		 mpc85xx_freqs[new].frequency,
>>> +		 mpc85xx_freqs[new].index);
>>> +
>>> +	set_pll(mpc85xx_freqs[new].index, policy->cpu);
>>> +
>>> +	cpufreq_notify_transition(&freqs, CPUFREQ_POSTCHANGE);
>>> +	mutex_unlock(&mpc85xx_switch_mutex);
>>> +
>>> +	ppc_proc_freq = freqs.new * 1000ul;
>>
>> ppc_proc_freq is global -- can CPUs not have their frequencies adjusted
>> separately?
>>
>> It should be under the lock, if the lock is needed at all.
>>
> 
> There is only one ppc_proc_freq. no lock.

I realize there's only one.

I'm asking whether CPUs can have their frequencies set indpendently --
if the answer is no, and this function is not specific to a CPU, my only
concern is the lock.  Either this function can be called multiple times
in parallel, in which case the ppc_proc_freq update should be inside the
lock, or it can't, in which case why do we need the lock at all?

-Scott
chenhui zhao Nov. 9, 2011, 11:38 a.m. UTC | #4
On Mon, Nov 07, 2011 at 12:50:24PM -0600, Scott Wood wrote:
> On 11/07/2011 04:27 AM, Zhao Chenhui wrote:
> > On Fri, Nov 04, 2011 at 02:42:54PM -0500, Scott Wood wrote:
> >> On 11/04/2011 07:36 AM, Zhao Chenhui wrote:
> >>> +	cpufreq_frequency_table_target(policy,
> >>> +				       mpc85xx_freqs,
> >>> +				       target_freq,
> >>> +				       relation,
> >>> +				       &new);
> >>> +
> >>> +	freqs.old = policy->cur;
> >>> +	freqs.new = mpc85xx_freqs[new].frequency;
> >>> +	freqs.cpu = policy->cpu;
> >>> +
> >>> +	mutex_lock(&mpc85xx_switch_mutex);
> >>> +	cpufreq_notify_transition(&freqs, CPUFREQ_PRECHANGE);
> >>> +
> >>> +	pr_info("Setting frequency for core %d to %d kHz, " \
> >>> +		 "PLL ratio is %d/2\n",
> >>> +		 policy->cpu,
> >>> +		 mpc85xx_freqs[new].frequency,
> >>> +		 mpc85xx_freqs[new].index);
> >>> +
> >>> +	set_pll(mpc85xx_freqs[new].index, policy->cpu);
> >>> +
> >>> +	cpufreq_notify_transition(&freqs, CPUFREQ_POSTCHANGE);
> >>> +	mutex_unlock(&mpc85xx_switch_mutex);
> >>> +
> >>> +	ppc_proc_freq = freqs.new * 1000ul;
> >>
> >> ppc_proc_freq is global -- can CPUs not have their frequencies adjusted
> >> separately?
> >>
> >> It should be under the lock, if the lock is needed at all.
> >>
> > 
> > There is only one ppc_proc_freq. no lock.
> 
> I realize there's only one.
> 
> I'm asking whether CPUs can have their frequencies set indpendently --
> if the answer is no, and this function is not specific to a CPU, my only
> concern is the lock.  Either this function can be called multiple times
> in parallel, in which case the ppc_proc_freq update should be inside the
> lock, or it can't, in which case why do we need the lock at all?
> 
> -Scott

Yes. They can be changed independently.
I will set ppc_proc_freq inside the lock.

-chenhui
Scott Wood Nov. 9, 2011, 4:13 p.m. UTC | #5
On Wed, Nov 09, 2011 at 07:38:13PM +0800, Zhao Chenhui wrote:
> On Mon, Nov 07, 2011 at 12:50:24PM -0600, Scott Wood wrote:
> > On 11/07/2011 04:27 AM, Zhao Chenhui wrote:
> > > There is only one ppc_proc_freq. no lock.
> > 
> > I realize there's only one.
> > 
> > I'm asking whether CPUs can have their frequencies set indpendently --
> > if the answer is no, and this function is not specific to a CPU, my only
> > concern is the lock.  Either this function can be called multiple times
> > in parallel, in which case the ppc_proc_freq update should be inside the
> > lock, or it can't, in which case why do we need the lock at all?
> > 
> > -Scott
> 
> Yes. They can be changed independently.
> I will set ppc_proc_freq inside the lock.

If they can be changed independently, what does the global ppc_proc_freq
mean?

-Scott
diff mbox

Patch

diff --git a/arch/powerpc/platforms/85xx/Makefile b/arch/powerpc/platforms/85xx/Makefile
index 0bdaddc..75432a5 100644
--- a/arch/powerpc/platforms/85xx/Makefile
+++ b/arch/powerpc/platforms/85xx/Makefile
@@ -3,6 +3,7 @@ 
 #
 obj-$(CONFIG_SMP) += smp.o
 obj-$(CONFIG_SUSPEND)	+= sleep.o
+obj-$(CONFIG_MPC85xx_CPUFREQ) += cpufreq.o
 
 obj-$(CONFIG_MPC8540_ADS) += mpc85xx_ads.o
 obj-$(CONFIG_MPC8560_ADS) += mpc85xx_ads.o
diff --git a/arch/powerpc/platforms/85xx/cpufreq.c b/arch/powerpc/platforms/85xx/cpufreq.c
new file mode 100644
index 0000000..20f0458
--- /dev/null
+++ b/arch/powerpc/platforms/85xx/cpufreq.c
@@ -0,0 +1,255 @@ 
+/*
+ * Copyright (C) 2008-2011 Freescale Semiconductor, Inc.
+ *	Dave Liu <daveliu@freescale.com>
+ *
+ * The cpufreq driver is for Freescale 85xx processor,
+ * based on arch/powerpc/platforms/cell/cbe_cpufreq.c
+ * (C) Copyright IBM Deutschland Entwicklung GmbH 2005-2007
+ *	Christian Krafft <krafft@de.ibm.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <linux/cpufreq.h>
+#include <linux/of_platform.h>
+
+#include <asm/prom.h>
+#include <asm/time.h>
+#include <asm/reg.h>
+#include <asm/io.h>
+
+#include <sysdev/fsl_soc.h>
+
+static DEFINE_MUTEX(mpc85xx_switch_mutex);
+static void __iomem *guts;
+
+static struct cpufreq_frequency_table mpc85xx_freqs[] = {
+	{2,	0},
+	{3,	0},
+	{4,	0},
+	{5,	0},
+	{6,	0},
+	{7,	0},
+	{8,	0},
+	{0,	CPUFREQ_TABLE_END},
+};
+
+#define FREQ_533MHz	533340000
+#define CORE0_RATIO_SHIFT	16
+#define CORE1_RATIO_SHIFT	24
+#define CORE_RATIO_MASK		0x3f
+
+#define PORPLLSR	0x0
+
+#define PMJCR		0x7c
+#define PMJCR_CORE0_SPD_MASK	0x00001000
+#define PMJCR_CORE1_SPD_MASK	0x00002000
+#define PMJCR_CORE0_RATIO_MASK	(CORE_RATIO_MASK << CORE0_RATIO_SHIFT)
+#define PMJCR_CORE1_RATIO_MASK	(CORE_RATIO_MASK << CORE1_RATIO_SHIFT)
+
+#define POWMGTCSR	0x80
+#define POWMGTCSR_LOSSLESS_MASK	0x00400000
+#define POWMGTCSR_JOG_MASK	0x00200000
+
+/*
+ * hardware specific functions
+ */
+static int get_pll(int cpu)
+{
+	int ret, shift;
+	u32 pll = in_be32(guts + PORPLLSR);
+	shift = (cpu == 1) ? CORE1_RATIO_SHIFT : CORE0_RATIO_SHIFT;
+	ret = (pll >> shift) & CORE_RATIO_MASK;
+
+	return ret;
+}
+
+static void set_pll(unsigned int pll, int cpu)
+{
+	int shift;
+	u32 busfreq, corefreq, val;
+	u32 core_spd, mask, tmp;
+
+	tmp = in_be32(guts + PMJCR);
+	shift = (cpu == 1) ? CORE1_RATIO_SHIFT : CORE0_RATIO_SHIFT;
+	busfreq = fsl_get_sys_freq();
+	val = (pll & CORE_RATIO_MASK) << shift;
+
+	corefreq = ((busfreq * pll) >> 1);
+	/* must set the bit[18/19] if the requested core freq > 533 MHz */
+	core_spd = (cpu == 1) ? PMJCR_CORE1_SPD_MASK : PMJCR_CORE0_SPD_MASK;
+	if (corefreq > FREQ_533MHz)
+		val |= core_spd;
+
+	mask = (cpu == 1) ? (PMJCR_CORE1_RATIO_MASK | PMJCR_CORE1_SPD_MASK) :
+		(PMJCR_CORE0_RATIO_MASK | PMJCR_CORE0_SPD_MASK);
+	tmp &= ~mask;
+	tmp |= val;
+	out_be32(guts + PMJCR, tmp);
+	val = in_be32(guts + PMJCR);
+	out_be32(guts + POWMGTCSR,
+			POWMGTCSR_LOSSLESS_MASK | POWMGTCSR_JOG_MASK);
+	pr_debug("PMJCR request %08x at CPU %d\n", tmp, cpu);
+}
+
+static void verify_pll(int cpu)
+{
+	int shift;
+	u32 busfreq, pll, corefreq;
+
+	shift = (cpu == 1) ? CORE1_RATIO_SHIFT : CORE0_RATIO_SHIFT;
+	busfreq = fsl_get_sys_freq();
+	pll = (in_be32(guts + PORPLLSR) >> shift) & CORE_RATIO_MASK;
+
+	corefreq = (busfreq * pll) >> 1;
+	corefreq /= 1000000;
+	pr_debug("PORPLLSR core freq %dMHz at CPU %d\n", corefreq, cpu);
+}
+
+/*
+ * cpufreq functions
+ */
+static int mpc85xx_cpufreq_cpu_init(struct cpufreq_policy *policy)
+{
+	u32 busfreq = fsl_get_sys_freq();
+	int i, cur_pll;
+
+	/* we need the freq unit with kHz */
+	busfreq /= 1000;
+
+	/* initialize frequency table */
+	pr_info("core %d frequency table:\n", policy->cpu);
+	for (i = 0; mpc85xx_freqs[i].frequency != CPUFREQ_TABLE_END; i++) {
+		mpc85xx_freqs[i].frequency =
+				(busfreq * mpc85xx_freqs[i].index) >> 1;
+		pr_info("%d: %dkHz\n", i, mpc85xx_freqs[i].frequency);
+	}
+
+	/* the latency of a transition, the unit is ns */
+	policy->cpuinfo.transition_latency = 2000;
+
+	cur_pll = get_pll(policy->cpu);
+	pr_debug("current pll is at %d\n", cur_pll);
+
+	for (i = 0; mpc85xx_freqs[i].frequency != CPUFREQ_TABLE_END; i++) {
+		if (mpc85xx_freqs[i].index == cur_pll)
+			policy->cur = mpc85xx_freqs[i].frequency;
+	}
+	pr_debug("current core freq is %d\n", policy->cur);
+
+	cpufreq_frequency_table_get_attr(mpc85xx_freqs, policy->cpu);
+
+	/* this ensures that policy->cpuinfo_min
+	 * and policy->cpuinfo_max are set correctly */
+	return cpufreq_frequency_table_cpuinfo(policy, mpc85xx_freqs);
+}
+
+static int mpc85xx_cpufreq_cpu_exit(struct cpufreq_policy *policy)
+{
+	cpufreq_frequency_table_put_attr(policy->cpu);
+	return 0;
+}
+
+static int mpc85xx_cpufreq_verify(struct cpufreq_policy *policy)
+{
+	return cpufreq_frequency_table_verify(policy, mpc85xx_freqs);
+}
+
+static int mpc85xx_cpufreq_target(struct cpufreq_policy *policy,
+			      unsigned int target_freq,
+			      unsigned int relation)
+{
+	struct cpufreq_freqs freqs;
+	unsigned int new;
+
+	cpufreq_frequency_table_target(policy,
+				       mpc85xx_freqs,
+				       target_freq,
+				       relation,
+				       &new);
+
+	freqs.old = policy->cur;
+	freqs.new = mpc85xx_freqs[new].frequency;
+	freqs.cpu = policy->cpu;
+
+	mutex_lock(&mpc85xx_switch_mutex);
+	cpufreq_notify_transition(&freqs, CPUFREQ_PRECHANGE);
+
+	pr_info("Setting frequency for core %d to %d kHz, " \
+		 "PLL ratio is %d/2\n",
+		 policy->cpu,
+		 mpc85xx_freqs[new].frequency,
+		 mpc85xx_freqs[new].index);
+
+	set_pll(mpc85xx_freqs[new].index, policy->cpu);
+
+	cpufreq_notify_transition(&freqs, CPUFREQ_POSTCHANGE);
+	mutex_unlock(&mpc85xx_switch_mutex);
+
+	ppc_proc_freq = freqs.new * 1000ul;
+
+	verify_pll(policy->cpu);
+
+	return 0;
+}
+
+static struct cpufreq_driver mpc85xx_cpufreq_driver = {
+	.verify		= mpc85xx_cpufreq_verify,
+	.target		= mpc85xx_cpufreq_target,
+	.init		= mpc85xx_cpufreq_cpu_init,
+	.exit		= mpc85xx_cpufreq_cpu_exit,
+	.name		= "mpc85xx-cpufreq",
+	.owner		= THIS_MODULE,
+	.flags		= CPUFREQ_CONST_LOOPS,
+};
+
+/*
+ * module init and destoy
+ */
+static struct of_device_id mpc85xx_jog_ids[] __initdata = {
+	{ .compatible = "fsl,mpc8536-guts", },
+	{ .compatible = "fsl,p1022-guts", },
+	{}
+};
+
+static int __init mpc85xx_cpufreq_init(void)
+{
+	struct device_node *np;
+
+	pr_info("Freescale MPC85xx CPU frequency switching driver\n");
+	np = of_find_matching_node(NULL, mpc85xx_jog_ids);
+	if (np == NULL)
+		return -ENODEV;
+
+	guts = of_iomap(np, 0);
+	of_node_put(np);
+	if (guts == NULL)
+		return -ENOMEM;
+
+	return cpufreq_register_driver(&mpc85xx_cpufreq_driver);
+}
+
+static void __exit mpc85xx_cpufreq_exit(void)
+{
+	iounmap(guts);
+
+	cpufreq_unregister_driver(&mpc85xx_cpufreq_driver);
+}
+
+module_init(mpc85xx_cpufreq_init);
+module_exit(mpc85xx_cpufreq_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Dave Liu <daveliu@freescale.com>");
diff --git a/arch/powerpc/platforms/Kconfig b/arch/powerpc/platforms/Kconfig
index b9ba861..64bddda 100644
--- a/arch/powerpc/platforms/Kconfig
+++ b/arch/powerpc/platforms/Kconfig
@@ -199,6 +199,14 @@  config CPU_FREQ_PMAC64
 	  This adds support for frequency switching on Apple iMac G5,
 	  and some of the more recent desktop G5 machines as well.
 
+config MPC85xx_CPUFREQ
+	bool "Support for Freescale MPC85xx CPU freq"
+	depends on PPC_85xx && PPC32
+	select CPU_FREQ_TABLE
+	help
+	  This adds support for frequency switching on Freescale MPC85xx,
+	  currently including P1022 and MPC8536.
+
 config PPC_PASEMI_CPUFREQ
 	bool "Support for PA Semi PWRficient"
 	depends on PPC_PASEMI