* [PATCH 4/7] powerpc/85xx: add support to JOG feature using cpufreq interface @ 2011-11-04 12:36 Zhao Chenhui 2011-11-04 19:42 ` Scott Wood 0 siblings, 1 reply; 7+ messages in thread From: Zhao Chenhui @ 2011-11-04 12:36 UTC (permalink / raw) To: linuxppc-dev; +Cc: Jerry Huang, Zhao Chenhui 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 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 -- 1.6.4.1 ^ permalink raw reply related [flat|nested] 7+ messages in thread
* Re: [PATCH 4/7] powerpc/85xx: add support to JOG feature using cpufreq interface 2011-11-04 12:36 [PATCH 4/7] powerpc/85xx: add support to JOG feature using cpufreq interface Zhao Chenhui @ 2011-11-04 19:42 ` Scott Wood 2011-11-07 10:27 ` Zhao Chenhui 0 siblings, 1 reply; 7+ messages in thread From: Scott Wood @ 2011-11-04 19:42 UTC (permalink / raw) To: Zhao Chenhui; +Cc: Jerry Huang, linuxppc-dev 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 ^ permalink raw reply [flat|nested] 7+ messages in thread
* Re: [PATCH 4/7] powerpc/85xx: add support to JOG feature using cpufreq interface 2011-11-04 19:42 ` Scott Wood @ 2011-11-07 10:27 ` Zhao Chenhui 2011-11-07 18:50 ` Scott Wood 0 siblings, 1 reply; 7+ messages in thread From: Zhao Chenhui @ 2011-11-07 10:27 UTC (permalink / raw) To: Scott Wood; +Cc: Jerry Huang, linuxppc-dev 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 ^ permalink raw reply [flat|nested] 7+ messages in thread
* Re: [PATCH 4/7] powerpc/85xx: add support to JOG feature using cpufreq interface 2011-11-07 10:27 ` Zhao Chenhui @ 2011-11-07 18:50 ` Scott Wood 2011-11-09 11:38 ` Zhao Chenhui 0 siblings, 1 reply; 7+ messages in thread From: Scott Wood @ 2011-11-07 18:50 UTC (permalink / raw) To: Zhao Chenhui; +Cc: Jerry Huang, linuxppc-dev 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 ^ permalink raw reply [flat|nested] 7+ messages in thread
* Re: [PATCH 4/7] powerpc/85xx: add support to JOG feature using cpufreq interface 2011-11-07 18:50 ` Scott Wood @ 2011-11-09 11:38 ` Zhao Chenhui 2011-11-09 16:13 ` Scott Wood 0 siblings, 1 reply; 7+ messages in thread From: Zhao Chenhui @ 2011-11-09 11:38 UTC (permalink / raw) To: Scott Wood; +Cc: Jerry Huang, linuxppc-dev 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 ^ permalink raw reply [flat|nested] 7+ messages in thread
* Re: [PATCH 4/7] powerpc/85xx: add support to JOG feature using cpufreq interface 2011-11-09 11:38 ` Zhao Chenhui @ 2011-11-09 16:13 ` Scott Wood 0 siblings, 0 replies; 7+ messages in thread From: Scott Wood @ 2011-11-09 16:13 UTC (permalink / raw) To: Zhao Chenhui; +Cc: Jerry Huang, linuxppc-dev 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 ^ permalink raw reply [flat|nested] 7+ messages in thread
* [PATCH 1/7] powerpc/85xx: re-enable timebase sync disabled by KEXEC patch @ 2010-12-03 12:34 Li Yang 2010-12-03 12:34 ` [PATCH 2/7] powerpc/85xx: add HOTPLUG_CPU support Li Yang 0 siblings, 1 reply; 7+ messages in thread From: Li Yang @ 2010-12-03 12:34 UTC (permalink / raw) To: linuxppc-dev The timebase sync is not only necessary when using KEXEC. It should also be used by normal boot up and cpu hotplug. Remove the ifdef added by the KEXEC patch. Fix a problem that cpu hotplugging freezes the whole system. Signed-off-by: Jin Qing <b24347@freescale.com> Singed-off-by: Li Yang <leoli@freescale.com> --- arch/powerpc/platforms/85xx/smp.c | 4 +--- 1 files changed, 1 insertions(+), 3 deletions(-) diff --git a/arch/powerpc/platforms/85xx/smp.c b/arch/powerpc/platforms/85xx/smp.c index 5c91a99..1e8aec8 100644 --- a/arch/powerpc/platforms/85xx/smp.c +++ b/arch/powerpc/platforms/85xx/smp.c @@ -2,7 +2,7 @@ * Author: Andy Fleming <afleming@freescale.com> * Kumar Gala <galak@kernel.crashing.org> * - * Copyright 2006-2008 Freescale Semiconductor Inc. + * Copyright 2006-2010 Freescale Semiconductor Inc. * * 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 @@ -115,10 +115,8 @@ smp_85xx_setup_cpu(int cpu_nr) struct smp_ops_t smp_85xx_ops = { .kick_cpu = smp_85xx_kick_cpu, -#ifdef CONFIG_KEXEC .give_timebase = smp_generic_give_timebase, .take_timebase = smp_generic_take_timebase, -#endif }; #ifdef CONFIG_KEXEC -- 1.6.6-rc1.GIT ^ permalink raw reply related [flat|nested] 7+ messages in thread
* [PATCH 2/7] powerpc/85xx: add HOTPLUG_CPU support 2010-12-03 12:34 [PATCH 1/7] powerpc/85xx: re-enable timebase sync disabled by KEXEC patch Li Yang @ 2010-12-03 12:34 ` Li Yang 2010-12-03 12:34 ` [PATCH 3/7] powerpc/85xx: add the deep sleep support Li Yang 0 siblings, 1 reply; 7+ messages in thread From: Li Yang @ 2010-12-03 12:34 UTC (permalink / raw) To: linuxppc-dev Add support to disable and re-enable individual cores at runtime on MPC85xx/QorIQ SMP machines. This makes suspend/resume possible for SMP systems, as the power management code on SMP always disable non-boot cpus on suspend. MPC85xx machines use ePAPR spin-table in boot page for CPU kick-off. This patch brings the bootpage and spin-table from bootloader into kernel because the bootpage in bootloader might have been lost at runtime. Also add support to boot from physical address larger than 32-bit. Signed-off-by: Yutaka Ando <y.ando@freescale.com> Signed-off-by: Li Yang <leoli@freescale.com> --- arch/powerpc/Kconfig | 2 +- arch/powerpc/kernel/Makefile | 2 +- arch/powerpc/kernel/head_fsl_booke.S | 32 +++++ arch/powerpc/kernel/smp.c | 4 +- arch/powerpc/platforms/85xx/Makefile | 4 +- arch/powerpc/platforms/85xx/bootpage.S | 206 +++++++++++++++++++++++++++++ arch/powerpc/platforms/85xx/smp.c | 222 ++++++++++++++++++++++++++------ 7 files changed, 428 insertions(+), 44 deletions(-) create mode 100644 arch/powerpc/platforms/85xx/bootpage.S diff --git a/arch/powerpc/Kconfig b/arch/powerpc/Kconfig index e625e9e..b1982dd 100644 --- a/arch/powerpc/Kconfig +++ b/arch/powerpc/Kconfig @@ -320,7 +320,7 @@ config SWIOTLB config HOTPLUG_CPU bool "Support for enabling/disabling CPUs" - depends on SMP && HOTPLUG && EXPERIMENTAL && (PPC_PSERIES || PPC_PMAC) + depends on SMP && HOTPLUG && EXPERIMENTAL && (PPC_PSERIES || PPC_PMAC || E500) ---help--- Say Y here to be able to disable and re-enable individual CPUs at runtime on SMP machines. diff --git a/arch/powerpc/kernel/Makefile b/arch/powerpc/kernel/Makefile index 36c30f3..bb20496 100644 --- a/arch/powerpc/kernel/Makefile +++ b/arch/powerpc/kernel/Makefile @@ -56,7 +56,7 @@ obj-$(CONFIG_IBMEBUS) += ibmebus.o obj-$(CONFIG_GENERIC_TBSYNC) += smp-tbsync.o obj-$(CONFIG_CRASH_DUMP) += crash_dump.o ifeq ($(CONFIG_PPC32),y) -obj-$(CONFIG_E500) += idle_e500.o +obj-$(CONFIG_E500) += idle_e500.o l2cr_85xx.o endif obj-$(CONFIG_6xx) += idle_6xx.o l2cr_6xx.o cpu_setup_6xx.o obj-$(CONFIG_TAU) += tau_6xx.o diff --git a/arch/powerpc/kernel/head_fsl_booke.S b/arch/powerpc/kernel/head_fsl_booke.S index 529b817..61d9c46 100644 --- a/arch/powerpc/kernel/head_fsl_booke.S +++ b/arch/powerpc/kernel/head_fsl_booke.S @@ -23,6 +23,7 @@ * PowerPC 44x support, Matt Porter <mporter@kernel.crashing.org> * Copyright 2004 Freescale Semiconductor, Inc * PowerPC e500 modifications, Kumar Gala <galak@kernel.crashing.org> + * Copyright 2008, 2010 Freescale Semiconductor, Inc. * * 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 @@ -254,6 +255,37 @@ _ENTRY(__early_start) lwz r11, 0(r12); /* Get Linux PTE */ #endif +_GLOBAL(flush_disable_L1) +/* + * Flush L1 d-cache, invalidate and disable d-cache, + * invalidate and disable i-cache + */ + mflr r10 + bl flush_dcache_L1 /* Flush L1 d-cache */ + mtlr r10 + + mfspr r4, SPRN_L1CSR0 /* Invalidate and disable d-cache */ + li r5, 2 + rlwimi r4, r5, 0, 3 + + msync + isync + mtspr SPRN_L1CSR0, r4 + isync + +1: mfspr r4, SPRN_L1CSR0 /* Wait for the invalidate to finish */ + andi. r4, r4, 2 + bne 1b + + mfspr r4, SPRN_L1CSR1 /* Invalidate and disable i-cache */ + li r5, 2 + rlwimi r4, r5, 0, 3 + + mtspr SPRN_L1CSR1, r4 + isync + + blr + /* * Interrupt vector entry code * diff --git a/arch/powerpc/kernel/smp.c b/arch/powerpc/kernel/smp.c index 68034bb..321cf2e 100644 --- a/arch/powerpc/kernel/smp.c +++ b/arch/powerpc/kernel/smp.c @@ -317,6 +317,8 @@ int generic_cpu_disable(void) set_cpu_online(cpu, false); #ifdef CONFIG_PPC64 vdso_data->processorCount--; +#endif +#if defined(CONFIG_PPC64) || defined(CONFIG_E500) fixup_irqs(cpu_online_mask); #endif return 0; @@ -336,7 +338,7 @@ int generic_cpu_enable(unsigned int cpu) while (!cpu_online(cpu)) cpu_relax(); -#ifdef CONFIG_PPC64 +#if defined(CONFIG_PPC64) || defined(CONFIG_E500) fixup_irqs(cpu_online_mask); /* counter the irq disable in fixup_irqs */ local_irq_enable(); diff --git a/arch/powerpc/platforms/85xx/Makefile b/arch/powerpc/platforms/85xx/Makefile index dd70db7..6bbcf22 100644 --- a/arch/powerpc/platforms/85xx/Makefile +++ b/arch/powerpc/platforms/85xx/Makefile @@ -1,7 +1,9 @@ # # Makefile for the PowerPC 85xx linux kernel. # -obj-$(CONFIG_SMP) += smp.o +obj-$(CONFIG_SMP) += smp.o +obj-$(CONFIG_HOTPLUG_CPU) += bootpage.o +obj-$(CONFIG_SUSPEND) += suspend-asm.o obj-$(CONFIG_MPC8540_ADS) += mpc85xx_ads.o obj-$(CONFIG_MPC8560_ADS) += mpc85xx_ads.o diff --git a/arch/powerpc/platforms/85xx/bootpage.S b/arch/powerpc/platforms/85xx/bootpage.S new file mode 100644 index 0000000..ff0ca10 --- /dev/null +++ b/arch/powerpc/platforms/85xx/bootpage.S @@ -0,0 +1,206 @@ +/* + * Copyright 2008-2010 Freescale Semiconductor, Inc. + * Kumar Gala <kumar.gala@freescale.com> + * This file is taken from u-boot + * + * 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; + */ +#include <linux/init.h> +#include <linux/threads.h> +#include <asm/processor.h> +#include <asm/page.h> +#include <asm/mmu.h> +#include <asm/pgtable.h> +#include <asm/cputable.h> +#include <asm/thread_info.h> +#include <asm/ppc_asm.h> +#include <asm/asm-offsets.h> +#include <asm/cache.h> + +/* To boot secondary cpus, we need a place for them to start up. + * Normally, they start at 0xfffffffc, but that's usually the + * firmware, and we don't want to have to run the firmware again. + * Instead, the primary cpu will set the BPTR to point here to + * this page. We then set up the core, and head to + * start_secondary. Note that this means that the code below + * must never exceed 1023 instructions (the branch at the end + * would then be the 1024th). + */ + .globl __secondary_start_page + .align 12 +__secondary_start_page: + lis r3, 0x8000 /* enable machine check */ +#ifndef CONFIG_PPC_E500MC + ori r3,r3,0x4000 /* enable Timebase */ +#endif +#ifdef CONFIG_PHYS_64BIT + /* for 36-bit addressing */ + ori r3,r3,0x0080 /* enable MAS7 updates */ +#endif + mtspr SPRN_HID0,r3 + +#ifndef CONFIG_PPC_E500MC + li r3,0x3000 /* Addr streaming & broadcast */ + mtspr SPRN_HID1,r3 +#endif + + /* Enable branch prediction */ + li r3,0x201 + mtspr SPRN_BUCSR,r3 + + /* Ensure TB is 0 */ + li r3,0 + mttbl r3 + mttbu r3 + + mfspr r0,SPRN_L1CSR1 + ori r0,r0,0x0003 /* Enable/invalidate the I-Cache */ + mtspr SPRN_L1CSR1,r0 + isync + + + mfspr r0,SPRN_L1CSR0 + ori r0,r0,0x0003 /* Enable/invalidate the D-Cache */ + msync + isync + mtspr SPRN_L1CSR0,r0 + isync + +#define toreset(x) (x - __secondary_start_page + 0xfffff000) + + /* get our PIR to figure out our table entry */ + lis r3,toreset(__spin_table)@h + ori r3,r3,toreset(__spin_table)@l + + /* r10 has the base address for the entry */ + mfspr r0,SPRN_PIR +#ifdef CONFIG_PPC_E500MC + rlwinm r4,r0,27,27,31 +#else + mr r4,r0 +#endif + slwi r8,r4,5 + add r10,r3,r8 + +#define EPAPR_MAGIC (0x45504150) +#define ENTRY_ADDR_UPPER 0 +#define ENTRY_ADDR_LOWER 4 +#define ENTRY_R3_UPPER 8 +#define ENTRY_R3_LOWER 12 +#define ENTRY_RESV 16 +#define ENTRY_PIR 20 +#define ENTRY_R6_UPPER 24 +#define ENTRY_R6_LOWER 28 +#define ENTRY_SIZE 32 + + /* setup the entry */ + li r3,0 + li r8,1 + stw r0,ENTRY_PIR(r10) + stw r3,ENTRY_ADDR_UPPER(r10) + stw r8,ENTRY_ADDR_LOWER(r10) + stw r3,ENTRY_R3_UPPER(r10) + stw r4,ENTRY_R3_LOWER(r10) + stw r3,ENTRY_R6_UPPER(r10) + stw r3,ENTRY_R6_LOWER(r10) + + /* setup mapping for AS = 1, and jump there */ + lis r11,(MAS0_TLBSEL(1)|MAS0_ESEL(1))@h + mtspr SPRN_MAS0,r11 + lis r11,(MAS1_VALID|MAS1_IPROT)@h + ori r11,r11,(MAS1_TS|MAS1_TSIZE(BOOK3E_PAGESZ_4K))@l + mtspr SPRN_MAS1,r11 + lis r11,(0xfffff000|MAS2_I|MAS2_G)@h + ori r11,r11,(0xfffff000|MAS2_I|MAS2_G)@l + mtspr SPRN_MAS2,r11 + lis r11,(0xfffff000|MAS3_SX|MAS3_SW|MAS3_SR)@h + ori r11,r11,(0xfffff000|MAS3_SX|MAS3_SW|MAS3_SR)@l + mtspr SPRN_MAS3,r11 + tlbwe + + bl 1f +1: mflr r11 + addi r11,r11,28 + mfmsr r13 + ori r12,r13,MSR_IS|MSR_DS@l + + mtspr SPRN_SRR0,r11 + mtspr SPRN_SRR1,r12 + rfi + + /* spin waiting for addr */ +2: + lwz r4,ENTRY_ADDR_LOWER(r10) + andi. r11,r4,1 + bne 2b + isync + + /* get the upper bits of the addr */ + lwz r11,ENTRY_ADDR_UPPER(r10) + + /* setup branch addr */ + mtspr SPRN_SRR0,r4 + + /* mark the entry as released */ + li r8,3 + stw r8,ENTRY_ADDR_LOWER(r10) + + /* mask by ~64M to setup our tlb we will jump to */ + rlwinm r12,r4,0,0,5 + + /* setup r3, r4, r5, r6, r7, r8, r9 */ + lwz r3,ENTRY_R3_LOWER(r10) + li r4,0 + li r5,0 + lwz r6,ENTRY_R6_LOWER(r10) + lis r7,(64*1024*1024)@h + li r8,0 + li r9,0 + + /* load up the pir */ + lwz r0,ENTRY_PIR(r10) + mtspr SPRN_PIR,r0 + mfspr r0,SPRN_PIR + stw r0,ENTRY_PIR(r10) + + mtspr SPRN_IVPR,r12 +/* + * Coming here, we know the cpu has one TLB mapping in TLB1[0] + * which maps 0xfffff000-0xffffffff one-to-one. We set up a + * second mapping that maps addr 1:1 for 64M, and then we jump to + * addr + */ + lis r10,(MAS0_TLBSEL(1)|MAS0_ESEL(0))@h + mtspr SPRN_MAS0,r10 + lis r10,(MAS1_VALID|MAS1_IPROT)@h + ori r10,r10,(MAS1_TSIZE(BOOK3E_PAGESZ_64M))@l + mtspr SPRN_MAS1,r10 + /* WIMGE = 0b00000 for now */ + mtspr SPRN_MAS2,r12 + ori r12,r12,(MAS3_SX|MAS3_SW|MAS3_SR) + mtspr SPRN_MAS3,r12 +#ifdef CONFIG_PHYS_64BIT + mtspr SPRN_MAS7,r11 +#endif + tlbwe + +/* Now we have another mapping for this page, so we jump to that + * mapping + */ + mtspr SPRN_SRR1,r13 + rfi + + .align L1_CACHE_SHIFT + .globl __spin_table +__spin_table: + .space NR_CPUS*ENTRY_SIZE + + /* Fill in the empty space. The actual reset vector is + * the last word of the page */ +__secondary_start_code_end: + .space 4092 - (__secondary_start_code_end - __secondary_start_page) + +__secondary_reset_vector: + b __secondary_start_page diff --git a/arch/powerpc/platforms/85xx/smp.c b/arch/powerpc/platforms/85xx/smp.c index 1e8aec8..2ef3e8e 100644 --- a/arch/powerpc/platforms/85xx/smp.c +++ b/arch/powerpc/platforms/85xx/smp.c @@ -17,6 +17,7 @@ #include <linux/of.h> #include <linux/kexec.h> #include <linux/highmem.h> +#include <linux/cpu.h> #include <asm/machdep.h> #include <asm/pgtable.h> @@ -28,26 +29,116 @@ #include <sysdev/fsl_soc.h> #include <sysdev/mpic.h> +#define MPC85xx_BPTR_OFF 0x00020 +#define MPC85xx_BPTR_EN 0x80000000 +#define MPC85xx_ECM_EEBPCR_OFF 0x01010 +#define MPC85xx_PIC_PIR_OFF 0x41090 + +extern void mpc85xx_cpu_down(void) __attribute__((noreturn)); extern void __early_start(void); +extern void __secondary_start_page(void); +extern volatile unsigned long __spin_table; + +struct epapr_entry { + u32 addr_h; + u32 addr_l; + u32 r3_h; + u32 r3_l; + u32 reserved; + u32 pir; + u32 r6_h; + u32 r6_l; +}; -#define BOOT_ENTRY_ADDR_UPPER 0 -#define BOOT_ENTRY_ADDR_LOWER 1 -#define BOOT_ENTRY_R3_UPPER 2 -#define BOOT_ENTRY_R3_LOWER 3 -#define BOOT_ENTRY_RESV 4 -#define BOOT_ENTRY_PIR 5 -#define BOOT_ENTRY_R6_UPPER 6 -#define BOOT_ENTRY_R6_LOWER 7 -#define NUM_BOOT_ENTRY 8 -#define SIZE_BOOT_ENTRY (NUM_BOOT_ENTRY * sizeof(u32)) +/* access per cpu vars from generic smp.c */ +DECLARE_PER_CPU(int, cpu_state); -static void __init +#ifdef CONFIG_HOTPLUG_CPU +static void __cpuinit +smp_85xx_mach_cpu_die(void) +{ + __get_cpu_var(cpu_state) = CPU_DEAD; + smp_wmb(); + + local_irq_disable(); + idle_task_exit(); + mtspr(SPRN_TSR, TSR_ENW | TSR_WIS | TSR_DIS | TSR_FIS); + mtspr(SPRN_TCR, 0); + mpc85xx_cpu_down(); +} + +static void __cpuinit +smp_85xx_reset_core(int nr) +{ + __iomem u32 *ecm_vaddr; + __iomem u32 *pic_vaddr; + u32 pcr, pir, cpu; + + cpu = (1 << 24) << nr; + ecm_vaddr = ioremap(get_immrbase() + MPC85xx_ECM_EEBPCR_OFF, 4); + pcr = in_be32(ecm_vaddr); + if (pcr & cpu) { + pic_vaddr = ioremap(get_immrbase() + MPC85xx_PIC_PIR_OFF, 4); + pir = in_be32(pic_vaddr); + /* reset assert */ + pir |= (1 << nr); + out_be32(pic_vaddr, pir); + pir = in_be32(pic_vaddr); + pir &= ~(1 << nr); + /* reset negate */ + out_be32(pic_vaddr, pir); + (void)in_be32(pic_vaddr); + iounmap(pic_vaddr); + } else { + out_be32(ecm_vaddr, pcr | cpu); + (void)in_be32(ecm_vaddr); + } + iounmap(ecm_vaddr); +} + +static int __cpuinit +smp_85xx_map_bootpg(unsigned long pa) +{ + __iomem u32 *bootpg_ptr; + u32 bptr; + + /* Get the BPTR */ + bootpg_ptr = ioremap(get_immrbase() + MPC85xx_BPTR_OFF, 4); + + /* Set the BPTR to the secondary boot page */ + (void)in_be32(bootpg_ptr); + + bptr = (MPC85xx_BPTR_EN | (pa >> 12)); + out_be32(bootpg_ptr, bptr); + (void)in_be32(bootpg_ptr); + iounmap(bootpg_ptr); + return 0; +} + +static int __cpuinit +smp_85xx_unmap_bootpg(void) +{ + __iomem u32 *bootpg_ptr; + + /* Get the BPTR */ + bootpg_ptr = ioremap(get_immrbase() + MPC85xx_BPTR_OFF, 4); + + /* Restore the BPTR */ + if (in_be32(bootpg_ptr) & MPC85xx_BPTR_EN) { + out_be32(bootpg_ptr, 0); + (void)in_be32(bootpg_ptr); + } + iounmap(bootpg_ptr); + return 0; +} +#endif + +static void __cpuinit smp_85xx_kick_cpu(int nr) { unsigned long flags; - const u64 *cpu_rel_addr; - __iomem u32 *bptr_vaddr; - struct device_node *np; + phys_addr_t cpu_rel_addr; + __iomem struct epapr_entry *epapr; int n = 0; int ioremappable; @@ -55,41 +146,83 @@ smp_85xx_kick_cpu(int nr) pr_debug("smp_85xx_kick_cpu: kick CPU #%d\n", nr); - np = of_get_cpu_node(nr, NULL); - cpu_rel_addr = of_get_property(np, "cpu-release-addr", NULL); + if (system_state < SYSTEM_RUNNING) { + /* booting, using __spin_table from u-boot */ + struct device_node *np; + const u64 *prop; - if (cpu_rel_addr == NULL) { - printk(KERN_ERR "No cpu-release-addr for cpu %d\n", nr); - return; - } + np = of_get_cpu_node(nr, NULL); + if (np == NULL) + return; - /* - * A secondary core could be in a spinloop in the bootpage - * (0xfffff000), somewhere in highmem, or somewhere in lowmem. - * The bootpage and highmem can be accessed via ioremap(), but - * we need to directly access the spinloop if its in lowmem. - */ - ioremappable = *cpu_rel_addr > virt_to_phys(high_memory); + prop = of_get_property(np, "cpu-release-addr", NULL); + if (prop == NULL) { + of_node_put(np); + printk(KERN_ERR "No cpu-release-addr for cpu %d\n", nr); + return; + } + cpu_rel_addr = (phys_addr_t)*prop; + of_node_put(np); + + /* + * A secondary core could be in a spinloop in the bootpage + * (0xfffff000), somewhere in highmem, or somewhere in lowmem. + * The bootpage and highmem can be accessed via ioremap(), but + * we need to directly access the spinloop if its in lowmem. + */ + ioremappable = cpu_rel_addr > virt_to_phys(high_memory); + + if (ioremappable) + epapr = ioremap(cpu_rel_addr, + sizeof(struct epapr_entry)); + else + epapr = phys_to_virt(cpu_rel_addr); + + local_irq_save(flags); + } else { +#ifdef CONFIG_HOTPLUG_CPU + /* spin table in kernel, no need to remap */ + ioremappable = 0; + epapr = (void *)&__spin_table + nr * sizeof(struct epapr_entry); - /* Map the spin table */ - if (ioremappable) - bptr_vaddr = ioremap(*cpu_rel_addr, SIZE_BOOT_ENTRY); - else - bptr_vaddr = phys_to_virt(*cpu_rel_addr); + /* prevent bootpage from being accessed by others */ + local_irq_save(flags); + + smp_85xx_map_bootpg(__pa(__secondary_start_page)); - local_irq_save(flags); + smp_85xx_reset_core(nr); - out_be32(bptr_vaddr + BOOT_ENTRY_PIR, nr); + /* wait until core(nr) is ready... */ + while ((in_be32(&epapr->addr_l) != 1) && (++n < 1000)) + udelay(100); + + if (n == 1000) { + pr_err("timeout waiting for core%d to reset\n", + nr); + goto out; + } +#else + pr_err("runtime kick cpu not supported\n"); + return; +#endif + } + + out_be32(&epapr->pir, nr); #ifdef CONFIG_PPC32 - out_be32(bptr_vaddr + BOOT_ENTRY_ADDR_LOWER, __pa(__early_start)); + /* clear the acknowledge status */ + __secondary_hold_acknowledge = -1; + out_be32(&epapr->addr_l, __pa(__early_start)); if (!ioremappable) - flush_dcache_range((ulong)bptr_vaddr, - (ulong)(bptr_vaddr + SIZE_BOOT_ENTRY)); + flush_dcache_range((ulong)epapr, + (ulong)epapr + sizeof(struct epapr_entry)); /* Wait a bit for the CPU to ack. */ + n = 0; while ((__secondary_hold_acknowledge != nr) && (++n < 1000)) mdelay(1); + if (n == 1000) + pr_err("timeout waiting for core%d to ack\n", nr); #else out_be64((u64 *)(bptr_vaddr + BOOT_ENTRY_ADDR_UPPER), __pa((u64)*((unsigned long long *) generic_secondary_smp_init))); @@ -97,12 +230,15 @@ smp_85xx_kick_cpu(int nr) smp_generic_kick_cpu(nr); #endif +out: +#ifdef CONFIG_HOTPLUG_CPU + if (system_state >= SYSTEM_RUNNING) + smp_85xx_unmap_bootpg(); +#endif local_irq_restore(flags); if (ioremappable) - iounmap(bptr_vaddr); - - pr_debug("waited %d msecs for CPU #%d.\n", n, nr); + iounmap(epapr); } static void __init @@ -232,6 +368,12 @@ void __init mpc85xx_smp_init(void) BUG_ON(!smp_85xx_ops.message_pass); +#ifdef CONFIG_HOTPLUG_CPU + smp_85xx_ops.cpu_disable = generic_cpu_disable; + smp_85xx_ops.cpu_die = generic_cpu_die; + ppc_md.cpu_die = smp_85xx_mach_cpu_die; +#endif + smp_ops = &smp_85xx_ops; #ifdef CONFIG_KEXEC -- 1.6.6-rc1.GIT ^ permalink raw reply related [flat|nested] 7+ messages in thread
* [PATCH 3/7] powerpc/85xx: add the deep sleep support 2010-12-03 12:34 ` [PATCH 2/7] powerpc/85xx: add HOTPLUG_CPU support Li Yang @ 2010-12-03 12:34 ` Li Yang 2010-12-03 12:34 ` [PATCH 4/7] powerpc/85xx: add support to JOG feature using cpufreq interface Li Yang 0 siblings, 1 reply; 7+ messages in thread From: Li Yang @ 2010-12-03 12:34 UTC (permalink / raw) To: linuxppc-dev Some Freescale chips like MPC8536 and P1022 has deep sleep PM mode in addtion to the sleep PM mode. In sleep power management mode, the clock of e500 core is turned off, the clocks of most IP blocks are shut off, only the modules clocks which allows to wake up the processor are still running. While in deep sleep PM mode, additionally, the power supply is removed for e500 core and most devices. Only the blocks needed to detect wakeup and sequence the chip out of deep sleep are ON. Also add APIs for setting wakeup source and lossless Ethernet in low power modes. The deep sleep mode is equal to the Suspend-to-RAM state of Linux Power Management. echo mem > /sys/power/state to enter deep sleep mode. Signed-off-by: Dave Liu <daveliu@freescale.com> Signed-off-by: Li Yang <leoli@freescale.com> Signed-off-by: Jin Qing <b24347@freescale.com> Cc: Scott Wood <scottwood@freescale.com> --- arch/powerpc/kernel/l2cr_85xx.S | 53 +++ arch/powerpc/platforms/85xx/suspend-asm.S | 625 +++++++++++++++++++++++++++++ arch/powerpc/sysdev/fsl_pmc.c | 153 ++++++- arch/powerpc/sysdev/fsl_soc.h | 11 + 4 files changed, 823 insertions(+), 19 deletions(-) create mode 100644 arch/powerpc/kernel/l2cr_85xx.S create mode 100644 arch/powerpc/platforms/85xx/suspend-asm.S diff --git a/arch/powerpc/kernel/l2cr_85xx.S b/arch/powerpc/kernel/l2cr_85xx.S new file mode 100644 index 0000000..5283d31 --- /dev/null +++ b/arch/powerpc/kernel/l2cr_85xx.S @@ -0,0 +1,53 @@ +/* + * Copyright (C) 2009-2010 Freescale Semiconductor, Inc. All rights reserved. + * Scott Wood <scottwood@freescale.com> + * Dave Liu <daveliu@freescale.com> + * implement the L2 cache operations of e500 based L2 controller + * + * 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 of the License, or (at your option) any later version. + */ + +#include <asm/reg.h> +#include <asm/cputable.h> +#include <asm/ppc_asm.h> +#include <asm/asm-offsets.h> + + .section .text + + /* r3 = virtual address of L2 controller, WIMG = 01xx */ +_GLOBAL(flush_disable_L2) + /* It's a write-through cache, so only invalidation is needed. */ + mbar + isync + lwz r4, 0(r3) + li r5, 1 + rlwimi r4, r5, 30, 0xc0000000 + stw r4, 0(r3) + + /* Wait for the invalidate to finish */ +1: lwz r4, 0(r3) + andis. r4, r4, 0x4000 + bne 1b + mbar + + blr + + /* r3 = virtual address of L2 controller, WIMG = 01xx */ +_GLOBAL(invalidate_enable_L2) + mbar + isync + lwz r4, 0(r3) + li r5, 3 + rlwimi r4, r5, 30, 0xc0000000 + stw r4, 0(r3) + + /* Wait for the invalidate to finish */ +1: lwz r4, 0(r3) + andis. r4, r4, 0x4000 + bne 1b + mbar + + blr diff --git a/arch/powerpc/platforms/85xx/suspend-asm.S b/arch/powerpc/platforms/85xx/suspend-asm.S new file mode 100644 index 0000000..99de481 --- /dev/null +++ b/arch/powerpc/platforms/85xx/suspend-asm.S @@ -0,0 +1,625 @@ +/* + * Enter and leave deep sleep/sleep state on MPC85xx + * + * Author: Scott Wood <scottwood@freescale.com> + * + * Copyright (C) 2006-2010 Freescale Semiconductor, Inc. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published + * by the Free Software Foundation. + */ + +#include <asm/page.h> +#include <asm/ppc_asm.h> +#include <asm/reg.h> +#include <asm/asm-offsets.h> + +#define SS_TB 0x00 +#define SS_HID 0x08 /* 2 HIDs */ +#define SS_IAC 0x10 /* 2 IACs */ +#define SS_DAC 0x18 /* 2 DACs */ +#define SS_DBCR 0x20 /* 3 DBCRs */ +#define SS_PID 0x2c /* 3 PIDs */ +#define SS_SPRG 0x38 /* 8 SPRGs */ +#define SS_IVOR 0x58 /* 20 interrupt vectors */ +#define SS_TCR 0xa8 +#define SS_BUCSR 0xac +#define SS_L1CSR 0xb0 /* 2 L1CSRs */ +#define SS_MSR 0xb8 +#define SS_USPRG 0xbc +#define SS_GPREG 0xc0 /* r12-r31 */ +#define SS_LR 0x110 +#define SS_CR 0x114 +#define SS_SP 0x118 +#define SS_CURRENT 0x11c +#define SS_IVPR 0x120 +#define SS_BPTR 0x124 + +#define STATE_SAVE_SIZE 0x128 + + .section .data + .align 5 +mpc85xx_sleep_save_area: + .space STATE_SAVE_SIZE +ccsrbase: + .long 0 +powmgtreq: + .long 0 + + .section .text + .align 12 + + /* + * r3 = physical address of CCSR + * r4 = JOG or deep sleep request + * JOG-0x00200000, deep sleep-0x00100000 + */ +_GLOBAL(mpc85xx_enter_deep_sleep) + lis r5, ccsrbase@ha + stw r3, ccsrbase@l(r5) + + lis r5, powmgtreq@ha + stw r4, powmgtreq@l(r5) + + lis r10, mpc85xx_sleep_save_area@h + ori r10, r10, mpc85xx_sleep_save_area@l + + mfspr r5, SPRN_HID0 + mfspr r6, SPRN_HID1 + + stw r5, SS_HID+0(r10) + stw r6, SS_HID+4(r10) + + mfspr r4, SPRN_IAC1 + mfspr r5, SPRN_IAC2 + mfspr r6, SPRN_DAC1 + mfspr r7, SPRN_DAC2 + + stw r4, SS_IAC+0(r10) + stw r5, SS_IAC+4(r10) + stw r6, SS_DAC+0(r10) + stw r7, SS_DAC+4(r10) + + mfspr r4, SPRN_DBCR0 + mfspr r5, SPRN_DBCR1 + mfspr r6, SPRN_DBCR2 + + stw r4, SS_DBCR+0(r10) + stw r5, SS_DBCR+4(r10) + stw r6, SS_DBCR+8(r10) + + mfspr r4, SPRN_PID0 + mfspr r5, SPRN_PID1 + mfspr r6, SPRN_PID2 + + stw r4, SS_PID+0(r10) + stw r5, SS_PID+4(r10) + stw r6, SS_PID+8(r10) + + mfspr r4, SPRN_SPRG0 + mfspr r5, SPRN_SPRG1 + mfspr r6, SPRN_SPRG2 + mfspr r7, SPRN_SPRG3 + + stw r4, SS_SPRG+0x00(r10) + stw r5, SS_SPRG+0x04(r10) + stw r6, SS_SPRG+0x08(r10) + stw r7, SS_SPRG+0x0c(r10) + + mfspr r4, SPRN_SPRG4 + mfspr r5, SPRN_SPRG5 + mfspr r6, SPRN_SPRG6 + mfspr r7, SPRN_SPRG7 + + stw r4, SS_SPRG+0x10(r10) + stw r5, SS_SPRG+0x14(r10) + stw r6, SS_SPRG+0x18(r10) + stw r7, SS_SPRG+0x1c(r10) + + mfspr r4, SPRN_IVPR + stw r4, SS_IVPR(r10) + + mfspr r4, SPRN_IVOR0 + mfspr r5, SPRN_IVOR1 + mfspr r6, SPRN_IVOR2 + mfspr r7, SPRN_IVOR3 + + stw r4, SS_IVOR+0x00(r10) + stw r5, SS_IVOR+0x04(r10) + stw r6, SS_IVOR+0x08(r10) + stw r7, SS_IVOR+0x0c(r10) + + mfspr r4, SPRN_IVOR4 + mfspr r5, SPRN_IVOR5 + mfspr r6, SPRN_IVOR6 + mfspr r7, SPRN_IVOR7 + + stw r4, SS_IVOR+0x10(r10) + stw r5, SS_IVOR+0x14(r10) + stw r6, SS_IVOR+0x18(r10) + stw r7, SS_IVOR+0x1c(r10) + + mfspr r4, SPRN_IVOR8 + mfspr r5, SPRN_IVOR9 + mfspr r6, SPRN_IVOR10 + mfspr r7, SPRN_IVOR11 + + stw r4, SS_IVOR+0x20(r10) + stw r5, SS_IVOR+0x24(r10) + stw r6, SS_IVOR+0x28(r10) + stw r7, SS_IVOR+0x2c(r10) + + mfspr r4, SPRN_IVOR12 + mfspr r5, SPRN_IVOR13 + mfspr r6, SPRN_IVOR14 + mfspr r7, SPRN_IVOR15 + + stw r4, SS_IVOR+0x30(r10) + stw r5, SS_IVOR+0x34(r10) + stw r6, SS_IVOR+0x38(r10) + stw r7, SS_IVOR+0x3c(r10) + + mfspr r4, SPRN_IVOR32 + mfspr r5, SPRN_IVOR33 + mfspr r6, SPRN_IVOR34 + mfspr r7, SPRN_IVOR35 + + stw r4, SS_IVOR+0x40(r10) + stw r5, SS_IVOR+0x44(r10) + stw r6, SS_IVOR+0x48(r10) + stw r7, SS_IVOR+0x4c(r10) + + mfspr r4, SPRN_TCR + mfspr r5, SPRN_BUCSR + mfspr r6, SPRN_L1CSR0 + mfspr r7, SPRN_L1CSR1 + mfspr r8, SPRN_USPRG0 + + stw r4, SS_TCR(r10) + stw r5, SS_BUCSR(r10) + stw r6, SS_L1CSR+0(r10) + stw r7, SS_L1CSR+4(r10) + stw r8, SS_USPRG+0(r10) + + stmw r12, SS_GPREG(r10) + + mfmsr r4 + mflr r5 + mfcr r6 + + stw r4, SS_MSR(r10) + stw r5, SS_LR(r10) + stw r6, SS_CR(r10) + stw r1, SS_SP(r10) + stw r2, SS_CURRENT(r10) + +1: mftbu r4 + mftb r5 + mftbu r6 + cmpw r4, r6 + bne 1b + + stw r4, SS_TB+0(r10) + stw r5, SS_TB+4(r10) + + lis r4, ccsrbase@ha + lwz r3, ccsrbase@l(r4) + + /* Disable machine checks and critical exceptions */ + mfmsr r4 + rlwinm r4, r4, 0, ~MSR_CE + rlwinm r4, r4, 0, ~MSR_ME + mtmsr r4 + isync + + /* Use TLB1[15] to map the CCSR at 0xf0000000 */ + lis r4, 0x100f + mtspr SPRN_MAS0, r4 + lis r4, 0xc000 + ori r4, r4, 0x0500 + mtspr SPRN_MAS1, r4 + lis r4, 0xf000 + ori r4, r4, 0x000a + mtspr SPRN_MAS2, r4 + rlwinm r4, r3, 0, 0xfffff000 + ori r4, r4, 0x0005 + mtspr SPRN_MAS3, r4 + li r4, 0 + mtspr SPRN_MAS7, r4 + isync + tlbwe + isync + + lis r3, 0xf000 + lwz r4, 0x20(r3) + stw r4, SS_BPTR(r10) + + lis r3, 0xf002 /* L2 cache controller at CCSR+0x20000 */ + bl flush_disable_L2 + bl flush_disable_L1 + + /* Enable I-cache, so as not to upset the bus + * with our loop. + */ + + mfspr r4, SPRN_L1CSR1 + ori r4, r4, 1 + mtspr SPRN_L1CSR1, r4 + isync + + /* Set boot page translation */ + lis r3, 0xf000 + lis r4, (mpc85xx_deep_resume - PAGE_OFFSET)@h + ori r4, r4, (mpc85xx_deep_resume - PAGE_OFFSET)@l + rlwinm r4, r4, 20, 0x000fffff + oris r4, r4, 0x8000 + stw r4, 0x20(r3) + lwz r4, 0x20(r3) /* read-back to flush write */ + twi 0, r4, 0 + isync + + /* Disable the decrementer */ + mfspr r4, SPRN_TCR + rlwinm r4, r4, 0, ~TCR_DIE + mtspr SPRN_TCR, r4 + + mfspr r4, SPRN_TSR + oris r4, r4, TSR_DIS@h + mtspr SPRN_TSR, r4 + + /* set PMRCCR[VRCNT] to wait power stable for 40ms */ + lis r3, 0xf00e + lwz r4, 0x84(r3) + clrlwi r4, r4, 16 + oris r4, r4, 0x12a3 + stw r4, 0x84(r3) + lwz r4, 0x84(r3) + + /* set deep sleep bit in POWMGTSCR */ + lis r3, powmgtreq@ha + lwz r8, powmgtreq@l(r3) + + lis r3, 0xf00e + mr r4, r8 + stw r4, 0x80(r3) + lwz r4, 0x80(r3) /* read-back to flush write */ + twi 0, r4, 0 + isync + + mftb r5 +1: /* spin until either we enter deep sleep, or the sleep process is + * aborted due to a pending wakeup event. Wait some time between + * accesses, so we don't flood the bus and prevent the pmc from + * detecting an idle system. + */ + + mftb r4 + subf r7, r5, r4 + cmpwi r7, 1000 + blt 1b + mr r5, r4 + + lwz r6, 0x80(r3) + andis. r6, r6, 0x0010 + bne 1b + b 2f + +2: mfspr r4, SPRN_PIR + andi. r4, r4, 1 +99: bne 99b + /* Establish a temporary 64MB 0->0 mapping in TLB1[1]. */ + lis r4, 0x1001 + mtspr SPRN_MAS0, r4 + lis r4, 0xc000 + ori r4, r4, 0x0800 + mtspr SPRN_MAS1, r4 + li r4, 0 + mtspr SPRN_MAS2, r4 + li r4, 0x0015 + mtspr SPRN_MAS3, r4 + li r4, 0 + mtspr SPRN_MAS7, r4 + isync + tlbwe + isync + + lis r3, (3f - PAGE_OFFSET)@h + ori r3, r3, (3f - PAGE_OFFSET)@l + mtctr r3 + bctr + + /* Locate the resume vector in the last word of the current page. */ + . = mpc85xx_enter_deep_sleep + 0xffc +mpc85xx_deep_resume: + b 2b + +3: + /* Restore the contents of TLB1[0]. It is assumed that it covers + * the currently executing code and the sleep save area, and that + * it does not alias our temporary mapping (which is at virtual zero). + */ + lis r3, (TLBCAM - PAGE_OFFSET)@h + ori r3, r3, (TLBCAM - PAGE_OFFSET)@l + + lwz r4, 0(r3) + lwz r5, 4(r3) + lwz r6, 8(r3) + lwz r7, 12(r3) + lwz r8, 16(r3) + + mtspr SPRN_MAS0, r4 + mtspr SPRN_MAS1, r5 + mtspr SPRN_MAS2, r6 + mtspr SPRN_MAS3, r7 + mtspr SPRN_MAS7, r8 + + isync + tlbwe + isync + + /* Access the ccsrbase address with TLB1[0] */ + lis r4, ccsrbase@ha + lwz r3, ccsrbase@l(r4) + + /* Use TLB1[15] to map the CCSR at 0xf0000000 */ + lis r4, 0x100f + mtspr SPRN_MAS0, r4 + lis r4, 0xc000 + ori r4, r4, 0x0500 + mtspr SPRN_MAS1, r4 + lis r4, 0xf000 + ori r4, r4, 0x000a + mtspr SPRN_MAS2, r4 + rlwinm r4, r3, 0, 0xfffff000 + ori r4, r4, 0x0005 + mtspr SPRN_MAS3, r4 + li r4, 0 + mtspr SPRN_MAS7, r4 + isync + tlbwe + isync + + lis r3, 0xf002 /* L2 cache controller at CCSR+0x20000 */ + bl invalidate_enable_L2 + + /* Access the MEM(r10) with TLB1[0] */ + lis r10, mpc85xx_sleep_save_area@h + ori r10, r10, mpc85xx_sleep_save_area@l + + lis r3, 0xf000 + lwz r4, SS_BPTR(r10) + stw r4, 0x20(r3) /* restore BPTR */ + + /* Program shift running space to PAGE_OFFSET */ + mfmsr r3 + lis r4, 1f@h + ori r4, r4, 1f@l + + mtsrr1 r3 + mtsrr0 r4 + rfi + +1: /* Restore the rest of TLB1, in ascending order so that + * the TLB1[1] gets invalidated first. + * + * XXX: It's better to invalidate the temporary mapping + * TLB1[15] for CCSR before restore any TLB1 entry include 0. + */ + lis r4, 0x100f + mtspr SPRN_MAS0, r4 + lis r4, 0 + mtspr SPRN_MAS1, r4 + isync + tlbwe + isync + + lis r3, (TLBCAM + 5*4 - 4)@h + ori r3, r3, (TLBCAM + 5*4 - 4)@l + li r4, 15 + mtctr r4 + +2: + lwz r5, 4(r3) + lwz r6, 8(r3) + lwz r7, 12(r3) + lwz r8, 16(r3) + lwzu r9, 20(r3) + + mtspr SPRN_MAS0, r5 + mtspr SPRN_MAS1, r6 + mtspr SPRN_MAS2, r7 + mtspr SPRN_MAS3, r8 + mtspr SPRN_MAS7, r9 + + isync + tlbwe + isync + bdnz 2b + + lis r10, mpc85xx_sleep_save_area@h + ori r10, r10, mpc85xx_sleep_save_area@l + + lwz r5, SS_HID+0(r10) + lwz r6, SS_HID+4(r10) + + isync + mtspr SPRN_HID0, r5 + isync + + msync + mtspr SPRN_HID1, r6 + isync + + lwz r4, SS_IAC+0(r10) + lwz r5, SS_IAC+4(r10) + lwz r6, SS_DAC+0(r10) + lwz r7, SS_DAC+4(r10) + + mtspr SPRN_IAC1, r4 + mtspr SPRN_IAC2, r5 + mtspr SPRN_DAC1, r6 + mtspr SPRN_DAC2, r7 + + lwz r4, SS_DBCR+0(r10) + lwz r5, SS_DBCR+4(r10) + lwz r6, SS_DBCR+8(r10) + + mtspr SPRN_DBCR0, r4 + mtspr SPRN_DBCR1, r5 + mtspr SPRN_DBCR2, r6 + + lwz r4, SS_PID+0(r10) + lwz r5, SS_PID+4(r10) + lwz r6, SS_PID+8(r10) + + mtspr SPRN_PID0, r4 + mtspr SPRN_PID1, r5 + mtspr SPRN_PID2, r6 + + lwz r4, SS_SPRG+0x00(r10) + lwz r5, SS_SPRG+0x04(r10) + lwz r6, SS_SPRG+0x08(r10) + lwz r7, SS_SPRG+0x0c(r10) + + mtspr SPRN_SPRG0, r4 + mtspr SPRN_SPRG1, r5 + mtspr SPRN_SPRG2, r6 + mtspr SPRN_SPRG3, r7 + + lwz r4, SS_SPRG+0x10(r10) + lwz r5, SS_SPRG+0x14(r10) + lwz r6, SS_SPRG+0x18(r10) + lwz r7, SS_SPRG+0x1c(r10) + + mtspr SPRN_SPRG4, r4 + mtspr SPRN_SPRG5, r5 + mtspr SPRN_SPRG6, r6 + mtspr SPRN_SPRG7, r7 + + lwz r4, SS_IVPR(r10) + mtspr SPRN_IVPR, r4 + + lwz r4, SS_IVOR+0x00(r10) + lwz r5, SS_IVOR+0x04(r10) + lwz r6, SS_IVOR+0x08(r10) + lwz r7, SS_IVOR+0x0c(r10) + + mtspr SPRN_IVOR0, r4 + mtspr SPRN_IVOR1, r5 + mtspr SPRN_IVOR2, r6 + mtspr SPRN_IVOR3, r7 + + lwz r4, SS_IVOR+0x10(r10) + lwz r5, SS_IVOR+0x14(r10) + lwz r6, SS_IVOR+0x18(r10) + lwz r7, SS_IVOR+0x1c(r10) + + mtspr SPRN_IVOR4, r4 + mtspr SPRN_IVOR5, r5 + mtspr SPRN_IVOR6, r6 + mtspr SPRN_IVOR7, r7 + + lwz r4, SS_IVOR+0x20(r10) + lwz r5, SS_IVOR+0x24(r10) + lwz r6, SS_IVOR+0x28(r10) + lwz r7, SS_IVOR+0x2c(r10) + + mtspr SPRN_IVOR8, r4 + mtspr SPRN_IVOR9, r5 + mtspr SPRN_IVOR10, r6 + mtspr SPRN_IVOR11, r7 + + lwz r4, SS_IVOR+0x30(r10) + lwz r5, SS_IVOR+0x34(r10) + lwz r6, SS_IVOR+0x38(r10) + lwz r7, SS_IVOR+0x3c(r10) + + mtspr SPRN_IVOR12, r4 + mtspr SPRN_IVOR13, r5 + mtspr SPRN_IVOR14, r6 + mtspr SPRN_IVOR15, r7 + + lwz r4, SS_IVOR+0x40(r10) + lwz r5, SS_IVOR+0x44(r10) + lwz r6, SS_IVOR+0x48(r10) + lwz r7, SS_IVOR+0x4c(r10) + + mtspr SPRN_IVOR32, r4 + mtspr SPRN_IVOR33, r5 + mtspr SPRN_IVOR34, r6 + mtspr SPRN_IVOR35, r7 + + lwz r4, SS_TCR(r10) + lwz r5, SS_BUCSR(r10) + lwz r6, SS_L1CSR+0(r10) + lwz r7, SS_L1CSR+4(r10) + lwz r8, SS_USPRG+0(r10) + + mtspr SPRN_TCR, r4 + mtspr SPRN_BUCSR, r5 + + msync + isync + mtspr SPRN_L1CSR0, r6 + isync + + mtspr SPRN_L1CSR1, r7 + isync + + mtspr SPRN_USPRG0, r8 + + lmw r12, SS_GPREG(r10) + + lwz r1, SS_SP(r10) + lwz r2, SS_CURRENT(r10) + lwz r4, SS_MSR(r10) + lwz r5, SS_LR(r10) + lwz r6, SS_CR(r10) + + msync + mtmsr r4 + isync + + mtlr r5 + mtcr r6 + + li r4, 0 + mtspr SPRN_TBWL, r4 + + lwz r4, SS_TB+0(r10) + lwz r5, SS_TB+4(r10) + + mtspr SPRN_TBWU, r4 + mtspr SPRN_TBWL, r5 + + lis r3, 1 + mtdec r3 + + blr + + +_GLOBAL(mpc85xx_cpu_down) + stwu r1,-16(r1) + mflr r0 + stw r0,20(r1) + bl flush_dcache_L1 + lwz r0,20(r1) + addi r1,r1,16 + mtlr r0 + lis r3,HID0_NAP@h + + /* Go to NAP or DOZE now */ + mfspr r4,SPRN_HID0 + rlwinm r4,r4,0,~(HID0_DOZE|HID0_NAP|HID0_SLEEP) + or r4,r4,r3 + isync + mtspr SPRN_HID0,r4 + isync + + mfmsr r7 + oris r7,r7,MSR_WE@h + msync + mtmsr r7 + isync +99: b 99b diff --git a/arch/powerpc/sysdev/fsl_pmc.c b/arch/powerpc/sysdev/fsl_pmc.c index 44de855..00014b5 100644 --- a/arch/powerpc/sysdev/fsl_pmc.c +++ b/arch/powerpc/sysdev/fsl_pmc.c @@ -2,6 +2,7 @@ * Suspend/resume support * * Copyright 2009 MontaVista Software, Inc. + * Copyright 2007-2010 Freescale Semiconductor Inc. * * Author: Anton Vorontsov <avorontsov@ru.mvista.com> * @@ -18,39 +19,137 @@ #include <linux/delay.h> #include <linux/device.h> #include <linux/of_platform.h> +#include <linux/pm.h> +#include <linux/interrupt.h> + +#include <sysdev/fsl_soc.h> struct pmc_regs { __be32 devdisr; - __be32 devdisr2; + __be32 :32; __be32 :32; __be32 :32; __be32 pmcsr; -#define PMCSR_SLP (1 << 17) + __be32 :32; + __be32 :32; + __be32 pmcdr; }; - static struct device *pmc_dev; static struct pmc_regs __iomem *pmc_regs; +#define PMCSR_SLP 0x00020000 +#define PMCSR_LOSSLESS 0x00400000 +static int has_deep_sleep, has_lossless; + +void mpc85xx_enter_deep_sleep(phys_addr_t ccsrbar, u32 powmgtreq); + +/** + * pmc_enable_wake - enable OF device as wakeup event source + * @pdev: platform device affected + * @state: PM state from which device will issue wakeup events + * @enable: True to enable event generation; false to disable + * + * This enables the device as a wakeup event source, or disables it. + * + * RETURN VALUE: + * 0 is returned on success + * -EINVAL is returned if device is not supposed to wake up the system + * Error code depending on the platform is returned if both the platform and + * the native mechanism fail to enable the generation of wake-up events + */ +int pmc_enable_wake(struct platform_device *pdev, suspend_state_t state, bool enable) +{ + int ret = 0; + struct device_node *clk_np; + u32 *pmcdr_mask; + + if (enable && !device_may_wakeup(&pdev->dev)) + return -EINVAL; + + clk_np = of_parse_phandle(pdev->dev.of_node, "clk-handle", 0); + if (!clk_np) + return -EINVAL; + + pmcdr_mask = (u32 *)of_get_property(clk_np, "fsl,pmcdr-mask", NULL); + if (!pmcdr_mask) { + ret = -EINVAL; + goto out; + } + + /* clear to enable clock in low power mode */ + if (enable) + clrbits32(&pmc_regs->pmcdr, *pmcdr_mask); + else + setbits32(&pmc_regs->pmcdr, *pmcdr_mask); + +out: + of_node_put(clk_np); + return ret; +} +EXPORT_SYMBOL_GPL(pmc_enable_wake); + +/** + * pmc_enable_lossless - enable lossless ethernet in low power mode + * @enable: True to enable event generation; false to disable + */ +void pmc_enable_lossless(int enable) +{ + if (enable && has_lossless) + setbits32(&pmc_regs->pmcsr, PMCSR_LOSSLESS); + else + clrbits32(&pmc_regs->pmcsr, PMCSR_LOSSLESS); +} +EXPORT_SYMBOL_GPL(pmc_enable_lossless); + static int pmc_suspend_enter(suspend_state_t state) { int ret; + u32 powmgtreq = 0x00500000; - setbits32(&pmc_regs->pmcsr, PMCSR_SLP); - /* At this point, the CPU is asleep. */ + switch (state) { + case PM_SUSPEND_MEM: +#ifdef CONFIG_SPE + enable_kernel_spe(); +#endif + pr_debug("Entering deep sleep\n"); + + local_irq_disable(); + mpc85xx_enter_deep_sleep(get_immrbase(), + powmgtreq); + pr_debug("Resumed from deep sleep\n"); + + return 0; + + /* else fall-through */ + case PM_SUSPEND_STANDBY: + local_irq_disable(); + + setbits32(&pmc_regs->pmcsr, PMCSR_SLP); + + /* At this point, the CPU is asleep. */ + /* Upon resume, wait for SLP bit to be clear. */ + ret = spin_event_timeout((in_be32(&pmc_regs->pmcsr) & PMCSR_SLP) + == 0, 10000, 10) ? 0 : -ETIMEDOUT; + if (ret) + dev_err(pmc_dev, + "timeout waiting for SLP bit to be cleared\n"); + + return 0; + + default: + return -EINVAL; + + } - /* Upon resume, wait for SLP bit to be clear. */ - ret = spin_event_timeout((in_be32(&pmc_regs->pmcsr) & PMCSR_SLP) == 0, - 10000, 10) ? 0 : -ETIMEDOUT; - if (ret) - dev_err(pmc_dev, "tired waiting for SLP bit to clear\n"); - return ret; } static int pmc_suspend_valid(suspend_state_t state) { - if (state != PM_SUSPEND_STANDBY) - return 0; - return 1; + if (state == PM_SUSPEND_STANDBY) + return 1; + if (has_deep_sleep && (state == PM_SUSPEND_MEM)) + return 1; + return 0; } static struct platform_suspend_ops pmc_suspend_ops = { @@ -58,14 +157,30 @@ static struct platform_suspend_ops pmc_suspend_ops = { .enter = pmc_suspend_enter, }; -static int pmc_probe(struct platform_device *ofdev, +static int pmc_probe(struct platform_device *pdev, const struct of_device_id *id) { - pmc_regs = of_iomap(ofdev->dev.of_node, 0); - if (!pmc_regs) - return -ENOMEM; + struct device_node *np = pdev->dev.of_node; + struct device_node *node; + + node = of_find_compatible_node(NULL, NULL, "fsl,mpc8548-pmc"); + + if (node) { + pmc_regs = of_iomap(pdev->dev.of_node, 0); + if (!pmc_regs) + return -ENOMEM; + + if (of_device_is_compatible(np, "fsl,mpc8536-pmc")) { + has_deep_sleep = 1; + } + if (of_device_is_compatible(np, "fsl,p1022-pmc")) { + has_lossless = 1; + } + + of_node_put(node); + } - pmc_dev = &ofdev->dev; + pmc_dev = &pdev->dev; suspend_set_ops(&pmc_suspend_ops); return 0; } diff --git a/arch/powerpc/sysdev/fsl_soc.h b/arch/powerpc/sysdev/fsl_soc.h index 5360948..5250539 100644 --- a/arch/powerpc/sysdev/fsl_soc.h +++ b/arch/powerpc/sysdev/fsl_soc.h @@ -3,6 +3,8 @@ #ifdef __KERNEL__ #include <asm/mmu.h> +#include <linux/platform_device.h> +#include <linux/suspend.h> struct spi_device; @@ -21,6 +23,15 @@ struct device_node; extern void fsl_rstcr_restart(char *cmd); +#ifdef CONFIG_FSL_PMC +int pmc_enable_wake(struct platform_device *pdev, suspend_state_t state, + bool enable); +void pmc_enable_lossless(int enable); +#else +#define pmc_enable_wake(pdev, state, enable) (-EINVAL) +#define pmc_enable_lossless(enable) do {} while (0); +#endif + #if defined(CONFIG_FB_FSL_DIU) || defined(CONFIG_FB_FSL_DIU_MODULE) struct platform_diu_data_ops { unsigned int (*get_pixel_format) (unsigned int bits_per_pixel, -- 1.6.6-rc1.GIT ^ permalink raw reply related [flat|nested] 7+ messages in thread
* [PATCH 4/7] powerpc/85xx: add support to JOG feature using cpufreq interface 2010-12-03 12:34 ` [PATCH 3/7] powerpc/85xx: add the deep sleep support Li Yang @ 2010-12-03 12:34 ` Li Yang 0 siblings, 0 replies; 7+ messages in thread From: Li Yang @ 2010-12-03 12:34 UTC (permalink / raw) To: linuxppc-dev 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. Signed-off-by: Dave Liu <daveliu@freescale.com> Signed-off-by: Li Yang <leoli@freescale.com> --- arch/powerpc/platforms/85xx/Makefile | 1 + arch/powerpc/platforms/85xx/cpufreq.c | 236 +++++++++++++++++++++++++++++++++ arch/powerpc/platforms/Kconfig | 8 + 3 files changed, 245 insertions(+), 0 deletions(-) create mode 100644 arch/powerpc/platforms/85xx/cpufreq.c diff --git a/arch/powerpc/platforms/85xx/Makefile b/arch/powerpc/platforms/85xx/Makefile index 6bbcf22..11cedde 100644 --- a/arch/powerpc/platforms/85xx/Makefile +++ b/arch/powerpc/platforms/85xx/Makefile @@ -4,6 +4,7 @@ obj-$(CONFIG_SMP) += smp.o obj-$(CONFIG_HOTPLUG_CPU) += bootpage.o obj-$(CONFIG_SUSPEND) += suspend-asm.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..f729c3d --- /dev/null +++ b/arch/powerpc/platforms/85xx/cpufreq.c @@ -0,0 +1,236 @@ +/* + * Copyright (C) 2008-2010 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; + +#define PORPLLSR 0x0 +#define PMJCR 0x7c +#define POWMGTCSR 0x80 + +static struct cpufreq_frequency_table mpc85xx_freqs[] = { + {2, 0}, + {3, 0}, + {4, 0}, + {0, CPUFREQ_TABLE_END}, +}; + +/* + * hardware specific functions + */ +static int get_pll(int cpu) +{ + int ret, shift; + u32 pll = in_be32(guts + PORPLLSR); + shift = (cpu == 1) ? 24 : 16; + ret = (pll >> shift) & 0x3f; + + 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) ? 24 : 16; + busfreq = fsl_get_sys_freq(); + val = (pll & 0x3f) << shift; + + corefreq = ((busfreq * pll) >> 1); + /* must set the bit[18/19] if the requested core freq > 533 MHz */ + core_spd = (cpu == 1) ? 0x00002000 : 0x00001000; + if (corefreq > 533000000) + val |= core_spd; + + mask = (cpu == 1) ? 0x3f002000 : 0x003f1000; + tmp &= ~mask; + tmp |= val; + out_be32(guts + PMJCR, tmp); + val = in_be32(guts + PMJCR); + out_be32(guts + POWMGTCSR, 0x00600000); + printk("PMJCR request %08x at CPU %d\n", tmp, cpu); +} + +static void verify_pll(int cpu) +{ + int shift; + u32 busfreq, pll, corefreq; + + shift = (cpu == 1) ? 24 : 16; + busfreq = fsl_get_sys_freq(); + pll = (in_be32(guts + PORPLLSR) >> shift) & 0x3f; + + corefreq = (busfreq * pll) >> 1; + corefreq /= 1000000; + printk("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 */ + for (i = 0; mpc85xx_freqs[i].frequency != CPUFREQ_TABLE_END; i++) { + mpc85xx_freqs[i].frequency = (busfreq * mpc85xx_freqs[i].index) >> 1; + printk("%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); + + printk("setting frequency for cpu %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; + + 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 956154f..df529f9 100644 --- a/arch/powerpc/platforms/Kconfig +++ b/arch/powerpc/platforms/Kconfig @@ -178,6 +178,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, + this currently includes P1022 processor. + config PPC_PASEMI_CPUFREQ bool "Support for PA Semi PWRficient" depends on PPC_PASEMI -- 1.6.6-rc1.GIT ^ permalink raw reply related [flat|nested] 7+ messages in thread
end of thread, other threads:[~2011-11-09 16:13 UTC | newest] Thread overview: 7+ messages (download: mbox.gz follow: Atom feed -- links below jump to the message on this page -- 2011-11-04 12:36 [PATCH 4/7] powerpc/85xx: add support to JOG feature using cpufreq interface Zhao Chenhui 2011-11-04 19:42 ` Scott Wood 2011-11-07 10:27 ` Zhao Chenhui 2011-11-07 18:50 ` Scott Wood 2011-11-09 11:38 ` Zhao Chenhui 2011-11-09 16:13 ` Scott Wood -- strict thread matches above, loose matches on Subject: below -- 2010-12-03 12:34 [PATCH 1/7] powerpc/85xx: re-enable timebase sync disabled by KEXEC patch Li Yang 2010-12-03 12:34 ` [PATCH 2/7] powerpc/85xx: add HOTPLUG_CPU support Li Yang 2010-12-03 12:34 ` [PATCH 3/7] powerpc/85xx: add the deep sleep support Li Yang 2010-12-03 12:34 ` [PATCH 4/7] powerpc/85xx: add support to JOG feature using cpufreq interface Li Yang
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox; as well as URLs for NNTP newsgroup(s).