I am assuming overcharge/overheat/etc protection will kick in and stop charging worst case. Updated overclocking code based on huanyu and michyprima's versions. Still overclocked by default to 1113MHz but can be pushed to 1152MHz with SetCPU. Custom compiling with EXOVERCLOCK can now go to 1305MHz though I wouldn't recommend it. I find it unlikely any gain would be worth the risk of shortening the life of your CPU.
693 lines
22 KiB
C
693 lines
22 KiB
C
/*
|
|
* Copyright (c) 2009 Google, Inc.
|
|
* Copyright (c) 2008 QUALCOMM Incorporated.
|
|
*
|
|
* This software is licensed under the terms of the GNU General Public
|
|
* License version 2, as published by the Free Software Foundation, and
|
|
* may be copied, distributed, and modified under those terms.
|
|
*
|
|
* 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.
|
|
*
|
|
*/
|
|
|
|
#include <linux/kernel.h>
|
|
#include <linux/init.h>
|
|
#include <linux/io.h>
|
|
#include <linux/delay.h>
|
|
#include <linux/err.h>
|
|
#include <linux/mutex.h>
|
|
#include <linux/errno.h>
|
|
#include <linux/cpufreq.h>
|
|
#include <linux/regulator/consumer.h>
|
|
|
|
#include <mach/board.h>
|
|
#include <mach/msm_iomap.h>
|
|
|
|
#include "acpuclock.h"
|
|
#include "proc_comm.h"
|
|
#include "clock.h"
|
|
|
|
#if 0
|
|
#define DEBUG(x...) pr_info(x)
|
|
#else
|
|
#define DEBUG(x...) do {} while (0)
|
|
#endif
|
|
|
|
#define SHOT_SWITCH 4
|
|
#define HOP_SWITCH 5
|
|
#define SIMPLE_SLEW 6
|
|
#define COMPLEX_SLEW 7
|
|
|
|
#define SPSS_CLK_CNTL_ADDR (MSM_CSR_BASE + 0x100)
|
|
#define SPSS_CLK_SEL_ADDR (MSM_CSR_BASE + 0x104)
|
|
|
|
/* Scorpion PLL registers */
|
|
#define SCPLL_CTL_ADDR (MSM_SCPLL_BASE + 0x4)
|
|
#define SCPLL_STATUS_ADDR (MSM_SCPLL_BASE + 0x18)
|
|
#define SCPLL_FSM_CTL_EXT_ADDR (MSM_SCPLL_BASE + 0x10)
|
|
|
|
struct clkctl_acpu_speed {
|
|
unsigned acpu_khz;
|
|
unsigned clk_cfg;
|
|
unsigned clk_sel;
|
|
unsigned sc_l_value;
|
|
unsigned lpj;
|
|
int vdd;
|
|
unsigned axiclk_khz;
|
|
};
|
|
|
|
/* clock sources */
|
|
#define CLK_TCXO 0 /* 19.2 MHz */
|
|
#define CLK_GLOBAL_PLL 1 /* 768 MHz */
|
|
#define CLK_MODEM_PLL 4 /* 245 MHz (UMTS) or 235.93 MHz (CDMA) */
|
|
|
|
#define CCTL(src, div) (((src) << 4) | (div - 1))
|
|
|
|
/* core sources */
|
|
#define SRC_RAW 0 /* clock from SPSS_CLK_CNTL */
|
|
#define SRC_SCPLL 1 /* output of scpll 128-998 MHZ */
|
|
#define SRC_AXI 2 /* 128 MHz */
|
|
#define SRC_PLL1 3 /* 768 MHz */
|
|
|
|
struct clkctl_acpu_speed acpu_freq_tbl[] = {
|
|
#ifdef CONFIG_HTCLEO_UNDERVOLT_1000
|
|
{ 19200, CCTL(CLK_TCXO, 1), SRC_RAW, 0, 0, 1000, 14000 },
|
|
{ 128000, CCTL(CLK_TCXO, 1), SRC_AXI, 0, 0, 1000, 14000 },
|
|
{ 245000, CCTL(CLK_MODEM_PLL, 1), SRC_RAW, 0, 0, 1000, 29000 },
|
|
//{ 256000, CCTL(CLK_GLOBAL_PLL, 3), SRC_RAW, 0, 0, 1000, 29000 },
|
|
{ 384000, CCTL(CLK_TCXO, 1), SRC_SCPLL, 0x0A, 0, 1000, 58000 },
|
|
{ 422400, CCTL(CLK_TCXO, 1), SRC_SCPLL, 0x0B, 0, 1000, 117000 },
|
|
{ 460800, CCTL(CLK_TCXO, 1), SRC_SCPLL, 0x0C, 0, 1000, 117000 },
|
|
{ 499200, CCTL(CLK_TCXO, 1), SRC_SCPLL, 0x0D, 0, 1025, 117000 },
|
|
{ 537600, CCTL(CLK_TCXO, 1), SRC_SCPLL, 0x0E, 0, 1050, 117000 },
|
|
{ 576000, CCTL(CLK_TCXO, 1), SRC_SCPLL, 0x0F, 0, 1050, 117000 },
|
|
{ 614400, CCTL(CLK_TCXO, 1), SRC_SCPLL, 0x10, 0, 1075, 117000 },
|
|
{ 652800, CCTL(CLK_TCXO, 1), SRC_SCPLL, 0x11, 0, 1100, 117000 },
|
|
{ 691200, CCTL(CLK_TCXO, 1), SRC_SCPLL, 0x12, 0, 1125, 117000 },
|
|
{ 729600, CCTL(CLK_TCXO, 1), SRC_SCPLL, 0x13, 0, 1150, 117000 },
|
|
{ 768000, CCTL(CLK_TCXO, 1), SRC_SCPLL, 0x14, 0, 1150, 128000 },
|
|
{ 806400, CCTL(CLK_TCXO, 1), SRC_SCPLL, 0x15, 0, 1175, 128000 },
|
|
{ 844800, CCTL(CLK_TCXO, 1), SRC_SCPLL, 0x16, 0, 1200, 128000 },
|
|
{ 883200, CCTL(CLK_TCXO, 1), SRC_SCPLL, 0x17, 0, 1200, 128000 },
|
|
{ 921600, CCTL(CLK_TCXO, 1), SRC_SCPLL, 0x18, 0, 1225, 128000 },
|
|
{ 960000, CCTL(CLK_TCXO, 1), SRC_SCPLL, 0x19, 0, 1225, 128000 },
|
|
{ 998400, CCTL(CLK_TCXO, 1), SRC_SCPLL, 0x1A, 0, 1225, 128000 },
|
|
#elif CONFIG_HTCLEO_UNDERVOLT_925
|
|
// should work with most of HD2s
|
|
{ 19200, CCTL(CLK_TCXO, 1), SRC_RAW, 0, 0, 925, 14000 },
|
|
{ 128000, CCTL(CLK_TCXO, 1), SRC_AXI, 0, 0, 925, 14000 },
|
|
{ 245000, CCTL(CLK_MODEM_PLL, 1), SRC_RAW, 0, 0, 925, 29000 },
|
|
//{ 256000, CCTL(CLK_GLOBAL_PLL, 3), SRC_RAW, 0, 0, 925, 29000 },
|
|
{ 384000, CCTL(CLK_TCXO, 1), SRC_SCPLL, 0x0A, 0, 950, 58000 },
|
|
{ 422400, CCTL(CLK_TCXO, 1), SRC_SCPLL, 0x0B, 0, 975, 117000 },
|
|
{ 460800, CCTL(CLK_TCXO, 1), SRC_SCPLL, 0x0C, 0, 1000, 117000 },
|
|
{ 499200, CCTL(CLK_TCXO, 1), SRC_SCPLL, 0x0D, 0, 1025, 117000 },
|
|
{ 537600, CCTL(CLK_TCXO, 1), SRC_SCPLL, 0x0E, 0, 1050, 117000 },
|
|
{ 576000, CCTL(CLK_TCXO, 1), SRC_SCPLL, 0x0F, 0, 1050, 117000 },
|
|
{ 614400, CCTL(CLK_TCXO, 1), SRC_SCPLL, 0x10, 0, 1075, 117000 },
|
|
{ 652800, CCTL(CLK_TCXO, 1), SRC_SCPLL, 0x11, 0, 1100, 117000 },
|
|
{ 691200, CCTL(CLK_TCXO, 1), SRC_SCPLL, 0x12, 0, 1125, 117000 },
|
|
{ 729600, CCTL(CLK_TCXO, 1), SRC_SCPLL, 0x13, 0, 1150, 117000 },
|
|
{ 768000, CCTL(CLK_TCXO, 1), SRC_SCPLL, 0x14, 0, 1150, 128000 },
|
|
{ 806400, CCTL(CLK_TCXO, 1), SRC_SCPLL, 0x15, 0, 1175, 128000 },
|
|
{ 844800, CCTL(CLK_TCXO, 1), SRC_SCPLL, 0x16, 0, 1200, 128000 },
|
|
{ 883200, CCTL(CLK_TCXO, 1), SRC_SCPLL, 0x17, 0, 1200, 128000 },
|
|
{ 921600, CCTL(CLK_TCXO, 1), SRC_SCPLL, 0x18, 0, 1225, 128000 },
|
|
{ 960000, CCTL(CLK_TCXO, 1), SRC_SCPLL, 0x19, 0, 1225, 128000 },
|
|
{ 998400, CCTL(CLK_TCXO, 1), SRC_SCPLL, 0x1A, 0, 1225, 128000 },
|
|
#elif CONFIG_HTCLEO_UNDERVOLT_800
|
|
// not working yet
|
|
{ 19200, CCTL(CLK_TCXO, 1), SRC_RAW, 0, 0, 850, 14000 },
|
|
{ 128000, CCTL(CLK_TCXO, 1), SRC_AXI, 0, 0, 850, 14000 },
|
|
{ 245000, CCTL(CLK_MODEM_PLL, 1), SRC_RAW, 0, 0, 850, 29000 },
|
|
//{ 256000, CCTL(CLK_GLOBAL_PLL, 3), SRC_RAW, 0, 0, 850, 29000 },
|
|
{ 384000, CCTL(CLK_TCXO, 1), SRC_SCPLL, 0x0A, 0, 850, 58000 },
|
|
{ 422400, CCTL(CLK_TCXO, 1), SRC_SCPLL, 0x0B, 0, 875, 117000 },
|
|
{ 460800, CCTL(CLK_TCXO, 1), SRC_SCPLL, 0x0C, 0, 900, 117000 },
|
|
{ 499200, CCTL(CLK_TCXO, 1), SRC_SCPLL, 0x0D, 0, 925, 117000 },
|
|
{ 537600, CCTL(CLK_TCXO, 1), SRC_SCPLL, 0x0E, 0, 950, 117000 },
|
|
{ 576000, CCTL(CLK_TCXO, 1), SRC_SCPLL, 0x0F, 0, 950, 117000 },
|
|
{ 614400, CCTL(CLK_TCXO, 1), SRC_SCPLL, 0x10, 0, 975, 117000 },
|
|
{ 652800, CCTL(CLK_TCXO, 1), SRC_SCPLL, 0x11, 0, 1000, 117000 },
|
|
{ 691200, CCTL(CLK_TCXO, 1), SRC_SCPLL, 0x12, 0, 1025, 117000 },
|
|
{ 729600, CCTL(CLK_TCXO, 1), SRC_SCPLL, 0x13, 0, 1050, 117000 },
|
|
{ 768000, CCTL(CLK_TCXO, 1), SRC_SCPLL, 0x14, 0, 1125, 128000 },
|
|
{ 806400, CCTL(CLK_TCXO, 1), SRC_SCPLL, 0x15, 0, 1125, 128000 },
|
|
{ 844800, CCTL(CLK_TCXO, 1), SRC_SCPLL, 0x16, 0, 1150, 128000 },
|
|
{ 883200, CCTL(CLK_TCXO, 1), SRC_SCPLL, 0x17, 0, 1150, 128000 },
|
|
{ 921600, CCTL(CLK_TCXO, 1), SRC_SCPLL, 0x18, 0, 1175, 128000 },
|
|
{ 960000, CCTL(CLK_TCXO, 1), SRC_SCPLL, 0x19, 0, 1175, 128000 },
|
|
{ 998400, CCTL(CLK_TCXO, 1), SRC_SCPLL, 0x1A, 0, 1200, 128000 },
|
|
#else
|
|
{ 19200, CCTL(CLK_TCXO, 1), SRC_RAW, 0, 0, 1050, 14000},
|
|
{ 128000, CCTL(CLK_TCXO, 1), SRC_AXI, 0, 0, 1050, 14000 },
|
|
{ 245000, CCTL(CLK_MODEM_PLL, 1), SRC_RAW, 0, 0, 1050, 29000 },
|
|
/* Work arround for acpu resume hung, GPLL is turn off by arm9 */
|
|
/*{ 256000, CCTL(CLK_GLOBAL_PLL, 3), SRC_RAW, 0, 0, 1050, 29000 },*/
|
|
{ 384000, CCTL(CLK_TCXO, 1), SRC_SCPLL, 0x0A, 0, 1050, 58000 },
|
|
{ 422400, CCTL(CLK_TCXO, 1), SRC_SCPLL, 0x0B, 0, 1050, 117000 },
|
|
{ 460800, CCTL(CLK_TCXO, 1), SRC_SCPLL, 0x0C, 0, 1050, 117000 },
|
|
{ 499200, CCTL(CLK_TCXO, 1), SRC_SCPLL, 0x0D, 0, 1075, 117000 },
|
|
{ 537600, CCTL(CLK_TCXO, 1), SRC_SCPLL, 0x0E, 0, 1100, 117000 },
|
|
{ 576000, CCTL(CLK_TCXO, 1), SRC_SCPLL, 0x0F, 0, 1100, 117000 },
|
|
{ 614400, CCTL(CLK_TCXO, 1), SRC_SCPLL, 0x10, 0, 1125, 117000 },
|
|
{ 652800, CCTL(CLK_TCXO, 1), SRC_SCPLL, 0x11, 0, 1150, 117000 },
|
|
{ 691200, CCTL(CLK_TCXO, 1), SRC_SCPLL, 0x12, 0, 1175, 117000 },
|
|
{ 729600, CCTL(CLK_TCXO, 1), SRC_SCPLL, 0x13, 0, 1200, 117000 },
|
|
{ 768000, CCTL(CLK_TCXO, 1), SRC_SCPLL, 0x14, 0, 1200, 128000 },
|
|
{ 806400, CCTL(CLK_TCXO, 1), SRC_SCPLL, 0x15, 0, 1225, 128000 },
|
|
{ 844800, CCTL(CLK_TCXO, 1), SRC_SCPLL, 0x16, 0, 1250, 128000 },
|
|
{ 883200, CCTL(CLK_TCXO, 1), SRC_SCPLL, 0x17, 0, 1275, 128000 },
|
|
{ 921600, CCTL(CLK_TCXO, 1), SRC_SCPLL, 0x18, 0, 1300, 128000 },
|
|
{ 960000, CCTL(CLK_TCXO, 1), SRC_SCPLL, 0x19, 0, 1300, 128000 },
|
|
{ 998400, CCTL(CLK_TCXO, 1), SRC_SCPLL, 0x1A, 0, 1300, 128000 },
|
|
#endif
|
|
#ifdef CONFIG_HTCLEO_OVERCLOCK
|
|
#ifdef CONFIG_HTCLEO_UNDERVOLT_1000
|
|
{ 1036800, CCTL(CLK_TCXO, 1), SRC_SCPLL, 0x1B, 0, 1225, 128000 },
|
|
{ 1075200, CCTL(CLK_TCXO, 1), SRC_SCPLL, 0x1C, 0, 1250, 128000 },
|
|
{ 1113600, CCTL(CLK_TCXO, 1), SRC_SCPLL, 0x1D, 0, 1275, 128000 },
|
|
{ 1152000, CCTL(CLK_TCXO, 1), SRC_SCPLL, 0x1E, 0, 1300, 128000 },
|
|
#elif CONFIG_HTCLEO_UNDERVOLT_925
|
|
{ 1036800, CCTL(CLK_TCXO, 1), SRC_SCPLL, 0x1B, 0, 1225, 128000 },
|
|
{ 1075200, CCTL(CLK_TCXO, 1), SRC_SCPLL, 0x1C, 0, 1250, 128000 },
|
|
{ 1113600, CCTL(CLK_TCXO, 1), SRC_SCPLL, 0x1D, 0, 1275, 128000 },
|
|
{ 1152000, CCTL(CLK_TCXO, 1), SRC_SCPLL, 0x1E, 0, 1300, 128000 },
|
|
#elif CONFIG_HTCLEO_UNDERVOLT_800
|
|
{ 1036800, CCTL(CLK_TCXO, 1), SRC_SCPLL, 0x1B, 0, 1225, 128000 },
|
|
{ 1075200, CCTL(CLK_TCXO, 1), SRC_SCPLL, 0x1C, 0, 1250, 128000 },
|
|
{ 1113600, CCTL(CLK_TCXO, 1), SRC_SCPLL, 0x1D, 0, 1275, 128000 },
|
|
{ 1152000, CCTL(CLK_TCXO, 1), SRC_SCPLL, 0x1E, 0, 1300, 128000 },
|
|
#else
|
|
{ 1036800, CCTL(CLK_TCXO, 1), SRC_SCPLL, 0x1B, 0, 1300, 128000 },
|
|
{ 1075200, CCTL(CLK_TCXO, 1), SRC_SCPLL, 0x1C, 0, 1300, 128000 },
|
|
{ 1113600, CCTL(CLK_TCXO, 1), SRC_SCPLL, 0x1D, 0, 1300, 128000 },
|
|
{ 1152000, CCTL(CLK_TCXO, 1), SRC_SCPLL, 0x1E, 0, 1325, 128000 },
|
|
#endif
|
|
#endif
|
|
#ifdef CONFIG_HTCLEO_EXOVERCLOCK
|
|
{ 1190400, CCTL(CLK_TCXO, 1), SRC_SCPLL, 0x1F, 0, 1325, 128000 },
|
|
{ 1228800, CCTL(CLK_TCXO, 1), SRC_SCPLL, 0x20, 0, 1325, 128000 },
|
|
{ 1267200, CCTL(CLK_TCXO, 1), SRC_SCPLL, 0x21, 0, 1350, 128000 },
|
|
{ 1305600, CCTL(CLK_TCXO, 1), SRC_SCPLL, 0x22, 0, 1350, 128000 },
|
|
#endif
|
|
{ 0 },
|
|
};
|
|
|
|
/* select the standby clock that is used when switching scpll
|
|
* frequencies
|
|
*
|
|
* Currently: MPLL
|
|
*/
|
|
struct clkctl_acpu_speed *acpu_stby = &acpu_freq_tbl[2];
|
|
#define IS_ACPU_STANDBY(x) (((x)->clk_cfg == acpu_stby->clk_cfg) && \
|
|
((x)->clk_sel == acpu_stby->clk_sel))
|
|
|
|
struct clkctl_acpu_speed *acpu_mpll = &acpu_freq_tbl[2];
|
|
|
|
#ifdef CONFIG_CPU_FREQ_TABLE
|
|
static struct cpufreq_frequency_table freq_table[ARRAY_SIZE(acpu_freq_tbl)];
|
|
|
|
static void __init acpuclk_init_cpufreq_table(void)
|
|
{
|
|
int i;
|
|
int vdd;
|
|
for (i = 0; acpu_freq_tbl[i].acpu_khz; i++) {
|
|
freq_table[i].index = i;
|
|
freq_table[i].frequency = CPUFREQ_ENTRY_INVALID;
|
|
|
|
/* Skip speeds we don't want */
|
|
if ( acpu_freq_tbl[i].acpu_khz == 19200 ||
|
|
acpu_freq_tbl[i].acpu_khz == 128000 ||
|
|
acpu_freq_tbl[i].acpu_khz == 256000)
|
|
continue;
|
|
|
|
vdd = acpu_freq_tbl[i].vdd;
|
|
/* Allow mpll and the first scpll speeds */
|
|
if (acpu_freq_tbl[i].acpu_khz == acpu_mpll->acpu_khz ||
|
|
acpu_freq_tbl[i].acpu_khz == 384000) {
|
|
freq_table[i].frequency = acpu_freq_tbl[i].acpu_khz;
|
|
continue;
|
|
}
|
|
|
|
/* hastarin - Take every frequency. Just because it has the
|
|
same vdd does not mean it has the same current draw */
|
|
freq_table[i].frequency = acpu_freq_tbl[i].acpu_khz;
|
|
}
|
|
|
|
freq_table[i].index = i;
|
|
freq_table[i].frequency = CPUFREQ_TABLE_END;
|
|
|
|
cpufreq_frequency_table_get_attr(freq_table, smp_processor_id());
|
|
}
|
|
#else
|
|
#define acpuclk_init_cpufreq_table() do {} while (0);
|
|
#endif
|
|
|
|
struct clock_state {
|
|
struct clkctl_acpu_speed *current_speed;
|
|
struct mutex lock;
|
|
uint32_t acpu_switch_time_us;
|
|
uint32_t max_speed_delta_khz;
|
|
uint32_t vdd_switch_time_us;
|
|
unsigned long power_collapse_khz;
|
|
unsigned long wait_for_irq_khz;
|
|
struct clk* clk_ebi1;
|
|
struct regulator *regulator;
|
|
};
|
|
|
|
static struct clock_state drv_state = { 0 };
|
|
|
|
|
|
struct clk *clk_get(struct device *dev, const char *id);
|
|
unsigned long clk_get_rate(struct clk *clk);
|
|
int clk_set_rate(struct clk *clk, unsigned long rate);
|
|
|
|
static DEFINE_SPINLOCK(acpu_lock);
|
|
|
|
#define PLLMODE_POWERDOWN 0
|
|
#define PLLMODE_BYPASS 1
|
|
#define PLLMODE_STANDBY 2
|
|
#define PLLMODE_FULL_CAL 4
|
|
#define PLLMODE_HALF_CAL 5
|
|
#define PLLMODE_STEP_CAL 6
|
|
#define PLLMODE_NORMAL 7
|
|
#define PLLMODE_MASK 7
|
|
|
|
static void scpll_power_down(void)
|
|
{
|
|
uint32_t val;
|
|
|
|
/* Wait for any frequency switches to finish. */
|
|
while (readl(SCPLL_STATUS_ADDR) & 0x1)
|
|
;
|
|
|
|
/* put the pll in standby mode */
|
|
val = readl(SCPLL_CTL_ADDR);
|
|
val = (val & (~PLLMODE_MASK)) | PLLMODE_STANDBY;
|
|
writel(val, SCPLL_CTL_ADDR);
|
|
dmb();
|
|
|
|
/* wait to stabilize in standby mode */
|
|
udelay(10);
|
|
|
|
val = (val & (~PLLMODE_MASK)) | PLLMODE_POWERDOWN;
|
|
writel(val, SCPLL_CTL_ADDR);
|
|
dmb();
|
|
}
|
|
|
|
static void scpll_set_freq(uint32_t lval)
|
|
{
|
|
uint32_t val, ctl;
|
|
|
|
if (lval > 33)
|
|
lval = 33;
|
|
if (lval < 10)
|
|
lval = 10;
|
|
|
|
/* wait for any calibrations or frequency switches to finish */
|
|
while (readl(SCPLL_STATUS_ADDR) & 0x3)
|
|
;
|
|
|
|
ctl = readl(SCPLL_CTL_ADDR);
|
|
|
|
if ((ctl & PLLMODE_MASK) != PLLMODE_NORMAL) {
|
|
/* put the pll in standby mode */
|
|
writel((ctl & (~PLLMODE_MASK)) | PLLMODE_STANDBY, SCPLL_CTL_ADDR);
|
|
dmb();
|
|
|
|
/* wait to stabilize in standby mode */
|
|
udelay(10);
|
|
|
|
/* switch to 384 MHz */
|
|
val = readl(SCPLL_FSM_CTL_EXT_ADDR);
|
|
val = (val & (~0x1FF)) | (0x0A << 3) | SHOT_SWITCH;
|
|
writel(val, SCPLL_FSM_CTL_EXT_ADDR);
|
|
dmb();
|
|
|
|
ctl = readl(SCPLL_CTL_ADDR);
|
|
writel(ctl | PLLMODE_NORMAL, SCPLL_CTL_ADDR);
|
|
dmb();
|
|
|
|
/* wait for frequency switch to finish */
|
|
while (readl(SCPLL_STATUS_ADDR) & 0x1)
|
|
;
|
|
|
|
/* completion bit is not reliable for SHOT switch */
|
|
udelay(25);
|
|
}
|
|
|
|
/* write the new L val and switch mode */
|
|
val = readl(SCPLL_FSM_CTL_EXT_ADDR);
|
|
val = (val & (~0x1FF)) | (lval << 3) | HOP_SWITCH;
|
|
writel(val, SCPLL_FSM_CTL_EXT_ADDR);
|
|
dmb();
|
|
|
|
ctl = readl(SCPLL_CTL_ADDR);
|
|
writel(ctl | PLLMODE_NORMAL, SCPLL_CTL_ADDR);
|
|
dmb();
|
|
|
|
/* wait for frequency switch to finish */
|
|
while (readl(SCPLL_STATUS_ADDR) & 0x1)
|
|
;
|
|
}
|
|
|
|
/* this is still a bit weird... */
|
|
static void select_clock(unsigned src, unsigned config)
|
|
{
|
|
uint32_t val;
|
|
|
|
if (src == SRC_RAW) {
|
|
uint32_t sel = readl(SPSS_CLK_SEL_ADDR);
|
|
unsigned shift = (sel & 1) ? 8 : 0;
|
|
|
|
/* set other clock source to the new configuration */
|
|
val = readl(SPSS_CLK_CNTL_ADDR);
|
|
val = (val & (~(0x7F << shift))) | (config << shift);
|
|
writel(val, SPSS_CLK_CNTL_ADDR);
|
|
|
|
/* switch to other clock source */
|
|
writel(sel ^ 1, SPSS_CLK_SEL_ADDR);
|
|
|
|
dmb(); /* necessary? */
|
|
}
|
|
|
|
/* switch to new source */
|
|
val = readl(SPSS_CLK_SEL_ADDR) & (~6);
|
|
writel(val | ((src & 3) << 1), SPSS_CLK_SEL_ADDR);
|
|
}
|
|
|
|
static int acpuclk_set_vdd_level(int vdd)
|
|
{
|
|
if (!drv_state.regulator || IS_ERR(drv_state.regulator)) {
|
|
drv_state.regulator = regulator_get(NULL, "acpu_vcore");
|
|
if (IS_ERR(drv_state.regulator)) {
|
|
pr_info("acpuclk_set_vdd_level %d no regulator\n", vdd);
|
|
/* Assume that the PMIC supports scaling the processor
|
|
* to its maximum frequency at its default voltage.
|
|
*/
|
|
return 0;
|
|
}
|
|
pr_info("acpuclk_set_vdd_level got regulator\n");
|
|
}
|
|
vdd *= 1000; /* mV -> uV */
|
|
return regulator_set_voltage(drv_state.regulator, vdd, vdd);
|
|
}
|
|
|
|
int acpuclk_set_rate(unsigned long rate, enum setrate_reason reason)
|
|
{
|
|
struct clkctl_acpu_speed *cur, *next;
|
|
unsigned long flags;
|
|
|
|
cur = drv_state.current_speed;
|
|
|
|
/* convert to KHz */
|
|
rate /= 1000;
|
|
|
|
DEBUG("acpuclk_set_rate(%d,%d)\n", (int) rate, reason);
|
|
|
|
if (rate == cur->acpu_khz || rate == 0)
|
|
return 0;
|
|
|
|
next = acpu_freq_tbl;
|
|
for (;;) {
|
|
if (next->acpu_khz == rate)
|
|
break;
|
|
if (next->acpu_khz == 0)
|
|
return -EINVAL;
|
|
next++;
|
|
}
|
|
|
|
if (reason == SETRATE_CPUFREQ) {
|
|
mutex_lock(&drv_state.lock);
|
|
/* Increase VDD if needed. */
|
|
if (next->vdd > cur->vdd) {
|
|
if (acpuclk_set_vdd_level(next->vdd)) {
|
|
pr_err("acpuclock: Unable to increase ACPU VDD.\n");
|
|
mutex_unlock(&drv_state.lock);
|
|
return -EINVAL;
|
|
}
|
|
}
|
|
}
|
|
|
|
spin_lock_irqsave(&acpu_lock, flags);
|
|
|
|
DEBUG("sel=%d cfg=%02x lv=%02x -> sel=%d, cfg=%02x lv=%02x\n",
|
|
cur->clk_sel, cur->clk_cfg, cur->sc_l_value,
|
|
next->clk_sel, next->clk_cfg, next->sc_l_value);
|
|
|
|
if (next->clk_sel == SRC_SCPLL) {
|
|
/* curr -> standby(MPLL speed) -> target */
|
|
if (!IS_ACPU_STANDBY(cur))
|
|
select_clock(acpu_stby->clk_sel, acpu_stby->clk_cfg);
|
|
loops_per_jiffy = next->lpj;
|
|
scpll_set_freq(next->sc_l_value);
|
|
select_clock(SRC_SCPLL, 0);
|
|
} else {
|
|
loops_per_jiffy = next->lpj;
|
|
if (cur->clk_sel == SRC_SCPLL) {
|
|
select_clock(acpu_stby->clk_sel, acpu_stby->clk_cfg);
|
|
select_clock(next->clk_sel, next->clk_cfg);
|
|
scpll_power_down();
|
|
} else {
|
|
select_clock(next->clk_sel, next->clk_cfg);
|
|
}
|
|
}
|
|
|
|
drv_state.current_speed = next;
|
|
|
|
spin_unlock_irqrestore(&acpu_lock, flags);
|
|
|
|
#ifndef CONFIG_AXI_SCREEN_POLICY
|
|
if (reason == SETRATE_CPUFREQ || reason == SETRATE_PC) {
|
|
if (cur->axiclk_khz != next->axiclk_khz)
|
|
clk_set_rate(drv_state.clk_ebi1, next->axiclk_khz * 1000);
|
|
DEBUG("acpuclk_set_rate switch axi to %d\n",
|
|
clk_get_rate(drv_state.clk_ebi1));
|
|
}
|
|
#endif
|
|
if (reason == SETRATE_CPUFREQ) {
|
|
/* Drop VDD level if we can. */
|
|
if (next->vdd < cur->vdd) {
|
|
if (acpuclk_set_vdd_level(next->vdd))
|
|
pr_err("acpuclock: Unable to drop ACPU VDD.\n");
|
|
}
|
|
mutex_unlock(&drv_state.lock);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static unsigned __init acpuclk_find_speed(void)
|
|
{
|
|
uint32_t sel, val;
|
|
|
|
sel = readl(SPSS_CLK_SEL_ADDR);
|
|
switch ((sel & 6) >> 1) {
|
|
case 1:
|
|
val = readl(SCPLL_FSM_CTL_EXT_ADDR);
|
|
val = (val >> 3) & 0x3f;
|
|
return val * 38400;
|
|
case 2:
|
|
return 128000;
|
|
default:
|
|
pr_err("acpu_find_speed: failed\n");
|
|
BUG();
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
#define PCOM_MODEM_PLL 0
|
|
static int pll_request(unsigned id, unsigned on)
|
|
{
|
|
on = !!on;
|
|
return msm_proc_comm(PCOM_CLKCTL_RPC_PLL_REQUEST, &id, &on);
|
|
}
|
|
|
|
/* Spare register populated with efuse data on max ACPU freq. */
|
|
#define CT_CSR_PHYS 0xA8700000
|
|
#define TCSR_SPARE2_ADDR (ct_csr_base + 0x60)
|
|
|
|
void __init acpu_freq_tbl_fixup(void)
|
|
{
|
|
void __iomem *ct_csr_base;
|
|
uint32_t tcsr_spare2;
|
|
unsigned int max_acpu_khz;
|
|
unsigned int i;
|
|
|
|
ct_csr_base = ioremap(CT_CSR_PHYS, PAGE_SIZE);
|
|
BUG_ON(ct_csr_base == NULL);
|
|
|
|
tcsr_spare2 = readl(TCSR_SPARE2_ADDR);
|
|
|
|
/* Check if the register is supported and meaningful. */
|
|
if ((tcsr_spare2 & 0xF000) != 0xA000) {
|
|
pr_info("Efuse data on Max ACPU freq not present.\n");
|
|
goto skip_efuse_fixup;
|
|
}
|
|
|
|
switch (tcsr_spare2 & 0xF0) {
|
|
case 0x70:
|
|
max_acpu_khz = 768000;
|
|
break;
|
|
case 0x30:
|
|
case 0x00:
|
|
#ifdef CONFIG_HTCLEO_EXOVERCLOCK
|
|
max_acpu_khz = 1305600;
|
|
#elif CONFIG_HTCLEO_OVERCLOCK
|
|
max_acpu_khz = 1152000;
|
|
#else
|
|
max_acpu_khz = 998400;
|
|
#endif
|
|
break;
|
|
case 0x10:
|
|
max_acpu_khz = 1267200;
|
|
break;
|
|
default:
|
|
pr_warning("Invalid efuse data (%x) on Max ACPU freq!\n",
|
|
tcsr_spare2);
|
|
goto skip_efuse_fixup;
|
|
}
|
|
|
|
pr_info("Max ACPU freq from efuse data is %d KHz\n", max_acpu_khz);
|
|
|
|
for (i = 0; acpu_freq_tbl[i].acpu_khz != 0; i++) {
|
|
if (acpu_freq_tbl[i].acpu_khz > max_acpu_khz) {
|
|
acpu_freq_tbl[i].acpu_khz = 0;
|
|
break;
|
|
}
|
|
}
|
|
skip_efuse_fixup:
|
|
iounmap(ct_csr_base);
|
|
}
|
|
|
|
static void __init acpuclk_init(void)
|
|
{
|
|
struct clkctl_acpu_speed *speed;
|
|
unsigned init_khz;
|
|
|
|
init_khz = acpuclk_find_speed();
|
|
|
|
/* request the modem pll, and then drop it. We don't want to keep a
|
|
* ref to it, but we do want to make sure that it is initialized at
|
|
* this point. The ARM9 will ensure that the MPLL is always on
|
|
* once it is fully booted, but it may not be up by the time we get
|
|
* to here. So, our pll_request for it will block until the mpll is
|
|
* actually up. We want it up because we will want to use it as a
|
|
* temporary step during frequency scaling. */
|
|
pll_request(PCOM_MODEM_PLL, 1);
|
|
pll_request(PCOM_MODEM_PLL, 0);
|
|
|
|
if (!(readl(MSM_CLK_CTL_BASE + 0x300) & 1)) {
|
|
pr_err("%s: MPLL IS NOT ON!!! RUN AWAY!!\n", __func__);
|
|
BUG();
|
|
}
|
|
|
|
/* Move to 768MHz for boot, which is a safe frequency
|
|
* for all versions of Scorpion at the moment.
|
|
*/
|
|
speed = acpu_freq_tbl;
|
|
for (;;) {
|
|
#ifdef CONFIG_HTCLEO_EXOVERCLOCK
|
|
if (speed->acpu_khz == 998400)
|
|
break;
|
|
#elif CONFIG_HTCLEO_OVERCLOCK
|
|
if (speed->acpu_khz == 883200)
|
|
break;
|
|
#else
|
|
if (speed->acpu_khz == 768000)
|
|
break;
|
|
#endif
|
|
#ifdef CONFIG_HTCLEO_EXOVERCLOCK
|
|
if (speed->acpu_khz == 0) {
|
|
pr_err("acpuclk_init: cannot find 998.4MHz\n");
|
|
#elif CONFIG_HTCLEO_OVERCLOCK
|
|
if (speed->acpu_khz == 0) {
|
|
pr_err("acpuclk_init: cannot find 883.2MHz\n");
|
|
#else
|
|
if (speed->acpu_khz == 0) {
|
|
pr_err("acpuclk_init: cannot find 768MHz\n");
|
|
#endif
|
|
BUG();
|
|
}
|
|
speed++;
|
|
}
|
|
|
|
if (init_khz != speed->acpu_khz) {
|
|
/* Bootloader needs to have SCPLL operating, but we're
|
|
* going to step over to the standby clock and make sure
|
|
* we select the right frequency on SCPLL and then
|
|
* step back to it, to make sure we're sane here.
|
|
*/
|
|
select_clock(acpu_stby->clk_sel, acpu_stby->clk_cfg);
|
|
scpll_power_down();
|
|
scpll_set_freq(speed->sc_l_value);
|
|
select_clock(SRC_SCPLL, 0);
|
|
}
|
|
drv_state.current_speed = speed;
|
|
|
|
for (speed = acpu_freq_tbl; speed->acpu_khz; speed++)
|
|
speed->lpj = cpufreq_scale(loops_per_jiffy,
|
|
init_khz, speed->acpu_khz);
|
|
|
|
loops_per_jiffy = drv_state.current_speed->lpj;
|
|
}
|
|
|
|
unsigned long acpuclk_get_rate(void)
|
|
{
|
|
return drv_state.current_speed->acpu_khz;
|
|
}
|
|
|
|
uint32_t acpuclk_get_switch_time(void)
|
|
{
|
|
return drv_state.acpu_switch_time_us;
|
|
}
|
|
|
|
unsigned long acpuclk_power_collapse(int from_idle)
|
|
{
|
|
int ret = acpuclk_get_rate();
|
|
enum setrate_reason reason = (from_idle) ? SETRATE_PC_IDLE : SETRATE_PC;
|
|
if (ret > drv_state.power_collapse_khz)
|
|
acpuclk_set_rate(drv_state.power_collapse_khz * 1000, reason);
|
|
return ret * 1000;
|
|
}
|
|
|
|
unsigned long acpuclk_get_wfi_rate(void)
|
|
{
|
|
return drv_state.wait_for_irq_khz * 1000;
|
|
}
|
|
|
|
unsigned long acpuclk_wait_for_irq(void)
|
|
{
|
|
int ret = acpuclk_get_rate();
|
|
if (ret > drv_state.wait_for_irq_khz)
|
|
acpuclk_set_rate(drv_state.wait_for_irq_khz * 1000, SETRATE_SWFI);
|
|
return ret * 1000;
|
|
}
|
|
|
|
void __init msm_acpu_clock_init(struct msm_acpu_clock_platform_data *clkdata)
|
|
{
|
|
spin_lock_init(&acpu_lock);
|
|
mutex_init(&drv_state.lock);
|
|
|
|
drv_state.acpu_switch_time_us = clkdata->acpu_switch_time_us;
|
|
drv_state.max_speed_delta_khz = clkdata->max_speed_delta_khz;
|
|
drv_state.vdd_switch_time_us = clkdata->vdd_switch_time_us;
|
|
drv_state.power_collapse_khz = clkdata->power_collapse_khz;
|
|
drv_state.wait_for_irq_khz = clkdata->wait_for_irq_khz;
|
|
|
|
if (clkdata->mpll_khz)
|
|
acpu_mpll->acpu_khz = clkdata->mpll_khz;
|
|
|
|
acpu_freq_tbl_fixup();
|
|
acpuclk_init();
|
|
acpuclk_init_cpufreq_table();
|
|
drv_state.clk_ebi1 = clk_get(NULL,"ebi1_clk");
|
|
#ifndef CONFIG_AXI_SCREEN_POLICY
|
|
clk_set_rate(drv_state.clk_ebi1, drv_state.current_speed->axiclk_khz * 1000);
|
|
#endif
|
|
}
|