cpufreq: sa11x0: move cpufreq driver to drivers/cpufreq
authorViresh Kumar <viresh.kumar@linaro.org>
Thu, 4 Apr 2013 12:54:16 +0000 (12:54 +0000)
committerRafael J. Wysocki <rafael.j.wysocki@intel.com>
Wed, 10 Apr 2013 11:19:24 +0000 (13:19 +0200)
This patch moves cpufreq driver of ARM based sa11x0 platform to drivers/cpufreq.

Signed-off-by: Viresh Kumar <viresh.kumar@linaro.org>
Acked-by: Arnd Bergmann <arnd@arndb.de>
Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
arch/arm/Kconfig
arch/arm/mach-sa1100/Kconfig
arch/arm/mach-sa1100/Makefile
arch/arm/mach-sa1100/cpu-sa1100.c [deleted file]
arch/arm/mach-sa1100/cpu-sa1110.c [deleted file]
arch/arm/mach-sa1100/include/mach/generic.h [new file with mode: 0644]
drivers/cpufreq/Kconfig.arm
drivers/cpufreq/Makefile
drivers/cpufreq/sa1100-cpufreq.c [new file with mode: 0644]
drivers/cpufreq/sa1110-cpufreq.c [new file with mode: 0644]

index c3563f6dc9f2389800fdd5c3955c1e2a1f9011f0..940b13ffd9c9b594c88172f68da73da17ac835e9 100644 (file)
@@ -2150,7 +2150,6 @@ endmenu
 menu "CPU Power Management"
 
 if ARCH_HAS_CPUFREQ
-
 source "drivers/cpufreq/Kconfig"
 
 config CPU_FREQ_IMX
@@ -2160,12 +2159,6 @@ config CPU_FREQ_IMX
        help
          This enables the CPUfreq driver for i.MX CPUs.
 
-config CPU_FREQ_SA1100
-       bool
-
-config CPU_FREQ_SA1110
-       bool
-
 config CPU_FREQ_S3C
        bool
        help
index ca14dbdcfb222df027713bc2eac93ae7ac6d3e2b..04f9784ff0edab68132d31bc15110935a4b60883 100644 (file)
@@ -4,7 +4,7 @@ menu "SA11x0 Implementations"
 
 config SA1100_ASSABET
        bool "Assabet"
-       select CPU_FREQ_SA1110
+       select ARM_SA1110_CPUFREQ
        help
          Say Y here if you are using the Intel(R) StrongARM(R) SA-1110
          Microprocessor Development Board (also known as the Assabet).
@@ -20,7 +20,7 @@ config ASSABET_NEPONSET
 
 config SA1100_CERF
        bool "CerfBoard"
-       select CPU_FREQ_SA1110
+       select ARM_SA1110_CPUFREQ
        help
          The Intrinsyc CerfBoard is based on the StrongARM 1110 (Discontinued).
          More information is available at:
@@ -47,7 +47,7 @@ endchoice
 
 config SA1100_COLLIE
        bool "Sharp Zaurus SL5500"
-       # FIXME: select CPU_FREQ_SA11x0
+       # FIXME: select ARM_SA11x0_CPUFREQ
        select SHARP_LOCOMO
        select SHARP_PARAM
        select SHARP_SCOOP
@@ -56,7 +56,7 @@ config SA1100_COLLIE
 
 config SA1100_H3100
        bool "Compaq iPAQ H3100"
-       select CPU_FREQ_SA1110
+       select ARM_SA1110_CPUFREQ
        select HTC_EGPIO
        help
          Say Y here if you intend to run this kernel on the Compaq iPAQ
@@ -67,7 +67,7 @@ config SA1100_H3100
 
 config SA1100_H3600
        bool "Compaq iPAQ H3600/H3700"
-       select CPU_FREQ_SA1110
+       select ARM_SA1110_CPUFREQ
        select HTC_EGPIO
        help
          Say Y here if you intend to run this kernel on the Compaq iPAQ
@@ -78,7 +78,7 @@ config SA1100_H3600
 
 config SA1100_BADGE4
        bool "HP Labs BadgePAD 4"
-       select CPU_FREQ_SA1100
+       select ARM_SA1100_CPUFREQ
        select SA1111
        help
          Say Y here if you want to build a kernel for the HP Laboratories
@@ -86,7 +86,7 @@ config SA1100_BADGE4
 
 config SA1100_JORNADA720
        bool "HP Jornada 720"
-       # FIXME: select CPU_FREQ_SA11x0
+       # FIXME: select ARM_SA11x0_CPUFREQ
        select SA1111
        help
          Say Y here if you want to build a kernel for the HP Jornada 720
@@ -105,14 +105,14 @@ config SA1100_JORNADA720_SSP
 
 config SA1100_HACKKIT
        bool "HackKit Core CPU Board"
-       select CPU_FREQ_SA1100
+       select ARM_SA1100_CPUFREQ
        help
          Say Y here to support the HackKit Core CPU Board
          <http://hackkit.eletztrick.de>;
 
 config SA1100_LART
        bool "LART"
-       select CPU_FREQ_SA1100
+       select ARM_SA1100_CPUFREQ
        help
          Say Y here if you are using the Linux Advanced Radio Terminal
          (also known as the LART).  See <http://www.lartmaker.nl/> for
@@ -120,7 +120,7 @@ config SA1100_LART
 
 config SA1100_NANOENGINE
        bool "nanoEngine"
-       select CPU_FREQ_SA1110
+       select ARM_SA1110_CPUFREQ
        select PCI
        select PCI_NANOENGINE
        help
@@ -130,7 +130,7 @@ config SA1100_NANOENGINE
 
 config SA1100_PLEB
        bool "PLEB"
-       select CPU_FREQ_SA1100
+       select ARM_SA1100_CPUFREQ
        help
          Say Y here if you are using version 1 of the Portable Linux
          Embedded Board (also known as PLEB).
@@ -139,7 +139,7 @@ config SA1100_PLEB
 
 config SA1100_SHANNON
        bool "Shannon"
-       select CPU_FREQ_SA1100
+       select ARM_SA1100_CPUFREQ
        help
          The Shannon (also known as a Tuxscreen, and also as a IS2630) was a
          limited edition webphone produced by Philips. The Shannon is a SA1100
@@ -148,7 +148,7 @@ config SA1100_SHANNON
 
 config SA1100_SIMPAD
        bool "Simpad"
-       select CPU_FREQ_SA1110
+       select ARM_SA1110_CPUFREQ
        help
          The SIEMENS webpad SIMpad is based on the StrongARM 1110. There
          are two different versions CL4 and SL4. CL4 has 32MB RAM and 16MB
index 1aed9e70465d5d3f8572c0bec308d3e06d089ab3..2732eef48966bfa337d34f32fb806f4e604d515c 100644 (file)
@@ -8,9 +8,6 @@ obj-m :=
 obj-n :=
 obj-  :=
 
-obj-$(CONFIG_CPU_FREQ_SA1100)          += cpu-sa1100.o
-obj-$(CONFIG_CPU_FREQ_SA1110)          += cpu-sa1110.o
-
 # Specific board support
 obj-$(CONFIG_SA1100_ASSABET)           += assabet.o
 obj-$(CONFIG_ASSABET_NEPONSET)         += neponset.o
diff --git a/arch/arm/mach-sa1100/cpu-sa1100.c b/arch/arm/mach-sa1100/cpu-sa1100.c
deleted file mode 100644 (file)
index 3268761..0000000
+++ /dev/null
@@ -1,248 +0,0 @@
-/*
- * cpu-sa1100.c: clock scaling for the SA1100
- *
- * Copyright (C) 2000 2001, The Delft University of Technology
- *
- * Authors:
- * - Johan Pouwelse (J.A.Pouwelse@its.tudelft.nl): initial version
- * - Erik Mouw (J.A.K.Mouw@its.tudelft.nl):
- *   - major rewrite for linux-2.3.99
- *   - rewritten for the more generic power management scheme in
- *     linux-2.4.5-rmk1
- *
- * This software has been developed while working on the LART
- * computing board (http://www.lartmaker.nl/), which is
- * sponsored by the Mobile Multi-media Communications
- * (http://www.mobimedia.org/) and Ubiquitous Communications
- * (http://www.ubicom.tudelft.nl/) projects.
- *
- * The authors can be reached at:
- *
- *  Erik Mouw
- *  Information and Communication Theory Group
- *  Faculty of Information Technology and Systems
- *  Delft University of Technology
- *  P.O. Box 5031
- *  2600 GA Delft
- *  The Netherlands
- *
- *
- * 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. This will give you a somewhat longer running time.
- *
- * The SA-1100 has a single register to change the core clock speed:
- *
- *   PPCR      0x90020014    PLL config
- *
- * However, the DRAM timings are closely related to the core clock
- * speed, so we need to change these, too. The used registers are:
- *
- *   MDCNFG    0xA0000000    DRAM config
- *   MDCAS0    0xA0000004    Access waveform
- *   MDCAS1    0xA0000008    Access waveform
- *   MDCAS2    0xA000000C    Access waveform
- *
- * Care must be taken to change the DRAM parameters the correct way,
- * because otherwise the DRAM becomes unusable and the kernel will
- * crash.
- *
- * The simple solution to avoid a kernel crash is to put the actual
- * clock change in ROM and jump to that code from the kernel. The main
- * disadvantage is that the ROM has to be modified, which is not
- * possible on all SA-1100 platforms. Another disadvantage is that
- * jumping to ROM makes clock switching unnecessary complicated.
- *
- * The idea behind this driver is that the memory configuration can be
- * changed while running from DRAM (even with interrupts turned on!)
- * as long as all re-configuration steps yield a valid DRAM
- * configuration. The advantages are clear: it will run on all SA-1100
- * platforms, and the code is very simple.
- *
- * If you really want to understand what is going on in
- * sa1100_update_dram_timings(), you'll have to read sections 8.2,
- * 9.5.7.3, and 10.2 from the "Intel StrongARM SA-1100 Microprocessor
- * Developers Manual" (available for free from Intel).
- *
- */
-
-#include <linux/kernel.h>
-#include <linux/types.h>
-#include <linux/init.h>
-#include <linux/cpufreq.h>
-#include <linux/io.h>
-
-#include <asm/cputype.h>
-
-#include <mach/hardware.h>
-
-#include "generic.h"
-
-struct sa1100_dram_regs {
-       int speed;
-       u32 mdcnfg;
-       u32 mdcas0;
-       u32 mdcas1;
-       u32 mdcas2;
-};
-
-
-static struct cpufreq_driver sa1100_driver;
-
-static struct sa1100_dram_regs sa1100_dram_settings[] = {
-       /*speed,     mdcnfg,     mdcas0,     mdcas1,     mdcas2,   clock freq */
-       { 59000, 0x00dc88a3, 0xcccccccf, 0xfffffffc, 0xffffffff},/*  59.0 MHz */
-       { 73700, 0x011490a3, 0xcccccccf, 0xfffffffc, 0xffffffff},/*  73.7 MHz */
-       { 88500, 0x014e90a3, 0xcccccccf, 0xfffffffc, 0xffffffff},/*  88.5 MHz */
-       {103200, 0x01889923, 0xcccccccf, 0xfffffffc, 0xffffffff},/* 103.2 MHz */
-       {118000, 0x01c29923, 0x9999998f, 0xfffffff9, 0xffffffff},/* 118.0 MHz */
-       {132700, 0x01fb2123, 0x9999998f, 0xfffffff9, 0xffffffff},/* 132.7 MHz */
-       {147500, 0x02352123, 0x3333330f, 0xfffffff3, 0xffffffff},/* 147.5 MHz */
-       {162200, 0x026b29a3, 0x38e38e1f, 0xfff8e38e, 0xffffffff},/* 162.2 MHz */
-       {176900, 0x02a329a3, 0x71c71c1f, 0xfff1c71c, 0xffffffff},/* 176.9 MHz */
-       {191700, 0x02dd31a3, 0xe38e383f, 0xffe38e38, 0xffffffff},/* 191.7 MHz */
-       {206400, 0x03153223, 0xc71c703f, 0xffc71c71, 0xffffffff},/* 206.4 MHz */
-       {221200, 0x034fba23, 0xc71c703f, 0xffc71c71, 0xffffffff},/* 221.2 MHz */
-       {235900, 0x03853a23, 0xe1e1e07f, 0xe1e1e1e1, 0xffffffe1},/* 235.9 MHz */
-       {250700, 0x03bf3aa3, 0xc3c3c07f, 0xc3c3c3c3, 0xffffffc3},/* 250.7 MHz */
-       {265400, 0x03f7c2a3, 0xc3c3c07f, 0xc3c3c3c3, 0xffffffc3},/* 265.4 MHz */
-       {280200, 0x0431c2a3, 0x878780ff, 0x87878787, 0xffffff87},/* 280.2 MHz */
-       { 0, 0, 0, 0, 0 } /* last entry */
-};
-
-static void sa1100_update_dram_timings(int current_speed, int new_speed)
-{
-       struct sa1100_dram_regs *settings = sa1100_dram_settings;
-
-       /* find speed */
-       while (settings->speed != 0) {
-               if (new_speed == settings->speed)
-                       break;
-
-               settings++;
-       }
-
-       if (settings->speed == 0) {
-               panic("%s: couldn't find dram setting for speed %d\n",
-                     __func__, new_speed);
-       }
-
-       /* No risk, no fun: run with interrupts on! */
-       if (new_speed > current_speed) {
-               /* We're going FASTER, so first relax the memory
-                * timings before changing the core frequency
-                */
-
-               /* Half the memory access clock */
-               MDCNFG |= MDCNFG_CDB2;
-
-               /* The order of these statements IS important, keep 8
-                * pulses!!
-                */
-               MDCAS2 = settings->mdcas2;
-               MDCAS1 = settings->mdcas1;
-               MDCAS0 = settings->mdcas0;
-               MDCNFG = settings->mdcnfg;
-       } else {
-               /* We're going SLOWER: first decrease the core
-                * frequency and then tighten the memory settings.
-                */
-
-               /* Half the memory access clock */
-               MDCNFG |= MDCNFG_CDB2;
-
-               /* The order of these statements IS important, keep 8
-                * pulses!!
-                */
-               MDCAS0 = settings->mdcas0;
-               MDCAS1 = settings->mdcas1;
-               MDCAS2 = settings->mdcas2;
-               MDCNFG = settings->mdcnfg;
-       }
-}
-
-static int sa1100_target(struct cpufreq_policy *policy,
-                        unsigned int target_freq,
-                        unsigned int relation)
-{
-       unsigned int cur = sa11x0_getspeed(0);
-       unsigned int new_ppcr;
-       struct cpufreq_freqs freqs;
-
-       new_ppcr = sa11x0_freq_to_ppcr(target_freq);
-       switch (relation) {
-       case CPUFREQ_RELATION_L:
-               if (sa11x0_ppcr_to_freq(new_ppcr) > policy->max)
-                       new_ppcr--;
-               break;
-       case CPUFREQ_RELATION_H:
-               if ((sa11x0_ppcr_to_freq(new_ppcr) > target_freq) &&
-                   (sa11x0_ppcr_to_freq(new_ppcr - 1) >= policy->min))
-                       new_ppcr--;
-               break;
-       }
-
-       freqs.old = cur;
-       freqs.new = sa11x0_ppcr_to_freq(new_ppcr);
-
-       cpufreq_notify_transition(policy, &freqs, CPUFREQ_PRECHANGE);
-
-       if (freqs.new > cur)
-               sa1100_update_dram_timings(cur, freqs.new);
-
-       PPCR = new_ppcr;
-
-       if (freqs.new < cur)
-               sa1100_update_dram_timings(cur, freqs.new);
-
-       cpufreq_notify_transition(policy, &freqs, CPUFREQ_POSTCHANGE);
-
-       return 0;
-}
-
-static int __init sa1100_cpu_init(struct cpufreq_policy *policy)
-{
-       if (policy->cpu != 0)
-               return -EINVAL;
-       policy->cur = policy->min = policy->max = sa11x0_getspeed(0);
-       policy->cpuinfo.min_freq = 59000;
-       policy->cpuinfo.max_freq = 287000;
-       policy->cpuinfo.transition_latency = CPUFREQ_ETERNAL;
-       return 0;
-}
-
-static struct cpufreq_driver sa1100_driver __refdata = {
-       .flags          = CPUFREQ_STICKY,
-       .verify         = sa11x0_verify_speed,
-       .target         = sa1100_target,
-       .get            = sa11x0_getspeed,
-       .init           = sa1100_cpu_init,
-       .name           = "sa1100",
-};
-
-static int __init sa1100_dram_init(void)
-{
-       if (cpu_is_sa1100())
-               return cpufreq_register_driver(&sa1100_driver);
-       else
-               return -ENODEV;
-}
-
-arch_initcall(sa1100_dram_init);
diff --git a/arch/arm/mach-sa1100/cpu-sa1110.c b/arch/arm/mach-sa1100/cpu-sa1110.c
deleted file mode 100644 (file)
index 38a7733..0000000
+++ /dev/null
@@ -1,407 +0,0 @@
-/*
- *  linux/arch/arm/mach-sa1100/cpu-sa1110.c
- *
- *  Copyright (C) 2001 Russell King
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * Note: there are two erratas that apply to the SA1110 here:
- *  7 - SDRAM auto-power-up failure (rev A0)
- * 13 - Corruption of internal register reads/writes following
- *      SDRAM reads (rev A0, B0, B1)
- *
- * We ignore rev. A0 and B0 devices; I don't think they're worth supporting.
- *
- * The SDRAM type can be passed on the command line as cpu_sa1110.sdram=type
- */
-#include <linux/cpufreq.h>
-#include <linux/delay.h>
-#include <linux/init.h>
-#include <linux/io.h>
-#include <linux/kernel.h>
-#include <linux/moduleparam.h>
-#include <linux/types.h>
-
-#include <asm/cputype.h>
-#include <asm/mach-types.h>
-
-#include <mach/hardware.h>
-
-#include "generic.h"
-
-#undef DEBUG
-
-struct sdram_params {
-       const char name[20];
-       u_char  rows;           /* bits                          */
-       u_char  cas_latency;    /* cycles                        */
-       u_char  tck;            /* clock cycle time (ns)         */
-       u_char  trcd;           /* activate to r/w (ns)          */
-       u_char  trp;            /* precharge to activate (ns)    */
-       u_char  twr;            /* write recovery time (ns)      */
-       u_short refresh;        /* refresh time for array (us)   */
-};
-
-struct sdram_info {
-       u_int   mdcnfg;
-       u_int   mdrefr;
-       u_int   mdcas[3];
-};
-
-static struct sdram_params sdram_tbl[] __initdata = {
-       {       /* Toshiba TC59SM716 CL2 */
-               .name           = "TC59SM716-CL2",
-               .rows           = 12,
-               .tck            = 10,
-               .trcd           = 20,
-               .trp            = 20,
-               .twr            = 10,
-               .refresh        = 64000,
-               .cas_latency    = 2,
-       }, {    /* Toshiba TC59SM716 CL3 */
-               .name           = "TC59SM716-CL3",
-               .rows           = 12,
-               .tck            = 8,
-               .trcd           = 20,
-               .trp            = 20,
-               .twr            = 8,
-               .refresh        = 64000,
-               .cas_latency    = 3,
-       }, {    /* Samsung K4S641632D TC75 */
-               .name           = "K4S641632D",
-               .rows           = 14,
-               .tck            = 9,
-               .trcd           = 27,
-               .trp            = 20,
-               .twr            = 9,
-               .refresh        = 64000,
-               .cas_latency    = 3,
-       }, {    /* Samsung K4S281632B-1H */
-               .name           = "K4S281632B-1H",
-               .rows           = 12,
-               .tck            = 10,
-               .trp            = 20,
-               .twr            = 10,
-               .refresh        = 64000,
-               .cas_latency    = 3,
-       }, {    /* Samsung KM416S4030CT */
-               .name           = "KM416S4030CT",
-               .rows           = 13,
-               .tck            = 8,
-               .trcd           = 24,   /* 3 CLKs */
-               .trp            = 24,   /* 3 CLKs */
-               .twr            = 16,   /* Trdl: 2 CLKs */
-               .refresh        = 64000,
-               .cas_latency    = 3,
-       }, {    /* Winbond W982516AH75L CL3 */
-               .name           = "W982516AH75L",
-               .rows           = 16,
-               .tck            = 8,
-               .trcd           = 20,
-               .trp            = 20,
-               .twr            = 8,
-               .refresh        = 64000,
-               .cas_latency    = 3,
-       }, {    /* Micron MT48LC8M16A2TG-75 */
-               .name           = "MT48LC8M16A2TG-75",
-               .rows           = 12,
-               .tck            = 8,
-               .trcd           = 20,
-               .trp            = 20,
-               .twr            = 8,
-               .refresh        = 64000,
-               .cas_latency    = 3,
-       },
-};
-
-static struct sdram_params sdram_params;
-
-/*
- * Given a period in ns and frequency in khz, calculate the number of
- * cycles of frequency in period.  Note that we round up to the next
- * cycle, even if we are only slightly over.
- */
-static inline u_int ns_to_cycles(u_int ns, u_int khz)
-{
-       return (ns * khz + 999999) / 1000000;
-}
-
-/*
- * Create the MDCAS register bit pattern.
- */
-static inline void set_mdcas(u_int *mdcas, int delayed, u_int rcd)
-{
-       u_int shift;
-
-       rcd = 2 * rcd - 1;
-       shift = delayed + 1 + rcd;
-
-       mdcas[0]  = (1 << rcd) - 1;
-       mdcas[0] |= 0x55555555 << shift;
-       mdcas[1]  = mdcas[2] = 0x55555555 << (shift & 1);
-}
-
-static void
-sdram_calculate_timing(struct sdram_info *sd, u_int cpu_khz,
-                      struct sdram_params *sdram)
-{
-       u_int mem_khz, sd_khz, trp, twr;
-
-       mem_khz = cpu_khz / 2;
-       sd_khz = mem_khz;
-
-       /*
-        * If SDCLK would invalidate the SDRAM timings,
-        * run SDCLK at half speed.
-        *
-        * CPU steppings prior to B2 must either run the memory at
-        * half speed or use delayed read latching (errata 13).
-        */
-       if ((ns_to_cycles(sdram->tck, sd_khz) > 1) ||
-           (CPU_REVISION < CPU_SA1110_B2 && sd_khz < 62000))
-               sd_khz /= 2;
-
-       sd->mdcnfg = MDCNFG & 0x007f007f;
-
-       twr = ns_to_cycles(sdram->twr, mem_khz);
-
-       /* trp should always be >1 */
-       trp = ns_to_cycles(sdram->trp, mem_khz) - 1;
-       if (trp < 1)
-               trp = 1;
-
-       sd->mdcnfg |= trp << 8;
-       sd->mdcnfg |= trp << 24;
-       sd->mdcnfg |= sdram->cas_latency << 12;
-       sd->mdcnfg |= sdram->cas_latency << 28;
-       sd->mdcnfg |= twr << 14;
-       sd->mdcnfg |= twr << 30;
-
-       sd->mdrefr = MDREFR & 0xffbffff0;
-       sd->mdrefr |= 7;
-
-       if (sd_khz != mem_khz)
-               sd->mdrefr |= MDREFR_K1DB2;
-
-       /* initial number of '1's in MDCAS + 1 */
-       set_mdcas(sd->mdcas, sd_khz >= 62000,
-               ns_to_cycles(sdram->trcd, mem_khz));
-
-#ifdef DEBUG
-       printk(KERN_DEBUG "MDCNFG: %08x MDREFR: %08x MDCAS0: %08x MDCAS1: %08x MDCAS2: %08x\n",
-               sd->mdcnfg, sd->mdrefr, sd->mdcas[0], sd->mdcas[1],
-               sd->mdcas[2]);
-#endif
-}
-
-/*
- * Set the SDRAM refresh rate.
- */
-static inline void sdram_set_refresh(u_int dri)
-{
-       MDREFR = (MDREFR & 0xffff000f) | (dri << 4);
-       (void) MDREFR;
-}
-
-/*
- * Update the refresh period.  We do this such that we always refresh
- * the SDRAMs within their permissible period.  The refresh period is
- * always a multiple of the memory clock (fixed at cpu_clock / 2).
- *
- * FIXME: we don't currently take account of burst accesses here,
- * but neither do Intels DM nor Angel.
- */
-static void
-sdram_update_refresh(u_int cpu_khz, struct sdram_params *sdram)
-{
-       u_int ns_row = (sdram->refresh * 1000) >> sdram->rows;
-       u_int dri = ns_to_cycles(ns_row, cpu_khz / 2) / 32;
-
-#ifdef DEBUG
-       mdelay(250);
-       printk(KERN_DEBUG "new dri value = %d\n", dri);
-#endif
-
-       sdram_set_refresh(dri);
-}
-
-/*
- * Ok, set the CPU frequency.
- */
-static int sa1110_target(struct cpufreq_policy *policy,
-                        unsigned int target_freq,
-                        unsigned int relation)
-{
-       struct sdram_params *sdram = &sdram_params;
-       struct cpufreq_freqs freqs;
-       struct sdram_info sd;
-       unsigned long flags;
-       unsigned int ppcr, unused;
-
-       switch (relation) {
-       case CPUFREQ_RELATION_L:
-               ppcr = sa11x0_freq_to_ppcr(target_freq);
-               if (sa11x0_ppcr_to_freq(ppcr) > policy->max)
-                       ppcr--;
-               break;
-       case CPUFREQ_RELATION_H:
-               ppcr = sa11x0_freq_to_ppcr(target_freq);
-               if (ppcr && (sa11x0_ppcr_to_freq(ppcr) > target_freq) &&
-                   (sa11x0_ppcr_to_freq(ppcr-1) >= policy->min))
-                       ppcr--;
-               break;
-       default:
-               return -EINVAL;
-       }
-
-       freqs.old = sa11x0_getspeed(0);
-       freqs.new = sa11x0_ppcr_to_freq(ppcr);
-
-       sdram_calculate_timing(&sd, freqs.new, sdram);
-
-#if 0
-       /*
-        * These values are wrong according to the SA1110 documentation
-        * and errata, but they seem to work.  Need to get a storage
-        * scope on to the SDRAM signals to work out why.
-        */
-       if (policy->max < 147500) {
-               sd.mdrefr |= MDREFR_K1DB2;
-               sd.mdcas[0] = 0xaaaaaa7f;
-       } else {
-               sd.mdrefr &= ~MDREFR_K1DB2;
-               sd.mdcas[0] = 0xaaaaaa9f;
-       }
-       sd.mdcas[1] = 0xaaaaaaaa;
-       sd.mdcas[2] = 0xaaaaaaaa;
-#endif
-
-       cpufreq_notify_transition(policy, &freqs, CPUFREQ_PRECHANGE);
-
-       /*
-        * The clock could be going away for some time.  Set the SDRAMs
-        * to refresh rapidly (every 64 memory clock cycles).  To get
-        * through the whole array, we need to wait 262144 mclk cycles.
-        * We wait 20ms to be safe.
-        */
-       sdram_set_refresh(2);
-       if (!irqs_disabled())
-               msleep(20);
-       else
-               mdelay(20);
-
-       /*
-        * Reprogram the DRAM timings with interrupts disabled, and
-        * ensure that we are doing this within a complete cache line.
-        * This means that we won't access SDRAM for the duration of
-        * the programming.
-        */
-       local_irq_save(flags);
-       asm("mcr p15, 0, %0, c7, c10, 4" : : "r" (0));
-       udelay(10);
-       __asm__ __volatile__("\n\
-               b       2f                                      \n\
-               .align  5                                       \n\
-1:             str     %3, [%1, #0]            @ MDCNFG        \n\
-               str     %4, [%1, #28]           @ MDREFR        \n\
-               str     %5, [%1, #4]            @ MDCAS0        \n\
-               str     %6, [%1, #8]            @ MDCAS1        \n\
-               str     %7, [%1, #12]           @ MDCAS2        \n\
-               str     %8, [%2, #0]            @ PPCR          \n\
-               ldr     %0, [%1, #0]                            \n\
-               b       3f                                      \n\
-2:             b       1b                                      \n\
-3:             nop                                             \n\
-               nop"
-               : "=&r" (unused)
-               : "r" (&MDCNFG), "r" (&PPCR), "0" (sd.mdcnfg),
-                 "r" (sd.mdrefr), "r" (sd.mdcas[0]),
-                 "r" (sd.mdcas[1]), "r" (sd.mdcas[2]), "r" (ppcr));
-       local_irq_restore(flags);
-
-       /*
-        * Now, return the SDRAM refresh back to normal.
-        */
-       sdram_update_refresh(freqs.new, sdram);
-
-       cpufreq_notify_transition(policy, &freqs, CPUFREQ_POSTCHANGE);
-
-       return 0;
-}
-
-static int __init sa1110_cpu_init(struct cpufreq_policy *policy)
-{
-       if (policy->cpu != 0)
-               return -EINVAL;
-       policy->cur = policy->min = policy->max = sa11x0_getspeed(0);
-       policy->cpuinfo.min_freq = 59000;
-       policy->cpuinfo.max_freq = 287000;
-       policy->cpuinfo.transition_latency = CPUFREQ_ETERNAL;
-       return 0;
-}
-
-/* sa1110_driver needs __refdata because it must remain after init registers
- * it with cpufreq_register_driver() */
-static struct cpufreq_driver sa1110_driver __refdata = {
-       .flags          = CPUFREQ_STICKY,
-       .verify         = sa11x0_verify_speed,
-       .target         = sa1110_target,
-       .get            = sa11x0_getspeed,
-       .init           = sa1110_cpu_init,
-       .name           = "sa1110",
-};
-
-static struct sdram_params *sa1110_find_sdram(const char *name)
-{
-       struct sdram_params *sdram;
-
-       for (sdram = sdram_tbl; sdram < sdram_tbl + ARRAY_SIZE(sdram_tbl);
-            sdram++)
-               if (strcmp(name, sdram->name) == 0)
-                       return sdram;
-
-       return NULL;
-}
-
-static char sdram_name[16];
-
-static int __init sa1110_clk_init(void)
-{
-       struct sdram_params *sdram;
-       const char *name = sdram_name;
-
-       if (!cpu_is_sa1110())
-               return -ENODEV;
-
-       if (!name[0]) {
-               if (machine_is_assabet())
-                       name = "TC59SM716-CL3";
-               if (machine_is_pt_system3())
-                       name = "K4S641632D";
-               if (machine_is_h3100())
-                       name = "KM416S4030CT";
-               if (machine_is_jornada720())
-                       name = "K4S281632B-1H";
-               if (machine_is_nanoengine())
-                       name = "MT48LC8M16A2TG-75";
-       }
-
-       sdram = sa1110_find_sdram(name);
-       if (sdram) {
-               printk(KERN_DEBUG "SDRAM: tck: %d trcd: %d trp: %d"
-                       " twr: %d refresh: %d cas_latency: %d\n",
-                       sdram->tck, sdram->trcd, sdram->trp,
-                       sdram->twr, sdram->refresh, sdram->cas_latency);
-
-               memcpy(&sdram_params, sdram, sizeof(sdram_params));
-
-               return cpufreq_register_driver(&sa1110_driver);
-       }
-
-       return 0;
-}
-
-module_param_string(sdram, sdram_name, sizeof(sdram_name), 0);
-arch_initcall(sa1110_clk_init);
diff --git a/arch/arm/mach-sa1100/include/mach/generic.h b/arch/arm/mach-sa1100/include/mach/generic.h
new file mode 100644 (file)
index 0000000..665542e
--- /dev/null
@@ -0,0 +1 @@
+#include "../../generic.h"
index 97f208daf8aef68a57085aeda7a9c23fa247de1b..09da6a3f0e8f7e5164ddcdab56a1bd31273cf867 100644 (file)
@@ -127,6 +127,12 @@ config ARM_S5PV210_CPUFREQ
 
          If in doubt, say N.
 
+config ARM_SA1100_CPUFREQ
+       bool
+
+config ARM_SA1110_CPUFREQ
+       bool
+
 config ARM_SPEAR_CPUFREQ
        bool "SPEAr CPUFreq support"
        depends on PLAT_SPEAR
index 8d5801645f9d4b860a7f46c15a058797a92b6176..8b21016ac1577fdf405a461a095b2c59d9dd4dbf 100644 (file)
@@ -66,6 +66,8 @@ obj-$(CONFIG_PXA3xx)                  += pxa3xx-cpufreq.o
 obj-$(CONFIG_ARM_S3C2416_CPUFREQ)      += s3c2416-cpufreq.o
 obj-$(CONFIG_ARM_S3C64XX_CPUFREQ)      += s3c64xx-cpufreq.o
 obj-$(CONFIG_ARM_S5PV210_CPUFREQ)      += s5pv210-cpufreq.o
+obj-$(CONFIG_ARM_SA1100_CPUFREQ)       += sa1100-cpufreq.o
+obj-$(CONFIG_ARM_SA1110_CPUFREQ)       += sa1110-cpufreq.o
 obj-$(CONFIG_ARM_SPEAR_CPUFREQ)                += spear-cpufreq.o
 obj-$(CONFIG_ARCH_TEGRA)               += tegra-cpufreq.o
 
diff --git a/drivers/cpufreq/sa1100-cpufreq.c b/drivers/cpufreq/sa1100-cpufreq.c
new file mode 100644 (file)
index 0000000..cff18e8
--- /dev/null
@@ -0,0 +1,247 @@
+/*
+ * cpu-sa1100.c: clock scaling for the SA1100
+ *
+ * Copyright (C) 2000 2001, The Delft University of Technology
+ *
+ * Authors:
+ * - Johan Pouwelse (J.A.Pouwelse@its.tudelft.nl): initial version
+ * - Erik Mouw (J.A.K.Mouw@its.tudelft.nl):
+ *   - major rewrite for linux-2.3.99
+ *   - rewritten for the more generic power management scheme in
+ *     linux-2.4.5-rmk1
+ *
+ * This software has been developed while working on the LART
+ * computing board (http://www.lartmaker.nl/), which is
+ * sponsored by the Mobile Multi-media Communications
+ * (http://www.mobimedia.org/) and Ubiquitous Communications
+ * (http://www.ubicom.tudelft.nl/) projects.
+ *
+ * The authors can be reached at:
+ *
+ *  Erik Mouw
+ *  Information and Communication Theory Group
+ *  Faculty of Information Technology and Systems
+ *  Delft University of Technology
+ *  P.O. Box 5031
+ *  2600 GA Delft
+ *  The Netherlands
+ *
+ *
+ * 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. This will give you a somewhat longer running time.
+ *
+ * The SA-1100 has a single register to change the core clock speed:
+ *
+ *   PPCR      0x90020014    PLL config
+ *
+ * However, the DRAM timings are closely related to the core clock
+ * speed, so we need to change these, too. The used registers are:
+ *
+ *   MDCNFG    0xA0000000    DRAM config
+ *   MDCAS0    0xA0000004    Access waveform
+ *   MDCAS1    0xA0000008    Access waveform
+ *   MDCAS2    0xA000000C    Access waveform
+ *
+ * Care must be taken to change the DRAM parameters the correct way,
+ * because otherwise the DRAM becomes unusable and the kernel will
+ * crash.
+ *
+ * The simple solution to avoid a kernel crash is to put the actual
+ * clock change in ROM and jump to that code from the kernel. The main
+ * disadvantage is that the ROM has to be modified, which is not
+ * possible on all SA-1100 platforms. Another disadvantage is that
+ * jumping to ROM makes clock switching unnecessary complicated.
+ *
+ * The idea behind this driver is that the memory configuration can be
+ * changed while running from DRAM (even with interrupts turned on!)
+ * as long as all re-configuration steps yield a valid DRAM
+ * configuration. The advantages are clear: it will run on all SA-1100
+ * platforms, and the code is very simple.
+ *
+ * If you really want to understand what is going on in
+ * sa1100_update_dram_timings(), you'll have to read sections 8.2,
+ * 9.5.7.3, and 10.2 from the "Intel StrongARM SA-1100 Microprocessor
+ * Developers Manual" (available for free from Intel).
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/types.h>
+#include <linux/init.h>
+#include <linux/cpufreq.h>
+#include <linux/io.h>
+
+#include <asm/cputype.h>
+
+#include <mach/generic.h>
+#include <mach/hardware.h>
+
+struct sa1100_dram_regs {
+       int speed;
+       u32 mdcnfg;
+       u32 mdcas0;
+       u32 mdcas1;
+       u32 mdcas2;
+};
+
+
+static struct cpufreq_driver sa1100_driver;
+
+static struct sa1100_dram_regs sa1100_dram_settings[] = {
+       /*speed,     mdcnfg,     mdcas0,     mdcas1,     mdcas2,   clock freq */
+       { 59000, 0x00dc88a3, 0xcccccccf, 0xfffffffc, 0xffffffff},/*  59.0 MHz */
+       { 73700, 0x011490a3, 0xcccccccf, 0xfffffffc, 0xffffffff},/*  73.7 MHz */
+       { 88500, 0x014e90a3, 0xcccccccf, 0xfffffffc, 0xffffffff},/*  88.5 MHz */
+       {103200, 0x01889923, 0xcccccccf, 0xfffffffc, 0xffffffff},/* 103.2 MHz */
+       {118000, 0x01c29923, 0x9999998f, 0xfffffff9, 0xffffffff},/* 118.0 MHz */
+       {132700, 0x01fb2123, 0x9999998f, 0xfffffff9, 0xffffffff},/* 132.7 MHz */
+       {147500, 0x02352123, 0x3333330f, 0xfffffff3, 0xffffffff},/* 147.5 MHz */
+       {162200, 0x026b29a3, 0x38e38e1f, 0xfff8e38e, 0xffffffff},/* 162.2 MHz */
+       {176900, 0x02a329a3, 0x71c71c1f, 0xfff1c71c, 0xffffffff},/* 176.9 MHz */
+       {191700, 0x02dd31a3, 0xe38e383f, 0xffe38e38, 0xffffffff},/* 191.7 MHz */
+       {206400, 0x03153223, 0xc71c703f, 0xffc71c71, 0xffffffff},/* 206.4 MHz */
+       {221200, 0x034fba23, 0xc71c703f, 0xffc71c71, 0xffffffff},/* 221.2 MHz */
+       {235900, 0x03853a23, 0xe1e1e07f, 0xe1e1e1e1, 0xffffffe1},/* 235.9 MHz */
+       {250700, 0x03bf3aa3, 0xc3c3c07f, 0xc3c3c3c3, 0xffffffc3},/* 250.7 MHz */
+       {265400, 0x03f7c2a3, 0xc3c3c07f, 0xc3c3c3c3, 0xffffffc3},/* 265.4 MHz */
+       {280200, 0x0431c2a3, 0x878780ff, 0x87878787, 0xffffff87},/* 280.2 MHz */
+       { 0, 0, 0, 0, 0 } /* last entry */
+};
+
+static void sa1100_update_dram_timings(int current_speed, int new_speed)
+{
+       struct sa1100_dram_regs *settings = sa1100_dram_settings;
+
+       /* find speed */
+       while (settings->speed != 0) {
+               if (new_speed == settings->speed)
+                       break;
+
+               settings++;
+       }
+
+       if (settings->speed == 0) {
+               panic("%s: couldn't find dram setting for speed %d\n",
+                     __func__, new_speed);
+       }
+
+       /* No risk, no fun: run with interrupts on! */
+       if (new_speed > current_speed) {
+               /* We're going FASTER, so first relax the memory
+                * timings before changing the core frequency
+                */
+
+               /* Half the memory access clock */
+               MDCNFG |= MDCNFG_CDB2;
+
+               /* The order of these statements IS important, keep 8
+                * pulses!!
+                */
+               MDCAS2 = settings->mdcas2;
+               MDCAS1 = settings->mdcas1;
+               MDCAS0 = settings->mdcas0;
+               MDCNFG = settings->mdcnfg;
+       } else {
+               /* We're going SLOWER: first decrease the core
+                * frequency and then tighten the memory settings.
+                */
+
+               /* Half the memory access clock */
+               MDCNFG |= MDCNFG_CDB2;
+
+               /* The order of these statements IS important, keep 8
+                * pulses!!
+                */
+               MDCAS0 = settings->mdcas0;
+               MDCAS1 = settings->mdcas1;
+               MDCAS2 = settings->mdcas2;
+               MDCNFG = settings->mdcnfg;
+       }
+}
+
+static int sa1100_target(struct cpufreq_policy *policy,
+                        unsigned int target_freq,
+                        unsigned int relation)
+{
+       unsigned int cur = sa11x0_getspeed(0);
+       unsigned int new_ppcr;
+       struct cpufreq_freqs freqs;
+
+       new_ppcr = sa11x0_freq_to_ppcr(target_freq);
+       switch (relation) {
+       case CPUFREQ_RELATION_L:
+               if (sa11x0_ppcr_to_freq(new_ppcr) > policy->max)
+                       new_ppcr--;
+               break;
+       case CPUFREQ_RELATION_H:
+               if ((sa11x0_ppcr_to_freq(new_ppcr) > target_freq) &&
+                   (sa11x0_ppcr_to_freq(new_ppcr - 1) >= policy->min))
+                       new_ppcr--;
+               break;
+       }
+
+       freqs.old = cur;
+       freqs.new = sa11x0_ppcr_to_freq(new_ppcr);
+
+       cpufreq_notify_transition(policy, &freqs, CPUFREQ_PRECHANGE);
+
+       if (freqs.new > cur)
+               sa1100_update_dram_timings(cur, freqs.new);
+
+       PPCR = new_ppcr;
+
+       if (freqs.new < cur)
+               sa1100_update_dram_timings(cur, freqs.new);
+
+       cpufreq_notify_transition(policy, &freqs, CPUFREQ_POSTCHANGE);
+
+       return 0;
+}
+
+static int __init sa1100_cpu_init(struct cpufreq_policy *policy)
+{
+       if (policy->cpu != 0)
+               return -EINVAL;
+       policy->cur = policy->min = policy->max = sa11x0_getspeed(0);
+       policy->cpuinfo.min_freq = 59000;
+       policy->cpuinfo.max_freq = 287000;
+       policy->cpuinfo.transition_latency = CPUFREQ_ETERNAL;
+       return 0;
+}
+
+static struct cpufreq_driver sa1100_driver __refdata = {
+       .flags          = CPUFREQ_STICKY,
+       .verify         = sa11x0_verify_speed,
+       .target         = sa1100_target,
+       .get            = sa11x0_getspeed,
+       .init           = sa1100_cpu_init,
+       .name           = "sa1100",
+};
+
+static int __init sa1100_dram_init(void)
+{
+       if (cpu_is_sa1100())
+               return cpufreq_register_driver(&sa1100_driver);
+       else
+               return -ENODEV;
+}
+
+arch_initcall(sa1100_dram_init);
diff --git a/drivers/cpufreq/sa1110-cpufreq.c b/drivers/cpufreq/sa1110-cpufreq.c
new file mode 100644 (file)
index 0000000..39c90b6
--- /dev/null
@@ -0,0 +1,406 @@
+/*
+ *  linux/arch/arm/mach-sa1100/cpu-sa1110.c
+ *
+ *  Copyright (C) 2001 Russell King
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Note: there are two erratas that apply to the SA1110 here:
+ *  7 - SDRAM auto-power-up failure (rev A0)
+ * 13 - Corruption of internal register reads/writes following
+ *      SDRAM reads (rev A0, B0, B1)
+ *
+ * We ignore rev. A0 and B0 devices; I don't think they're worth supporting.
+ *
+ * The SDRAM type can be passed on the command line as cpu_sa1110.sdram=type
+ */
+#include <linux/cpufreq.h>
+#include <linux/delay.h>
+#include <linux/init.h>
+#include <linux/io.h>
+#include <linux/kernel.h>
+#include <linux/moduleparam.h>
+#include <linux/types.h>
+
+#include <asm/cputype.h>
+#include <asm/mach-types.h>
+
+#include <mach/generic.h>
+#include <mach/hardware.h>
+
+#undef DEBUG
+
+struct sdram_params {
+       const char name[20];
+       u_char  rows;           /* bits                          */
+       u_char  cas_latency;    /* cycles                        */
+       u_char  tck;            /* clock cycle time (ns)         */
+       u_char  trcd;           /* activate to r/w (ns)          */
+       u_char  trp;            /* precharge to activate (ns)    */
+       u_char  twr;            /* write recovery time (ns)      */
+       u_short refresh;        /* refresh time for array (us)   */
+};
+
+struct sdram_info {
+       u_int   mdcnfg;
+       u_int   mdrefr;
+       u_int   mdcas[3];
+};
+
+static struct sdram_params sdram_tbl[] __initdata = {
+       {       /* Toshiba TC59SM716 CL2 */
+               .name           = "TC59SM716-CL2",
+               .rows           = 12,
+               .tck            = 10,
+               .trcd           = 20,
+               .trp            = 20,
+               .twr            = 10,
+               .refresh        = 64000,
+               .cas_latency    = 2,
+       }, {    /* Toshiba TC59SM716 CL3 */
+               .name           = "TC59SM716-CL3",
+               .rows           = 12,
+               .tck            = 8,
+               .trcd           = 20,
+               .trp            = 20,
+               .twr            = 8,
+               .refresh        = 64000,
+               .cas_latency    = 3,
+       }, {    /* Samsung K4S641632D TC75 */
+               .name           = "K4S641632D",
+               .rows           = 14,
+               .tck            = 9,
+               .trcd           = 27,
+               .trp            = 20,
+               .twr            = 9,
+               .refresh        = 64000,
+               .cas_latency    = 3,
+       }, {    /* Samsung K4S281632B-1H */
+               .name           = "K4S281632B-1H",
+               .rows           = 12,
+               .tck            = 10,
+               .trp            = 20,
+               .twr            = 10,
+               .refresh        = 64000,
+               .cas_latency    = 3,
+       }, {    /* Samsung KM416S4030CT */
+               .name           = "KM416S4030CT",
+               .rows           = 13,
+               .tck            = 8,
+               .trcd           = 24,   /* 3 CLKs */
+               .trp            = 24,   /* 3 CLKs */
+               .twr            = 16,   /* Trdl: 2 CLKs */
+               .refresh        = 64000,
+               .cas_latency    = 3,
+       }, {    /* Winbond W982516AH75L CL3 */
+               .name           = "W982516AH75L",
+               .rows           = 16,
+               .tck            = 8,
+               .trcd           = 20,
+               .trp            = 20,
+               .twr            = 8,
+               .refresh        = 64000,
+               .cas_latency    = 3,
+       }, {    /* Micron MT48LC8M16A2TG-75 */
+               .name           = "MT48LC8M16A2TG-75",
+               .rows           = 12,
+               .tck            = 8,
+               .trcd           = 20,
+               .trp            = 20,
+               .twr            = 8,
+               .refresh        = 64000,
+               .cas_latency    = 3,
+       },
+};
+
+static struct sdram_params sdram_params;
+
+/*
+ * Given a period in ns and frequency in khz, calculate the number of
+ * cycles of frequency in period.  Note that we round up to the next
+ * cycle, even if we are only slightly over.
+ */
+static inline u_int ns_to_cycles(u_int ns, u_int khz)
+{
+       return (ns * khz + 999999) / 1000000;
+}
+
+/*
+ * Create the MDCAS register bit pattern.
+ */
+static inline void set_mdcas(u_int *mdcas, int delayed, u_int rcd)
+{
+       u_int shift;
+
+       rcd = 2 * rcd - 1;
+       shift = delayed + 1 + rcd;
+
+       mdcas[0]  = (1 << rcd) - 1;
+       mdcas[0] |= 0x55555555 << shift;
+       mdcas[1]  = mdcas[2] = 0x55555555 << (shift & 1);
+}
+
+static void
+sdram_calculate_timing(struct sdram_info *sd, u_int cpu_khz,
+                      struct sdram_params *sdram)
+{
+       u_int mem_khz, sd_khz, trp, twr;
+
+       mem_khz = cpu_khz / 2;
+       sd_khz = mem_khz;
+
+       /*
+        * If SDCLK would invalidate the SDRAM timings,
+        * run SDCLK at half speed.
+        *
+        * CPU steppings prior to B2 must either run the memory at
+        * half speed or use delayed read latching (errata 13).
+        */
+       if ((ns_to_cycles(sdram->tck, sd_khz) > 1) ||
+           (CPU_REVISION < CPU_SA1110_B2 && sd_khz < 62000))
+               sd_khz /= 2;
+
+       sd->mdcnfg = MDCNFG & 0x007f007f;
+
+       twr = ns_to_cycles(sdram->twr, mem_khz);
+
+       /* trp should always be >1 */
+       trp = ns_to_cycles(sdram->trp, mem_khz) - 1;
+       if (trp < 1)
+               trp = 1;
+
+       sd->mdcnfg |= trp << 8;
+       sd->mdcnfg |= trp << 24;
+       sd->mdcnfg |= sdram->cas_latency << 12;
+       sd->mdcnfg |= sdram->cas_latency << 28;
+       sd->mdcnfg |= twr << 14;
+       sd->mdcnfg |= twr << 30;
+
+       sd->mdrefr = MDREFR & 0xffbffff0;
+       sd->mdrefr |= 7;
+
+       if (sd_khz != mem_khz)
+               sd->mdrefr |= MDREFR_K1DB2;
+
+       /* initial number of '1's in MDCAS + 1 */
+       set_mdcas(sd->mdcas, sd_khz >= 62000,
+               ns_to_cycles(sdram->trcd, mem_khz));
+
+#ifdef DEBUG
+       printk(KERN_DEBUG "MDCNFG: %08x MDREFR: %08x MDCAS0: %08x MDCAS1: %08x MDCAS2: %08x\n",
+               sd->mdcnfg, sd->mdrefr, sd->mdcas[0], sd->mdcas[1],
+               sd->mdcas[2]);
+#endif
+}
+
+/*
+ * Set the SDRAM refresh rate.
+ */
+static inline void sdram_set_refresh(u_int dri)
+{
+       MDREFR = (MDREFR & 0xffff000f) | (dri << 4);
+       (void) MDREFR;
+}
+
+/*
+ * Update the refresh period.  We do this such that we always refresh
+ * the SDRAMs within their permissible period.  The refresh period is
+ * always a multiple of the memory clock (fixed at cpu_clock / 2).
+ *
+ * FIXME: we don't currently take account of burst accesses here,
+ * but neither do Intels DM nor Angel.
+ */
+static void
+sdram_update_refresh(u_int cpu_khz, struct sdram_params *sdram)
+{
+       u_int ns_row = (sdram->refresh * 1000) >> sdram->rows;
+       u_int dri = ns_to_cycles(ns_row, cpu_khz / 2) / 32;
+
+#ifdef DEBUG
+       mdelay(250);
+       printk(KERN_DEBUG "new dri value = %d\n", dri);
+#endif
+
+       sdram_set_refresh(dri);
+}
+
+/*
+ * Ok, set the CPU frequency.
+ */
+static int sa1110_target(struct cpufreq_policy *policy,
+                        unsigned int target_freq,
+                        unsigned int relation)
+{
+       struct sdram_params *sdram = &sdram_params;
+       struct cpufreq_freqs freqs;
+       struct sdram_info sd;
+       unsigned long flags;
+       unsigned int ppcr, unused;
+
+       switch (relation) {
+       case CPUFREQ_RELATION_L:
+               ppcr = sa11x0_freq_to_ppcr(target_freq);
+               if (sa11x0_ppcr_to_freq(ppcr) > policy->max)
+                       ppcr--;
+               break;
+       case CPUFREQ_RELATION_H:
+               ppcr = sa11x0_freq_to_ppcr(target_freq);
+               if (ppcr && (sa11x0_ppcr_to_freq(ppcr) > target_freq) &&
+                   (sa11x0_ppcr_to_freq(ppcr-1) >= policy->min))
+                       ppcr--;
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       freqs.old = sa11x0_getspeed(0);
+       freqs.new = sa11x0_ppcr_to_freq(ppcr);
+
+       sdram_calculate_timing(&sd, freqs.new, sdram);
+
+#if 0
+       /*
+        * These values are wrong according to the SA1110 documentation
+        * and errata, but they seem to work.  Need to get a storage
+        * scope on to the SDRAM signals to work out why.
+        */
+       if (policy->max < 147500) {
+               sd.mdrefr |= MDREFR_K1DB2;
+               sd.mdcas[0] = 0xaaaaaa7f;
+       } else {
+               sd.mdrefr &= ~MDREFR_K1DB2;
+               sd.mdcas[0] = 0xaaaaaa9f;
+       }
+       sd.mdcas[1] = 0xaaaaaaaa;
+       sd.mdcas[2] = 0xaaaaaaaa;
+#endif
+
+       cpufreq_notify_transition(policy, &freqs, CPUFREQ_PRECHANGE);
+
+       /*
+        * The clock could be going away for some time.  Set the SDRAMs
+        * to refresh rapidly (every 64 memory clock cycles).  To get
+        * through the whole array, we need to wait 262144 mclk cycles.
+        * We wait 20ms to be safe.
+        */
+       sdram_set_refresh(2);
+       if (!irqs_disabled())
+               msleep(20);
+       else
+               mdelay(20);
+
+       /*
+        * Reprogram the DRAM timings with interrupts disabled, and
+        * ensure that we are doing this within a complete cache line.
+        * This means that we won't access SDRAM for the duration of
+        * the programming.
+        */
+       local_irq_save(flags);
+       asm("mcr p15, 0, %0, c7, c10, 4" : : "r" (0));
+       udelay(10);
+       __asm__ __volatile__("\n\
+               b       2f                                      \n\
+               .align  5                                       \n\
+1:             str     %3, [%1, #0]            @ MDCNFG        \n\
+               str     %4, [%1, #28]           @ MDREFR        \n\
+               str     %5, [%1, #4]            @ MDCAS0        \n\
+               str     %6, [%1, #8]            @ MDCAS1        \n\
+               str     %7, [%1, #12]           @ MDCAS2        \n\
+               str     %8, [%2, #0]            @ PPCR          \n\
+               ldr     %0, [%1, #0]                            \n\
+               b       3f                                      \n\
+2:             b       1b                                      \n\
+3:             nop                                             \n\
+               nop"
+               : "=&r" (unused)
+               : "r" (&MDCNFG), "r" (&PPCR), "0" (sd.mdcnfg),
+                 "r" (sd.mdrefr), "r" (sd.mdcas[0]),
+                 "r" (sd.mdcas[1]), "r" (sd.mdcas[2]), "r" (ppcr));
+       local_irq_restore(flags);
+
+       /*
+        * Now, return the SDRAM refresh back to normal.
+        */
+       sdram_update_refresh(freqs.new, sdram);
+
+       cpufreq_notify_transition(policy, &freqs, CPUFREQ_POSTCHANGE);
+
+       return 0;
+}
+
+static int __init sa1110_cpu_init(struct cpufreq_policy *policy)
+{
+       if (policy->cpu != 0)
+               return -EINVAL;
+       policy->cur = policy->min = policy->max = sa11x0_getspeed(0);
+       policy->cpuinfo.min_freq = 59000;
+       policy->cpuinfo.max_freq = 287000;
+       policy->cpuinfo.transition_latency = CPUFREQ_ETERNAL;
+       return 0;
+}
+
+/* sa1110_driver needs __refdata because it must remain after init registers
+ * it with cpufreq_register_driver() */
+static struct cpufreq_driver sa1110_driver __refdata = {
+       .flags          = CPUFREQ_STICKY,
+       .verify         = sa11x0_verify_speed,
+       .target         = sa1110_target,
+       .get            = sa11x0_getspeed,
+       .init           = sa1110_cpu_init,
+       .name           = "sa1110",
+};
+
+static struct sdram_params *sa1110_find_sdram(const char *name)
+{
+       struct sdram_params *sdram;
+
+       for (sdram = sdram_tbl; sdram < sdram_tbl + ARRAY_SIZE(sdram_tbl);
+            sdram++)
+               if (strcmp(name, sdram->name) == 0)
+                       return sdram;
+
+       return NULL;
+}
+
+static char sdram_name[16];
+
+static int __init sa1110_clk_init(void)
+{
+       struct sdram_params *sdram;
+       const char *name = sdram_name;
+
+       if (!cpu_is_sa1110())
+               return -ENODEV;
+
+       if (!name[0]) {
+               if (machine_is_assabet())
+                       name = "TC59SM716-CL3";
+               if (machine_is_pt_system3())
+                       name = "K4S641632D";
+               if (machine_is_h3100())
+                       name = "KM416S4030CT";
+               if (machine_is_jornada720())
+                       name = "K4S281632B-1H";
+               if (machine_is_nanoengine())
+                       name = "MT48LC8M16A2TG-75";
+       }
+
+       sdram = sa1110_find_sdram(name);
+       if (sdram) {
+               printk(KERN_DEBUG "SDRAM: tck: %d trcd: %d trp: %d"
+                       " twr: %d refresh: %d cas_latency: %d\n",
+                       sdram->tck, sdram->trcd, sdram->trp,
+                       sdram->twr, sdram->refresh, sdram->cas_latency);
+
+               memcpy(&sdram_params, sdram, sizeof(sdram_params));
+
+               return cpufreq_register_driver(&sa1110_driver);
+       }
+
+       return 0;
+}
+
+module_param_string(sdram, sdram_name, sizeof(sdram_name), 0);
+arch_initcall(sa1110_clk_init);