blob: 3e7602deb0e2c481a818a9dbf51cde441eae43be [file] [log] [blame]
/*
* Copyright (c) 2008, Google Inc.
* All rights reserved.
* Copyright (c) 2009-2012, The Linux Foundation. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following
* disclaimer in the documentation and/or other materials provided
* with the distribution.
* * Neither the name of The Linux Foundation nor the names of its
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
* BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
* OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
* IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include <stdint.h>
#include <kernel/thread.h>
#include <platform/iomap.h>
#include <reg.h>
#include <smem.h>
#include <debug.h>
#include <mmc.h>
#define ARRAY_SIZE(x) (sizeof(x)/sizeof((x)[0]))
#define BIT(x) (1 << (x))
#define A11S_CLK_CNTL_ADDR (MSM_CSR_BASE + 0x100)
#define A11S_CLK_SEL_ADDR (MSM_CSR_BASE + 0x104)
#define VDD_SVS_PLEVEL_ADDR (MSM_CSR_BASE + 0x124)
#define PLL2_MODE_ADDR (MSM_CLK_CTL_BASE + 0x338)
#define PLL4_MODE_ADDR (MSM_CLK_CTL_BASE + 0x374)
#define PLL_RESET_N BIT(2)
#define PLL_BYPASSNL BIT(1)
#define PLL_OUTCTRL BIT(0)
#define SRC_SEL_TCX0 0 /* TCXO */
#define SRC_SEL_PLL1 1 /* PLL1: modem_pll */
#define SRC_SEL_PLL2 2 /* PLL2: backup_pll_0 */
#define SRC_SEL_PLL3 3 /* PLL3: backup_pll_1 */
#define SRC_SEL_PLL4 6 /* PLL4: sparrow_pll */
#define DIV_1 0
#define DIV_2 1
#define DIV_3 2
#define DIV_4 3
#define DIV_5 4
#define DIV_6 5
#define DIV_7 6
#define DIV_8 7
#define DIV_9 8
#define DIV_10 9
#define DIV_11 10
#define DIV_12 11
#define DIV_13 12
#define DIV_14 13
#define DIV_15 14
#define DIV_16 15
#define WAIT_CNT 100
#define MIN_AXI_HZ 120000000
#define ACPU_800MHZ 41
#define A11S_CLK_SEL_MASK 0x1 /* bits 2:0 */
/* The stepping frequencies have been choosen to make sure the step
* is <= 256 MHz for both 7x27a and 7x25a targets. The
* table also assumes the ACPU is running at TCXO freq and AHB div is
* set to DIV_1.
*
* To use the tables:
* - Start at location 0/1 depending on clock source sel bit.
* - Set values till end of table skipping every other entry.
* - When you reach the end of the table, you are done scaling.
*/
uint32_t const clk_cntl_reg_val_7627A[] = {
(WAIT_CNT << 16) | (SRC_SEL_PLL2 << 4) | DIV_16,
(WAIT_CNT << 16) | (SRC_SEL_PLL2 << 12) | (DIV_8 << 8),
(WAIT_CNT << 16) | (SRC_SEL_PLL2 << 4) | DIV_4,
(WAIT_CNT << 16) | (SRC_SEL_PLL2 << 12) | (DIV_2 << 8),
/* TODO: Fix it for 800MHz */
#if 0
(WAIT_CNT << 16) | (SRC_SEL_PLL4 << 4) | DIV_1,
#endif
};
/*
* Use PLL4 to run acpu @ 1.2 GHZ
*/
uint32_t const clk_cntl_reg_val_8X25[] = {
(WAIT_CNT << 16) | (SRC_SEL_PLL4 << 4) | DIV_2,
(WAIT_CNT << 16) | (SRC_SEL_PLL4 << 12) | (DIV_1 << 8),
};
uint32_t const clk_cntl_reg_val_7625A[] = {
(WAIT_CNT << 16) | (SRC_SEL_PLL2 << 4) | DIV_16,
(WAIT_CNT << 16) | (SRC_SEL_PLL2 << 12) | (DIV_8 << 8),
(WAIT_CNT << 16) | (SRC_SEL_PLL2 << 4) | DIV_4,
(WAIT_CNT << 16) | (SRC_SEL_PLL2 << 12) | (DIV_2 << 8),
};
/* Using DIV_1 for all cases to avoid worrying about turbo vs. normal
* mode. Able to use DIV_1 for all steps because it's the largest AND
* the final value. */
uint32_t const clk_sel_reg_val[] = {
DIV_1 << 1 | 1, /* Switch to src1 */
DIV_1 << 1 | 0, /* Switch to src0 */
};
/*
* Mask to make sure current selected src frequency doesn't change.
*/
uint32_t const clk_cntl_mask[] = {
0x0000FF00, /* Mask to read src0 */
0x000000FF /* Mask to read src1 */
};
/* enum for SDC CLK IDs */
enum {
SDC1_CLK = 19,
SDC1_PCLK = 20,
SDC2_CLK = 21,
SDC2_PCLK = 22,
SDC3_CLK = 23,
SDC3_PCLK = 24,
SDC4_CLK = 25,
SDC4_PCLK = 26
};
/* Zero'th entry is dummy */
static uint8_t sdc_clk[] = { 0, SDC1_CLK, SDC2_CLK, SDC3_CLK, SDC4_CLK };
static uint8_t sdc_pclk[] = { 0, SDC1_PCLK, SDC2_PCLK, SDC3_PCLK, SDC4_PCLK };
/* VDD_PLEVEL */
unsigned vdd_plevel = 0;
void mdelay(unsigned msecs);
unsigned board_msm_id(void);
unsigned board_msm_version(void);
void pll_enable(void *pll_mode_addr)
{
/* TODO: Need to add spin-lock to avoid race conditions */
uint32_t nVal;
/* Check status */
nVal = readl(pll_mode_addr);
if (nVal & PLL_OUTCTRL)
return;
/* Put the PLL in reset mode */
nVal = 0;
nVal &= ~PLL_RESET_N;
nVal &= ~PLL_BYPASSNL;
nVal &= ~PLL_OUTCTRL;
writel(nVal, pll_mode_addr);
/* Put the PLL in warm-up mode */
nVal |= PLL_RESET_N;
nVal |= PLL_BYPASSNL;
writel(nVal, pll_mode_addr);
/* Wait for the PLL warm-up time */
udelay(50);
/* Put the PLL in active mode */
nVal |= PLL_RESET_N;
nVal |= PLL_BYPASSNL;
nVal |= PLL_OUTCTRL;
writel(nVal, pll_mode_addr);
}
void pll_request(unsigned pll, unsigned enable)
{
int val = 0;
if (!enable) {
/* Disable not supported */
return;
}
switch (pll) {
case 2:
pll_enable(PLL2_MODE_ADDR);
return;
case 4:
pll_enable(PLL4_MODE_ADDR);
return;
default:
return;
};
}
void acpu_clock_init(void)
{
uint32_t i, clk;
uint32_t val;
uint32_t *clk_cntl_reg_val, size;
unsigned msm_id, msm_version;
msm_version = board_msm_version();
if (msm_version == 2)
vdd_plevel = 4;
else
vdd_plevel = 6;
/* Set VDD plevel */
writel((1 << 7) | (vdd_plevel << 3), VDD_SVS_PLEVEL_ADDR);
#if (!ENABLE_NANDWRITE)
thread_sleep(1);
#else
mdelay(1);
#endif
msm_id = board_msm_id();
switch (msm_id) {
case MSM7227A:
case MSM7627A:
case ESM7227A:
clk_cntl_reg_val = clk_cntl_reg_val_7627A;
size = ARRAY_SIZE(clk_cntl_reg_val_7627A);
pll_request(2, 1);
/* TODO: Enable this PLL while switching to 800MHz */
#if 0
pll_request(4, 1);
#endif
break;
case MSM8625:
/* Fix me: Will move to PLL4 later */
clk_cntl_reg_val = clk_cntl_reg_val_7627A;
size = ARRAY_SIZE(clk_cntl_reg_val_7627A);
pll_request(2, 1);
break;
case MSM7225A:
case MSM7625A:
default:
clk_cntl_reg_val = clk_cntl_reg_val_7625A;
size = ARRAY_SIZE(clk_cntl_reg_val_7625A);
pll_request(2, 1);
break;
};
/* Read clock source select bit. */
val = readl(A11S_CLK_SEL_ADDR);
i = val & 1;
/* Jump into table and set every entry. */
for (; i < size; i++) {
val = readl(A11S_CLK_SEL_ADDR);
val |= BIT(1) | BIT(2);
writel(val, A11S_CLK_SEL_ADDR);
val = readl(A11S_CLK_CNTL_ADDR);
/* Make sure not to disturb already used src */
val &= clk_cntl_mask[i % 2];
val += clk_cntl_reg_val[i];
writel(val, A11S_CLK_CNTL_ADDR);
/* Would need a dmb() here but the whole address space is
* strongly ordered, so it should be fine.
*/
val = readl(A11S_CLK_SEL_ADDR);
val &= ~(A11S_CLK_SEL_MASK);
val |= (A11S_CLK_SEL_MASK & clk_sel_reg_val[i % 2]);
writel(val, A11S_CLK_SEL_ADDR);
#if (!ENABLE_NANDWRITE)
thread_sleep(1);
#else
mdelay(1);
#endif
}
}
void hsusb_clock_init(void)
{
/* USB local clock control not enabled; use proc comm */
usb_clock_init();
}
/* Configure MMC clock */
void clock_config_mmc(uint32_t interface, uint32_t freq)
{
uint32_t reg = 0;
if (mmc_clock_set_rate(sdc_clk[interface], freq) < 0) {
dprintf(CRITICAL, "Failure setting clock rate for MCLK - "
"clk_rate: %d\n!", freq);
ASSERT(0);
}
/* enable clock */
if (mmc_clock_enable_disable(sdc_clk[interface], MMC_CLK_ENABLE) < 0) {
dprintf(CRITICAL, "Failure enabling MMC Clock!\n");
ASSERT(0);
}
reg |= MMC_BOOT_MCI_CLK_ENABLE;
reg |= MMC_BOOT_MCI_CLK_ENA_FLOW;
reg |= MMC_BOOT_MCI_CLK_IN_FEEDBACK;
writel(reg, MMC_BOOT_MCI_CLK);
/* Wait for the MMC_BOOT_MCI_CLK write to go through. */
mmc_mclk_reg_wr_delay();
/* Wait 1 ms to provide the free running SD CLK to the card. */
mdelay(1);
}
/* Intialize MMC clock */
void clock_init_mmc(uint32_t interface)
{
if (mmc_clock_enable_disable(sdc_pclk[interface], MMC_CLK_ENABLE) < 0) {
dprintf(CRITICAL, "Failure enabling PCLK!\n");
ASSERT(0);
}
}