Path: blob/master/arch/arm/mach-pxa/cpufreq-pxa3xx.c
10817 views
/*1* linux/arch/arm/mach-pxa/cpufreq-pxa3xx.c2*3* Copyright (C) 2008 Marvell International Ltd.4*5* This program is free software; you can redistribute it and/or modify6* it under the terms of the GNU General Public License as published by7* the Free Software Foundation; either version 2 of the License, or8* (at your option) any later version.9*/1011#include <linux/kernel.h>12#include <linux/module.h>13#include <linux/sched.h>14#include <linux/init.h>15#include <linux/cpufreq.h>16#include <linux/slab.h>1718#include <mach/pxa3xx-regs.h>1920#include "generic.h"2122#define HSS_104M (0)23#define HSS_156M (1)24#define HSS_208M (2)25#define HSS_312M (3)2627#define SMCFS_78M (0)28#define SMCFS_104M (2)29#define SMCFS_208M (5)3031#define SFLFS_104M (0)32#define SFLFS_156M (1)33#define SFLFS_208M (2)34#define SFLFS_312M (3)3536#define XSPCLK_156M (0)37#define XSPCLK_NONE (3)3839#define DMCFS_26M (0)40#define DMCFS_260M (3)4142struct pxa3xx_freq_info {43unsigned int cpufreq_mhz;44unsigned int core_xl : 5;45unsigned int core_xn : 3;46unsigned int hss : 2;47unsigned int dmcfs : 2;48unsigned int smcfs : 3;49unsigned int sflfs : 2;50unsigned int df_clkdiv : 3;5152int vcc_core; /* in mV */53int vcc_sram; /* in mV */54};5556#define OP(cpufreq, _xl, _xn, _hss, _dmc, _smc, _sfl, _dfi, vcore, vsram) \57{ \58.cpufreq_mhz = cpufreq, \59.core_xl = _xl, \60.core_xn = _xn, \61.hss = HSS_##_hss##M, \62.dmcfs = DMCFS_##_dmc##M, \63.smcfs = SMCFS_##_smc##M, \64.sflfs = SFLFS_##_sfl##M, \65.df_clkdiv = _dfi, \66.vcc_core = vcore, \67.vcc_sram = vsram, \68}6970static struct pxa3xx_freq_info pxa300_freqs[] = {71/* CPU XL XN HSS DMEM SMEM SRAM DFI VCC_CORE VCC_SRAM */72OP(104, 8, 1, 104, 260, 78, 104, 3, 1000, 1100), /* 104MHz */73OP(208, 16, 1, 104, 260, 104, 156, 2, 1000, 1100), /* 208MHz */74OP(416, 16, 2, 156, 260, 104, 208, 2, 1100, 1200), /* 416MHz */75OP(624, 24, 2, 208, 260, 208, 312, 3, 1375, 1400), /* 624MHz */76};7778static struct pxa3xx_freq_info pxa320_freqs[] = {79/* CPU XL XN HSS DMEM SMEM SRAM DFI VCC_CORE VCC_SRAM */80OP(104, 8, 1, 104, 260, 78, 104, 3, 1000, 1100), /* 104MHz */81OP(208, 16, 1, 104, 260, 104, 156, 2, 1000, 1100), /* 208MHz */82OP(416, 16, 2, 156, 260, 104, 208, 2, 1100, 1200), /* 416MHz */83OP(624, 24, 2, 208, 260, 208, 312, 3, 1375, 1400), /* 624MHz */84OP(806, 31, 2, 208, 260, 208, 312, 3, 1400, 1400), /* 806MHz */85};8687static unsigned int pxa3xx_freqs_num;88static struct pxa3xx_freq_info *pxa3xx_freqs;89static struct cpufreq_frequency_table *pxa3xx_freqs_table;9091static int setup_freqs_table(struct cpufreq_policy *policy,92struct pxa3xx_freq_info *freqs, int num)93{94struct cpufreq_frequency_table *table;95int i;9697table = kzalloc((num + 1) * sizeof(*table), GFP_KERNEL);98if (table == NULL)99return -ENOMEM;100101for (i = 0; i < num; i++) {102table[i].index = i;103table[i].frequency = freqs[i].cpufreq_mhz * 1000;104}105table[num].index = i;106table[num].frequency = CPUFREQ_TABLE_END;107108pxa3xx_freqs = freqs;109pxa3xx_freqs_num = num;110pxa3xx_freqs_table = table;111112return cpufreq_frequency_table_cpuinfo(policy, table);113}114115static void __update_core_freq(struct pxa3xx_freq_info *info)116{117uint32_t mask = ACCR_XN_MASK | ACCR_XL_MASK;118uint32_t accr = ACCR;119uint32_t xclkcfg;120121accr &= ~(ACCR_XN_MASK | ACCR_XL_MASK | ACCR_XSPCLK_MASK);122accr |= ACCR_XN(info->core_xn) | ACCR_XL(info->core_xl);123124/* No clock until core PLL is re-locked */125accr |= ACCR_XSPCLK(XSPCLK_NONE);126127xclkcfg = (info->core_xn == 2) ? 0x3 : 0x2; /* turbo bit */128129ACCR = accr;130__asm__("mcr p14, 0, %0, c6, c0, 0\n" : : "r"(xclkcfg));131132while ((ACSR & mask) != (accr & mask))133cpu_relax();134}135136static void __update_bus_freq(struct pxa3xx_freq_info *info)137{138uint32_t mask;139uint32_t accr = ACCR;140141mask = ACCR_SMCFS_MASK | ACCR_SFLFS_MASK | ACCR_HSS_MASK |142ACCR_DMCFS_MASK;143144accr &= ~mask;145accr |= ACCR_SMCFS(info->smcfs) | ACCR_SFLFS(info->sflfs) |146ACCR_HSS(info->hss) | ACCR_DMCFS(info->dmcfs);147148ACCR = accr;149150while ((ACSR & mask) != (accr & mask))151cpu_relax();152}153154static int pxa3xx_cpufreq_verify(struct cpufreq_policy *policy)155{156return cpufreq_frequency_table_verify(policy, pxa3xx_freqs_table);157}158159static unsigned int pxa3xx_cpufreq_get(unsigned int cpu)160{161return pxa3xx_get_clk_frequency_khz(0);162}163164static int pxa3xx_cpufreq_set(struct cpufreq_policy *policy,165unsigned int target_freq,166unsigned int relation)167{168struct pxa3xx_freq_info *next;169struct cpufreq_freqs freqs;170unsigned long flags;171int idx;172173if (policy->cpu != 0)174return -EINVAL;175176/* Lookup the next frequency */177if (cpufreq_frequency_table_target(policy, pxa3xx_freqs_table,178target_freq, relation, &idx))179return -EINVAL;180181next = &pxa3xx_freqs[idx];182183freqs.old = policy->cur;184freqs.new = next->cpufreq_mhz * 1000;185freqs.cpu = policy->cpu;186187pr_debug("CPU frequency from %d MHz to %d MHz%s\n",188freqs.old / 1000, freqs.new / 1000,189(freqs.old == freqs.new) ? " (skipped)" : "");190191if (freqs.old == target_freq)192return 0;193194cpufreq_notify_transition(&freqs, CPUFREQ_PRECHANGE);195196local_irq_save(flags);197__update_core_freq(next);198__update_bus_freq(next);199local_irq_restore(flags);200201cpufreq_notify_transition(&freqs, CPUFREQ_POSTCHANGE);202203return 0;204}205206static int pxa3xx_cpufreq_init(struct cpufreq_policy *policy)207{208int ret = -EINVAL;209210/* set default policy and cpuinfo */211policy->cpuinfo.min_freq = 104000;212policy->cpuinfo.max_freq = (cpu_is_pxa320()) ? 806000 : 624000;213policy->cpuinfo.transition_latency = 1000; /* FIXME: 1 ms, assumed */214policy->max = pxa3xx_get_clk_frequency_khz(0);215policy->cur = policy->min = policy->max;216217if (cpu_is_pxa300() || cpu_is_pxa310())218ret = setup_freqs_table(policy, ARRAY_AND_SIZE(pxa300_freqs));219220if (cpu_is_pxa320())221ret = setup_freqs_table(policy, ARRAY_AND_SIZE(pxa320_freqs));222223if (ret) {224pr_err("failed to setup frequency table\n");225return ret;226}227228pr_info("CPUFREQ support for PXA3xx initialized\n");229return 0;230}231232static struct cpufreq_driver pxa3xx_cpufreq_driver = {233.verify = pxa3xx_cpufreq_verify,234.target = pxa3xx_cpufreq_set,235.init = pxa3xx_cpufreq_init,236.get = pxa3xx_cpufreq_get,237.name = "pxa3xx-cpufreq",238};239240static int __init cpufreq_init(void)241{242if (cpu_is_pxa3xx())243return cpufreq_register_driver(&pxa3xx_cpufreq_driver);244245return 0;246}247module_init(cpufreq_init);248249static void __exit cpufreq_exit(void)250{251cpufreq_unregister_driver(&pxa3xx_cpufreq_driver);252}253module_exit(cpufreq_exit);254255MODULE_DESCRIPTION("CPU frequency scaling driver for PXA3xx");256MODULE_LICENSE("GPL");257258259