* cpufreq for freescale mx51 @ 2010-10-08 8:08 yong.shen at linaro.org 2010-10-08 8:08 ` [PATCHv2] " yong.shen at linaro.org 0 siblings, 1 reply; 11+ messages in thread From: yong.shen at linaro.org @ 2010-10-08 8:08 UTC (permalink / raw) To: linux-arm-kernel Hope this time I can cover all the comments. :) ^ permalink raw reply [flat|nested] 11+ messages in thread
* [PATCHv2] cpufreq for freescale mx51 2010-10-08 8:08 cpufreq for freescale mx51 yong.shen at linaro.org @ 2010-10-08 8:08 ` yong.shen at linaro.org 2010-10-13 10:38 ` Amit Kucheria 2010-10-13 17:54 ` Sascha Hauer 0 siblings, 2 replies; 11+ messages in thread From: yong.shen at linaro.org @ 2010-10-08 8:08 UTC (permalink / raw) To: linux-arm-kernel From: Yong Shen <yong.shen@linaro.org> it is tested on babbage 3.0 Signed-off-by: Yong Shen <yong.shen@linaro.org> --- arch/arm/Kconfig | 6 + arch/arm/mach-mx5/Kconfig | 1 + arch/arm/mach-mx5/Makefile | 1 + arch/arm/mach-mx5/board-mx51_babbage.c | 12 ++- arch/arm/mach-mx5/clock-mx51.c | 24 ++++ arch/arm/mach-mx5/cpu.c | 2 + arch/arm/mach-mx5/cpu_wp-mx51.c | 42 ++++++ arch/arm/mach-mx5/cpu_wp-mx51.h | 14 ++ arch/arm/plat-mxc/Makefile | 2 + arch/arm/plat-mxc/cpufreq.c | 236 ++++++++++++++++++++++++++++++++ arch/arm/plat-mxc/include/mach/mxc.h | 20 +++- 11 files changed, 358 insertions(+), 2 deletions(-) create mode 100644 arch/arm/mach-mx5/cpu_wp-mx51.c create mode 100644 arch/arm/mach-mx5/cpu_wp-mx51.h create mode 100644 arch/arm/plat-mxc/cpufreq.c diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig index d203b84..71a572a 100644 --- a/arch/arm/Kconfig +++ b/arch/arm/Kconfig @@ -1690,6 +1690,12 @@ if ARCH_HAS_CPUFREQ source "drivers/cpufreq/Kconfig" +config CPU_FREQ_IMX + tristate "CPUfreq driver for i.MX CPUs" + depends on ARCH_MXC && CPU_FREQ + help + This enables the CPUfreq driver for i.MX CPUs. + config CPU_FREQ_SA1100 bool diff --git a/arch/arm/mach-mx5/Kconfig b/arch/arm/mach-mx5/Kconfig index 0848db5..7a621b4 100644 --- a/arch/arm/mach-mx5/Kconfig +++ b/arch/arm/mach-mx5/Kconfig @@ -5,6 +5,7 @@ config ARCH_MX51 default y select MXC_TZIC select ARCH_MXC_IOMUX_V3 + select ARCH_HAS_CPUFREQ comment "MX5 platforms:" diff --git a/arch/arm/mach-mx5/Makefile b/arch/arm/mach-mx5/Makefile index 86c66e7..e2af3fb 100644 --- a/arch/arm/mach-mx5/Makefile +++ b/arch/arm/mach-mx5/Makefile @@ -5,6 +5,7 @@ # Object file lists. obj-y := cpu.o mm.o clock-mx51.o devices.o +obj-$(CONFIG_CPU_FREQ_IMX) += cpu_wp-mx51.o obj-$(CONFIG_MACH_MX51_BABBAGE) += board-mx51_babbage.o obj-$(CONFIG_MACH_MX51_3DS) += board-mx51_3ds.o obj-$(CONFIG_MACH_EUKREA_CPUIMX51) += board-cpuimx51.o diff --git a/arch/arm/mach-mx5/board-mx51_babbage.c b/arch/arm/mach-mx5/board-mx51_babbage.c index 6e384d9..2d2a052 100644 --- a/arch/arm/mach-mx5/board-mx51_babbage.c +++ b/arch/arm/mach-mx5/board-mx51_babbage.c @@ -1,5 +1,5 @@ /* - * Copyright 2009 Freescale Semiconductor, Inc. All Rights Reserved. + * Copyright 2009-2010 Freescale Semiconductor, Inc. All Rights Reserved. * Copyright (C) 2009-2010 Amit Kucheria <amit.kucheria@canonical.com> * * The code contained herein is licensed under the GNU General Public @@ -32,6 +32,7 @@ #include <asm/mach/time.h> #include "devices.h" +#include "cpu_wp-mx51.h" #define BABBAGE_USB_HUB_RESET (0*32 + 7) /* GPIO_1_7 */ #define BABBAGE_USBH1_STP (0*32 + 27) /* GPIO_1_27 */ @@ -279,8 +280,17 @@ static struct sys_timer mxc_timer = { .init = mx51_babbage_timer_init, }; +static void __init fixup_mxc_board(struct machine_desc *desc, struct tag *tags, + char **cmdline, struct meminfo *mi) +{ +#if defined(CONFIG_CPU_FREQ_IMX) + get_cpu_wp = mx51_get_cpu_wp; +#endif +} + MACHINE_START(MX51_BABBAGE, "Freescale MX51 Babbage Board") /* Maintainer: Amit Kucheria <amit.kucheria@canonical.com> */ + .fixup = fixup_mxc_board, .phys_io = MX51_AIPS1_BASE_ADDR, .io_pg_offst = ((MX51_AIPS1_BASE_ADDR_VIRT) >> 18) & 0xfffc, .boot_params = PHYS_OFFSET + 0x100, diff --git a/arch/arm/mach-mx5/clock-mx51.c b/arch/arm/mach-mx5/clock-mx51.c index 6af69de..f23cfab 100644 --- a/arch/arm/mach-mx5/clock-mx51.c +++ b/arch/arm/mach-mx5/clock-mx51.c @@ -39,6 +39,8 @@ static struct clk ahb_clk; static struct clk ipg_clk; static struct clk usboh3_clk; +DEFINE_SPINLOCK(clk_lock); + #define MAX_DPLL_WAIT_TRIES 1000 /* 1000 * udelay(1) = 1ms */ static int _clk_ccgr_enable(struct clk *clk) @@ -342,6 +344,26 @@ static unsigned long clk_arm_get_rate(struct clk *clk) return parent_rate / div; } +static int _clk_cpu_set_rate(struct clk *clk, unsigned long rate) +{ + u32 reg, cpu_podf; + unsigned long flags, parent_rate; + + parent_rate = clk_get_rate(clk->parent); + cpu_podf = parent_rate / rate - 1; + /* use post divider to change freq */ + spin_lock_irqsave(&clk_lock, flags); + + reg = __raw_readl(MXC_CCM_CACRR); + reg &= ~MXC_CCM_CACRR_ARM_PODF_MASK; + reg |= cpu_podf << MXC_CCM_CACRR_ARM_PODF_OFFSET; + __raw_writel(reg, MXC_CCM_CACRR); + + spin_unlock_irqrestore(&clk_lock, flags); + + return 0; +} + static int _clk_periph_apm_set_parent(struct clk *clk, struct clk *parent) { u32 reg, mux; @@ -694,6 +716,7 @@ static struct clk periph_apm_clk = { static struct clk cpu_clk = { .parent = &pll1_sw_clk, .get_rate = clk_arm_get_rate, + .set_rate = _clk_cpu_set_rate, }; static struct clk ahb_clk = { @@ -837,6 +860,7 @@ static struct clk_lookup lookups[] = { _REGISTER_CLOCK("fsl-usb2-udc", "usb", usboh3_clk) _REGISTER_CLOCK("fsl-usb2-udc", "usb_ahb", ahb_clk) _REGISTER_CLOCK("imx-keypad.0", NULL, kpp_clk) + _REGISTER_CLOCK(NULL, "cpu_clk", cpu_clk) }; static void clk_tree_init(void) diff --git a/arch/arm/mach-mx5/cpu.c b/arch/arm/mach-mx5/cpu.c index 2d37785..83add9c 100644 --- a/arch/arm/mach-mx5/cpu.c +++ b/arch/arm/mach-mx5/cpu.c @@ -22,6 +22,8 @@ static int cpu_silicon_rev = -1; #define SI_REV 0x48 +struct cpu_wp *(*get_cpu_wp)(int *wp); + static void query_silicon_parameter(void) { void __iomem *rom = ioremap(MX51_IROM_BASE_ADDR, MX51_IROM_SIZE); diff --git a/arch/arm/mach-mx5/cpu_wp-mx51.c b/arch/arm/mach-mx5/cpu_wp-mx51.c new file mode 100644 index 0000000..51bde45 --- /dev/null +++ b/arch/arm/mach-mx5/cpu_wp-mx51.c @@ -0,0 +1,42 @@ +/* + * Copyright (C) 2010 Freescale Semiconductor, Inc. All Rights Reserved. + */ + +/* + * The code contained herein is licensed under the GNU General Public + * License. You may obtain a copy of the GNU General Public License + * Version 2 or later@the following locations: + * + * http://www.opensource.org/licenses/gpl-license.html + * http://www.gnu.org/copyleft/gpl.html + */ + +#include <linux/types.h> +#include <mach/hardware.h> + +static struct cpu_wp cpu_wp_auto[] = { + { + .pll_rate = 800000000, + .cpu_rate = 160000000, + .pdf = 4, + .mfi = 8, + .mfd = 2, + .mfn = 1, + .cpu_podf = 4, + .cpu_voltage = 850000,}, + { + .pll_rate = 800000000, + .cpu_rate = 800000000, + .pdf = 0, + .mfi = 8, + .mfd = 2, + .mfn = 1, + .cpu_podf = 0, + .cpu_voltage = 1100000,}, +}; + +struct cpu_wp *mx51_get_cpu_wp(int *wp) +{ + *wp = sizeof(cpu_wp_auto) / sizeof(struct cpu_wp); + return cpu_wp_auto; +} diff --git a/arch/arm/mach-mx5/cpu_wp-mx51.h b/arch/arm/mach-mx5/cpu_wp-mx51.h new file mode 100644 index 0000000..8038b62 --- /dev/null +++ b/arch/arm/mach-mx5/cpu_wp-mx51.h @@ -0,0 +1,14 @@ +/* + * Copyright (C) 2010 Freescale Semiconductor, Inc. All Rights Reserved. + */ + +/* + * The code contained herein is licensed under the GNU General Public + * License. You may obtain a copy of the GNU General Public License + * Version 2 or later at the following locations: + * + * http://www.opensource.org/licenses/gpl-license.html + * http://www.gnu.org/copyleft/gpl.html + */ + +extern struct cpu_wp *mx51_get_cpu_wp(int *wp); diff --git a/arch/arm/plat-mxc/Makefile b/arch/arm/plat-mxc/Makefile index 78d405e..0b8464f 100644 --- a/arch/arm/plat-mxc/Makefile +++ b/arch/arm/plat-mxc/Makefile @@ -16,6 +16,8 @@ obj-$(CONFIG_MXC_ULPI) += ulpi.o obj-$(CONFIG_ARCH_MXC_AUDMUX_V1) += audmux-v1.o obj-$(CONFIG_ARCH_MXC_AUDMUX_V2) += audmux-v2.o obj-$(CONFIG_MXC_DEBUG_BOARD) += 3ds_debugboard.o +# CPU FREQ support +obj-$(CONFIG_CPU_FREQ_IMX) += cpufreq.o ifdef CONFIG_SND_IMX_SOC obj-y += ssi-fiq.o obj-y += ssi-fiq-ksym.o diff --git a/arch/arm/plat-mxc/cpufreq.c b/arch/arm/plat-mxc/cpufreq.c new file mode 100644 index 0000000..9990ea8 --- /dev/null +++ b/arch/arm/plat-mxc/cpufreq.c @@ -0,0 +1,236 @@ +/* + * Copyright (C) 2010 Freescale Semiconductor, Inc. All Rights Reserved. + */ + +/* + * The code contained herein is licensed under the GNU General Public + * License. You may obtain a copy of the GNU General Public License + * Version 2 or later at the following locations: + * + * http://www.opensource.org/licenses/gpl-license.html + * http://www.gnu.org/copyleft/gpl.html + */ + +/* + * A driver for the Freescale Semiconductor i.MXC CPUfreq module. + * The CPUFREQ driver is for controling CPU frequency. It allows you to change + * the CPU clock speed on the fly. + */ + +#include <linux/types.h> +#include <linux/kernel.h> +#include <linux/cpufreq.h> +#include <linux/init.h> +#include <linux/proc_fs.h> +#include <linux/regulator/consumer.h> +#include <linux/clk.h> +#include <linux/delay.h> +#include <linux/io.h> +#include <mach/hardware.h> +#include <asm/setup.h> +#include <mach/clock.h> +#include <asm/cacheflush.h> +#include <linux/hrtimer.h> + +static int cpu_freq_khz_min; +static int cpu_freq_khz_max; +static int arm_lpm_clk; +static int arm_normal_clk; +static int cpufreq_suspended; + +static struct clk *cpu_clk; +static struct cpufreq_frequency_table *imx_freq_table; + +static int cpu_wp_nr; +static struct cpu_wp *cpu_wp_tbl; + +static int set_cpu_freq(int freq) +{ + int ret = 0; + int org_cpu_rate; + int gp_volt = 0; + int i; + + org_cpu_rate = clk_get_rate(cpu_clk); + if (org_cpu_rate == freq) + return ret; + + for (i = 0; i < cpu_wp_nr; i++) { + if (freq == cpu_wp_tbl[i].cpu_rate) + gp_volt = cpu_wp_tbl[i].cpu_voltage; + } + + if (gp_volt == 0) + return ret; + + ret = clk_set_rate(cpu_clk, freq); + if (ret != 0) { + printk(KERN_DEBUG "cannot set CPU clock rate\n"); + return ret; + } + + return ret; +} + +static int mxc_verify_speed(struct cpufreq_policy *policy) +{ + if (policy->cpu != 0) + return -EINVAL; + + return cpufreq_frequency_table_verify(policy, imx_freq_table); +} + +static unsigned int mxc_get_speed(unsigned int cpu) +{ + if (cpu) + return 0; + + return clk_get_rate(cpu_clk) / 1000; +} + +static int mxc_set_target(struct cpufreq_policy *policy, + unsigned int target_freq, unsigned int relation) +{ + struct cpufreq_freqs freqs; + int freq_Hz; + int ret = 0; + unsigned int index; + + if (cpufreq_suspended) { + target_freq = clk_get_rate(cpu_clk) / 1000; + cpufreq_frequency_table_target(policy, imx_freq_table, + target_freq, relation, &index); + freq_Hz = imx_freq_table[index].frequency * 1000; + + if (freq_Hz == arm_lpm_clk) + freqs.old = cpu_wp_tbl[cpu_wp_nr - 2].cpu_rate / 1000; + else + freqs.old = arm_lpm_clk / 1000; + + freqs.new = freq_Hz / 1000; + freqs.cpu = 0; + freqs.flags = 0; + cpufreq_notify_transition(&freqs, CPUFREQ_PRECHANGE); + cpufreq_notify_transition(&freqs, CPUFREQ_POSTCHANGE); + return ret; + } + + cpufreq_frequency_table_target(policy, imx_freq_table, + target_freq, relation, &index); + freq_Hz = imx_freq_table[index].frequency * 1000; + + freqs.old = clk_get_rate(cpu_clk) / 1000; + freqs.new = freq_Hz / 1000; + freqs.cpu = 0; + freqs.flags = 0; + cpufreq_notify_transition(&freqs, CPUFREQ_PRECHANGE); + + if (freqs.old != freqs.new) + ret = set_cpu_freq(freq_Hz); + + cpufreq_notify_transition(&freqs, CPUFREQ_POSTCHANGE); + + return ret; +} + +static int __init mxc_cpufreq_init(struct cpufreq_policy *policy) +{ + int ret; + int i; + + printk(KERN_INFO "i.MXC CPU frequency driver\n"); + + if (policy->cpu != 0) + return -EINVAL; + + cpu_clk = clk_get(NULL, "cpu_clk"); + if (IS_ERR(cpu_clk)) { + printk(KERN_ERR "%s: failed to get cpu clock\n", __func__); + return PTR_ERR(cpu_clk); + } + + /* Set the current working point. */ + cpu_wp_tbl = get_cpu_wp(&cpu_wp_nr); + + cpu_freq_khz_min = cpu_wp_tbl[0].cpu_rate / 1000; + cpu_freq_khz_max = cpu_wp_tbl[0].cpu_rate / 1000; + + imx_freq_table = kmalloc( + sizeof(struct cpufreq_frequency_table) * (cpu_wp_nr + 1), + GFP_KERNEL); + + for (i = 0; i < cpu_wp_nr; i++) { + imx_freq_table[i].index = i; + imx_freq_table[i].frequency = + cpu_wp_tbl[i].cpu_rate / 1000; + + if ((cpu_wp_tbl[i].cpu_rate / 1000) < cpu_freq_khz_min) + cpu_freq_khz_min = cpu_wp_tbl[i].cpu_rate / 1000; + + if ((cpu_wp_tbl[i].cpu_rate / 1000) > cpu_freq_khz_max) + cpu_freq_khz_max = cpu_wp_tbl[i].cpu_rate / 1000; + } + + imx_freq_table[i].index = i; + imx_freq_table[i].frequency = CPUFREQ_TABLE_END; + + policy->cur = clk_get_rate(cpu_clk) / 1000; + policy->governor = CPUFREQ_DEFAULT_GOVERNOR; + policy->min = policy->cpuinfo.min_freq = cpu_freq_khz_min; + policy->max = policy->cpuinfo.max_freq = cpu_freq_khz_max; + + arm_lpm_clk = cpu_freq_khz_min * 1000; + arm_normal_clk = cpu_freq_khz_max * 1000; + + /* Manual states, that PLL stabilizes in two CLK32 periods */ + policy->cpuinfo.transition_latency = 10; + + ret = cpufreq_frequency_table_cpuinfo(policy, imx_freq_table); + + if (ret < 0) { + clk_put(cpu_clk); + printk(KERN_ERR "%s: failed to register i.MXC CPUfreq\n", + __func__); + return ret; + } + + cpufreq_frequency_table_get_attr(imx_freq_table, policy->cpu); + return 0; +} + +static int mxc_cpufreq_exit(struct cpufreq_policy *policy) +{ + cpufreq_frequency_table_put_attr(policy->cpu); + + /* Reset CPU to 665MHz */ + set_cpu_freq(arm_normal_clk); + clk_put(cpu_clk); + return 0; +} + +static struct cpufreq_driver mxc_driver = { + .flags = CPUFREQ_STICKY, + .verify = mxc_verify_speed, + .target = mxc_set_target, + .get = mxc_get_speed, + .init = mxc_cpufreq_init, + .exit = mxc_cpufreq_exit, + .name = "imx", +}; + +static int __devinit mxc_cpufreq_driver_init(void) +{ + return cpufreq_register_driver(&mxc_driver); +} + +static void mxc_cpufreq_driver_exit(void) +{ + cpufreq_unregister_driver(&mxc_driver); +} + +module_init(mxc_cpufreq_driver_init); +module_exit(mxc_cpufreq_driver_exit); + +MODULE_AUTHOR("Freescale Semiconductor Inc. Yong Shen <yong.shen@linaro.org>"); +MODULE_DESCRIPTION("CPUfreq driver for i.MX"); +MODULE_LICENSE("GPL"); diff --git a/arch/arm/plat-mxc/include/mach/mxc.h b/arch/arm/plat-mxc/include/mach/mxc.h index a790bf2..31df991 100644 --- a/arch/arm/plat-mxc/include/mach/mxc.h +++ b/arch/arm/plat-mxc/include/mach/mxc.h @@ -1,5 +1,5 @@ /* - * Copyright 2004-2007 Freescale Semiconductor, Inc. All Rights Reserved. + * Copyright 2004-2010 Freescale Semiconductor, Inc. All Rights Reserved. * Copyright (C) 2008 Juergen Beisert (kernel at pengutronix.de) * * This program is free software; you can redistribute it and/or @@ -133,6 +133,24 @@ extern unsigned int __mxc_cpu_type; # define cpu_is_mxc91231() (0) #endif +#ifndef __ASSEMBLY__ + +struct cpu_wp { + u32 pll_reg; + u32 pll_rate; + u32 cpu_rate; + u32 pdr0_reg; + u32 pdf; + u32 mfi; + u32 mfd; + u32 mfn; + u32 cpu_voltage; + u32 cpu_podf; +}; + +extern struct cpu_wp *(*get_cpu_wp)(int *wp); +#endif + #if defined(CONFIG_ARCH_MX3) || defined(CONFIG_ARCH_MX2) /* These are deprecated, use mx[23][157]_setup_weimcs instead. */ #define CSCR_U(n) (IO_ADDRESS(WEIM_BASE_ADDR + n * 0x10)) -- 1.6.3.3 ^ permalink raw reply related [flat|nested] 11+ messages in thread
* [PATCHv2] cpufreq for freescale mx51 2010-10-08 8:08 ` [PATCHv2] " yong.shen at linaro.org @ 2010-10-13 10:38 ` Amit Kucheria 2010-10-18 5:46 ` Yong Shen 2010-10-13 17:54 ` Sascha Hauer 1 sibling, 1 reply; 11+ messages in thread From: Amit Kucheria @ 2010-10-13 10:38 UTC (permalink / raw) To: linux-arm-kernel Yong, Some more comments. But the patch is looking good now. On Fri, Oct 8, 2010 at 11:08 AM, <yong.shen@linaro.org> wrote: > From: Yong Shen <yong.shen@linaro.org> > > it is tested on babbage 3.0 Change to "Cpufreq driver for imx51. The operating points are currently tested on babbage 3.0." > Signed-off-by: Yong Shen <yong.shen@linaro.org> > --- > ?arch/arm/Kconfig ? ? ? ? ? ? ? ? ? ? ? | ? ?6 + > ?arch/arm/mach-mx5/Kconfig ? ? ? ? ? ? ?| ? ?1 + > ?arch/arm/mach-mx5/Makefile ? ? ? ? ? ? | ? ?1 + > ?arch/arm/mach-mx5/board-mx51_babbage.c | ? 12 ++- > ?arch/arm/mach-mx5/clock-mx51.c ? ? ? ? | ? 24 ++++ > ?arch/arm/mach-mx5/cpu.c ? ? ? ? ? ? ? ?| ? ?2 + > ?arch/arm/mach-mx5/cpu_wp-mx51.c ? ? ? ?| ? 42 ++++++ > ?arch/arm/mach-mx5/cpu_wp-mx51.h ? ? ? ?| ? 14 ++ > ?arch/arm/plat-mxc/Makefile ? ? ? ? ? ? | ? ?2 + > ?arch/arm/plat-mxc/cpufreq.c ? ? ? ? ? ?| ?236 ++++++++++++++++++++++++++++++++ > ?arch/arm/plat-mxc/include/mach/mxc.h ? | ? 20 +++- > ?11 files changed, 358 insertions(+), 2 deletions(-) > ?create mode 100644 arch/arm/mach-mx5/cpu_wp-mx51.c > ?create mode 100644 arch/arm/mach-mx5/cpu_wp-mx51.h > ?create mode 100644 arch/arm/plat-mxc/cpufreq.c > > diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig > index d203b84..71a572a 100644 > --- a/arch/arm/Kconfig > +++ b/arch/arm/Kconfig > @@ -1690,6 +1690,12 @@ if ARCH_HAS_CPUFREQ > > ?source "drivers/cpufreq/Kconfig" > > +config CPU_FREQ_IMX > + ? ? ? tristate "CPUfreq driver for i.MX CPUs" > + ? ? ? depends on ARCH_MXC && CPU_FREQ > + ? ? ? help > + ? ? ? ? This enables the CPUfreq driver for i.MX CPUs. > + > ?config CPU_FREQ_SA1100 > ? ? ? ?bool > > diff --git a/arch/arm/mach-mx5/Kconfig b/arch/arm/mach-mx5/Kconfig > index 0848db5..7a621b4 100644 > --- a/arch/arm/mach-mx5/Kconfig > +++ b/arch/arm/mach-mx5/Kconfig > @@ -5,6 +5,7 @@ config ARCH_MX51 > ? ? ? ?default y > ? ? ? ?select MXC_TZIC > ? ? ? ?select ARCH_MXC_IOMUX_V3 > + ? ? ? select ARCH_HAS_CPUFREQ > > ?comment "MX5 platforms:" > > diff --git a/arch/arm/mach-mx5/Makefile b/arch/arm/mach-mx5/Makefile > index 86c66e7..e2af3fb 100644 > --- a/arch/arm/mach-mx5/Makefile > +++ b/arch/arm/mach-mx5/Makefile > @@ -5,6 +5,7 @@ > ?# Object file lists. > ?obj-y ? := cpu.o mm.o clock-mx51.o devices.o > > +obj-$(CONFIG_CPU_FREQ_IMX) ? ?+= cpu_wp-mx51.o s/wp/op/ ? Operating point is a more widely used word for frequency/voltage pairs in the ARM world. We will also want to consider using the OPP library currently being discussed elsewhere on LAKML. So change all instances of working point or wp to operating point or op. > ?obj-$(CONFIG_MACH_MX51_BABBAGE) += board-mx51_babbage.o > ?obj-$(CONFIG_MACH_MX51_3DS) += board-mx51_3ds.o > ?obj-$(CONFIG_MACH_EUKREA_CPUIMX51) += board-cpuimx51.o > diff --git a/arch/arm/mach-mx5/board-mx51_babbage.c b/arch/arm/mach-mx5/board-mx51_babbage.c > index 6e384d9..2d2a052 100644 > --- a/arch/arm/mach-mx5/board-mx51_babbage.c > +++ b/arch/arm/mach-mx5/board-mx51_babbage.c > @@ -1,5 +1,5 @@ > ?/* > - * Copyright 2009 Freescale Semiconductor, Inc. All Rights Reserved. > + * Copyright 2009-2010 Freescale Semiconductor, Inc. All Rights Reserved. > ?* Copyright (C) 2009-2010 Amit Kucheria <amit.kucheria@canonical.com> > ?* > ?* The code contained herein is licensed under the GNU General Public > @@ -32,6 +32,7 @@ > ?#include <asm/mach/time.h> > > ?#include "devices.h" > +#include "cpu_wp-mx51.h" > > ?#define BABBAGE_USB_HUB_RESET ?(0*32 + 7) ? ? ?/* GPIO_1_7 */ > ?#define BABBAGE_USBH1_STP ? ? ?(0*32 + 27) ? ? /* GPIO_1_27 */ > @@ -279,8 +280,17 @@ static struct sys_timer mxc_timer = { > ? ? ? ?.init ? = mx51_babbage_timer_init, > ?}; > > +static void __init fixup_mxc_board(struct machine_desc *desc, struct tag *tags, > + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?char **cmdline, struct meminfo *mi) > +{ > +#if defined(CONFIG_CPU_FREQ_IMX) > + ? ? ? get_cpu_wp = mx51_get_cpu_wp; s/wp/op > +#endif > +} > + > ?MACHINE_START(MX51_BABBAGE, "Freescale MX51 Babbage Board") > ? ? ? ?/* Maintainer: Amit Kucheria <amit.kucheria@canonical.com> */ > + ? ? ? .fixup = fixup_mxc_board, > ? ? ? ?.phys_io = MX51_AIPS1_BASE_ADDR, > ? ? ? ?.io_pg_offst = ((MX51_AIPS1_BASE_ADDR_VIRT) >> 18) & 0xfffc, > ? ? ? ?.boot_params = PHYS_OFFSET + 0x100, > diff --git a/arch/arm/mach-mx5/clock-mx51.c b/arch/arm/mach-mx5/clock-mx51.c > index 6af69de..f23cfab 100644 > --- a/arch/arm/mach-mx5/clock-mx51.c > +++ b/arch/arm/mach-mx5/clock-mx51.c > @@ -39,6 +39,8 @@ static struct clk ahb_clk; > ?static struct clk ipg_clk; > ?static struct clk usboh3_clk; > > +DEFINE_SPINLOCK(clk_lock); > + > ?#define MAX_DPLL_WAIT_TRIES ? ?1000 /* 1000 * udelay(1) = 1ms */ > > ?static int _clk_ccgr_enable(struct clk *clk) > @@ -342,6 +344,26 @@ static unsigned long clk_arm_get_rate(struct clk *clk) > ? ? ? ?return parent_rate / div; > ?} > > +static int _clk_cpu_set_rate(struct clk *clk, unsigned long rate) > +{ > + ? ? ? u32 reg, cpu_podf; > + ? ? ? unsigned long flags, parent_rate; > + > + ? ? ? parent_rate = clk_get_rate(clk->parent); > + ? ? ? cpu_podf = parent_rate / rate - 1; > + ? ? ? /* use post divider to change freq */ > + ? ? ? spin_lock_irqsave(&clk_lock, flags); > + > + ? ? ? reg = __raw_readl(MXC_CCM_CACRR); > + ? ? ? reg &= ~MXC_CCM_CACRR_ARM_PODF_MASK; > + ? ? ? reg |= cpu_podf << MXC_CCM_CACRR_ARM_PODF_OFFSET; > + ? ? ? __raw_writel(reg, MXC_CCM_CACRR); > + > + ? ? ? spin_unlock_irqrestore(&clk_lock, flags); > + > + ? ? ? return 0; > +} > + > ?static int _clk_periph_apm_set_parent(struct clk *clk, struct clk *parent) > ?{ > ? ? ? ?u32 reg, mux; > @@ -694,6 +716,7 @@ static struct clk periph_apm_clk = { > ?static struct clk cpu_clk = { > ? ? ? ?.parent = &pll1_sw_clk, > ? ? ? ?.get_rate = clk_arm_get_rate, > + ? ? ? .set_rate = _clk_cpu_set_rate, > ?}; > > ?static struct clk ahb_clk = { > @@ -837,6 +860,7 @@ static struct clk_lookup lookups[] = { > ? ? ? ?_REGISTER_CLOCK("fsl-usb2-udc", "usb", usboh3_clk) > ? ? ? ?_REGISTER_CLOCK("fsl-usb2-udc", "usb_ahb", ahb_clk) > ? ? ? ?_REGISTER_CLOCK("imx-keypad.0", NULL, kpp_clk) > + ? ? ? _REGISTER_CLOCK(NULL, "cpu_clk", cpu_clk) > ?}; > > ?static void clk_tree_init(void) > diff --git a/arch/arm/mach-mx5/cpu.c b/arch/arm/mach-mx5/cpu.c > index 2d37785..83add9c 100644 > --- a/arch/arm/mach-mx5/cpu.c > +++ b/arch/arm/mach-mx5/cpu.c > @@ -22,6 +22,8 @@ static int cpu_silicon_rev = -1; > > ?#define SI_REV 0x48 > > +struct cpu_wp *(*get_cpu_wp)(int *wp); > + s/wp/op > ?static void query_silicon_parameter(void) > ?{ > ? ? ? ?void __iomem *rom = ioremap(MX51_IROM_BASE_ADDR, MX51_IROM_SIZE); > diff --git a/arch/arm/mach-mx5/cpu_wp-mx51.c b/arch/arm/mach-mx5/cpu_wp-mx51.c > new file mode 100644 > index 0000000..51bde45 > --- /dev/null > +++ b/arch/arm/mach-mx5/cpu_wp-mx51.c filename s/wp/op > @@ -0,0 +1,42 @@ > +/* > + * Copyright (C) 2010 Freescale Semiconductor, Inc. All Rights Reserved. > + */ > + > +/* > + * The code contained herein is licensed under the GNU General Public > + * License. You may obtain a copy of the GNU General Public License > + * Version 2 or later at the following locations: > + * > + * http://www.opensource.org/licenses/gpl-license.html > + * http://www.gnu.org/copyleft/gpl.html > + */ > + > +#include <linux/types.h> > +#include <mach/hardware.h> > + > +static struct cpu_wp cpu_wp_auto[] = { s/cpu_wp/mx51_cpu_wp This also makes this struct explicity to mx51. > + ? ? ? { > + ? ? ? .pll_rate = 800000000, > + ? ? ? .cpu_rate = 160000000, > + ? ? ? .pdf = 4, > + ? ? ? .mfi = 8, > + ? ? ? .mfd = 2, > + ? ? ? .mfn = 1, > + ? ? ? .cpu_podf = 4, > + ? ? ? .cpu_voltage = 850000,}, > + ? ? ? { > + ? ? ? .pll_rate = 800000000, > + ? ? ? .cpu_rate = 800000000, > + ? ? ? .pdf = 0, > + ? ? ? .mfi = 8, > + ? ? ? .mfd = 2, > + ? ? ? .mfn = 1, > + ? ? ? .cpu_podf = 0, > + ? ? ? .cpu_voltage = 1100000,}, > +}; Except for cpu_rate and cpu_voltage, I don't see the other fields being used anywhere. > +struct cpu_wp *mx51_get_cpu_wp(int *wp) > +{ > + ? ? ? *wp = sizeof(cpu_wp_auto) / sizeof(struct cpu_wp); > + ? ? ? return cpu_wp_auto; > +} s/wp/op > diff --git a/arch/arm/mach-mx5/cpu_wp-mx51.h b/arch/arm/mach-mx5/cpu_wp-mx51.h > new file mode 100644 > index 0000000..8038b62 > --- /dev/null > +++ b/arch/arm/mach-mx5/cpu_wp-mx51.h > @@ -0,0 +1,14 @@ > +/* > + * Copyright (C) 2010 Freescale Semiconductor, Inc. All Rights Reserved. > + */ > + > +/* > + * The code contained herein is licensed under the GNU General Public > + * License. You may obtain a copy of the GNU General Public License > + * Version 2 or later at the following locations: > + * > + * http://www.opensource.org/licenses/gpl-license.html > + * http://www.gnu.org/copyleft/gpl.html > + */ > + > +extern struct cpu_wp *mx51_get_cpu_wp(int *wp); > diff --git a/arch/arm/plat-mxc/Makefile b/arch/arm/plat-mxc/Makefile > index 78d405e..0b8464f 100644 > --- a/arch/arm/plat-mxc/Makefile > +++ b/arch/arm/plat-mxc/Makefile > @@ -16,6 +16,8 @@ obj-$(CONFIG_MXC_ULPI) += ulpi.o > ?obj-$(CONFIG_ARCH_MXC_AUDMUX_V1) += audmux-v1.o > ?obj-$(CONFIG_ARCH_MXC_AUDMUX_V2) += audmux-v2.o > ?obj-$(CONFIG_MXC_DEBUG_BOARD) += 3ds_debugboard.o > +# CPU FREQ support > +obj-$(CONFIG_CPU_FREQ_IMX) ? ?+= cpufreq.o > ?ifdef CONFIG_SND_IMX_SOC > ?obj-y += ssi-fiq.o > ?obj-y += ssi-fiq-ksym.o > diff --git a/arch/arm/plat-mxc/cpufreq.c b/arch/arm/plat-mxc/cpufreq.c > new file mode 100644 > index 0000000..9990ea8 > --- /dev/null > +++ b/arch/arm/plat-mxc/cpufreq.c > @@ -0,0 +1,236 @@ > +/* > + * Copyright (C) 2010 Freescale Semiconductor, Inc. All Rights Reserved. > + */ > + > +/* > + * The code contained herein is licensed under the GNU General Public > + * License. You may obtain a copy of the GNU General Public License > + * Version 2 or later at the following locations: > + * > + * http://www.opensource.org/licenses/gpl-license.html > + * http://www.gnu.org/copyleft/gpl.html > + */ > + > +/* > + * A driver for the Freescale Semiconductor i.MXC CPUfreq module. > + * The CPUFREQ driver is for controling CPU frequency. It allows you to change > + * the CPU clock speed on the fly. > + */ > + > +#include <linux/types.h> > +#include <linux/kernel.h> > +#include <linux/cpufreq.h> > +#include <linux/init.h> > +#include <linux/proc_fs.h> > +#include <linux/regulator/consumer.h> > +#include <linux/clk.h> > +#include <linux/delay.h> > +#include <linux/io.h> > +#include <mach/hardware.h> > +#include <asm/setup.h> > +#include <mach/clock.h> > +#include <asm/cacheflush.h> > +#include <linux/hrtimer.h> > + > +static int cpu_freq_khz_min; > +static int cpu_freq_khz_max; > +static int arm_lpm_clk; > +static int arm_normal_clk; > +static int cpufreq_suspended; > + > +static struct clk *cpu_clk; > +static struct cpufreq_frequency_table *imx_freq_table; > + > +static int cpu_wp_nr; > +static struct cpu_wp *cpu_wp_tbl; > + > +static int set_cpu_freq(int freq) > +{ > + ? ? ? int ret = 0; > + ? ? ? int org_cpu_rate; > + ? ? ? int gp_volt = 0; > + ? ? ? int i; > + > + ? ? ? org_cpu_rate = clk_get_rate(cpu_clk); > + ? ? ? if (org_cpu_rate == freq) > + ? ? ? ? ? ? ? return ret; > + > + ? ? ? for (i = 0; i < cpu_wp_nr; i++) { > + ? ? ? ? ? ? ? if (freq == cpu_wp_tbl[i].cpu_rate) > + ? ? ? ? ? ? ? ? ? ? ? gp_volt = cpu_wp_tbl[i].cpu_voltage; > + ? ? ? } > + > + ? ? ? if (gp_volt == 0) > + ? ? ? ? ? ? ? return ret; > + > + ? ? ? ret = clk_set_rate(cpu_clk, freq); > + ? ? ? if (ret != 0) { > + ? ? ? ? ? ? ? printk(KERN_DEBUG "cannot set CPU clock rate\n"); > + ? ? ? ? ? ? ? return ret; > + ? ? ? } > + > + ? ? ? return ret; > +} > + > +static int mxc_verify_speed(struct cpufreq_policy *policy) > +{ > + ? ? ? if (policy->cpu != 0) > + ? ? ? ? ? ? ? return -EINVAL; > + > + ? ? ? return cpufreq_frequency_table_verify(policy, imx_freq_table); > +} > + > +static unsigned int mxc_get_speed(unsigned int cpu) > +{ > + ? ? ? if (cpu) > + ? ? ? ? ? ? ? return 0; > + > + ? ? ? return clk_get_rate(cpu_clk) / 1000; > +} > + > +static int mxc_set_target(struct cpufreq_policy *policy, > + ? ? ? ? ? ? ? ? ? ? ? ? unsigned int target_freq, unsigned int relation) > +{ > + ? ? ? struct cpufreq_freqs freqs; > + ? ? ? int freq_Hz; > + ? ? ? int ret = 0; > + ? ? ? unsigned int index; > + > + ? ? ? if (cpufreq_suspended) { > + ? ? ? ? ? ? ? target_freq = clk_get_rate(cpu_clk) / 1000; > + ? ? ? ? ? ? ? cpufreq_frequency_table_target(policy, imx_freq_table, > + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? target_freq, relation, &index); > + ? ? ? ? ? ? ? freq_Hz = imx_freq_table[index].frequency * 1000; > + > + ? ? ? ? ? ? ? if (freq_Hz == arm_lpm_clk) > + ? ? ? ? ? ? ? ? ? ? ? freqs.old = cpu_wp_tbl[cpu_wp_nr - 2].cpu_rate / 1000; ^^^^^^^^^^^^^ Use an enum for the various OP names instead of depending on an operating point to be at a certain place in the table. > + ? ? ? ? ? ? ? else > + ? ? ? ? ? ? ? ? ? ? ? freqs.old = arm_lpm_clk / 1000; > + > + ? ? ? ? ? ? ? freqs.new = freq_Hz / 1000; > + ? ? ? ? ? ? ? freqs.cpu = 0; > + ? ? ? ? ? ? ? freqs.flags = 0; > + ? ? ? ? ? ? ? cpufreq_notify_transition(&freqs, CPUFREQ_PRECHANGE); > + ? ? ? ? ? ? ? cpufreq_notify_transition(&freqs, CPUFREQ_POSTCHANGE); > + ? ? ? ? ? ? ? return ret; > + ? ? ? } > + > + ? ? ? cpufreq_frequency_table_target(policy, imx_freq_table, > + ? ? ? ? ? ? ? ? ? ? ? target_freq, relation, &index); > + ? ? ? freq_Hz = imx_freq_table[index].frequency * 1000; > + > + ? ? ? freqs.old = clk_get_rate(cpu_clk) / 1000; > + ? ? ? freqs.new = freq_Hz / 1000; > + ? ? ? freqs.cpu = 0; > + ? ? ? freqs.flags = 0; > + ? ? ? cpufreq_notify_transition(&freqs, CPUFREQ_PRECHANGE); > + > + ? ? ? if (freqs.old != freqs.new) > + ? ? ? ? ? ? ? ret = set_cpu_freq(freq_Hz); > + > + ? ? ? cpufreq_notify_transition(&freqs, CPUFREQ_POSTCHANGE); > + > + ? ? ? return ret; > +} > + > +static int __init mxc_cpufreq_init(struct cpufreq_policy *policy) > +{ > + ? ? ? int ret; > + ? ? ? int i; > + > + ? ? ? printk(KERN_INFO "i.MXC CPU frequency driver\n"); > + > + ? ? ? if (policy->cpu != 0) > + ? ? ? ? ? ? ? return -EINVAL; > + > + ? ? ? cpu_clk = clk_get(NULL, "cpu_clk"); > + ? ? ? if (IS_ERR(cpu_clk)) { > + ? ? ? ? ? ? ? printk(KERN_ERR "%s: failed to get cpu clock\n", __func__); > + ? ? ? ? ? ? ? return PTR_ERR(cpu_clk); > + ? ? ? } > + > + ? ? ? /* Set the current working point. */ > + ? ? ? cpu_wp_tbl = get_cpu_wp(&cpu_wp_nr); > + > + ? ? ? cpu_freq_khz_min = cpu_wp_tbl[0].cpu_rate / 1000; > + ? ? ? cpu_freq_khz_max = cpu_wp_tbl[0].cpu_rate / 1000; > + > + ? ? ? imx_freq_table = kmalloc( > + ? ? ? ? ? ? ? sizeof(struct cpufreq_frequency_table) * (cpu_wp_nr + 1), > + ? ? ? ? ? ? ? ? ? ? ? GFP_KERNEL); > + > + ? ? ? for (i = 0; i < cpu_wp_nr; i++) { > + ? ? ? ? ? ? ? imx_freq_table[i].index = i; > + ? ? ? ? ? ? ? imx_freq_table[i].frequency = > + ? ? ? ? ? ? ? ? ? cpu_wp_tbl[i].cpu_rate / 1000; > + > + ? ? ? ? ? ? ? if ((cpu_wp_tbl[i].cpu_rate / 1000) < cpu_freq_khz_min) > + ? ? ? ? ? ? ? ? ? ? ? cpu_freq_khz_min = cpu_wp_tbl[i].cpu_rate / 1000; > + > + ? ? ? ? ? ? ? if ((cpu_wp_tbl[i].cpu_rate / 1000) > cpu_freq_khz_max) > + ? ? ? ? ? ? ? ? ? ? ? cpu_freq_khz_max = cpu_wp_tbl[i].cpu_rate / 1000; > + ? ? ? } > + > + ? ? ? imx_freq_table[i].index = i; > + ? ? ? imx_freq_table[i].frequency = CPUFREQ_TABLE_END; > + > + ? ? ? policy->cur = clk_get_rate(cpu_clk) / 1000; > + ? ? ? policy->governor = CPUFREQ_DEFAULT_GOVERNOR; > + ? ? ? policy->min = policy->cpuinfo.min_freq = cpu_freq_khz_min; > + ? ? ? policy->max = policy->cpuinfo.max_freq = cpu_freq_khz_max; > + > + ? ? ? arm_lpm_clk = cpu_freq_khz_min * 1000; > + ? ? ? arm_normal_clk = cpu_freq_khz_max * 1000; > + > + ? ? ? /* Manual states, that PLL stabilizes in two CLK32 periods */ > + ? ? ? policy->cpuinfo.transition_latency = 10; > + > + ? ? ? ret = cpufreq_frequency_table_cpuinfo(policy, imx_freq_table); > + > + ? ? ? if (ret < 0) { > + ? ? ? ? ? ? ? clk_put(cpu_clk); > + ? ? ? ? ? ? ? printk(KERN_ERR "%s: failed to register i.MXC CPUfreq\n", > + ? ? ? ? ? ? ? ? ? ? ?__func__); > + ? ? ? ? ? ? ? return ret; > + ? ? ? } > + > + ? ? ? cpufreq_frequency_table_get_attr(imx_freq_table, policy->cpu); > + ? ? ? return 0; > +} > + > +static int mxc_cpufreq_exit(struct cpufreq_policy *policy) > +{ > + ? ? ? cpufreq_frequency_table_put_attr(policy->cpu); > + > + ? ? ? /* Reset CPU to 665MHz */ > + ? ? ? set_cpu_freq(arm_normal_clk); > + ? ? ? clk_put(cpu_clk); > + ? ? ? return 0; > +} > + > +static struct cpufreq_driver mxc_driver = { > + ? ? ? .flags = CPUFREQ_STICKY, > + ? ? ? .verify = mxc_verify_speed, > + ? ? ? .target = mxc_set_target, > + ? ? ? .get = mxc_get_speed, > + ? ? ? .init = mxc_cpufreq_init, > + ? ? ? .exit = mxc_cpufreq_exit, > + ? ? ? .name = "imx", > +}; > + > +static int __devinit mxc_cpufreq_driver_init(void) > +{ > + ? ? ? return cpufreq_register_driver(&mxc_driver); > +} > + > +static void mxc_cpufreq_driver_exit(void) > +{ > + ? ? ? cpufreq_unregister_driver(&mxc_driver); > +} > + > +module_init(mxc_cpufreq_driver_init); > +module_exit(mxc_cpufreq_driver_exit); > + > +MODULE_AUTHOR("Freescale Semiconductor Inc. Yong Shen <yong.shen@linaro.org>"); > +MODULE_DESCRIPTION("CPUfreq driver for i.MX"); > +MODULE_LICENSE("GPL"); > diff --git a/arch/arm/plat-mxc/include/mach/mxc.h b/arch/arm/plat-mxc/include/mach/mxc.h > index a790bf2..31df991 100644 > --- a/arch/arm/plat-mxc/include/mach/mxc.h > +++ b/arch/arm/plat-mxc/include/mach/mxc.h > @@ -1,5 +1,5 @@ > ?/* > - * Copyright 2004-2007 Freescale Semiconductor, Inc. All Rights Reserved. > + * Copyright 2004-2010 Freescale Semiconductor, Inc. All Rights Reserved. Copyright 2004-2007,2010 ? > ?* Copyright (C) 2008 Juergen Beisert (kernel at pengutronix.de) > ?* > ?* This program is free software; you can redistribute it and/or > @@ -133,6 +133,24 @@ extern unsigned int __mxc_cpu_type; > ?# define cpu_is_mxc91231() ? ? (0) > ?#endif > > +#ifndef __ASSEMBLY__ > + > +struct cpu_wp { s/cpu_wp/mx51_cpu_wp > + ? ? ? u32 pll_reg; > + ? ? ? u32 pll_rate; > + ? ? ? u32 cpu_rate; > + ? ? ? u32 pdr0_reg; > + ? ? ? u32 pdf; > + ? ? ? u32 mfi; > + ? ? ? u32 mfd; > + ? ? ? u32 mfn; > + ? ? ? u32 cpu_voltage; > + ? ? ? u32 cpu_podf; > +}; > + > +extern struct cpu_wp *(*get_cpu_wp)(int *wp); > +#endif > + > ?#if defined(CONFIG_ARCH_MX3) || defined(CONFIG_ARCH_MX2) > ?/* These are deprecated, use mx[23][157]_setup_weimcs instead. */ > ?#define CSCR_U(n) (IO_ADDRESS(WEIM_BASE_ADDR + n * 0x10)) > -- > 1.6.3.3 ^ permalink raw reply [flat|nested] 11+ messages in thread
* [PATCHv2] cpufreq for freescale mx51 2010-10-13 10:38 ` Amit Kucheria @ 2010-10-18 5:46 ` Yong Shen 0 siblings, 0 replies; 11+ messages in thread From: Yong Shen @ 2010-10-18 5:46 UTC (permalink / raw) To: linux-arm-kernel Hi Amit, I agree about all the comments. New patch is coming. Yong On Wed, Oct 13, 2010 at 6:38 PM, Amit Kucheria <amit.kucheria@linaro.org>wrote: > Yong, > > Some more comments. But the patch is looking good now. > > On Fri, Oct 8, 2010 at 11:08 AM, <yong.shen@linaro.org> wrote: > > From: Yong Shen <yong.shen@linaro.org> > > > > it is tested on babbage 3.0 > > Change to > > "Cpufreq driver for imx51. The operating points are currently tested > on babbage 3.0." > > > Signed-off-by: Yong Shen <yong.shen@linaro.org> > > --- > > arch/arm/Kconfig | 6 + > > arch/arm/mach-mx5/Kconfig | 1 + > > arch/arm/mach-mx5/Makefile | 1 + > > arch/arm/mach-mx5/board-mx51_babbage.c | 12 ++- > > arch/arm/mach-mx5/clock-mx51.c | 24 ++++ > > arch/arm/mach-mx5/cpu.c | 2 + > > arch/arm/mach-mx5/cpu_wp-mx51.c | 42 ++++++ > > arch/arm/mach-mx5/cpu_wp-mx51.h | 14 ++ > > arch/arm/plat-mxc/Makefile | 2 + > > arch/arm/plat-mxc/cpufreq.c | 236 > ++++++++++++++++++++++++++++++++ > > arch/arm/plat-mxc/include/mach/mxc.h | 20 +++- > > 11 files changed, 358 insertions(+), 2 deletions(-) > > create mode 100644 arch/arm/mach-mx5/cpu_wp-mx51.c > > create mode 100644 arch/arm/mach-mx5/cpu_wp-mx51.h > > create mode 100644 arch/arm/plat-mxc/cpufreq.c > > > > diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig > > index d203b84..71a572a 100644 > > --- a/arch/arm/Kconfig > > +++ b/arch/arm/Kconfig > > @@ -1690,6 +1690,12 @@ if ARCH_HAS_CPUFREQ > > > > source "drivers/cpufreq/Kconfig" > > > > +config CPU_FREQ_IMX > > + tristate "CPUfreq driver for i.MX CPUs" > > + depends on ARCH_MXC && CPU_FREQ > > + help > > + This enables the CPUfreq driver for i.MX CPUs. > > + > > config CPU_FREQ_SA1100 > > bool > > > > diff --git a/arch/arm/mach-mx5/Kconfig b/arch/arm/mach-mx5/Kconfig > > index 0848db5..7a621b4 100644 > > --- a/arch/arm/mach-mx5/Kconfig > > +++ b/arch/arm/mach-mx5/Kconfig > > @@ -5,6 +5,7 @@ config ARCH_MX51 > > default y > > select MXC_TZIC > > select ARCH_MXC_IOMUX_V3 > > + select ARCH_HAS_CPUFREQ > > > > comment "MX5 platforms:" > > > > diff --git a/arch/arm/mach-mx5/Makefile b/arch/arm/mach-mx5/Makefile > > index 86c66e7..e2af3fb 100644 > > --- a/arch/arm/mach-mx5/Makefile > > +++ b/arch/arm/mach-mx5/Makefile > > @@ -5,6 +5,7 @@ > > # Object file lists. > > obj-y := cpu.o mm.o clock-mx51.o devices.o > > > > +obj-$(CONFIG_CPU_FREQ_IMX) += cpu_wp-mx51.o > > s/wp/op/ ? > > Operating point is a more widely used word for frequency/voltage pairs > in the ARM world. We will also want to consider using the OPP library > currently being discussed elsewhere on LAKML. So change all instances > of working point or wp to operating point or op. > > > obj-$(CONFIG_MACH_MX51_BABBAGE) += board-mx51_babbage.o > > obj-$(CONFIG_MACH_MX51_3DS) += board-mx51_3ds.o > > obj-$(CONFIG_MACH_EUKREA_CPUIMX51) += board-cpuimx51.o > > diff --git a/arch/arm/mach-mx5/board-mx51_babbage.c > b/arch/arm/mach-mx5/board-mx51_babbage.c > > index 6e384d9..2d2a052 100644 > > --- a/arch/arm/mach-mx5/board-mx51_babbage.c > > +++ b/arch/arm/mach-mx5/board-mx51_babbage.c > > @@ -1,5 +1,5 @@ > > /* > > - * Copyright 2009 Freescale Semiconductor, Inc. All Rights Reserved. > > + * Copyright 2009-2010 Freescale Semiconductor, Inc. All Rights > Reserved. > > * Copyright (C) 2009-2010 Amit Kucheria <amit.kucheria@canonical.com> > > * > > * The code contained herein is licensed under the GNU General Public > > @@ -32,6 +32,7 @@ > > #include <asm/mach/time.h> > > > > #include "devices.h" > > +#include "cpu_wp-mx51.h" > > > > #define BABBAGE_USB_HUB_RESET (0*32 + 7) /* GPIO_1_7 */ > > #define BABBAGE_USBH1_STP (0*32 + 27) /* GPIO_1_27 */ > > @@ -279,8 +280,17 @@ static struct sys_timer mxc_timer = { > > .init = mx51_babbage_timer_init, > > }; > > > > +static void __init fixup_mxc_board(struct machine_desc *desc, struct tag > *tags, > > + char **cmdline, struct meminfo *mi) > > +{ > > +#if defined(CONFIG_CPU_FREQ_IMX) > > + get_cpu_wp = mx51_get_cpu_wp; > > s/wp/op > > > +#endif > > +} > > + > > MACHINE_START(MX51_BABBAGE, "Freescale MX51 Babbage Board") > > /* Maintainer: Amit Kucheria <amit.kucheria@canonical.com> */ > > + .fixup = fixup_mxc_board, > > .phys_io = MX51_AIPS1_BASE_ADDR, > > .io_pg_offst = ((MX51_AIPS1_BASE_ADDR_VIRT) >> 18) & 0xfffc, > > .boot_params = PHYS_OFFSET + 0x100, > > diff --git a/arch/arm/mach-mx5/clock-mx51.c > b/arch/arm/mach-mx5/clock-mx51.c > > index 6af69de..f23cfab 100644 > > --- a/arch/arm/mach-mx5/clock-mx51.c > > +++ b/arch/arm/mach-mx5/clock-mx51.c > > @@ -39,6 +39,8 @@ static struct clk ahb_clk; > > static struct clk ipg_clk; > > static struct clk usboh3_clk; > > > > +DEFINE_SPINLOCK(clk_lock); > > + > > #define MAX_DPLL_WAIT_TRIES 1000 /* 1000 * udelay(1) = 1ms */ > > > > static int _clk_ccgr_enable(struct clk *clk) > > @@ -342,6 +344,26 @@ static unsigned long clk_arm_get_rate(struct clk > *clk) > > return parent_rate / div; > > } > > > > +static int _clk_cpu_set_rate(struct clk *clk, unsigned long rate) > > +{ > > + u32 reg, cpu_podf; > > + unsigned long flags, parent_rate; > > + > > + parent_rate = clk_get_rate(clk->parent); > > + cpu_podf = parent_rate / rate - 1; > > + /* use post divider to change freq */ > > + spin_lock_irqsave(&clk_lock, flags); > > + > > + reg = __raw_readl(MXC_CCM_CACRR); > > + reg &= ~MXC_CCM_CACRR_ARM_PODF_MASK; > > + reg |= cpu_podf << MXC_CCM_CACRR_ARM_PODF_OFFSET; > > + __raw_writel(reg, MXC_CCM_CACRR); > > + > > + spin_unlock_irqrestore(&clk_lock, flags); > > + > > + return 0; > > +} > > + > > static int _clk_periph_apm_set_parent(struct clk *clk, struct clk > *parent) > > { > > u32 reg, mux; > > @@ -694,6 +716,7 @@ static struct clk periph_apm_clk = { > > static struct clk cpu_clk = { > > .parent = &pll1_sw_clk, > > .get_rate = clk_arm_get_rate, > > + .set_rate = _clk_cpu_set_rate, > > }; > > > > static struct clk ahb_clk = { > > @@ -837,6 +860,7 @@ static struct clk_lookup lookups[] = { > > _REGISTER_CLOCK("fsl-usb2-udc", "usb", usboh3_clk) > > _REGISTER_CLOCK("fsl-usb2-udc", "usb_ahb", ahb_clk) > > _REGISTER_CLOCK("imx-keypad.0", NULL, kpp_clk) > > + _REGISTER_CLOCK(NULL, "cpu_clk", cpu_clk) > > }; > > > > static void clk_tree_init(void) > > diff --git a/arch/arm/mach-mx5/cpu.c b/arch/arm/mach-mx5/cpu.c > > index 2d37785..83add9c 100644 > > --- a/arch/arm/mach-mx5/cpu.c > > +++ b/arch/arm/mach-mx5/cpu.c > > @@ -22,6 +22,8 @@ static int cpu_silicon_rev = -1; > > > > #define SI_REV 0x48 > > > > +struct cpu_wp *(*get_cpu_wp)(int *wp); > > + > > s/wp/op > > > static void query_silicon_parameter(void) > > { > > void __iomem *rom = ioremap(MX51_IROM_BASE_ADDR, MX51_IROM_SIZE); > > diff --git a/arch/arm/mach-mx5/cpu_wp-mx51.c > b/arch/arm/mach-mx5/cpu_wp-mx51.c > > new file mode 100644 > > index 0000000..51bde45 > > --- /dev/null > > +++ b/arch/arm/mach-mx5/cpu_wp-mx51.c > > filename s/wp/op > > > @@ -0,0 +1,42 @@ > > +/* > > + * Copyright (C) 2010 Freescale Semiconductor, Inc. All Rights Reserved. > > + */ > > + > > +/* > > + * The code contained herein is licensed under the GNU General Public > > + * License. You may obtain a copy of the GNU General Public License > > + * Version 2 or later at the following locations: > > + * > > + * http://www.opensource.org/licenses/gpl-license.html > > + * http://www.gnu.org/copyleft/gpl.html > > + */ > > + > > +#include <linux/types.h> > > +#include <mach/hardware.h> > > + > > +static struct cpu_wp cpu_wp_auto[] = { > > s/cpu_wp/mx51_cpu_wp > > This also makes this struct explicity to mx51. > > > + { > > + .pll_rate = 800000000, > > + .cpu_rate = 160000000, > > + .pdf = 4, > > + .mfi = 8, > > + .mfd = 2, > > + .mfn = 1, > > + .cpu_podf = 4, > > + .cpu_voltage = 850000,}, > > + { > > + .pll_rate = 800000000, > > + .cpu_rate = 800000000, > > + .pdf = 0, > > + .mfi = 8, > > + .mfd = 2, > > + .mfn = 1, > > + .cpu_podf = 0, > > + .cpu_voltage = 1100000,}, > > +}; > > Except for cpu_rate and cpu_voltage, I don't see the other fields > being used anywhere. > > > +struct cpu_wp *mx51_get_cpu_wp(int *wp) > > +{ > > + *wp = sizeof(cpu_wp_auto) / sizeof(struct cpu_wp); > > + return cpu_wp_auto; > > +} > > s/wp/op > > > diff --git a/arch/arm/mach-mx5/cpu_wp-mx51.h > b/arch/arm/mach-mx5/cpu_wp-mx51.h > > new file mode 100644 > > index 0000000..8038b62 > > --- /dev/null > > +++ b/arch/arm/mach-mx5/cpu_wp-mx51.h > > @@ -0,0 +1,14 @@ > > +/* > > + * Copyright (C) 2010 Freescale Semiconductor, Inc. All Rights Reserved. > > + */ > > + > > +/* > > + * The code contained herein is licensed under the GNU General Public > > + * License. You may obtain a copy of the GNU General Public License > > + * Version 2 or later at the following locations: > > + * > > + * http://www.opensource.org/licenses/gpl-license.html > > + * http://www.gnu.org/copyleft/gpl.html > > + */ > > + > > +extern struct cpu_wp *mx51_get_cpu_wp(int *wp); > > diff --git a/arch/arm/plat-mxc/Makefile b/arch/arm/plat-mxc/Makefile > > index 78d405e..0b8464f 100644 > > --- a/arch/arm/plat-mxc/Makefile > > +++ b/arch/arm/plat-mxc/Makefile > > @@ -16,6 +16,8 @@ obj-$(CONFIG_MXC_ULPI) += ulpi.o > > obj-$(CONFIG_ARCH_MXC_AUDMUX_V1) += audmux-v1.o > > obj-$(CONFIG_ARCH_MXC_AUDMUX_V2) += audmux-v2.o > > obj-$(CONFIG_MXC_DEBUG_BOARD) += 3ds_debugboard.o > > +# CPU FREQ support > > +obj-$(CONFIG_CPU_FREQ_IMX) += cpufreq.o > > ifdef CONFIG_SND_IMX_SOC > > obj-y += ssi-fiq.o > > obj-y += ssi-fiq-ksym.o > > diff --git a/arch/arm/plat-mxc/cpufreq.c b/arch/arm/plat-mxc/cpufreq.c > > new file mode 100644 > > index 0000000..9990ea8 > > --- /dev/null > > +++ b/arch/arm/plat-mxc/cpufreq.c > > @@ -0,0 +1,236 @@ > > +/* > > + * Copyright (C) 2010 Freescale Semiconductor, Inc. All Rights Reserved. > > + */ > > + > > +/* > > + * The code contained herein is licensed under the GNU General Public > > + * License. You may obtain a copy of the GNU General Public License > > + * Version 2 or later at the following locations: > > + * > > + * http://www.opensource.org/licenses/gpl-license.html > > + * http://www.gnu.org/copyleft/gpl.html > > + */ > > + > > +/* > > + * A driver for the Freescale Semiconductor i.MXC CPUfreq module. > > + * The CPUFREQ driver is for controling CPU frequency. It allows you to > change > > + * the CPU clock speed on the fly. > > + */ > > + > > +#include <linux/types.h> > > +#include <linux/kernel.h> > > +#include <linux/cpufreq.h> > > +#include <linux/init.h> > > +#include <linux/proc_fs.h> > > +#include <linux/regulator/consumer.h> > > +#include <linux/clk.h> > > +#include <linux/delay.h> > > +#include <linux/io.h> > > +#include <mach/hardware.h> > > +#include <asm/setup.h> > > +#include <mach/clock.h> > > +#include <asm/cacheflush.h> > > +#include <linux/hrtimer.h> > > + > > +static int cpu_freq_khz_min; > > +static int cpu_freq_khz_max; > > +static int arm_lpm_clk; > > +static int arm_normal_clk; > > +static int cpufreq_suspended; > > + > > +static struct clk *cpu_clk; > > +static struct cpufreq_frequency_table *imx_freq_table; > > + > > +static int cpu_wp_nr; > > +static struct cpu_wp *cpu_wp_tbl; > > + > > +static int set_cpu_freq(int freq) > > +{ > > + int ret = 0; > > + int org_cpu_rate; > > + int gp_volt = 0; > > + int i; > > + > > + org_cpu_rate = clk_get_rate(cpu_clk); > > + if (org_cpu_rate == freq) > > + return ret; > > + > > + for (i = 0; i < cpu_wp_nr; i++) { > > + if (freq == cpu_wp_tbl[i].cpu_rate) > > + gp_volt = cpu_wp_tbl[i].cpu_voltage; > > + } > > + > > + if (gp_volt == 0) > > + return ret; > > + > > + ret = clk_set_rate(cpu_clk, freq); > > + if (ret != 0) { > > + printk(KERN_DEBUG "cannot set CPU clock rate\n"); > > + return ret; > > + } > > + > > + return ret; > > +} > > + > > +static int mxc_verify_speed(struct cpufreq_policy *policy) > > +{ > > + if (policy->cpu != 0) > > + return -EINVAL; > > + > > + return cpufreq_frequency_table_verify(policy, imx_freq_table); > > +} > > + > > +static unsigned int mxc_get_speed(unsigned int cpu) > > +{ > > + if (cpu) > > + return 0; > > + > > + return clk_get_rate(cpu_clk) / 1000; > > +} > > + > > +static int mxc_set_target(struct cpufreq_policy *policy, > > + unsigned int target_freq, unsigned int > relation) > > +{ > > + struct cpufreq_freqs freqs; > > + int freq_Hz; > > + int ret = 0; > > + unsigned int index; > > + > > + if (cpufreq_suspended) { > > + target_freq = clk_get_rate(cpu_clk) / 1000; > > + cpufreq_frequency_table_target(policy, imx_freq_table, > > + target_freq, relation, &index); > > + freq_Hz = imx_freq_table[index].frequency * 1000; > > + > > + if (freq_Hz == arm_lpm_clk) > > + freqs.old = cpu_wp_tbl[cpu_wp_nr - 2].cpu_rate / > 1000; > ^^^^^^^^^^^^^ > Use an enum for the various OP names instead of depending on an > operating point to be at a certain place in the table. > > > + else > > + freqs.old = arm_lpm_clk / 1000; > > + > > + freqs.new = freq_Hz / 1000; > > + freqs.cpu = 0; > > + freqs.flags = 0; > > + cpufreq_notify_transition(&freqs, CPUFREQ_PRECHANGE); > > + cpufreq_notify_transition(&freqs, CPUFREQ_POSTCHANGE); > > + return ret; > > + } > > + > > + cpufreq_frequency_table_target(policy, imx_freq_table, > > + target_freq, relation, &index); > > + freq_Hz = imx_freq_table[index].frequency * 1000; > > + > > + freqs.old = clk_get_rate(cpu_clk) / 1000; > > + freqs.new = freq_Hz / 1000; > > + freqs.cpu = 0; > > + freqs.flags = 0; > > + cpufreq_notify_transition(&freqs, CPUFREQ_PRECHANGE); > > + > > + if (freqs.old != freqs.new) > > + ret = set_cpu_freq(freq_Hz); > > + > > + cpufreq_notify_transition(&freqs, CPUFREQ_POSTCHANGE); > > + > > + return ret; > > +} > > + > > +static int __init mxc_cpufreq_init(struct cpufreq_policy *policy) > > +{ > > + int ret; > > + int i; > > + > > + printk(KERN_INFO "i.MXC CPU frequency driver\n"); > > + > > + if (policy->cpu != 0) > > + return -EINVAL; > > + > > + cpu_clk = clk_get(NULL, "cpu_clk"); > > + if (IS_ERR(cpu_clk)) { > > + printk(KERN_ERR "%s: failed to get cpu clock\n", > __func__); > > + return PTR_ERR(cpu_clk); > > + } > > + > > + /* Set the current working point. */ > > + cpu_wp_tbl = get_cpu_wp(&cpu_wp_nr); > > + > > + cpu_freq_khz_min = cpu_wp_tbl[0].cpu_rate / 1000; > > + cpu_freq_khz_max = cpu_wp_tbl[0].cpu_rate / 1000; > > + > > + imx_freq_table = kmalloc( > > + sizeof(struct cpufreq_frequency_table) * (cpu_wp_nr + 1), > > + GFP_KERNEL); > > + > > + for (i = 0; i < cpu_wp_nr; i++) { > > + imx_freq_table[i].index = i; > > + imx_freq_table[i].frequency = > > + cpu_wp_tbl[i].cpu_rate / 1000; > > + > > + if ((cpu_wp_tbl[i].cpu_rate / 1000) < cpu_freq_khz_min) > > + cpu_freq_khz_min = cpu_wp_tbl[i].cpu_rate / 1000; > > + > > + if ((cpu_wp_tbl[i].cpu_rate / 1000) > cpu_freq_khz_max) > > + cpu_freq_khz_max = cpu_wp_tbl[i].cpu_rate / 1000; > > + } > > + > > + imx_freq_table[i].index = i; > > + imx_freq_table[i].frequency = CPUFREQ_TABLE_END; > > + > > + policy->cur = clk_get_rate(cpu_clk) / 1000; > > + policy->governor = CPUFREQ_DEFAULT_GOVERNOR; > > + policy->min = policy->cpuinfo.min_freq = cpu_freq_khz_min; > > + policy->max = policy->cpuinfo.max_freq = cpu_freq_khz_max; > > + > > + arm_lpm_clk = cpu_freq_khz_min * 1000; > > + arm_normal_clk = cpu_freq_khz_max * 1000; > > + > > + /* Manual states, that PLL stabilizes in two CLK32 periods */ > > + policy->cpuinfo.transition_latency = 10; > > + > > + ret = cpufreq_frequency_table_cpuinfo(policy, imx_freq_table); > > + > > + if (ret < 0) { > > + clk_put(cpu_clk); > > + printk(KERN_ERR "%s: failed to register i.MXC CPUfreq\n", > > + __func__); > > + return ret; > > + } > > + > > + cpufreq_frequency_table_get_attr(imx_freq_table, policy->cpu); > > + return 0; > > +} > > + > > +static int mxc_cpufreq_exit(struct cpufreq_policy *policy) > > +{ > > + cpufreq_frequency_table_put_attr(policy->cpu); > > + > > + /* Reset CPU to 665MHz */ > > + set_cpu_freq(arm_normal_clk); > > + clk_put(cpu_clk); > > + return 0; > > +} > > + > > +static struct cpufreq_driver mxc_driver = { > > + .flags = CPUFREQ_STICKY, > > + .verify = mxc_verify_speed, > > + .target = mxc_set_target, > > + .get = mxc_get_speed, > > + .init = mxc_cpufreq_init, > > + .exit = mxc_cpufreq_exit, > > + .name = "imx", > > +}; > > + > > +static int __devinit mxc_cpufreq_driver_init(void) > > +{ > > + return cpufreq_register_driver(&mxc_driver); > > +} > > + > > +static void mxc_cpufreq_driver_exit(void) > > +{ > > + cpufreq_unregister_driver(&mxc_driver); > > +} > > + > > +module_init(mxc_cpufreq_driver_init); > > +module_exit(mxc_cpufreq_driver_exit); > > + > > +MODULE_AUTHOR("Freescale Semiconductor Inc. Yong Shen < > yong.shen at linaro.org>"); > > +MODULE_DESCRIPTION("CPUfreq driver for i.MX"); > > +MODULE_LICENSE("GPL"); > > diff --git a/arch/arm/plat-mxc/include/mach/mxc.h > b/arch/arm/plat-mxc/include/mach/mxc.h > > index a790bf2..31df991 100644 > > --- a/arch/arm/plat-mxc/include/mach/mxc.h > > +++ b/arch/arm/plat-mxc/include/mach/mxc.h > > @@ -1,5 +1,5 @@ > > /* > > - * Copyright 2004-2007 Freescale Semiconductor, Inc. All Rights > Reserved. > > + * Copyright 2004-2010 Freescale Semiconductor, Inc. All Rights > Reserved. > > Copyright 2004-2007,2010 ? > > > * Copyright (C) 2008 Juergen Beisert (kernel at pengutronix.de) > > * > > * This program is free software; you can redistribute it and/or > > @@ -133,6 +133,24 @@ extern unsigned int __mxc_cpu_type; > > # define cpu_is_mxc91231() (0) > > #endif > > > > +#ifndef __ASSEMBLY__ > > + > > +struct cpu_wp { > > s/cpu_wp/mx51_cpu_wp > > > + u32 pll_reg; > > + u32 pll_rate; > > + u32 cpu_rate; > > + u32 pdr0_reg; > > + u32 pdf; > > + u32 mfi; > > + u32 mfd; > > + u32 mfn; > > + u32 cpu_voltage; > > + u32 cpu_podf; > > +}; > > + > > +extern struct cpu_wp *(*get_cpu_wp)(int *wp); > > +#endif > > + > > #if defined(CONFIG_ARCH_MX3) || defined(CONFIG_ARCH_MX2) > > /* These are deprecated, use mx[23][157]_setup_weimcs instead. */ > > #define CSCR_U(n) (IO_ADDRESS(WEIM_BASE_ADDR + n * 0x10)) > > -- > > 1.6.3.3 > -------------- next part -------------- An HTML attachment was scrubbed... URL: <http://lists.infradead.org/pipermail/linux-arm-kernel/attachments/20101018/640ebc87/attachment-0001.html> ^ permalink raw reply [flat|nested] 11+ messages in thread
* [PATCHv2] cpufreq for freescale mx51 2010-10-08 8:08 ` [PATCHv2] " yong.shen at linaro.org 2010-10-13 10:38 ` Amit Kucheria @ 2010-10-13 17:54 ` Sascha Hauer 2010-10-18 5:43 ` Yong Shen 1 sibling, 1 reply; 11+ messages in thread From: Sascha Hauer @ 2010-10-13 17:54 UTC (permalink / raw) To: linux-arm-kernel On Fri, Oct 08, 2010 at 04:08:27PM +0800, yong.shen at linaro.org wrote: > From: Yong Shen <yong.shen@linaro.org> > > it is tested on babbage 3.0 > > Signed-off-by: Yong Shen <yong.shen@linaro.org> > --- > arch/arm/Kconfig | 6 + > arch/arm/mach-mx5/Kconfig | 1 + > arch/arm/mach-mx5/Makefile | 1 + > arch/arm/mach-mx5/board-mx51_babbage.c | 12 ++- > arch/arm/mach-mx5/clock-mx51.c | 24 ++++ > arch/arm/mach-mx5/cpu.c | 2 + > arch/arm/mach-mx5/cpu_wp-mx51.c | 42 ++++++ > arch/arm/mach-mx5/cpu_wp-mx51.h | 14 ++ > arch/arm/plat-mxc/Makefile | 2 + > arch/arm/plat-mxc/cpufreq.c | 236 ++++++++++++++++++++++++++++++++ > arch/arm/plat-mxc/include/mach/mxc.h | 20 +++- > 11 files changed, 358 insertions(+), 2 deletions(-) > create mode 100644 arch/arm/mach-mx5/cpu_wp-mx51.c > create mode 100644 arch/arm/mach-mx5/cpu_wp-mx51.h > create mode 100644 arch/arm/plat-mxc/cpufreq.c > > diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig > index d203b84..71a572a 100644 > --- a/arch/arm/Kconfig > +++ b/arch/arm/Kconfig > @@ -1690,6 +1690,12 @@ if ARCH_HAS_CPUFREQ > > source "drivers/cpufreq/Kconfig" > > +config CPU_FREQ_IMX > + tristate "CPUfreq driver for i.MX CPUs" > + depends on ARCH_MXC && CPU_FREQ > + help > + This enables the CPUfreq driver for i.MX CPUs. > + > config CPU_FREQ_SA1100 > bool > > diff --git a/arch/arm/mach-mx5/Kconfig b/arch/arm/mach-mx5/Kconfig > index 0848db5..7a621b4 100644 > --- a/arch/arm/mach-mx5/Kconfig > +++ b/arch/arm/mach-mx5/Kconfig > @@ -5,6 +5,7 @@ config ARCH_MX51 > default y > select MXC_TZIC > select ARCH_MXC_IOMUX_V3 > + select ARCH_HAS_CPUFREQ > > comment "MX5 platforms:" > > diff --git a/arch/arm/mach-mx5/Makefile b/arch/arm/mach-mx5/Makefile > index 86c66e7..e2af3fb 100644 > --- a/arch/arm/mach-mx5/Makefile > +++ b/arch/arm/mach-mx5/Makefile > @@ -5,6 +5,7 @@ > # Object file lists. > obj-y := cpu.o mm.o clock-mx51.o devices.o > > +obj-$(CONFIG_CPU_FREQ_IMX) += cpu_wp-mx51.o > obj-$(CONFIG_MACH_MX51_BABBAGE) += board-mx51_babbage.o > obj-$(CONFIG_MACH_MX51_3DS) += board-mx51_3ds.o > obj-$(CONFIG_MACH_EUKREA_CPUIMX51) += board-cpuimx51.o > diff --git a/arch/arm/mach-mx5/board-mx51_babbage.c b/arch/arm/mach-mx5/board-mx51_babbage.c > index 6e384d9..2d2a052 100644 > --- a/arch/arm/mach-mx5/board-mx51_babbage.c > +++ b/arch/arm/mach-mx5/board-mx51_babbage.c > @@ -1,5 +1,5 @@ > /* > - * Copyright 2009 Freescale Semiconductor, Inc. All Rights Reserved. > + * Copyright 2009-2010 Freescale Semiconductor, Inc. All Rights Reserved. > * Copyright (C) 2009-2010 Amit Kucheria <amit.kucheria@canonical.com> > * > * The code contained herein is licensed under the GNU General Public > @@ -32,6 +32,7 @@ > #include <asm/mach/time.h> > > #include "devices.h" > +#include "cpu_wp-mx51.h" > > #define BABBAGE_USB_HUB_RESET (0*32 + 7) /* GPIO_1_7 */ > #define BABBAGE_USBH1_STP (0*32 + 27) /* GPIO_1_27 */ > @@ -279,8 +280,17 @@ static struct sys_timer mxc_timer = { > .init = mx51_babbage_timer_init, > }; > > +static void __init fixup_mxc_board(struct machine_desc *desc, struct tag *tags, > + char **cmdline, struct meminfo *mi) > +{ > +#if defined(CONFIG_CPU_FREQ_IMX) > + get_cpu_wp = mx51_get_cpu_wp; > +#endif > +} > + > MACHINE_START(MX51_BABBAGE, "Freescale MX51 Babbage Board") > /* Maintainer: Amit Kucheria <amit.kucheria@canonical.com> */ > + .fixup = fixup_mxc_board, > .phys_io = MX51_AIPS1_BASE_ADDR, > .io_pg_offst = ((MX51_AIPS1_BASE_ADDR_VIRT) >> 18) & 0xfffc, > .boot_params = PHYS_OFFSET + 0x100, > diff --git a/arch/arm/mach-mx5/clock-mx51.c b/arch/arm/mach-mx5/clock-mx51.c > index 6af69de..f23cfab 100644 > --- a/arch/arm/mach-mx5/clock-mx51.c > +++ b/arch/arm/mach-mx5/clock-mx51.c > @@ -39,6 +39,8 @@ static struct clk ahb_clk; > static struct clk ipg_clk; > static struct clk usboh3_clk; > > +DEFINE_SPINLOCK(clk_lock); static, if needed at all. > + > #define MAX_DPLL_WAIT_TRIES 1000 /* 1000 * udelay(1) = 1ms */ > > static int _clk_ccgr_enable(struct clk *clk) > @@ -342,6 +344,26 @@ static unsigned long clk_arm_get_rate(struct clk *clk) > return parent_rate / div; > } > > +static int _clk_cpu_set_rate(struct clk *clk, unsigned long rate) > +{ > + u32 reg, cpu_podf; > + unsigned long flags, parent_rate; > + > + parent_rate = clk_get_rate(clk->parent); > + cpu_podf = parent_rate / rate - 1; > + /* use post divider to change freq */ > + spin_lock_irqsave(&clk_lock, flags); > + > + reg = __raw_readl(MXC_CCM_CACRR); > + reg &= ~MXC_CCM_CACRR_ARM_PODF_MASK; > + reg |= cpu_podf << MXC_CCM_CACRR_ARM_PODF_OFFSET; > + __raw_writel(reg, MXC_CCM_CACRR); > + > + spin_unlock_irqrestore(&clk_lock, flags); Why this spinlock? The whole clock code is protected by a mutex already. > + > + return 0; > +} > + > static int _clk_periph_apm_set_parent(struct clk *clk, struct clk *parent) > { > u32 reg, mux; > @@ -694,6 +716,7 @@ static struct clk periph_apm_clk = { > static struct clk cpu_clk = { > .parent = &pll1_sw_clk, > .get_rate = clk_arm_get_rate, > + .set_rate = _clk_cpu_set_rate, The names should be consistent. Either clk_arm_[sg]et_rate or clk_cpu. ALso, please remove those leading underscores. > }; > > static struct clk ahb_clk = { > @@ -837,6 +860,7 @@ static struct clk_lookup lookups[] = { > _REGISTER_CLOCK("fsl-usb2-udc", "usb", usboh3_clk) > _REGISTER_CLOCK("fsl-usb2-udc", "usb_ahb", ahb_clk) > _REGISTER_CLOCK("imx-keypad.0", NULL, kpp_clk) > + _REGISTER_CLOCK(NULL, "cpu_clk", cpu_clk) > }; > > static void clk_tree_init(void) > diff --git a/arch/arm/mach-mx5/cpu.c b/arch/arm/mach-mx5/cpu.c > index 2d37785..83add9c 100644 > --- a/arch/arm/mach-mx5/cpu.c > +++ b/arch/arm/mach-mx5/cpu.c > @@ -22,6 +22,8 @@ static int cpu_silicon_rev = -1; > > #define SI_REV 0x48 > > +struct cpu_wp *(*get_cpu_wp)(int *wp); > + This is not needed. > static void query_silicon_parameter(void) > { > void __iomem *rom = ioremap(MX51_IROM_BASE_ADDR, MX51_IROM_SIZE); > diff --git a/arch/arm/mach-mx5/cpu_wp-mx51.c b/arch/arm/mach-mx5/cpu_wp-mx51.c > new file mode 100644 > index 0000000..51bde45 > --- /dev/null > +++ b/arch/arm/mach-mx5/cpu_wp-mx51.c > @@ -0,0 +1,42 @@ > +/* > + * Copyright (C) 2010 Freescale Semiconductor, Inc. All Rights Reserved. > + */ > + > +/* > + * The code contained herein is licensed under the GNU General Public > + * License. You may obtain a copy of the GNU General Public License > + * Version 2 or later at the following locations: > + * > + * http://www.opensource.org/licenses/gpl-license.html > + * http://www.gnu.org/copyleft/gpl.html > + */ > + > +#include <linux/types.h> > +#include <mach/hardware.h> > + > +static struct cpu_wp cpu_wp_auto[] = { > + { > + .pll_rate = 800000000, > + .cpu_rate = 160000000, > + .pdf = 4, > + .mfi = 8, > + .mfd = 2, > + .mfn = 1, > + .cpu_podf = 4, > + .cpu_voltage = 850000,}, > + { > + .pll_rate = 800000000, > + .cpu_rate = 800000000, > + .pdf = 0, > + .mfi = 8, > + .mfd = 2, > + .mfn = 1, > + .cpu_podf = 0, > + .cpu_voltage = 1100000,}, > +}; > + > +struct cpu_wp *mx51_get_cpu_wp(int *wp) > +{ > + *wp = sizeof(cpu_wp_auto) / sizeof(struct cpu_wp); use ARRAY_SIZE here. > + return cpu_wp_auto; > +} > diff --git a/arch/arm/mach-mx5/cpu_wp-mx51.h b/arch/arm/mach-mx5/cpu_wp-mx51.h > new file mode 100644 > index 0000000..8038b62 > --- /dev/null > +++ b/arch/arm/mach-mx5/cpu_wp-mx51.h > @@ -0,0 +1,14 @@ > +/* > + * Copyright (C) 2010 Freescale Semiconductor, Inc. All Rights Reserved. > + */ > + > +/* > + * The code contained herein is licensed under the GNU General Public > + * License. You may obtain a copy of the GNU General Public License > + * Version 2 or later at the following locations: > + * > + * http://www.opensource.org/licenses/gpl-license.html > + * http://www.gnu.org/copyleft/gpl.html > + */ > + > +extern struct cpu_wp *mx51_get_cpu_wp(int *wp); > diff --git a/arch/arm/plat-mxc/Makefile b/arch/arm/plat-mxc/Makefile > index 78d405e..0b8464f 100644 > --- a/arch/arm/plat-mxc/Makefile > +++ b/arch/arm/plat-mxc/Makefile > @@ -16,6 +16,8 @@ obj-$(CONFIG_MXC_ULPI) += ulpi.o > obj-$(CONFIG_ARCH_MXC_AUDMUX_V1) += audmux-v1.o > obj-$(CONFIG_ARCH_MXC_AUDMUX_V2) += audmux-v2.o > obj-$(CONFIG_MXC_DEBUG_BOARD) += 3ds_debugboard.o > +# CPU FREQ support > +obj-$(CONFIG_CPU_FREQ_IMX) += cpufreq.o I think most people digging in kernel code get that without the comment. > ifdef CONFIG_SND_IMX_SOC > obj-y += ssi-fiq.o > obj-y += ssi-fiq-ksym.o > diff --git a/arch/arm/plat-mxc/cpufreq.c b/arch/arm/plat-mxc/cpufreq.c > new file mode 100644 > index 0000000..9990ea8 > --- /dev/null > +++ b/arch/arm/plat-mxc/cpufreq.c > @@ -0,0 +1,236 @@ > +/* > + * Copyright (C) 2010 Freescale Semiconductor, Inc. All Rights Reserved. > + */ > + > +/* > + * The code contained herein is licensed under the GNU General Public > + * License. You may obtain a copy of the GNU General Public License > + * Version 2 or later at the following locations: > + * > + * http://www.opensource.org/licenses/gpl-license.html > + * http://www.gnu.org/copyleft/gpl.html > + */ > + > +/* > + * A driver for the Freescale Semiconductor i.MXC CPUfreq module. > + * The CPUFREQ driver is for controling CPU frequency. It allows you to change > + * the CPU clock speed on the fly. > + */ > + > +#include <linux/types.h> > +#include <linux/kernel.h> > +#include <linux/cpufreq.h> > +#include <linux/init.h> > +#include <linux/proc_fs.h> > +#include <linux/regulator/consumer.h> > +#include <linux/clk.h> > +#include <linux/delay.h> > +#include <linux/io.h> > +#include <mach/hardware.h> > +#include <asm/setup.h> > +#include <mach/clock.h> > +#include <asm/cacheflush.h> > +#include <linux/hrtimer.h> Please clean up the includes. At least linux/proc_fs.h, linux/regulator/consumer.h, asm/cacheflush.h and linux/hrtimer.h are unused. > + > +static int cpu_freq_khz_min; > +static int cpu_freq_khz_max; > +static int arm_lpm_clk; > +static int arm_normal_clk; > +static int cpufreq_suspended; > + > +static struct clk *cpu_clk; > +static struct cpufreq_frequency_table *imx_freq_table; > + > +static int cpu_wp_nr; > +static struct cpu_wp *cpu_wp_tbl; > + > +static int set_cpu_freq(int freq) > +{ > + int ret = 0; > + int org_cpu_rate; > + int gp_volt = 0; > + int i; > + > + org_cpu_rate = clk_get_rate(cpu_clk); > + if (org_cpu_rate == freq) > + return ret; > + > + for (i = 0; i < cpu_wp_nr; i++) { > + if (freq == cpu_wp_tbl[i].cpu_rate) > + gp_volt = cpu_wp_tbl[i].cpu_voltage; > + } > + > + if (gp_volt == 0) > + return ret; I can't see why we need this check here. This can only happen when the table is not correctly initialized. Furthermore, cpu voltages are not handled yet. I'm sure we'll find a better place for this check once we start supporting voltage changes, so I think you can remove it here. > + > + ret = clk_set_rate(cpu_clk, freq); > + if (ret != 0) { > + printk(KERN_DEBUG "cannot set CPU clock rate\n"); > + return ret; > + } > + > + return ret; > +} > + > +static int mxc_verify_speed(struct cpufreq_policy *policy) > +{ > + if (policy->cpu != 0) > + return -EINVAL; > + > + return cpufreq_frequency_table_verify(policy, imx_freq_table); > +} > + > +static unsigned int mxc_get_speed(unsigned int cpu) > +{ > + if (cpu) > + return 0; > + > + return clk_get_rate(cpu_clk) / 1000; > +} > + > +static int mxc_set_target(struct cpufreq_policy *policy, > + unsigned int target_freq, unsigned int relation) > +{ > + struct cpufreq_freqs freqs; > + int freq_Hz; > + int ret = 0; > + unsigned int index; > + > + if (cpufreq_suspended) { cpufreq_suspended is never set in this code, so please remove this until we get a user for this. > + target_freq = clk_get_rate(cpu_clk) / 1000; > + cpufreq_frequency_table_target(policy, imx_freq_table, > + target_freq, relation, &index); > + freq_Hz = imx_freq_table[index].frequency * 1000; > + > + if (freq_Hz == arm_lpm_clk) > + freqs.old = cpu_wp_tbl[cpu_wp_nr - 2].cpu_rate / 1000; > + else > + freqs.old = arm_lpm_clk / 1000; > + > + freqs.new = freq_Hz / 1000; > + freqs.cpu = 0; > + freqs.flags = 0; > + cpufreq_notify_transition(&freqs, CPUFREQ_PRECHANGE); > + cpufreq_notify_transition(&freqs, CPUFREQ_POSTCHANGE); > + return ret; > + } > + > + cpufreq_frequency_table_target(policy, imx_freq_table, > + target_freq, relation, &index); > + freq_Hz = imx_freq_table[index].frequency * 1000; > + > + freqs.old = clk_get_rate(cpu_clk) / 1000; > + freqs.new = freq_Hz / 1000; > + freqs.cpu = 0; > + freqs.flags = 0; > + cpufreq_notify_transition(&freqs, CPUFREQ_PRECHANGE); > + > + if (freqs.old != freqs.new) > + ret = set_cpu_freq(freq_Hz); > + > + cpufreq_notify_transition(&freqs, CPUFREQ_POSTCHANGE); > + > + return ret; > +} > + > +static int __init mxc_cpufreq_init(struct cpufreq_policy *policy) > +{ > + int ret; > + int i; > + > + printk(KERN_INFO "i.MXC CPU frequency driver\n"); > + > + if (policy->cpu != 0) > + return -EINVAL; > + > + cpu_clk = clk_get(NULL, "cpu_clk"); > + if (IS_ERR(cpu_clk)) { > + printk(KERN_ERR "%s: failed to get cpu clock\n", __func__); > + return PTR_ERR(cpu_clk); > + } > + > + /* Set the current working point. */ > + cpu_wp_tbl = get_cpu_wp(&cpu_wp_nr); The comment seems to have nothing to do with what the code does. You don't set a workpoint here but get the table of available workpoints. > + > + cpu_freq_khz_min = cpu_wp_tbl[0].cpu_rate / 1000; > + cpu_freq_khz_max = cpu_wp_tbl[0].cpu_rate / 1000; > + > + imx_freq_table = kmalloc( > + sizeof(struct cpufreq_frequency_table) * (cpu_wp_nr + 1), > + GFP_KERNEL); kmalloc can fail. > + > + for (i = 0; i < cpu_wp_nr; i++) { > + imx_freq_table[i].index = i; > + imx_freq_table[i].frequency = > + cpu_wp_tbl[i].cpu_rate / 1000; > + > + if ((cpu_wp_tbl[i].cpu_rate / 1000) < cpu_freq_khz_min) > + cpu_freq_khz_min = cpu_wp_tbl[i].cpu_rate / 1000; > + > + if ((cpu_wp_tbl[i].cpu_rate / 1000) > cpu_freq_khz_max) > + cpu_freq_khz_max = cpu_wp_tbl[i].cpu_rate / 1000; > + } > + > + imx_freq_table[i].index = i; > + imx_freq_table[i].frequency = CPUFREQ_TABLE_END; > + > + policy->cur = clk_get_rate(cpu_clk) / 1000; > + policy->governor = CPUFREQ_DEFAULT_GOVERNOR; > + policy->min = policy->cpuinfo.min_freq = cpu_freq_khz_min; > + policy->max = policy->cpuinfo.max_freq = cpu_freq_khz_max; > + > + arm_lpm_clk = cpu_freq_khz_min * 1000; > + arm_normal_clk = cpu_freq_khz_max * 1000; > + > + /* Manual states, that PLL stabilizes in two CLK32 periods */ > + policy->cpuinfo.transition_latency = 10; transition_latency is in nano seconds, the correct value here is 2/32768*1000*1000*1000 = 61035. > + > + ret = cpufreq_frequency_table_cpuinfo(policy, imx_freq_table); > + > + if (ret < 0) { > + clk_put(cpu_clk); > + printk(KERN_ERR "%s: failed to register i.MXC CPUfreq\n", > + __func__); It's always good to print the error code with such messages. > + return ret; > + } > + > + cpufreq_frequency_table_get_attr(imx_freq_table, policy->cpu); > + return 0; > +} > + > +static int mxc_cpufreq_exit(struct cpufreq_policy *policy) > +{ > + cpufreq_frequency_table_put_attr(policy->cpu); > + > + /* Reset CPU to 665MHz */ > + set_cpu_freq(arm_normal_clk); arm_normal_clk is initialized to cpu_freq_khz_max * 1000 and never touched again. It would be clearer here to remove arm_normal_clk and write cpu_freq_khz_max * 1000 directly here. Then you can also remove the comment which is wrong for most i.MXs, even for the i.MX51. Also, you forget to kfree the resources alloced above. > + clk_put(cpu_clk); > + return 0; > +} > + > +static struct cpufreq_driver mxc_driver = { > + .flags = CPUFREQ_STICKY, > + .verify = mxc_verify_speed, > + .target = mxc_set_target, > + .get = mxc_get_speed, > + .init = mxc_cpufreq_init, > + .exit = mxc_cpufreq_exit, > + .name = "imx", > +}; > + > +static int __devinit mxc_cpufreq_driver_init(void) > +{ > + return cpufreq_register_driver(&mxc_driver); > +} > + > +static void mxc_cpufreq_driver_exit(void) > +{ > + cpufreq_unregister_driver(&mxc_driver); > +} > + > +module_init(mxc_cpufreq_driver_init); > +module_exit(mxc_cpufreq_driver_exit); > + > +MODULE_AUTHOR("Freescale Semiconductor Inc. Yong Shen <yong.shen@linaro.org>"); > +MODULE_DESCRIPTION("CPUfreq driver for i.MX"); > +MODULE_LICENSE("GPL"); > diff --git a/arch/arm/plat-mxc/include/mach/mxc.h b/arch/arm/plat-mxc/include/mach/mxc.h > index a790bf2..31df991 100644 > --- a/arch/arm/plat-mxc/include/mach/mxc.h > +++ b/arch/arm/plat-mxc/include/mach/mxc.h > @@ -1,5 +1,5 @@ > /* > - * Copyright 2004-2007 Freescale Semiconductor, Inc. All Rights Reserved. > + * Copyright 2004-2010 Freescale Semiconductor, Inc. All Rights Reserved. > * Copyright (C) 2008 Juergen Beisert (kernel at pengutronix.de) > * > * This program is free software; you can redistribute it and/or > @@ -133,6 +133,24 @@ extern unsigned int __mxc_cpu_type; > # define cpu_is_mxc91231() (0) > #endif > > +#ifndef __ASSEMBLY__ > + > +struct cpu_wp { > + u32 pll_reg; > + u32 pll_rate; > + u32 cpu_rate; > + u32 pdr0_reg; > + u32 pdf; > + u32 mfi; > + u32 mfd; > + u32 mfn; > + u32 cpu_voltage; > + u32 cpu_podf; > +}; > + > +extern struct cpu_wp *(*get_cpu_wp)(int *wp); > +#endif > + > #if defined(CONFIG_ARCH_MX3) || defined(CONFIG_ARCH_MX2) > /* These are deprecated, use mx[23][157]_setup_weimcs instead. */ > #define CSCR_U(n) (IO_ADDRESS(WEIM_BASE_ADDR + n * 0x10)) > -- > 1.6.3.3 > > > _______________________________________________ > linux-arm-kernel mailing list > linux-arm-kernel at lists.infradead.org > http://lists.infradead.org/mailman/listinfo/linux-arm-kernel > -- Pengutronix e.K. | | Industrial Linux Solutions | http://www.pengutronix.de/ | Peiner Str. 6-8, 31137 Hildesheim, Germany | Phone: +49-5121-206917-0 | Amtsgericht Hildesheim, HRA 2686 | Fax: +49-5121-206917-5555 | ^ permalink raw reply [flat|nested] 11+ messages in thread
* [PATCHv2] cpufreq for freescale mx51 2010-10-13 17:54 ` Sascha Hauer @ 2010-10-18 5:43 ` Yong Shen 2010-10-18 8:31 ` Sascha Hauer 0 siblings, 1 reply; 11+ messages in thread From: Yong Shen @ 2010-10-18 5:43 UTC (permalink / raw) To: linux-arm-kernel Hi Sascha, Thanks for your thorough review. I have two feedbacks to your commends. Sorry for delayed response, cause I had a hard time due to my computer crash and data loss. > diff --git a/arch/arm/mach-mx5/cpu.c b/arch/arm/mach-mx5/cpu.c > > index 2d37785..83add9c 100644 > > --- a/arch/arm/mach-mx5/cpu.c > > +++ b/arch/arm/mach-mx5/cpu.c > > @@ -22,6 +22,8 @@ static int cpu_silicon_rev = -1; > > > > #define SI_REV 0x48 > > > > +struct cpu_wp *(*get_cpu_wp)(int *wp); > > + > > This is not needed. > > This is needed, otherwise it does not pass compile. > > > + return ret; > > + } > > + > > + cpufreq_frequency_table_get_attr(imx_freq_table, policy->cpu); > > + return 0; > > +} > > + > > +static int mxc_cpufreq_exit(struct cpufreq_policy *policy) > > +{ > > + cpufreq_frequency_table_put_attr(policy->cpu); > > + > > + /* Reset CPU to 665MHz */ > > + set_cpu_freq(arm_normal_clk); > > arm_normal_clk is initialized to cpu_freq_khz_max * 1000 and never touched > again. It would be clearer here to remove arm_normal_clk and write > cpu_freq_khz_max * 1000 directly here. Then you can also remove the > comment which is wrong for most i.MXs, even for the i.MX51. > > Actually, this is arm_normal_clk is touched later in mxc_cpufreq_exit() fuction. Yong -------------- next part -------------- An HTML attachment was scrubbed... URL: <http://lists.infradead.org/pipermail/linux-arm-kernel/attachments/20101018/9e3e23a6/attachment-0001.html> ^ permalink raw reply [flat|nested] 11+ messages in thread
* [PATCHv2] cpufreq for freescale mx51 2010-10-18 5:43 ` Yong Shen @ 2010-10-18 8:31 ` Sascha Hauer 2010-10-18 9:08 ` Yong Shen 0 siblings, 1 reply; 11+ messages in thread From: Sascha Hauer @ 2010-10-18 8:31 UTC (permalink / raw) To: linux-arm-kernel Hi Yong, On Mon, Oct 18, 2010 at 01:43:43PM +0800, Yong Shen wrote: > Hi Sascha, > > Thanks for your thorough review. I have two feedbacks to your commends. > Sorry for delayed response, cause I had a hard time due to my computer crash > and data loss. > > > diff --git a/arch/arm/mach-mx5/cpu.c b/arch/arm/mach-mx5/cpu.c > > > index 2d37785..83add9c 100644 > > > --- a/arch/arm/mach-mx5/cpu.c > > > +++ b/arch/arm/mach-mx5/cpu.c > > > @@ -22,6 +22,8 @@ static int cpu_silicon_rev = -1; > > > > > > #define SI_REV 0x48 > > > > > > +struct cpu_wp *(*get_cpu_wp)(int *wp); > > > + > > > > This is not needed. > > > This is needed, otherwise it does not pass compile. This hunk is the only change to arch/arm/mach-mx5/cpu.c and get_cpu_wp is introduced with this patch, so how can this break compilation? Also, you should move this to a header file. Otherwise you risk of having multiple (and possibly different) declarations of the same function which can lead to hard to find bugs. > > > > > > + return ret; > > > + } > > > + > > > + cpufreq_frequency_table_get_attr(imx_freq_table, policy->cpu); > > > + return 0; > > > +} > > > + > > > +static int mxc_cpufreq_exit(struct cpufreq_policy *policy) > > > +{ > > > + cpufreq_frequency_table_put_attr(policy->cpu); > > > + > > > + /* Reset CPU to 665MHz */ > > > + set_cpu_freq(arm_normal_clk); > > > > arm_normal_clk is initialized to cpu_freq_khz_max * 1000 and never touched > > again. It would be clearer here to remove arm_normal_clk and write > > cpu_freq_khz_max * 1000 directly here. Then you can also remove the > > comment which is wrong for most i.MXs, even for the i.MX51. > > > > > Actually, this is arm_normal_clk is touched later in mxc_cpufreq_exit() > fuction. It's *read* but not changed. That's why I suggested to just remove arm_normal_clk and instead do a set_cpu_freq(cpu_freq_khz_max * 1000) in mxc_cpufreq_exit. Sascha -- Pengutronix e.K. | | Industrial Linux Solutions | http://www.pengutronix.de/ | Peiner Str. 6-8, 31137 Hildesheim, Germany | Phone: +49-5121-206917-0 | Amtsgericht Hildesheim, HRA 2686 | Fax: +49-5121-206917-5555 | ^ permalink raw reply [flat|nested] 11+ messages in thread
* [PATCHv2] cpufreq for freescale mx51 2010-10-18 8:31 ` Sascha Hauer @ 2010-10-18 9:08 ` Yong Shen 2010-10-18 9:31 ` Sascha Hauer 0 siblings, 1 reply; 11+ messages in thread From: Yong Shen @ 2010-10-18 9:08 UTC (permalink / raw) To: linux-arm-kernel Hi Sascha, On Mon, Oct 18, 2010 at 4:31 PM, Sascha Hauer <s.hauer@pengutronix.de>wrote: > Hi Yong, > > On Mon, Oct 18, 2010 at 01:43:43PM +0800, Yong Shen wrote: > > Hi Sascha, > > > > Thanks for your thorough review. I have two feedbacks to your commends. > > Sorry for delayed response, cause I had a hard time due to my computer > crash > > and data loss. > > > > > diff --git a/arch/arm/mach-mx5/cpu.c b/arch/arm/mach-mx5/cpu.c > > > > index 2d37785..83add9c 100644 > > > > --- a/arch/arm/mach-mx5/cpu.c > > > > +++ b/arch/arm/mach-mx5/cpu.c > > > > @@ -22,6 +22,8 @@ static int cpu_silicon_rev = -1; > > > > > > > > #define SI_REV 0x48 > > > > > > > > +struct cpu_wp *(*get_cpu_wp)(int *wp); > > > > + > > > > > > This is not needed. > > > > > This is needed, otherwise it does not pass compile. > > This hunk is the only change to arch/arm/mach-mx5/cpu.c and get_cpu_wp > is introduced with this patch, so how can this break compilation? > Also, you should move this to a header file. Otherwise you risk of > having multiple (and possibly different) declarations of the same > function which can lead to hard to find bugs. > IMHO, get_cpu_wp is definition rather than a declaration and without it there will be errors in link phase. its declaration is in arch/arm/plat-mxc/include/mach/mxc.h. -------------- next part -------------- An HTML attachment was scrubbed... URL: <http://lists.infradead.org/pipermail/linux-arm-kernel/attachments/20101018/d4c26487/attachment-0001.html> ^ permalink raw reply [flat|nested] 11+ messages in thread
* [PATCHv2] cpufreq for freescale mx51 2010-10-18 9:08 ` Yong Shen @ 2010-10-18 9:31 ` Sascha Hauer 2010-10-19 8:31 ` Yong Shen 0 siblings, 1 reply; 11+ messages in thread From: Sascha Hauer @ 2010-10-18 9:31 UTC (permalink / raw) To: linux-arm-kernel On Mon, Oct 18, 2010 at 05:08:14PM +0800, Yong Shen wrote: > Hi Sascha, > > > On Mon, Oct 18, 2010 at 4:31 PM, Sascha Hauer <s.hauer@pengutronix.de>wrote: > > > Hi Yong, > > > > On Mon, Oct 18, 2010 at 01:43:43PM +0800, Yong Shen wrote: > > > Hi Sascha, > > > > > > Thanks for your thorough review. I have two feedbacks to your commends. > > > Sorry for delayed response, cause I had a hard time due to my computer > > crash > > > and data loss. > > > > > > > diff --git a/arch/arm/mach-mx5/cpu.c b/arch/arm/mach-mx5/cpu.c > > > > > index 2d37785..83add9c 100644 > > > > > --- a/arch/arm/mach-mx5/cpu.c > > > > > +++ b/arch/arm/mach-mx5/cpu.c > > > > > @@ -22,6 +22,8 @@ static int cpu_silicon_rev = -1; > > > > > > > > > > #define SI_REV 0x48 > > > > > > > > > > +struct cpu_wp *(*get_cpu_wp)(int *wp); > > > > > + > > > > > > > > This is not needed. > > > > > > > This is needed, otherwise it does not pass compile. > > > > This hunk is the only change to arch/arm/mach-mx5/cpu.c and get_cpu_wp > > is introduced with this patch, so how can this break compilation? > > Also, you should move this to a header file. Otherwise you risk of > > having multiple (and possibly different) declarations of the same > > function which can lead to hard to find bugs. > > > IMHO, get_cpu_wp is definition rather than a declaration and without it > there will be errors in link phase. its declaration is in > arch/arm/plat-mxc/include/mach/mxc.h. Of course, you are right, my bad. Isn't this function common to al i.MXs? In this case it should be somewhere in plat-mxc. Anyway, it seems very odd to me to provide a global function pointer which gets overwritten by the boards. For me it is more logical to provide a mxc_register_workpoints() function along with a mxc_for_each_workpoint() iterator. Sascha -- Pengutronix e.K. | | Industrial Linux Solutions | http://www.pengutronix.de/ | Peiner Str. 6-8, 31137 Hildesheim, Germany | Phone: +49-5121-206917-0 | Amtsgericht Hildesheim, HRA 2686 | Fax: +49-5121-206917-5555 | ^ permalink raw reply [flat|nested] 11+ messages in thread
* [PATCHv2] cpufreq for freescale mx51 2010-10-18 9:31 ` Sascha Hauer @ 2010-10-19 8:31 ` Yong Shen 0 siblings, 0 replies; 11+ messages in thread From: Yong Shen @ 2010-10-19 8:31 UTC (permalink / raw) To: linux-arm-kernel > > > > > > > > > > > > > > +struct cpu_wp *(*get_cpu_wp)(int *wp); > > > > > > + > > > > > > > > > > This is not needed. > > > > > > > > > This is needed, otherwise it does not pass compile. > > > > > > This hunk is the only change to arch/arm/mach-mx5/cpu.c and get_cpu_wp > > > is introduced with this patch, so how can this break compilation? > > > Also, you should move this to a header file. Otherwise you risk of > > > having multiple (and possibly different) declarations of the same > > > function which can lead to hard to find bugs. > > > > > IMHO, get_cpu_wp is definition rather than a declaration and without it > > there will be errors in link phase. its declaration is in > > arch/arm/plat-mxc/include/mach/mxc.h. > > Of course, you are right, my bad. Isn't this function common to al > i.MXs? In this case it should be somewhere in plat-mxc. > Anyway, it seems very odd to me to provide a global function pointer > which gets overwritten by the boards. For me it is more logical to > provide a mxc_register_workpoints() function along with a > mxc_for_each_workpoint() iterator. > > About this, I am thinking move the global function pointer to plat-mxc/cpufreq.c and add protection before call the function pointer. Anyway, it is just about provide cpu operating points to cpufreq driver, right? Even, mxc_register_workpoints() has to go some where in cpufreq.c and export a declaration in a head file, also it needs to be called before cpufreq initialization. IMO, it's merely a preference of individuals. No offence about that, correct me:) -------------- next part -------------- An HTML attachment was scrubbed... URL: <http://lists.infradead.org/pipermail/linux-arm-kernel/attachments/20101019/505075ec/attachment-0001.html> ^ permalink raw reply [flat|nested] 11+ messages in thread
[parent not found: <1285738417-1638-1-git-send-email-yong.shen@linaro.org>]
* [PATCH] cpufreq for freescale mx51 [not found] <1285738417-1638-1-git-send-email-yong.shen@linaro.org> @ 2010-09-30 10:48 ` Amit Kucheria 2010-10-06 9:51 ` Sascha Hauer 0 siblings, 1 reply; 11+ messages in thread From: Amit Kucheria @ 2010-09-30 10:48 UTC (permalink / raw) To: linux-arm-kernel Add'ed linaro-dev and linux-arm-kernel to CC. Thanks Yong, some feeback follows inline. On 10 Sep 29, Yong Shen wrote: > From: Yong Shen <yong.shen@linaro.org> > > --- > arch/arm/Kconfig | 6 + > arch/arm/mach-mx5/Kconfig | 1 + > arch/arm/mach-mx5/board-mx51_babbage.c | 32 ++++ > arch/arm/mach-mx5/clock-mx51.c | 53 ++++++ > arch/arm/plat-mxc/Makefile | 2 + > arch/arm/plat-mxc/cpufreq.c | 282 ++++++++++++++++++++++++++++++++ > arch/arm/plat-mxc/include/mach/mxc.h | 20 +++ > 7 files changed, 396 insertions(+), 0 deletions(-) > create mode 100644 arch/arm/plat-mxc/cpufreq.c > > diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig > index 4db064e..64ebbc0 100644 > --- a/arch/arm/Kconfig > +++ b/arch/arm/Kconfig > @@ -1517,6 +1517,12 @@ if ARCH_HAS_CPUFREQ > > source "drivers/cpufreq/Kconfig" > > +config CPU_FREQ_IMX > + tristate "CPUfreq driver for i.MX CPUs" > + depends on ARCH_MXC && CPU_FREQ > + help > + This enables the CPUfreq driver for i.MX CPUs. > + > config CPU_FREQ_SA1100 > bool > > diff --git a/arch/arm/mach-mx5/Kconfig b/arch/arm/mach-mx5/Kconfig > index 1576d51..5956fee 100644 > --- a/arch/arm/mach-mx5/Kconfig > +++ b/arch/arm/mach-mx5/Kconfig > @@ -5,6 +5,7 @@ config ARCH_MX51 > default y > select MXC_TZIC > select ARCH_MXC_IOMUX_V3 > + select ARCH_HAS_CPUFREQ > > comment "MX5 platforms:" > > diff --git a/arch/arm/mach-mx5/board-mx51_babbage.c b/arch/arm/mach-mx5/board-mx51_babbage.c > index ed885f9..e449e0b 100644 > --- a/arch/arm/mach-mx5/board-mx51_babbage.c > +++ b/arch/arm/mach-mx5/board-mx51_babbage.c > @@ -43,6 +43,31 @@ > #define MX51_USB_PLL_DIV_19_2_MHZ 0x01 > #define MX51_USB_PLL_DIV_24_MHZ 0x02 > > +struct cpu_wp *(*get_cpu_wp)(int *wp); > +static int num_cpu_wp = 2; use sizeof(array) instead of hard coding this. > +/* working point(wp): 0 - 800MHz; 1 - 166.25MHz; */ > +static struct cpu_wp cpu_wp_auto[] = { > + { > + .pll_rate = 800000000, > + .cpu_rate = 800000000, > + .pdf = 0, > + .mfi = 8, > + .mfd = 2, > + .mfn = 1, > + .cpu_podf = 0, > + .cpu_voltage = 1100000,}, > + { > + .pll_rate = 800000000, > + .cpu_rate = 160000000, > + .pdf = 4, > + .mfi = 8, > + .mfd = 2, > + .mfn = 1, > + .cpu_podf = 4, > + .cpu_voltage = 850000,}, > +}; This data should be moved out to a separate file (e.g. mx51_ratetable.h) since it will be useful to other boards too. If other boards can have different rate tables (and they can, depending on the revision of the silicon), then we can either 'assemble' the correct rate table based on a flag field or have explicit separate rate tables for each silicon revision. In any case, I suspect that there will be some core rates that will be common across silicon revisions. > static struct platform_device *devices[] __initdata = { > &mxc_fec_device, > }; > @@ -87,6 +112,12 @@ static struct imxuart_platform_data uart_pdata = { > .flags = IMXUART_HAVE_RTSCTS, > }; > > +struct cpu_wp *mx51_babbage_get_cpu_wp(int *wp) > +{ > + *wp = num_cpu_wp; > + return cpu_wp_auto; > +} > + > static inline void mxc_init_imx_uart(void) > { > mxc_register_device(&mxc_uart_device0, &uart_pdata); > @@ -246,6 +277,7 @@ static void __init mxc_board_init(void) > > static void __init mx51_babbage_timer_init(void) > { > + get_cpu_wp = mx51_babbage_get_cpu_wp; > mx51_clocks_init(32768, 24000000, 22579200, 0); > } > > diff --git a/arch/arm/mach-mx5/clock-mx51.c b/arch/arm/mach-mx5/clock-mx51.c > index d9f612d..f2488e6 100644 > --- a/arch/arm/mach-mx5/clock-mx51.c > +++ b/arch/arm/mach-mx5/clock-mx51.c > @@ -14,6 +14,7 @@ > #include <linux/delay.h> > #include <linux/clk.h> > #include <linux/io.h> > +#include <linux/time.h> > > #include <asm/clkdev.h> > #include <asm/div64.h> > @@ -28,6 +29,11 @@ > static unsigned long external_high_reference, external_low_reference; > static unsigned long oscillator_reference, ckih2_reference; > > +extern struct cpu_wp *(*get_cpu_wp)(int *wp); > +static int cpu_wp_nr; > +static int cpu_curr_wp; > +static struct cpu_wp *cpu_wp_tbl; > + > static struct clk osc_clk; > static struct clk pll1_main_clk; > static struct clk pll1_sw_clk; > @@ -38,7 +44,9 @@ static struct clk periph_apm_clk; > static struct clk ahb_clk; > static struct clk ipg_clk; > static struct clk usboh3_clk; > +static void __iomem *pll1_base; > > +#define SPIN_DELAY 1000000 /* in nanoseconds */ > #define MAX_DPLL_WAIT_TRIES 1000 /* 1000 * udelay(1) = 1ms */ > > static int _clk_ccgr_enable(struct clk *clk) > @@ -330,6 +338,32 @@ static int _clk_lp_apm_set_parent(struct clk *clk, struct clk *parent) > return 0; > } > > +/*! > + * Setup cpu clock based on working point. > + * @param wp cpu freq working point > + * @return 0 on success or error code on failure. > + */ > +int cpu_clk_set_wp(int wp) > +{ > + struct cpu_wp *p; > + u32 reg; > + > + if (wp == cpu_curr_wp) > + return 0; > + > + p = &cpu_wp_tbl[wp]; > + > + /*use post divider to change freq > + */ > + reg = __raw_readl(MXC_CCM_CACRR); > + reg &= ~MXC_CCM_CACRR_ARM_PODF_MASK; > + reg |= cpu_wp_tbl[wp].cpu_podf << MXC_CCM_CACRR_ARM_PODF_OFFSET; > + __raw_writel(reg, MXC_CCM_CACRR); > + cpu_curr_wp = wp; > + > + return 0; > +} > + > static unsigned long clk_arm_get_rate(struct clk *clk) > { > u32 cacrr, div; > @@ -342,6 +376,20 @@ static unsigned long clk_arm_get_rate(struct clk *clk) > return parent_rate / div; > } > > +int _clk_cpu_set_rate(struct clk *clk, unsigned long rate) > +{ > + u32 i; > + for (i = 0; i < cpu_wp_nr; i++) { > + if (rate == cpu_wp_tbl[i].cpu_rate) > + break; > + } > + if (i >= cpu_wp_nr) > + return -EINVAL; > + cpu_clk_set_wp(i); > + > + return 0; > +} > + > static int _clk_periph_apm_set_parent(struct clk *clk, struct clk *parent) > { > u32 reg, mux; > @@ -694,6 +742,7 @@ static struct clk periph_apm_clk = { > static struct clk cpu_clk = { > .parent = &pll1_sw_clk, > .get_rate = clk_arm_get_rate, > + .set_rate = _clk_cpu_set_rate, > }; > > static struct clk ahb_clk = { > @@ -821,6 +870,7 @@ static struct clk_lookup lookups[] = { > _REGISTER_CLOCK("mxc-ehci.1", "usb_ahb", ahb_clk) > _REGISTER_CLOCK("fsl-usb2-udc", "usb", usboh3_clk) > _REGISTER_CLOCK("fsl-usb2-udc", "usb_ahb", ahb_clk) > + _REGISTER_CLOCK(NULL, "cpu_clk", cpu_clk) > }; > > static void clk_tree_init(void) > @@ -848,10 +898,13 @@ int __init mx51_clocks_init(unsigned long ckil, unsigned long osc, > { > int i; > > + pll1_base = ioremap(MX51_PLL1_BASE_ADDR, SZ_4K); > + > external_low_reference = ckil; > external_high_reference = ckih1; > ckih2_reference = ckih2; > oscillator_reference = osc; > + cpu_wp_tbl = get_cpu_wp(&cpu_wp_nr); > > for (i = 0; i < ARRAY_SIZE(lookups); i++) > clkdev_add(&lookups[i]); > diff --git a/arch/arm/plat-mxc/Makefile b/arch/arm/plat-mxc/Makefile > index 895bc3c..c1bb400 100644 > --- a/arch/arm/plat-mxc/Makefile > +++ b/arch/arm/plat-mxc/Makefile > @@ -17,6 +17,8 @@ obj-$(CONFIG_USB_EHCI_MXC) += ehci.o > obj-$(CONFIG_MXC_ULPI) += ulpi.o > obj-$(CONFIG_ARCH_MXC_AUDMUX_V1) += audmux-v1.o > obj-$(CONFIG_ARCH_MXC_AUDMUX_V2) += audmux-v2.o > +# CPU FREQ support > +obj-$(CONFIG_CPU_FREQ_IMX) += cpufreq.o > ifdef CONFIG_SND_IMX_SOC > obj-y += ssi-fiq.o > obj-y += ssi-fiq-ksym.o > diff --git a/arch/arm/plat-mxc/cpufreq.c b/arch/arm/plat-mxc/cpufreq.c > new file mode 100644 > index 0000000..cae40f1 > --- /dev/null > +++ b/arch/arm/plat-mxc/cpufreq.c > @@ -0,0 +1,282 @@ > +/* > + * Copyright (C) 2010 Freescale Semiconductor, Inc. All Rights Reserved. > + */ > + > +/* > + * The code contained herein is licensed under the GNU General Public > + * License. You may obtain a copy of the GNU General Public License > + * Version 2 or later at the following locations: > + * > + * http://www.opensource.org/licenses/gpl-license.html > + * http://www.gnu.org/copyleft/gpl.html > + */ > + > +/*! > + * @file cpufreq.c > + * > + * @brief A driver for the Freescale Semiconductor i.MXC CPUfreq module. > + * > + * The CPUFREQ driver is for controling CPU frequency. It allows you to change > + * the CPU clock speed on the fly. > + * > + * @ingroup PM > + */ Fix these comments to follow the kernel commenting style. > +#include <linux/types.h> > +#include <linux/kernel.h> > +#include <linux/cpufreq.h> > +#include <linux/init.h> > +#include <linux/proc_fs.h> > +#include <linux/regulator/consumer.h> > +#include <linux/clk.h> > +#include <linux/delay.h> > +#include <linux/io.h> > +#include <mach/hardware.h> > +#include <asm/setup.h> > +#include <mach/clock.h> > +#include <asm/cacheflush.h> > +#include <linux/hrtimer.h> > + > +int cpu_freq_khz_min; > +int cpu_freq_khz_max; > +int arm_lpm_clk; > +int arm_normal_clk; > +int cpufreq_suspended; > +int cpufreq_trig_needed; > + > +static struct clk *cpu_clk; > +static struct cpufreq_frequency_table imx_freq_table[4]; > + > +extern int set_low_bus_freq(void); > +extern int set_high_bus_freq(int high_bus_speed); > +extern int low_freq_bus_used(void); > + > +extern struct cpu_wp *(*get_cpu_wp)(int *wp); > +static int cpu_wp_nr; > +static struct cpu_wp *cpu_wp_tbl; > + > +int set_cpu_freq(int freq) > +{ > + int ret = 0; > + int org_cpu_rate; > + int gp_volt = 0; > + int i; > + > + org_cpu_rate = clk_get_rate(cpu_clk); > + if (org_cpu_rate == freq) > + return ret; > + > + for (i = 0; i < cpu_wp_nr; i++) { > + if (freq == cpu_wp_tbl[i].cpu_rate) > + gp_volt = cpu_wp_tbl[i].cpu_voltage; > + } > + > + if (gp_volt == 0) > + return ret; > + > + ret = clk_set_rate(cpu_clk, freq); > + if (ret != 0) { > + printk(KERN_DEBUG "cannot set CPU clock rate\n"); > + return ret; > + } > + > + return ret; > +} > + > +static int mxc_verify_speed(struct cpufreq_policy *policy) > +{ > + if (policy->cpu != 0) > + return -EINVAL; > + > + return cpufreq_frequency_table_verify(policy, imx_freq_table); > +} > + > +static unsigned int mxc_get_speed(unsigned int cpu) > +{ > + if (cpu) > + return 0; > + > + return clk_get_rate(cpu_clk) / 1000; > +} > + > +static int calc_frequency_khz(int target, unsigned int relation) > +{ > + int i; > + > + if ((target * 1000) == clk_get_rate(cpu_clk)) > + return target; > + > + if (relation == CPUFREQ_RELATION_H) { > + for (i = cpu_wp_nr - 1; i >= 0; i--) { > + if (imx_freq_table[i].frequency <= target) > + return imx_freq_table[i].frequency; > + } > + } else if (relation == CPUFREQ_RELATION_L) { > + for (i = 0; i < cpu_wp_nr; i++) { > + if (imx_freq_table[i].frequency >= target) > + return imx_freq_table[i].frequency; > + } > + } > + printk(KERN_ERR "Error: No valid cpufreq relation\n"); > + return cpu_freq_khz_max; > +} > + > +static int mxc_set_target(struct cpufreq_policy *policy, > + unsigned int target_freq, unsigned int relation) > +{ > + struct cpufreq_freqs freqs; > + int freq_Hz; > + int ret = 0; > + > + if (cpufreq_suspended) { > + target_freq = clk_get_rate(cpu_clk) / 1000; > + freq_Hz = calc_frequency_khz(target_freq, relation) * 1000; > + if (freq_Hz == arm_lpm_clk) > + freqs.old = cpu_wp_tbl[cpu_wp_nr - 2].cpu_rate / 1000; > + else > + freqs.old = arm_lpm_clk / 1000; > + > + freqs.new = freq_Hz / 1000; > + freqs.cpu = 0; > + freqs.flags = 0; > + cpufreq_notify_transition(&freqs, CPUFREQ_PRECHANGE); > + cpufreq_notify_transition(&freqs, CPUFREQ_POSTCHANGE); > + return ret; > + } > + /* > + * Some governors do not respects CPU and policy lower limits > + * which leads to bad things (division by zero etc), ensure > + * that such things do not happen. > + */ Isn't that a bug in the governor? Can you explain a bit? > + if (target_freq < policy->cpuinfo.min_freq) > + target_freq = policy->cpuinfo.min_freq; > + > + if (target_freq < policy->min) > + target_freq = policy->min; > + > + freq_Hz = calc_frequency_khz(target_freq, relation) * 1000; > + > + freqs.old = clk_get_rate(cpu_clk) / 1000; > + freqs.new = freq_Hz / 1000; > + freqs.cpu = 0; > + freqs.flags = 0; > + cpufreq_notify_transition(&freqs, CPUFREQ_PRECHANGE); > + > + if (freqs.old != freqs.new) > + ret = set_cpu_freq(freq_Hz); > + > + cpufreq_notify_transition(&freqs, CPUFREQ_POSTCHANGE); > + > + return ret; > +} > + > +static int __init mxc_cpufreq_driver_init(struct cpufreq_policy *policy) > +{ > + int ret; > + int i; > + > + printk(KERN_INFO "i.MXC CPU frequency driver\n"); > + > + if (policy->cpu != 0) > + return -EINVAL; > + > + cpu_clk = clk_get(NULL, "cpu_clk"); > + if (IS_ERR(cpu_clk)) { > + printk(KERN_ERR "%s: failed to get cpu clock\n", __func__); > + return PTR_ERR(cpu_clk); > + } > + > + /* Set the current working point. */ > + cpu_wp_tbl = get_cpu_wp(&cpu_wp_nr); > + > + cpu_freq_khz_min = cpu_wp_tbl[0].cpu_rate / 1000; > + cpu_freq_khz_max = cpu_wp_tbl[0].cpu_rate / 1000; > + > + for (i = 0; i < cpu_wp_nr; i++) { > + imx_freq_table[cpu_wp_nr - 1 - i].index = cpu_wp_nr - i; cpu_wp_nr = 2 here 1st iteration of for loop: imx_freq_table[2 - 1 - 0].index = 2 - 0 so, imx_freq_table[1].index = 2 2nd iteration: imx_freq_table[2 - 1 - 1].index = 2 - 1 imx_freq_table[0].index = 1 So you're trying to reverse the table order? Why not just sort the table date in the way you want and add a comment on the top to keep it sorted. > + imx_freq_table[cpu_wp_nr - 1 - i].frequency = > + cpu_wp_tbl[i].cpu_rate / 1000; > + > + if ((cpu_wp_tbl[i].cpu_rate / 1000) < cpu_freq_khz_min) > + cpu_freq_khz_min = cpu_wp_tbl[i].cpu_rate / 1000; > + > + if ((cpu_wp_tbl[i].cpu_rate / 1000) > cpu_freq_khz_max) > + cpu_freq_khz_max = cpu_wp_tbl[i].cpu_rate / 1000; > + } > + > + imx_freq_table[i].index = 0; imx_freq_table[i].index = i ? > + imx_freq_table[i].frequency = CPUFREQ_TABLE_END; > + > + policy->cur = clk_get_rate(cpu_clk) / 1000; > + policy->governor = CPUFREQ_DEFAULT_GOVERNOR; > + policy->min = policy->cpuinfo.min_freq = cpu_freq_khz_min; > + policy->max = policy->cpuinfo.max_freq = cpu_freq_khz_max; > + > + arm_lpm_clk = cpu_freq_khz_min * 1000; > + arm_normal_clk = cpu_freq_khz_max * 1000; > + > + /* Manual states, that PLL stabilizes in two CLK32 periods */ > + policy->cpuinfo.transition_latency = 10; > + > + ret = cpufreq_frequency_table_cpuinfo(policy, imx_freq_table); > + > + if (ret < 0) { > + clk_put(cpu_clk); > + printk(KERN_ERR "%s: failed to register i.MXC CPUfreq\n", > + __func__); > + return ret; > + } > + > + cpufreq_frequency_table_get_attr(imx_freq_table, policy->cpu); > + return 0; > +} > + > +static int mxc_cpufreq_suspend(struct cpufreq_policy *policy, > + pm_message_t state) > +{ > + return 0; > +} Get rid of these, since you don't use them. > +static int mxc_cpufreq_resume(struct cpufreq_policy *policy) > +{ > + return 0; > +} Same here. > +static int mxc_cpufreq_driver_exit(struct cpufreq_policy *policy) > +{ > + cpufreq_frequency_table_put_attr(policy->cpu); > + > + /* Reset CPU to 665MHz */ > + set_cpu_freq(arm_normal_clk); > + clk_put(cpu_clk); > + return 0; > +} > + > +static struct cpufreq_driver mxc_driver = { > + .flags = CPUFREQ_STICKY, > + .verify = mxc_verify_speed, > + .target = mxc_set_target, > + .get = mxc_get_speed, > + .init = mxc_cpufreq_driver_init, mxc_cpufreq_init is ok. Lose the driver. > + .exit = mxc_cpufreq_driver_exit, same. > + .suspend = mxc_cpufreq_suspend, > + .resume = mxc_cpufreq_resume, Get rid of suspend/resume > + .name = "imx", > +}; > + > +static int __devinit mxc_cpufreq_init(void) > +{ > + return cpufreq_register_driver(&mxc_driver); > +} > + > +static void mxc_cpufreq_exit(void) > +{ > + cpufreq_unregister_driver(&mxc_driver); > +} > + > +module_init(mxc_cpufreq_init); > +module_exit(mxc_cpufreq_exit); > + > +MODULE_AUTHOR("Freescale Semiconductor, Inc."); > +MODULE_DESCRIPTION("CPUfreq driver for i.MX"); > +MODULE_LICENSE("GPL"); > diff --git a/arch/arm/plat-mxc/include/mach/mxc.h b/arch/arm/plat-mxc/include/mach/mxc.h > index a790bf2..14003b9 100644 > --- a/arch/arm/plat-mxc/include/mach/mxc.h > +++ b/arch/arm/plat-mxc/include/mach/mxc.h > @@ -133,6 +133,26 @@ extern unsigned int __mxc_cpu_type; > # define cpu_is_mxc91231() (0) > #endif > > +#ifndef __ASSEMBLY__ > + > +struct cpu_wp { > + u32 pll_reg; > + u32 pll_rate; > + u32 cpu_rate; > + u32 pdr0_reg; > + u32 pdf; > + u32 mfi; > + u32 mfd; > + u32 mfn; > + u32 cpu_voltage; > + u32 cpu_podf; > +}; > + > +#ifndef CONFIG_ARCH_MX5 > +struct cpu_wp *get_cpu_wp(int *wp); > +#endif > +#endif > + > #if defined(CONFIG_ARCH_MX3) || defined(CONFIG_ARCH_MX2) > /* These are deprecated, use mx[23][157]_setup_weimcs instead. */ > #define CSCR_U(n) (IO_ADDRESS(WEIM_BASE_ADDR + n * 0x10)) > -- > 1.6.3.3 > ^ permalink raw reply [flat|nested] 11+ messages in thread
* [PATCH] cpufreq for freescale mx51 2010-09-30 10:48 ` [PATCH] " Amit Kucheria @ 2010-10-06 9:51 ` Sascha Hauer 2010-10-07 3:36 ` Yong Shen 0 siblings, 1 reply; 11+ messages in thread From: Sascha Hauer @ 2010-10-06 9:51 UTC (permalink / raw) To: linux-arm-kernel On Thu, Sep 30, 2010 at 01:48:17PM +0300, Amit Kucheria wrote: > Add'ed linaro-dev and linux-arm-kernel to CC. > > Thanks Yong, some feeback follows inline. > > On 10 Sep 29, Yong Shen wrote: > > From: Yong Shen <yong.shen@linaro.org> > > > > --- > > arch/arm/Kconfig | 6 + > > arch/arm/mach-mx5/Kconfig | 1 + > > arch/arm/mach-mx5/board-mx51_babbage.c | 32 ++++ > > arch/arm/mach-mx5/clock-mx51.c | 53 ++++++ > > arch/arm/plat-mxc/Makefile | 2 + > > arch/arm/plat-mxc/cpufreq.c | 282 ++++++++++++++++++++++++++++++++ > > arch/arm/plat-mxc/include/mach/mxc.h | 20 +++ > > 7 files changed, 396 insertions(+), 0 deletions(-) > > create mode 100644 arch/arm/plat-mxc/cpufreq.c > > > > diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig > > index 4db064e..64ebbc0 100644 > > --- a/arch/arm/Kconfig > > +++ b/arch/arm/Kconfig > > @@ -1517,6 +1517,12 @@ if ARCH_HAS_CPUFREQ > > > > source "drivers/cpufreq/Kconfig" > > > > +config CPU_FREQ_IMX > > + tristate "CPUfreq driver for i.MX CPUs" > > + depends on ARCH_MXC && CPU_FREQ > > + help > > + This enables the CPUfreq driver for i.MX CPUs. > > + > > config CPU_FREQ_SA1100 > > bool > > > > diff --git a/arch/arm/mach-mx5/Kconfig b/arch/arm/mach-mx5/Kconfig > > index 1576d51..5956fee 100644 > > --- a/arch/arm/mach-mx5/Kconfig > > +++ b/arch/arm/mach-mx5/Kconfig > > @@ -5,6 +5,7 @@ config ARCH_MX51 > > default y > > select MXC_TZIC > > select ARCH_MXC_IOMUX_V3 > > + select ARCH_HAS_CPUFREQ > > > > comment "MX5 platforms:" > > > > diff --git a/arch/arm/mach-mx5/board-mx51_babbage.c b/arch/arm/mach-mx5/board-mx51_babbage.c > > index ed885f9..e449e0b 100644 > > --- a/arch/arm/mach-mx5/board-mx51_babbage.c > > +++ b/arch/arm/mach-mx5/board-mx51_babbage.c > > @@ -43,6 +43,31 @@ > > #define MX51_USB_PLL_DIV_19_2_MHZ 0x01 > > #define MX51_USB_PLL_DIV_24_MHZ 0x02 > > > > +struct cpu_wp *(*get_cpu_wp)(int *wp); This should be moved to a generic place, otherwise it breaks the build when multiple boards are selected. > > +static int num_cpu_wp = 2; > > use sizeof(array) instead of hard coding this. > > > +/* working point(wp): 0 - 800MHz; 1 - 166.25MHz; */ > > +static struct cpu_wp cpu_wp_auto[] = { > > + { > > + .pll_rate = 800000000, > > + .cpu_rate = 800000000, > > + .pdf = 0, > > + .mfi = 8, > > + .mfd = 2, > > + .mfn = 1, > > + .cpu_podf = 0, > > + .cpu_voltage = 1100000,}, > > + { > > + .pll_rate = 800000000, > > + .cpu_rate = 160000000, > > + .pdf = 4, > > + .mfi = 8, > > + .mfd = 2, > > + .mfn = 1, > > + .cpu_podf = 4, > > + .cpu_voltage = 850000,}, > > +}; > > > This data should be moved out to a separate file (e.g. mx51_ratetable.h) > since it will be useful to other boards too. > > If other boards can have different rate tables (and they can, depending on > the revision of the silicon), then we can either 'assemble' the correct rate > table based on a flag field or have explicit separate rate tables for each > silicon revision. > > In any case, I suspect that there will be some core rates that will be common > across silicon revisions. > > > static struct platform_device *devices[] __initdata = { > > &mxc_fec_device, > > }; > > @@ -87,6 +112,12 @@ static struct imxuart_platform_data uart_pdata = { > > .flags = IMXUART_HAVE_RTSCTS, > > }; > > > > +struct cpu_wp *mx51_babbage_get_cpu_wp(int *wp) static > > +{ > > + *wp = num_cpu_wp; > > + return cpu_wp_auto; > > +} > > + > > static inline void mxc_init_imx_uart(void) > > { > > mxc_register_device(&mxc_uart_device0, &uart_pdata); > > @@ -246,6 +277,7 @@ static void __init mxc_board_init(void) > > > > static void __init mx51_babbage_timer_init(void) > > { > > + get_cpu_wp = mx51_babbage_get_cpu_wp; It looks strange to have a global function pointer which gets overwritten by the boards. I would expect a imx_add_cpu_workpoints(cpu_wp_auto, ARRAY_SIZE(cpu_wp_auto)); here. > > mx51_clocks_init(32768, 24000000, 22579200, 0); > > } > > > > diff --git a/arch/arm/mach-mx5/clock-mx51.c b/arch/arm/mach-mx5/clock-mx51.c > > index d9f612d..f2488e6 100644 > > --- a/arch/arm/mach-mx5/clock-mx51.c > > +++ b/arch/arm/mach-mx5/clock-mx51.c > > @@ -14,6 +14,7 @@ > > #include <linux/delay.h> > > #include <linux/clk.h> > > #include <linux/io.h> > > +#include <linux/time.h> unneeded include > > > > #include <asm/clkdev.h> > > #include <asm/div64.h> > > @@ -28,6 +29,11 @@ > > static unsigned long external_high_reference, external_low_reference; > > static unsigned long oscillator_reference, ckih2_reference; > > > > +extern struct cpu_wp *(*get_cpu_wp)(int *wp); > > +static int cpu_wp_nr; > > +static int cpu_curr_wp; > > +static struct cpu_wp *cpu_wp_tbl; > > + > > static struct clk osc_clk; > > static struct clk pll1_main_clk; > > static struct clk pll1_sw_clk; > > @@ -38,7 +44,9 @@ static struct clk periph_apm_clk; > > static struct clk ahb_clk; > > static struct clk ipg_clk; > > static struct clk usboh3_clk; > > +static void __iomem *pll1_base; > > > > +#define SPIN_DELAY 1000000 /* in nanoseconds */ unused > > #define MAX_DPLL_WAIT_TRIES 1000 /* 1000 * udelay(1) = 1ms */ > > > > static int _clk_ccgr_enable(struct clk *clk) > > @@ -330,6 +338,32 @@ static int _clk_lp_apm_set_parent(struct clk *clk, struct clk *parent) > > return 0; > > } > > > > +/*! > > + * Setup cpu clock based on working point. > > + * @param wp cpu freq working point > > + * @return 0 on success or error code on failure. > > + */ > > +int cpu_clk_set_wp(int wp) static > > +{ > > + struct cpu_wp *p; > > + u32 reg; > > + > > + if (wp == cpu_curr_wp) > > + return 0; > > + > > + p = &cpu_wp_tbl[wp]; > > + > > + /*use post divider to change freq > > + */ > > + reg = __raw_readl(MXC_CCM_CACRR); > > + reg &= ~MXC_CCM_CACRR_ARM_PODF_MASK; > > + reg |= cpu_wp_tbl[wp].cpu_podf << MXC_CCM_CACRR_ARM_PODF_OFFSET; > > + __raw_writel(reg, MXC_CCM_CACRR); We have a simple divider here. Why do we need a reference to struct cpu_wp then? This code could look much simpler. (side note: I'm aware that the original Freescale code is also able to change the cpu frequency using the pll instead of the divider, but is this really necessary?) > > + cpu_curr_wp = wp; > > + > > + return 0; > > +} > > + > > static unsigned long clk_arm_get_rate(struct clk *clk) > > { > > u32 cacrr, div; > > @@ -342,6 +376,20 @@ static unsigned long clk_arm_get_rate(struct clk *clk) > > return parent_rate / div; > > } > > > > +int _clk_cpu_set_rate(struct clk *clk, unsigned long rate) > > +{ > > + u32 i; > > + for (i = 0; i < cpu_wp_nr; i++) { > > + if (rate == cpu_wp_tbl[i].cpu_rate) > > + break; > > + } > > + if (i >= cpu_wp_nr) > > + return -EINVAL; > > + cpu_clk_set_wp(i); > > + > > + return 0; > > +} > > + > > static int _clk_periph_apm_set_parent(struct clk *clk, struct clk *parent) > > { > > u32 reg, mux; > > @@ -694,6 +742,7 @@ static struct clk periph_apm_clk = { > > static struct clk cpu_clk = { > > .parent = &pll1_sw_clk, > > .get_rate = clk_arm_get_rate, > > + .set_rate = _clk_cpu_set_rate, > > }; > > > > static struct clk ahb_clk = { > > @@ -821,6 +870,7 @@ static struct clk_lookup lookups[] = { > > _REGISTER_CLOCK("mxc-ehci.1", "usb_ahb", ahb_clk) > > _REGISTER_CLOCK("fsl-usb2-udc", "usb", usboh3_clk) > > _REGISTER_CLOCK("fsl-usb2-udc", "usb_ahb", ahb_clk) > > + _REGISTER_CLOCK(NULL, "cpu_clk", cpu_clk) > > }; > > > > static void clk_tree_init(void) > > @@ -848,10 +898,13 @@ int __init mx51_clocks_init(unsigned long ckil, unsigned long osc, > > { > > int i; > > > > + pll1_base = ioremap(MX51_PLL1_BASE_ADDR, SZ_4K); > > + this is unused. > > external_low_reference = ckil; > > external_high_reference = ckih1; > > ckih2_reference = ckih2; > > oscillator_reference = osc; > > + cpu_wp_tbl = get_cpu_wp(&cpu_wp_nr); > > > > for (i = 0; i < ARRAY_SIZE(lookups); i++) > > clkdev_add(&lookups[i]); > > diff --git a/arch/arm/plat-mxc/Makefile b/arch/arm/plat-mxc/Makefile > > index 895bc3c..c1bb400 100644 > > --- a/arch/arm/plat-mxc/Makefile > > +++ b/arch/arm/plat-mxc/Makefile > > @@ -17,6 +17,8 @@ obj-$(CONFIG_USB_EHCI_MXC) += ehci.o > > obj-$(CONFIG_MXC_ULPI) += ulpi.o > > obj-$(CONFIG_ARCH_MXC_AUDMUX_V1) += audmux-v1.o > > obj-$(CONFIG_ARCH_MXC_AUDMUX_V2) += audmux-v2.o > > +# CPU FREQ support > > +obj-$(CONFIG_CPU_FREQ_IMX) += cpufreq.o > > ifdef CONFIG_SND_IMX_SOC > > obj-y += ssi-fiq.o > > obj-y += ssi-fiq-ksym.o > > diff --git a/arch/arm/plat-mxc/cpufreq.c b/arch/arm/plat-mxc/cpufreq.c > > new file mode 100644 > > index 0000000..cae40f1 > > --- /dev/null > > +++ b/arch/arm/plat-mxc/cpufreq.c > > @@ -0,0 +1,282 @@ > > +/* > > + * Copyright (C) 2010 Freescale Semiconductor, Inc. All Rights Reserved. > > + */ > > + > > +/* > > + * The code contained herein is licensed under the GNU General Public > > + * License. You may obtain a copy of the GNU General Public License > > + * Version 2 or later at the following locations: > > + * > > + * http://www.opensource.org/licenses/gpl-license.html > > + * http://www.gnu.org/copyleft/gpl.html > > + */ > > + > > +/*! > > + * @file cpufreq.c > > + * > > + * @brief A driver for the Freescale Semiconductor i.MXC CPUfreq module. > > + * > > + * The CPUFREQ driver is for controling CPU frequency. It allows you to change > > + * the CPU clock speed on the fly. > > + * > > + * @ingroup PM > > + */ > > Fix these comments to follow the kernel commenting style. > > > +#include <linux/types.h> > > +#include <linux/kernel.h> > > +#include <linux/cpufreq.h> > > +#include <linux/init.h> > > +#include <linux/proc_fs.h> > > +#include <linux/regulator/consumer.h> > > +#include <linux/clk.h> > > +#include <linux/delay.h> > > +#include <linux/io.h> > > +#include <mach/hardware.h> > > +#include <asm/setup.h> > > +#include <mach/clock.h> > > +#include <asm/cacheflush.h> > > +#include <linux/hrtimer.h> > > + > > +int cpu_freq_khz_min; > > +int cpu_freq_khz_max; > > +int arm_lpm_clk; > > +int arm_normal_clk; > > +int cpufreq_suspended; > > +int cpufreq_trig_needed; > > + > > +static struct clk *cpu_clk; > > +static struct cpufreq_frequency_table imx_freq_table[4]; Three frequencies should be enough for everyone, right? This should be dynamically allocated like in other cpufreq drivers. > > + > > +extern int set_low_bus_freq(void); > > +extern int set_high_bus_freq(int high_bus_speed); > > +extern int low_freq_bus_used(void); declared but never used. > > + > > +extern struct cpu_wp *(*get_cpu_wp)(int *wp); > > +static int cpu_wp_nr; > > +static struct cpu_wp *cpu_wp_tbl; > > + > > +int set_cpu_freq(int freq) > > +{ > > + int ret = 0; > > + int org_cpu_rate; > > + int gp_volt = 0; > > + int i; > > + > > + org_cpu_rate = clk_get_rate(cpu_clk); > > + if (org_cpu_rate == freq) > > + return ret; > > + > > + for (i = 0; i < cpu_wp_nr; i++) { > > + if (freq == cpu_wp_tbl[i].cpu_rate) > > + gp_volt = cpu_wp_tbl[i].cpu_voltage; > > + } > > + > > + if (gp_volt == 0) > > + return ret; > > + > > + ret = clk_set_rate(cpu_clk, freq); > > + if (ret != 0) { > > + printk(KERN_DEBUG "cannot set CPU clock rate\n"); > > + return ret; > > + } > > + Please remove trailing whitespaces. > > + return ret; > > +} > > + > > +static int mxc_verify_speed(struct cpufreq_policy *policy) > > +{ > > + if (policy->cpu != 0) > > + return -EINVAL; > > + > > + return cpufreq_frequency_table_verify(policy, imx_freq_table); > > +} > > + > > +static unsigned int mxc_get_speed(unsigned int cpu) > > +{ > > + if (cpu) > > + return 0; > > + > > + return clk_get_rate(cpu_clk) / 1000; > > +} > > + > > +static int calc_frequency_khz(int target, unsigned int relation) > > +{ > > + int i; > > + > > + if ((target * 1000) == clk_get_rate(cpu_clk)) > > + return target; > > + > > + if (relation == CPUFREQ_RELATION_H) { > > + for (i = cpu_wp_nr - 1; i >= 0; i--) { > > + if (imx_freq_table[i].frequency <= target) > > + return imx_freq_table[i].frequency; > > + } > > + } else if (relation == CPUFREQ_RELATION_L) { > > + for (i = 0; i < cpu_wp_nr; i++) { > > + if (imx_freq_table[i].frequency >= target) > > + return imx_freq_table[i].frequency; > > + } > > + } It looks like cpufreq_frequency_table_target could help here. > > + printk(KERN_ERR "Error: No valid cpufreq relation\n"); > > + return cpu_freq_khz_max; > > +} > > + > > +static int mxc_set_target(struct cpufreq_policy *policy, > > + unsigned int target_freq, unsigned int relation) > > +{ > > + struct cpufreq_freqs freqs; > > + int freq_Hz; > > + int ret = 0; > > + > > + if (cpufreq_suspended) { > > + target_freq = clk_get_rate(cpu_clk) / 1000; > > + freq_Hz = calc_frequency_khz(target_freq, relation) * 1000; > > + if (freq_Hz == arm_lpm_clk) > > + freqs.old = cpu_wp_tbl[cpu_wp_nr - 2].cpu_rate / 1000; > > + else > > + freqs.old = arm_lpm_clk / 1000; > > + > > + freqs.new = freq_Hz / 1000; > > + freqs.cpu = 0; > > + freqs.flags = 0; > > + cpufreq_notify_transition(&freqs, CPUFREQ_PRECHANGE); > > + cpufreq_notify_transition(&freqs, CPUFREQ_POSTCHANGE); > > + return ret; > > + } > > + /* > > + * Some governors do not respects CPU and policy lower limits > > + * which leads to bad things (division by zero etc), ensure > > + * that such things do not happen. > > + */ > > Isn't that a bug in the governor? Can you explain a bit? > > > + if (target_freq < policy->cpuinfo.min_freq) > > + target_freq = policy->cpuinfo.min_freq; > > + > > + if (target_freq < policy->min) > > + target_freq = policy->min; > > + > > + freq_Hz = calc_frequency_khz(target_freq, relation) * 1000; > > + > > + freqs.old = clk_get_rate(cpu_clk) / 1000; > > + freqs.new = freq_Hz / 1000; > > + freqs.cpu = 0; > > + freqs.flags = 0; > > + cpufreq_notify_transition(&freqs, CPUFREQ_PRECHANGE); > > + > > + if (freqs.old != freqs.new) > > + ret = set_cpu_freq(freq_Hz); > > + > > + cpufreq_notify_transition(&freqs, CPUFREQ_POSTCHANGE); > > + > > + return ret; > > +} > > + > > +static int __init mxc_cpufreq_driver_init(struct cpufreq_policy *policy) > > +{ > > + int ret; > > + int i; > > + > > + printk(KERN_INFO "i.MXC CPU frequency driver\n"); > > + > > + if (policy->cpu != 0) > > + return -EINVAL; > > + > > + cpu_clk = clk_get(NULL, "cpu_clk"); > > + if (IS_ERR(cpu_clk)) { > > + printk(KERN_ERR "%s: failed to get cpu clock\n", __func__); > > + return PTR_ERR(cpu_clk); > > + } > > + > > + /* Set the current working point. */ > > + cpu_wp_tbl = get_cpu_wp(&cpu_wp_nr); > > + > > + cpu_freq_khz_min = cpu_wp_tbl[0].cpu_rate / 1000; > > + cpu_freq_khz_max = cpu_wp_tbl[0].cpu_rate / 1000; > > + > > + for (i = 0; i < cpu_wp_nr; i++) { > > + imx_freq_table[cpu_wp_nr - 1 - i].index = cpu_wp_nr - i; > > cpu_wp_nr = 2 here > > 1st iteration of for loop: > imx_freq_table[2 - 1 - 0].index = 2 - 0 > so, imx_freq_table[1].index = 2 > > 2nd iteration: > imx_freq_table[2 - 1 - 1].index = 2 - 1 > imx_freq_table[0].index = 1 > > So you're trying to reverse the table order? Why not just sort the table date > in the way you want and add a comment on the top to keep it sorted. > > > + imx_freq_table[cpu_wp_nr - 1 - i].frequency = > > + cpu_wp_tbl[i].cpu_rate / 1000; > > + > > + if ((cpu_wp_tbl[i].cpu_rate / 1000) < cpu_freq_khz_min) > > + cpu_freq_khz_min = cpu_wp_tbl[i].cpu_rate / 1000; > > + > > + if ((cpu_wp_tbl[i].cpu_rate / 1000) > cpu_freq_khz_max) > > + cpu_freq_khz_max = cpu_wp_tbl[i].cpu_rate / 1000; > > + } > > + > > + imx_freq_table[i].index = 0; > > imx_freq_table[i].index = i ? > > > + imx_freq_table[i].frequency = CPUFREQ_TABLE_END; > > + > > + policy->cur = clk_get_rate(cpu_clk) / 1000; > > + policy->governor = CPUFREQ_DEFAULT_GOVERNOR; > > + policy->min = policy->cpuinfo.min_freq = cpu_freq_khz_min; > > + policy->max = policy->cpuinfo.max_freq = cpu_freq_khz_max; > > + > > + arm_lpm_clk = cpu_freq_khz_min * 1000; > > + arm_normal_clk = cpu_freq_khz_max * 1000; > > + > > + /* Manual states, that PLL stabilizes in two CLK32 periods */ > > + policy->cpuinfo.transition_latency = 10; > > + > > + ret = cpufreq_frequency_table_cpuinfo(policy, imx_freq_table); > > + > > + if (ret < 0) { > > + clk_put(cpu_clk); > > + printk(KERN_ERR "%s: failed to register i.MXC CPUfreq\n", > > + __func__); > > + return ret; > > + } > > + > > + cpufreq_frequency_table_get_attr(imx_freq_table, policy->cpu); > > + return 0; > > +} > > + > > +static int mxc_cpufreq_suspend(struct cpufreq_policy *policy, > > + pm_message_t state) > > +{ > > + return 0; > > +} > > Get rid of these, since you don't use them. > > > +static int mxc_cpufreq_resume(struct cpufreq_policy *policy) > > +{ > > + return 0; > > +} > > Same here. > > > +static int mxc_cpufreq_driver_exit(struct cpufreq_policy *policy) > > +{ > > + cpufreq_frequency_table_put_attr(policy->cpu); > > + > > + /* Reset CPU to 665MHz */ > > + set_cpu_freq(arm_normal_clk); > > + clk_put(cpu_clk); > > + return 0; > > +} > > + > > +static struct cpufreq_driver mxc_driver = { > > + .flags = CPUFREQ_STICKY, > > + .verify = mxc_verify_speed, > > + .target = mxc_set_target, > > + .get = mxc_get_speed, > > + .init = mxc_cpufreq_driver_init, > > mxc_cpufreq_init is ok. Lose the driver. > > > + .exit = mxc_cpufreq_driver_exit, > > same. > > > + .suspend = mxc_cpufreq_suspend, > > + .resume = mxc_cpufreq_resume, > > Get rid of suspend/resume > > > + .name = "imx", > > +}; > > + > > +static int __devinit mxc_cpufreq_init(void) > > +{ > > + return cpufreq_register_driver(&mxc_driver); > > +} > > + > > +static void mxc_cpufreq_exit(void) > > +{ > > + cpufreq_unregister_driver(&mxc_driver); > > +} > > + > > +module_init(mxc_cpufreq_init); > > +module_exit(mxc_cpufreq_exit); > > + > > +MODULE_AUTHOR("Freescale Semiconductor, Inc."); > > +MODULE_DESCRIPTION("CPUfreq driver for i.MX"); > > +MODULE_LICENSE("GPL"); > > diff --git a/arch/arm/plat-mxc/include/mach/mxc.h b/arch/arm/plat-mxc/include/mach/mxc.h > > index a790bf2..14003b9 100644 > > --- a/arch/arm/plat-mxc/include/mach/mxc.h > > +++ b/arch/arm/plat-mxc/include/mach/mxc.h > > @@ -133,6 +133,26 @@ extern unsigned int __mxc_cpu_type; > > # define cpu_is_mxc91231() (0) > > #endif > > > > +#ifndef __ASSEMBLY__ > > + > > +struct cpu_wp { > > + u32 pll_reg; > > + u32 pll_rate; > > + u32 cpu_rate; > > + u32 pdr0_reg; > > + u32 pdf; > > + u32 mfi; > > + u32 mfd; > > + u32 mfn; > > + u32 cpu_voltage; > > + u32 cpu_podf; > > +}; > > + > > +#ifndef CONFIG_ARCH_MX5 > > +struct cpu_wp *get_cpu_wp(int *wp); > > +#endif > > +#endif > > + > > #if defined(CONFIG_ARCH_MX3) || defined(CONFIG_ARCH_MX2) > > /* These are deprecated, use mx[23][157]_setup_weimcs instead. */ > > #define CSCR_U(n) (IO_ADDRESS(WEIM_BASE_ADDR + n * 0x10)) > > -- > > 1.6.3.3 > > > > _______________________________________________ > linux-arm-kernel mailing list > linux-arm-kernel at lists.infradead.org > http://lists.infradead.org/mailman/listinfo/linux-arm-kernel > -- Pengutronix e.K. | | Industrial Linux Solutions | http://www.pengutronix.de/ | Peiner Str. 6-8, 31137 Hildesheim, Germany | Phone: +49-5121-206917-0 | Amtsgericht Hildesheim, HRA 2686 | Fax: +49-5121-206917-5555 | ^ permalink raw reply [flat|nested] 11+ messages in thread
* [PATCH] cpufreq for freescale mx51 2010-10-06 9:51 ` Sascha Hauer @ 2010-10-07 3:36 ` Yong Shen 2010-10-07 7:30 ` Sascha Hauer 0 siblings, 1 reply; 11+ messages in thread From: Yong Shen @ 2010-10-07 3:36 UTC (permalink / raw) To: linux-arm-kernel Hi Sascha, Thanks for your thorough comments. I have already received comments from Arnd before yours arrived. So some of the commends you two provided are common. I acknowledge most of your opinions, except for two, I have some explanations. > > + */ > > > + reg = __raw_readl(MXC_CCM_CACRR); > > > + reg &= ~MXC_CCM_CACRR_ARM_PODF_MASK; > > > + reg |= cpu_wp_tbl[wp].cpu_podf << MXC_CCM_CACRR_ARM_PODF_OFFSET; > > > + __raw_writel(reg, MXC_CCM_CACRR); > > We have a simple divider here. Why do we need a reference to struct > cpu_wp then? This code could look much simpler. > > (side note: I'm aware that the original Freescale code is also able > to change the cpu frequency using the pll instead of the divider, but is > this really necessary?) > Using wp_tbl is because that it also contains information like regulator voltage. Since the regulator driver for imx51 have not been upstreamed, you don't see any regulator operation here. Also, like you mentioned, there are two ways to change cpufreq, by post divider or pll change. And post divider change is a simpler way and also the only way for babbage, since cpu freq and ddr freq are all root from the same pll on babbage and they will interfere each other. > > +static struct cpufreq_frequency_table imx_freq_table[4]; > > Three frequencies should be enough for everyone, right? This should be > dynamically allocated like in other cpufreq drivers. > Yes, we only support 3 work points, which is for sure. Actually, we only support 2 points on most mx51 chips. I supposed it is ok to use static array here. Thanks again for review. I will send out updated patch again. Yong On Wed, Oct 6, 2010 at 5:51 PM, Sascha Hauer <s.hauer@pengutronix.de> wrote: > On Thu, Sep 30, 2010 at 01:48:17PM +0300, Amit Kucheria wrote: > > Add'ed linaro-dev and linux-arm-kernel to CC. > > > > Thanks Yong, some feeback follows inline. > > > > On 10 Sep 29, Yong Shen wrote: > > > From: Yong Shen <yong.shen@linaro.org> > > > > > > --- > > > arch/arm/Kconfig | 6 + > > > arch/arm/mach-mx5/Kconfig | 1 + > > > arch/arm/mach-mx5/board-mx51_babbage.c | 32 ++++ > > > arch/arm/mach-mx5/clock-mx51.c | 53 ++++++ > > > arch/arm/plat-mxc/Makefile | 2 + > > > arch/arm/plat-mxc/cpufreq.c | 282 > ++++++++++++++++++++++++++++++++ > > > arch/arm/plat-mxc/include/mach/mxc.h | 20 +++ > > > 7 files changed, 396 insertions(+), 0 deletions(-) > > > create mode 100644 arch/arm/plat-mxc/cpufreq.c > > > > > > diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig > > > index 4db064e..64ebbc0 100644 > > > --- a/arch/arm/Kconfig > > > +++ b/arch/arm/Kconfig > > > @@ -1517,6 +1517,12 @@ if ARCH_HAS_CPUFREQ > > > > > > source "drivers/cpufreq/Kconfig" > > > > > > +config CPU_FREQ_IMX > > > + tristate "CPUfreq driver for i.MX CPUs" > > > + depends on ARCH_MXC && CPU_FREQ > > > + help > > > + This enables the CPUfreq driver for i.MX CPUs. > > > + > > > config CPU_FREQ_SA1100 > > > bool > > > > > > diff --git a/arch/arm/mach-mx5/Kconfig b/arch/arm/mach-mx5/Kconfig > > > index 1576d51..5956fee 100644 > > > --- a/arch/arm/mach-mx5/Kconfig > > > +++ b/arch/arm/mach-mx5/Kconfig > > > @@ -5,6 +5,7 @@ config ARCH_MX51 > > > default y > > > select MXC_TZIC > > > select ARCH_MXC_IOMUX_V3 > > > + select ARCH_HAS_CPUFREQ > > > > > > comment "MX5 platforms:" > > > > > > diff --git a/arch/arm/mach-mx5/board-mx51_babbage.c > b/arch/arm/mach-mx5/board-mx51_babbage.c > > > index ed885f9..e449e0b 100644 > > > --- a/arch/arm/mach-mx5/board-mx51_babbage.c > > > +++ b/arch/arm/mach-mx5/board-mx51_babbage.c > > > @@ -43,6 +43,31 @@ > > > #define MX51_USB_PLL_DIV_19_2_MHZ 0x01 > > > #define MX51_USB_PLL_DIV_24_MHZ 0x02 > > > > > > +struct cpu_wp *(*get_cpu_wp)(int *wp); > > This should be moved to a generic place, otherwise it breaks the build > when multiple boards are selected. > > > > +static int num_cpu_wp = 2; > > > > use sizeof(array) instead of hard coding this. > > > > > +/* working point(wp): 0 - 800MHz; 1 - 166.25MHz; */ > > > +static struct cpu_wp cpu_wp_auto[] = { > > > + { > > > + .pll_rate = 800000000, > > > + .cpu_rate = 800000000, > > > + .pdf = 0, > > > + .mfi = 8, > > > + .mfd = 2, > > > + .mfn = 1, > > > + .cpu_podf = 0, > > > + .cpu_voltage = 1100000,}, > > > + { > > > + .pll_rate = 800000000, > > > + .cpu_rate = 160000000, > > > + .pdf = 4, > > > + .mfi = 8, > > > + .mfd = 2, > > > + .mfn = 1, > > > + .cpu_podf = 4, > > > + .cpu_voltage = 850000,}, > > > +}; > > > > > > This data should be moved out to a separate file (e.g. mx51_ratetable.h) > > since it will be useful to other boards too. > > > > If other boards can have different rate tables (and they can, depending > on > > the revision of the silicon), then we can either 'assemble' the correct > rate > > table based on a flag field or have explicit separate rate tables for > each > > silicon revision. > > > > In any case, I suspect that there will be some core rates that will be > common > > across silicon revisions. > > > > > static struct platform_device *devices[] __initdata = { > > > &mxc_fec_device, > > > }; > > > @@ -87,6 +112,12 @@ static struct imxuart_platform_data uart_pdata = { > > > .flags = IMXUART_HAVE_RTSCTS, > > > }; > > > > > > +struct cpu_wp *mx51_babbage_get_cpu_wp(int *wp) > > static > > > > +{ > > > + *wp = num_cpu_wp; > > > + return cpu_wp_auto; > > > +} > > > + > > > static inline void mxc_init_imx_uart(void) > > > { > > > mxc_register_device(&mxc_uart_device0, &uart_pdata); > > > @@ -246,6 +277,7 @@ static void __init mxc_board_init(void) > > > > > > static void __init mx51_babbage_timer_init(void) > > > { > > > + get_cpu_wp = mx51_babbage_get_cpu_wp; > > It looks strange to have a global function pointer which gets > overwritten by the boards. I would expect a > > imx_add_cpu_workpoints(cpu_wp_auto, ARRAY_SIZE(cpu_wp_auto)); > > here. > > > > mx51_clocks_init(32768, 24000000, 22579200, 0); > > > } > > > > > > diff --git a/arch/arm/mach-mx5/clock-mx51.c > b/arch/arm/mach-mx5/clock-mx51.c > > > index d9f612d..f2488e6 100644 > > > --- a/arch/arm/mach-mx5/clock-mx51.c > > > +++ b/arch/arm/mach-mx5/clock-mx51.c > > > @@ -14,6 +14,7 @@ > > > #include <linux/delay.h> > > > #include <linux/clk.h> > > > #include <linux/io.h> > > > +#include <linux/time.h> > > unneeded include > > > > > > > #include <asm/clkdev.h> > > > #include <asm/div64.h> > > > @@ -28,6 +29,11 @@ > > > static unsigned long external_high_reference, external_low_reference; > > > static unsigned long oscillator_reference, ckih2_reference; > > > > > > +extern struct cpu_wp *(*get_cpu_wp)(int *wp); > > > +static int cpu_wp_nr; > > > +static int cpu_curr_wp; > > > +static struct cpu_wp *cpu_wp_tbl; > > > + > > > static struct clk osc_clk; > > > static struct clk pll1_main_clk; > > > static struct clk pll1_sw_clk; > > > @@ -38,7 +44,9 @@ static struct clk periph_apm_clk; > > > static struct clk ahb_clk; > > > static struct clk ipg_clk; > > > static struct clk usboh3_clk; > > > +static void __iomem *pll1_base; > > > > > > +#define SPIN_DELAY 1000000 /* in nanoseconds */ > > unused > > > > #define MAX_DPLL_WAIT_TRIES 1000 /* 1000 * udelay(1) = 1ms */ > > > > > > static int _clk_ccgr_enable(struct clk *clk) > > > @@ -330,6 +338,32 @@ static int _clk_lp_apm_set_parent(struct clk *clk, > struct clk *parent) > > > return 0; > > > } > > > > > > +/*! > > > + * Setup cpu clock based on working point. > > > + * @param wp cpu freq working point > > > + * @return 0 on success or error code on failure. > > > + */ > > > +int cpu_clk_set_wp(int wp) > > static > > > > +{ > > > + struct cpu_wp *p; > > > + u32 reg; > > > + > > > + if (wp == cpu_curr_wp) > > > + return 0; > > > + > > > + p = &cpu_wp_tbl[wp]; > > > + > > > + /*use post divider to change freq > > > + */ > > > + reg = __raw_readl(MXC_CCM_CACRR); > > > + reg &= ~MXC_CCM_CACRR_ARM_PODF_MASK; > > > + reg |= cpu_wp_tbl[wp].cpu_podf << MXC_CCM_CACRR_ARM_PODF_OFFSET; > > > + __raw_writel(reg, MXC_CCM_CACRR); > > We have a simple divider here. Why do we need a reference to struct > cpu_wp then? This code could look much simpler. > > (side note: I'm aware that the original Freescale code is also able > to change the cpu frequency using the pll instead of the divider, but is > this really necessary?) > > > > + cpu_curr_wp = wp; > > > + > > > + return 0; > > > +} > > > + > > > static unsigned long clk_arm_get_rate(struct clk *clk) > > > { > > > u32 cacrr, div; > > > @@ -342,6 +376,20 @@ static unsigned long clk_arm_get_rate(struct clk > *clk) > > > return parent_rate / div; > > > } > > > > > > +int _clk_cpu_set_rate(struct clk *clk, unsigned long rate) > > > +{ > > > + u32 i; > > > + for (i = 0; i < cpu_wp_nr; i++) { > > > + if (rate == cpu_wp_tbl[i].cpu_rate) > > > + break; > > > + } > > > + if (i >= cpu_wp_nr) > > > + return -EINVAL; > > > + cpu_clk_set_wp(i); > > > + > > > + return 0; > > > +} > > > + > > > static int _clk_periph_apm_set_parent(struct clk *clk, struct clk > *parent) > > > { > > > u32 reg, mux; > > > @@ -694,6 +742,7 @@ static struct clk periph_apm_clk = { > > > static struct clk cpu_clk = { > > > .parent = &pll1_sw_clk, > > > .get_rate = clk_arm_get_rate, > > > + .set_rate = _clk_cpu_set_rate, > > > }; > > > > > > static struct clk ahb_clk = { > > > @@ -821,6 +870,7 @@ static struct clk_lookup lookups[] = { > > > _REGISTER_CLOCK("mxc-ehci.1", "usb_ahb", ahb_clk) > > > _REGISTER_CLOCK("fsl-usb2-udc", "usb", usboh3_clk) > > > _REGISTER_CLOCK("fsl-usb2-udc", "usb_ahb", ahb_clk) > > > + _REGISTER_CLOCK(NULL, "cpu_clk", cpu_clk) > > > }; > > > > > > static void clk_tree_init(void) > > > @@ -848,10 +898,13 @@ int __init mx51_clocks_init(unsigned long ckil, > unsigned long osc, > > > { > > > int i; > > > > > > + pll1_base = ioremap(MX51_PLL1_BASE_ADDR, SZ_4K); > > > + > > this is unused. > > > > external_low_reference = ckil; > > > external_high_reference = ckih1; > > > ckih2_reference = ckih2; > > > oscillator_reference = osc; > > > + cpu_wp_tbl = get_cpu_wp(&cpu_wp_nr); > > > > > > for (i = 0; i < ARRAY_SIZE(lookups); i++) > > > clkdev_add(&lookups[i]); > > > diff --git a/arch/arm/plat-mxc/Makefile b/arch/arm/plat-mxc/Makefile > > > index 895bc3c..c1bb400 100644 > > > --- a/arch/arm/plat-mxc/Makefile > > > +++ b/arch/arm/plat-mxc/Makefile > > > @@ -17,6 +17,8 @@ obj-$(CONFIG_USB_EHCI_MXC) += ehci.o > > > obj-$(CONFIG_MXC_ULPI) += ulpi.o > > > obj-$(CONFIG_ARCH_MXC_AUDMUX_V1) += audmux-v1.o > > > obj-$(CONFIG_ARCH_MXC_AUDMUX_V2) += audmux-v2.o > > > +# CPU FREQ support > > > +obj-$(CONFIG_CPU_FREQ_IMX) += cpufreq.o > > > ifdef CONFIG_SND_IMX_SOC > > > obj-y += ssi-fiq.o > > > obj-y += ssi-fiq-ksym.o > > > diff --git a/arch/arm/plat-mxc/cpufreq.c b/arch/arm/plat-mxc/cpufreq.c > > > new file mode 100644 > > > index 0000000..cae40f1 > > > --- /dev/null > > > +++ b/arch/arm/plat-mxc/cpufreq.c > > > @@ -0,0 +1,282 @@ > > > +/* > > > + * Copyright (C) 2010 Freescale Semiconductor, Inc. All Rights > Reserved. > > > + */ > > > + > > > +/* > > > + * The code contained herein is licensed under the GNU General Public > > > + * License. You may obtain a copy of the GNU General Public License > > > + * Version 2 or later at the following locations: > > > + * > > > + * http://www.opensource.org/licenses/gpl-license.html > > > + * http://www.gnu.org/copyleft/gpl.html > > > + */ > > > + > > > +/*! > > > + * @file cpufreq.c > > > + * > > > + * @brief A driver for the Freescale Semiconductor i.MXC CPUfreq > module. > > > + * > > > + * The CPUFREQ driver is for controling CPU frequency. It allows you > to change > > > + * the CPU clock speed on the fly. > > > + * > > > + * @ingroup PM > > > + */ > > > > Fix these comments to follow the kernel commenting style. > > > > > +#include <linux/types.h> > > > +#include <linux/kernel.h> > > > +#include <linux/cpufreq.h> > > > +#include <linux/init.h> > > > +#include <linux/proc_fs.h> > > > +#include <linux/regulator/consumer.h> > > > +#include <linux/clk.h> > > > +#include <linux/delay.h> > > > +#include <linux/io.h> > > > +#include <mach/hardware.h> > > > +#include <asm/setup.h> > > > +#include <mach/clock.h> > > > +#include <asm/cacheflush.h> > > > +#include <linux/hrtimer.h> > > > + > > > +int cpu_freq_khz_min; > > > +int cpu_freq_khz_max; > > > +int arm_lpm_clk; > > > +int arm_normal_clk; > > > +int cpufreq_suspended; > > > +int cpufreq_trig_needed; > > > + > > > +static struct clk *cpu_clk; > > > +static struct cpufreq_frequency_table imx_freq_table[4]; > > Three frequencies should be enough for everyone, right? This should be > dynamically allocated like in other cpufreq drivers. > > > > + > > > +extern int set_low_bus_freq(void); > > > +extern int set_high_bus_freq(int high_bus_speed); > > > +extern int low_freq_bus_used(void); > > declared but never used. > > > > + > > > +extern struct cpu_wp *(*get_cpu_wp)(int *wp); > > > +static int cpu_wp_nr; > > > +static struct cpu_wp *cpu_wp_tbl; > > > + > > > +int set_cpu_freq(int freq) > > > +{ > > > + int ret = 0; > > > + int org_cpu_rate; > > > + int gp_volt = 0; > > > + int i; > > > + > > > + org_cpu_rate = clk_get_rate(cpu_clk); > > > + if (org_cpu_rate == freq) > > > + return ret; > > > + > > > + for (i = 0; i < cpu_wp_nr; i++) { > > > + if (freq == cpu_wp_tbl[i].cpu_rate) > > > + gp_volt = cpu_wp_tbl[i].cpu_voltage; > > > + } > > > + > > > + if (gp_volt == 0) > > > + return ret; > > > + > > > + ret = clk_set_rate(cpu_clk, freq); > > > + if (ret != 0) { > > > + printk(KERN_DEBUG "cannot set CPU clock rate\n"); > > > + return ret; > > > + } > > > + > > Please remove trailing whitespaces. > > > > + return ret; > > > +} > > > + > > > +static int mxc_verify_speed(struct cpufreq_policy *policy) > > > +{ > > > + if (policy->cpu != 0) > > > + return -EINVAL; > > > + > > > + return cpufreq_frequency_table_verify(policy, imx_freq_table); > > > +} > > > + > > > +static unsigned int mxc_get_speed(unsigned int cpu) > > > +{ > > > + if (cpu) > > > + return 0; > > > + > > > + return clk_get_rate(cpu_clk) / 1000; > > > +} > > > + > > > +static int calc_frequency_khz(int target, unsigned int relation) > > > +{ > > > + int i; > > > + > > > + if ((target * 1000) == clk_get_rate(cpu_clk)) > > > + return target; > > > + > > > + if (relation == CPUFREQ_RELATION_H) { > > > + for (i = cpu_wp_nr - 1; i >= 0; i--) { > > > + if (imx_freq_table[i].frequency <= target) > > > + return imx_freq_table[i].frequency; > > > + } > > > + } else if (relation == CPUFREQ_RELATION_L) { > > > + for (i = 0; i < cpu_wp_nr; i++) { > > > + if (imx_freq_table[i].frequency >= target) > > > + return imx_freq_table[i].frequency; > > > + } > > > + } > > It looks like cpufreq_frequency_table_target could help here. > > > > + printk(KERN_ERR "Error: No valid cpufreq relation\n"); > > > + return cpu_freq_khz_max; > > > +} > > > + > > > +static int mxc_set_target(struct cpufreq_policy *policy, > > > + unsigned int target_freq, unsigned int relation) > > > +{ > > > + struct cpufreq_freqs freqs; > > > + int freq_Hz; > > > + int ret = 0; > > > + > > > + if (cpufreq_suspended) { > > > + target_freq = clk_get_rate(cpu_clk) / 1000; > > > + freq_Hz = calc_frequency_khz(target_freq, relation) * 1000; > > > + if (freq_Hz == arm_lpm_clk) > > > + freqs.old = cpu_wp_tbl[cpu_wp_nr - 2].cpu_rate / > 1000; > > > + else > > > + freqs.old = arm_lpm_clk / 1000; > > > + > > > + freqs.new = freq_Hz / 1000; > > > + freqs.cpu = 0; > > > + freqs.flags = 0; > > > + cpufreq_notify_transition(&freqs, CPUFREQ_PRECHANGE); > > > + cpufreq_notify_transition(&freqs, CPUFREQ_POSTCHANGE); > > > + return ret; > > > + } > > > + /* > > > + * Some governors do not respects CPU and policy lower limits > > > + * which leads to bad things (division by zero etc), ensure > > > + * that such things do not happen. > > > + */ > > > > Isn't that a bug in the governor? Can you explain a bit? > > > > > + if (target_freq < policy->cpuinfo.min_freq) > > > + target_freq = policy->cpuinfo.min_freq; > > > + > > > + if (target_freq < policy->min) > > > + target_freq = policy->min; > > > + > > > + freq_Hz = calc_frequency_khz(target_freq, relation) * 1000; > > > + > > > + freqs.old = clk_get_rate(cpu_clk) / 1000; > > > + freqs.new = freq_Hz / 1000; > > > + freqs.cpu = 0; > > > + freqs.flags = 0; > > > + cpufreq_notify_transition(&freqs, CPUFREQ_PRECHANGE); > > > + > > > + if (freqs.old != freqs.new) > > > + ret = set_cpu_freq(freq_Hz); > > > + > > > + cpufreq_notify_transition(&freqs, CPUFREQ_POSTCHANGE); > > > + > > > + return ret; > > > +} > > > + > > > +static int __init mxc_cpufreq_driver_init(struct cpufreq_policy > *policy) > > > +{ > > > + int ret; > > > + int i; > > > + > > > + printk(KERN_INFO "i.MXC CPU frequency driver\n"); > > > + > > > + if (policy->cpu != 0) > > > + return -EINVAL; > > > + > > > + cpu_clk = clk_get(NULL, "cpu_clk"); > > > + if (IS_ERR(cpu_clk)) { > > > + printk(KERN_ERR "%s: failed to get cpu clock\n", __func__); > > > + return PTR_ERR(cpu_clk); > > > + } > > > + > > > + /* Set the current working point. */ > > > + cpu_wp_tbl = get_cpu_wp(&cpu_wp_nr); > > > + > > > + cpu_freq_khz_min = cpu_wp_tbl[0].cpu_rate / 1000; > > > + cpu_freq_khz_max = cpu_wp_tbl[0].cpu_rate / 1000; > > > + > > > + for (i = 0; i < cpu_wp_nr; i++) { > > > + imx_freq_table[cpu_wp_nr - 1 - i].index = cpu_wp_nr - i; > > > > cpu_wp_nr = 2 here > > > > 1st iteration of for loop: > > imx_freq_table[2 - 1 - 0].index = 2 - 0 > > so, imx_freq_table[1].index = 2 > > > > 2nd iteration: > > imx_freq_table[2 - 1 - 1].index = 2 - 1 > > imx_freq_table[0].index = 1 > > > > So you're trying to reverse the table order? Why not just sort the table > date > > in the way you want and add a comment on the top to keep it sorted. > > > > > + imx_freq_table[cpu_wp_nr - 1 - i].frequency = > > > + cpu_wp_tbl[i].cpu_rate / 1000; > > > + > > > + if ((cpu_wp_tbl[i].cpu_rate / 1000) < cpu_freq_khz_min) > > > + cpu_freq_khz_min = cpu_wp_tbl[i].cpu_rate / 1000; > > > + > > > + if ((cpu_wp_tbl[i].cpu_rate / 1000) > cpu_freq_khz_max) > > > + cpu_freq_khz_max = cpu_wp_tbl[i].cpu_rate / 1000; > > > + } > > > + > > > + imx_freq_table[i].index = 0; > > > > imx_freq_table[i].index = i ? > > > > > + imx_freq_table[i].frequency = CPUFREQ_TABLE_END; > > > + > > > + policy->cur = clk_get_rate(cpu_clk) / 1000; > > > + policy->governor = CPUFREQ_DEFAULT_GOVERNOR; > > > + policy->min = policy->cpuinfo.min_freq = cpu_freq_khz_min; > > > + policy->max = policy->cpuinfo.max_freq = cpu_freq_khz_max; > > > + > > > + arm_lpm_clk = cpu_freq_khz_min * 1000; > > > + arm_normal_clk = cpu_freq_khz_max * 1000; > > > + > > > + /* Manual states, that PLL stabilizes in two CLK32 periods */ > > > + policy->cpuinfo.transition_latency = 10; > > > + > > > + ret = cpufreq_frequency_table_cpuinfo(policy, imx_freq_table); > > > + > > > + if (ret < 0) { > > > + clk_put(cpu_clk); > > > + printk(KERN_ERR "%s: failed to register i.MXC CPUfreq\n", > > > + __func__); > > > + return ret; > > > + } > > > + > > > + cpufreq_frequency_table_get_attr(imx_freq_table, policy->cpu); > > > + return 0; > > > +} > > > + > > > +static int mxc_cpufreq_suspend(struct cpufreq_policy *policy, > > > + pm_message_t state) > > > +{ > > > + return 0; > > > +} > > > > Get rid of these, since you don't use them. > > > > > +static int mxc_cpufreq_resume(struct cpufreq_policy *policy) > > > +{ > > > + return 0; > > > +} > > > > Same here. > > > > > +static int mxc_cpufreq_driver_exit(struct cpufreq_policy *policy) > > > +{ > > > + cpufreq_frequency_table_put_attr(policy->cpu); > > > + > > > + /* Reset CPU to 665MHz */ > > > + set_cpu_freq(arm_normal_clk); > > > + clk_put(cpu_clk); > > > + return 0; > > > +} > > > + > > > +static struct cpufreq_driver mxc_driver = { > > > + .flags = CPUFREQ_STICKY, > > > + .verify = mxc_verify_speed, > > > + .target = mxc_set_target, > > > + .get = mxc_get_speed, > > > + .init = mxc_cpufreq_driver_init, > > > > mxc_cpufreq_init is ok. Lose the driver. > > > > > + .exit = mxc_cpufreq_driver_exit, > > > > same. > > > > > + .suspend = mxc_cpufreq_suspend, > > > + .resume = mxc_cpufreq_resume, > > > > Get rid of suspend/resume > > > > > + .name = "imx", > > > +}; > > > + > > > +static int __devinit mxc_cpufreq_init(void) > > > +{ > > > + return cpufreq_register_driver(&mxc_driver); > > > +} > > > + > > > +static void mxc_cpufreq_exit(void) > > > +{ > > > + cpufreq_unregister_driver(&mxc_driver); > > > +} > > > + > > > +module_init(mxc_cpufreq_init); > > > +module_exit(mxc_cpufreq_exit); > > > + > > > +MODULE_AUTHOR("Freescale Semiconductor, Inc."); > > > +MODULE_DESCRIPTION("CPUfreq driver for i.MX"); > > > +MODULE_LICENSE("GPL"); > > > diff --git a/arch/arm/plat-mxc/include/mach/mxc.h > b/arch/arm/plat-mxc/include/mach/mxc.h > > > index a790bf2..14003b9 100644 > > > --- a/arch/arm/plat-mxc/include/mach/mxc.h > > > +++ b/arch/arm/plat-mxc/include/mach/mxc.h > > > @@ -133,6 +133,26 @@ extern unsigned int __mxc_cpu_type; > > > # define cpu_is_mxc91231() (0) > > > #endif > > > > > > +#ifndef __ASSEMBLY__ > > > + > > > +struct cpu_wp { > > > + u32 pll_reg; > > > + u32 pll_rate; > > > + u32 cpu_rate; > > > + u32 pdr0_reg; > > > + u32 pdf; > > > + u32 mfi; > > > + u32 mfd; > > > + u32 mfn; > > > + u32 cpu_voltage; > > > + u32 cpu_podf; > > > +}; > > > + > > > +#ifndef CONFIG_ARCH_MX5 > > > +struct cpu_wp *get_cpu_wp(int *wp); > > > +#endif > > > +#endif > > > + > > > #if defined(CONFIG_ARCH_MX3) || defined(CONFIG_ARCH_MX2) > > > /* These are deprecated, use mx[23][157]_setup_weimcs instead. */ > > > #define CSCR_U(n) (IO_ADDRESS(WEIM_BASE_ADDR + n * 0x10)) > > > -- > > > 1.6.3.3 > > > > > > > _______________________________________________ > > linux-arm-kernel mailing list > > linux-arm-kernel at lists.infradead.org > > http://lists.infradead.org/mailman/listinfo/linux-arm-kernel > > > > -- > Pengutronix e.K. | | > Industrial Linux Solutions | http://www.pengutronix.de/ | > Peiner Str. 6-8, 31137 Hildesheim, Germany | Phone: +49-5121-206917-0 | > Amtsgericht Hildesheim, HRA 2686 | Fax: +49-5121-206917-5555 | > -------------- next part -------------- An HTML attachment was scrubbed... URL: <http://lists.infradead.org/pipermail/linux-arm-kernel/attachments/20101007/4f4a5b6a/attachment-0001.html> ^ permalink raw reply [flat|nested] 11+ messages in thread
* [PATCH] cpufreq for freescale mx51 2010-10-07 3:36 ` Yong Shen @ 2010-10-07 7:30 ` Sascha Hauer 2010-10-07 12:00 ` Yong Shen 0 siblings, 1 reply; 11+ messages in thread From: Sascha Hauer @ 2010-10-07 7:30 UTC (permalink / raw) To: linux-arm-kernel On Thu, Oct 07, 2010 at 11:36:07AM +0800, Yong Shen wrote: > Hi Sascha, > > Thanks for your thorough comments. > I have already received comments from Arnd before yours arrived. So some of > the commends you two provided are common. > I acknowledge most of your opinions, except for two, I have some > explanations. > > > > + */ > > > > + reg = __raw_readl(MXC_CCM_CACRR); > > > > + reg &= ~MXC_CCM_CACRR_ARM_PODF_MASK; > > > > + reg |= cpu_wp_tbl[wp].cpu_podf << MXC_CCM_CACRR_ARM_PODF_OFFSET; > > > > + __raw_writel(reg, MXC_CCM_CACRR); > > > > We have a simple divider here. Why do we need a reference to struct > > cpu_wp then? This code could look much simpler. > > > > (side note: I'm aware that the original Freescale code is also able > > to change the cpu frequency using the pll instead of the divider, but is > > this really necessary?) > > > Using wp_tbl is because that it also contains information like regulator > voltage. The clock code does not handle the regulators, not even in the fsl kernel. > Since the regulator driver for imx51 have not been upstreamed, you > don't see any regulator operation here. Also, like you mentioned, there are > two ways to change cpufreq, by post divider or pll change. And post divider > change is a simpler way and also the only way for babbage, since cpu freq > and ddr freq are all root from the same pll on babbage and they will > interfere each other. I understand what you have to do, but the way you did really looks strange. You introduce get_cpu_wp() to get the complete array of workpoints. In the cpufreq driver you have this complete array, pick the desired workpoint, pass the frequency to the clock layer which in turn calls get_cpu_wp() to get the array and loop around it to get the workpoint from the frequency again. Addionally you maintain a pointer to what the clock code thinks the current workpoint is. How about making the clock code agnostic of such workpoints and calculate the pll values and dividers for a given frequency based on the frequency. If that's to complicated you could maintain a table in the clock code. If that's not flexible enough you could pass a pointer to this table to mx51_clocks_init. This array could carry information relevant only to the clock code and only relevant to the i.MX51, In the fsl kernel struct cpu_wp is a catch-all struct for all i.MXs. The i.MX51 uses the pdf/mfi/mfd/mfn/cpu_podf fields, the i.MX35 uses the pll_reg and pdr0_reg fields and who knows what the next i.MX needs. > > > > +static struct cpufreq_frequency_table imx_freq_table[4]; > > > > Three frequencies should be enough for everyone, right? This should be > > dynamically allocated like in other cpufreq drivers. > > > > Yes, we only support 3 work points, which is for sure. Actually, we only > support 2 points on most mx51 chips. I supposed it is ok to use static array > here. Please just add dynamic allocation, then we are safe for any potential future use. Sascha -- Pengutronix e.K. | | Industrial Linux Solutions | http://www.pengutronix.de/ | Peiner Str. 6-8, 31137 Hildesheim, Germany | Phone: +49-5121-206917-0 | Amtsgericht Hildesheim, HRA 2686 | Fax: +49-5121-206917-5555 | ^ permalink raw reply [flat|nested] 11+ messages in thread
* [PATCH] cpufreq for freescale mx51 2010-10-07 7:30 ` Sascha Hauer @ 2010-10-07 12:00 ` Yong Shen 2010-10-12 7:11 ` [PATCHv2] " Yong Shen 0 siblings, 1 reply; 11+ messages in thread From: Yong Shen @ 2010-10-07 12:00 UTC (permalink / raw) To: linux-arm-kernel > > Using wp_tbl is because that it also contains information like regulator > > voltage. > > The clock code does not handle the regulators, not even in the fsl > kernel. > I did not mean to say clock code will handle regulators. I mean this table also contains such information which may be needed in other freescale drivers, like dvfs and busfreq. > > > Since the regulator driver for imx51 have not been upstreamed, you > > don't see any regulator operation here. Also, like you mentioned, there > are > > two ways to change cpufreq, by post divider or pll change. And post > divider > > change is a simpler way and also the only way for babbage, since cpu freq > > and ddr freq are all root from the same pll on babbage and they will > > interfere each other. > > I understand what you have to do, but the way you did really looks > strange. You introduce get_cpu_wp() to get the complete array of > workpoints. In the cpufreq driver you have this complete array, pick > the desired workpoint, pass the frequency to the clock layer which in > turn calls get_cpu_wp() to get the array and loop around it to get > the workpoint from the frequency again. Addionally you maintain a > pointer to what the clock code thinks the current workpoint is. > > How about making the clock code agnostic of such workpoints and > calculate the pll values and dividers for a given frequency based > on the frequency. If that's to complicated you could maintain > a table in the clock code. If that's not flexible enough you could > pass a pointer to this table to mx51_clocks_init. This array could > carry information relevant only to the clock code and only relevant > to the i.MX51, In the fsl kernel struct cpu_wp is a catch-all struct > for all i.MXs. The i.MX51 uses the pdf/mfi/mfd/mfn/cpu_podf fields, > the i.MX35 uses the pll_reg and pdr0_reg fields and who knows what > the next i.MX needs. > I strongly agree with you about this, and it is better to letter clock calculate by itself. > > > > > > > +static struct cpufreq_frequency_table imx_freq_table[4]; > > > > > > Three frequencies should be enough for everyone, right? This should be > > > dynamically allocated like in other cpufreq drivers. > > > > > > > Yes, we only support 3 work points, which is for sure. Actually, we only > > support 2 points on most mx51 chips. I supposed it is ok to use static > array > > here. > > Please just add dynamic allocation, then we are safe for any potential > future use. > > Ok! sorry for misunderstanding. Here, I am not trying to find excuse for the cpufreq driver designer which is my colleague, however the truth is that since in our company the schedule is tight for all the tasks, that's why we always code with the first rough idea in the head, and having less time to think about the flexibility and other good conventions in programming. You can see that even freescale code upstreaming is done by people out of freescale, because of we lacking of resource. Therefore, I would say we did not mean to have some short-sight design, we also want to achieve a better life of coding like you guys are doing, which is my direct feeling while I am in Linaro. :) Thanks for comments. Yong -------------- next part -------------- An HTML attachment was scrubbed... URL: <http://lists.infradead.org/pipermail/linux-arm-kernel/attachments/20101007/62053234/attachment-0001.html> ^ permalink raw reply [flat|nested] 11+ messages in thread
* [PATCHv2] cpufreq for freescale mx51 2010-10-07 12:00 ` Yong Shen @ 2010-10-12 7:11 ` Yong Shen 0 siblings, 0 replies; 11+ messages in thread From: Yong Shen @ 2010-10-12 7:11 UTC (permalink / raw) To: linux-arm-kernel Does anybody have more comments on [PATCHv2] ? On Thu, Oct 7, 2010 at 8:00 PM, Yong Shen <yong.shen@linaro.org> wrote: > > > Using wp_tbl is because that it also contains information like regulator >> > voltage. >> >> The clock code does not handle the regulators, not even in the fsl >> kernel. >> > I did not mean to say clock code will handle regulators. I mean this table > also contains such information which may be needed in other freescale > drivers, like dvfs and busfreq. > >> >> > > Since the regulator driver for imx51 have not been upstreamed, you >> > don't see any regulator operation here. Also, like you mentioned, there >> are >> > two ways to change cpufreq, by post divider or pll change. And post >> divider >> > change is a simpler way and also the only way for babbage, since cpu >> freq >> > and ddr freq are all root from the same pll on babbage and they will >> > interfere each other. >> >> I understand what you have to do, but the way you did really looks >> strange. You introduce get_cpu_wp() to get the complete array of >> workpoints. In the cpufreq driver you have this complete array, pick >> the desired workpoint, pass the frequency to the clock layer which in >> turn calls get_cpu_wp() to get the array and loop around it to get >> the workpoint from the frequency again. Addionally you maintain a >> pointer to what the clock code thinks the current workpoint is. >> >> How about making the clock code agnostic of such workpoints and >> calculate the pll values and dividers for a given frequency based >> on the frequency. If that's to complicated you could maintain >> a table in the clock code. If that's not flexible enough you could >> pass a pointer to this table to mx51_clocks_init. This array could >> carry information relevant only to the clock code and only relevant >> to the i.MX51, In the fsl kernel struct cpu_wp is a catch-all struct >> for all i.MXs. The i.MX51 uses the pdf/mfi/mfd/mfn/cpu_podf fields, >> the i.MX35 uses the pll_reg and pdr0_reg fields and who knows what >> the next i.MX needs. >> > > I strongly agree with you about this, and it is better to letter clock > calculate by itself. > >> >> > > >> > > > +static struct cpufreq_frequency_table imx_freq_table[4]; >> > > >> > > Three frequencies should be enough for everyone, right? This should be >> > > dynamically allocated like in other cpufreq drivers. >> > > >> > >> > Yes, we only support 3 work points, which is for sure. Actually, we only >> > support 2 points on most mx51 chips. I supposed it is ok to use static >> array >> > here. >> >> Please just add dynamic allocation, then we are safe for any potential >> future use. >> >> Ok! sorry for misunderstanding. > > Here, I am not trying to find excuse for the cpufreq driver designer which > is my colleague, however the truth is that since in our company the schedule > is tight for all the tasks, that's why we always code with the first rough > idea in the head, and having less time to think about the flexibility and > other good conventions in programming. You can see that even freescale code > upstreaming is done by people out of freescale, because of we lacking of > resource. > Therefore, I would say we did not mean to have some short-sight design, we > also want to achieve a better life of coding like you guys are doing, which > is my direct feeling while I am in Linaro. :) > Thanks for comments. > > Yong > -------------- next part -------------- An HTML attachment was scrubbed... URL: <http://lists.infradead.org/pipermail/linux-arm-kernel/attachments/20101012/c5d94aa6/attachment-0001.html> ^ permalink raw reply [flat|nested] 11+ messages in thread
end of thread, other threads:[~2010-10-19 8:31 UTC | newest]
Thread overview: 11+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2010-10-08 8:08 cpufreq for freescale mx51 yong.shen at linaro.org
2010-10-08 8:08 ` [PATCHv2] " yong.shen at linaro.org
2010-10-13 10:38 ` Amit Kucheria
2010-10-18 5:46 ` Yong Shen
2010-10-13 17:54 ` Sascha Hauer
2010-10-18 5:43 ` Yong Shen
2010-10-18 8:31 ` Sascha Hauer
2010-10-18 9:08 ` Yong Shen
2010-10-18 9:31 ` Sascha Hauer
2010-10-19 8:31 ` Yong Shen
[not found] <1285738417-1638-1-git-send-email-yong.shen@linaro.org>
2010-09-30 10:48 ` [PATCH] " Amit Kucheria
2010-10-06 9:51 ` Sascha Hauer
2010-10-07 3:36 ` Yong Shen
2010-10-07 7:30 ` Sascha Hauer
2010-10-07 12:00 ` Yong Shen
2010-10-12 7:11 ` [PATCHv2] " Yong Shen
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).