blob: ba56f996871a02059d7cda5c99f99981fde9499e [file] [log] [blame]
/*
* linux/arch/arm/mach-mmp/clock-mmp3.c
*
* Author: Raul Xiong <xjian@marvell.com>
* Alan Zhu <wzhu10@marvell.com>
* Copyright: (C) 2011 Marvell International Ltd.
*
* based on arch/arm/mach-tegra/tegra2_clocks.c
* Copyright (C) 2010 Google, Inc. by Colin Cross <ccross@google.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/list.h>
#include <linux/spinlock.h>
#include <linux/clk.h>
#include <linux/io.h>
#include <linux/delay.h>
#include <linux/err.h>
#include <mach/mmp3_pm.h>
#include <mach/regs-apbc.h>
#include <mach/regs-apmu.h>
#include <mach/regs-mpmu.h>
#include <mach/cputype.h>
#include <plat/clock.h>
#include <plat/devfreq.h>
#include <linux/reboot.h>
#include "common.h"
#include <linux/pm_qos_params.h>
#define CORE_NUM 3
#define PMUA_CC APMU_REG(0x4)
#define PMUA_CC_2 APMU_REG(0x150)
#define PMUA_CC_3 APMU_REG(0x188)
#define PMUA_BUS APMU_REG(0x6c)
#define PMUA_DM_CC APMU_REG(0xc)
#define PMUA_DM_2_CC APMU_REG(0x158)
#define MMP3_PROTECT_CC(x) (((x) & 0x0003fe3f) | 0x00bc0000)
#define MMP3_PROTECT_CC2(x) ((x) & 0xfffffe07)
#define MMP3_PROTECT_CC3(x) ((x) & 0x0effff1f)
#define MMP3_PROTECT_BUSCLKRST(x) ((x) & 0x000001c3)
#define MMP3_PROTECT_FCCR(x) ((x) & 0xff83ffff)
#define CONFIG_MMP3_QSEVEN_26MHZ
/* Dynamic Frequency Change Part */
enum {
MMP3_FREQ_OP_GET = 0,
MMP3_FREQ_OP_UPDATE = 1,
MMP3_FREQ_OP_SHOW = 2,
MMP3_FREQ_PH_D_BY_4 = 1,
MMP3_FREQ_PH_D_BY_6 = 2,
MMP3_FREQ_PH_D_BY_8 = 3,
MMP3_FREQ_PH_D_BY_10 = 4,
MMP3_FREQ_PH_D_BY_12 = 5,
MMP3_FREQ_PH_D_BY_14 = 6,
MMP3_FREQ_PH_D_BY_15 = 7,
MMP3_FREQCH_VOLTING = (1u << 27),
MMP3_FREQCH_CORE = (1u << 24),
MMP3_FREQCH_DRAM = (9u << 22), /* both channel */
MMP3_FREQCH_AXI = (1u << 26),
};
static struct clk mmp3_clk_pll1_d_2 = {
.name = "pll1_d_2",
.rate = 400000000,
.ops = NULL,
};
static struct clk mmp3_clk_pll1 = {
.name = "pll1",
.rate = 800000000,
.ops = NULL,
};
static int mmp3_clk_pll2_enable(struct clk *clk)
{
u32 value;
/* disable PLL2 */
value = __raw_readl(MPMU_PLL2CR);
if (value & (1 << 8))
return 0; /* already enabled */
value &= ~(1 << 8);
__raw_writel(value, MPMU_PLL2CR);
/* select VCO as clock source */
value = __raw_readl(MPMU_PLL2_CTRL2);
value |= 1 << 0;
__raw_writel(value, MPMU_PLL2_CTRL2);
/* Program PLL2 for 1200Mhz, (VCO 2.4G)*/
__raw_writel(0x05390699, MPMU_PLL2_CTRL1);
__raw_writel(0x001C5200, MPMU_PLL2CR);
/* enable PLL2 */
value = __raw_readl(MPMU_PLL2CR);
value |= 1 << 8;
__raw_writel(value, MPMU_PLL2CR);
udelay(500);
/* take PLL2 out of reset */
value = __raw_readl(MPMU_PLL2_CTRL1);
value |= 1 << 29;
__raw_writel(value, MPMU_PLL2_CTRL1);
udelay(500);
return 0;
}
static void mmp3_clk_pll2_disable(struct clk *clk)
{
u32 value;
/* PLL2 Control register, disable SW PLL2 */
value = __raw_readl(MPMU_PLL2CR);
value &= ~(1 << 8);
__raw_writel(value, MPMU_PLL2CR);
/* wait for PLLs to lock */
udelay(500);
/* MPMU_PLL2_CTRL1: put PLL2 into reset */
value = __raw_readl(MPMU_PLL2_CTRL1);
value = ~(1 << 29);
__raw_writel(value, MPMU_PLL2_CTRL1);
udelay(500);
}
static struct clkops mmp3_clk_pll2_ops = {
.enable = mmp3_clk_pll2_enable,
.disable = mmp3_clk_pll2_disable,
};
static struct clk mmp3_clk_pll2 = {
.name = "pll2",
.rate = 1200000000,
.ops = &mmp3_clk_pll2_ops,
};
static int mmp3_clk_pll1_clkoutp_enable(struct clk *clk)
{
u32 value = __raw_readl(PMUM_PLL_DIFF_CTRL);
/* Set PLL1 CLKOUTP post VCO divider as 1.5 */
value &= ~(0xf << 0);
value |= 1 << 0;
value |= 1 << 4;
__raw_writel(value, PMUM_PLL_DIFF_CTRL);
return 0;
}
static void mmp3_clk_pll1_clkoutp_disable(struct clk *clk)
{
u32 value = __raw_readl(PMUM_PLL_DIFF_CTRL);
__raw_writel(value & ~(1 << 4), PMUM_PLL_DIFF_CTRL);
}
static struct clkops mmp3_clk_pll1_clkoutp_ops = {
.enable = mmp3_clk_pll1_clkoutp_enable,
.disable = mmp3_clk_pll1_clkoutp_disable,
};
/*
* NOTE: actually pll1_clkoutp and pll1 share the same clock source
* pll1_VCO, which is 1600MHz. And pll1 has a post didiver 2, pll1_clkoutp
* has a post didiver 1.5. Since we don't expose pll1_VCO as a visible
* clock, here we use pll1 as its parent to workaround. So we have to
* set the div as 3 and mul as 4.
*/
static struct clk mmp3_clk_pll1_clkoutp = {
.name = "pll1_clkoutp",
.rate = 1066000000,
.parent = &mmp3_clk_pll1,
.mul = 4,
.div = 3,
.ops = &mmp3_clk_pll1_clkoutp_ops,
};
static int mmp3_clk_pll2_clkoutp_enable(struct clk *clk)
{
u32 value = __raw_readl(PMUM_PLL_DIFF_CTRL);
/* Set PLL2 CLKOUTP post VCO divider as 2.5 */
value &= ~(0xf << 5);
value |= 3u << 5;
value |= 1u << 9;
__raw_writel(value, PMUM_PLL_DIFF_CTRL);
return 0;
}
static void mmp3_clk_pll2_clkoutp_disable(struct clk *clk)
{
u32 value = __raw_readl(PMUM_PLL_DIFF_CTRL);
__raw_writel(value & ~(1 << 9), PMUM_PLL_DIFF_CTRL);
}
static struct clkops mmp3_clk_pll2_clkoutp_ops = {
.enable = mmp3_clk_pll2_clkoutp_enable,
.disable = mmp3_clk_pll2_clkoutp_disable,
};
/*
* NOTE: pll2_clkoutp and pll2 both divided from PLL2 VCO, which is 2.4G
*.in current configuration. And pll2 has a post didiver 2, pll2_clkoutp
* has a post didiver 2.5. Since we don't expose pll2_VCO as a visible
* clock, here we use pll2 as its parent to workaround. So we have to
* set the div as 5 and mul as 4.
*/
static struct clk mmp3_clk_pll2_clkoutp = {
.name = "pll2_clkoutp",
.rate = 960000000,
.parent = &mmp3_clk_pll2,
.mul = 4,
.div = 5,
.ops = &mmp3_clk_pll2_clkoutp_ops,
};
static struct clk mmp3_clk_vctcxo = {
.name = "vctcxo",
.rate = 26000000,
.ops = NULL,
};
static struct clk mmp3_clk_32k = {
.name = "32k",
.rate = 32768,
.ops = NULL,
};
static struct clk mmp3_clk_pll1_d_4 = {
.name = "pll1_d_4",
.rate = 200000000,
.ops = NULL,
};
static int mmp3_clk_pll3_enable_parallel(struct clk *clk)
{
unsigned int pll3_ctrl1, tmp, pll3_cr, calculated_pclk, CLK_INT_DIV;
unsigned long parent_rate, div_result;
unsigned int FBDIV;
unsigned int REFDIV = 0x3;
unsigned int VCODIV_SEL_SE = 2;
unsigned int KVCO;
unsigned int VCO_VRNG;
unsigned int rate = clk->rate;
tmp = (__raw_readl(APMU_FSIC3_CLK_RES_CTRL) >> 8) & 0xF;
if (tmp == 0xD)
parent_rate = clk_get_rate(&mmp3_clk_vctcxo);
else
parent_rate = 25000000;
VCODIV_SEL_SE = 0x2;
pr_info("Desired clk_rate in Hz = %lu\n", rate);
/* PLL3 control register 1 - program ICP = 4 */
pll3_ctrl1 = 0x01010099;
if ((rate >= 120000000UL) && (rate <= 240000000UL)) {
rate = rate * 10;
CLK_INT_DIV = 5;
} else if ((rate > 60000000UL) && (rate < 120000000UL)) {
rate = rate * 20;
CLK_INT_DIV = 10;
} else if ((rate > 30000000UL) && (rate <= 60000000UL)) {
rate = rate * 40;
CLK_INT_DIV = 20;
} else {
rate = rate * 60;
CLK_INT_DIV = 30;
}
if (rate > 1200000000UL && rate <= 1360000000UL)
KVCO = 0x1;
else if (rate > 1360000000UL && rate <= 1530000000UL)
KVCO = 0x2;
else if (rate > 1530000000UL && rate <= 1700000000UL)
KVCO = 0x3;
else if (rate > 1700000000UL && rate <= 1900000000UL)
KVCO = 0x4;
else if (rate > 1900000000UL && rate <= 2100000000UL)
KVCO = 0x5;
else if (rate > 2100000000UL && rate <= 2300000000UL)
KVCO = 0x6;
else if (rate > 2300000000UL && rate <= 2400000000UL)
KVCO = 0x7;
else
KVCO = 0x6;
if (KVCO >= 0x1 && KVCO <= 0x6)
VCO_VRNG = KVCO - 1;
else
VCO_VRNG = 0x5;
div_result = rate / parent_rate * REFDIV;
FBDIV = div_result & 0x1ff;
/* Make sure calculated PCLK is higher than target PCLK */
calculated_pclk = (parent_rate/REFDIV*FBDIV/VCODIV_SEL_SE/CLK_INT_DIV);
pr_info("Inside %s\n", __func__);
pr_info("Calculated pclk from pll3 will be %u\n", calculated_pclk);
pr_info("REFDIV=%d\n", REFDIV);
pr_info("FBDIV=%d\n", FBDIV);
pr_info("CLK_INT_DIV=%d\n", CLK_INT_DIV);
pll3_cr = (REFDIV << 19) | (FBDIV << 10) | (0x1 << 9);
pll3_ctrl1 |= (VCODIV_SEL_SE << 25 | KVCO << 19 | VCO_VRNG << 8);
/* Disable PLL3 */
tmp = __raw_readl(PMUM_PLL3_CR);
__raw_writel(tmp & ~(0x00000100), PMUM_PLL3_CR);
tmp = __raw_readl(PMUM_PLL3_CTRL2);
/* set SEL_VCO_CLK_SE in PMUM_PLL3_CTRL2 register */
__raw_writel(tmp | 0x00000001, PMUM_PLL3_CTRL2);
/* PLL3 control register 1 ICP = 4.*/
__raw_writel(pll3_ctrl1, PMUM_PLL3_CTRL1);
/* MPMU_PLL3CR: Program PLL3 VCO */
__raw_writel(pll3_cr, PMUM_PLL3_CR);
/* PLL3 Control register -Enable SW PLL3 */
tmp = __raw_readl(PMUM_PLL3_CR);
__raw_writel(tmp | 0x00000100, PMUM_PLL3_CR);
/* wait for PLLs to lock */
udelay(500);
/* PMUM_PLL3_CTRL1: take PLL3 out of reset */
tmp = __raw_readl(PMUM_PLL3_CTRL1);
__raw_writel(tmp | 0x20000000, PMUM_PLL3_CTRL1);
udelay(500);
/*clk->div = CLK_INT_DIV;*/
return 0;
}
static int mmp3_clk_pll3_enable(struct clk *clk)
{
#if defined(CONFIG_MACH_QSEVEN)
pr_info("calling our customized mmp3_pll3 enable function\n");
mmp3_clk_pll3_enable_parallel(clk);
return 0;
#else
u64 div_result;
u32 tmp, pll3_ctrl1, pll3_cr, FBDIV;
u8 VCODIV_SEL_SE, KVCO, VCO_VRNG, REFDIV = 0x3;
unsigned long parent_rate, rate = clk->rate;
tmp = (__raw_readl(APMU_FSIC3_CLK_RES_CTRL) >> 8) & 0xF;
if (tmp == 0xD)
parent_rate = clk_get_rate(&mmp3_clk_vctcxo);
else {
pr_err("Fatal error: pll3 has error clock input config\n");
BUG_ON(1);
}
/* PLL3 control register 1 - program ICP = 4 */
pll3_ctrl1 = 0x01010099;
/* 1.2GHz ~2.4GHz */
if ((rate >= 1200000000UL) && (rate <= 2400000000UL))
VCODIV_SEL_SE = 0x0 ;
else if (rate >= 800000000UL) { /* 800MHz */
rate /= 2;
VCODIV_SEL_SE = 0x1;
} else if (rate >= 600000000UL) /* 600MHz */
VCODIV_SEL_SE = 0x2;
else if (rate >= 480000000UL) { /* 480MHz */
rate /= 2;
VCODIV_SEL_SE = 0x3;
} else if (rate >= 400000000UL) /* 400MHz */
VCODIV_SEL_SE = 0x4;
else if (rate >= 300000000UL) /* 300MHz */
VCODIV_SEL_SE = 0x5;
else if (rate >= 240000000UL) /* 240MHz */
VCODIV_SEL_SE = 0x6;
else if (rate >= 200000000UL) /* 200MHz */
VCODIV_SEL_SE = 0x7;
else if (rate >= 150000000UL) /* 150MHz */
VCODIV_SEL_SE = 0x8;
else {
pr_err("Fatal error: pll3 config out of range\n");
BUG_ON(1);
}
/* rate = VCO/post_div, post_div = clk->div / REFDIV / (?2:1)*/
rate *= (clk->div / REFDIV);
if (rate > 1200000000UL && rate <= 1360000000UL)
KVCO = 0x1;
else if (rate > 1360000000UL && rate <= 1530000000UL)
KVCO = 0x2;
else if (rate > 1530000000UL && rate <= 1700000000UL)
KVCO = 0x3;
else if (rate > 1700000000UL && rate <= 1900000000UL)
KVCO = 0x4;
else if (rate > 1900000000UL && rate <= 2100000000UL)
KVCO = 0x5;
else if (rate > 2100000000UL && rate <= 2300000000UL)
KVCO = 0x6;
else if (rate > 2300000000UL && rate <= 2400000000UL)
KVCO = 0x7;
else
KVCO = 0x6;
if (KVCO >= 0x1 && KVCO <= 0x6)
VCO_VRNG = KVCO - 1;
else
VCO_VRNG = 0x5;
pll3_ctrl1 |= (VCODIV_SEL_SE << 25 | KVCO << 19 | VCO_VRNG << 8);
div_result = (u64)rate * REFDIV;
do_div(div_result, parent_rate);
FBDIV = div_result & 0x1ff;
pll3_cr = (REFDIV << 19) | (FBDIV << 10) | (0x1 << 9);
tmp = __raw_readl(PMUM_PLL3_CTRL2);
/* set SEL_VCO_CLK_SE in PMUM_PLL3_CTRL2 register */
__raw_writel(tmp | 0x00000001, PMUM_PLL3_CTRL2);
/* PLL3 control register 1 ICP = 4.*/
__raw_writel(pll3_ctrl1, PMUM_PLL3_CTRL1);
/* MPMU_PLL3CR: Program PLL3 VCO */
__raw_writel(pll3_cr, PMUM_PLL3_CR);
/* PLL3 Control register -Enable SW PLL3 */
tmp = __raw_readl(PMUM_PLL3_CR);
__raw_writel(tmp | 0x00000100, PMUM_PLL3_CR);
/* wait for PLLs to lock */
udelay(500);
/* PMUM_PLL3_CTRL1: take PLL3 out of reset */
tmp = __raw_readl(PMUM_PLL3_CTRL1);
__raw_writel(tmp | 0x20000000, PMUM_PLL3_CTRL1);
udelay(500);
return 0;
#endif
}
static void mmp3_clk_pll3_disable(struct clk *clk)
{
u32 tmp = __raw_readl(PMUM_PLL3_CR);
/* PLL3 Control register, disable SW PLL3 */
__raw_writel(tmp & ~0x00000100, PMUM_PLL3_CR);
/* wait for PLLs to lock */
udelay(500);
/* PMUM_PLL3_CTRL1: put PLL3 into reset */
tmp = __raw_readl(PMUM_PLL3_CTRL1);
__raw_writel(tmp & ~0x20000000, PMUM_PLL3_CTRL1);
udelay(500);
}
static long mmp3_clk_pll3_round_rate(struct clk *clk, unsigned long rate)
{
#if defined(CONFIG_MACH_QSEVEN)
pr_info("Returning rate %u from function %s\n", rate, __func__);
pr_info("We will not round rate or do anything here...\n");
return rate;
#endif
unsigned int fb_div, post_div, ref_div = 0x3;
unsigned long parent_rate, rate_rounded;
u64 div_result;
u32 tmp = (__raw_readl(APMU_FSIC3_CLK_RES_CTRL) >> 8) & 0xF;
if (tmp == 0xD)
parent_rate = clk_get_rate(&mmp3_clk_vctcxo);
else {
pr_err("Fatal error: pll3 has error clock input config");
BUG_ON(1);
}
/* 1.2GHz ~2.4GHz */
if (rate >= 1200000000UL && rate <= 2400000000UL)
post_div = 1;
else if (rate >= 800000000UL) /* 800MHz */
post_div = 3; /* post divider actually is 1.5; */
else if (rate >= 600000000UL) /* 600MHz */
post_div = 2;
else if (rate >= 480000000UL) /* 480MHz */
post_div = 5; /* post divider actually is 2.5; */
else if (rate >= 400000000UL) /* 400MHz */
post_div = 3;
else if (rate >= 300000000UL) /* 300MHz */
post_div = 4;
else if (rate >= 240000000UL) /* 240MHz */
post_div = 5;
else if (rate >= 200000000UL) /* 200MHz */
post_div = 6;
else if (rate >= 150000000UL) /* 150MHz */
post_div = 8;
else {
pr_err("Fatal error: pll3 config out of range: %lu\n", rate);
BUG_ON(1);
}
div_result = ((u64)rate) * ref_div * post_div;
div_result += parent_rate / 2; /* to round the FBDIV */
do_div(div_result, parent_rate);
fb_div = div_result;
div_result = ((u64)parent_rate) * fb_div;
do_div(div_result, (ref_div * post_div));
rate_rounded = div_result;
return rate_rounded;
}
static int mmp3_clk_pll3_setrate(struct clk *clk, unsigned long rate)
{
#if defined(CONFIG_MACH_QSEVEN)
pr_info("Setting pixclk rate as a pll3 desired rate in its clock...\n");
clk->rate = rate;
return 0;
#endif
unsigned long parent_rate;
u64 div_result;
unsigned int ref_div = 0x3;
clk->div = 0;
parent_rate = clk_get_rate(&mmp3_clk_vctcxo);
if (rate >= 1200000000UL && rate <= 2400000000UL) /* 1.2GHz ~2.4GHz */
clk->div = 1;
else if (rate >= 800000000UL) /* 800MHz */
clk->div = 3;
else if (rate >= 600000000UL) /* 600MHz */
clk->div = 2;
else if (rate >= 480000000UL) /* 480MHz */
clk->div = 5;
else if (rate >= 400000000UL) /* 400MHz */
clk->div = 3;
else if (rate >= 300000000UL) /* 300MHz */
clk->div = 4;
else if (rate >= 240000000UL) /* 240MHz */
clk->div = 5;
else if (rate >= 200000000UL) /* 200MHz */
clk->div = 6;
else if (rate >= 150000000UL) /* 150MHz */
clk->div = 8;
else {
pr_err("Fatal error: pll3 config out of range\n");
BUG_ON(1);
}
clk->div *= ref_div;
div_result = ((u64)rate) * clk->div;
div_result += parent_rate / 2; /* to round the mul */
do_div(div_result, parent_rate);
clk->mul = div_result;
return 0;
}
static struct clk_mux_sel mux_pll1_pll2_vctcxo[] = {
{.input = &mmp3_clk_pll1_d_2, .value = 0},
{.input = &mmp3_clk_pll1, .value = 1},
{.input = &mmp3_clk_pll2, .value = 2},
{.input = &mmp3_clk_pll1_clkoutp, .value = 3},
{.input = &mmp3_clk_vctcxo, .value = 4},
{0, 0},
};
static void mmp3_core_clk_trigger_change(void)
{
u32 val;
val = __raw_readl(PMUA_CC);
val = MMP3_PROTECT_CC(val); /* set reserved */
val = val | MMP3_FREQCH_CORE | MMP3_FREQCH_VOLTING;
/* A0 need to all cores run, we use coherent broadcasts */
dsb();
/* trigger change */
__raw_writel(val, PMUA_CC);
/* done, PJ_RD_STATUS should have been cleared by HW */
}
static void mmp3_clk_source_init(struct clk *c)
{
u32 val, source;
const struct clk_mux_sel *sel;
c->dynamic_change = 1;
c->mul = 1;
c->div = 1;
val = __raw_readl(c->reg_data[SOURCE][STATUS].reg);
source = (val >> c->reg_data[SOURCE][STATUS].reg_shift)
& c->reg_data[SOURCE][STATUS].reg_mask;
for (sel = c->inputs; sel->input != NULL; sel++) {
if (sel->value == source)
break;
}
BUG_ON(sel->input == NULL);
c->parent = sel->input;
}
static int mmp3_clk_set_parent(struct clk *c, struct clk *p)
{
u32 val;
const struct clk_mux_sel *sel;
val = __raw_readl(c->reg_data[SOURCE][CONTROL].reg);
for (sel = c->inputs; sel->input != NULL; sel++) {
if (sel->input == p) {
if (c->reg_data[SOURCE][CONTROL].reg == MPMU_FCCR)
val = MMP3_PROTECT_FCCR(val);
else if (c->reg_data[SOURCE][CONTROL].reg == PMUA_BUS)
val = MMP3_PROTECT_BUSCLKRST(val);
val &= ~(c->reg_data[SOURCE][CONTROL].reg_mask
<< c->reg_data[SOURCE][CONTROL].reg_shift);
val |= sel->value
<< c->reg_data[SOURCE][CONTROL].reg_shift;
__raw_writel(val, c->reg_data[SOURCE][CONTROL].reg);
/*
* FIXME: DO NOT trigger here since
* we will triggered the changes together later
*/
/* mmp3_core_clk_trigger_change(); */
clk_reparent(c, p);
return 0;
}
}
return -EINVAL;
}
static struct clkops mmp3_clk_root_ops = {
.init = mmp3_clk_source_init,
.set_parent = mmp3_clk_set_parent,
};
static struct clk mmp3_clk_core_root = {
.name = "core_root",
.inputs = mux_pll1_pll2_vctcxo,
.ops = &mmp3_clk_root_ops,
.reg_data = {
{ {MPMU_FCCR, 29, 0x7}, {MPMU_FCCR, 29, 0x7} },
{ {0, 0, 0}, {0, 0, 0} } },
};
static void mmp3_clk_div_init(struct clk *c)
{
u32 val;
c->dynamic_change = 1;
c->mul = 1;
val = __raw_readl(c->reg_data[DIV][STATUS].reg);
c->div = ((val >> c->reg_data[DIV][STATUS].reg_shift)
& c->reg_data[DIV][STATUS].reg_mask) + 1;
}
static int mmp3_clk_set_rate(struct clk *c, unsigned long rate)
{
int i;
int max_div = c->reg_data[DIV][CONTROL].reg_mask + 1;
u32 val = __raw_readl(c->reg_data[DIV][CONTROL].reg);
unsigned long parent_rate = clk_get_rate(c->parent);
for (i = 1; i <= max_div; i++) {
if (rate == parent_rate / i) {
if (c->reg_data[DIV][CONTROL].reg == PMUA_CC)
val = MMP3_PROTECT_CC(val);
else if (c->reg_data[DIV][CONTROL].reg == PMUA_CC_2)
val = MMP3_PROTECT_CC2(val);
else if (c->reg_data[DIV][CONTROL].reg == PMUA_CC_3)
val = MMP3_PROTECT_CC3(val);
val &= ~(c->reg_data[DIV][CONTROL].reg_mask
<< c->reg_data[DIV][CONTROL].reg_shift);
val |= (i - 1) << c->reg_data[DIV][CONTROL].reg_shift;
__raw_writel(val, c->reg_data[DIV][CONTROL].reg);
/*
* FIXME: DO NOT trigger here since
* we will triggered the changes together later
*/
/* mmp3_core_clk_trigger_change(); */
c->div = i;
c->mul = 1;
return 0;
}
}
return -EINVAL;
}
static struct clkops mmp3_clk_div_ops = {
.init = mmp3_clk_div_init,
.setrate = mmp3_clk_set_rate,
};
static struct clk mmp3_clk_virtual_pj = {
.name = "pj",
.parent = &mmp3_clk_core_root,
.ops = &mmp3_clk_div_ops,
.reg_data = {
{ {0, 0, 0}, {0, 0, 0} },
{ {PMUA_DM_CC, 0, 0x7}, {PMUA_CC, 0, 0x7} } },
};
static struct clk mmp3_clk_mp1 = {
.name = "mp1",
.parent = &mmp3_clk_virtual_pj,
.ops = &mmp3_clk_div_ops,
.reg_data = {
{ {0, 0, 0}, {0, 0, 0} },
{ {PMUA_DM_2_CC, 9, 0xf}, {PMUA_CC_2, 9, 0xf} } },
};
static struct clk mmp3_clk_mp2 = {
.name = "mp2",
.parent = &mmp3_clk_virtual_pj,
.ops = &mmp3_clk_div_ops,
.reg_data = {
{ {0, 0, 0}, {0, 0, 0} },
{ {PMUA_DM_2_CC, 13, 0xf}, {PMUA_CC_2, 13, 0xf} } },
};
static struct clk mmp3_clk_mm = {
.name = "mm",
.parent = &mmp3_clk_virtual_pj,
.ops = &mmp3_clk_div_ops,
.reg_data = {
{ {0, 0, 0}, {0, 0, 0} },
{ {PMUA_DM_2_CC, 17, 0xf}, {PMUA_CC_2, 17, 0xf} } },
};
static struct clk mmp3_clk_aclk = {
.name = "aclk",
.parent = &mmp3_clk_virtual_pj,
.ops = &mmp3_clk_div_ops,
.reg_data = {
{ {0, 0, 0}, {0, 0, 0} },
{ {PMUA_DM_2_CC, 21, 0xf}, {PMUA_CC_2, 21, 0xf} } },
};
static void mmp3_core_periph_init(struct clk *c)
{
u32 val, div_val;
c->dynamic_change = 1;
c->mul = 1;
val = __raw_readl(c->reg_data[DIV][STATUS].reg);
div_val = (val >> c->reg_data[DIV][STATUS].reg_shift)
& c->reg_data[DIV][STATUS].reg_mask;
if (div_val != 7)
c->div = (div_val + 1) * 2;
else
c->div = 15;
}
static int mmp3_core_periph_set_rate(struct clk *c, unsigned long rate)
{
int i;
int max_div = 15;
u32 val = __raw_readl(c->reg_data[DIV][CONTROL].reg);
unsigned long parent_rate = clk_get_rate(c->parent);
for (i = 4; i < max_div; i += 2) {
if (rate == parent_rate / i) {
val = MMP3_PROTECT_CC(val);
val &= ~(c->reg_data[DIV][CONTROL].reg_mask
<< c->reg_data[DIV][CONTROL].reg_shift);
val |= (i / 2 - 1)
<< c->reg_data[DIV][CONTROL].reg_shift;
__raw_writel(val, c->reg_data[DIV][CONTROL].reg);
/*
* FIXME: DO NOT trigger here since
* we will triggered the changes together later
*/
/* mmp3_core_clk_trigger_change(); */
c->div = i;
c->mul = 1;
return 0;
}
}
if (rate == parent_rate / max_div) {
val = MMP3_PROTECT_CC(val);
val &= ~(c->reg_data[DIV][CONTROL].reg_mask
<< c->reg_data[DIV][CONTROL].reg_shift);
val |= 7 << c->reg_data[DIV][CONTROL].reg_shift;
__raw_writel(val, c->reg_data[DIV][CONTROL].reg);
/*
* FIXME: DO NOT trigger here since
* we will triggered the changes together later
*/
/* mmp3_core_clk_trigger_change(); */
c->div = max_div;
c->mul = 1;
return 0;
}
return -EINVAL;
}
static struct clkops mmp3_core_periph_ops = {
.init = mmp3_core_periph_init,
.setrate = mmp3_core_periph_set_rate,
};
static struct clk mmp3_clk_core_periph = {
.name = "periph",
.parent = &mmp3_clk_core_root,
.ops = &mmp3_core_periph_ops,
.reg_data = {
{ {0, 0, 0}, {0, 0, 0} },
{ {PMUA_DM_2_CC, 25, 0x7}, {PMUA_CC, 9, 0x7} } },
};
static struct clk mmp3_clk_atclk = {
.name = "atclk",
.parent = &mmp3_clk_core_root,
.ops = &mmp3_clk_div_ops,
.reg_data = {
{ {0, 0, 0}, {0, 0, 0} },
{ {PMUA_DM_CC, 3, 0x7}, {PMUA_CC, 3, 0x7} } },
};
static struct {
struct clk *source_clk;
unsigned long pj_rate;
unsigned long mp1_rate;
unsigned long mp2_rate;
unsigned long mm_rate;
unsigned long aclk_rate;
unsigned long ph_rate;
unsigned long atclk_rate;
} mmp3_op_table[] = {
{
&mmp3_clk_pll1_d_2, 200000000, 200000000,
200000000, 200000000, 200000000, 100000000, 200000000}, {
&mmp3_clk_pll1, 400000000, 400000000,
400000000, 400000000, 400000000, 200000000, 400000000}, {
&mmp3_clk_pll1, 800000000, 800000000,
800000000, 400000000, 400000000, 200000000, 400000000}, {
&mmp3_clk_pll1_clkoutp, 1062000000, 1062000000,
1062000000, 531000000, 531000000, 177000000, 354000000}, {
/* must be the last line! */
NULL, 0, 0, 0, 0, 0, 0, 0} };
static void mmp3_cpu_clk_init(struct clk *c)
{
c->dynamic_change = 1;
c->mul = 1;
c->div = 1;
}
static int mmp3_cpu_clk_set_rate(struct clk *c, unsigned long rate)
{
int ret, index, i;
for (i = 0; mmp3_op_table[i].source_clk != NULL; i++) {
if (mmp3_op_table[i].mp1_rate >= rate) {
index = i;
break;
}
}
if (mmp3_op_table[i].source_clk == NULL)
return -EINVAL;
/* obtain FC onwership, should always pass */
i = 1000;
while ((__raw_readl(PMUA_DM_CC) & (1u << 24)) != 0) {
i--;
if (i <= 0) {
pr_err("Cannot gain owner of PMU DFC\n");
return -EAGAIN;
}
}
ret = clk_set_parent(&mmp3_clk_core_root,
mmp3_op_table[index].source_clk);
if (ret)
return ret;
ret = clk_set_rate(&mmp3_clk_virtual_pj, mmp3_op_table[index].pj_rate);
if (ret)
return ret;
ret = clk_set_rate(&mmp3_clk_mp1, mmp3_op_table[index].mp1_rate);
if (ret)
return ret;
ret = clk_set_rate(&mmp3_clk_mp2, mmp3_op_table[index].mp2_rate);
if (ret)
return ret;
ret = clk_set_rate(&mmp3_clk_mm, mmp3_op_table[index].mm_rate);
if (ret)
return ret;
ret = clk_set_rate(&mmp3_clk_aclk, mmp3_op_table[index].aclk_rate);
if (ret)
return ret;
ret = clk_set_rate(&mmp3_clk_core_periph, mmp3_op_table[index].ph_rate);
if (ret)
return ret;
ret = clk_set_rate(&mmp3_clk_atclk, mmp3_op_table[index].atclk_rate);
if (ret)
return ret;
mmp3_core_clk_trigger_change();
return 0;
}
static struct clkops mmp3_cpu_ops = {
.init = mmp3_cpu_clk_init,
.setrate = mmp3_cpu_clk_set_rate,
};
static struct clk mmp3_clk_virtual_cpu __maybe_unused = {
.name = "cpu",
.parent = &mmp3_clk_virtual_pj,
.ops = &mmp3_cpu_ops,
};
/* ddr clock definitions */
static struct clk mmp3_clk_ddr_root = {
.name = "ddr_root",
.inputs = mux_pll1_pll2_vctcxo,
.ops = &mmp3_clk_root_ops,
.reg_data = {
{ {MPMU_FCCR, 23, 0x7}, {MPMU_FCCR, 23, 0x7} },
{ {0, 0, 0}, {0, 0, 0} } },
};
static struct clk mmp3_clk_ddr1 = {
.name = "ddr1",
.parent = &mmp3_clk_ddr_root,
.ops = &mmp3_clk_div_ops,
.reg_data = {
{ {0, 0, 0}, {0, 0, 0} },
{ {PMUA_DM_CC, 12, 0x7}, {PMUA_CC, 12, 0x7} } },
};
static struct clk mmp3_clk_ddr2 = {
.name = "ddr2",
.parent = &mmp3_clk_ddr_root,
.ops = &mmp3_clk_div_ops,
.reg_data = {
{ {0, 0, 0}, {0, 0, 0} },
{ {PMUA_DM_CC, 9, 0x7}, {PMUA_CC_3, 17, 0x7} } },
};
/* axi clock definitions */
static struct clk mmp3_clk_axi_root = {
.name = "axi_root",
.inputs = mux_pll1_pll2_vctcxo,
.ops = &mmp3_clk_root_ops,
.reg_data = {
{ {PMUA_BUS, 6, 0x7}, {PMUA_BUS, 6, 0x7} },
{ {0, 0, 0}, {0, 0, 0} } },
};
static struct clk mmp3_clk_axi1 = {
.name = "axi1",
.parent = &mmp3_clk_axi_root,
.ops = &mmp3_clk_div_ops,
.reg_data = {
{ {0, 0, 0}, {0, 0, 0} },
{ {PMUA_DM_CC, 15, 0x7}, {PMUA_CC, 15, 0x7} } },
};
static struct clk mmp3_clk_axi2 = {
.name = "axi2",
.parent = &mmp3_clk_axi_root,
.ops = &mmp3_clk_div_ops,
.reg_data = {
{ {0, 0, 0}, {0, 0, 0} },
{ {PMUA_DM_2_CC, 0, 0x7}, {PMUA_CC_2, 0, 0x7} } },
};
static int clk_cpu_setrate(struct clk *clk, unsigned long val)
{
/*
* FIXME this need change when smp process id to real process id
* mapping is ready, then we can do frequency table for each core
* currently the mapping depends on which core to boot.
* Also currently we assume only MP1/2 are active and they always runs
* at the same frequency. so we only use and trigger DFC target on MP1
*/
mmp3_setfreq(MMP3_CLK_MP1, val);
return 0;
}
static unsigned long clk_cpu_getrate(struct clk *clk)
{
return mmp3_getfreq(MMP3_CLK_MP1);
}
static struct clkops clk_cpu_ops = {
.setrate = clk_cpu_setrate,
.getrate = clk_cpu_getrate,
};
static struct clk mmp3_clk_cpu = {
.name = "cpu",
.ops = &clk_cpu_ops,
.dynamic_change = 1,
};
#ifdef CONFIG_DDR_DEVFREQ
extern struct devfreq_frequency_table *mmp3_ddr_freq_table;
static unsigned int target_freq = 800000;
static unsigned int cur_freq = 800000;
static atomic_t ddr_dfc_disabled = ATOMIC_INIT(0);
static atomic_t dfc_trigger = ATOMIC_INIT(0);
static struct mutex disable_ddr_lock;
DECLARE_COMPLETION(vsync_complete);
extern atomic_t mmp3_fb_is_suspended;
int wakeup_ddr_fc_seq(void)
{
if (atomic_read(&dfc_trigger)) {
if (cur_freq != target_freq) {
if (!atomic_read(&ddr_dfc_disabled))
mmp3_setfreq(MMP3_CLK_DDR_1, target_freq);
cur_freq = mmp3_getfreq(MMP3_CLK_DDR_1);
}
complete(&vsync_complete);
}
return 0;
}
void disable_ddr_devfreq(int disable)
{
mutex_lock(&disable_ddr_lock);
if (disable == 1)
atomic_add(1, &ddr_dfc_disabled);
/* disable must be called first */
else if (readl(&ddr_dfc_disabled) > 0)
atomic_sub(1, &ddr_dfc_disabled);
mutex_unlock(&disable_ddr_lock);
}
static int clk_ddr_setrate(struct clk *clk, unsigned long val)
{
int i;
val /= 1000;
for (i = 0; mmp3_ddr_freq_table[i+1].frequency != DEVFREQ_TABLE_END;
i++)
if (mmp3_ddr_freq_table[i].frequency >= val) break;
target_freq = mmp3_ddr_freq_table[i].frequency;
atomic_set(&dfc_trigger, 1);
if (atomic_read(&mmp3_fb_is_suspended))
wakeup_ddr_fc_seq();
wait_for_completion_timeout(&vsync_complete, msecs_to_jiffies(50));
atomic_set(&dfc_trigger, 0);
return 0;
}
static unsigned long clk_ddr_getrate(struct clk *clk)
{
return mmp3_getfreq(MMP3_CLK_DDR_1) * 1000;
}
static struct clkops clk_ddr_ops = {
.setrate = clk_ddr_setrate,
.getrate = clk_ddr_getrate,
};
static struct clk mmp3_clk_ddr = {
.name = "ddr",
.ops = &clk_ddr_ops,
.dynamic_change = 1,
};
#endif
static struct devfreq_frequency_table mmp3_gc_clk_table[] = {
INIT_FREQ_TABLE(1, 100000000),
INIT_FREQ_TABLE(2, 200000000),
INIT_FREQ_TABLE(3, 355000000),
INIT_FREQ_TABLE(4, 400000000),
INIT_FREQ_TABLE(5, 533000000),
INIT_FREQ_TABLE(6, DEVFREQ_TABLE_END),
};
static int devfreq_reboot_notifier_call(struct notifier_block *this,
unsigned long code, void *_cmd)
{
#ifdef CONFIG_DDR_DEVFREQ
disable_ddr_devfreq(1);
#endif
return NOTIFY_DONE;
}
static struct notifier_block devfreq_reboot_notifier = {
.notifier_call = devfreq_reboot_notifier_call,
};
#define GC2D_CLK_DIV(n) ((n & 0xF) << 28)
#define GC2D_CLK_DIV_MSK GC2D_CLK_DIV(0xF)
#define GC2D_CLK_SRC_SEL(n) ((n & 3) << 12)
#define GC2D_CLK_SRC_SEL_MSK GC2D_CLK_SRC_SEL(3)
#define GC2D_AXICLK_EN (1u << 19)
#define GC3D_CLK_DIV(n) ((n & 0xF) << 24)
#define GC3D_CLK_DIV_MSK GC3D_CLK_DIV(0xF)
#define GC3D_CLK_SRC_SEL(n) ((n & 3) << 6)
#define GC3D_CLK_SRC_SEL_MSK GC3D_CLK_SRC_SEL(3)
#define GC3D_ACLK_SEL(n) ((n & 3) << 4)
#define GC3D_ACLK_SEL_MSK GC3D_ACLK_SEL(3)
#define GC3D_AXICLK_EN (1u << 2)
#define GC2D3D_CLK_EN (1u << 3)
#define GC2D_CLK_EN (1u << 20)
#define GC_PWRUP(n) ((n & 3) << 9)
#define GC_PWRUP_MSK GC_PWRUP(3)
#define GC_ISB (1u << 8)
#define GC_CLK_RATE(div, src, aclk) \
(GC2D_CLK_DIV(div) | GC2D_CLK_SRC_SEL(src) \
| GC3D_CLK_DIV(div) | GC3D_CLK_SRC_SEL(src) \
| GC3D_ACLK_SEL(aclk))
#define GC_CLK_RATE_MSK \
(GC2D_CLK_DIV_MSK | GC2D_CLK_SRC_SEL_MSK \
| GC3D_CLK_DIV_MSK | GC3D_CLK_SRC_SEL_MSK \
| GC3D_ACLK_SEL_MSK)
#define CS_PLL1 0
#define CS_PLL2 1
#define CS_PLL1_COP 2
#define CS_PLL2_COP 3
#define PLL1D4 0
#define PLL1D6 1
#define PLL1D2 2
#define PLL2D2 3
#define GC_SET_BITS(set, clear) {\
unsigned long tmp;\
tmp = __raw_readl(clk->clk_rst);\
tmp &= ~(clear);\
tmp |= set;\
__raw_writel(tmp, clk->clk_rst);\
}
#ifdef CONFIG_DDR_DEVFREQ
struct pm_qos_request_list gc_qos_ddrfreq_min;
#endif
static bool gc_force_high_rate = false;
static struct delayed_work gc_idle_work;
static void gc_set_constraint(struct work_struct *work)
{
gc_force_high_rate = true;
#ifdef CONFIG_DDR_DEVFREQ
pm_qos_update_request(&gc_qos_ddrfreq_min, PM_QOS_DEFAULT_VALUE);
#endif
}
static void gc_clk_init(struct clk *clk)
{
clk->rate = clk_get_rate(&mmp3_clk_pll1_clkoutp) / 2;
clk->enable_val = PLL1D2; /* reuse this field for the ACLK setting */
clk->div = 2;
clk->mul = 1;
clk_reparent(clk, &mmp3_clk_pll1_clkoutp);
/*
init_timer(&gc_idle_timer);
gc_idle_timer.function = gc_set_constraint;
gc_idle_timer.data = 1;
gc_idle_timer.expires = 1000 + jiffies;
*/
INIT_DELAYED_WORK(&gc_idle_work, gc_set_constraint);
gc_force_high_rate = true;
#ifdef CONFIG_DDR_DEVFREQ
pm_qos_add_request(&gc_qos_ddrfreq_min, PM_QOS_DDR_DEVFREQ_MIN,
PM_QOS_DEFAULT_VALUE);
#endif
}
static int gc_clk_setrate(struct clk *clk, unsigned long rate)
{
unsigned long rate1 = rate;
if (gc_force_high_rate == true)
rate1 = 533333333;
clk->mul = 1;
if (rate1 == clk_get_rate(&mmp3_clk_pll1)/8) {
clk->enable_val = PLL1D6;
clk->div = 8;
clk_reparent(clk, &mmp3_clk_pll1);
} else if (rate1 == clk_get_rate(&mmp3_clk_pll1)/4) {
clk->enable_val = PLL1D4;
clk->div = 4;
clk_reparent(clk, &mmp3_clk_pll1);
} else if (rate1 == clk_get_rate(&mmp3_clk_pll1_clkoutp)/3) {
clk->enable_val = PLL1D2;
clk->div = 3;
clk_reparent(clk, &mmp3_clk_pll1_clkoutp);
} else if (rate1 == clk_get_rate(&mmp3_clk_pll1)/2) {
clk->enable_val = PLL1D2;
clk->div = 2;
clk_reparent(clk, &mmp3_clk_pll1);
} else if (rate1 == clk_get_rate(&mmp3_clk_pll2_clkoutp)/2) {
clk->enable_val = PLL1D2;
clk->div = 2;
clk_reparent(clk, &mmp3_clk_pll2_clkoutp);
} else if (rate1 == clk_get_rate(&mmp3_clk_pll1_clkoutp)/2) {
clk->enable_val = PLL1D2;
clk->div = 2;
clk_reparent(clk, &mmp3_clk_pll1_clkoutp);
} else if (rate1 == clk_get_rate(&mmp3_clk_pll2)/2) {
clk->enable_val = PLL2D2;
clk->div = 2;
clk_reparent(clk, &mmp3_clk_pll2);
} else if (rate1 == clk_get_rate(&mmp3_clk_pll1)) {
clk->enable_val = PLL1D2;
clk->div = 1;
clk_reparent(clk, &mmp3_clk_pll1);
} else {
pr_err("%s: unexpected gc clock rate %ld\n", __func__, rate1);
BUG();
}
return 0;
}
static int gc_clk_enable(struct clk *clk)
{
unsigned long gc_rate_cfg;
unsigned long i;
/* TODO may need to request for different core voltage according to
* different gc clock rate.
*/
cancel_delayed_work_sync(&gc_idle_work);
#ifdef CONFIG_DDR_DEVFREQ
pm_qos_update_request(&gc_qos_ddrfreq_min, DDR_CONSTRAINT_LVL1);
#endif
if (gc_force_high_rate == true)
gc_clk_setrate(clk, 533333333);
gc_force_high_rate = false;
i = 0;
while ((clk->inputs[i].input != clk->parent) && clk->inputs[i].input)
i++;
if (clk->inputs[i].input == 0) {
pr_err("%s: unexpected gc clock source\n", __func__);
return -1;
}
gc_rate_cfg = GC_CLK_RATE(clk->div,
clk->inputs[i].value, clk->enable_val);
gc_rate_cfg &= GC_CLK_RATE_MSK;
GC_SET_BITS(gc_rate_cfg, GC_CLK_RATE_MSK);
GC_SET_BITS(GC2D_CLK_EN | GC2D3D_CLK_EN | GC2D_AXICLK_EN\
| GC3D_AXICLK_EN, 0);
return 0;
}
static void gc_clk_disable(struct clk *clk)
{
GC_SET_BITS(0, GC2D_AXICLK_EN | GC3D_AXICLK_EN | GC2D3D_CLK_EN\
| GC2D_CLK_EN);
schedule_delayed_work(&gc_idle_work, 1000);
/*
+ GC_SET_BITS(0,GC2D3D_CLK_EN | GC2D_CLK_EN);
+ GC_SET_BITS(0, GC2D_AXICLK_EN | GC3D_AXICLK_EN);
*/
}
static long gc_clk_round_rate(struct clk *clk, unsigned long rate)
{
if (rate <= clk_get_rate(&mmp3_clk_pll1)/8)
return clk_get_rate(&mmp3_clk_pll1)/8; /* 100M */
else if (rate <= clk_get_rate(&mmp3_clk_pll1)/4)
return clk_get_rate(&mmp3_clk_pll1)/4; /* 200M */
else if (rate <= clk_get_rate(&mmp3_clk_pll1_clkoutp)/3)
return clk_get_rate(&mmp3_clk_pll1_clkoutp)/3; /* 354M */
else if (rate <= clk_get_rate(&mmp3_clk_pll1)/2)
return clk_get_rate(&mmp3_clk_pll1)/2; /* 400M */
else if (rate <= clk_get_rate(&mmp3_clk_pll2_clkoutp)/2)
return clk_get_rate(&mmp3_clk_pll2_clkoutp)/2; /* 480M */
else if (rate <= clk_get_rate(&mmp3_clk_pll1_clkoutp)/2)
return clk_get_rate(&mmp3_clk_pll1_clkoutp)/2; /* 531M */
else if (rate <= clk_get_rate(&mmp3_clk_pll2)/2)
return clk_get_rate(&mmp3_clk_pll2)/2; /* 600M */
else
return clk_get_rate(&mmp3_clk_pll1); /* 800M */
}
/*
static int gc_clk_setrate(struct clk *clk, unsigned long rate)
{
clk->mul = 1;
if (rate == clk_get_rate(&mmp3_clk_pll1)/8) {
clk->enable_val = PLL1D6;
clk->div = 8;
clk_reparent(clk, &mmp3_clk_pll1);
} else if (rate == clk_get_rate(&mmp3_clk_pll1)/4) {
clk->enable_val = PLL1D4;
clk->div = 4;
clk_reparent(clk, &mmp3_clk_pll1);
} else if (rate == clk_get_rate(&mmp3_clk_pll1_clkoutp)/3) {
clk->enable_val = PLL1D2;
clk->div = 3;
clk_reparent(clk, &mmp3_clk_pll1_clkoutp);
} else if (rate == clk_get_rate(&mmp3_clk_pll1)/2) {
clk->enable_val = PLL1D2;
clk->div = 2;
clk_reparent(clk, &mmp3_clk_pll1);
} else if (rate == clk_get_rate(&mmp3_clk_pll2_clkoutp)/2) {
clk->enable_val = PLL1D2;
clk->div = 2;
clk_reparent(clk, &mmp3_clk_pll2_clkoutp);
} else if (rate == clk_get_rate(&mmp3_clk_pll1_clkoutp)/2) {
clk->enable_val = PLL1D2;
clk->div = 2;
clk_reparent(clk, &mmp3_clk_pll1_clkoutp);
} else if (rate == clk_get_rate(&mmp3_clk_pll2)/2) {
clk->enable_val = PLL2D2;
clk->div = 2;
clk_reparent(clk, &mmp3_clk_pll2);
} else if (rate == clk_get_rate(&mmp3_clk_pll1)) {
clk->enable_val = PLL1D2;
clk->div = 1;
clk_reparent(clk, &mmp3_clk_pll1);
} else {
pr_err("%s: unexpected gc clock rate %ld\n", __func__, rate);
BUG();
}
return 0;
}
*/
int get_gcu_freqs_table(unsigned long *gcu_freqs_table, unsigned int *item_counts,
unsigned int max_item_counts)
{
int i;
if (max_item_counts < ARRAY_SIZE(mmp3_gc_clk_table)) {
pr_err("Too many GC frequencies!\n");
return -1;
}
for (i = 0; (mmp3_gc_clk_table[i].frequency != DEVFREQ_TABLE_END); i++)
gcu_freqs_table[i] = mmp3_gc_clk_table[i].frequency;
*item_counts = i;
return 0;
}
EXPORT_SYMBOL(get_gcu_freqs_table);
struct clkops gc_clk_ops = {
.init = gc_clk_init,
.enable = gc_clk_enable,
.disable = gc_clk_disable,
.setrate = gc_clk_setrate,
.round_rate = gc_clk_round_rate,
.set_parent = mmp3_clk_set_parent,
};
static struct clk_mux_sel gc_mux_pll1_pll2[] = {
{.input = &mmp3_clk_pll1, .value = 0},
{.input = &mmp3_clk_pll2, .value = 1},
{.input = &mmp3_clk_pll1_clkoutp, .value = 2},
{.input = &mmp3_clk_pll2_clkoutp, .value = 3},
{0, 0},
};
static struct clk mmp3_clk_gc = {
.name = "gc",
.inputs = gc_mux_pll1_pll2,
.lookup = {
.con_id = "GCCLK",
},
.clk_rst = (void __iomem *)APMU_GC,
.ops = &gc_clk_ops,
};
static int disp1_axi_clk_enable(struct clk *clk)
{
u32 val = __raw_readl(clk->clk_rst);
/* enable Display1 AXI clock */
val |= (1<<3);
__raw_writel(val, clk->clk_rst);
/* release from reset */
val |= 1 | (1 << 1);
__raw_writel(val, clk->clk_rst);
return 0;
}
static void disp1_axi_clk_disable(struct clk *clk)
{
u32 val = __raw_readl(clk->clk_rst);
/*
* disable display1 AXI clock:
* In MMP3 A0, display1 AXI clock enable and reset
* are connected opposite at the bus clock module,
* so on A0, bit[0]: control axi clock enable/disable;
* bit[3]: axi clock reset control. From B0, will fix back.
*/
if (cpu_is_mmp3_a0())
val &= ~1;
else
val &= ~(1<<3);
__raw_writel(val, clk->clk_rst);
}
struct clkops disp1_axi_clk_ops = {
.enable = disp1_axi_clk_enable,
.disable = disp1_axi_clk_disable,
};
static struct clk mmp3_clk_disp1_axi = {
.name = "disp1_axi",
.lookup = {
.con_id = "DISP1AXI",
},
.clk_rst = (void __iomem *)APMU_LCD_CLK_RES_CTRL,
.ops = &disp1_axi_clk_ops,
};
static int disp1_clk_enable(struct clk *clk)
{
u32 val;
val = __raw_readl(clk->reg_data[SOURCE][CONTROL].reg);
/* enable Display1 peripheral clock */
val |= (1 << 4);
__raw_writel(val, clk->reg_data[SOURCE][CONTROL].reg);
/* release from reset */
val |= (1 << 1);
__raw_writel(val, clk->reg_data[SOURCE][CONTROL].reg);
return 0;
}
static void disp1_clk_disable(struct clk *clk)
{
u32 val;
val = __raw_readl(clk->reg_data[SOURCE][CONTROL].reg);
/* disable Display1 peripheral clock */
val &= ~(1<<4);
__raw_writel(val, clk->reg_data[SOURCE][CONTROL].reg);
}
static long disp1_clk_round_rate(struct clk *clk, unsigned long rate)
{
/*
* disp1 clock actually has four clock source: pll1, pll1/16, pll2
* and vctcxo.The range of the divider is 1 to 15.
* here the policy is try to not use pll2 as possile as it can.
* since the divider can be as large as 15 so it don't need pll1/16
* as the clock source. For the very low rate requirement, we can
* just use vctcxo as the clock source.
*/
int i;
unsigned long parent_rate;
/* for those which is less than 26M, use vctcxo as clock source */
if (rate <= clk_get_rate(&mmp3_clk_vctcxo)) {
parent_rate = clk_get_rate(&mmp3_clk_vctcxo);
for (i = 2; i < 16; i++) {
if (rate > parent_rate / i)
break;
}
return parent_rate / (i - 1);
/* for those which is less than 800M, use pll1 as clock source */
} else if (rate <= clk_get_rate(&mmp3_clk_pll1)) {
parent_rate = clk_get_rate(&mmp3_clk_pll1);
for (i = 2; i < 16; i++) {
if (rate > parent_rate / i)
break;
}
return parent_rate / (i - 1);
/* for those which is larger than 800M, use pll2 as clock source */
} else
return clk_get_rate(&mmp3_clk_pll2);
}
static int disp1_clk_setrate(struct clk *clk, unsigned long rate)
{
unsigned long parent_rate;
const struct clk_mux_sel *sel;
u32 val = __raw_readl(clk->reg_data[SOURCE][CONTROL].reg);
if (rate <= clk_get_rate(&mmp3_clk_vctcxo)) {
parent_rate = clk_get_rate(&mmp3_clk_vctcxo);
clk->mul = 1;
clk->div = parent_rate / rate;
clk_reparent(clk, &mmp3_clk_vctcxo);
val &= ~(clk->reg_data[DIV][CONTROL].reg_mask
<< clk->reg_data[DIV][CONTROL].reg_shift);
val |= clk->div
<< clk->reg_data[DIV][CONTROL].reg_shift;
for (sel = clk->inputs; sel->input != 0; sel++) {
if (sel->input == &mmp3_clk_vctcxo)
break;
}
if (sel->input == 0) {
pr_err("lcd: no matched input for this parent!\n");
BUG();
}
val &= ~(clk->reg_data[SOURCE][CONTROL].reg_mask
<< clk->reg_data[SOURCE][CONTROL].reg_shift);
val |= sel->value
<< clk->reg_data[SOURCE][CONTROL].reg_shift;
} else if (rate <= clk_get_rate(&mmp3_clk_pll1)) {
parent_rate = clk_get_rate(&mmp3_clk_pll1);
clk->mul = 1;
clk->div = parent_rate / rate;
clk_reparent(clk, &mmp3_clk_pll1);
val &= ~(clk->reg_data[DIV][CONTROL].reg_mask
<< clk->reg_data[DIV][CONTROL].reg_shift);
val |= clk->div
<< clk->reg_data[DIV][CONTROL].reg_shift;
for (sel = clk->inputs; sel->input != 0; sel++) {
if (sel->input == &mmp3_clk_pll1)
break;
}
if (sel->input == 0) {
pr_err("lcd: no matched input for this parent!\n");
BUG();
}
val &= ~(clk->reg_data[SOURCE][CONTROL].reg_mask
<< clk->reg_data[SOURCE][CONTROL].reg_shift);
val |= sel->value
<< clk->reg_data[SOURCE][CONTROL].reg_shift;
} else if (rate <= clk_get_rate(&mmp3_clk_pll2)) {
parent_rate = clk_get_rate(&mmp3_clk_pll2);
clk->mul = 1;
clk->div = parent_rate / rate;
clk_reparent(clk, &mmp3_clk_pll2);
val &= ~(clk->reg_data[DIV][CONTROL].reg_mask
<< clk->reg_data[DIV][CONTROL].reg_shift);
val |= clk->div
<< clk->reg_data[DIV][CONTROL].reg_shift;
for (sel = clk->inputs; sel->input != 0; sel++) {
if (sel->input == &mmp3_clk_pll2)
break;
}
if (sel->input == 0) {
pr_err("lcd: no matched input for this parent!\n");
BUG();
}
val &= ~(clk->reg_data[SOURCE][CONTROL].reg_mask
<< clk->reg_data[SOURCE][CONTROL].reg_shift);
val |= sel->value
<< clk->reg_data[SOURCE][CONTROL].reg_shift;
}
__raw_writel(val, clk->reg_data[SOURCE][CONTROL].reg);
return 0;
}
struct clkops disp1_clk_ops = {
.enable = disp1_clk_enable,
.disable = disp1_clk_disable,
.round_rate = disp1_clk_round_rate,
.setrate = disp1_clk_setrate,
};
static struct clk_mux_sel disp1_clk_mux[] = {
{.input = &mmp3_clk_pll1, .value = 0},
{.input = &mmp3_clk_pll2, .value = 2},
{.input = &mmp3_clk_vctcxo, .value = 3},
{0, 0},
};
/* Disp1 clock can be one of the source for lcd */
static struct clk mmp3_clk_disp1 = {
.name = "disp1",
.lookup = {
.con_id = "DISP1_CLK",
},
.ops = &disp1_clk_ops,
.inputs = disp1_clk_mux,
.reg_data = {
{ {APMU_LCD_CLK_RES_CTRL, 6, 0x3},
{APMU_LCD_CLK_RES_CTRL, 6, 0x3} },
{{APMU_LCD_CLK_RES_CTRL, 8, 0xf},
{APMU_LCD_CLK_RES_CTRL, 8, 0xf} } },
};
static struct clk *disp_depend_clk[] = {
&mmp3_clk_disp1_axi,
&mmp3_clk_disp1,
};
#define LCD_PN1_DSI_PHYSLOW_PRER 0x1A
#define LCD_PN1_DSI_PHYSLOW_PRER_SHIFT 15
#define LCD_PN1_DSI_PHYSLOW_PRER_MASK 0x1F
static int lcd_pn1_clk_enable(struct clk *clk)
{
u32 val = __raw_readl(clk->reg_data[SOURCE][CONTROL].reg);
printk(KERN_INFO"***Frank %s/%d clk=%p clk->name=%s\n", __func__, __LINE__, clk, clk->name);
if (clk->parent == &mmp3_clk_vctcxo) {
#ifndef CONFIG_MACH_QSEVEN
/* enable DSI PHY ESC/SLOW clock */
val |= (1 << 12) | (1 << 5);
__raw_writel(val, clk->reg_data[SOURCE][CONTROL].reg);
/* use fixed prescaler for DSI PHY slow clock */
val &= ~(LCD_PN1_DSI_PHYSLOW_PRER_MASK <<
LCD_PN1_DSI_PHYSLOW_PRER_SHIFT);
val |= (LCD_PN1_DSI_PHYSLOW_PRER <<
LCD_PN1_DSI_PHYSLOW_PRER_SHIFT);
__raw_writel(val, clk->reg_data[SOURCE][CONTROL].reg);
/* release DSI PHY SLOW clock from reset */
val |= (1 << 2);
__raw_writel(val, clk->reg_data[SOURCE][CONTROL].reg);
/*
* Here we fix the lcd clock souce as DSI PLL(PLL3). Because it
* provides a more flexible clocking options for the DSI interface
* which has very peculiar frequency requirements driven by the
* display panel parameters. So enable the pll3 clk here.
*/
mmp3_clk_pll3_enable(clk);
#else
pr_info("We do not need to touch APMU_LCD, just enable pll3\n");
mmp3_clk_pll3_enable(clk);
#endif
} else if (clk->parent == &mmp3_clk_pll1) {
/* select PLL1 as clock source and disable unused clocks */
val &= ~((3 << 6) | (1 << 12) | (1 << 5) | (1 << 2));
/* divider select 1 */
val &= ~(3 << 8);
val |= 1 << 8;
__raw_writel(val, clk->reg_data[SOURCE][CONTROL].reg);
} else if (clk->parent == &mmp3_clk_pll2) {
/* select PLL2 as clock source and disable unused clocks */
val &= ~((3 << 6) | (1 << 12) | (1 << 5) | (1 << 2));
/* divider select 1 */
val &= ~(3 << 8);
val |= 1 << 8;
val |= 2 << 6;
__raw_writel(val, clk->reg_data[SOURCE][CONTROL].reg);
} else {
pr_err("%s clk->parent not inited\n", __func__);
return -EAGAIN;
}
/* release from reset */
val |= (1 << 1);
__raw_writel(val, clk->reg_data[SOURCE][CONTROL].reg);
pr_info("%s PMUA_DISPLAY1 = 0x%x, clk from %s", __func__, val,
(clk->parent == &mmp3_clk_pll2) ? "pll2" :
(clk->parent == &mmp3_clk_pll1) ? "pll1" : "pll3");
return 0;
}
static void lcd_pn1_clk_disable(struct clk *clk)
{
u32 val = __raw_readl(clk->reg_data[SOURCE][CONTROL].reg);
printk(KERN_INFO"***Frank %s/%d clk=%p clk->name=%s\n", __func__, __LINE__, clk, clk->name);
if (clk->parent == &mmp3_clk_vctcxo) {
/* lcd clock source is fixed as DSI PLL(PLL3), so disable pll3 clk */
mmp3_clk_pll3_disable(clk);
/* disable DSI clock */
val &= ~((1<<12) | (1<<5));
__raw_writel(val, clk->reg_data[SOURCE][CONTROL].reg);
}
pr_info("%s PMUA_DISPLAY1 = 0x%x, clk from %s", __func__, val,
(clk->parent == &mmp3_clk_pll2) ? "pll2" :
(clk->parent == &mmp3_clk_pll1) ? "pll1" : "pll3");
}
static long lcd_clk_round_rate(struct clk *clk, unsigned long rate)
{
switch (rate) {
case 800000000:
clk_reparent(clk, &mmp3_clk_pll1);
break;
case 1200000000:
clk_reparent(clk, &mmp3_clk_pll2);
break;
default:
clk_reparent(clk, &mmp3_clk_vctcxo);
if (clk->dependence_count > 1)
/* remove depend clk disp1 */
clk->dependence_count--;
rate = mmp3_clk_pll3_round_rate(clk, rate);
break;
}
pr_debug("%s line %d rate %lu\n\n", __func__, __LINE__, rate);
return rate;
}
static int lcd_clk_setrate(struct clk *clk, unsigned long rate)
{
int ret = 0;
printk(KERN_INFO"***Frank %s/%d clk=%p clk->name=%s\n", __func__, __LINE__, clk, clk->name);
switch (rate) {
case 800000000:
break;
case 1200000000:
break;
default:
/* lcd clock source is fixed as DSI PLL(PLL3), so set pll3 clk rate */
ret = mmp3_clk_pll3_setrate(clk, rate);
break;
}
pr_debug("%s line %d rate %lu\n\n", __func__, __LINE__, rate);
return ret;
}
struct clkops lcd_pn1_clk_ops = {
.enable = lcd_pn1_clk_enable,
.disable = lcd_pn1_clk_disable,
.round_rate = lcd_clk_round_rate,
.setrate = lcd_clk_setrate,
};
static struct clk_mux_sel lcd_pn1_clk_mux[] = {
{.input = &mmp3_clk_vctcxo, 0},
{0, 0},
};
/*
* lcd clk actually has five clock souce: axi, display 1,
* display 2, HDMI PLL and DSI PLL(PLL3). Here we just fix it to be
* PLL3 since it provides a more flexible clocking options for the
* DSI interface which has very peculiar frequency requirements driven
* by the display panel parameters. And PLL3 satisfy the LCD/DSI very well.
*/
static struct clk mmp3_clk_lcd1 = {
.name = "lcd1",
.lookup = {
.con_id = "LCDCLK",
},
.ops = &lcd_pn1_clk_ops,
.dependence = disp_depend_clk,
.dependence_count = ARRAY_SIZE(disp_depend_clk),
.inputs = lcd_pn1_clk_mux,
.reg_data = {
{{APMU_LCD_CLK_RES_CTRL, 6, 0x3},
{APMU_LCD_CLK_RES_CTRL, 6, 0x3} },
{{APMU_LCD_CLK_RES_CTRL, 8, 0xf},
{APMU_LCD_CLK_RES_CTRL, 8, 0xf} } },
};
static int hdmi_clk_enable(struct clk *clk)
{
/*
* hdmi pll clock enable is done by user space,
* to control it's dependence clock disp1_axi
* here we just enable hdmi ref clock
*/
u32 val = __raw_readl(clk->clk_rst);
val |= (1 << 13);
__raw_writel(val, clk->clk_rst);
return 0;
};
static void hdmi_clk_disable(struct clk *clk)
{
/*
* hdmi pll clock disable is done by user space,
* to control it's dependence clock disp1_axi
* here we just enable hdmi ref clock
*/
u32 val = __raw_readl(clk->clk_rst);
val &= ~(1 << 13);
__raw_writel(val, clk->clk_rst);
return;
};
struct clkops hdmi_clk_ops = {
.enable = hdmi_clk_enable,
.disable = hdmi_clk_disable,
};
static struct clk mmp3_clk_hdmi = {
.name = "hdmi",
.lookup = {
.con_id = "HDMICLK",
},
.clk_rst = (void __iomem *)APMU_LCD_CLK_RES_CTRL,
.ops = &hdmi_clk_ops,
.dependence = disp_depend_clk,
.dependence_count = ARRAY_SIZE(disp_depend_clk),
};
static int thermal_clk_enable(struct clk *clk)
{
uint32_t clk_rst, i;
ulong inc[4] = {0, 0x8, 0xc, 0x10};
ulong base;
for (i = 0; i < 4; i ++) {
base = (ulong)clk->clk_rst + inc[i];
clk_rst = __raw_readl(base);
clk_rst |= APBC_FNCLK | APBC_FNCLKSEL(0x0);
__raw_writel(clk_rst, base);
/*
* delay two cycles of the solwest clock between the APB
* bus clock and the functional module clock.
*/
udelay(10);
clk_rst |= APBC_APBCLK;
__raw_writel(clk_rst, base);
udelay(10);
clk_rst &= ~(APBC_RST);
__raw_writel(clk_rst, base);
}
return 0;
}
static void thermal_clk_disable(struct clk *clk)
{
int inc[4] = {0, 0x8, 0xc, 0x10}, i;
for (i = 0; i < 4; i ++)
__raw_writel(0, clk->clk_rst + inc[i]);
mdelay(1);
}
struct clkops thermal_clk_ops = {
.enable = thermal_clk_enable,
.disable = thermal_clk_disable,
};
static struct clk mmp3_clk_thermal = {
.name = "mmp-thermal",
.lookup = {
.con_id = "THERMALCLK",
},
.clk_rst = (void __iomem *)APBC_MMP2_THSENS1,
.ops = &thermal_clk_ops,
};
static void sdhc_clk_init(struct clk *clk)
{
const struct clk_mux_sel *sel;
u32 val = 0;
clk->mul = 1;
clk->div = 2;
clk_reparent(clk, &mmp3_clk_pll1_d_4);
if (!strcmp(clk->name, "sdh0")) {
val &= ~(clk->reg_data[DIV][CONTROL].reg_mask
<< clk->reg_data[DIV][CONTROL].reg_shift);
val |= clk->div
<< clk->reg_data[DIV][CONTROL].reg_shift;
for (sel = clk->inputs; sel->input != 0; sel++) {
if (sel->input == &mmp3_clk_pll1_d_4)
break;
}
if (sel->input == 0) {
pr_err("sdh: no matched input for this parent!\n");
BUG();
}
val &= ~(clk->reg_data[SOURCE][CONTROL].reg_mask
<< clk->reg_data[SOURCE][CONTROL].reg_shift);
val |= sel->value
<< clk->reg_data[SOURCE][CONTROL].reg_shift;
}
clk->enable_val = val;
}
static int sdhc_clk_enable(struct clk *clk)
{
u32 val;
val = clk->enable_val | 0x1b;
__raw_writel(val, clk->clk_rst);
return 0;
}
static void sdhc_clk_disable(struct clk *clk)
{
__raw_writel(0, clk->clk_rst);
}
static long sdhc_clk_round_rate(struct clk *clk, unsigned long rate)
{
/*
* SDH has four clock source: PLL1, PLL1/2, PLL1/4 and PLL2.
* Here PLL1/2 is not used since PLL1 and PLL1/4 can cover it.
* Only use PLL2 as clock source if the rate is larger than PLL1.
*/
int i;
unsigned long parent_rate;
/* for those which is less than pll1/4, use pll1/4 as clock source */
if (rate <= clk_get_rate(&mmp3_clk_pll1_d_4)) {
parent_rate = clk_get_rate(&mmp3_clk_pll1_d_4);
for (i = 2; i < 16; i++) {
if (rate > parent_rate / i)
break;
}
return parent_rate / (i - 1);
/* else for those which is less than pll1, use pll1 as clock source */
} else if (rate <= clk_get_rate(&mmp3_clk_pll1)) {
parent_rate = clk_get_rate(&mmp3_clk_pll1);
for (i = 2; i < 16; i++) {
if (rate > parent_rate / i)
break;
}
return parent_rate / (i - 1);
/* else for those which is larger than pll1, use pll2 as clock source */
} else {
parent_rate = clk_get_rate(&mmp3_clk_pll2);
for (i = 2; i < 16; i++) {
if (rate > parent_rate / i)
break;
}
}
return parent_rate / (i - 1);
}
static int sdhc_clk_setrate(struct clk *clk, unsigned long rate)
{
unsigned long parent_rate;
const struct clk_mux_sel *sel;
u32 val = 0;
if (strcmp(clk->name, "sdh0"))
return -ENOSYS;
if (rate <= clk_get_rate(&mmp3_clk_pll1_d_4)) {
parent_rate = clk_get_rate(&mmp3_clk_pll1_d_4);
clk->mul = 1;
clk->div = parent_rate / rate;
clk_reparent(clk, &mmp3_clk_pll1_d_4);
val &= ~(clk->reg_data[DIV][CONTROL].reg_mask
<< clk->reg_data[DIV][CONTROL].reg_shift);
val |= clk->div
<< clk->reg_data[DIV][CONTROL].reg_shift;
for (sel = clk->inputs; sel->input != 0; sel++) {
if (sel->input == &mmp3_clk_pll1_d_4)
break;
}
if (sel->input == 0) {
pr_err("sdh: no matched input for this parent!\n");
BUG();
}
val &= ~(clk->reg_data[SOURCE][CONTROL].reg_mask
<< clk->reg_data[SOURCE][CONTROL].reg_shift);
val |= sel->value
<< clk->reg_data[SOURCE][CONTROL].reg_shift;
} else if (rate <= clk_get_rate(&mmp3_clk_pll1)) {
parent_rate = clk_get_rate(&mmp3_clk_pll1);
clk->mul = 1;
clk->div = parent_rate / rate;
clk_reparent(clk, &mmp3_clk_pll1);
val &= ~(clk->reg_data[DIV][CONTROL].reg_mask
<< clk->reg_data[DIV][CONTROL].reg_shift);
val |= clk->div
<< clk->reg_data[DIV][CONTROL].reg_shift;
for (sel = clk->inputs; sel->input != 0; sel++) {
if (sel->input == &mmp3_clk_pll1)
break;
}
if (sel->input == 0) {
pr_err("sdh: no matched input for this parent!\n");
BUG();
}
val &= ~(clk->reg_data[SOURCE][CONTROL].reg_mask
<< clk->reg_data[SOURCE][CONTROL].reg_shift);
val |= sel->value
<< clk->reg_data[SOURCE][CONTROL].reg_shift;
} else if (rate <= clk_get_rate(&mmp3_clk_pll2)) {
parent_rate = clk_get_rate(&mmp3_clk_pll2);
clk->mul = 1;
clk->div = parent_rate / rate;
clk_reparent(clk, &mmp3_clk_pll2);
val &= ~(clk->reg_data[DIV][CONTROL].reg_mask
<< clk->reg_data[DIV][CONTROL].reg_shift);
val |= clk->div
<< clk->reg_data[DIV][CONTROL].reg_shift;
for (sel = clk->inputs; sel->input != 0; sel++) {
if (sel->input == &mmp3_clk_pll2)
break;
}
if (sel->input == 0) {
pr_err("sdh: no matched input for this parent!\n");
BUG();
}
val &= ~(clk->reg_data[SOURCE][CONTROL].reg_mask
<< clk->reg_data[SOURCE][CONTROL].reg_shift);
val |= sel->value
<< clk->reg_data[SOURCE][CONTROL].reg_shift;
}
clk->enable_val = val;
return 0;
}
struct clkops sdhc0_clk_ops = {
.init = sdhc_clk_init,
.enable = sdhc_clk_enable,
.disable = sdhc_clk_disable,
.round_rate = sdhc_clk_round_rate,
.setrate = sdhc_clk_setrate,
};
struct clkops sdhc1_4_clk_ops = {
.init = sdhc_clk_init,
.enable = sdhc_clk_enable,
.disable = sdhc_clk_disable,
.round_rate = sdhc_clk_round_rate,
};
static struct clk_mux_sel sdhc_clk_mux[] = {
{.input = &mmp3_clk_pll1_d_4, .value = 0},
{.input = &mmp3_clk_pll2, .value = 1},
{.input = &mmp3_clk_pll1, .value = 3},
{0, 0},
};
/*
* all sdhx share the same clock which will be enabled when any of sdhx clock
* enable is set. but the clock source select and devider ratio is controlled
* by sdh0.
*/
static struct clk mmp3_clk_sdh0 = {
.name = "sdh0",
.lookup = {
.dev_id = "sdhci-pxa.0",
.con_id = "PXA-SDHCLK",
},
.ops = &sdhc0_clk_ops,
.inputs = sdhc_clk_mux,
.reg_data = {
{ {APMU_SDH0, 8, 0x3},
{APMU_SDH0, 8, 0x3} },
{{APMU_SDH0, 10, 0xf},
{APMU_SDH0, 10, 0xf} } },
.clk_rst = (void __iomem *)APMU_SDH0,
};
static struct clk mmp3_clk_sdh1 = {
.name = "sdh1",
.lookup = {
.dev_id = "sdhci-pxa.1",
.con_id = "PXA-SDHCLK",
},
.ops = &sdhc1_4_clk_ops,
.inputs = sdhc_clk_mux,
.clk_rst = (void __iomem *)APMU_SDH1,
};
static struct clk mmp3_clk_sdh2 = {
.name = "sdh2",
.lookup = {
.dev_id = "sdhci-pxa.2",
.con_id = "PXA-SDHCLK",
},
.ops = &sdhc1_4_clk_ops,
.inputs = sdhc_clk_mux,
.clk_rst = (void __iomem *)APMU_SDH2,
};
static struct clk mmp3_clk_sdh3 = {
.name = "sdh3",
.lookup = {
.dev_id = "sdhci-pxa.3",
.con_id = "PXA-SDHCLK",
},
.ops = &sdhc1_4_clk_ops,
.inputs = sdhc_clk_mux,
.clk_rst = (void __iomem *)APMU_SDH3,
};
#ifdef CONFIG_MMP3_HSI
#define HSI_PLL1_DIV_2 0
#define HSI_PLL1 1
#define HSI_PLL2 2
static void hsi_clk_init(struct clk *clk)
{
/* HSI Clock default value is PLL1/2: 400MHz */
clk->rate = clk_get_rate(&mmp3_clk_pll1)/2; /* 400MHz */
clk->enable_val = HSI_PLL1;
clk->div = 2;
clk->mul = 1;
clk_reparent(clk, &mmp3_clk_pll1);
}
static int hsi_clk_enable(struct clk *clk)
{
int reg;
clk_reparent(clk, clk->inputs[clk->enable_val].input);
/* Configure HSI Controller Clock */
reg = readl(MPMU_HSI_CLK_RES_CTRL);
reg &= ~0x3F;
reg |= (clk->enable_val);
reg |= (clk->div) << 2;
writel(reg, MPMU_HSI_CLK_RES_CTRL);
/* Release HSI Controller Reset */
reg = readl(MPMU_HSI_CLK_RES_CTRL);
reg |= 0x1 << 7;
reg |= 0x1 << 6;
writel(reg, MPMU_HSI_CLK_RES_CTRL);
/* enable APMU HSI bus and release HSI reset */
reg = readl(APMU_BUS);
reg |= 3 << 14;
writel(reg, APMU_BUS);
mdelay(10);
return 0;
}
static void hsi_clk_disable(struct clk *clk)
{
int reg;
/* Reset HSI Controller */
reg = readl(MPMU_HSI_CLK_RES_CTRL);
reg &= ~(0x1 << 7);
reg &= ~(0x1 << 6);
writel(reg, MPMU_HSI_CLK_RES_CTRL);
}
static long hsi_clk_round_rate(struct clk *clk, unsigned long rate)
{
if (rate <= clk_get_rate(&mmp3_clk_pll1)/4)
return clk_get_rate(&mmp3_clk_pll1)/4; /* 200M */
else
return clk_get_rate(&mmp3_clk_pll1)/2; /* 400M */
}
static int hsi_clk_setrate(struct clk *clk, unsigned long rate)
{
clk->mul = 1;
if (rate == clk_get_rate(&mmp3_clk_pll1)/4) {
clk->enable_val = HSI_PLL1_DIV_2;
clk->div = 2;
clk_reparent(clk, &mmp3_clk_pll1);
} else if (rate == clk_get_rate(&mmp3_clk_pll1)/2) {
clk->enable_val = HSI_PLL1;
clk->div = 2;
clk_reparent(clk, &mmp3_clk_pll1);
} else {
pr_err("%s: unexpected hsi clock rate %ld\n",
__func__, rate);
BUG();
}
return 0;
}
struct clkops hsi_clk_ops = {
.init = hsi_clk_init,
.enable = hsi_clk_enable,
.disable = hsi_clk_disable,
.setrate = hsi_clk_setrate,
.round_rate = hsi_clk_round_rate,
};
static struct clk_mux_sel hsi_mux_pll1_pll2[] = {
{.input = &mmp3_clk_pll1, .value = 0},
{.input = &mmp3_clk_pll2, .value = 1},
{0, 0},
};
static struct clk mmp3_clk_hsi = {
.name = "hsiclk",
.inputs = hsi_mux_pll1_pll2,
.lookup = {
.con_id = "hsi-clk",
},
.clk_rst = (void __iomem *)MPMU_HSI_CLK_RES_CTRL,
.ops = &hsi_clk_ops,
};
#endif
#ifdef CONFIG_VIDEO_MVISP
#define DXOISP_PLL1 1
#define DXOISP_PLL2 2
int mmp3_isp_reset_hw(void *param)
{
int reg;
param = param;
reg = readl(APMU_ISPCLK);
reg &= ~(0x1 << 1);
writel(reg, APMU_ISPCLK);
mdelay(20);
reg |= 0x1 << 1;
writel(reg, APMU_ISPCLK);
return 0;
}
static void dxoisp_clk_init(struct clk *clk)
{
clk->rate = clk_get_rate(&mmp3_clk_pll1)/2; /* 400MHz */
clk->enable_val = DXOISP_PLL1;
clk->div = 2;
clk->mul = 1;
clk_reparent(clk, &mmp3_clk_pll1);
}
static int dxoisp_clk_enable(struct clk *clk)
{
int reg;
/*clock source and clock divider */
reg = readl(APMU_ISPCLK);
reg &= ~0xF00;
reg |= (clk->div) << 8;
writel(reg, APMU_ISPCLK);
reg &= ~0xC0;
reg |= (clk->enable_val) << 6;
writel(reg, APMU_ISPCLK);
/*enable ISP AXI clock*/
reg |= 0x1 << 3;
writel(reg, APMU_ISPCLK);
/*enable ISP clk*/
reg |= 0x1 << 4;
writel(reg, APMU_ISPCLK);
/*enable CCIC AXI Arbiter clock*/
reg = readl(APMU_CCIC_RST);
reg |= 0x1 << 15;
writel(reg, APMU_CCIC_RST);
return 0;
}
static void dxoisp_clk_disable(struct clk *clk)
{
int reg;
/*disable ccic AXI Arbiter Clock*/
reg = readl(APMU_CCIC_RST);
reg &= ~(0x1 << 15);
writel(reg, APMU_CCIC_RST);
/*Disable ISP AXI clock*/
reg = readl(APMU_ISPCLK);
reg &= ~(0x1 << 4);
writel(reg, APMU_ISPCLK);
/*Disable ISP clock*/
reg &= ~(0x1 << 3);
writel(reg, APMU_ISPCLK);
}
static long dxoisp_clk_round_rate(struct clk *clk, unsigned long rate)
{
if (rate <= clk_get_rate(&mmp3_clk_pll1)/4)
return clk_get_rate(&mmp3_clk_pll1)/4; /* 200M */
else
return clk_get_rate(&mmp3_clk_pll1)/2; /* 400M */
}
static int dxoisp_clk_setrate(struct clk *clk, unsigned long rate)
{
clk->mul = 1;
if (rate == clk_get_rate(&mmp3_clk_pll1)/4) {
clk->enable_val = DXOISP_PLL1;
clk->div = 4;
clk_reparent(clk, &mmp3_clk_pll1);
} else if (rate == clk_get_rate(&mmp3_clk_pll1)/2) {
clk->enable_val = DXOISP_PLL1;
clk->div = 2;
clk_reparent(clk, &mmp3_clk_pll1);
} else {
pr_err("%s: unexpected dxoisp clock rate %ld\n",
__func__, rate);
BUG();
}
return 0;
}
struct clkops dxoisp_clk_ops = {
.init = dxoisp_clk_init,
.enable = dxoisp_clk_enable,
.disable = dxoisp_clk_disable,
.setrate = dxoisp_clk_setrate,
.round_rate = dxoisp_clk_round_rate,
};
static struct clk_mux_sel dxoisp_mux_pll1_pll2[] = {
{.input = &mmp3_clk_pll1, .value = 0},
{.input = &mmp3_clk_pll2, .value = 1},
{0, 0},
};
static struct clk mmp3_clk_dxoisp = {
.name = "dxoisp",
.inputs = dxoisp_mux_pll1_pll2,
.lookup = {
.con_id = "ISP-CLK",
},
.clk_rst = (void __iomem *)APMU_ISPCLK,
.ops = &dxoisp_clk_ops,
};
#define DXOCCIC_PLL1_DIV2 0
static void dxoccic_clk_init(struct clk *clk)
{
clk->rate = clk_get_rate(&mmp3_clk_pll1)/2; /* 400MHz */
clk->enable_val = DXOCCIC_PLL1_DIV2;
clk->div = 1;
clk->mul = 1;
clk_reparent(clk, &mmp3_clk_pll1);
}
static int dxoccic_clk_enable(struct clk *clk)
{
int reg;
reg = readl(clk->clk_rst);
/* Select PLL1/2 as CCIC clock source */
reg &= ~(0x3 << 6);
reg |= (clk->enable_val) << 6;
/* Select CCIC Clock Divider to 1 */
reg |= (clk->div) << 17;
/* Select PHY SLOW Divider to 26 */
reg |= (0x1A << 10);
writel(reg, clk->clk_rst);
/* Enable PHY SLOW clock */
reg |= (0x1 << 9);
/* Enable PHY clock */
reg |= (0x1 << 5);
/* Enable CCIC clock */
reg |= (0x1 << 4);
/* Enable AXI clock */
reg |= (0x1 << 3);
writel(reg, clk->clk_rst);
/* Deassert RST for PHY SLOW clock */
reg |= (0x1 << 8);
/* Deassert RST for PHY clock */
reg |= (0x1 << 2);
/* Deassert RST for CCIC clock */
reg |= (0x1 << 1);
/* Deassert RST for AXI clock */
reg |= (0x1 << 0);
writel(reg, clk->clk_rst);
reg = readl(APMU_CCIC_DBG);
reg |= (1 << 25) | (1 << 27);
writel(reg, APMU_CCIC_DBG);
reg = 0xFFFF;
writel(reg, APMU_CCIC_GATE);
return 0;
}
static void dxoccic_clk_disable(struct clk *clk)
{
int reg;
reg = readl(APMU_CCIC_GATE);
reg &= ~(0xFFFF);
writel(reg, APMU_CCIC_GATE);
reg = readl(APMU_CCIC_DBG);
reg &= ~((1 << 25) | (1 << 27));
writel(reg, APMU_CCIC_DBG);
reg = readl(clk->clk_rst);
/* Assert RST for PHY SLOW clock */
reg &= ~(0x1 << 8);
/* Assert RST for PHY clock */
reg &= ~(0x1 << 2);
/* Assert RST for CCIC clock */
reg &= ~(0x1 << 1);
/* Assert RST for AXI clock */
reg &= ~(0x1 << 0);
writel(reg, clk->clk_rst);
reg = readl(clk->clk_rst);
/* Disable PHY SLOW clock */
reg &= ~(0x1 << 9);
/* Disable PHY clock */
reg &= ~(0x1 << 5);
/* Disable CCIC clock */
reg &= ~(0x1 << 4);
/* Disable AXI clock */
reg &= ~(0x1 << 3);
writel(reg, clk->clk_rst);
}
static long dxoccic_clk_round_rate(struct clk *clk, unsigned long rate)
{
return clk_get_rate(&mmp3_clk_pll1)/2; /* 400M */
}
static int dxoccic_clk_setrate(struct clk *clk, unsigned long rate)
{
clk->mul = 1;
if (rate == clk_get_rate(&mmp3_clk_pll1)/2) {
clk->enable_val = DXOCCIC_PLL1_DIV2;
clk->div = 1;
clk_reparent(clk, &mmp3_clk_pll1);
} else {
pr_err("%s: unexpected dxoccic clock rate %ld\n",
__func__, rate);
BUG();
}
return 0;
}
struct clkops dxoccic_clk_ops = {
.init = dxoccic_clk_init,
.enable = dxoccic_clk_enable,
.disable = dxoccic_clk_disable,
.setrate = dxoccic_clk_setrate,
.round_rate = dxoccic_clk_round_rate,
};
static struct clk_mux_sel dxoccic_mux_pll1_pll2[] = {
{.input = &mmp3_clk_pll1, .value = 0},
{0, 0},
};
static struct clk mmp3_clk_dxoccic = {
.name = "dxoccic",
.inputs = dxoccic_mux_pll1_pll2,
.lookup = {
.con_id = "CCIC-CLK",
},
.clk_rst = (void __iomem *)APMU_CCIC_RST,
.ops = &dxoccic_clk_ops,
};
#endif
/* Frequency is in unit of Khz*/
static struct devfreq_frequency_table mmp3_vmeta_clk_table[] = {
INIT_FREQ_TABLE(1, 200000),
INIT_FREQ_TABLE(2, 266666),
INIT_FREQ_TABLE(3, 400000),
INIT_FREQ_TABLE(4, 533333),
INIT_FREQ_TABLE(5, DEVFREQ_TABLE_END),
};
int set_vmeta_freqs_table(struct devfreq *devfreq)
{
devfreq_set_freq_table(devfreq, mmp3_vmeta_clk_table);
devfreq->max_freq = 533333; /* max is 533MHz */
return 0;
}
#ifdef CONFIG_UIO_VMETA
static void vmeta_clk_init(struct clk *clk)
{
clk->rate = clk_get_rate(&mmp3_clk_pll1_clkoutp) / 2;
clk->enable_val = PLL1D2; /* for ACLK setting */
clk->div = 2;
clk->mul = 1;
clk_reparent(clk, &mmp3_clk_pll1_clkoutp);
}
static int vmeta_clk_enable(struct clk *clk)
{
int reg;
int i;
i = 0;
while ((clk->inputs[i].input != clk->parent) && clk->inputs[i].input)
i++;
if (clk->inputs[i].input == 0) {
pr_err("%s: unexpected vMeta clock source\n", __func__);
return -1;
}
reg = readl(clk->clk_rst);
/* vMeta clock setting */
reg &= ~APMU_VMETA_CLK_SEL_MASK;
reg &= ~APMU_VMETA_CLK_DIV_MASK;
reg |= (clk->inputs[i].value) << APMU_VMETA_CLK_SEL_SHIFT;
reg |= (clk->div) << APMU_VMETA_CLK_DIV_SHIFT;
/* vMeta bus clock setting */
reg &= ~APMU_VMETA_ACLK_MASK;
reg |= (clk->enable_val) << APMU_VMETA_ACLK_SEL_SHIFT;
writel(reg, clk->clk_rst);
reg = readl(clk->clk_rst);
reg |= (APMU_VMETA_AXICLK_EN | APMU_VMETA_CLK_EN);
writel(reg, clk->clk_rst);
return 0;
}
static void vmeta_clk_disable(struct clk *clk)
{
int reg;
reg = readl(clk->clk_rst);
reg &= ~(APMU_VMETA_CLK_EN | APMU_VMETA_AXICLK_EN);
writel(reg, clk->clk_rst);
}
static long vmeta_clk_round_rate(struct clk *clk, unsigned long rate)
{
if (rate <= clk_get_rate(&mmp3_clk_pll1)/4)
return clk_get_rate(&mmp3_clk_pll1)/4; /* 200M */
else if (rate <= clk_get_rate(&mmp3_clk_pll1_clkoutp)/4)
return clk_get_rate(&mmp3_clk_pll1_clkoutp)/4; /* 266M */
else if (rate <= clk_get_rate(&mmp3_clk_pll1)/2)
return clk_get_rate(&mmp3_clk_pll1)/2; /* 400M */
else if (rate <= clk_get_rate(&mmp3_clk_pll2_clkoutp)/2)
return clk_get_rate(&mmp3_clk_pll2_clkoutp)/2; /* 480M */
else if (rate <= clk_get_rate(&mmp3_clk_pll1_clkoutp)/2)
return clk_get_rate(&mmp3_clk_pll1_clkoutp)/2; /* 533M */
else if (rate <= clk_get_rate(&mmp3_clk_pll2)/2)
return clk_get_rate(&mmp3_clk_pll2)/2; /* 600M */
else
return clk_get_rate(&mmp3_clk_pll1); /* 800M */
}
static int vmeta_clk_setrate(struct clk *clk, unsigned long rate)
{
clk->mul = 1;
if (rate == clk_get_rate(&mmp3_clk_pll1)/4) {
clk->enable_val = PLL1D4;
clk->div = 4;
clk_reparent(clk, &mmp3_clk_pll1);
} else if (rate == clk_get_rate(&mmp3_clk_pll1_clkoutp)/4) {
clk->enable_val = PLL1D4;
clk->div = 4;
clk_reparent(clk, &mmp3_clk_pll1_clkoutp);
} else if (rate == clk_get_rate(&mmp3_clk_pll1)/2) {
clk->enable_val = PLL1D2;
clk->div = 2;
clk_reparent(clk, &mmp3_clk_pll1);
} else if (rate == clk_get_rate(&mmp3_clk_pll2_clkoutp)/2) {
clk->enable_val = PLL1D2;
clk->div = 2;
clk_reparent(clk, &mmp3_clk_pll2_clkoutp);
} else if (rate == clk_get_rate(&mmp3_clk_pll1_clkoutp)/2) {
clk->enable_val = PLL1D2;
clk->div = 2;
clk_reparent(clk, &mmp3_clk_pll1_clkoutp);
} else if (rate == clk_get_rate(&mmp3_clk_pll2)/2) {
clk->enable_val = PLL1D2;
clk->div = 2;
clk_reparent(clk, &mmp3_clk_pll2);
} else if (rate == clk_get_rate(&mmp3_clk_pll1)) {
clk->enable_val = PLL1D2;
clk->div = 1;
clk_reparent(clk, &mmp3_clk_pll1);
} else {
pr_err("%s: unexpected vmeta clock rate %ld\n", __func__, rate);
BUG();
}
return 0;
}
static unsigned long vmeta_clk_getrate(struct clk *clk)
{
if (clk->parent && clk->div)
return clk_get_rate(clk->parent)/clk->div;
else
pr_err("%s, get vMeta clock fail\n", __func__);
return -EINVAL;
}
struct clkops vmeta_clk_ops = {
.init = vmeta_clk_init,
.enable = vmeta_clk_enable,
.disable = vmeta_clk_disable,
.getrate = vmeta_clk_getrate,
.setrate = vmeta_clk_setrate,
.round_rate = vmeta_clk_round_rate,
};
static struct clk_mux_sel vmeta_mux_pll1_pll2[] = {
{.input = &mmp3_clk_pll1, .value = 0},
{.input = &mmp3_clk_pll2, .value = 1},
{.input = &mmp3_clk_pll1_clkoutp, .value = 2},
{.input = &mmp3_clk_pll2_clkoutp, .value = 3},
{0, 0},
};
static struct clk mmp3_clk_vmeta = {
.name = "vmeta",
.inputs = vmeta_mux_pll1_pll2,
.lookup = {
.con_id = "VMETA_CLK",
},
.clk_rst = (void __iomem *)APMU_VMETA_CLK_RES_CTRL,
.ops = &vmeta_clk_ops,
.dynamic_change = 1,
};
#endif
static void ccic_clk_init(struct clk *clk)
{
const struct clk_mux_sel *sel;
u32 val = 0;
/* by default select pll1/2 as clock source and divider 1 */
clk->mul = 1;
clk->div = 1;
clk_reparent(clk, &mmp3_clk_pll1_d_2);
val &= ~(clk->reg_data[DIV][CONTROL].reg_mask
<< clk->reg_data[DIV][CONTROL].reg_shift);
val |= clk->div
<< clk->reg_data[DIV][CONTROL].reg_shift;
for (sel = clk->inputs; sel->input != 0; sel++) {
if (sel->input == &mmp3_clk_pll1_d_2)
break;
}
if (sel->input == 0) {
pr_err("ccic: no matched input for this parent!\n");
BUG();
}
val &= ~(clk->reg_data[SOURCE][CONTROL].reg_mask
<< clk->reg_data[SOURCE][CONTROL].reg_shift);
val |= sel->value
<< clk->reg_data[SOURCE][CONTROL].reg_shift;
/* use fixed value 0x1a for CCIC_PHYSLOW_PRER */
clk->enable_val = val | (0x1a << 10);
__raw_writel(val, clk->reg_data[SOURCE][CONTROL].reg);
}
static int ccic_clk_enable(struct clk *clk)
{
u32 val;
val = __raw_readl(clk->reg_data[SOURCE][CONTROL].reg) & 0x18000;
val |= clk->enable_val;
/* enable clocks */
val |= 0x38;
__raw_writel(val, clk->reg_data[SOURCE][CONTROL].reg);
/* release reset */
val |= 0x307;
__raw_writel(val, clk->reg_data[SOURCE][CONTROL].reg);
/* enable MIPI DPHY CSI2's DVDD and AVDD */
val = __raw_readl(APMU_CCIC_DBG);
if (strncmp(clk->name, "ccic1", 5) == 0)
val |= (1 << 25) | (1 << 27);
else
val |= (1 << 26) | (1 << 28);
__raw_writel(val, APMU_CCIC_DBG);
return 0;
}
static void ccic_clk_disable(struct clk *clk)
{
u32 val;
val = __raw_readl(clk->reg_data[SOURCE][CONTROL].reg);
val &= 0x18000;
__raw_writel(val, clk->reg_data[SOURCE][CONTROL].reg);
/* disable MIPI DPHY CSI2's DVDD and AVDD */
val = __raw_readl(APMU_CCIC_DBG);
if (strncmp(clk->name, "ccic1", 5) == 0)
val &= ~((1 << 25) | (1 << 27));
else
val &= ~((1 << 26) | (1 << 28));
__raw_writel(val, APMU_CCIC_DBG);
}
static long ccic_clk_round_rate(struct clk *clk, unsigned long rate)
{
/*
* CCIC has four clock source: PLL1/2, PLL1/16, and VCTCXO.
* Here PLL1/16 is not used since PLL1/2 and VCTCXO can cover it.
* Only use PLL1/2 as clock source if the rate is larger than VCTCXO.
*/
int i;
unsigned long parent_rate;
/* for those which is less than vctcxo, use vctcxo as clock source */
if (rate <= clk_get_rate(&mmp3_clk_vctcxo)) {
parent_rate = clk_get_rate(&mmp3_clk_vctcxo);
for (i = 2; i < 16; i++) {
if (rate > parent_rate / i)
break;
}
return parent_rate / (i - 1);
/* else use pll1/2 as clock source */
} else {
parent_rate = clk_get_rate(&mmp3_clk_pll1_d_2);
for (i = 2; i < 16; i++) {
if (rate > parent_rate / i)
break;
}
}
return parent_rate / (i - 1);
}
static int ccic_clk_setrate(struct clk *clk, unsigned long rate)
{
unsigned long parent_rate;
const struct clk_mux_sel *sel;
u32 val = 0;
if (rate <= clk_get_rate(&mmp3_clk_vctcxo)) {
parent_rate = clk_get_rate(&mmp3_clk_vctcxo);
clk->mul = 1;
clk->div = parent_rate / rate;
clk_reparent(clk, &mmp3_clk_vctcxo);
val &= ~(clk->reg_data[DIV][CONTROL].reg_mask
<< clk->reg_data[DIV][CONTROL].reg_shift);
val |= clk->div
<< clk->reg_data[DIV][CONTROL].reg_shift;
for (sel = clk->inputs; sel->input != 0; sel++) {
if (sel->input == &mmp3_clk_vctcxo)
break;
}
if (sel->input == 0) {
pr_err("ccic: no matched input for this parent!\n");
BUG();
}
val &= ~(clk->reg_data[SOURCE][CONTROL].reg_mask
<< clk->reg_data[SOURCE][CONTROL].reg_shift);
val |= sel->value
<< clk->reg_data[SOURCE][CONTROL].reg_shift;
} else if (rate <= clk_get_rate(&mmp3_clk_pll1_d_2)) {
parent_rate = clk_get_rate(&mmp3_clk_pll1_d_2);
clk->mul = 1;
clk->div = parent_rate / rate;
clk_reparent(clk, &mmp3_clk_pll1_d_2);
val &= ~(clk->reg_data[DIV][CONTROL].reg_mask
<< clk->reg_data[DIV][CONTROL].reg_shift);
val |= clk->div
<< clk->reg_data[DIV][CONTROL].reg_shift;
for (sel = clk->inputs; sel->input != 0; sel++) {
if (sel->input == &mmp3_clk_pll2)
break;
}
if (sel->input == 0) {
pr_err("ccic: no matched input for this parent!\n");
BUG();
}
val &= ~(clk->reg_data[SOURCE][CONTROL].reg_mask
<< clk->reg_data[SOURCE][CONTROL].reg_shift);
val |= sel->value
<< clk->reg_data[SOURCE][CONTROL].reg_shift;
}
/* use fixed value 0x1a for CCIC_PHYSLOW_PRER */
clk->enable_val = val | (0x1a << 10);
__raw_writel(val, clk->reg_data[SOURCE][CONTROL].reg);
return 0;
}
struct clkops ccic_clk_ops = {
.init = ccic_clk_init,
.enable = ccic_clk_enable,
.disable = ccic_clk_disable,
.round_rate = ccic_clk_round_rate,
.setrate = ccic_clk_setrate,
};
static struct clk_mux_sel ccic_clk_mux[] = {
{.input = &mmp3_clk_pll1_d_2, .value = 0},
{.input = &mmp3_clk_vctcxo, .value = 2},
{0, 0},
};
static int ccic_share_clk_enable(struct clk *clk)
{
u32 val;
/* ccic axi enable clocks */
val = 0x10000;
__raw_writel(val, clk->reg_data[SOURCE][CONTROL].reg);
/* ccic axi clock release reset */
val |= 0x8000;
__raw_writel(val, clk->reg_data[SOURCE][CONTROL].reg);
return 0;
}
static void ccic_share_clk_disable(struct clk *clk)
{
u32 val;
val = __raw_readl(clk->reg_data[SOURCE][CONTROL].reg);
val &= ~0x18000;
__raw_writel(val, clk->reg_data[SOURCE][CONTROL].reg);
}
struct clkops ccic_share_clk_ops = {
.init = NULL,
.enable = ccic_share_clk_enable,
.disable = ccic_share_clk_disable,
.round_rate = NULL,
.setrate = NULL,
};
static struct clk mmp3_clk_ccic0 = {
.name = "ccic0",
.ops = &ccic_share_clk_ops,
.inputs = ccic_clk_mux,
.reg_data = {
{ {APMU_CCIC_RST, 6, 0x3},
{APMU_CCIC_RST, 6, 0x3} },
{{APMU_CCIC_RST, 17, 0xf},
{APMU_CCIC_RST, 17, 0xf} } },
};
static struct clk *ccic_depend_clk[] = {
&mmp3_clk_ccic0,
};
static struct clk mmp3_clk_ccic1 = {
.name = "ccic1",
.lookup = {
.dev_id = "mv-camera.0",
.con_id = "CCICRSTCLK",
},
.dependence = ccic_depend_clk,
.dependence_count = ARRAY_SIZE(ccic_depend_clk),
.ops = &ccic_clk_ops,
.inputs = ccic_clk_mux,
.reg_data = {
{ {APMU_CCIC_RST, 6, 0x3},
{APMU_CCIC_RST, 6, 0x3} },
{{APMU_CCIC_RST, 17, 0xf},
{APMU_CCIC_RST, 17, 0xf} } },
};
static struct clk mmp3_clk_ccic2 = {
.name = "ccic2",
.lookup = {
.dev_id = "mv-camera.1",
.con_id = "CCICRSTCLK",
},
.dependence = ccic_depend_clk,
.dependence_count = ARRAY_SIZE(ccic_depend_clk),
.ops = &ccic_clk_ops,
.inputs = ccic_clk_mux,
.reg_data = {
{ {APMU_CCIC2_RST, 6, 0x3},
{APMU_CCIC2_RST, 6, 0x3} },
{{APMU_CCIC2_RST, 17, 0xf},
{APMU_CCIC2_RST, 17, 0xf} } },
};
static struct clk *mmp3_clks_ptr[] = {
&mmp3_clk_pll1_d_2,
&mmp3_clk_pll1,
&mmp3_clk_pll2,
&mmp3_clk_pll1_clkoutp,
&mmp3_clk_pll2_clkoutp,
&mmp3_clk_vctcxo,
&mmp3_clk_32k,
&mmp3_clk_core_root,
&mmp3_clk_virtual_pj,
&mmp3_clk_mp1,
&mmp3_clk_mp2,
&mmp3_clk_mm,
&mmp3_clk_cpu,
&mmp3_clk_aclk,
&mmp3_clk_core_periph,
&mmp3_clk_atclk,
&mmp3_clk_ddr_root,
&mmp3_clk_ddr1,
&mmp3_clk_ddr2,
#ifdef CONFIG_DDR_DEVFREQ
&mmp3_clk_ddr,
#endif
&mmp3_clk_axi_root,
&mmp3_clk_axi1,
&mmp3_clk_axi2,
&mmp3_clk_pll1_d_4,
&mmp3_clk_gc,
&mmp3_clk_disp1,
&mmp3_clk_lcd1,
&mmp3_clk_sdh0,
&mmp3_clk_sdh1,
&mmp3_clk_sdh2,
&mmp3_clk_sdh3,
&mmp3_clk_disp1_axi,
&mmp3_clk_hdmi,
&mmp3_clk_thermal,
&mmp3_clk_ccic1,
&mmp3_clk_ccic2,
#ifdef CONFIG_UIO_VMETA
&mmp3_clk_vmeta,
#endif
#ifdef CONFIG_VIDEO_MVISP
&mmp3_clk_dxoisp,
&mmp3_clk_dxoccic,
#endif
#ifdef CONFIG_MMP3_HSI
&mmp3_clk_hsi,
#endif
};
static int apbc_clk_enable(struct clk *clk)
{
unsigned long data;
data = __raw_readl(clk->clk_rst) & ~(APBC_FNCLKSEL(7));
data |= APBC_FNCLK | APBC_FNCLKSEL(clk->fnclksel);
__raw_writel(data, clk->clk_rst);
/*
* delay two cycles of the solwest clock between the APB bus clock
* and the functional module clock.
*/
udelay(10);
data |= APBC_APBCLK;
__raw_writel(data, clk->clk_rst);
udelay(10);
data |= APBC_RST;
__raw_writel(data, clk->clk_rst);
udelay(100);
data &= ~APBC_RST;
__raw_writel(data, clk->clk_rst);
udelay(100);
return 0;
}
static void apbc_clk_disable(struct clk *clk)
{
unsigned long data;
data = __raw_readl(clk->clk_rst) & ~(APBC_FNCLK | APBC_FNCLKSEL(7));
__raw_writel(data, clk->clk_rst);
udelay(10);
data &= ~APBC_APBCLK;
__raw_writel(data, clk->clk_rst);
}
struct clkops apbc_clk_ops = {
.enable = apbc_clk_enable,
.disable = apbc_clk_disable,
};
#define APBC_CLK(_name, _dev, _con, _reg, _fnclksel, _rate, _parent)\
{ \
.name = _name, \
.lookup = { \
.dev_id = _dev, \
.con_id = _con, \
}, \
.clk_rst = (void __iomem *)APBC_##_reg, \
.fnclksel = _fnclksel, \
.rate = _rate, \
.ops = &apbc_clk_ops, \
.parent = _parent, \
}
#define APBC_CLK_OPS(_name, _dev, _con, _reg, _fnclksel, _rate, _parent, _ops)\
{ \
.name = _name, \
.lookup = { \
.dev_id = _dev, \
.con_id = _con, \
}, \
.clk_rst = (void __iomem *)APBC_##_reg, \
.fnclksel = _fnclksel, \
.rate = _rate, \
.ops = _ops, \
.parent = _parent, \
}
static void uart_clk_init(struct clk *clk)
{
clk->mul = clk->div = 1;
/*
* Bit(s) PMUM_SUCCR_RSRV_31_29 reserved
* UART Clock Generation Programmable Divider
* Numerator Value
*/
clk->reg_data[DIV][CONTROL].reg = MPMU_SUCCR;
clk->reg_data[DIV][CONTROL].reg_shift = 16;
clk->reg_data[DIV][CONTROL].reg_mask = 0x1fff;
/*
* Bit(s) PMUM_SUCCR_RSRV_15_13 reserved
* UART Clock Generation Programmable Divider
* Denominator Value
*/
clk->reg_data[MUL][CONTROL].reg = MPMU_SUCCR;
clk->reg_data[MUL][CONTROL].reg_shift = 0;
clk->reg_data[MUL][CONTROL].reg_mask = 0x1fff;
}
static int uart_clk_enable(struct clk *clk)
{
uint32_t clk_rst;
clk_rst = __raw_readl(clk->clk_rst);
clk_rst |= APBC_FNCLK;
__raw_writel(clk_rst, clk->clk_rst);
mdelay(1);
clk_rst |= APBC_APBCLK;
__raw_writel(clk_rst, clk->clk_rst);
mdelay(1);
clk_rst &= ~(APBC_RST);
__raw_writel(clk_rst, clk->clk_rst);
if (clk->rate == clk_get_rate(&mmp3_clk_vctcxo)) {
/* choose vctcxo */
clk_rst = __raw_readl(clk->clk_rst);
clk_rst &= ~(APBC_FNCLKSEL(0x7));
clk_rst |= APBC_FNCLKSEL(0x1);
__raw_writel(clk_rst, clk->clk_rst);
} else {
/* choose programmable clk */
clk_rst = __raw_readl(clk->clk_rst);
clk_rst &= ~(APBC_FNCLKSEL(0x7));
__raw_writel(clk_rst, clk->clk_rst);
}
return 0;
}
static void uart_clk_disable(struct clk *clk)
{
__raw_writel(0, clk->clk_rst);
mdelay(1);
}
static long uart_clk_round_rate(struct clk *clk, unsigned long rate)
{
unsigned long parent_rate;
if (rate >= clk_get_rate(&mmp3_clk_vctcxo)) {
parent_rate = clk_get_rate(&mmp3_clk_pll1_d_4);
return parent_rate * 16 / 27 / 2;
} else
return clk_get_rate(&mmp3_clk_vctcxo);
}
static int uart_clk_setrate(struct clk *clk, unsigned long val)
{
uint32_t clk_rst;
if (val == clk_get_rate(&mmp3_clk_vctcxo)) {
/* choose vctcxo */
clk_rst = __raw_readl(clk->clk_rst);
clk_rst &= ~(APBC_FNCLKSEL(0x7));
clk_rst |= APBC_FNCLKSEL(0x1);
__raw_writel(clk_rst, clk->clk_rst);
clk->div = clk->mul = 1;
clk_reparent(clk, &mmp3_clk_vctcxo);
} else {
/* set m/n for high speed */
unsigned int numer = 27;
unsigned int denom = 16;
/*
* n/d = base_clk/(2*out_clk)
* base_clk = 199.33M, out_clk=199.33*16/27/2=59.06M
* buadrate = clk/(16*divisor)
*/
clk_rst = __raw_readl(clk->reg_data[DIV][CONTROL].reg);
clk_rst &= ~(clk->reg_data[DIV][CONTROL].reg_mask <<
clk->reg_data[DIV][CONTROL].reg_shift);
clk_rst |= numer << clk->reg_data[DIV][CONTROL].reg_shift;
clk_rst &= ~(clk->reg_data[MUL][CONTROL].reg_mask <<
clk->reg_data[MUL][CONTROL].reg_shift);
clk_rst |= denom << clk->reg_data[MUL][CONTROL].reg_shift;
__raw_writel(clk_rst, clk->reg_data[DIV][CONTROL].reg);
/* choose programmable clk */
clk_rst = __raw_readl(clk->clk_rst);
clk_rst &= ~(APBC_FNCLKSEL(0x7));
__raw_writel(clk_rst, clk->clk_rst);
clk->div = numer * 2;
clk->mul = denom;
clk_reparent(clk, &mmp3_clk_pll1_d_4);
}
return 0;
}
struct clkops uart_clk_ops = {
.init = uart_clk_init,
.enable = uart_clk_enable,
.disable = uart_clk_disable,
.round_rate = uart_clk_round_rate,
.setrate = uart_clk_setrate,
};
static void nand_clk_init(struct clk *clk)
{
clk->div = 8;
clk->mul = 1;
/* by default select pll1 as clock source and divider 8 */
clk->enable_val = 0x80;
}
static int nand_clk_enable(struct clk *clk)
{
__raw_writel(clk->enable_val | 0x2d, clk->clk_rst);
return 0;
}
static void nand_clk_disable(struct clk *clk)
{
__raw_writel(0x0, clk->clk_rst);
}
static long nand_clk_round_rate(struct clk *clk, unsigned long rate)
{
if (rate <= clk_get_rate(&mmp3_clk_vctcxo) / 2)
return clk_get_rate(&mmp3_clk_vctcxo) / 2; /* 13M */
else if (rate <= clk_get_rate(&mmp3_clk_pll1) / 12)
return clk_get_rate(&mmp3_clk_pll1) / 12; /* 67M */
else if (rate <= clk_get_rate(&mmp3_clk_pll1) / 8)
return clk_get_rate(&mmp3_clk_pll1) / 8; /* 100M */
else if (rate <= clk_get_rate(&mmp3_clk_pll2) / 12)
return clk_get_rate(&mmp3_clk_pll2) / 12; /* 111M */
else if (rate <= clk_get_rate(&mmp3_clk_pll1) / 6)
return clk_get_rate(&mmp3_clk_pll1) / 6; /* 133M */
else if (rate <= clk_get_rate(&mmp3_clk_pll2) / 8)
return clk_get_rate(&mmp3_clk_pll2) / 8; /* 167M */
else if (rate <= clk_get_rate(&mmp3_clk_pll1) / 4)
return clk_get_rate(&mmp3_clk_pll1) / 4; /* 200M */
else
return clk_get_rate(&mmp3_clk_pll2) / 6; /* 222M */
}
static int nand_clk_setrate(struct clk *clk, unsigned long rate)
{
if (rate == clk_get_rate(&mmp3_clk_vctcxo) / 2) {
clk->enable_val = 0x1c0;
clk->div = 2;
clk->mul = 1;
clk_reparent(clk, &mmp3_clk_vctcxo);
} else if (rate == clk_get_rate(&mmp3_clk_pll1) / 12) {
clk->enable_val = 0xc0;
clk->div = 12;
clk->mul = 1;
clk_reparent(clk, &mmp3_clk_pll1);
} else if (rate == clk_get_rate(&mmp3_clk_pll1) / 8) {
clk->enable_val = 0x80;
clk->div = 8;
clk->mul = 1;
clk_reparent(clk, &mmp3_clk_pll1);
} else if (rate == clk_get_rate(&mmp3_clk_pll2) / 12) {
clk->enable_val = 0x180;
clk->div = 12;
clk->mul = 1;
clk_reparent(clk, &mmp3_clk_pll2);
} else if (rate == clk_get_rate(&mmp3_clk_pll1) / 6) {
clk->enable_val = 0x40;
clk->div = 6;
clk->mul = 1;
clk_reparent(clk, &mmp3_clk_pll1);
} else if (rate == clk_get_rate(&mmp3_clk_pll2) / 8) {
clk->enable_val = 0x140;
clk->div = 8;
clk->mul = 1;
clk_reparent(clk, &mmp3_clk_pll2);
} else if (rate == clk_get_rate(&mmp3_clk_pll1) / 4) {
clk->enable_val = 0x0;
clk->div = 4;
clk->mul = 1;
clk_reparent(clk, &mmp3_clk_pll1);
} else if (rate == clk_get_rate(&mmp3_clk_pll2) / 6) {
clk->enable_val = 0x100;
clk->div = 6;
clk->mul = 1;
clk_reparent(clk, &mmp3_clk_pll2);
} else {
pr_err("%s: unexpected clock rate %ld\n", __func__, rate);
BUG();
}
return 0;
}
struct clkops nand_clk_ops = {
.init = nand_clk_init,
.enable = nand_clk_enable,
.disable = nand_clk_disable,
.round_rate = nand_clk_round_rate,
.setrate = nand_clk_setrate,
};
static int apmu_clk_enable(struct clk *clk)
{
__raw_writel(clk->enable_val, clk->clk_rst);
return 0;
}
static void apmu_clk_disable(struct clk *clk)
{
__raw_writel(0, clk->clk_rst);
}
static int apmu_clk_setrate(struct clk *clk, unsigned long rate)
{
__raw_writel(rate, clk->clk_rst);
return 0;
}
struct clkops apmu_clk_ops = {
.enable = apmu_clk_enable,
.disable = apmu_clk_disable,
.setrate = apmu_clk_setrate,
};
#define APMU_CLK(_name, _dev, _con, _reg, _eval, _rate, _parent)\
{ \
.name = _name, \
.lookup = { \
.dev_id = _dev, \
.con_id = _con, \
}, \
.clk_rst = (void __iomem *)APMU_##_reg, \
.enable_val = _eval, \
.rate = _rate, \
.ops = &apmu_clk_ops, \
.parent = _parent, \
}
#define APMU_CLK_OPS(_name, _dev, _con, _reg, _eval, _rate, _parent, _ops)\
{ \
.name = _name, \
.lookup = { \
.dev_id = _dev, \
.con_id = _con, \
}, \
.clk_rst = (void __iomem *)APMU_##_reg, \
.enable_val = _eval, \
.rate = _rate, \
.parent = _parent, \
.ops = _ops, \
}
/* usb: hsic clock */
static int hsic_clk_enable(struct clk *clk)
{
uint32_t clk_rst;
clk_rst = __raw_readl(clk->clk_rst);
clk_rst |= 0x1b;
__raw_writel(clk_rst, clk->clk_rst);
return 0;
}
static void hsic_clk_disable(struct clk *clk)
{
uint32_t clk_rst;
clk_rst = __raw_readl(clk->clk_rst);
clk_rst &= ~0x18;
__raw_writel(clk_rst, clk->clk_rst);
}
struct clkops hsic_clk_ops = {
.enable = hsic_clk_enable,
.disable = hsic_clk_disable,
};
/* usb: fsic clock */
static int fsic_clk_enable(struct clk *clk)
{
uint32_t clk_rst;
clk_rst = __raw_readl(clk->clk_rst);
clk_rst |= 0x1b;
__raw_writel(clk_rst, clk->clk_rst);
return 0;
}
static void fsic_clk_disable(struct clk *clk)
{
uint32_t clk_rst;
clk_rst = __raw_readl(clk->clk_rst);
clk_rst &= ~0x18;
__raw_writel(clk_rst, clk->clk_rst);
}
struct clkops fsic_clk_ops = {
.enable = fsic_clk_enable,
.disable = fsic_clk_disable,
};
static int pwm_clk_enable(struct clk *clk)
{
struct clk *clk_apb = NULL, *clk_share = NULL;
unsigned long data;
data = __raw_readl(clk->clk_rst) & ~(APBC_FNCLKSEL(7));
data |= APBC_FNCLK | APBC_FNCLKSEL(clk->fnclksel);
__raw_writel(data, clk->clk_rst);
/*
* delay two cycles of the solwest clock between the APB bus clock
* and the functional module clock.
*/
udelay(10);
/*
* A dependence exists between pwm1 and pwm2.pwm1 can controll its
* apb bus clk independently, while pwm2 apb bus clk is controlled
* by pwm1's. The same relationship exists between pwm3 and pwm4.
*/
if (!strcmp(clk->name, "pwm1")) {
clk_share = clk_get_sys("mmp2-pwm.1", NULL);
BUG_ON(IS_ERR(clk_share));
clk_apb = clk;
} else if (!strcmp(clk->name, "pwm2")) {
clk_share = clk_get_sys("mmp2-pwm.0", NULL);
BUG_ON(IS_ERR(clk_share));
clk_apb = clk_share;
} else if (!strcmp(clk->name, "pwm3")) {
clk_share = clk_get_sys("mmp2-pwm.3", NULL);
BUG_ON(IS_ERR(clk_share));
clk_apb = clk;
} else if (!strcmp(clk->name, "pwm4")) {
clk_share = clk_get_sys("mmp2-pwm.2", NULL);
BUG_ON(IS_ERR(clk_share));
clk_apb = clk_share;
}
if ((clk->refcnt + clk_share->refcnt) == 0) {
data = __raw_readl(clk_apb->clk_rst);
data |= APBC_APBCLK;
__raw_writel(data, clk_apb->clk_rst);
udelay(10);
data = __raw_readl(clk->clk_rst);
data &= ~APBC_RST;
__raw_writel(data, clk->clk_rst);
if (strcmp(clk->name, clk_apb->name)) {
data = __raw_readl(clk_apb->clk_rst);
data &= ~APBC_RST;
__raw_writel(data, clk_apb->clk_rst);
}
}
return 0;
}
static void pwm_clk_disable(struct clk *clk)
{
struct clk *clk_apb = NULL, *clk_share = NULL;
unsigned long data;
data = __raw_readl(clk->clk_rst) & ~(APBC_FNCLK | APBC_FNCLKSEL(7));
__raw_writel(data, clk->clk_rst);
udelay(10);
if (!strcmp(clk->name, "pwm1")) {
clk_share = clk_get_sys("mmp2-pwm.1", NULL);
BUG_ON(IS_ERR(clk_share));
clk_apb = clk;
} else if (!strcmp(clk->name, "pwm2")) {
clk_share = clk_get_sys("mmp2-pwm.0", NULL);
BUG_ON(IS_ERR(clk_share));
clk_apb = clk_share;
} else if (!strcmp(clk->name, "pwm3")) {
clk_share = clk_get_sys("mmp2-pwm.3", NULL);
BUG_ON(IS_ERR(clk_share));
clk_apb = clk;
} else if (!strcmp(clk->name, "pwm4")) {
clk_share = clk_get_sys("mmp2-pwm.2", NULL);
BUG_ON(IS_ERR(clk_share));
clk_apb = clk_share;
}
if ((clk->refcnt + clk_share->refcnt) == 1) {
data = __raw_readl(clk_apb->clk_rst);
data &= ~APBC_APBCLK;
__raw_writel(data, clk_apb->clk_rst);
}
}
struct clkops pwm_clk_ops = {
.enable = pwm_clk_enable,
.disable = pwm_clk_disable,
};
static struct clk mmp3_list_clks[] = {
APBC_CLK("twsi1", "pxa2xx-i2c.0", NULL, MMP2_TWSI1,
0, 26000000, &mmp3_clk_vctcxo),
APBC_CLK("twsi2", "pxa2xx-i2c.1", NULL, MMP2_TWSI2,
0, 26000000, &mmp3_clk_vctcxo),
APBC_CLK("twsi3", "pxa2xx-i2c.2", NULL, MMP2_TWSI3,
0, 26000000, &mmp3_clk_vctcxo),
APBC_CLK("twsi4", "pxa2xx-i2c.3", NULL, MMP2_TWSI4,
0, 26000000, &mmp3_clk_vctcxo),
APBC_CLK("twsi5", "pxa2xx-i2c.4", NULL, MMP2_TWSI5,
0, 26000000, &mmp3_clk_vctcxo),
APBC_CLK("twsi6", "pxa2xx-i2c.5", NULL, MMP2_TWSI6,
0, 26000000, &mmp3_clk_vctcxo),
APBC_CLK("keypad", "pxa27x-keypad", NULL, MMP2_KPC,
0, 32768, &mmp3_clk_32k),
APBC_CLK("ssp.1", "mmp-ssp.1", NULL, MMP2_SSP1,
0, 6500000, &mmp3_clk_vctcxo),
APBC_CLK("ssp.2", "mmp-ssp.2", NULL, MMP2_SSP2,
0, 6500000, &mmp3_clk_vctcxo),
APBC_CLK("ssp.3", "mmp-ssp.3", NULL, MMP2_SSP3,
0, 6500000, &mmp3_clk_vctcxo),
APBC_CLK("ssp.4", "mmp-ssp.4", NULL, MMP2_SSP4,
0, 6500000, &mmp3_clk_vctcxo),
/*
* Bit 7 in APBC_RTC_CLK_RST must be set before enabling
* RTC operations.
*/
APBC_CLK("rtc", "mmp-rtc", NULL, MMP2_RTC,
0x8, 32768, &mmp3_clk_32k),
APBC_CLK_OPS("pwm1", "mmp2-pwm.0", NULL, MMP2_PWM0,
0, 26000000, &mmp3_clk_vctcxo, &pwm_clk_ops),
APBC_CLK_OPS("pwm2", "mmp2-pwm.1", NULL, MMP2_PWM1,
0, 26000000, &mmp3_clk_vctcxo, &pwm_clk_ops),
APBC_CLK_OPS("pwm3", "mmp2-pwm.2", NULL, MMP2_PWM2,
0, 26000000, &mmp3_clk_vctcxo, &pwm_clk_ops),
APBC_CLK_OPS("pwm4", "mmp2-pwm.3", NULL, MMP2_PWM3,
0, 26000000, &mmp3_clk_vctcxo, &pwm_clk_ops),
APBC_CLK_OPS("uart1", "pxa2xx-uart.0", NULL, MMP2_UART1,
1, 26000000, &mmp3_clk_vctcxo, &uart_clk_ops),
APBC_CLK_OPS("uart2", "pxa2xx-uart.1", NULL, MMP2_UART2,
1, 26000000, &mmp3_clk_vctcxo, &uart_clk_ops),
APBC_CLK_OPS("uart3", "pxa2xx-uart.2", NULL, MMP2_UART3,
1, 26000000, &mmp3_clk_vctcxo, &uart_clk_ops),
APBC_CLK_OPS("uart4", "pxa2xx-uart.3", NULL, MMP2_UART4,
1, 26000000, &mmp3_clk_vctcxo, &uart_clk_ops),
APMU_CLK("u2o", NULL, "U2OCLK", USB,
0x9, 480000000, NULL),
APMU_CLK_OPS("nand", "pxa3xx-nand", NULL, NAND,
0xbf, 100000000, &mmp3_clk_pll1, &nand_clk_ops),
APMU_CLK_OPS("hsic1", NULL, "HSIC1CLK", USBHSIC1,
0x1b, 480000000, NULL, &hsic_clk_ops),
APMU_CLK_OPS("hsic2", NULL, "HSIC2CLK", USBHSIC2,
0x1b, 480000000, NULL, &hsic_clk_ops),
#ifdef CONFIG_MMP3_QSEVEN_26MHZ
APMU_CLK_OPS("fsic", NULL, "FSICCLK", USBFSIC,
0x1b, 480000000, NULL, &fsic_clk_ops),
#else
APMU_CLK("fsic", NULL, "FSICCLK", USBFSIC,
0x1b, 480000000, NULL),
#endif
};
static void mmp3_init_one_clock(struct clk *c)
{
clk_init(c);
INIT_LIST_HEAD(&c->shared_bus_list);
if (!c->lookup.dev_id && !c->lookup.con_id)
c->lookup.con_id = c->name;
c->lookup.clk = c;
clkdev_add(&c->lookup);
}
static int __init mmp3_clk_init(void)
{
int i;
for (i = 0; i < ARRAY_SIZE(mmp3_clks_ptr); i++)
mmp3_init_one_clock(mmp3_clks_ptr[i]);
for (i = 0; i < ARRAY_SIZE(mmp3_list_clks); i++)
mmp3_init_one_clock(&mmp3_list_clks[i]);
register_reboot_notifier(&devfreq_reboot_notifier);
#ifdef CONFIG_DDR_DEVFREQ
clk_set_cansleep(&mmp3_clk_ddr);
mutex_init(&disable_ddr_lock);
#endif
clk_set_cansleep(&mmp3_clk_gc);
return 0;
}
core_initcall(mmp3_clk_init);