cregit-Linux how code gets into the kernel

Release 4.7 drivers/cpufreq/pxa2xx-cpufreq.c

Directory: drivers/cpufreq
/*
 *  Copyright (C) 2002,2003 Intrinsyc Software
 *
 * 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
 *
 * History:
 *   31-Jul-2002 : Initial version [FB]
 *   29-Jan-2003 : added PXA255 support [FB]
 *   20-Apr-2003 : ported to v2.5 (Dustin McIntire, Sensoria Corp.)
 *
 * Note:
 *   This driver may change the memory bus clock rate, but will not do any
 *   platform specific access timing changes... for example if you have flash
 *   memory connected to CS0, you will need to register a platform specific
 *   notifier which will adjust the memory access strobes to maintain a
 *   minimum strobe width.
 *
 */


#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt

#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/sched.h>
#include <linux/init.h>
#include <linux/cpufreq.h>
#include <linux/err.h>
#include <linux/regulator/consumer.h>
#include <linux/io.h>

#include <mach/pxa2xx-regs.h>
#include <mach/smemc.h>

#ifdef DEBUG

static unsigned int freq_debug;
module_param(freq_debug, uint, 0);
MODULE_PARM_DESC(freq_debug, "Set the debug messages to on=1/off=0");
#else

#define freq_debug  0
#endif


static struct regulator *vcc_core;


static unsigned int pxa27x_maxfreq;
module_param(pxa27x_maxfreq, uint, 0);
MODULE_PARM_DESC(pxa27x_maxfreq, "Set the pxa27x maxfreq in MHz"
		 "(typically 624=>pxa270, 416=>pxa271, 520=>pxa272)");


struct pxa_freqs {
	
unsigned int khz;
	
unsigned int membus;
	
unsigned int cccr;
	
unsigned int div2;
	
unsigned int cclkcfg;
	
int vmin;
	
int vmax;
};

/* Define the refresh period in mSec for the SDRAM and the number of rows */

#define SDRAM_TREF	64	
/* standard 64ms SDRAM */

static unsigned int sdram_rows;


#define CCLKCFG_TURBO		0x1

#define CCLKCFG_FCS		0x2

#define CCLKCFG_HALFTURBO	0x4

#define CCLKCFG_FASTBUS		0x8

#define MDREFR_DB2_MASK		(MDREFR_K2DB2 | MDREFR_K1DB2)

#define MDREFR_DRI_MASK		0xFFF


#define MDCNFG_DRAC2(mdcnfg) (((mdcnfg) >> 21) & 0x3)

#define MDCNFG_DRAC0(mdcnfg) (((mdcnfg) >> 5) & 0x3)

/*
 * PXA255 definitions
 */
/* Use the run mode frequencies for the CPUFREQ_POLICY_PERFORMANCE policy */

#define CCLKCFG			CCLKCFG_TURBO | CCLKCFG_FCS


static const struct pxa_freqs pxa255_run_freqs[] =
{
	/* CPU   MEMBUS  CCCR  DIV2 CCLKCFG                run  turbo PXbus SDRAM */
	{ 99500,  99500, 0x121, 1,  CCLKCFG, -1, -1},	/*  99,   99,   50,   50  */
	{132700, 132700, 0x123, 1,  CCLKCFG, -1, -1},	/* 133,  133,   66,   66  */
	{199100,  99500, 0x141, 0,  CCLKCFG, -1, -1},	/* 199,  199,   99,   99  */
	{265400, 132700, 0x143, 1,  CCLKCFG, -1, -1},	/* 265,  265,  133,   66  */
	{331800, 165900, 0x145, 1,  CCLKCFG, -1, -1},	/* 331,  331,  166,   83  */
	{398100,  99500, 0x161, 0,  CCLKCFG, -1, -1},	/* 398,  398,  196,   99  */
};

/* Use the turbo mode frequencies for the CPUFREQ_POLICY_POWERSAVE policy */

static const struct pxa_freqs pxa255_turbo_freqs[] =
{
	/* CPU   MEMBUS  CCCR  DIV2 CCLKCFG        run  turbo PXbus SDRAM */
	{ 99500, 99500,  0x121, 1,  CCLKCFG, -1, -1},	/*  99,   99,   50,   50  */
	{199100, 99500,  0x221, 0,  CCLKCFG, -1, -1},	/*  99,  199,   50,   99  */
	{298500, 99500,  0x321, 0,  CCLKCFG, -1, -1},	/*  99,  287,   50,   99  */
	{298600, 99500,  0x1c1, 0,  CCLKCFG, -1, -1},	/* 199,  287,   99,   99  */
	{398100, 99500,  0x241, 0,  CCLKCFG, -1, -1},	/* 199,  398,   99,   99  */
};


#define NUM_PXA25x_RUN_FREQS ARRAY_SIZE(pxa255_run_freqs)

#define NUM_PXA25x_TURBO_FREQS ARRAY_SIZE(pxa255_turbo_freqs)

static struct cpufreq_frequency_table
	
pxa255_run_freq_table[NUM_PXA25x_RUN_FREQS+1];
static struct cpufreq_frequency_table
	
pxa255_turbo_freq_table[NUM_PXA25x_TURBO_FREQS+1];


static unsigned int pxa255_turbo_table;
module_param(pxa255_turbo_table, uint, 0);
MODULE_PARM_DESC(pxa255_turbo_table, "Selects the frequency table (0 = run table, !0 = turbo table)");

/*
 * PXA270 definitions
 *
 * For the PXA27x:
 * Control variables are A, L, 2N for CCCR; B, HT, T for CLKCFG.
 *
 * A = 0 => memory controller clock from table 3-7,
 * A = 1 => memory controller clock = system bus clock
 * Run mode frequency   = 13 MHz * L
 * Turbo mode frequency = 13 MHz * L * N
 * System bus frequency = 13 MHz * L / (B + 1)
 *
 * In CCCR:
 * A = 1
 * L = 16         oscillator to run mode ratio
 * 2N = 6         2 * (turbo mode to run mode ratio)
 *
 * In CCLKCFG:
 * B = 1          Fast bus mode
 * HT = 0         Half-Turbo mode
 * T = 1          Turbo mode
 *
 * For now, just support some of the combinations in table 3-7 of
 * PXA27x Processor Family Developer's Manual to simplify frequency
 * change sequences.
 */

#define PXA27x_CCCR(A, L, N2) (A << 25 | N2 << 7 | L)

#define CCLKCFG2(B, HT, T) \
  (CCLKCFG_FCS | \
   ((B)  ? CCLKCFG_FASTBUS : 0) | \
   ((HT) ? CCLKCFG_HALFTURBO : 0) | \
   ((T)  ? CCLKCFG_TURBO : 0))


static struct pxa_freqs pxa27x_freqs[] = {
	{104000, 104000, PXA27x_CCCR(1,	 8, 2), 0, CCLKCFG2(1, 0, 1),  900000, 1705000 },
	{156000, 104000, PXA27x_CCCR(1,	 8, 3), 0, CCLKCFG2(1, 0, 1), 1000000, 1705000 },
	{208000, 208000, PXA27x_CCCR(0, 16, 2), 1, CCLKCFG2(0, 0, 1), 1180000, 1705000 },
	{312000, 208000, PXA27x_CCCR(1, 16, 3), 1, CCLKCFG2(1, 0, 1), 1250000, 1705000 },
	{416000, 208000, PXA27x_CCCR(1, 16, 4), 1, CCLKCFG2(1, 0, 1), 1350000, 1705000 },
	{520000, 208000, PXA27x_CCCR(1, 16, 5), 1, CCLKCFG2(1, 0, 1), 1450000, 1705000 },
	{624000, 208000, PXA27x_CCCR(1, 16, 6), 1, CCLKCFG2(1, 0, 1), 1550000, 1705000 }
};


#define NUM_PXA27x_FREQS ARRAY_SIZE(pxa27x_freqs)
static struct cpufreq_frequency_table
	
pxa27x_freq_table[NUM_PXA27x_FREQS+1];

extern unsigned get_clk_frequency_khz(int info);

#ifdef CONFIG_REGULATOR


static int pxa_cpufreq_change_voltage(const struct pxa_freqs *pxa_freq) { int ret = 0; int vmin, vmax; if (!cpu_is_pxa27x()) return 0; vmin = pxa_freq->vmin; vmax = pxa_freq->vmax; if ((vmin == -1) || (vmax == -1)) return 0; ret = regulator_set_voltage(vcc_core, vmin, vmax); if (ret) pr_err("Failed to set vcc_core in [%dmV..%dmV]\n", vmin, vmax); return ret; }

Contributors

PersonTokensPropCommitsCommitProp
robert jarzmikrobert jarzmik8595.51%125.00%
fabian frederickfabian frederick33.37%250.00%
joe perchesjoe perches11.12%125.00%
Total89100.00%4100.00%


static void __init pxa_cpufreq_init_voltages(void) { vcc_core = regulator_get(NULL, "vcc_core"); if (IS_ERR(vcc_core)) { pr_info("Didn't find vcc_core regulator\n"); vcc_core = NULL; } else { pr_info("Found vcc_core regulator\n"); } }

Contributors

PersonTokensPropCommitsCommitProp
robert jarzmikrobert jarzmik4193.18%133.33%
joe perchesjoe perches24.55%133.33%
hanjun guohanjun guo12.27%133.33%
Total44100.00%3100.00%

#else
static int pxa_cpufreq_change_voltage(const struct pxa_freqs *pxa_freq) { return 0; }

Contributors

PersonTokensPropCommitsCommitProp
robert jarzmikrobert jarzmik1280.00%133.33%
fabian frederickfabian frederick213.33%133.33%
arnd bergmannarnd bergmann16.67%133.33%
Total15100.00%3100.00%


static void __init pxa_cpufreq_init_voltages(void) { }

Contributors

PersonTokensPropCommitsCommitProp
robert jarzmikrobert jarzmik787.50%150.00%
hanjun guohanjun guo112.50%150.00%
Total8100.00%2100.00%

#endif
static void find_freq_tables(struct cpufreq_frequency_table **freq_table, const struct pxa_freqs **pxa_freqs) { if (cpu_is_pxa25x()) { if (!pxa255_turbo_table) { *pxa_freqs = pxa255_run_freqs; *freq_table = pxa255_run_freq_table; } else { *pxa_freqs = pxa255_turbo_freqs; *freq_table = pxa255_turbo_freq_table; } } else if (cpu_is_pxa27x()) { *pxa_freqs = pxa27x_freqs; *freq_table = pxa27x_freq_table; } else { BUG(); } }

Contributors

PersonTokensPropCommitsCommitProp
robert jarzmikrobert jarzmik4961.25%116.67%
russell kingrussell king1822.50%116.67%
arnd bergmannarnd bergmann78.75%116.67%
fabian frederickfabian frederick33.75%233.33%
marc zyngiermarc zyngier33.75%116.67%
Total80100.00%6100.00%


static void pxa27x_guess_max_freq(void) { if (!pxa27x_maxfreq) { pxa27x_maxfreq = 416000; pr_info("PXA CPU 27x max frequency not defined (pxa27x_maxfreq), assuming pxa271 with %dkHz maxfreq\n", pxa27x_maxfreq); } else { pxa27x_maxfreq *= 1000; } }

Contributors

PersonTokensPropCommitsCommitProp
robert jarzmikrobert jarzmik3193.94%150.00%
joe perchesjoe perches26.06%150.00%
Total33100.00%2100.00%


static void init_sdram_rows(void) { uint32_t mdcnfg = __raw_readl(MDCNFG); unsigned int drac2 = 0, drac0 = 0; if (mdcnfg & (MDCNFG_DE2 | MDCNFG_DE3)) drac2 = MDCNFG_DRAC2(mdcnfg); if (mdcnfg & (MDCNFG_DE0 | MDCNFG_DE1)) drac0 = MDCNFG_DRAC0(mdcnfg); sdram_rows = 1 << (11 + max(drac0, drac2)); }

Contributors

PersonTokensPropCommitsCommitProp
philipp zabelphilipp zabel7296.00%150.00%
marek vasutmarek vasut34.00%150.00%
Total75100.00%2100.00%


static u32 mdrefr_dri(unsigned int freq) { u32 interval = freq * SDRAM_TREF / sdram_rows; return (interval - (cpu_is_pxa27x() ? 31 : 0)) / 32; }

Contributors

PersonTokensPropCommitsCommitProp
robert jarzmikrobert jarzmik2468.57%133.33%
marek vasutmarek vasut1028.57%133.33%
philipp zabelphilipp zabel12.86%133.33%
Total35100.00%3100.00%


static unsigned int pxa_cpufreq_get(unsigned int cpu) { return get_clk_frequency_khz(0); }

Contributors

PersonTokensPropCommitsCommitProp
robert jarzmikrobert jarzmik1588.24%150.00%
russell kingrussell king211.76%150.00%
Total17100.00%2100.00%


static int pxa_set_target(struct cpufreq_policy *policy, unsigned int idx) { struct cpufreq_frequency_table *pxa_freqs_table; const struct pxa_freqs *pxa_freq_settings; unsigned long flags; unsigned int new_freq_cpu, new_freq_mem; unsigned int unused, preset_mdrefr, postset_mdrefr, cclkcfg; int ret = 0; /* Get the current policy */ find_freq_tables(&pxa_freqs_table, &pxa_freq_settings); new_freq_cpu = pxa_freq_settings[idx].khz; new_freq_mem = pxa_freq_settings[idx].membus; if (freq_debug) pr_debug("Changing CPU frequency to %d Mhz, (SDRAM %d Mhz)\n", new_freq_cpu / 1000, (pxa_freq_settings[idx].div2) ? (new_freq_mem / 2000) : (new_freq_mem / 1000)); if (vcc_core && new_freq_cpu > policy->cur) { ret = pxa_cpufreq_change_voltage(&pxa_freq_settings[idx]); if (ret) return ret; } /* Calculate the next MDREFR. If we're slowing down the SDRAM clock * we need to preset the smaller DRI before the change. If we're * speeding up we need to set the larger DRI value after the change. */ preset_mdrefr = postset_mdrefr = __raw_readl(MDREFR); if ((preset_mdrefr & MDREFR_DRI_MASK) > mdrefr_dri(new_freq_mem)) { preset_mdrefr = (preset_mdrefr & ~MDREFR_DRI_MASK); preset_mdrefr |= mdrefr_dri(new_freq_mem); } postset_mdrefr = (postset_mdrefr & ~MDREFR_DRI_MASK) | mdrefr_dri(new_freq_mem); /* If we're dividing the memory clock by two for the SDRAM clock, this * must be set prior to the change. Clearing the divide must be done * after the change. */ if (pxa_freq_settings[idx].div2) { preset_mdrefr |= MDREFR_DB2_MASK; postset_mdrefr |= MDREFR_DB2_MASK; } else { postset_mdrefr &= ~MDREFR_DB2_MASK; } local_irq_save(flags); /* Set new the CCCR and prepare CCLKCFG */ writel(pxa_freq_settings[idx].cccr, CCCR); cclkcfg = pxa_freq_settings[idx].cclkcfg; asm volatile(" \n\ ldr r4, [%1] /* load MDREFR */ \n\ b 2f \n\ .align 5 \n\ 1: \n\ str %3, [%1] /* preset the MDREFR */ \n\ mcr p14, 0, %2, c6, c0, 0 /* set CCLKCFG[FCS] */ \n\ str %4, [%1] /* postset the MDREFR */ \n\ \n\ b 3f \n\ 2: b 1b \n\ 3: nop \n\ " : "=&r" (unused) : "r" (MDREFR), "r" (cclkcfg), "r" (preset_mdrefr), "r" (postset_mdrefr) : "r4", "r5"); local_irq_restore(flags); /* * Even if voltage setting fails, we don't report it, as the frequency * change succeeded. The voltage reduction is not a critical failure, * only power savings will suffer from this. * * Note: if the voltage change fails, and a return value is returned, a * bug is triggered (seems a deadlock). Should anybody find out where, * the "return 0" should become a "return ret". */ if (vcc_core && new_freq_cpu < policy->cur) ret = pxa_cpufreq_change_voltage(&pxa_freq_settings[idx]); return 0; }

Contributors

PersonTokensPropCommitsCommitProp
russell kingrussell king15955.59%18.33%
robert jarzmikrobert jarzmik10034.97%325.00%
viresh kumarviresh kumar124.20%216.67%
marek vasutmarek vasut51.75%18.33%
arnd bergmannarnd bergmann51.75%18.33%
fabian frederickfabian frederick31.05%216.67%
joe perchesjoe perches10.35%18.33%
holger schurigholger schurig10.35%18.33%
Total286100.00%12100.00%


static int pxa_cpufreq_init(struct cpufreq_policy *policy) { int i; unsigned int freq; struct cpufreq_frequency_table *pxa255_freq_table; const struct pxa_freqs *pxa255_freqs; /* try to guess pxa27x cpu */ if (cpu_is_pxa27x()) pxa27x_guess_max_freq(); pxa_cpufreq_init_voltages(); init_sdram_rows(); /* set default policy and cpuinfo */ policy->cpuinfo.transition_latency = 1000; /* FIXME: 1 ms, assumed */ /* Generate pxa25x the run cpufreq_frequency_table struct */ for (i = 0; i < NUM_PXA25x_RUN_FREQS; i++) { pxa255_run_freq_table[i].frequency = pxa255_run_freqs[i].khz; pxa255_run_freq_table[i].driver_data = i; } pxa255_run_freq_table[i].frequency = CPUFREQ_TABLE_END; /* Generate pxa25x the turbo cpufreq_frequency_table struct */ for (i = 0; i < NUM_PXA25x_TURBO_FREQS; i++) { pxa255_turbo_freq_table[i].frequency = pxa255_turbo_freqs[i].khz; pxa255_turbo_freq_table[i].driver_data = i; } pxa255_turbo_freq_table[i].frequency = CPUFREQ_TABLE_END; pxa255_turbo_table = !!pxa255_turbo_table; /* Generate the pxa27x cpufreq_frequency_table struct */ for (i = 0; i < NUM_PXA27x_FREQS; i++) { freq = pxa27x_freqs[i].khz; if (freq > pxa27x_maxfreq) break; pxa27x_freq_table[i].frequency = freq; pxa27x_freq_table[i].driver_data = i; } pxa27x_freq_table[i].driver_data = i; pxa27x_freq_table[i].frequency = CPUFREQ_TABLE_END; /* * Set the policy's minimum and maximum frequencies from the tables * just constructed. This sets cpuinfo.mxx_freq, min and max. */ if (cpu_is_pxa25x()) { find_freq_tables(&pxa255_freq_table, &pxa255_freqs); pr_info("using %s frequency table\n", pxa255_turbo_table ? "turbo" : "run"); cpufreq_table_validate_and_show(policy, pxa255_freq_table); } else if (cpu_is_pxa27x()) { cpufreq_table_validate_and_show(policy, pxa27x_freq_table); } pr_info("frequency change support initialized\n"); return 0; }

Contributors

PersonTokensPropCommitsCommitProp
russell kingrussell king12042.55%17.69%
robert jarzmikrobert jarzmik10135.82%215.38%
marc zyngiermarc zyngier3612.77%17.69%
viresh kumarviresh kumar82.84%323.08%
daniel mackdaniel mack82.84%17.69%
philipp zabelphilipp zabel31.06%17.69%
joe perchesjoe perches31.06%215.38%
fabian frederickfabian frederick31.06%215.38%
Total282100.00%13100.00%

static struct cpufreq_driver pxa_cpufreq_driver = { .flags = CPUFREQ_NEED_INITIAL_FREQ_CHECK, .verify = cpufreq_generic_frequency_table_verify, .target_index = pxa_set_target, .init = pxa_cpufreq_init, .get = pxa_cpufreq_get, .name = "PXA2xx", };
static int __init pxa_cpu_init(void) { int ret = -ENODEV; if (cpu_is_pxa25x() || cpu_is_pxa27x()) ret = cpufreq_register_driver(&pxa_cpufreq_driver); return ret; }

Contributors

PersonTokensPropCommitsCommitProp
russell kingrussell king3191.18%150.00%
robert jarzmikrobert jarzmik38.82%150.00%
Total34100.00%2100.00%


static void __exit pxa_cpu_exit(void) { cpufreq_unregister_driver(&pxa_cpufreq_driver); }

Contributors

PersonTokensPropCommitsCommitProp
russell kingrussell king15100.00%1100.00%
Total15100.00%1100.00%

MODULE_AUTHOR("Intrinsyc Software Inc."); MODULE_DESCRIPTION("CPU frequency changing driver for the PXA architecture"); MODULE_LICENSE("GPL"); module_init(pxa_cpu_init); module_exit(pxa_cpu_exit);

Overall Contributors

PersonTokensPropCommitsCommitProp
robert jarzmikrobert jarzmik89849.59%310.00%
russell kingrussell king63134.84%26.67%
philipp zabelphilipp zabel955.25%13.33%
marc zyngiermarc zyngier603.31%13.33%
viresh kumarviresh kumar281.55%826.67%
marek vasutmarek vasut241.33%26.67%
fabian frederickfabian frederick241.33%26.67%
joe perchesjoe perches160.88%310.00%
arnd bergmannarnd bergmann130.72%310.00%
daniel mackdaniel mack80.44%13.33%
holger schurigholger schurig60.33%13.33%
randy dunlaprandy dunlap40.22%13.33%
dennis o'briendennis o'brien20.11%13.33%
hanjun guohanjun guo20.11%13.33%
Total1811100.00%30100.00%
Directory: drivers/cpufreq
Information contained on this website is for historical information purposes only and does not indicate or represent copyright ownership.
{% endraw %}