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 <dave...@freescale.com> Signed-off-by: Li Yang <le...@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 <dave...@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 <kra...@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 <dave...@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 _______________________________________________ Linuxppc-dev mailing list Linuxppc-dev@lists.ozlabs.org https://lists.ozlabs.org/listinfo/linuxppc-dev