^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1) // SPDX-License-Identifier: GPL-2.0-or-later
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 2) /*
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 3) * cpu-sa1100.c: clock scaling for the SA1100
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 4) *
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 5) * Copyright (C) 2000 2001, The Delft University of Technology
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 6) *
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 7) * Authors:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 8) * - Johan Pouwelse (J.A.Pouwelse@its.tudelft.nl): initial version
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 9) * - Erik Mouw (J.A.K.Mouw@its.tudelft.nl):
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 10) * - major rewrite for linux-2.3.99
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 11) * - rewritten for the more generic power management scheme in
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 12) * linux-2.4.5-rmk1
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 13) *
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 14) * This software has been developed while working on the LART
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 15) * computing board (http://www.lartmaker.nl/), which is
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 16) * sponsored by the Mobile Multi-media Communications
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 17) * (http://www.mobimedia.org/) and Ubiquitous Communications
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 18) * (http://www.ubicom.tudelft.nl/) projects.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 19) *
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 20) * The authors can be reached at:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 21) *
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 22) * Erik Mouw
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 23) * Information and Communication Theory Group
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 24) * Faculty of Information Technology and Systems
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 25) * Delft University of Technology
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 26) * P.O. Box 5031
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 27) * 2600 GA Delft
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 28) * The Netherlands
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 29) *
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 30) * Theory of operations
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 31) * ====================
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 32) *
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 33) * Clock scaling can be used to lower the power consumption of the CPU
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 34) * core. This will give you a somewhat longer running time.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 35) *
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 36) * The SA-1100 has a single register to change the core clock speed:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 37) *
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 38) * PPCR 0x90020014 PLL config
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 39) *
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 40) * However, the DRAM timings are closely related to the core clock
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 41) * speed, so we need to change these, too. The used registers are:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 42) *
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 43) * MDCNFG 0xA0000000 DRAM config
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 44) * MDCAS0 0xA0000004 Access waveform
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 45) * MDCAS1 0xA0000008 Access waveform
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 46) * MDCAS2 0xA000000C Access waveform
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 47) *
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 48) * Care must be taken to change the DRAM parameters the correct way,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 49) * because otherwise the DRAM becomes unusable and the kernel will
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 50) * crash.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 51) *
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 52) * The simple solution to avoid a kernel crash is to put the actual
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 53) * clock change in ROM and jump to that code from the kernel. The main
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 54) * disadvantage is that the ROM has to be modified, which is not
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 55) * possible on all SA-1100 platforms. Another disadvantage is that
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 56) * jumping to ROM makes clock switching unnecessary complicated.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 57) *
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 58) * The idea behind this driver is that the memory configuration can be
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 59) * changed while running from DRAM (even with interrupts turned on!)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 60) * as long as all re-configuration steps yield a valid DRAM
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 61) * configuration. The advantages are clear: it will run on all SA-1100
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 62) * platforms, and the code is very simple.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 63) *
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 64) * If you really want to understand what is going on in
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 65) * sa1100_update_dram_timings(), you'll have to read sections 8.2,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 66) * 9.5.7.3, and 10.2 from the "Intel StrongARM SA-1100 Microprocessor
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 67) * Developers Manual" (available for free from Intel).
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 68) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 69)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 70) #include <linux/kernel.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 71) #include <linux/types.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 72) #include <linux/init.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 73) #include <linux/cpufreq.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 74) #include <linux/io.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 75)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 76) #include <asm/cputype.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 77)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 78) #include <mach/generic.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 79) #include <mach/hardware.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 80)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 81) struct sa1100_dram_regs {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 82) int speed;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 83) u32 mdcnfg;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 84) u32 mdcas0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 85) u32 mdcas1;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 86) u32 mdcas2;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 87) };
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 88)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 89)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 90) static struct cpufreq_driver sa1100_driver;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 91)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 92) static struct sa1100_dram_regs sa1100_dram_settings[] = {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 93) /*speed, mdcnfg, mdcas0, mdcas1, mdcas2, clock freq */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 94) { 59000, 0x00dc88a3, 0xcccccccf, 0xfffffffc, 0xffffffff},/* 59.0 MHz */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 95) { 73700, 0x011490a3, 0xcccccccf, 0xfffffffc, 0xffffffff},/* 73.7 MHz */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 96) { 88500, 0x014e90a3, 0xcccccccf, 0xfffffffc, 0xffffffff},/* 88.5 MHz */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 97) {103200, 0x01889923, 0xcccccccf, 0xfffffffc, 0xffffffff},/* 103.2 MHz */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 98) {118000, 0x01c29923, 0x9999998f, 0xfffffff9, 0xffffffff},/* 118.0 MHz */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 99) {132700, 0x01fb2123, 0x9999998f, 0xfffffff9, 0xffffffff},/* 132.7 MHz */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 100) {147500, 0x02352123, 0x3333330f, 0xfffffff3, 0xffffffff},/* 147.5 MHz */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 101) {162200, 0x026b29a3, 0x38e38e1f, 0xfff8e38e, 0xffffffff},/* 162.2 MHz */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 102) {176900, 0x02a329a3, 0x71c71c1f, 0xfff1c71c, 0xffffffff},/* 176.9 MHz */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 103) {191700, 0x02dd31a3, 0xe38e383f, 0xffe38e38, 0xffffffff},/* 191.7 MHz */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 104) {206400, 0x03153223, 0xc71c703f, 0xffc71c71, 0xffffffff},/* 206.4 MHz */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 105) {221200, 0x034fba23, 0xc71c703f, 0xffc71c71, 0xffffffff},/* 221.2 MHz */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 106) {235900, 0x03853a23, 0xe1e1e07f, 0xe1e1e1e1, 0xffffffe1},/* 235.9 MHz */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 107) {250700, 0x03bf3aa3, 0xc3c3c07f, 0xc3c3c3c3, 0xffffffc3},/* 250.7 MHz */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 108) {265400, 0x03f7c2a3, 0xc3c3c07f, 0xc3c3c3c3, 0xffffffc3},/* 265.4 MHz */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 109) {280200, 0x0431c2a3, 0x878780ff, 0x87878787, 0xffffff87},/* 280.2 MHz */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 110) { 0, 0, 0, 0, 0 } /* last entry */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 111) };
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 112)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 113) static void sa1100_update_dram_timings(int current_speed, int new_speed)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 114) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 115) struct sa1100_dram_regs *settings = sa1100_dram_settings;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 116)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 117) /* find speed */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 118) while (settings->speed != 0) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 119) if (new_speed == settings->speed)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 120) break;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 121)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 122) settings++;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 123) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 124)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 125) if (settings->speed == 0) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 126) panic("%s: couldn't find dram setting for speed %d\n",
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 127) __func__, new_speed);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 128) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 129)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 130) /* No risk, no fun: run with interrupts on! */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 131) if (new_speed > current_speed) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 132) /* We're going FASTER, so first relax the memory
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 133) * timings before changing the core frequency
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 134) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 135)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 136) /* Half the memory access clock */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 137) MDCNFG |= MDCNFG_CDB2;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 138)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 139) /* The order of these statements IS important, keep 8
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 140) * pulses!!
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 141) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 142) MDCAS2 = settings->mdcas2;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 143) MDCAS1 = settings->mdcas1;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 144) MDCAS0 = settings->mdcas0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 145) MDCNFG = settings->mdcnfg;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 146) } else {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 147) /* We're going SLOWER: first decrease the core
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 148) * frequency and then tighten the memory settings.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 149) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 150)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 151) /* Half the memory access clock */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 152) MDCNFG |= MDCNFG_CDB2;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 153)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 154) /* The order of these statements IS important, keep 8
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 155) * pulses!!
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 156) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 157) MDCAS0 = settings->mdcas0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 158) MDCAS1 = settings->mdcas1;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 159) MDCAS2 = settings->mdcas2;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 160) MDCNFG = settings->mdcnfg;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 161) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 162) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 163)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 164) static int sa1100_target(struct cpufreq_policy *policy, unsigned int ppcr)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 165) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 166) unsigned int cur = sa11x0_getspeed(0);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 167) unsigned int new_freq;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 168)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 169) new_freq = sa11x0_freq_table[ppcr].frequency;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 170)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 171) if (new_freq > cur)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 172) sa1100_update_dram_timings(cur, new_freq);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 173)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 174) PPCR = ppcr;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 175)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 176) if (new_freq < cur)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 177) sa1100_update_dram_timings(cur, new_freq);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 178)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 179) return 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 180) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 181)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 182) static int __init sa1100_cpu_init(struct cpufreq_policy *policy)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 183) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 184) cpufreq_generic_init(policy, sa11x0_freq_table, 0);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 185) return 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 186) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 187)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 188) static struct cpufreq_driver sa1100_driver __refdata = {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 189) .flags = CPUFREQ_STICKY | CPUFREQ_NEED_INITIAL_FREQ_CHECK |
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 190) CPUFREQ_NO_AUTO_DYNAMIC_SWITCHING,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 191) .verify = cpufreq_generic_frequency_table_verify,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 192) .target_index = sa1100_target,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 193) .get = sa11x0_getspeed,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 194) .init = sa1100_cpu_init,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 195) .name = "sa1100",
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 196) };
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 197)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 198) static int __init sa1100_dram_init(void)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 199) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 200) if (cpu_is_sa1100())
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 201) return cpufreq_register_driver(&sa1100_driver);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 202) else
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 203) return -ENODEV;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 204) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 205)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 206) arch_initcall(sa1100_dram_init);