OpenCloudOS-Kernel/arch/arm/plat-stmp3xxx/clock.c

1135 lines
24 KiB
C

/*
* Clock manipulation routines for Freescale STMP37XX/STMP378X
*
* Author: Vitaly Wool <vital@embeddedalley.com>
*
* Copyright 2008 Freescale Semiconductor, Inc. All Rights Reserved.
* Copyright 2008 Embedded Alley Solutions, Inc All Rights Reserved.
*/
/*
* The code contained herein is licensed under the GNU General Public
* License. You may obtain a copy of the GNU General Public License
* Version 2 or later at the following locations:
*
* http://www.opensource.org/licenses/gpl-license.html
* http://www.gnu.org/copyleft/gpl.html
*/
#define DEBUG
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/clk.h>
#include <linux/spinlock.h>
#include <linux/errno.h>
#include <linux/err.h>
#include <linux/delay.h>
#include <linux/io.h>
#include <asm/mach-types.h>
#include <asm/clkdev.h>
#include <mach/platform.h>
#include <mach/regs-clkctrl.h>
#include "clock.h"
static DEFINE_SPINLOCK(clocks_lock);
static struct clk osc_24M;
static struct clk pll_clk;
static struct clk cpu_clk;
static struct clk hclk;
static int propagate_rate(struct clk *);
static inline int clk_is_busy(struct clk *clk)
{
return __raw_readl(clk->busy_reg) & (1 << clk->busy_bit);
}
static inline int clk_good(struct clk *clk)
{
return clk && !IS_ERR(clk) && clk->ops;
}
static int std_clk_enable(struct clk *clk)
{
if (clk->enable_reg) {
u32 clk_reg = __raw_readl(clk->enable_reg);
if (clk->enable_negate)
clk_reg &= ~(1 << clk->enable_shift);
else
clk_reg |= (1 << clk->enable_shift);
__raw_writel(clk_reg, clk->enable_reg);
if (clk->enable_wait)
udelay(clk->enable_wait);
return 0;
} else
return -EINVAL;
}
static int std_clk_disable(struct clk *clk)
{
if (clk->enable_reg) {
u32 clk_reg = __raw_readl(clk->enable_reg);
if (clk->enable_negate)
clk_reg |= (1 << clk->enable_shift);
else
clk_reg &= ~(1 << clk->enable_shift);
__raw_writel(clk_reg, clk->enable_reg);
return 0;
} else
return -EINVAL;
}
static int io_set_rate(struct clk *clk, u32 rate)
{
u32 reg_frac, clkctrl_frac;
int i, ret = 0, mask = 0x1f;
clkctrl_frac = (clk->parent->rate * 18 + rate - 1) / rate;
if (clkctrl_frac < 18 || clkctrl_frac > 35) {
ret = -EINVAL;
goto out;
}
reg_frac = __raw_readl(clk->scale_reg);
reg_frac &= ~(mask << clk->scale_shift);
__raw_writel(reg_frac | (clkctrl_frac << clk->scale_shift),
clk->scale_reg);
if (clk->busy_reg) {
for (i = 10000; i; i--)
if (!clk_is_busy(clk))
break;
if (!i)
ret = -ETIMEDOUT;
else
ret = 0;
}
out:
return ret;
}
static long io_get_rate(struct clk *clk)
{
long rate = clk->parent->rate * 18;
int mask = 0x1f;
rate /= (__raw_readl(clk->scale_reg) >> clk->scale_shift) & mask;
clk->rate = rate;
return rate;
}
static long per_get_rate(struct clk *clk)
{
long rate = clk->parent->rate;
long div;
const int mask = 0xff;
if (clk->enable_reg &&
!(__raw_readl(clk->enable_reg) & clk->enable_shift))
clk->rate = 0;
else {
div = (__raw_readl(clk->scale_reg) >> clk->scale_shift) & mask;
if (div)
rate /= div;
clk->rate = rate;
}
return clk->rate;
}
static int per_set_rate(struct clk *clk, u32 rate)
{
int ret = -EINVAL;
int div = (clk->parent->rate + rate - 1) / rate;
u32 reg_frac;
const int mask = 0xff;
int try = 10;
int i = -1;
if (div == 0 || div > mask)
goto out;
reg_frac = __raw_readl(clk->scale_reg);
reg_frac &= ~(mask << clk->scale_shift);
while (try--) {
__raw_writel(reg_frac | (div << clk->scale_shift),
clk->scale_reg);
if (clk->busy_reg) {
for (i = 10000; i; i--)
if (!clk_is_busy(clk))
break;
}
if (i)
break;
}
if (!i)
ret = -ETIMEDOUT;
else
ret = 0;
out:
if (ret != 0)
printk(KERN_ERR "%s: error %d\n", __func__, ret);
return ret;
}
static long lcdif_get_rate(struct clk *clk)
{
long rate = clk->parent->rate;
long div;
const int mask = 0xff;
div = (__raw_readl(clk->scale_reg) >> clk->scale_shift) & mask;
if (div) {
rate /= div;
div = (__raw_readl(REGS_CLKCTRL_BASE + HW_CLKCTRL_FRAC) &
BM_CLKCTRL_FRAC_PIXFRAC) >> BP_CLKCTRL_FRAC_PIXFRAC;
rate /= div;
}
clk->rate = rate;
return rate;
}
static int lcdif_set_rate(struct clk *clk, u32 rate)
{
int ret = 0;
/*
* On 3700, we can get most timings exact by modifying ref_pix
* and the divider, but keeping the phase timings at 1 (2
* phases per cycle).
*
* ref_pix can be between 480e6*18/35=246.9MHz and 480e6*18/18=480MHz,
* which is between 18/(18*480e6)=2.084ns and 35/(18*480e6)=4.050ns.
*
* ns_cycle >= 2*18e3/(18*480) = 25/6
* ns_cycle <= 2*35e3/(18*480) = 875/108
*
* Multiply the ns_cycle by 'div' to lengthen it until it fits the
* bounds. This is the divider we'll use after ref_pix.
*
* 6 * ns_cycle >= 25 * div
* 108 * ns_cycle <= 875 * div
*/
u32 ns_cycle = 1000000 / rate;
u32 div, reg_val;
u32 lowest_result = (u32) -1;
u32 lowest_div = 0, lowest_fracdiv = 0;
for (div = 1; div < 256; ++div) {
u32 fracdiv;
u32 ps_result;
int lower_bound = 6 * ns_cycle >= 25 * div;
int upper_bound = 108 * ns_cycle <= 875 * div;
if (!lower_bound)
break;
if (!upper_bound)
continue;
/*
* Found a matching div. Calculate fractional divider needed,
* rounded up.
*/
fracdiv = ((clk->parent->rate / 1000 * 18 / 2) *
ns_cycle + 1000 * div - 1) /
(1000 * div);
if (fracdiv < 18 || fracdiv > 35) {
ret = -EINVAL;
goto out;
}
/* Calculate the actual cycle time this results in */
ps_result = 6250 * div * fracdiv / 27;
/* Use the fastest result that doesn't break ns_cycle */
if (ps_result <= lowest_result) {
lowest_result = ps_result;
lowest_div = div;
lowest_fracdiv = fracdiv;
}
}
if (div >= 256 || lowest_result == (u32) -1) {
ret = -EINVAL;
goto out;
}
pr_debug("Programming PFD=%u,DIV=%u ref_pix=%uMHz "
"PIXCLK=%uMHz cycle=%u.%03uns\n",
lowest_fracdiv, lowest_div,
480*18/lowest_fracdiv, 480*18/lowest_fracdiv/lowest_div,
lowest_result / 1000, lowest_result % 1000);
/* Program ref_pix phase fractional divider */
reg_val = __raw_readl(REGS_CLKCTRL_BASE + HW_CLKCTRL_FRAC);
reg_val &= ~BM_CLKCTRL_FRAC_PIXFRAC;
reg_val |= BF(lowest_fracdiv, CLKCTRL_FRAC_PIXFRAC);
__raw_writel(reg_val, REGS_CLKCTRL_BASE + HW_CLKCTRL_FRAC);
/* Ungate PFD */
stmp3xxx_clearl(BM_CLKCTRL_FRAC_CLKGATEPIX,
REGS_CLKCTRL_BASE + HW_CLKCTRL_FRAC);
/* Program pix divider */
reg_val = __raw_readl(clk->scale_reg);
reg_val &= ~(BM_CLKCTRL_PIX_DIV | BM_CLKCTRL_PIX_CLKGATE);
reg_val |= BF(lowest_div, CLKCTRL_PIX_DIV);
__raw_writel(reg_val, clk->scale_reg);
/* Wait for divider update */
if (clk->busy_reg) {
int i;
for (i = 10000; i; i--)
if (!clk_is_busy(clk))
break;
if (!i) {
ret = -ETIMEDOUT;
goto out;
}
}
/* Switch to ref_pix source */
reg_val = __raw_readl(REGS_CLKCTRL_BASE + HW_CLKCTRL_CLKSEQ);
reg_val &= ~BM_CLKCTRL_CLKSEQ_BYPASS_PIX;
__raw_writel(reg_val, REGS_CLKCTRL_BASE + HW_CLKCTRL_CLKSEQ);
out:
return ret;
}
static int cpu_set_rate(struct clk *clk, u32 rate)
{
u32 reg_val;
if (rate < 24000)
return -EINVAL;
else if (rate == 24000) {
/* switch to the 24M source */
clk_set_parent(clk, &osc_24M);
} else {
int i;
u32 clkctrl_cpu = 1;
u32 c = clkctrl_cpu;
u32 clkctrl_frac = 1;
u32 val;
for ( ; c < 0x40; c++) {
u32 f = (pll_clk.rate*18/c + rate/2) / rate;
int s1, s2;
if (f < 18 || f > 35)
continue;
s1 = pll_clk.rate*18/clkctrl_frac/clkctrl_cpu - rate;
s2 = pll_clk.rate*18/c/f - rate;
pr_debug("%s: s1 %d, s2 %d\n", __func__, s1, s2);
if (abs(s1) > abs(s2)) {
clkctrl_cpu = c;
clkctrl_frac = f;
}
if (s2 == 0)
break;
};
pr_debug("%s: clkctrl_cpu %d, clkctrl_frac %d\n", __func__,
clkctrl_cpu, clkctrl_frac);
if (c == 0x40) {
int d = pll_clk.rate*18/clkctrl_frac/clkctrl_cpu -
rate;
if (abs(d) > 100 ||
clkctrl_frac < 18 || clkctrl_frac > 35)
return -EINVAL;
}
/* 4.6.2 */
val = __raw_readl(clk->scale_reg);
val &= ~(0x3f << clk->scale_shift);
val |= clkctrl_frac;
clk_set_parent(clk, &osc_24M);
udelay(10);
__raw_writel(val, clk->scale_reg);
/* ungate */
__raw_writel(1<<7, clk->scale_reg + 8);
/* write clkctrl_cpu */
clk->saved_div = clkctrl_cpu;
reg_val = __raw_readl(REGS_CLKCTRL_BASE + HW_CLKCTRL_CPU);
reg_val &= ~0x3F;
reg_val |= clkctrl_cpu;
__raw_writel(reg_val, REGS_CLKCTRL_BASE + HW_CLKCTRL_CPU);
for (i = 10000; i; i--)
if (!clk_is_busy(clk))
break;
if (!i) {
printk(KERN_ERR "couldn't set up CPU divisor\n");
return -ETIMEDOUT;
}
clk_set_parent(clk, &pll_clk);
clk->saved_div = 0;
udelay(10);
}
return 0;
}
static long cpu_get_rate(struct clk *clk)
{
long rate = clk->parent->rate * 18;
rate /= (__raw_readl(clk->scale_reg) >> clk->scale_shift) & 0x3f;
rate /= __raw_readl(REGS_CLKCTRL_BASE + HW_CLKCTRL_CPU) & 0x3f;
rate = ((rate + 9) / 10) * 10;
clk->rate = rate;
return rate;
}
static long cpu_round_rate(struct clk *clk, u32 rate)
{
unsigned long r = 0;
if (rate <= 24000)
r = 24000;
else {
u32 clkctrl_cpu = 1;
u32 clkctrl_frac;
do {
clkctrl_frac =
(pll_clk.rate*18 / clkctrl_cpu + rate/2) / rate;
if (clkctrl_frac > 35)
continue;
if (pll_clk.rate*18 / clkctrl_frac / clkctrl_cpu/10 ==
rate / 10)
break;
} while (pll_clk.rate / 2 >= clkctrl_cpu++ * rate);
if (pll_clk.rate / 2 < (clkctrl_cpu - 1) * rate)
clkctrl_cpu--;
pr_debug("%s: clkctrl_cpu %d, clkctrl_frac %d\n", __func__,
clkctrl_cpu, clkctrl_frac);
if (clkctrl_frac < 18)
clkctrl_frac = 18;
if (clkctrl_frac > 35)
clkctrl_frac = 35;
r = pll_clk.rate * 18;
r /= clkctrl_frac;
r /= clkctrl_cpu;
r = 10 * ((r + 9) / 10);
}
return r;
}
static long emi_get_rate(struct clk *clk)
{
long rate = clk->parent->rate * 18;
rate /= (__raw_readl(clk->scale_reg) >> clk->scale_shift) & 0x3f;
rate /= __raw_readl(REGS_CLKCTRL_BASE + HW_CLKCTRL_EMI) & 0x3f;
clk->rate = rate;
return rate;
}
static int clkseq_set_parent(struct clk *clk, struct clk *parent)
{
int ret = -EINVAL;
int shift = 8;
/* bypass? */
if (parent == &osc_24M)
shift = 4;
if (clk->bypass_reg) {
#ifdef CONFIG_ARCH_STMP378X
u32 hbus_val, cpu_val;
if (clk == &cpu_clk && shift == 4) {
hbus_val = __raw_readl(REGS_CLKCTRL_BASE +
HW_CLKCTRL_HBUS);
cpu_val = __raw_readl(REGS_CLKCTRL_BASE +
HW_CLKCTRL_CPU);
hbus_val &= ~(BM_CLKCTRL_HBUS_DIV_FRAC_EN |
BM_CLKCTRL_HBUS_DIV);
clk->saved_div = cpu_val & BM_CLKCTRL_CPU_DIV_CPU;
cpu_val &= ~BM_CLKCTRL_CPU_DIV_CPU;
cpu_val |= 1;
if (machine_is_stmp378x()) {
__raw_writel(hbus_val,
REGS_CLKCTRL_BASE + HW_CLKCTRL_HBUS);
__raw_writel(cpu_val,
REGS_CLKCTRL_BASE + HW_CLKCTRL_CPU);
hclk.rate = 0;
}
} else if (clk == &cpu_clk && shift == 8) {
hbus_val = __raw_readl(REGS_CLKCTRL_BASE +
HW_CLKCTRL_HBUS);
cpu_val = __raw_readl(REGS_CLKCTRL_BASE +
HW_CLKCTRL_CPU);
hbus_val &= ~(BM_CLKCTRL_HBUS_DIV_FRAC_EN |
BM_CLKCTRL_HBUS_DIV);
hbus_val |= 2;
cpu_val &= ~BM_CLKCTRL_CPU_DIV_CPU;
if (clk->saved_div)
cpu_val |= clk->saved_div;
else
cpu_val |= 2;
if (machine_is_stmp378x()) {
__raw_writel(hbus_val,
REGS_CLKCTRL_BASE + HW_CLKCTRL_HBUS);
__raw_writel(cpu_val,
REGS_CLKCTRL_BASE + HW_CLKCTRL_CPU);
hclk.rate = 0;
}
}
#endif
__raw_writel(1 << clk->bypass_shift, clk->bypass_reg + shift);
ret = 0;
}
return ret;
}
static int hbus_set_rate(struct clk *clk, u32 rate)
{
u8 div = 0;
int is_frac = 0;
u32 clkctrl_hbus;
struct clk *parent = clk->parent;
pr_debug("%s: rate %d, parent rate %d\n", __func__, rate,
parent->rate);
if (rate > parent->rate)
return -EINVAL;
if (((parent->rate + rate/2) / rate) * rate != parent->rate &&
parent->rate / rate < 32) {
pr_debug("%s: switching to fractional mode\n", __func__);
is_frac = 1;
}
if (is_frac)
div = (32 * rate + parent->rate / 2) / parent->rate;
else
div = (parent->rate + rate - 1) / rate;
pr_debug("%s: div calculated is %d\n", __func__, div);
if (!div || div > 0x1f)
return -EINVAL;
clk_set_parent(&cpu_clk, &osc_24M);
udelay(10);
clkctrl_hbus = __raw_readl(clk->scale_reg);
clkctrl_hbus &= ~0x3f;
clkctrl_hbus |= div;
clkctrl_hbus |= (is_frac << 5);
__raw_writel(clkctrl_hbus, clk->scale_reg);
if (clk->busy_reg) {
int i;
for (i = 10000; i; i--)
if (!clk_is_busy(clk))
break;
if (!i) {
printk(KERN_ERR "couldn't set up CPU divisor\n");
return -ETIMEDOUT;
}
}
clk_set_parent(&cpu_clk, &pll_clk);
__raw_writel(clkctrl_hbus, clk->scale_reg);
udelay(10);
return 0;
}
static long hbus_get_rate(struct clk *clk)
{
long rate = clk->parent->rate;
if (__raw_readl(clk->scale_reg) & 0x20) {
rate *= __raw_readl(clk->scale_reg) & 0x1f;
rate /= 32;
} else
rate /= __raw_readl(clk->scale_reg) & 0x1f;
clk->rate = rate;
return rate;
}
static int xbus_set_rate(struct clk *clk, u32 rate)
{
u16 div = 0;
u32 clkctrl_xbus;
pr_debug("%s: rate %d, parent rate %d\n", __func__, rate,
clk->parent->rate);
div = (clk->parent->rate + rate - 1) / rate;
pr_debug("%s: div calculated is %d\n", __func__, div);
if (!div || div > 0x3ff)
return -EINVAL;
clkctrl_xbus = __raw_readl(clk->scale_reg);
clkctrl_xbus &= ~0x3ff;
clkctrl_xbus |= div;
__raw_writel(clkctrl_xbus, clk->scale_reg);
if (clk->busy_reg) {
int i;
for (i = 10000; i; i--)
if (!clk_is_busy(clk))
break;
if (!i) {
printk(KERN_ERR "couldn't set up xbus divisor\n");
return -ETIMEDOUT;
}
}
return 0;
}
static long xbus_get_rate(struct clk *clk)
{
long rate = clk->parent->rate;
rate /= __raw_readl(clk->scale_reg) & 0x3ff;
clk->rate = rate;
return rate;
}
/* Clock ops */
static struct clk_ops std_ops = {
.enable = std_clk_enable,
.disable = std_clk_disable,
.get_rate = per_get_rate,
.set_rate = per_set_rate,
.set_parent = clkseq_set_parent,
};
static struct clk_ops min_ops = {
.enable = std_clk_enable,
.disable = std_clk_disable,
};
static struct clk_ops cpu_ops = {
.enable = std_clk_enable,
.disable = std_clk_disable,
.get_rate = cpu_get_rate,
.set_rate = cpu_set_rate,
.round_rate = cpu_round_rate,
.set_parent = clkseq_set_parent,
};
static struct clk_ops io_ops = {
.enable = std_clk_enable,
.disable = std_clk_disable,
.get_rate = io_get_rate,
.set_rate = io_set_rate,
};
static struct clk_ops hbus_ops = {
.get_rate = hbus_get_rate,
.set_rate = hbus_set_rate,
};
static struct clk_ops xbus_ops = {
.get_rate = xbus_get_rate,
.set_rate = xbus_set_rate,
};
static struct clk_ops lcdif_ops = {
.enable = std_clk_enable,
.disable = std_clk_disable,
.get_rate = lcdif_get_rate,
.set_rate = lcdif_set_rate,
.set_parent = clkseq_set_parent,
};
static struct clk_ops emi_ops = {
.get_rate = emi_get_rate,
};
/* List of on-chip clocks */
static struct clk osc_24M = {
.flags = FIXED_RATE | ENABLED,
.rate = 24000,
};
static struct clk pll_clk = {
.parent = &osc_24M,
.enable_reg = REGS_CLKCTRL_BASE + HW_CLKCTRL_PLLCTRL0,
.enable_shift = 16,
.enable_wait = 10,
.flags = FIXED_RATE | ENABLED,
.rate = 480000,
.ops = &min_ops,
};
static struct clk cpu_clk = {
.parent = &pll_clk,
.scale_reg = REGS_CLKCTRL_BASE + HW_CLKCTRL_FRAC,
.scale_shift = 0,
.bypass_reg = REGS_CLKCTRL_BASE + HW_CLKCTRL_CLKSEQ,
.bypass_shift = 7,
.busy_reg = REGS_CLKCTRL_BASE + HW_CLKCTRL_CPU,
.busy_bit = 28,
.flags = RATE_PROPAGATES | ENABLED,
.ops = &cpu_ops,
};
static struct clk io_clk = {
.parent = &pll_clk,
.enable_reg = REGS_CLKCTRL_BASE + HW_CLKCTRL_FRAC,
.enable_shift = 31,
.enable_negate = 1,
.scale_reg = REGS_CLKCTRL_BASE + HW_CLKCTRL_FRAC,
.scale_shift = 24,
.flags = RATE_PROPAGATES | ENABLED,
.ops = &io_ops,
};
static struct clk hclk = {
.parent = &cpu_clk,
.scale_reg = REGS_CLKCTRL_BASE + HW_CLKCTRL_HBUS,
.bypass_reg = REGS_CLKCTRL_BASE + HW_CLKCTRL_CLKSEQ,
.bypass_shift = 7,
.busy_reg = REGS_CLKCTRL_BASE + HW_CLKCTRL_HBUS,
.busy_bit = 29,
.flags = RATE_PROPAGATES | ENABLED,
.ops = &hbus_ops,
};
static struct clk xclk = {
.parent = &osc_24M,
.scale_reg = REGS_CLKCTRL_BASE + HW_CLKCTRL_XBUS,
.busy_reg = REGS_CLKCTRL_BASE + HW_CLKCTRL_XBUS,
.busy_bit = 31,
.flags = RATE_PROPAGATES | ENABLED,
.ops = &xbus_ops,
};
static struct clk uart_clk = {
.parent = &xclk,
.enable_reg = REGS_CLKCTRL_BASE + HW_CLKCTRL_XTAL,
.enable_shift = 31,
.enable_negate = 1,
.flags = ENABLED,
.ops = &min_ops,
};
static struct clk audio_clk = {
.parent = &xclk,
.enable_reg = REGS_CLKCTRL_BASE + HW_CLKCTRL_XTAL,
.enable_shift = 30,
.enable_negate = 1,
.ops = &min_ops,
};
static struct clk pwm_clk = {
.parent = &xclk,
.enable_reg = REGS_CLKCTRL_BASE + HW_CLKCTRL_XTAL,
.enable_shift = 29,
.enable_negate = 1,
.ops = &min_ops,
};
static struct clk dri_clk = {
.parent = &xclk,
.enable_reg = REGS_CLKCTRL_BASE + HW_CLKCTRL_XTAL,
.enable_shift = 28,
.enable_negate = 1,
.ops = &min_ops,
};
static struct clk digctl_clk = {
.parent = &xclk,
.enable_reg = REGS_CLKCTRL_BASE + HW_CLKCTRL_XTAL,
.enable_shift = 27,
.enable_negate = 1,
.ops = &min_ops,
};
static struct clk timer_clk = {
.parent = &xclk,
.enable_reg = REGS_CLKCTRL_BASE + HW_CLKCTRL_XTAL,
.enable_shift = 26,
.enable_negate = 1,
.flags = ENABLED,
.ops = &min_ops,
};
static struct clk lcdif_clk = {
.parent = &pll_clk,
.scale_reg = REGS_CLKCTRL_BASE + HW_CLKCTRL_PIX,
.busy_reg = REGS_CLKCTRL_BASE + HW_CLKCTRL_PIX,
.busy_bit = 29,
.enable_reg = REGS_CLKCTRL_BASE + HW_CLKCTRL_PIX,
.enable_shift = 31,
.enable_negate = 1,
.bypass_reg = REGS_CLKCTRL_BASE + HW_CLKCTRL_CLKSEQ,
.bypass_shift = 1,
.flags = NEEDS_SET_PARENT,
.ops = &lcdif_ops,
};
static struct clk ssp_clk = {
.parent = &io_clk,
.scale_reg = REGS_CLKCTRL_BASE + HW_CLKCTRL_SSP,
.busy_reg = REGS_CLKCTRL_BASE + HW_CLKCTRL_SSP,
.busy_bit = 29,
.enable_reg = REGS_CLKCTRL_BASE + HW_CLKCTRL_SSP,
.enable_shift = 31,
.bypass_reg = REGS_CLKCTRL_BASE + HW_CLKCTRL_CLKSEQ,
.bypass_shift = 5,
.enable_negate = 1,
.flags = NEEDS_SET_PARENT,
.ops = &std_ops,
};
static struct clk gpmi_clk = {
.parent = &io_clk,
.scale_reg = REGS_CLKCTRL_BASE + HW_CLKCTRL_GPMI,
.busy_reg = REGS_CLKCTRL_BASE + HW_CLKCTRL_GPMI,
.busy_bit = 29,
.enable_reg = REGS_CLKCTRL_BASE + HW_CLKCTRL_GPMI,
.enable_shift = 31,
.enable_negate = 1,
.bypass_reg = REGS_CLKCTRL_BASE + HW_CLKCTRL_CLKSEQ,
.bypass_shift = 4,
.flags = NEEDS_SET_PARENT,
.ops = &std_ops,
};
static struct clk spdif_clk = {
.parent = &pll_clk,
.enable_reg = REGS_CLKCTRL_BASE + HW_CLKCTRL_SPDIF,
.enable_shift = 31,
.enable_negate = 1,
.ops = &min_ops,
};
static struct clk emi_clk = {
.parent = &pll_clk,
.enable_reg = REGS_CLKCTRL_BASE + HW_CLKCTRL_EMI,
.enable_shift = 31,
.enable_negate = 1,
.scale_reg = REGS_CLKCTRL_BASE + HW_CLKCTRL_FRAC,
.scale_shift = 8,
.busy_reg = REGS_CLKCTRL_BASE + HW_CLKCTRL_EMI,
.busy_bit = 28,
.bypass_reg = REGS_CLKCTRL_BASE + HW_CLKCTRL_CLKSEQ,
.bypass_shift = 6,
.flags = ENABLED,
.ops = &emi_ops,
};
static struct clk ir_clk = {
.parent = &io_clk,
.enable_reg = REGS_CLKCTRL_BASE + HW_CLKCTRL_IR,
.enable_shift = 31,
.enable_negate = 1,
.bypass_reg = REGS_CLKCTRL_BASE + HW_CLKCTRL_CLKSEQ,
.bypass_shift = 3,
.ops = &min_ops,
};
static struct clk saif_clk = {
.parent = &pll_clk,
.scale_reg = REGS_CLKCTRL_BASE + HW_CLKCTRL_SAIF,
.busy_reg = REGS_CLKCTRL_BASE + HW_CLKCTRL_SAIF,
.busy_bit = 29,
.enable_reg = REGS_CLKCTRL_BASE + HW_CLKCTRL_SAIF,
.enable_shift = 31,
.enable_negate = 1,
.bypass_reg = REGS_CLKCTRL_BASE + HW_CLKCTRL_CLKSEQ,
.bypass_shift = 0,
.ops = &std_ops,
};
static struct clk usb_clk = {
.parent = &pll_clk,
.enable_reg = REGS_CLKCTRL_BASE + HW_CLKCTRL_PLLCTRL0,
.enable_shift = 18,
.enable_negate = 1,
.ops = &min_ops,
};
/* list of all the clocks */
static struct clk_lookup onchip_clks[] = {
{
.con_id = "osc_24M",
.clk = &osc_24M,
}, {
.con_id = "pll",
.clk = &pll_clk,
}, {
.con_id = "cpu",
.clk = &cpu_clk,
}, {
.con_id = "hclk",
.clk = &hclk,
}, {
.con_id = "xclk",
.clk = &xclk,
}, {
.con_id = "io",
.clk = &io_clk,
}, {
.con_id = "uart",
.clk = &uart_clk,
}, {
.con_id = "audio",
.clk = &audio_clk,
}, {
.con_id = "pwm",
.clk = &pwm_clk,
}, {
.con_id = "dri",
.clk = &dri_clk,
}, {
.con_id = "digctl",
.clk = &digctl_clk,
}, {
.con_id = "timer",
.clk = &timer_clk,
}, {
.con_id = "lcdif",
.clk = &lcdif_clk,
}, {
.con_id = "ssp",
.clk = &ssp_clk,
}, {
.con_id = "gpmi",
.clk = &gpmi_clk,
}, {
.con_id = "spdif",
.clk = &spdif_clk,
}, {
.con_id = "emi",
.clk = &emi_clk,
}, {
.con_id = "ir",
.clk = &ir_clk,
}, {
.con_id = "saif",
.clk = &saif_clk,
}, {
.con_id = "usb",
.clk = &usb_clk,
},
};
static int __init propagate_rate(struct clk *clk)
{
struct clk_lookup *cl;
for (cl = onchip_clks; cl < onchip_clks + ARRAY_SIZE(onchip_clks);
cl++) {
if (unlikely(!clk_good(cl->clk)))
continue;
if (cl->clk->parent == clk && cl->clk->ops->get_rate) {
cl->clk->ops->get_rate(cl->clk);
if (cl->clk->flags & RATE_PROPAGATES)
propagate_rate(cl->clk);
}
}
return 0;
}
/* Exported API */
unsigned long clk_get_rate(struct clk *clk)
{
if (unlikely(!clk_good(clk)))
return 0;
if (clk->rate != 0)
return clk->rate;
if (clk->ops->get_rate != NULL)
return clk->ops->get_rate(clk);
return clk_get_rate(clk->parent);
}
EXPORT_SYMBOL(clk_get_rate);
long clk_round_rate(struct clk *clk, unsigned long rate)
{
if (unlikely(!clk_good(clk)))
return 0;
if (clk->ops->round_rate)
return clk->ops->round_rate(clk, rate);
return 0;
}
EXPORT_SYMBOL(clk_round_rate);
static inline int close_enough(long rate1, long rate2)
{
return rate1 && !((rate2 - rate1) * 1000 / rate1);
}
int clk_set_rate(struct clk *clk, unsigned long rate)
{
int ret = -EINVAL;
if (unlikely(!clk_good(clk)))
goto out;
if (clk->flags & FIXED_RATE || !clk->ops->set_rate)
goto out;
else if (!close_enough(clk->rate, rate)) {
ret = clk->ops->set_rate(clk, rate);
if (ret < 0)
goto out;
clk->rate = rate;
if (clk->flags & RATE_PROPAGATES)
propagate_rate(clk);
} else
ret = 0;
out:
return ret;
}
EXPORT_SYMBOL(clk_set_rate);
int clk_enable(struct clk *clk)
{
unsigned long clocks_flags;
if (unlikely(!clk_good(clk)))
return -EINVAL;
if (clk->parent)
clk_enable(clk->parent);
spin_lock_irqsave(&clocks_lock, clocks_flags);
clk->usage++;
if (clk->ops && clk->ops->enable)
clk->ops->enable(clk);
spin_unlock_irqrestore(&clocks_lock, clocks_flags);
return 0;
}
EXPORT_SYMBOL(clk_enable);
static void local_clk_disable(struct clk *clk)
{
if (unlikely(!clk_good(clk)))
return;
if (clk->usage == 0 && clk->ops->disable)
clk->ops->disable(clk);
if (clk->parent)
local_clk_disable(clk->parent);
}
void clk_disable(struct clk *clk)
{
unsigned long clocks_flags;
if (unlikely(!clk_good(clk)))
return;
spin_lock_irqsave(&clocks_lock, clocks_flags);
if ((--clk->usage) == 0 && clk->ops->disable)
clk->ops->disable(clk);
spin_unlock_irqrestore(&clocks_lock, clocks_flags);
if (clk->parent)
clk_disable(clk->parent);
}
EXPORT_SYMBOL(clk_disable);
/* Some additional API */
int clk_set_parent(struct clk *clk, struct clk *parent)
{
int ret = -ENODEV;
unsigned long clocks_flags;
if (unlikely(!clk_good(clk)))
goto out;
if (!clk->ops->set_parent)
goto out;
spin_lock_irqsave(&clocks_lock, clocks_flags);
ret = clk->ops->set_parent(clk, parent);
if (!ret) {
/* disable if usage count is 0 */
local_clk_disable(parent);
parent->usage += clk->usage;
clk->parent->usage -= clk->usage;
/* disable if new usage count is 0 */
local_clk_disable(clk->parent);
clk->parent = parent;
}
spin_unlock_irqrestore(&clocks_lock, clocks_flags);
out:
return ret;
}
EXPORT_SYMBOL(clk_set_parent);
struct clk *clk_get_parent(struct clk *clk)
{
if (unlikely(!clk_good(clk)))
return NULL;
return clk->parent;
}
EXPORT_SYMBOL(clk_get_parent);
static int __init clk_init(void)
{
struct clk_lookup *cl;
struct clk_ops *ops;
spin_lock_init(&clocks_lock);
for (cl = onchip_clks; cl < onchip_clks + ARRAY_SIZE(onchip_clks);
cl++) {
if (cl->clk->flags & ENABLED)
clk_enable(cl->clk);
else
local_clk_disable(cl->clk);
ops = cl->clk->ops;
if ((cl->clk->flags & NEEDS_INITIALIZATION) &&
ops && ops->set_rate)
ops->set_rate(cl->clk, cl->clk->rate);
if (cl->clk->flags & FIXED_RATE) {
if (cl->clk->flags & RATE_PROPAGATES)
propagate_rate(cl->clk);
} else {
if (ops && ops->get_rate)
ops->get_rate(cl->clk);
}
if (cl->clk->flags & NEEDS_SET_PARENT) {
if (ops && ops->set_parent)
ops->set_parent(cl->clk, cl->clk->parent);
}
}
clkdev_add_table(onchip_clks, ARRAY_SIZE(onchip_clks));
return 0;
}
arch_initcall(clk_init);