/*
* arch/arm/mach-pnx4008/clock.c
*
* Clock control driver for PNX4008
*
* Authors: Vitaly Wool, Dmitry Chigirev <source@mvista.com>
* Generic clock management functions are partially based on:
* linux/arch/arm/mach-omap/clock.c
*
* 2005-2006 (c) MontaVista Software, Inc. This file is licensed under
* the terms of the GNU General Public License version 2. This program
* is licensed "as is" without any warranty of any kind, whether express
* or implied.
*/
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/list.h>
#include <linux/errno.h>
#include <linux/device.h>
#include <linux/err.h>
#include <linux/delay.h>
#include <asm/semaphore.h>
#include <asm/hardware.h>
#include <asm/io.h>
#include <asm/arch/clock.h>
#include "clock.h"
/*forward declaration*/
static struct clk per_ck;
static struct clk hclk_ck;
static struct clk ck_1MHz;
static struct clk ck_13MHz;
static struct clk ck_pll1;
static int local_set_rate(struct clk *clk, u32 rate);
static inline void clock_lock(void)
{
local_irq_disable();
}
static inline void clock_unlock(void)
{
local_irq_enable();
}
static void propagate_rate(struct clk *clk)
{
struct clk *tmp_clk;
tmp_clk = clk;
while (tmp_clk->propagate_next) {
tmp_clk = tmp_clk->propagate_next;
local_set_rate(tmp_clk, tmp_clk->user_rate);
}
}
static inline void clk_reg_disable(struct clk *clk)
{
if (clk->enable_reg)
__raw_writel(__raw_readl(clk->enable_reg) &
~(1 << clk->enable_shift), clk->enable_reg);
}
static inline void clk_reg_enable(struct clk *clk)
{
if (clk->enable_reg)
__raw_writel(__raw_readl(clk->enable_reg) |
(1 << clk->enable_shift), clk->enable_reg);
}
static inline void clk_reg_disable1(struct clk *clk)
{
if (clk->enable_reg1)
__raw_writel(__raw_readl(clk->enable_reg1) &
~(1 << clk->enable_shift1), clk->enable_reg1);
}
static inline void clk_reg_enable1(struct clk *clk)
{
if (clk->enable_reg1)
__raw_writel(__raw_readl(clk->enable_reg1) |
(1 << clk->enable_shift1), clk->enable_reg1);
}
static int clk_wait_for_pll_lock(struct clk *clk)
{
int i;
i = 0;
while (i++ < 0xFFF && !(__raw_readl(clk->scale_reg) & 1)) ; /*wait for PLL to lock */
if (!(__raw_readl(clk->scale_reg) & 1)) {
printk(KERN_ERR
"%s ERROR: failed to lock, scale reg data: %x\n",
clk->name, __raw_readl(clk->scale_reg));
return -1;
}
return 0;
}
static int switch_to_dirty_13mhz(struct clk *clk)
{
int i;
int ret;
u32 tmp_reg;
ret = 0;
if (!clk->rate)
clk_reg_enable1(clk);
tmp_reg = __raw_readl(clk->parent_switch_reg);
/*if 13Mhz clock selected, select 13'MHz (dirty) source from OSC */
if (!(tmp_reg & 1)) {
tmp_reg |= (1 << 1); /* Trigger switch to 13'MHz (dirty) clock */
__raw_writel(tmp_reg, clk->parent_switch_reg);
i = 0;
while (i++ < 0xFFF && !(__raw_readl(clk->parent_switch_reg) & 1)) ; /*wait for 13'MHz selection status */
if (!(__raw_readl(clk->parent_switch_reg) & 1)) {
printk(KERN_ERR
"%s ERROR: failed to select 13'MHz, parent sw reg data: %x\n",
clk->name, __raw_readl(clk->parent_switch_reg));
ret = -1;
}
}
if (!clk->rate)
clk_reg_disable1(clk);
return ret;
}
static int switch_to_clean_13mhz(struct clk *clk)
{
int i;
int ret;
u32 tmp_reg;
ret = 0;
if (!clk->rate)
clk_reg_enable1(clk);
tmp_reg = __raw_readl(clk->parent_switch_reg);
/*if 13'Mhz clock selected, select 13MHz (clean) source from OSC */
if (tmp_reg & 1) {
tmp_reg &= ~(1 << 1); /* Trigger switch to 13MHz (clean) clock */
__raw_writel(tmp_reg, clk->parent_switch_reg);
i = 0;
while (i