221 lines
5.3 KiB
C
221 lines
5.3 KiB
C
|
/*
|
||
|
* Copyright (C) 2005 STMicroelectronics Limited
|
||
|
*
|
||
|
* May be copied or modified under the terms of the GNU General Public
|
||
|
* License. See linux/COPYING for more information.
|
||
|
*
|
||
|
* Code to handle the clockgen hardware on the STx7100.
|
||
|
*/
|
||
|
|
||
|
#include <linux/init.h>
|
||
|
#include <linux/kernel.h>
|
||
|
#include <linux/gfp.h>
|
||
|
#include <linux/slab.h>
|
||
|
#include <linux/stm/clk.h>
|
||
|
#include <linux/io.h>
|
||
|
#include <asm-generic/div64.h>
|
||
|
|
||
|
#include "clock-utils.h"
|
||
|
|
||
|
static void __iomem *clkgena_base;
|
||
|
|
||
|
#define CLOCKGEN_PLL0_CFG 0x08
|
||
|
#define CLOCKGEN_PLL0_CLK1_CTRL 0x14
|
||
|
#define CLOCKGEN_PLL0_CLK2_CTRL 0x18
|
||
|
#define CLOCKGEN_PLL0_CLK3_CTRL 0x1c
|
||
|
#define CLOCKGEN_PLL0_CLK4_CTRL 0x20
|
||
|
#define CLOCKGEN_PLL1_CFG 0x24
|
||
|
|
||
|
/* to enable/disable and reduce the coprocessor clock*/
|
||
|
#define CLOCKGEN_CLK_DIV 0x30
|
||
|
#define CLOCKGEN_CLK_EN 0x34
|
||
|
|
||
|
/* 0 1 2 3 4 5 6 7 */
|
||
|
static unsigned char ratio1[8] = { 1, 2, 3, 4, 6, 8, 0, 0 };
|
||
|
static unsigned char ratio2[8] = { 1, 2, 3, 4, 6, 8, 0, 0 };
|
||
|
static unsigned char ratio3[8] = { 4, 2, 4, 4, 6, 8, 0, 0 };
|
||
|
static unsigned char ratio4[8] = { 1, 2, 3, 4, 6, 8, 0, 0 };
|
||
|
|
||
|
static int pll_freq(unsigned long addr)
|
||
|
{
|
||
|
unsigned long freq, data, ndiv, pdiv, mdiv;
|
||
|
|
||
|
data = readl(clkgena_base+addr);
|
||
|
mdiv = (data >> 0) & 0xff;
|
||
|
ndiv = (data >> 8) & 0xff;
|
||
|
pdiv = (data >> 16) & 0x7;
|
||
|
freq = (((2 * (CONFIG_SH_EXTERNAL_CLOCK / 1000) * ndiv) / mdiv) /
|
||
|
(1 << pdiv)) * 1000;
|
||
|
|
||
|
return freq;
|
||
|
}
|
||
|
|
||
|
static int pll_clk_init(struct clk *clk)
|
||
|
{
|
||
|
clk->rate = pll_freq(
|
||
|
(strcmp(clk->name, "pll0_clk") ?
|
||
|
CLOCKGEN_PLL1_CFG : CLOCKGEN_PLL0_CFG));
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
static struct clk_ops pll_clk_ops = {
|
||
|
.init = pll_clk_init,
|
||
|
};
|
||
|
|
||
|
static struct clk pll_clk[] = {
|
||
|
{
|
||
|
.name = "pll0_clk",
|
||
|
.ops = &pll_clk_ops,
|
||
|
}, {
|
||
|
.name = "pll1_clk",
|
||
|
.ops = &pll_clk_ops,
|
||
|
}
|
||
|
};
|
||
|
|
||
|
struct clokgenA {
|
||
|
unsigned long ctrl_reg;
|
||
|
unsigned int div;
|
||
|
unsigned char *ratio;
|
||
|
};
|
||
|
|
||
|
|
||
|
enum clockgenA_ID {
|
||
|
SH4_CLK_ID = 0,
|
||
|
SH4IC_CLK_ID,
|
||
|
MODULE_ID,
|
||
|
SLIM_ID,
|
||
|
LX_AUD_ID,
|
||
|
LX_VID_ID,
|
||
|
LMISYS_ID,
|
||
|
LMIVID_ID,
|
||
|
IC_ID,
|
||
|
IC_100_ID,
|
||
|
EMI_ID
|
||
|
};
|
||
|
|
||
|
static int clockgenA_clk_recalc(struct clk *clk)
|
||
|
{
|
||
|
struct clokgenA *cga = (struct clokgenA *)clk->private_data;
|
||
|
clk->rate = clk->parent->rate / cga->div;
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
static int clockgenA_clk_set_rate(struct clk *clk, unsigned long value)
|
||
|
{
|
||
|
unsigned long data = readl(clkgena_base + CLOCKGEN_CLK_DIV);
|
||
|
unsigned long val = 1 << (clk->id - 5);
|
||
|
|
||
|
if (clk->id != LMISYS_ID && clk->id != LMIVID_ID)
|
||
|
return -1;
|
||
|
writel(0xc0de, clkgena_base);
|
||
|
if (clk->rate > value) {/* downscale */
|
||
|
writel(data | val, clkgena_base + CLOCKGEN_CLK_DIV);
|
||
|
clk->rate /= 1024;
|
||
|
} else {/* upscale */
|
||
|
writel(data & ~val, clkgena_base + CLOCKGEN_CLK_DIV);
|
||
|
clk->rate *= 1024;
|
||
|
}
|
||
|
writel(0x0, clkgena_base);
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
static int clockgenA_clk_init(struct clk *clk)
|
||
|
{
|
||
|
struct clokgenA *cga = (struct clokgenA *)clk->private_data;
|
||
|
if (cga->ratio) {
|
||
|
unsigned long data = readl(clkgena_base + cga->ctrl_reg) & 0x7;
|
||
|
unsigned char ratio = cga->ratio[data];
|
||
|
BUG_ON(!ratio);
|
||
|
cga->div = 2*ratio;
|
||
|
}
|
||
|
clk->rate = clk->parent->rate / cga->div;
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
static int clockgenA_clk_XXable(struct clk *clk, int enable)
|
||
|
{
|
||
|
unsigned long tmp, value;
|
||
|
struct clokgenA *cga = (struct clokgenA *)clk->private_data;
|
||
|
|
||
|
if (clk->id != LMISYS_ID && clk->id != LMIVID_ID)
|
||
|
return 0;
|
||
|
|
||
|
tmp = readl(clkgena_base+cga->ctrl_reg) ;
|
||
|
value = 1 << (clk->id - 5);
|
||
|
writel(0xc0de, clkgena_base);
|
||
|
if (enable) {
|
||
|
writel(tmp | value, clkgena_base + cga->ctrl_reg);
|
||
|
clockgenA_clk_init(clk); /* to evaluate the rate */
|
||
|
} else {
|
||
|
writel(tmp & ~value, clkgena_base + cga->ctrl_reg);
|
||
|
clk->rate = 0;
|
||
|
}
|
||
|
writel(0x0, clkgena_base);
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
static int clockgenA_clk_enable(struct clk *clk)
|
||
|
{
|
||
|
return clockgenA_clk_XXable(clk, 1);
|
||
|
}
|
||
|
|
||
|
static int clockgenA_clk_disable(struct clk *clk)
|
||
|
{
|
||
|
return clockgenA_clk_XXable(clk, 0);
|
||
|
}
|
||
|
|
||
|
static struct clk_ops clokgenA_ops = {
|
||
|
.init = clockgenA_clk_init,
|
||
|
.recalc = clockgenA_clk_recalc,
|
||
|
.set_rate = clockgenA_clk_set_rate,
|
||
|
.enable = clockgenA_clk_enable,
|
||
|
.disable = clockgenA_clk_disable,
|
||
|
};
|
||
|
|
||
|
#define CLKGENA(_id, clock, pll, _ctrl_reg, _div, _ratio) \
|
||
|
[_id] = { \
|
||
|
.name = #clock "_clk", \
|
||
|
.parent = &(pll), \
|
||
|
.ops = &clokgenA_ops, \
|
||
|
.id = (_id), \
|
||
|
.private_data = &(struct clokgenA){ \
|
||
|
.div = (_div), \
|
||
|
.ctrl_reg = (_ctrl_reg), \
|
||
|
.ratio = (_ratio) \
|
||
|
}, \
|
||
|
}
|
||
|
|
||
|
static struct clk clkgena_clks[] = {
|
||
|
CLKGENA(SH4_CLK_ID, st40, pll_clk[0], CLOCKGEN_PLL0_CLK1_CTRL, 1, ratio1),
|
||
|
CLKGENA(SH4IC_CLK_ID, st40_ic, pll_clk[0], CLOCKGEN_PLL0_CLK2_CTRL, 1, ratio2),
|
||
|
CLKGENA(MODULE_ID, st40_per, pll_clk[0], CLOCKGEN_PLL0_CLK3_CTRL, 1, ratio3),
|
||
|
CLKGENA(SLIM_ID, slim, pll_clk[0], CLOCKGEN_PLL0_CLK4_CTRL, 1, ratio4),
|
||
|
|
||
|
CLKGENA(LX_AUD_ID, st231aud, pll_clk[1], CLOCKGEN_CLK_EN, 1, NULL),
|
||
|
CLKGENA(LX_VID_ID, st231vid, pll_clk[1], CLOCKGEN_CLK_EN, 1, NULL),
|
||
|
CLKGENA(LMISYS_ID, lmisys, pll_clk[1], 0, 1, NULL),
|
||
|
CLKGENA(LMIVID_ID, lmivid, pll_clk[1], 0, 1, NULL),
|
||
|
CLKGENA(IC_ID, ic, pll_clk[1], 0, 2, NULL),
|
||
|
CLKGENA(IC_100_ID, ic_100, pll_clk[1], 0, 4, NULL),
|
||
|
CLKGENA(EMI_ID, emi, pll_clk[1], 0, 4, NULL)
|
||
|
};
|
||
|
|
||
|
int __init plat_clk_init(void)
|
||
|
{
|
||
|
int ret;
|
||
|
|
||
|
/**************/
|
||
|
/* Clockgen A */
|
||
|
/**************/
|
||
|
clkgena_base = ioremap(0x19213000, 0x100);
|
||
|
if (!clkgena_base)
|
||
|
return -ENOMEM;
|
||
|
|
||
|
ret = clk_register_table(pll_clk, ARRAY_SIZE(pll_clk), 1);
|
||
|
if (ret)
|
||
|
return ret;
|
||
|
|
||
|
ret = clk_register_table(clkgena_clks, ARRAY_SIZE(clkgena_clks), 1);
|
||
|
return ret;
|
||
|
}
|