Commit d35119f3 authored by Tony Lindgren's avatar Tony Lindgren Committed by Russell King

[ARM PATCH] 2166/1: OMAP update 5/8: Change OMAP to use generic clock framework

Patch from Tony Lindgren

This patch by Tuukka Tikkanen changes OMAP to use ARM generic
clock framework instead of older OMAP specific clock framework.
Patch replaces old clocks.c with new clock.[ch].

Signed-off-by: Tony Lindgren
parent 57d31ffa
/*
* linux/arch/arm/mach-omap/clock.c
*
* Copyright (C) 2004 Nokia corporation
* Written by Tuukka Tikkanen <tuukka.tikkanen@elektrobit.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/errno.h>
#include <linux/err.h>
#include <asm/semaphore.h>
#include <asm/hardware/clock.h>
#include <asm/arch/board.h>
#include "clock.h"
static LIST_HEAD(clocks);
static DECLARE_MUTEX(clocks_sem);
static spinlock_t clockfw_lock = SPIN_LOCK_UNLOCKED;
static void propagate_rate(struct clk * clk);
/* MPU virtual clock functions */
static int select_table_rate(unsigned long rate);
static long round_to_table_rate(unsigned long rate);
void clk_setdpll(__u16, __u16);
struct mpu_rate rate_table[] = {
/* MPU MHz, xtal MHz, dpll1 MHz, CKCTL, DPLL_CTL
* armdiv, dspdiv, dspmmu, tcdiv, perdiv, lcddiv
*/
#if defined(CONFIG_OMAP_ARM_195MHZ) && defined(CONFIG_ARCH_OMAP730)
{ 195000000, 13000000, 195000000, 0x050e, 0x2790 }, /* 1/1/2/2/4/8 */
#endif
#if defined(CONFIG_OMAP_ARM_192MHZ) && defined(CONFIG_ARCH_OMAP16XX)
{ 192000000, 19200000, 192000000, 0x050f, 0x2510 }, /* 1/1/2/2/8/8 */
{ 192000000, 12000000, 192000000, 0x050f, 0x2810 }, /* 1/1/2/2/8/8 */
{ 96000000, 12000000, 192000000, 0x055f, 0x2810 }, /* 2/2/2/2/8/8 */
{ 48000000, 12000000, 192000000, 0x0ccf, 0x2810 }, /* 4/4/4/4/8/8 */
{ 24000000, 12000000, 192000000, 0x0fff, 0x2810 }, /* 8/8/8/8/8/8 */
#endif
#if defined(CONFIG_OMAP_ARM_182MHZ) && defined(CONFIG_ARCH_OMAP730)
{ 182000000, 13000000, 182000000, 0x050e, 0x2710 }, /* 1/1/2/2/4/8 */
#endif
#if defined(CONFIG_OMAP_ARM_168MHZ)
{ 168000000, 12000000, 168000000, 0x010f, 0x2710 }, /* 1/1/1/2/8/8 */
#endif
#if defined(CONFIG_OMAP_ARM_120MHZ)
{ 120000000, 12000000, 120000000, 0x010a, 0x2510 }, /* 1/1/1/2/4/4 */
#endif
#if defined(CONFIG_OMAP_ARM_96MHZ)
{ 96000000, 12000000, 96000000, 0x0005, 0x2410 }, /* 1/1/1/1/2/2 */
#endif
#if defined(CONFIG_OMAP_ARM_60MHZ)
{ 60000000, 12000000, 60000000, 0x0005, 0x2290 }, /* 1/1/1/1/2/2 */
#endif
#if defined(CONFIG_OMAP_ARM_30MHZ)
{ 30000000, 12000000, 60000000, 0x0555, 0x2290 }, /* 2/2/2/2/2/2 */
#endif
{ 0, 0, 0, 0, 0 },
};
static void ckctl_recalc(struct clk * clk)
{
int dsor;
/* Calculate divisor encoded as 2-bit exponent */
dsor = 1 << (3 & (omap_readw(ARM_CKCTL) >> clk->rate_offset));
if (unlikely(clk->rate == clk->parent->rate / dsor))
return; /* No change, quick exit */
clk->rate = clk->parent->rate / dsor;
if (unlikely(clk->flags & RATE_PROPAGATES))
propagate_rate(clk);
}
static void followparent_recalc(struct clk * clk)
{
clk->rate = clk->parent->rate;
}
static void watchdog_recalc(struct clk * clk)
{
clk->rate = clk->parent->rate / 14;
}
static struct clk ck_ref = {
.name = "ck_ref",
.rate = 12000000,
.flags = ALWAYS_ENABLED,
};
static struct clk ck_dpll1 = {
.name = "ck_dpll1",
.parent = &ck_ref,
.flags = RATE_PROPAGATES | ALWAYS_ENABLED,
};
static struct clk ck_dpll1out = {
.name = "ck_dpll1out",
.parent = &ck_dpll1,
.flags = DOES_NOT_EXIST_ON_1510,
.enable_reg = ARM_IDLECT2,
.enable_bit = EN_CKOUT_ARM,
.recalc = &followparent_recalc,
};
static struct clk arm_ck = {
.name = "arm_ck",
.parent = &ck_dpll1,
.flags = RATE_CKCTL | RATE_PROPAGATES | ALWAYS_ENABLED,
.rate_offset = CKCTL_ARMDIV_OFFSET,
.recalc = &ckctl_recalc,
};
static struct clk armper_ck = {
.name = "armper_ck",
.parent = &ck_dpll1,
.flags = RATE_CKCTL,
.enable_reg = ARM_IDLECT2,
.enable_bit = EN_PERCK,
.rate_offset = CKCTL_PERDIV_OFFSET,
.recalc = &ckctl_recalc,
};
static struct clk arm_gpio_ck = {
.name = "arm_gpio_ck",
.parent = &ck_dpll1,
.flags = DOES_NOT_EXIST_ON_1610,
.enable_reg = ARM_IDLECT2,
.enable_bit = EN_GPIOCK,
.recalc = &followparent_recalc,
};
static struct clk armxor_ck = {
.name = "armxor_ck",
.parent = &ck_ref,
.enable_reg = ARM_IDLECT2,
.enable_bit = EN_XORPCK,
.recalc = &followparent_recalc,
};
static struct clk armtim_ck = {
.name = "armtim_ck",
.parent = &ck_ref,
.enable_reg = ARM_IDLECT2,
.enable_bit = EN_TIMCK,
.recalc = &followparent_recalc,
};
static struct clk armwdt_ck = {
.name = "armwdt_ck",
.parent = &ck_ref,
.enable_reg = ARM_IDLECT2,
.enable_bit = EN_WDTCK,
.recalc = &watchdog_recalc,
};
static struct clk arminth_ck1610 = {
.name = "arminth_ck",
.parent = &arm_ck,
.flags = DOES_NOT_EXIST_ON_1510,
.recalc = &followparent_recalc,
/* Note: On 1610/1710 frequency can be divided by 2 by programming
* ARM_CKCTL:ARM_INTHCK_SEL(14) to 1
*
* 1510 version is in TC clocks.
*/
};
static struct clk dsp_ck = {
.name = "dsp_ck",
.parent = &ck_dpll1,
.flags = RATE_CKCTL,
.enable_reg = ARM_CKCTL,
.enable_bit = EN_DSPCK,
.rate_offset = CKCTL_DSPDIV_OFFSET,
.recalc = &ckctl_recalc,
};
static struct clk dspmmu_ck = {
.name = "dspmmu_ck",
.parent = &ck_dpll1,
.flags = RATE_CKCTL | ALWAYS_ENABLED,
.rate_offset = CKCTL_DSPMMUDIV_OFFSET,
.recalc = &ckctl_recalc,
};
static struct clk tc_ck = {
.name = "tc_ck",
.parent = &ck_dpll1,
.flags = RATE_CKCTL | RATE_PROPAGATES | ALWAYS_ENABLED,
.rate_offset = CKCTL_TCDIV_OFFSET,
.recalc = &ckctl_recalc,
};
static struct clk arminth_ck1510 = {
.name = "arminth_ck",
.parent = &tc_ck,
.flags = DOES_NOT_EXIST_ON_1610,
.recalc = &followparent_recalc,
/* Note: On 1510 frequency follows TC_CK
*
* 1610/1710 version is in MPU clocks.
*/
};
static struct clk tipb_ck = {
.name = "tibp_ck",
.parent = &tc_ck,
.flags = DOES_NOT_EXIST_ON_1610,
.recalc = &followparent_recalc,
};
static struct clk l3_ocpi_ck = {
.name = "l3_ocpi_ck",
.parent = &tc_ck,
.flags = DOES_NOT_EXIST_ON_1510,
.enable_reg = ARM_IDLECT3,
.enable_bit = EN_OCPI_CK,
.recalc = &followparent_recalc,
};
static struct clk tc1_ck = {
.name = "tc1_ck",
.parent = &tc_ck,
.flags = DOES_NOT_EXIST_ON_1510,
.enable_reg = ARM_IDLECT3,
.enable_bit = EN_TC1_CK,
.recalc = &followparent_recalc,
};
static struct clk tc2_ck = {
.name = "tc2_ck",
.parent = &tc_ck,
.flags = DOES_NOT_EXIST_ON_1510,
.enable_reg = ARM_IDLECT3,
.enable_bit = EN_TC2_CK,
.recalc = &followparent_recalc,
};
static struct clk dma_ck = {
.name = "dma_ck",
.parent = &tc_ck,
.recalc = &followparent_recalc,
};
static struct clk dma_lcdfree_ck = {
.name = "dma_lcdfree_ck",
.parent = &tc_ck,
.flags = DOES_NOT_EXIST_ON_1510,
.recalc = &followparent_recalc,
};
static struct clk api_ck = {
.name = "api_ck",
.parent = &tc_ck,
.enable_reg = ARM_IDLECT2,
.enable_bit = EN_APICK,
.recalc = &followparent_recalc,
};
static struct clk lb_ck = {
.name = "lb_ck",
.parent = &tc_ck,
.flags = DOES_NOT_EXIST_ON_1610,
.enable_reg = ARM_IDLECT2,
.enable_bit = EN_LBCK,
.recalc = &followparent_recalc,
};
static struct clk rhea1_ck = {
.name = "rhea1_ck",
.parent = &tc_ck,
.flags = DOES_NOT_EXIST_ON_1510,
.recalc = &followparent_recalc,
};
static struct clk rhea2_ck = {
.name = "rhea2_ck",
.parent = &tc_ck,
.flags = DOES_NOT_EXIST_ON_1510,
.recalc = &followparent_recalc,
};
static struct clk lcd_ck = {
.name = "lcd_ck",
.parent = &ck_dpll1,
.flags = RATE_CKCTL,
.enable_reg = ARM_IDLECT2,
.enable_bit = EN_LCDCK,
.rate_offset = CKCTL_LCDDIV_OFFSET,
.recalc = &ckctl_recalc,
};
static struct clk uart1_ck = {
.name = "uart1_ck",
/* Direct from ULPD, no parent */
.rate = 48000000,
.flags = RATE_FIXED | ENABLE_REG_32BIT,
.enable_reg = MOD_CONF_CTRL_0,
.enable_bit = 29,
/* (Only on 1510)
* The "enable bit" actually chooses between 48MHz and 12MHz.
*/
};
static struct clk uart2_ck = {
.name = "uart2_ck",
/* Direct from ULPD, no parent */
.rate = 48000000,
.flags = RATE_FIXED | ENABLE_REG_32BIT,
.enable_reg = MOD_CONF_CTRL_0,
.enable_bit = 30,
/* (1510/1610/1710)
* The "enable bit" actually chooses between 48MHz and 12MHz/32kHz.
*/
};
static struct clk uart3_ck = {
.name = "uart3_ck",
/* Direct from ULPD, no parent */
.rate = 48000000,
.flags = RATE_FIXED | ENABLE_REG_32BIT,
.enable_reg = MOD_CONF_CTRL_0,
.enable_bit = 31,
/* (Only on 1510)
* The "enable bit" actually chooses between 48MHz and 12MHz.
*/
};
static struct clk usb_ck1610 = {
.name = "usb_ck",
/* Direct from ULPD, no parent */
.rate = 48000000,
.flags = RATE_FIXED | ENABLE_REG_32BIT |
DOES_NOT_EXIST_ON_1510,
.enable_reg = ULPD_CLOCK_CTRL,
.enable_bit = USB_MCLK_EN,
};
static struct clk usb_ck1510 = {
.name = "usb_ck",
/* Direct from ULPD, no parent */
.rate = 48000000,
.flags = RATE_FIXED | DOES_NOT_EXIST_ON_1610,
};
static struct clk usb_hhc_ck = {
.name = "usb_hhc_ck",
/* Direct from ULPD, no parent */
.rate = 48000000, /* Actually 2 clocks, 12MHz and 48MHz */
.flags = RATE_FIXED | ENABLE_REG_32BIT,
.enable_reg = MOD_CONF_CTRL_0,
.enable_bit = USB_HOST_HHC_UHOST_EN,
};
/* To be done --
static struct clk mclk = {
.name = "mclk",
};
static struct clk bclk = {
.name = "bclk",
};
-- to be done */
static struct clk mmc_ck = {
.name = "mmc1_ck",
.parent = &armxor_ck, /* (1510) */
.rate = 48000000,
.flags = RATE_FIXED | ENABLE_REG_32BIT |
DOES_NOT_EXIST_ON_1610,
.enable_reg = MOD_CONF_CTRL_0,
.enable_bit = 23,
};
static struct clk mmc1_ck = {
.name = "mmc1_ck",
/* Direct from ULPD, no parent (1610/1710) */
.rate = 48000000,
.flags = RATE_FIXED | ENABLE_REG_32BIT |
DOES_NOT_EXIST_ON_1510,
.enable_reg = MOD_CONF_CTRL_0,
.enable_bit = 23,
};
static struct clk mmc2_ck = {
.name = "mmc2_ck",
/* Direct from ULPD, no parent */
.rate = 48000000,
.flags = RATE_FIXED | ENABLE_REG_32BIT |
DOES_NOT_EXIST_ON_1510,
.enable_reg = MOD_CONF_CTRL_0,
.enable_bit = 20,
};
static struct clk virtual_ck_mpu = {
.name = "mpu",
.flags = VIRTUAL_CLOCK | ALWAYS_ENABLED,
.parent = &arm_ck, /* Is smarter alias for */
.recalc = &followparent_recalc,
.set_rate = &select_table_rate,
.round_rate = &round_to_table_rate,
};
static struct clk * onchip_clks[] = {
/* non-ULPD clocks */
&ck_ref,
&ck_dpll1,
/* CK_GEN1 clocks */
&ck_dpll1out,
&arm_ck,
&armper_ck,
&arm_gpio_ck,
&armxor_ck,
&armtim_ck,
&armwdt_ck,
&arminth_ck1510,
&arminth_ck1610,
/* CK_GEN2 clocks */
&dsp_ck,
&dspmmu_ck,
/* CK_GEN3 clocks */
&tc_ck,
&tipb_ck,
&l3_ocpi_ck,
&tc1_ck,
&tc2_ck,
&dma_ck,
&dma_lcdfree_ck,
&api_ck,
&lb_ck,
&rhea1_ck,
&rhea2_ck,
&lcd_ck,
/* ULPD clocks */
&uart1_ck,
&uart2_ck,
&uart3_ck,
&usb_ck1510,
&usb_ck1610,
&usb_hhc_ck,
/* To be done --
&mclk,
&bclk,
-- to be done */
&mmc_ck, /* 1510 */
&mmc1_ck, /* 1610/1710 */
&mmc2_ck,
/* Virtual clocks */
&virtual_ck_mpu,
};
struct clk *clk_get(struct device *dev, const char *id)
{
struct clk *p, *clk = ERR_PTR(-ENOENT);
down(&clocks_sem);
list_for_each_entry(p, &clocks, node) {
if (strcmp(id, p->name) == 0 && try_module_get(p->owner)) {
clk = p;
break;
}
}
up(&clocks_sem);
return clk;
}
EXPORT_SYMBOL(clk_get);
void clk_put(struct clk *clk)
{
if (clk && !IS_ERR(clk))
module_put(clk->owner);
}
EXPORT_SYMBOL(clk_put);
int __clk_enable(struct clk *clk)
{
__u16 regval16;
__u32 regval32;
if (clk->flags & ALWAYS_ENABLED)
return 0;
if (unlikely(clk->enable_reg == 0)) {
printk("Enable for %s without enable enabled\n", clk->name);
return 0;
}
if (clk->flags & ENABLE_REG_32BIT) {
regval32 = omap_readl(clk->enable_reg);
regval32 |= (1 << clk->enable_bit);
omap_writel(regval32, clk->enable_reg);
} else {
regval16 = omap_readw(clk->enable_reg);
regval16 |= (1 << clk->enable_bit);
omap_writew(regval16, clk->enable_reg);
}
return 0;
}
void __clk_disable(struct clk *clk)
{
__u16 regval16;
__u32 regval32;
if (clk->enable_reg == 0)
return;
if (clk->flags & ENABLE_REG_32BIT) {
regval32 = omap_readl(clk->enable_reg);
regval32 &= ~(1 << clk->enable_bit);
omap_writel(regval32, clk->enable_reg);
} else {
regval16 = omap_readw(clk->enable_reg);
regval16 &= ~(1 << clk->enable_bit);
omap_writew(regval16, clk->enable_reg);
}
}
void __clk_unuse(struct clk *clk)
{
if (clk->usecount > 0 && !(--clk->usecount)) {
__clk_disable(clk);
if (likely(clk->parent))
__clk_unuse(clk->parent);
}
}
int __clk_use(struct clk *clk)
{
int ret = 0;
if (clk->usecount++ == 0) {
if (likely(clk->parent))
ret = __clk_use(clk->parent);
if (unlikely(ret != 0)) {
clk->usecount--;
return ret;
}
ret = __clk_enable(clk);
if (unlikely(ret != 0) && clk->parent) {
__clk_unuse(clk->parent);
clk->usecount--;
}
}
return ret;
}
int clk_enable(struct clk *clk)
{
unsigned long flags;
int ret;
spin_lock_irqsave(&clockfw_lock, flags);
ret = __clk_enable(clk);
spin_unlock_irqrestore(&clockfw_lock, flags);
return ret;
}
EXPORT_SYMBOL(clk_enable);
void clk_disable(struct clk *clk)
{
unsigned long flags;
spin_lock_irqsave(&clockfw_lock, flags);
__clk_disable(clk);
spin_unlock_irqrestore(&clockfw_lock, flags);
}
EXPORT_SYMBOL(clk_disable);
int clk_use(struct clk *clk)
{
unsigned long flags;
int ret = 0;
spin_lock_irqsave(&clockfw_lock, flags);
ret = __clk_use(clk);
spin_unlock_irqrestore(&clockfw_lock, flags);
return ret;
}
EXPORT_SYMBOL(clk_use);
void clk_unuse(struct clk *clk)
{
unsigned long flags;
spin_lock_irqsave(&clockfw_lock, flags);
__clk_unuse(clk);
spin_unlock_irqrestore(&clockfw_lock, flags);
}
EXPORT_SYMBOL(clk_unuse);
unsigned long clk_get_rate(struct clk *clk)
{
if (clk->rate == 0) {
printk("Get rate for %s without cached clock\n", clk->name);
}
return clk->rate;
}
EXPORT_SYMBOL(clk_get_rate);
static __u16 verify_ckctl_value(__u16 newval)
{
/* This function checks for following limitations set
* by the hardware (all conditions must be true):
* DSPMMU_CK == DSP_CK or DSPMMU_CK == DSP_CK/2
* ARM_CK >= TC_CK
* DSP_CK >= TC_CK
* DSPMMU_CK >= TC_CK
*
* In addition following rules are enforced:
* LCD_CK <= TC_CK
* ARMPER_CK <= TC_CK
*
* However, maximum frequencies are not checked for!
*/
__u8 per_exp;
__u8 lcd_exp;
__u8 arm_exp;
__u8 dsp_exp;
__u8 tc_exp;
__u8 dspmmu_exp;
per_exp = (newval >> CKCTL_PERDIV_OFFSET) & 3;
lcd_exp = (newval >> CKCTL_LCDDIV_OFFSET) & 3;
arm_exp = (newval >> CKCTL_ARMDIV_OFFSET) & 3;
dsp_exp = (newval >> CKCTL_DSPDIV_OFFSET) & 3;
tc_exp = (newval >> CKCTL_TCDIV_OFFSET) & 3;
dspmmu_exp = (newval >> CKCTL_DSPMMUDIV_OFFSET) & 3;
if (dspmmu_exp < dsp_exp)
dspmmu_exp = dsp_exp;
if (dspmmu_exp > dsp_exp+1)
dspmmu_exp = dsp_exp+1;
if (tc_exp < arm_exp)
tc_exp = arm_exp;
if (tc_exp < dspmmu_exp)
tc_exp = dspmmu_exp;
if (tc_exp > lcd_exp)
lcd_exp = tc_exp;
if (tc_exp > per_exp)
per_exp = tc_exp;
newval &= 0xf000;
newval |= per_exp << CKCTL_PERDIV_OFFSET;
newval |= lcd_exp << CKCTL_LCDDIV_OFFSET;
newval |= arm_exp << CKCTL_ARMDIV_OFFSET;
newval |= dsp_exp << CKCTL_DSPDIV_OFFSET;
newval |= tc_exp << CKCTL_TCDIV_OFFSET;
newval |= dspmmu_exp << CKCTL_DSPMMUDIV_OFFSET;
return newval;
}
static int calc_dsor_exp(struct clk *clk, unsigned long rate)
{
/* Note: If target frequency is too low, this function will return 4,
* which is invalid value. Caller must check for this value and act
* accordingly.
*
* Note: This function does not check for following limitations set
* by the hardware (all conditions must be true):
* DSPMMU_CK == DSP_CK or DSPMMU_CK == DSP_CK/2
* ARM_CK >= TC_CK
* DSP_CK >= TC_CK
* DSPMMU_CK >= TC_CK
*/
unsigned long realrate;
struct clk * parent;
unsigned dsor_exp;
if (unlikely(!(clk->flags & RATE_CKCTL)))
return -EINVAL;
parent = clk->parent;
if (unlikely(parent == 0))
return -EIO;
realrate = parent->rate;
for (dsor_exp=0; dsor_exp<4; dsor_exp++) {
if (realrate <= rate)
break;
realrate /= 2;
}
return dsor_exp;
}
long clk_round_rate(struct clk *clk, unsigned long rate)
{
int dsor_exp;
if (clk->flags & RATE_FIXED)
return clk->rate;
if (clk->flags & RATE_CKCTL) {
dsor_exp = calc_dsor_exp(clk, rate);
if (dsor_exp < 0)
return dsor_exp;
if (dsor_exp > 3)
dsor_exp = 3;
return clk->parent->rate / (1 << dsor_exp);
}
if(clk->round_rate != 0)
return clk->round_rate(rate);
return clk->rate;
}
EXPORT_SYMBOL(clk_round_rate);
static void propagate_rate(struct clk * clk)
{
struct clk ** clkp;
for (clkp = onchip_clks; clkp < onchip_clks+ARRAY_SIZE(onchip_clks); clkp++) {
if (likely((*clkp)->parent != clk)) continue;
if (likely((*clkp)->recalc))
(*clkp)->recalc(*clkp);
}
}
static int select_table_rate(unsigned long rate)
{
/* Find the highest supported frequency <= rate and switch to it */
struct mpu_rate * ptr;
for (ptr = rate_table; ptr->rate; ptr++) {
if (ptr->xtal != ck_ref.rate)
continue;
/* DPLL1 cannot be reprogrammed without risking system crash */
if (likely(ck_dpll1.rate!=0) && ptr->pll_rate != ck_dpll1.rate)
continue;
/* Can check only after xtal frequency check */
if (ptr->rate <= rate)
break;
}
if (!ptr->rate)
return -EINVAL;
if (unlikely(ck_dpll1.rate == 0)) {
omap_writew(ptr->dpllctl_val, DPLL_CTL);
ck_dpll1.rate = ptr->pll_rate;
}
omap_writew(ptr->ckctl_val, ARM_CKCTL);
propagate_rate(&ck_dpll1);
return 0;
}
static long round_to_table_rate(unsigned long rate)
{
/* Find the highest supported frequency <= rate */
struct mpu_rate * ptr;
long highest_rate;
highest_rate = -EINVAL;
for (ptr = rate_table; ptr->rate; ptr++) {
if (ptr->xtal != ck_ref.rate)
continue;
highest_rate = ptr->rate;
/* Can check only after xtal frequency check */
if (ptr->rate <= rate)
break;
}
return highest_rate;
}
int clk_set_rate(struct clk *clk, unsigned long rate)
{
int ret = -EINVAL;
int dsor_exp;
__u16 regval;
unsigned long flags;
if (clk->flags & RATE_CKCTL) {
dsor_exp = calc_dsor_exp(clk, rate);
if (dsor_exp > 3)
dsor_exp = -EINVAL;
if (dsor_exp < 0)
return dsor_exp;
spin_lock_irqsave(&clockfw_lock, flags);
regval = omap_readw(ARM_CKCTL);
regval &= ~(3 << clk->rate_offset);
regval |= dsor_exp << clk->rate_offset;
regval = verify_ckctl_value(regval);
omap_writew(regval, ARM_CKCTL);
clk->rate = clk->parent->rate / (1 << dsor_exp);
spin_unlock_irqrestore(&clockfw_lock, flags);
ret = 0;
} else if(clk->set_rate != 0) {
spin_lock_irqsave(&clockfw_lock, flags);
ret = clk->set_rate(rate);
spin_unlock_irqrestore(&clockfw_lock, flags);
}
if (unlikely(ret == 0 && (clk->flags & RATE_PROPAGATES)))
propagate_rate(clk);
return ret;
}
EXPORT_SYMBOL(clk_set_rate);
int clk_register(struct clk *clk)
{
down(&clocks_sem);
list_add(&clk->node, &clocks);
up(&clocks_sem);
return 0;
}
EXPORT_SYMBOL(clk_register);
void clk_unregister(struct clk *clk)
{
down(&clocks_sem);
list_del(&clk->node);
up(&clocks_sem);
}
EXPORT_SYMBOL(clk_unregister);
int __init clk_init(void)
{
struct clk ** clkp;
const struct omap_clock_config *info;
int crystal_type = 0; /* Default 12 MHz */
for (clkp = onchip_clks; clkp < onchip_clks+ARRAY_SIZE(onchip_clks); clkp++) {
if ((((*clkp)->flags & DOES_NOT_EXIST_ON_1510) &&
cpu_is_omap1510()) ||
(((*clkp)->flags & DOES_NOT_EXIST_ON_1610) &&
(cpu_is_omap1610() || cpu_is_omap1710()))) {
(*clkp)->parent = 0;
continue;
}
clk_register(*clkp);
}
info = omap_get_config(OMAP_TAG_CLOCK, struct omap_clock_config);
if (info != NULL) {
if (!cpu_is_omap1510())
crystal_type = info->system_clock_type;
}
#if defined(CONFIG_ARCH_OMAP730)
ck_ref.rate = 13000000;
#elif defined(CONFIG_ARCH_OMAP16XX)
if (crystal_type == 2)
ck_ref.rate = 19200000;
#endif
/* We want to be in syncronous scalable mode */
omap_writew(0x1000, ARM_SYSST);
/* Find the highest supported frequency and enable it */
if (select_table_rate(~0)) {
printk("System frequencies not set. Check your config.\n");
/* Guess sane values (60MHz) */
omap_writew(0x2290, DPLL_CTL);
omap_writew(0x1005, ARM_CKCTL);
ck_dpll1.rate = 60000000;
propagate_rate(&ck_dpll1);
printk("Clocking rate (xtal/DPLL1/MPU): %ld/%ld/%ld\n",
ck_ref.rate, ck_dpll1.rate, arm_ck.rate);
}
/* Cache rates for clocks connected to ck_ref (not dpll1) */
propagate_rate(&ck_ref);
#ifdef CONFIG_MACH_OMAP_PERSEUS2
/* Select slicer output as OMAP input clock */
omap_writew(omap_readw(OMAP730_PCC_UPLD_CTRL) & ~0x1, OMAP730_PCC_UPLD_CTRL);
#endif
/* Turn off DSP and ARM_TIMXO. Make sure ARM_INTHCK is not divided */
omap_writew(omap_readw(ARM_CKCTL) & 0x0fff, ARM_CKCTL);
/* Put DSP/MPUI into reset until needed */
omap_writew(0, ARM_RSTCT1);
omap_writew(1, ARM_RSTCT2);
omap_writew(0x400, ARM_IDLECT1);
/*
* According to OMAP5910 Erratum SYS_DMA_1, bit DMACK_REQ (bit 8)
* of the ARM_IDLECT2 register must be set to zero. The power-on
* default value of this bit is one.
*/
omap_writew(0x0000, ARM_IDLECT2); /* Turn LCD clock off also */
/*
* Only enable those clocks we will need, let the drivers
* enable other clocks as necessary
*/
clk_use(&armper_ck);
clk_use(&armxor_ck);
clk_use(&armtim_ck);
if (cpu_is_omap1510())
clk_enable(&arm_gpio_ck);
start_mputimer1(0xffffffff);
return 0;
}
/*
* linux/arch/arm/mach-omap/clock.h
*
* Copyright (C) 2004 Nokia corporation
* Written by Tuukka Tikkanen <tuukka.tikkanen@elektrobit.com>
* Based on clocks.h by Tony Lindgren, Gordon McNutt and RidgeRun, Inc
*
* 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.
*/
#ifndef __ARCH_ARM_OMAP_CLOCK_H
#define __ARCH_ARM_OMAP_CLOCK_H
struct module;
struct clk {
struct list_head node;
struct module *owner;
const char *name;
struct clk *parent;
unsigned long rate;
__s8 usecount;
__u8 flags;
__u32 enable_reg;
__u8 enable_bit;
__u8 rate_offset;
void (*recalc)(struct clk *);
int (*set_rate)(unsigned long);
long (*round_rate)(unsigned long);
};
struct mpu_rate {
unsigned long rate;
unsigned long xtal;
unsigned long pll_rate;
__u16 ckctl_val;
__u16 dpllctl_val;
};
/* Clock flags */
#define RATE_CKCTL 1
#define RATE_FIXED 2
#define RATE_PROPAGATES 4
#define VIRTUAL_CLOCK 8
#define ALWAYS_ENABLED 16
#define ENABLE_REG_32BIT 32
#define DOES_NOT_EXIST_ON_1510 64
#define DOES_NOT_EXIST_ON_1610 128 /* Including 1710 */
/* ARM_CKCTL bit shifts */
#define CKCTL_PERDIV_OFFSET 0
#define CKCTL_LCDDIV_OFFSET 2
#define CKCTL_ARMDIV_OFFSET 4
#define CKCTL_DSPDIV_OFFSET 6
#define CKCTL_TCDIV_OFFSET 8
#define CKCTL_DSPMMUDIV_OFFSET 10
/*#define ARM_TIMXO 12*/
#define EN_DSPCK 13
/*#define ARM_INTHCK_SEL 14*/ /* Divide-by-2 for mpu inth_ck */
/* ARM_IDLECT1 bit shifts */
/*#define IDLWDT_ARM 0*/
/*#define IDLXORP_ARM 1*/
/*#define IDLPER_ARM 2*/
/*#define IDLLCD_ARM 3*/
/*#define IDLLB_ARM 4*/
/*#define IDLHSAB_ARM 5*/
/*#define IDLIF_ARM 6*/
/*#define IDLDPLL_ARM 7*/
/*#define IDLAPI_ARM 8*/
/*#define IDLTIM_ARM 9*/
/*#define SETARM_IDLE 11*/
/* ARM_IDLECT2 bit shifts */
#define EN_WDTCK 0
#define EN_XORPCK 1
#define EN_PERCK 2
#define EN_LCDCK 3
#define EN_LBCK 4 /* Not on 1610/1710 */
/*#define EN_HSABCK 5*/
#define EN_APICK 6
#define EN_TIMCK 7
#define DMACK_REQ 8
#define EN_GPIOCK 9 /* Not on 1610/1710 */
/*#define EN_LBFREECK 10*/
#define EN_CKOUT_ARM 11
/* ARM_IDLECT3 bit shifts */
#define EN_OCPI_CK 0
#define EN_TC1_CK 2
#define EN_TC2_CK 4
/* Various register defines for clock controls scattered around OMAP chip */
#define USB_MCLK_EN 4 /* In ULPD_CLKC_CTRL */
#define USB_HOST_HHC_UHOST_EN 9 /* In MOD_CONF_CTRL_0 */
int clk_register(struct clk *clk);
void clk_unregister(struct clk *clk);
int clk_init(void);
#endif
/*
* Clock interface for OMAP
*
* Copyright (C) 2001 RidgeRun, Inc
* Written by Gordon McNutt <gmcnutt@ridgerun.com>
* Updated 2004 for Linux 2.6 by Tony Lindgren <tony@atomide.com>
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation; either version 2 of the License, or (at your
* option) any later version.
*
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN
* NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
* USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/types.h>
#include <linux/spinlock.h>
#include <asm/errno.h>
#include <asm/io.h>
#include <asm/arch/clocks.h>
#include <asm/arch/board.h>
extern void start_mputimer1(unsigned long load_val);
/* Input clock in MHz */
static unsigned int source_clock = 12;
/*
* We use one spinlock for all clock registers for now. We may want to
* change this to be clock register specific later on. Before we can do
* that, we need to map out the shared clock registers.
*/
static spinlock_t clock_lock = SPIN_LOCK_UNLOCKED;
typedef struct {
char *name;
__u8 flags;
ck_t parent;
unsigned long rate_reg; /* Clock rate register */
unsigned long enbl_reg; /* Enable register */
unsigned long idle_reg; /* Idle register */
unsigned long slct_reg; /* Select register */
__s8 rate_shift; /* Clock rate bit shift */
__s8 enbl_shift; /* Clock enable bit shift */
__s8 idle_shift; /* Clock idle bit shift */
__s8 slct_shift; /* Clock select bit shift */
} ck_info_t;
#define CK_NAME(ck) ck_info_table[ck].name
#define CK_FLAGS(ck) ck_info_table[ck].flags
#define CK_PARENT(ck) ck_info_table[ck].parent
#define CK_RATE_REG(ck) ck_info_table[ck].rate_reg
#define CK_ENABLE_REG(ck) ck_info_table[ck].enbl_reg
#define CK_IDLE_REG(ck) ck_info_table[ck].idle_reg
#define CK_SELECT_REG(ck) ck_info_table[ck].slct_reg
#define CK_RATE_SHIFT(ck) ck_info_table[ck].rate_shift
#define CK_ENABLE_SHIFT(ck) ck_info_table[ck].enbl_shift
#define CK_IDLE_SHIFT(ck) ck_info_table[ck].idle_shift
#define CK_SELECT_SHIFT(ck) ck_info_table[ck].slct_shift
#define CK_CAN_CHANGE_RATE(cl) (CK_FLAGS(ck) & CK_RATEF)
#define CK_CAN_DISABLE(cl) (CK_FLAGS(ck) & CK_ENABLEF)
#define CK_CAN_IDLE(cl) (CK_FLAGS(ck) & CK_IDLEF)
#define CK_CAN_SWITCH(cl) (CK_FLAGS(ck) & CK_SELECTF)
static ck_info_t ck_info_table[] = {
{
.name = "clkin",
.flags = 0,
.parent = OMAP_CLKIN,
}, {
.name = "ck_gen1",
.flags = CK_RATEF | CK_IDLEF,
.rate_reg = DPLL_CTL,
.idle_reg = ARM_IDLECT1,
.idle_shift = IDLDPLL_ARM,
.parent = OMAP_CLKIN,
}, {
.name = "ck_gen2",
.flags = 0,
.parent = OMAP_CK_GEN1,
}, {
.name = "ck_gen3",
.flags = 0,
.parent = OMAP_CK_GEN1,
}, {
.name = "tc_ck",
.flags = CK_RATEF | CK_IDLEF,
.parent = OMAP_CK_GEN3,
.rate_reg = ARM_CKCTL, /* ARM_CKCTL[TCDIV(9:8)] */
.idle_reg = ARM_IDLECT1,
.rate_shift = TCDIV,
.idle_shift = IDLIF_ARM
}, {
.name = "arm_ck",
.flags = CK_IDLEF | CK_RATEF,
.parent = OMAP_CK_GEN1,
.rate_reg = ARM_CKCTL, /* ARM_CKCTL[ARMDIV(5:4)] */
.idle_reg = ARM_IDLECT1,
.rate_shift = ARMDIV,
.idle_shift = SETARM_IDLE,
}, {
.name = "mpuper_ck",
.flags = CK_RATEF | CK_IDLEF | CK_ENABLEF,
.parent = OMAP_CK_GEN1,
.rate_reg = ARM_CKCTL, /* ARM_CKCTL[PERDIV(1:0)] */
.enbl_reg = ARM_IDLECT2,
.idle_reg = ARM_IDLECT1,
.rate_shift = PERDIV,
.enbl_shift = EN_PERCK,
.idle_shift = IDLPER_ARM
}, {
.name = "arm_gpio_ck",
.flags = CK_ENABLEF,
.parent = OMAP_CK_GEN1,
.enbl_reg = ARM_IDLECT2,
.enbl_shift = EN_GPIOCK
}, {
.name = "mpuxor_ck",
.flags = CK_ENABLEF | CK_IDLEF,
.parent = OMAP_CLKIN,
.idle_reg = ARM_IDLECT1,
.enbl_reg = ARM_IDLECT2,
.idle_shift = IDLXORP_ARM,
.enbl_shift = EN_XORPCK
}, {
.name = "mputim_ck",
.flags = CK_IDLEF | CK_ENABLEF | CK_SELECTF,
.parent = OMAP_CLKIN,
.idle_reg = ARM_IDLECT1,
.enbl_reg = ARM_IDLECT2,
.slct_reg = ARM_CKCTL,
.idle_shift = IDLTIM_ARM,
.enbl_shift = EN_TIMCK,
.slct_shift = ARM_TIMXO
}, {
.name = "mpuwd_ck",
.flags = CK_IDLEF | CK_ENABLEF,
.parent = OMAP_CLKIN,
.idle_reg = ARM_IDLECT1,
.enbl_reg = ARM_IDLECT2,
.idle_shift = IDLWDT_ARM,
.enbl_shift = EN_WDTCK,
}, {
.name = "dsp_ck",
.flags = CK_RATEF | CK_ENABLEF,
.parent = OMAP_CK_GEN2,
.rate_reg = ARM_CKCTL, /* ARM_CKCTL[DSPDIV(7:6)] */
.enbl_reg = ARM_CKCTL,
.rate_shift = DSPDIV,
.enbl_shift = EN_DSPCK,
}, {
.name = "dspmmu_ck",
.flags = CK_RATEF | CK_ENABLEF,
.parent = OMAP_CK_GEN2,
.rate_reg = ARM_CKCTL, /* ARM_CKCTL[DSPMMUDIV(11:10)] */
.enbl_reg = ARM_CKCTL,
.rate_shift = DSPMMUDIV,
.enbl_shift = EN_DSPCK,
}, {
.name = "dma_ck",
.flags = CK_RATEF | CK_IDLEF | CK_ENABLEF,
.parent = OMAP_CK_GEN3,
.rate_reg = ARM_CKCTL, /* ARM_CKCTL[TCDIV(9:8)] */
.idle_reg = ARM_IDLECT1,
.enbl_reg = ARM_IDLECT2,
.rate_shift = TCDIV,
.idle_shift = IDLIF_ARM,
.enbl_shift = DMACK_REQ
}, {
.name = "api_ck",
.flags = CK_RATEF | CK_IDLEF | CK_ENABLEF,
.parent = OMAP_CK_GEN3,
.rate_reg = ARM_CKCTL, /* ARM_CKCTL[TCDIV(9:8)] */
.idle_reg = ARM_IDLECT1,
.enbl_reg = ARM_IDLECT2,
.rate_shift = TCDIV,
.idle_shift = IDLAPI_ARM,
.enbl_shift = EN_APICK,
}, {
.name = "hsab_ck",
.flags = CK_RATEF | CK_IDLEF | CK_ENABLEF,
.parent = OMAP_CK_GEN3,
.rate_reg = ARM_CKCTL, /* ARM_CKCTL[TCDIV(9:8)] */
.idle_reg = ARM_IDLECT1,
.enbl_reg = ARM_IDLECT2,
.rate_shift = TCDIV,
.idle_shift = IDLHSAB_ARM,
.enbl_shift = EN_HSABCK,
}, {
.name = "lbfree_ck",
.flags = CK_RATEF | CK_ENABLEF,
.parent = OMAP_CK_GEN3,
.rate_reg = ARM_CKCTL, /* ARM_CKCTL[TCDIV(9:8)] */
.enbl_reg = ARM_IDLECT2,
.rate_shift = TCDIV,
.enbl_shift = EN_LBFREECK,
}, {
.name = "lb_ck",
.flags = CK_RATEF | CK_IDLEF | CK_ENABLEF,
.parent = OMAP_CK_GEN3,
.rate_reg = ARM_CKCTL, /* ARM_CKCTL[TCDIV(9:8)] */
.idle_reg = ARM_IDLECT1,
.enbl_reg = ARM_IDLECT2,
.rate_shift = TCDIV,
.idle_shift = IDLLB_ARM,
.enbl_shift = EN_LBCK,
}, {
.name = "lcd_ck",
.flags = CK_RATEF | CK_IDLEF | CK_ENABLEF,
.parent = OMAP_CK_GEN3,
.rate_reg = ARM_CKCTL, /* ARM_CKCTL[LCDDIV(3:2)] */
.idle_reg = ARM_IDLECT1,
.enbl_reg = ARM_IDLECT2,
.rate_shift = LCDDIV,
.idle_shift = IDLLCD_ARM,
.enbl_shift = EN_LCDCK,
},
};
/*****************************************************************************/
#define CK_IN_RANGE(ck) (!((ck < OMAP_CK_MIN) || (ck > OMAP_CK_MAX)))
int ck_auto_unclock = 1;
int ck_debug = 0;
#define CK_MAX_PLL_FREQ OMAP_CK_MAX_RATE
static __u32 ck_valid_table[CK_MAX_PLL_FREQ / 32 + 1];
static __u8 ck_lookup_table[CK_MAX_PLL_FREQ];
int
ck_set_input(ck_t ck, ck_t input)
{
int ret = 0, shift;
unsigned short reg;
unsigned long flags;
if (!CK_IN_RANGE(ck) || !CK_CAN_SWITCH(ck)) {
ret = -EINVAL;
goto exit;
}
reg = omap_readw(CK_SELECT_REG(ck));
shift = CK_SELECT_SHIFT(ck);
spin_lock_irqsave(&clock_lock, flags);
if (input == OMAP_CLKIN) {
reg &= ~(1 << shift);
omap_writew(reg, CK_SELECT_REG(ck));
goto exit;
} else if (input == CK_PARENT(ck)) {
reg |= (1 << shift);
omap_writew(reg, CK_SELECT_REG(ck));
goto exit;
}
ret = -EINVAL;
exit:
spin_unlock_irqrestore(&clock_lock, flags);
return ret;
}
int
ck_get_input(ck_t ck, ck_t * input)
{
int ret = -EINVAL;
unsigned long flags;
if (!CK_IN_RANGE(ck))
goto exit;
ret = 0;
spin_lock_irqsave(&clock_lock, flags);
if (CK_CAN_SWITCH(ck)) {
int shift;
unsigned short reg;
reg = omap_readw(CK_SELECT_REG(ck));
shift = CK_SELECT_SHIFT(ck);
if (reg & (1 << shift)) {
*input = CK_PARENT(ck);
goto exit;
}
}
*input = OMAP_CLKIN;
exit:
spin_unlock_irqrestore(&clock_lock, flags);
return ret;
}
static int
__ck_set_pll_rate(ck_t ck, int rate)
{
unsigned short pll;
unsigned long flags;
if ((rate < 0) || (rate > CK_MAX_PLL_FREQ))
return -EINVAL;
/* Scan downward for the closest matching frequency */
while (rate && !test_bit(rate, (unsigned long *)&ck_valid_table))
rate--;
if (!rate) {
printk(KERN_ERR "%s: couldn't find a matching rate\n",
__FUNCTION__);
return -EINVAL;
}
spin_lock_irqsave(&clock_lock, flags);
pll = omap_readw(CK_RATE_REG(ck));
/* Clear the rate bits */
pll &= ~(0x1f << 5);
/* Set the rate bits */
pll |= (ck_lookup_table[rate - 1] << 5);
omap_writew(pll, CK_RATE_REG(ck));
spin_unlock_irqrestore(&clock_lock, flags);
return 0;
}
static int
__ck_set_clkm_rate(ck_t ck, int rate)
{
int shift, prate, div, ret;
unsigned short reg;
unsigned long flags;
spin_lock_irqsave(&clock_lock, flags);
/*
* We can only set this clock's value to a fraction of its
* parent's value. The interface says I'll round down when necessary.
* So first let's get the parent's current rate.
*/
prate = ck_get_rate(CK_PARENT(ck));
/*
* Let's just start with the highest fraction and keep searching
* down through available rates until we find one less than or equal
* to the desired rate.
*/
for (div = 0; div < 4; div++) {
if (prate <= rate)
break;
prate = prate / 2;
}
/*
* Oops. Looks like the caller wants a rate lower than we can support.
*/
if (div == 5) {
printk(KERN_ERR "%s: %d is too low\n",
__FUNCTION__, rate);
ret = -EINVAL;
goto exit;
}
/*
* One more detail: if this clock supports more than one parent, then
* we're going to automatically switch over to the parent which runs
* through the divisor. For omap this is not ambiguous because for all
* such clocks one choice is always OMAP_CLKIN (which doesn't run
* through the divisor) and the other is whatever I encoded as
* CK_PARENT. Note that I wait until we get this far because I don't
* want to switch the input until we're sure this is going to work.
*/
if (CK_CAN_SWITCH(ck))
if ((ret = ck_set_input(ck, CK_PARENT(ck))) < 0) {
BUG();
goto exit;
}
/*
* At last, we can set the divisor. Clear the old rate bits and
* set the new ones.
*/
reg = omap_readw(CK_RATE_REG(ck));
shift = CK_RATE_SHIFT(ck);
reg &= ~(3 << shift);
reg |= (div << shift);
omap_writew(reg, CK_RATE_REG(ck));
/* And return the new (actual, after rounding down) rate. */
ret = prate;
exit:
spin_unlock_irqrestore(&clock_lock, flags);
return ret;
}
int
ck_set_rate(ck_t ck, int rate)
{
int ret = -EINVAL;
if (!CK_IN_RANGE(ck) || !CK_CAN_CHANGE_RATE(ck))
goto exit;
switch (ck) {
default:
ret = __ck_set_clkm_rate(ck, rate);
break;
case OMAP_CK_GEN1:
ret = __ck_set_pll_rate(ck, rate);
break;
};
exit:
return ret;
}
static int
__ck_get_pll_rate(ck_t ck)
{
int m, d;
unsigned short pll = omap_readw(CK_RATE_REG(ck));
m = (pll & (0x1f << 7)) >> 7;
m = m ? m : 1;
d = (pll & (3 << 5)) >> 5;
d++;
return ((source_clock * m) / d);
}
static int
__ck_get_clkm_rate(ck_t ck)
{
static int bits2div[] = { 1, 2, 4, 8 };
int in, bits, reg, shift;
reg = omap_readw(CK_RATE_REG(ck));
shift = CK_RATE_SHIFT(ck);
in = ck_get_rate(CK_PARENT(ck));
bits = (reg & (3 << shift)) >> shift;
return (in / bits2div[bits]);
}
int
ck_get_rate(ck_t ck)
{
int ret = 0;
ck_t parent;
if (!CK_IN_RANGE(ck)) {
ret = -EINVAL;
goto exit;
}
switch (ck) {
case OMAP_CK_GEN1:
ret = __ck_get_pll_rate(ck);
break;
case OMAP_CLKIN:
ret = source_clock;
break;
case OMAP_MPUXOR_CK:
case OMAP_CK_GEN2:
case OMAP_CK_GEN3:
case OMAP_ARM_GPIO_CK:
ret = ck_get_rate(CK_PARENT(ck));
break;
case OMAP_ARM_CK:
case OMAP_MPUPER_CK:
case OMAP_DSP_CK:
case OMAP_DSPMMU_CK:
case OMAP_LCD_CK:
case OMAP_TC_CK:
case OMAP_DMA_CK:
case OMAP_API_CK:
case OMAP_HSAB_CK:
case OMAP_LBFREE_CK:
case OMAP_LB_CK:
ret = __ck_get_clkm_rate(ck);
break;
case OMAP_MPUTIM_CK:
ck_get_input(ck, &parent);
ret = ck_get_rate(parent);
break;
case OMAP_MPUWD_CK:
/* Note that this evaluates to zero if source_clock is 12MHz. */
ret = source_clock / 14;
break;
default:
ret = -EINVAL;
break;
}
exit:
return ret;
}
int
ck_enable(ck_t ck)
{
unsigned short reg;
int ret = -EINVAL, shift;
unsigned long flags;
if (!CK_IN_RANGE(ck))
goto exit;
if (ck_debug)
printk(KERN_DEBUG "%s: %s\n", __FUNCTION__, CK_NAME(ck));
ret = 0;
if (!CK_CAN_DISABLE(ck))
/* Then it must be on... */
goto exit;
spin_lock_irqsave(&clock_lock, flags);
reg = omap_readw(CK_ENABLE_REG(ck));
shift = CK_ENABLE_SHIFT(ck);
reg |= (1 << shift);
omap_writew(reg, CK_ENABLE_REG(ck));
spin_unlock_irqrestore(&clock_lock, flags);
exit:
return ret;
}
int
ck_disable(ck_t ck)
{
unsigned short reg;
int ret = -EINVAL, shift;
unsigned long flags;
if (!CK_IN_RANGE(ck))
goto exit;
if (ck_debug)
printk(KERN_DEBUG "%s: %s\n", __FUNCTION__, CK_NAME(ck));
if (!CK_CAN_DISABLE(ck))
goto exit;
ret = 0;
if (ck == OMAP_CLKIN)
return -EINVAL;
spin_lock_irqsave(&clock_lock, flags);
reg = omap_readw(CK_ENABLE_REG(ck));
shift = CK_ENABLE_SHIFT(ck);
reg &= ~(1 << shift);
omap_writew(reg, CK_ENABLE_REG(ck));
spin_unlock_irqrestore(&clock_lock, flags);
exit:
return ret;
}
int ck_valid_rate(int rate)
{
return test_bit(rate, (unsigned long *)&ck_valid_table);
}
static void
__ck_make_lookup_table(void)
{
__u8 m, d;
memset(ck_valid_table, 0, sizeof (ck_valid_table));
for (m = 1; m < 32; m++)
for (d = 1; d < 5; d++) {
int rate = ((source_clock * m) / (d));
if (rate > CK_MAX_PLL_FREQ)
continue;
if (test_bit(rate, (unsigned long *)&ck_valid_table))
continue;
set_bit(rate, (unsigned long *)&ck_valid_table);
ck_lookup_table[rate - 1] = (m << 2) | (d - 1);
}
}
int __init
init_ck(void)
{
const struct omap_clock_config *info;
int crystal_type = 0; /* Default 12 MHz */
__ck_make_lookup_table();
info = omap_get_config(OMAP_TAG_CLOCK, struct omap_clock_config);
if (info != NULL) {
if (!cpu_is_omap1510())
crystal_type = info->system_clock_type;
}
/* We want to be in syncronous scalable mode */
omap_writew(0x1000, ARM_SYSST);
#if defined(CONFIG_OMAP_ARM_30MHZ)
omap_writew(0x1555, ARM_CKCTL);
omap_writew(0x2290, DPLL_CTL);
#elif defined(CONFIG_OMAP_ARM_60MHZ)
omap_writew(0x1005, ARM_CKCTL);
omap_writew(0x2290, DPLL_CTL);
#elif defined(CONFIG_OMAP_ARM_96MHZ)
omap_writew(0x1005, ARM_CKCTL);
omap_writew(0x2410, DPLL_CTL);
#elif defined(CONFIG_OMAP_ARM_120MHZ)
omap_writew(0x110a, ARM_CKCTL);
omap_writew(0x2510, DPLL_CTL);
#elif defined(CONFIG_OMAP_ARM_168MHZ)
omap_writew(0x110f, ARM_CKCTL);
omap_writew(0x2710, DPLL_CTL);
#elif defined(CONFIG_OMAP_ARM_182MHZ) && defined(CONFIG_ARCH_OMAP730)
omap_writew(0x250E, ARM_CKCTL);
omap_writew(0x2710, DPLL_CTL);
#elif defined(CONFIG_OMAP_ARM_192MHZ) && (defined(CONFIG_ARCH_OMAP1610) || defined(CONFIG_ARCH_OMAP5912) \
|| defined(CONFIG_ARCH_OMAP1710))
omap_writew(0x150f, ARM_CKCTL);
if (crystal_type == 2) {
source_clock = 13; /* MHz */
omap_writew(0x2510, DPLL_CTL);
} else
omap_writew(0x2810, DPLL_CTL);
#elif defined(CONFIG_OMAP_ARM_195MHZ) && defined(CONFIG_ARCH_OMAP730)
omap_writew(0x250E, ARM_CKCTL);
omap_writew(0x2790, DPLL_CTL);
#else
#error "OMAP MHZ not set, please run make xconfig"
#endif
#ifdef CONFIG_MACH_OMAP_PERSEUS2
/* Select slicer output as OMAP input clock */
omap_writew(omap_readw(OMAP730_PCC_UPLD_CTRL) & ~0x1, OMAP730_PCC_UPLD_CTRL);
#endif
/* Turn off some other junk the bootloader might have turned on */
/* Turn off DSP, ARM_INTHCK, ARM_TIMXO */
omap_writew(omap_readw(ARM_CKCTL) & 0x0fff, ARM_CKCTL);
/* Put DSP/MPUI into reset until needed */
omap_writew(0, ARM_RSTCT1);
omap_writew(1, ARM_RSTCT2);
omap_writew(0x400, ARM_IDLECT1);
/*
* According to OMAP5910 Erratum SYS_DMA_1, bit DMACK_REQ (bit 8)
* of the ARM_IDLECT2 register must be set to zero. The power-on
* default value of this bit is one.
*/
omap_writew(0x0000, ARM_IDLECT2); /* Turn LCD clock off also */
/*
* Only enable those clocks we will need, let the drivers
* enable other clocks as necessary
*/
ck_enable(OMAP_MPUPER_CK);
ck_enable(OMAP_ARM_GPIO_CK);
ck_enable(OMAP_MPUXOR_CK);
//ck_set_rate(OMAP_MPUTIM_CK, OMAP_CLKIN);
ck_enable(OMAP_MPUTIM_CK);
start_mputimer1(0xffffffff);
return 0;
}
EXPORT_SYMBOL(ck_get_rate);
EXPORT_SYMBOL(ck_set_rate);
EXPORT_SYMBOL(ck_enable);
EXPORT_SYMBOL(ck_disable);
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment