android_kernel_cmhtcleo/arch/arm/mach-msm/acpuclock-scorpion.c
2010-08-27 11:19:57 +02:00

571 lines
15 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[] = {
{ 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 },
{ 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 using the global pll */
if (acpu_freq_tbl[i].acpu_khz == 256000 ||
acpu_freq_tbl[i].acpu_khz == 19200)
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;
}
/* Take the fastest speed available at the specified VDD level */
if (vdd != acpu_freq_tbl[i + 1].vdd)
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:
max_acpu_khz = 998400;
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 (;;) {
if (speed->acpu_khz == 768000)
break;
if (speed->acpu_khz == 0) {
pr_err("acpuclk_init: cannot find 768MHz\n");
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
}