Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0-or-later
0002 /*
0003  * cpu-sa1100.c: clock scaling for the SA1100
0004  *
0005  * Copyright (C) 2000 2001, The Delft University of Technology
0006  *
0007  * Authors:
0008  * - Johan Pouwelse (J.A.Pouwelse@its.tudelft.nl): initial version
0009  * - Erik Mouw (J.A.K.Mouw@its.tudelft.nl):
0010  *   - major rewrite for linux-2.3.99
0011  *   - rewritten for the more generic power management scheme in
0012  *     linux-2.4.5-rmk1
0013  *
0014  * This software has been developed while working on the LART
0015  * computing board (http://www.lartmaker.nl/), which is
0016  * sponsored by the Mobile Multi-media Communications
0017  * (http://www.mobimedia.org/) and Ubiquitous Communications
0018  * (http://www.ubicom.tudelft.nl/) projects.
0019  *
0020  * The authors can be reached at:
0021  *
0022  *  Erik Mouw
0023  *  Information and Communication Theory Group
0024  *  Faculty of Information Technology and Systems
0025  *  Delft University of Technology
0026  *  P.O. Box 5031
0027  *  2600 GA Delft
0028  *  The Netherlands
0029  *
0030  * Theory of operations
0031  * ====================
0032  *
0033  * Clock scaling can be used to lower the power consumption of the CPU
0034  * core. This will give you a somewhat longer running time.
0035  *
0036  * The SA-1100 has a single register to change the core clock speed:
0037  *
0038  *   PPCR      0x90020014    PLL config
0039  *
0040  * However, the DRAM timings are closely related to the core clock
0041  * speed, so we need to change these, too. The used registers are:
0042  *
0043  *   MDCNFG    0xA0000000    DRAM config
0044  *   MDCAS0    0xA0000004    Access waveform
0045  *   MDCAS1    0xA0000008    Access waveform
0046  *   MDCAS2    0xA000000C    Access waveform
0047  *
0048  * Care must be taken to change the DRAM parameters the correct way,
0049  * because otherwise the DRAM becomes unusable and the kernel will
0050  * crash.
0051  *
0052  * The simple solution to avoid a kernel crash is to put the actual
0053  * clock change in ROM and jump to that code from the kernel. The main
0054  * disadvantage is that the ROM has to be modified, which is not
0055  * possible on all SA-1100 platforms. Another disadvantage is that
0056  * jumping to ROM makes clock switching unnecessary complicated.
0057  *
0058  * The idea behind this driver is that the memory configuration can be
0059  * changed while running from DRAM (even with interrupts turned on!)
0060  * as long as all re-configuration steps yield a valid DRAM
0061  * configuration. The advantages are clear: it will run on all SA-1100
0062  * platforms, and the code is very simple.
0063  *
0064  * If you really want to understand what is going on in
0065  * sa1100_update_dram_timings(), you'll have to read sections 8.2,
0066  * 9.5.7.3, and 10.2 from the "Intel StrongARM SA-1100 Microprocessor
0067  * Developers Manual" (available for free from Intel).
0068  */
0069 
0070 #include <linux/kernel.h>
0071 #include <linux/types.h>
0072 #include <linux/init.h>
0073 #include <linux/cpufreq.h>
0074 #include <linux/io.h>
0075 
0076 #include <asm/cputype.h>
0077 
0078 #include <mach/generic.h>
0079 #include <mach/hardware.h>
0080 
0081 struct sa1100_dram_regs {
0082     int speed;
0083     u32 mdcnfg;
0084     u32 mdcas0;
0085     u32 mdcas1;
0086     u32 mdcas2;
0087 };
0088 
0089 
0090 static struct cpufreq_driver sa1100_driver;
0091 
0092 static struct sa1100_dram_regs sa1100_dram_settings[] = {
0093     /*speed,     mdcnfg,     mdcas0,     mdcas1,     mdcas2,   clock freq */
0094     { 59000, 0x00dc88a3, 0xcccccccf, 0xfffffffc, 0xffffffff},/*  59.0 MHz */
0095     { 73700, 0x011490a3, 0xcccccccf, 0xfffffffc, 0xffffffff},/*  73.7 MHz */
0096     { 88500, 0x014e90a3, 0xcccccccf, 0xfffffffc, 0xffffffff},/*  88.5 MHz */
0097     {103200, 0x01889923, 0xcccccccf, 0xfffffffc, 0xffffffff},/* 103.2 MHz */
0098     {118000, 0x01c29923, 0x9999998f, 0xfffffff9, 0xffffffff},/* 118.0 MHz */
0099     {132700, 0x01fb2123, 0x9999998f, 0xfffffff9, 0xffffffff},/* 132.7 MHz */
0100     {147500, 0x02352123, 0x3333330f, 0xfffffff3, 0xffffffff},/* 147.5 MHz */
0101     {162200, 0x026b29a3, 0x38e38e1f, 0xfff8e38e, 0xffffffff},/* 162.2 MHz */
0102     {176900, 0x02a329a3, 0x71c71c1f, 0xfff1c71c, 0xffffffff},/* 176.9 MHz */
0103     {191700, 0x02dd31a3, 0xe38e383f, 0xffe38e38, 0xffffffff},/* 191.7 MHz */
0104     {206400, 0x03153223, 0xc71c703f, 0xffc71c71, 0xffffffff},/* 206.4 MHz */
0105     {221200, 0x034fba23, 0xc71c703f, 0xffc71c71, 0xffffffff},/* 221.2 MHz */
0106     {235900, 0x03853a23, 0xe1e1e07f, 0xe1e1e1e1, 0xffffffe1},/* 235.9 MHz */
0107     {250700, 0x03bf3aa3, 0xc3c3c07f, 0xc3c3c3c3, 0xffffffc3},/* 250.7 MHz */
0108     {265400, 0x03f7c2a3, 0xc3c3c07f, 0xc3c3c3c3, 0xffffffc3},/* 265.4 MHz */
0109     {280200, 0x0431c2a3, 0x878780ff, 0x87878787, 0xffffff87},/* 280.2 MHz */
0110     { 0, 0, 0, 0, 0 } /* last entry */
0111 };
0112 
0113 static void sa1100_update_dram_timings(int current_speed, int new_speed)
0114 {
0115     struct sa1100_dram_regs *settings = sa1100_dram_settings;
0116 
0117     /* find speed */
0118     while (settings->speed != 0) {
0119         if (new_speed == settings->speed)
0120             break;
0121 
0122         settings++;
0123     }
0124 
0125     if (settings->speed == 0) {
0126         panic("%s: couldn't find dram setting for speed %d\n",
0127               __func__, new_speed);
0128     }
0129 
0130     /* No risk, no fun: run with interrupts on! */
0131     if (new_speed > current_speed) {
0132         /* We're going FASTER, so first relax the memory
0133          * timings before changing the core frequency
0134          */
0135 
0136         /* Half the memory access clock */
0137         MDCNFG |= MDCNFG_CDB2;
0138 
0139         /* The order of these statements IS important, keep 8
0140          * pulses!!
0141          */
0142         MDCAS2 = settings->mdcas2;
0143         MDCAS1 = settings->mdcas1;
0144         MDCAS0 = settings->mdcas0;
0145         MDCNFG = settings->mdcnfg;
0146     } else {
0147         /* We're going SLOWER: first decrease the core
0148          * frequency and then tighten the memory settings.
0149          */
0150 
0151         /* Half the memory access clock */
0152         MDCNFG |= MDCNFG_CDB2;
0153 
0154         /* The order of these statements IS important, keep 8
0155          * pulses!!
0156          */
0157         MDCAS0 = settings->mdcas0;
0158         MDCAS1 = settings->mdcas1;
0159         MDCAS2 = settings->mdcas2;
0160         MDCNFG = settings->mdcnfg;
0161     }
0162 }
0163 
0164 static int sa1100_target(struct cpufreq_policy *policy, unsigned int ppcr)
0165 {
0166     unsigned int cur = sa11x0_getspeed(0);
0167     unsigned int new_freq;
0168 
0169     new_freq = sa11x0_freq_table[ppcr].frequency;
0170 
0171     if (new_freq > cur)
0172         sa1100_update_dram_timings(cur, new_freq);
0173 
0174     PPCR = ppcr;
0175 
0176     if (new_freq < cur)
0177         sa1100_update_dram_timings(cur, new_freq);
0178 
0179     return 0;
0180 }
0181 
0182 static int __init sa1100_cpu_init(struct cpufreq_policy *policy)
0183 {
0184     cpufreq_generic_init(policy, sa11x0_freq_table, 0);
0185     return 0;
0186 }
0187 
0188 static struct cpufreq_driver sa1100_driver __refdata = {
0189     .flags      = CPUFREQ_NEED_INITIAL_FREQ_CHECK |
0190               CPUFREQ_NO_AUTO_DYNAMIC_SWITCHING,
0191     .verify     = cpufreq_generic_frequency_table_verify,
0192     .target_index   = sa1100_target,
0193     .get        = sa11x0_getspeed,
0194     .init       = sa1100_cpu_init,
0195     .name       = "sa1100",
0196 };
0197 
0198 static int __init sa1100_dram_init(void)
0199 {
0200     if (cpu_is_sa1100())
0201         return cpufreq_register_driver(&sa1100_driver);
0202     else
0203         return -ENODEV;
0204 }
0205 
0206 arch_initcall(sa1100_dram_init);