summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorNancy Chen <Nancy.Chen@freescale.com>2010-04-06 22:42:16 -0500
committerAlejandro Gonzalez <alex.gonzalez@digi.com>2010-05-25 11:20:22 +0200
commit1b86778c6746d9173d8d00dd615318472f72ce60 (patch)
tree2003de6d571b83c559ed38d4c1960b80f443d7f9
parentbcd64ee41f84611baa03ea12f887256015b87ef6 (diff)
ENGR00121905-1 MX23: Add cpufreq support
Add cpufreq support. Signed-off-by: Nancy Chen <Nancy.Chen@freescale.com> Signed-off-by: Alejandro Gonzalez <alex.gonzalez@digi.com>
-rw-r--r--arch/arm/Kconfig2
-rw-r--r--arch/arm/configs/imx23evk_defconfig15
-rw-r--r--arch/arm/mach-mx23/Makefile2
-rw-r--r--arch/arm/mach-mx23/bus_freq.c220
-rw-r--r--arch/arm/mach-mx23/clock.c10
5 files changed, 245 insertions, 4 deletions
diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig
index fa492c2e051a..3f1b470beacf 100644
--- a/arch/arm/Kconfig
+++ b/arch/arm/Kconfig
@@ -1272,7 +1272,7 @@ endmenu
menu "CPU Power Management"
-if (ARCH_SA1100 || ARCH_INTEGRATOR || ARCH_OMAP || ARCH_PXA || ARCH_S3C64XX || ARCH_MXC || ARCH_STMP3XXX || ARCH_MX28)
+if (ARCH_SA1100 || ARCH_INTEGRATOR || ARCH_OMAP || ARCH_PXA || ARCH_S3C64XX || ARCH_MXC || ARCH_STMP3XXX || ARCH_MX23 || ARCH_MX28)
source "drivers/cpufreq/Kconfig"
diff --git a/arch/arm/configs/imx23evk_defconfig b/arch/arm/configs/imx23evk_defconfig
index 1d406883867e..8fc293845675 100644
--- a/arch/arm/configs/imx23evk_defconfig
+++ b/arch/arm/configs/imx23evk_defconfig
@@ -295,6 +295,21 @@ CONFIG_CMDLINE="console=ttyAM0,115200 root=/dev/mmcblk0p2 rootwait lcd_panel=lms
#
# CPU Power Management
#
+CONFIG_CPU_FREQ=y
+CONFIG_CPU_FREQ_TABLE=y
+# CONFIG_CPU_FREQ_DEBUG is not set
+CONFIG_CPU_FREQ_STAT=y
+# CONFIG_CPU_FREQ_STAT_DETAILS is not set
+# CONFIG_CPU_FREQ_DEFAULT_GOV_PERFORMANCE is not set
+# CONFIG_CPU_FREQ_DEFAULT_GOV_POWERSAVE is not set
+CONFIG_CPU_FREQ_DEFAULT_GOV_USERSPACE=y
+# CONFIG_CPU_FREQ_DEFAULT_GOV_ONDEMAND is not set
+# CONFIG_CPU_FREQ_DEFAULT_GOV_CONSERVATIVE is not set
+CONFIG_CPU_FREQ_GOV_PERFORMANCE=y
+CONFIG_CPU_FREQ_GOV_POWERSAVE=y
+CONFIG_CPU_FREQ_GOV_USERSPACE=y
+# CONFIG_CPU_FREQ_GOV_ONDEMAND is not set
+# CONFIG_CPU_FREQ_GOV_CONSERVATIVE is not set
# CONFIG_CPU_IDLE is not set
#
diff --git a/arch/arm/mach-mx23/Makefile b/arch/arm/mach-mx23/Makefile
index 72c252d452ff..622981c9572d 100644
--- a/arch/arm/mach-mx23/Makefile
+++ b/arch/arm/mach-mx23/Makefile
@@ -1,7 +1,7 @@
#
# Makefile for the linux kernel.
#
-obj-y += pinctrl.o clock.o device.o serial.o power.o pm.o sleep.o
+obj-y += pinctrl.o clock.o device.o serial.o power.o pm.o sleep.o bus_freq.o
# Board select
obj-$(CONFIG_MACH_MX23EVK) += mx23evk.o mx23evk_pins.o
diff --git a/arch/arm/mach-mx23/bus_freq.c b/arch/arm/mach-mx23/bus_freq.c
new file mode 100644
index 000000000000..b4efabdfefcc
--- /dev/null
+++ b/arch/arm/mach-mx23/bus_freq.c
@@ -0,0 +1,220 @@
+/*
+ * Copyright (C) 2010 Freescale Semiconductor, Inc. All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+/*!
+ * @file bus_freq.c
+ *
+ * @brief A common API for the Freescale Semiconductor i.MXC CPUfreq module.
+ *
+ * @ingroup PM
+ */
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/delay.h>
+#include <linux/init.h>
+#include <linux/err.h>
+#include <linux/clk.h>
+#include <linux/spinlock.h>
+#include <linux/platform_device.h>
+#include <linux/notifier.h>
+#include <linux/cpufreq.h>
+
+#include <mach/hardware.h>
+#include <linux/io.h>
+#include <asm/system.h>
+#include <mach/clock.h>
+#include <mach/bus_freq.h>
+
+#include "regs-clkctrl.h"
+#include "regs-digctl.h"
+
+#define CLKCTRL_BASE_ADDR IO_ADDRESS(CLKCTRL_PHYS_ADDR)
+#define DIGCTRL_BASE_ADDR IO_ADDRESS(DIGCTL_PHYS_ADDR)
+#define BP_CLKCTRL_HBUS_ASM_ENABLE 20
+#define CLKCTRL_PLL_PWD_BIT 17
+#define CLKCTRL_PLL_BYPASS 0x1ff
+#define BF(value, field) (((value) << BP_##field) & BM_##field)
+
+struct profile profiles[] = {
+ { 454736, 151580, 130910, 0, 1550000,
+ 1450000, 355000, 3300000, 1750000, 0 },
+ { 392727, 130910, 130910, 0, 1475000,
+ 1375000, 225000, 3300000, 1750000, 0 },
+ { 360000, 120000, 120000, 0, 1375000,
+ 1275000, 200000, 3300000, 1750000, 0 },
+ { 261818, 130910, 130910, 0, 1275000,
+ 1175000, 173000, 3300000, 1750000, 0 },
+#ifdef CONFIG_MXS_RAM_MDDR
+ { 64000, 64000, 48000, 3, 1050000,
+ 975000, 150000, 3300000, 1750000, 0 },
+ { 24000, 24000, 24000, 3, 1050000,
+ 975000, 150000, 3075000, 1725000, 1 },
+#else
+ { 64000, 64000, 96000, 3, 1050000,
+ 975000, 150000, 3300000, 1750000, 0 },
+ { 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0 },
+#endif
+};
+
+static struct clk *usb_clk;
+static struct clk *lcdif_clk;
+u32 clkseq_setting;
+
+int low_freq_used(void)
+{
+ if ((clk_get_usecount(usb_clk) == 0)
+ && (clk_get_usecount(lcdif_clk) == 0))
+ return 1;
+ else
+ return 0;
+ }
+
+void hbus_auto_slow_mode_enable(void)
+{
+ __raw_writel(BP_CLKCTRL_HBUS_ASM_ENABLE,
+ CLKCTRL_BASE_ADDR + HW_CLKCTRL_HBUS_SET);
+}
+EXPORT_SYMBOL(hbus_auto_slow_mode_enable);
+
+void hbus_auto_slow_mode_disable(void)
+{
+ __raw_writel(BP_CLKCTRL_HBUS_ASM_ENABLE,
+ CLKCTRL_BASE_ADDR + HW_CLKCTRL_HBUS_CLR);
+}
+EXPORT_SYMBOL(hbus_auto_slow_mode_disable);
+
+int cpu_clk_set_pll_on(struct clk *clk, unsigned int freq)
+{
+ struct cpufreq_freqs freqs;
+
+ freqs.old = clk_get_rate(clk);
+ freqs.cpu = 0;
+ freqs.new = freq;
+
+ if (freqs.old == 24000 && freqs.new > 24000) {
+ /* turn pll on */
+ __raw_writel(CLKCTRL_PLL_PWD_BIT, CLKCTRL_BASE_ADDR +
+ HW_CLKCTRL_PLLCTRL0_SET);
+ udelay(10);
+ } else if (freqs.old > 24000 && freqs.new == 24000)
+ clkseq_setting = __raw_readl(CLKCTRL_BASE_ADDR +
+ HW_CLKCTRL_CLKSEQ);
+ return 0;
+}
+
+int cpu_clk_set_pll_off(struct clk *clk, unsigned int freq)
+{
+ struct cpufreq_freqs freqs;
+
+ freqs.old = clk_get_rate(clk);
+ freqs.cpu = 0;
+ freqs.new = freq;
+
+ if (freqs.old > 24000 && freqs.new == 24000) {
+ /* turn pll off */
+ __raw_writel(CLKCTRL_PLL_PWD_BIT, CLKCTRL_BASE_ADDR +
+ HW_CLKCTRL_PLLCTRL0_CLR);
+ __raw_writel(CLKCTRL_PLL_BYPASS, CLKCTRL_BASE_ADDR +
+ HW_CLKCTRL_CLKSEQ);
+ } else if (freqs.old == 24000 && freqs.new > 24000)
+ __raw_writel(clkseq_setting, CLKCTRL_BASE_ADDR +
+ HW_CLKCTRL_CLKSEQ);
+
+ return 0;
+}
+
+int timing_ctrl_rams(int ss)
+{
+ __raw_writel(BF(ss, DIGCTL_ARMCACHE_VALID_SS) |
+ BF(ss, DIGCTL_ARMCACHE_DRTY_SS) |
+ BF(ss, DIGCTL_ARMCACHE_CACHE_SS) |
+ BF(ss, DIGCTL_ARMCACHE_DTAG_SS) |
+ BF(ss, DIGCTL_ARMCACHE_ITAG_SS),
+ DIGCTRL_BASE_ADDR + HW_DIGCTL_ARMCACHE);
+ return 0;
+}
+
+/*!
+ * This is the probe routine for the bus frequency driver.
+ *
+ * @param pdev The platform device structure
+ *
+ * @return The function returns 0 on success
+ *
+ */
+static int __devinit busfreq_probe(struct platform_device *pdev)
+{
+ int ret = 0;
+
+ usb_clk = clk_get(NULL, "usb_clk0");
+ if (IS_ERR(usb_clk)) {
+ ret = PTR_ERR(usb_clk);
+ goto out_usb;
+ }
+
+ lcdif_clk = clk_get(NULL, "lcdif");
+ if (IS_ERR(lcdif_clk)) {
+ ret = PTR_ERR(lcdif_clk);
+ goto out_lcd;
+ }
+ return 0;
+
+out_lcd:
+ clk_put(usb_clk);
+out_usb:
+ return ret;
+}
+
+static struct platform_driver busfreq_driver = {
+ .driver = {
+ .name = "busfreq",
+ },
+ .probe = busfreq_probe,
+};
+
+/*!
+ * Initialise the busfreq_driver.
+ *
+ * @return The function always returns 0.
+ */
+
+static int __init busfreq_init(void)
+{
+ if (platform_driver_register(&busfreq_driver) != 0) {
+ printk(KERN_ERR "busfreq_driver register failed\n");
+ return -ENODEV;
+ }
+
+ printk(KERN_INFO "Bus freq driver module loaded\n");
+ return 0;
+}
+
+static void __exit busfreq_cleanup(void)
+{
+ /* Unregister the device structure */
+ platform_driver_unregister(&busfreq_driver);
+}
+
+module_init(busfreq_init);
+module_exit(busfreq_cleanup);
+
+MODULE_AUTHOR("Freescale Semiconductor, Inc.");
+MODULE_DESCRIPTION("BusFreq driver");
+MODULE_LICENSE("GPL");
diff --git a/arch/arm/mach-mx23/clock.c b/arch/arm/mach-mx23/clock.c
index b73b4a6c8bc5..957a70213399 100644
--- a/arch/arm/mach-mx23/clock.c
+++ b/arch/arm/mach-mx23/clock.c
@@ -428,6 +428,7 @@ static struct clk lcdif_clk = {
.get_rate = lcdif_get_rate,
.set_rate = lcdif_set_rate,
.set_parent = clkseq_set_parent,
+ .flags = CPU_FREQ_TRIG_UPDATE,
};
static unsigned long cpu_get_rate(struct clk *clk)
@@ -472,9 +473,9 @@ static int cpu_set_rate(struct clk *clk, unsigned long rate)
u32 val;
u32 reg_val;
- if (rate < 24000)
+ if (rate < 24000000)
return -EINVAL;
- else if (rate == 24000) {
+ else if (rate == 24000000) {
/* switch to the 24M source */
clk_set_parent(clk, &ref_xtal_clk);
} else {
@@ -511,6 +512,7 @@ static int cpu_set_rate(struct clk *clk, unsigned long rate)
/* Do not gate */
__raw_writel(BM_CLKCTRL_FRAC_CLKGATECPU, CLKCTRL_BASE_ADDR +
HW_CLKCTRL_FRAC_CLR);
+
/* write clkctrl_cpu */
reg_val = __raw_readl(CLKCTRL_BASE_ADDR + HW_CLKCTRL_CPU);
reg_val &= ~0x3F;
@@ -654,6 +656,9 @@ static int h_set_rate(struct clk *clk, unsigned long rate)
if (root_rate % round_rate)
return -EINVAL;
+ if ((root_rate < rate) && (root_rate == 64000000))
+ div = 3;
+
reg = __raw_readl(CLKCTRL_BASE_ADDR + HW_CLKCTRL_HBUS);
reg &= ~(BM_CLKCTRL_HBUS_DIV_FRAC_EN | BM_CLKCTRL_HBUS_DIV);
reg |= BF_CLKCTRL_HBUS_DIV(div);
@@ -919,6 +924,7 @@ static struct clk usb_clk = {
.disable = mx23_raw_disable,
.enable_reg = DIGCTRL_BASE_ADDR + HW_DIGCTL_CTRL,
.enable_bits = BM_DIGCTL_CTRL_USB_CLKGATE,
+ .flags = CPU_FREQ_TRIG_UPDATE,
};
static struct clk audio_clk = {