ARM: mach-shmobile: extend clock definitions on sh7372

Add definitions for DV_CLKI and HDMI clocks, extend support for PLLC2 and some
other clocks.

Signed-off-by: Guennadi Liakhovetski <g.liakhovetski@gmx.de>
Acked-by: Magnus Damm <damm@opensource.se>
Signed-off-by: Paul Mundt <lethal@linux-sh.org>
This commit is contained in:
Guennadi Liakhovetski 2010-07-21 10:13:14 +00:00 committed by Paul Mundt
parent b3dd51a8a6
commit b90884c886
2 changed files with 172 additions and 5 deletions

View File

@ -50,6 +50,10 @@
#define SMSTPCR3 0xe615013c #define SMSTPCR3 0xe615013c
#define SMSTPCR4 0xe6150140 #define SMSTPCR4 0xe6150140
/* Platforms must set frequency on their DV_CLKI pin */
struct clk dv_clki_clk = {
};
/* Fixed 32 KHz root clock from EXTALR pin */ /* Fixed 32 KHz root clock from EXTALR pin */
static struct clk r_clk = { static struct clk r_clk = {
.rate = 32768, .rate = 32768,
@ -81,6 +85,12 @@ static struct clk_ops div2_clk_ops = {
.recalc = div2_recalc, .recalc = div2_recalc,
}; };
/* Divide dv_clki by two */
struct clk dv_clki_div2_clk = {
.ops = &div2_clk_ops,
.parent = &dv_clki_clk,
};
/* Divide extal1 by two */ /* Divide extal1 by two */
static struct clk extal1_div2_clk = { static struct clk extal1_div2_clk = {
.ops = &div2_clk_ops, .ops = &div2_clk_ops,
@ -135,30 +145,160 @@ static struct clk pllc1_div2_clk = {
}; };
/* PLLC2 */ /* PLLC2 */
/* Indices are important - they are the actual src selecting values */
static struct clk *pllc2_parent[] = {
[0] = &extal1_div2_clk,
[1] = &extal2_div2_clk,
[2] = &dv_clki_div2_clk,
};
/* Only multipliers 20 * 2 to 46 * 2 are valid, last entry for CPUFREQ_TABLE_END */
static struct cpufreq_frequency_table pllc2_freq_table[29];
static void pllc2_table_rebuild(struct clk *clk)
{
int i;
/* Initialise PLLC2 frequency table */
for (i = 0; i < ARRAY_SIZE(pllc2_freq_table) - 2; i++) {
pllc2_freq_table[i].frequency = clk->parent->rate * (i + 20) * 2;
pllc2_freq_table[i].index = i;
}
/* This is a special entry - switching PLL off makes it a repeater */
pllc2_freq_table[i].frequency = clk->parent->rate;
pllc2_freq_table[i].index = i;
pllc2_freq_table[++i].frequency = CPUFREQ_TABLE_END;
pllc2_freq_table[i].index = i;
}
static unsigned long pllc2_recalc(struct clk *clk) static unsigned long pllc2_recalc(struct clk *clk)
{ {
unsigned long mult = 1; unsigned long mult = 1;
pllc2_table_rebuild(clk);
/*
* If the PLL is off, mult == 1, clk->rate will be updated in
* pllc2_enable().
*/
if (__raw_readl(PLLC2CR) & (1 << 31)) if (__raw_readl(PLLC2CR) & (1 << 31))
mult = (((__raw_readl(PLLC2CR) >> 24) & 0x3f) + 1) * 2; mult = (((__raw_readl(PLLC2CR) >> 24) & 0x3f) + 1) * 2;
return clk->parent->rate * mult; return clk->parent->rate * mult;
} }
static long pllc2_round_rate(struct clk *clk, unsigned long rate)
{
return clk_rate_table_round(clk, clk->freq_table, rate);
}
static int pllc2_enable(struct clk *clk)
{
int i;
__raw_writel(__raw_readl(PLLC2CR) | 0x80000000, PLLC2CR);
for (i = 0; i < 100; i++)
if (__raw_readl(PLLC2CR) & 0x80000000) {
clk->rate = pllc2_recalc(clk);
return 0;
}
pr_err("%s(): timeout!\n", __func__);
return -ETIMEDOUT;
}
static void pllc2_disable(struct clk *clk)
{
__raw_writel(__raw_readl(PLLC2CR) & ~0x80000000, PLLC2CR);
}
static int pllc2_set_rate(struct clk *clk,
unsigned long rate, int algo_id)
{
unsigned long value;
int idx;
idx = clk_rate_table_find(clk, clk->freq_table, rate);
if (idx < 0)
return idx;
if (rate == clk->parent->rate) {
pllc2_disable(clk);
return 0;
}
value = __raw_readl(PLLC2CR) & ~(0x3f << 24);
if (value & 0x80000000)
pllc2_disable(clk);
__raw_writel((value & ~0x80000000) | ((idx + 19) << 24), PLLC2CR);
if (value & 0x80000000)
return pllc2_enable(clk);
return 0;
}
static int pllc2_set_parent(struct clk *clk, struct clk *parent)
{
u32 value;
int ret, i;
if (!clk->parent_table || !clk->parent_num)
return -EINVAL;
/* Search the parent */
for (i = 0; i < clk->parent_num; i++)
if (clk->parent_table[i] == parent)
break;
if (i == clk->parent_num)
return -ENODEV;
ret = clk_reparent(clk, parent);
if (ret < 0)
return ret;
value = __raw_readl(PLLC2CR) & ~(3 << 6);
__raw_writel(value | (i << 6), PLLC2CR);
/* Rebiuld the frequency table */
pllc2_table_rebuild(clk);
return 0;
}
static struct clk_ops pllc2_clk_ops = { static struct clk_ops pllc2_clk_ops = {
.recalc = pllc2_recalc, .recalc = pllc2_recalc,
.round_rate = pllc2_round_rate,
.set_rate = pllc2_set_rate,
.enable = pllc2_enable,
.disable = pllc2_disable,
.set_parent = pllc2_set_parent,
}; };
static struct clk pllc2_clk = { struct clk pllc2_clk = {
.ops = &pllc2_clk_ops, .ops = &pllc2_clk_ops,
.flags = CLK_ENABLE_ON_INIT, .flags = CLK_ENABLE_ON_INIT,
.parent = &extal1_div2_clk, .parent = &extal1_div2_clk,
.freq_table = pllc2_freq_table,
.parent_table = pllc2_parent,
.parent_num = ARRAY_SIZE(pllc2_parent),
}; };
static struct clk *main_clks[] = { static struct clk *main_clks[] = {
&dv_clki_clk,
&r_clk, &r_clk,
&sh7372_extal1_clk, &sh7372_extal1_clk,
&sh7372_extal2_clk, &sh7372_extal2_clk,
&dv_clki_div2_clk,
&extal1_div2_clk, &extal1_div2_clk,
&extal2_div2_clk, &extal2_div2_clk,
&extal2_div4_clk, &extal2_div4_clk,
@ -219,7 +359,7 @@ static struct clk div4_clks[DIV4_NR] = {
enum { DIV6_VCK1, DIV6_VCK2, DIV6_VCK3, DIV6_FMSI, DIV6_FMSO, enum { DIV6_VCK1, DIV6_VCK2, DIV6_VCK3, DIV6_FMSI, DIV6_FMSO,
DIV6_FSIA, DIV6_FSIB, DIV6_SUB, DIV6_SPU, DIV6_FSIA, DIV6_FSIB, DIV6_SUB, DIV6_SPU,
DIV6_VOU, DIV6_HDMI, DIV6_DSIT, DIV6_DSI0P, DIV6_DSI1P, DIV6_VOU, DIV6_DSIT, DIV6_DSI0P, DIV6_DSI1P,
DIV6_NR }; DIV6_NR };
static struct clk div6_clks[DIV6_NR] = { static struct clk div6_clks[DIV6_NR] = {
@ -233,12 +373,26 @@ static struct clk div6_clks[DIV6_NR] = {
[DIV6_SUB] = SH_CLK_DIV6(&sh7372_extal2_clk, SUBCKCR, 0), [DIV6_SUB] = SH_CLK_DIV6(&sh7372_extal2_clk, SUBCKCR, 0),
[DIV6_SPU] = SH_CLK_DIV6(&pllc1_div2_clk, SPUCKCR, 0), [DIV6_SPU] = SH_CLK_DIV6(&pllc1_div2_clk, SPUCKCR, 0),
[DIV6_VOU] = SH_CLK_DIV6(&pllc1_div2_clk, VOUCKCR, 0), [DIV6_VOU] = SH_CLK_DIV6(&pllc1_div2_clk, VOUCKCR, 0),
[DIV6_HDMI] = SH_CLK_DIV6(&pllc1_div2_clk, HDMICKCR, 0),
[DIV6_DSIT] = SH_CLK_DIV6(&pllc1_div2_clk, DSITCKCR, 0), [DIV6_DSIT] = SH_CLK_DIV6(&pllc1_div2_clk, DSITCKCR, 0),
[DIV6_DSI0P] = SH_CLK_DIV6(&pllc1_div2_clk, DSI0PCKCR, 0), [DIV6_DSI0P] = SH_CLK_DIV6(&pllc1_div2_clk, DSI0PCKCR, 0),
[DIV6_DSI1P] = SH_CLK_DIV6(&pllc1_div2_clk, DSI1PCKCR, 0), [DIV6_DSI1P] = SH_CLK_DIV6(&pllc1_div2_clk, DSI1PCKCR, 0),
}; };
enum { DIV6_HDMI, DIV6_REPARENT_NR };
/* Indices are important - they are the actual src selecting values */
static struct clk *hdmi_parent[] = {
[0] = &pllc1_div2_clk,
[1] = &pllc2_clk,
[2] = &dv_clki_clk,
[3] = NULL, /* pllc2_div4 not implemented yet */
};
static struct clk div6_reparent_clks[DIV6_REPARENT_NR] = {
[DIV6_HDMI] = SH_CLK_DIV6_EXT(&pllc1_div2_clk, HDMICKCR, 0,
hdmi_parent, ARRAY_SIZE(hdmi_parent), 6, 2),
};
enum { MSTP001, enum { MSTP001,
MSTP131, MSTP130, MSTP131, MSTP130,
MSTP129, MSTP128, MSTP129, MSTP128,
@ -247,7 +401,7 @@ enum { MSTP001,
MSTP223, MSTP223,
MSTP207, MSTP206, MSTP204, MSTP203, MSTP202, MSTP201, MSTP200, MSTP207, MSTP206, MSTP204, MSTP203, MSTP202, MSTP201, MSTP200,
MSTP329, MSTP328, MSTP323, MSTP322, MSTP314, MSTP313, MSTP312, MSTP329, MSTP328, MSTP323, MSTP322, MSTP314, MSTP313, MSTP312,
MSTP415, MSTP410, MSTP411, MSTP406, MSTP403, MSTP415, MSTP413, MSTP411, MSTP410, MSTP406, MSTP403,
MSTP_NR }; MSTP_NR };
#define MSTP(_parent, _reg, _bit, _flags) \ #define MSTP(_parent, _reg, _bit, _flags) \
@ -281,6 +435,7 @@ static struct clk mstp_clks[MSTP_NR] = {
[MSTP313] = MSTP(&div4_clks[DIV4_HP], SMSTPCR3, 13, 0), /* SDHI1 */ [MSTP313] = MSTP(&div4_clks[DIV4_HP], SMSTPCR3, 13, 0), /* SDHI1 */
[MSTP312] = MSTP(&div4_clks[DIV4_HP], SMSTPCR3, 12, 0), /* MMC */ [MSTP312] = MSTP(&div4_clks[DIV4_HP], SMSTPCR3, 12, 0), /* MMC */
[MSTP415] = MSTP(&div4_clks[DIV4_HP], SMSTPCR4, 15, 0), /* SDHI2 */ [MSTP415] = MSTP(&div4_clks[DIV4_HP], SMSTPCR4, 15, 0), /* SDHI2 */
[MSTP413] = MSTP(&pllc1_div2_clk, SMSTPCR4, 13, 0), /* HDMI */
[MSTP411] = MSTP(&div6_clks[DIV6_SUB], SMSTPCR4, 11, 0), /* IIC3 */ [MSTP411] = MSTP(&div6_clks[DIV6_SUB], SMSTPCR4, 11, 0), /* IIC3 */
[MSTP410] = MSTP(&div6_clks[DIV6_SUB], SMSTPCR4, 10, 0), /* IIC4 */ [MSTP410] = MSTP(&div6_clks[DIV6_SUB], SMSTPCR4, 10, 0), /* IIC4 */
[MSTP406] = MSTP(&div6_clks[DIV6_SUB], SMSTPCR4, 6, 0), /* USB1 */ [MSTP406] = MSTP(&div6_clks[DIV6_SUB], SMSTPCR4, 6, 0), /* USB1 */
@ -292,6 +447,7 @@ static struct clk mstp_clks[MSTP_NR] = {
static struct clk_lookup lookups[] = { static struct clk_lookup lookups[] = {
/* main clocks */ /* main clocks */
CLKDEV_CON_ID("dv_clki_div2_clk", &dv_clki_div2_clk),
CLKDEV_CON_ID("r_clk", &r_clk), CLKDEV_CON_ID("r_clk", &r_clk),
CLKDEV_CON_ID("extal1", &sh7372_extal1_clk), CLKDEV_CON_ID("extal1", &sh7372_extal1_clk),
CLKDEV_CON_ID("extal2", &sh7372_extal2_clk), CLKDEV_CON_ID("extal2", &sh7372_extal2_clk),
@ -331,7 +487,7 @@ static struct clk_lookup lookups[] = {
CLKDEV_CON_ID("sub_clk", &div6_clks[DIV6_SUB]), CLKDEV_CON_ID("sub_clk", &div6_clks[DIV6_SUB]),
CLKDEV_CON_ID("spu_clk", &div6_clks[DIV6_SPU]), CLKDEV_CON_ID("spu_clk", &div6_clks[DIV6_SPU]),
CLKDEV_CON_ID("vou_clk", &div6_clks[DIV6_VOU]), CLKDEV_CON_ID("vou_clk", &div6_clks[DIV6_VOU]),
CLKDEV_CON_ID("hdmi_clk", &div6_clks[DIV6_HDMI]), CLKDEV_CON_ID("hdmi_clk", &div6_reparent_clks[DIV6_HDMI]),
CLKDEV_CON_ID("dsit_clk", &div6_clks[DIV6_DSIT]), CLKDEV_CON_ID("dsit_clk", &div6_clks[DIV6_DSIT]),
CLKDEV_CON_ID("dsi0p_clk", &div6_clks[DIV6_DSI0P]), CLKDEV_CON_ID("dsi0p_clk", &div6_clks[DIV6_DSI0P]),
CLKDEV_CON_ID("dsi1p_clk", &div6_clks[DIV6_DSI1P]), CLKDEV_CON_ID("dsi1p_clk", &div6_clks[DIV6_DSI1P]),
@ -366,11 +522,13 @@ static struct clk_lookup lookups[] = {
CLKDEV_DEV_ID("sh_mobile_sdhi.1", &mstp_clks[MSTP313]), /* SDHI1 */ CLKDEV_DEV_ID("sh_mobile_sdhi.1", &mstp_clks[MSTP313]), /* SDHI1 */
CLKDEV_DEV_ID("sh_mmcif.0", &mstp_clks[MSTP312]), /* MMC */ CLKDEV_DEV_ID("sh_mmcif.0", &mstp_clks[MSTP312]), /* MMC */
CLKDEV_DEV_ID("sh_mobile_sdhi.2", &mstp_clks[MSTP415]), /* SDHI2 */ CLKDEV_DEV_ID("sh_mobile_sdhi.2", &mstp_clks[MSTP415]), /* SDHI2 */
CLKDEV_DEV_ID("sh-mobile-hdmi", &mstp_clks[MSTP413]), /* HDMI */
CLKDEV_DEV_ID("i2c-sh_mobile.3", &mstp_clks[MSTP411]), /* IIC3 */ CLKDEV_DEV_ID("i2c-sh_mobile.3", &mstp_clks[MSTP411]), /* IIC3 */
CLKDEV_DEV_ID("i2c-sh_mobile.4", &mstp_clks[MSTP410]), /* IIC4 */ CLKDEV_DEV_ID("i2c-sh_mobile.4", &mstp_clks[MSTP410]), /* IIC4 */
CLKDEV_DEV_ID("r8a66597_hcd.1", &mstp_clks[MSTP406]), /* USB1 */ CLKDEV_DEV_ID("r8a66597_hcd.1", &mstp_clks[MSTP406]), /* USB1 */
CLKDEV_DEV_ID("r8a66597_udc.1", &mstp_clks[MSTP406]), /* USB1 */ CLKDEV_DEV_ID("r8a66597_udc.1", &mstp_clks[MSTP406]), /* USB1 */
CLKDEV_DEV_ID("sh_keysc.0", &mstp_clks[MSTP403]), /* KEYSC */ CLKDEV_DEV_ID("sh_keysc.0", &mstp_clks[MSTP403]), /* KEYSC */
{.con_id = "ick", .dev_id = "sh-mobile-hdmi", .clk = &div6_reparent_clks[DIV6_HDMI]},
}; };
void __init sh7372_clock_init(void) void __init sh7372_clock_init(void)
@ -386,6 +544,9 @@ void __init sh7372_clock_init(void)
if (!ret) if (!ret)
ret = sh_clk_div6_register(div6_clks, DIV6_NR); ret = sh_clk_div6_register(div6_clks, DIV6_NR);
if (!ret)
ret = sh_clk_div6_reparent_register(div6_reparent_clks, DIV6_NR);
if (!ret) if (!ret)
ret = sh_clk_mstp32_register(mstp_clks, MSTP_NR); ret = sh_clk_mstp32_register(mstp_clks, MSTP_NR);

View File

@ -11,6 +11,8 @@
#ifndef __ASM_SH7372_H__ #ifndef __ASM_SH7372_H__
#define __ASM_SH7372_H__ #define __ASM_SH7372_H__
#include <linux/sh_clk.h>
/* /*
* Pin Function Controller: * Pin Function Controller:
* GPIO_FN_xx - GPIO used to select pin function * GPIO_FN_xx - GPIO used to select pin function
@ -455,4 +457,8 @@ enum {
SHDMA_SLAVE_SDHI2_TX, SHDMA_SLAVE_SDHI2_TX,
}; };
extern struct clk dv_clki_clk;
extern struct clk dv_clki_div2_clk;
extern struct clk pllc2_clk;
#endif /* __ASM_SH7372_H__ */ #endif /* __ASM_SH7372_H__ */