summaryrefslogtreecommitdiff
path: root/recipes/linux/linux-2.6.27/ts72xx/0030-EP93xx-CPUfreq-driver.patch
diff options
context:
space:
mode:
authorPetr Štetiar <ynezz@true.cz>2010-03-03 20:43:16 +0100
committerMarcin Juszkiewicz <marcin@juszkiewicz.com.pl>2010-03-04 15:11:11 +0100
commit07046be146f644760476cc593c31a3c7e16636ad (patch)
tree4a941854558b24acedb981720a106e9543a81811 /recipes/linux/linux-2.6.27/ts72xx/0030-EP93xx-CPUfreq-driver.patch
parentb5263c641ea29e0d3064318bf8a95ceef010d4fd (diff)
linux 2.6.27: add support for ts72xx and make it default kernel
Signed-off-by: Petr Štetiar <ynezz@true.cz> Acked-by: Marcin Juszkiewicz <marcin@juszkiewicz.com.pl>
Diffstat (limited to 'recipes/linux/linux-2.6.27/ts72xx/0030-EP93xx-CPUfreq-driver.patch')
-rw-r--r--recipes/linux/linux-2.6.27/ts72xx/0030-EP93xx-CPUfreq-driver.patch332
1 files changed, 332 insertions, 0 deletions
diff --git a/recipes/linux/linux-2.6.27/ts72xx/0030-EP93xx-CPUfreq-driver.patch b/recipes/linux/linux-2.6.27/ts72xx/0030-EP93xx-CPUfreq-driver.patch
new file mode 100644
index 0000000000..eecbf0b3ce
--- /dev/null
+++ b/recipes/linux/linux-2.6.27/ts72xx/0030-EP93xx-CPUfreq-driver.patch
@@ -0,0 +1,332 @@
+From 9334371292b94cebacd6383c8d60a06bdd7d899b Mon Sep 17 00:00:00 2001
+From: Matthieu Crapet <mcrapet@gmail.com>
+Date: Sun, 4 Jan 2009 14:37:24 +0100
+Subject: [PATCH] EP93xx CPUfreq driver
+MIME-Version: 1.0
+Content-Type: text/plain; charset=utf-8
+Content-Transfer-Encoding: 8bit
+
+Signed-off-by: Petr Štetiar <ynezz@true.cz>
+---
+ arch/arm/Kconfig | 11 ++-
+ arch/arm/mach-ep93xx/Makefile | 2 +
+ arch/arm/mach-ep93xx/cpufreq.c | 265 ++++++++++++++++++++++++++++++++++++++++
+ 3 files changed, 277 insertions(+), 1 deletions(-)
+ create mode 100644 arch/arm/mach-ep93xx/cpufreq.c
+
+diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig
+index d196fe8..f6259a8 100644
+--- a/arch/arm/Kconfig
++++ b/arch/arm/Kconfig
+@@ -1007,7 +1007,7 @@ config ATAGS_PROC
+
+ endmenu
+
+-if (ARCH_SA1100 || ARCH_INTEGRATOR || ARCH_OMAP || ARCH_IMX || ARCH_PXA)
++if (ARCH_SA1100 || ARCH_INTEGRATOR || ARCH_OMAP || ARCH_IMX || ARCH_EP93XX || ARCH_PXA)
+
+ menu "CPU Frequency scaling"
+
+@@ -1043,6 +1043,15 @@ config CPU_FREQ_IMX
+
+ If in doubt, say N.
+
++config CPU_FREQ_EP93XX
++ tristate "CPUfreq driver for EP93XX CPUs"
++ depends on ARCH_EP93XX && CPU_FREQ
++ default n
++ help
++ This enables the CPUfreq driver for EP9301 CPUs. Not tested with EP9302.
++
++ If in doubt, say N.
++
+ config CPU_FREQ_PXA
+ bool
+ depends on CPU_FREQ && ARCH_PXA && PXA25x
+diff --git a/arch/arm/mach-ep93xx/Makefile b/arch/arm/mach-ep93xx/Makefile
+index 2f65745..4b6b542 100644
+--- a/arch/arm/mach-ep93xx/Makefile
++++ b/arch/arm/mach-ep93xx/Makefile
+@@ -6,6 +6,8 @@ obj-m :=
+ obj-n :=
+ obj- :=
+
++obj-$(CONFIG_CPU_FREQ_EP93XX) += cpufreq.o
++
+ obj-$(CONFIG_MACH_ADSSPHERE) += adssphere.o
+ obj-$(CONFIG_MACH_EDB9302) += edb9302.o
+ obj-$(CONFIG_MACH_EDB9302A) += edb9302a.o
+diff --git a/arch/arm/mach-ep93xx/cpufreq.c b/arch/arm/mach-ep93xx/cpufreq.c
+new file mode 100644
+index 0000000..1721ac4
+--- /dev/null
++++ b/arch/arm/mach-ep93xx/cpufreq.c
+@@ -0,0 +1,265 @@
++/*
++ * cpufreq.c: clock scaling for Cirrus EP93XX embedded chip
++ *
++ * Copyright (C) 2008 Matthieu Crapet <mcrapet@gmail.com>
++ *
++ * Based on "cpu-ep93xx.c" driver (for 2.4 kernel) by
++ * Bob Lees bob@diamond.demon.co.uk (Diamond Consulting Services Ltd)
++ * Ideas taken from "clock.c" by Lennert Buytenhek <buytenh@wantstofly.org>
++ *
++ * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
++ *
++ * Theory of operations
++ * ====================
++ *
++ * Clock scaling can be used to lower the power consumption of the CPU
++ * core. For this processor the major power saving is reducing the mem clk.
++ *
++ * The ep93xx has 2 registers to control the 2 PLLs of the ep93xx:
++ * PLL1 controls the cpu, bus and peripheral clocks;
++ * PLL2 controls the USB, MIR and ADC clocks.
++ *
++ * ClkSet1 (EP93XX_SYSCON_CLOCK_SET1) 0x80930020 Clock speed control 1 (i.e. PLL1 config)
++ * ClkSet2 (EP93XX_SYSCON_CLOCK_SET2) 0x80930024 Clock speed control 2 (i.e. PLL2 config)
++ *
++ * This driver only focus on PLL1. The pll has two multipliers/dividers:
++ * Fout = 14.7456 * (PLL1_X1FBD + 1) * (PLL1_X2FBD + 1) / ((PLL1_X2IPD + 1) * 2 ^ PLL1_PS)
++ * = 14.7456 * (PLL1_X1FBD + 1) * (PLL1_X2FBD + 1) / (PLL1_X2IPD + 1) / 2 ^ PLL1_PS
++ *
++ *
++ * fclk [processor ] = pll1 / fclk_divisor
++ * hclk [AHB bus clock] = pll1 / hclk_divisor
++ * pclk [APB bus clock] = hclk / pclk_divisor
++ * fclk >= hclk > pclk
++ *
++ * EP9301 EP9302/07/12/15
++ * PLL1 fout max (MHz) 528 528
++ * fclk min (MHz) 12.9 12.9
++ * fclk max (MHz) 166 200
++ * hclk max (MHz) 66 100
++ * pclk max (MHz) 50 50
++ *
++ * Notes:
++ * - Ethernet (100 MBit) doesn't work with hclk < 25MHz.
++ * - This driver does not use the clk_{put,roundrate} (clock.c) functions. It is
++ * standalone.
++ * - Is it safe to have fclk = hclk ?
++ */
++
++#include <linux/kernel.h>
++#include <linux/types.h>
++#include <linux/init.h>
++#include <linux/cpufreq.h>
++#include <mach/hardware.h>
++
++
++/* ClkSet1 register */
++#define SYSCON_CLKSET1_PLL1_PS_SHIFT 16
++#define SYSCON_CLKSET1_PCLK_DIV_SHIFT 18
++#define SYSCON_CLKSET1_HCLK_DIV_SHIFT 20
++#define SYSCON_CLKSET1_FCLK_DIV_SHIFT 25
++
++#define CLKSET1(p, pl, pd, h, f) ( p | \
++ ( pl << SYSCON_CLKSET1_PLL1_PS_SHIFT) | \
++ ( pd << SYSCON_CLKSET1_PCLK_DIV_SHIFT)| \
++ ( h << SYSCON_CLKSET1_HCLK_DIV_SHIFT) | \
++ ( f << SYSCON_CLKSET1_FCLK_DIV_SHIFT))
++
++typedef struct {
++ int speed; /* in kHz */
++ u32 preset; /* x1fbd, x2fbd and x2ipd are left unchanged */
++ u32 pll1_ps; /* sets final divide from pll */
++ u32 pdiv; /* sets pclk, peripheral clk (division of hclk) */
++ u32 hdiv; /* sets hclk, bus (memory) clk */
++ u32 fdiv; /* sets fclk, processor clk */
++} ep93xx_speed_settings_t;
++
++static char fclk_divisors[] = { 1, 2, 4, 8, 16, 1, 1, 1 };
++
++
++/* Suitable for EP9301. Assumed: PLL1 = 331.8 MHz (X1FBD1=19, X1FBD2=17, X2IPD=15)*/
++static ep93xx_speed_settings_t ep93xx_clkset1_settings[] =
++{
++ /* { speed, preset, pll1_ps, pdiv, hdiv, fdiv } */
++ { 165888, 0x00809a2f, 0, 1, 3, 1 }, /* [0x02B49A2F] fclk=165.9 (fdiv=2), hclk=66.4 (hdiv=5), pclk=33.2 (pdiv=2), ps=1 */
++ { 165887, 0x00809a2f, 1, 1, 3, 0 }, /* [0x00B59A2F] fclk=165.9 (fdiv=1), hclk=33.2 (hdiv=5), pclk=16.6 (pdiv=2), ps=2 */
++ { 82944, 0x00809a2f, 0, 1, 3, 2 }, /* [0x04B49A2F] fclk=82.9 (fdiv=4), hclk=66.4 (hdiv=5), pclk=33.2 (pdiv=2), ps=1 */
++ { 82943, 0x00809a2f, 0, 1, 4, 2 }, /* [0x04C49A2F] fclk=82.9 (fdiv=4), hclk=55.3 (hdiv=6), pclk=27.6 (pdiv=2), ps=1 */
++ { 82942, 0x00809a2f, 1, 1, 2, 1 }, /* [0x02A59A2F] fclk=82.9 (fdiv=2), hclk=41.5 (hdiv=4), pclk=20.7 (pdiv=2), ps=2 */
++ { 41472, 0x00809a2f, 0, 1, 5, 3 }, /* [0x06D49A2F] fclk=41.5 (fdiv=8), hclk=41.5 (hdiv=8), pclk=20.7 (pdiv=2), ps=1 */
++};
++
++#if 0
++/* Suitable for EP9302/07/12/15. Assumed: PLL1 = 400.1 MHz (X1FBD1=23, X1FBD2=25, X2IPD=22) */
++static ep93xx_speed_settings_t ep93xx_clkset1_settings[] =
++{
++ /* { speed, preset, pll1_ps, pdiv, hdiv, fdiv } */
++ { 200027, 0x0080bb36, 0, 1, 2, 1 }, /* [0x02A4BB36] fclk=200.0 (fdiv=2), hclk=100.0 (hdiv=4), pclk=50.0 (pdiv=2), ps=1 */
++ { 200026, 0x0080bb36, 0, 1, 3, 1 }, /* [0x02B4BB36] fclk=200.0 (fdiv=2), hclk=80.0 (hdiv=5), pclk=40.0 (pdiv=2), ps=1 */
++ { 200025, 0x0080bb36, 0, 1, 4, 1 }, /* [0x02C4BB36] fclk=200.0 (fdiv=2), hclk=66.7 (hdiv=6), pclk=33.3 (pdiv=2), ps=1 */
++ { 100013, 0x0080bb36, 0, 1, 3, 2 }, /* [0x04B4BB36] fclk=100.0 (fdiv=4), hclk=80.0 (hdiv=5), pclk=40.0 (pdiv=2), ps=1 */
++ { 100012, 0x0080bb36, 1, 1, 2, 1 }, /* [0x02A5BB36] fclk=100.0 (fdiv=2), hclk=50.0 (hdiv=4), pclk=25.0 (pdiv=2), ps=2 */
++ { 100011, 0x0080bb36, 1, 1, 1, 1 }, /* [0x0295BB36] fclk=100.0 (fdiv=2), hclk=100.0 (hdiv=2), pclk=50.0 (pdiv=2), ps=2 */
++ { 50006, 0x0080bb36, 2, 1, 2, 1 }, /* [0x02A6BB36] fclk=50.0 (fdiv=2), hclk=25.0 (hdiv=4), pclk=12.5 (pdiv=2), ps=4 */
++ { 50005, 0x0080bb36, 2, 1, 1, 1 }, /* [0x0296BB36] fclk=50.0 (fdiv=2), hclk=50.0 (hdiv=2), pclk=25.0 (pdiv=2), ps=4 */
++ { 25003, 0x0080bb36, 3, 1, 1, 1 }, /* [0x0297BB36] fclk=25.0 (fdiv=2), hclk=25.0 (hdiv=2), pclk=12.5 (pdiv=2), ps=8 */
++};
++#endif
++
++
++static unsigned long calc_pll_rate(u32 config_word)
++{
++ unsigned long long rate;
++
++ rate = 14745600;
++ rate *= ((config_word >> 11) & 0x1f) + 1; /* X1FBD (5 bits) */
++ rate *= ((config_word >> 5) & 0x3f) + 1; /* X2FBD (6 bits) */
++ do_div(rate, (config_word & 0x1f) + 1); /* X2IPD (5 bits) */
++ rate = rate >> ((config_word >> 16) & 3); /* PS (2 bits) */
++
++ return (unsigned long)rate;
++}
++
++
++static ep93xx_speed_settings_t *ep93xx_find_clkset1(unsigned int khz, unsigned int relation)
++{
++ int i;
++ ep93xx_speed_settings_t *p = &ep93xx_clkset1_settings[0];
++
++ switch (relation) {
++ case CPUFREQ_RELATION_L: /* lowest frequency at or above target */
++ for (i = 0; i < sizeof(ep93xx_clkset1_settings)/sizeof(ep93xx_speed_settings_t); i++) {
++ if (ep93xx_clkset1_settings[i].speed < khz)
++ continue;
++ if (p->speed > ep93xx_clkset1_settings[i].speed) // take lowest value
++ p = &ep93xx_clkset1_settings[i];
++ }
++ break;
++
++ case CPUFREQ_RELATION_H: /* highest frequency below or at target */
++ for (i = 0; i < sizeof(ep93xx_clkset1_settings)/sizeof(ep93xx_speed_settings_t); i++) {
++ if (ep93xx_clkset1_settings[i].speed > khz)
++ continue;
++ if (p->speed < ep93xx_clkset1_settings[i].speed) // take highest value
++ p = &ep93xx_clkset1_settings[i];
++ }
++ break;
++ }
++
++ return p;
++}
++
++
++static int ep93xx_verify_speed(struct cpufreq_policy *policy)
++{
++ if (policy->cpu != 0)
++ return -EINVAL;
++
++ cpufreq_verify_within_limits(policy, policy->cpuinfo.min_freq, policy->cpuinfo.max_freq);
++
++ return 0;
++}
++
++
++static unsigned int ep93xx_get_speed(unsigned int cpu)
++{
++ unsigned int freq;
++ u32 value;
++
++ if (cpu)
++ return 0;
++
++ value = __raw_readl(EP93XX_SYSCON_CLOCK_SET1);
++ if (!(value & 0x00800000)) { /* PLL1 bypassed? */
++ freq = 14745600;
++ } else {
++ freq = calc_pll_rate(value);
++ }
++ freq /= fclk_divisors[(value >> 25) & 0x7];
++
++ freq = (freq + 500) / 1000; /* rounded result in kHz */
++ return freq;
++}
++
++
++static int ep93xx_set_target(struct cpufreq_policy *policy,
++ unsigned int target_freq,
++ unsigned int relation)
++{
++ struct cpufreq_freqs freqs;
++ ep93xx_speed_settings_t *config;
++ u32 value;
++
++ config = ep93xx_find_clkset1(target_freq, relation);
++
++ freqs.old = ep93xx_get_speed(0);
++ freqs.new = config->speed;
++ freqs.cpu = 0;
++ freqs.flags = 0;
++
++ //printk("ep93xx: target_freq=%d, old=%d new=%d (kHz) rel=%d\n", target_freq, freqs.old, freqs.new, relation);
++
++ cpufreq_notify_transition(&freqs, CPUFREQ_PRECHANGE);
++
++ value = CLKSET1(config->preset, config->pll1_ps,
++ config->pdiv, config->hdiv, config->fdiv);
++ __raw_writel(value, EP93XX_SYSCON_CLOCK_SET1);
++
++ /* 5 nops required to fluch instruction pipeline */
++ __asm__ __volatile__("nop; nop; nop; nop; nop");
++
++ cpufreq_notify_transition(&freqs, CPUFREQ_POSTCHANGE);
++
++ return 0;
++}
++
++
++static int __init ep93xx_cpufreq_driver_init(struct cpufreq_policy *policy)
++{
++ printk(KERN_INFO "ep93xx-cpufreq: driver v1.0\n");
++
++ if (policy->cpu != 0)
++ return -EINVAL;
++
++ policy->cur = policy->min = policy->max = ep93xx_get_speed(0);
++
++ policy->cpuinfo.min_freq = 13000;
++
++ /* Check CPU version (ep9301 special case) */
++ if (policy->cur <= 166000)
++ policy->cpuinfo.max_freq = 166000;
++ else
++ policy->cpuinfo.max_freq = 200000;
++
++ policy->cpuinfo.transition_latency = 1000000; /* 1ms (unknown = CPUFREQ_ETERNAL) */
++
++ return 0;
++}
++
++static struct cpufreq_driver ep93xx_driver = {
++ .flags = CPUFREQ_STICKY,
++ .verify = ep93xx_verify_speed,
++ .target = ep93xx_set_target,
++ .get = ep93xx_get_speed,
++ .init = ep93xx_cpufreq_driver_init,
++ .name = "ep93xx",
++};
++
++static int __init ep93xx_cpufreq_init(void)
++{
++ return cpufreq_register_driver(&ep93xx_driver);
++}
++
++arch_initcall(ep93xx_cpufreq_init);
+--
+1.6.0.4
+