/*
* Copyright 2004-2007 Freescale Semiconductor, Inc. All Rights Reserved.
* Copyright 2008 Juergen Beisert, kernel@pengutronix.de
* Copyright 2008 Martin Fuzzey, mfuzzey@gmail.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 program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* 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., 51 Franklin Street, Fifth Floor, Boston,
* MA 02110-1301, USA.
*/
#include <linux/clk.h>
#include <linux/io.h>
#include <linux/module.h>
#include <mach/clock.h>
#include <mach/common.h>
#include <asm/clkdev.h>
#include <asm/div64.h>
#include "crm_regs.h"
static int _clk_enable(struct clk *clk)
{
u32 reg;
reg = __raw_readl(clk->enable_reg);
reg |= 1 << clk->enable_shift;
__raw_writel(reg, clk->enable_reg);
return 0;
}
static void _clk_disable(struct clk *clk)
{
u32 reg;
reg = __raw_readl(clk->enable_reg);
reg &= ~(1 << clk->enable_shift);
__raw_writel(reg, clk->enable_reg);
}
static unsigned long _clk_generic_round_rate(struct clk *clk,
unsigned long rate,
u32 max_divisor)
{
u32 div;
unsigned long parent_rate;
parent_rate = clk_get_rate(clk->parent);
div = parent_rate / rate;
if (parent_rate % rate)
div++;
if (div > max_divisor)
div = max_divisor;
return parent_rate / div;
}
static int _clk_spll_enable(struct clk *clk)
{
u32 reg;
reg = __raw_readl(CCM_CSCR);
reg |= CCM_CSCR_SPEN;
__raw_writel(reg, CCM_CSCR);
while ((__raw_readl(CCM_SPCTL1) & CCM_SPCTL1_LF) == 0)
;
return 0;
}
static void _clk_spll_disable(struct clk *clk)
{
u32 reg;
reg = __raw_readl(CCM_CSCR);
reg &= ~CCM_CSCR_SPEN;
__raw_writel(reg, CCM_CSCR);
}
#define CSCR() (__raw_readl(CCM_CSCR))
#define PCDR0() (__raw_readl(CCM_PCDR0))
#define PCDR1() (__raw_readl(CCM_PCDR1))
static unsigned long _clk_perclkx_round_rate(struct clk *clk,
unsigned long rate)
{
return _clk_generic_round_rate(clk, rate, 64);
}
static int _clk_perclkx_set_rate(struct clk *clk, unsigned long rate)
{
u32 reg;
u32 div;
unsigned long parent_rate;
parent_rate = clk_get_rate(clk->parent);
if (clk->id < 0 || clk->id > 3)
return -EINVAL;
div = parent_rate / rate;
if (div > 64 || div < 1 || ((parent_rate / div) != rate))
return -EINVAL;
div--;
reg =
__raw_readl(CCM_PCDR1) & ~(CCM_PCDR1_PERDIV1_MASK <<
(clk->id << 3));
reg |= div << (clk->id << 3);
__raw_writel(reg, CCM_PCDR1);
return 0;
}
static unsigned long _clk_usb_recalc(struct clk *clk)
{
unsigned long usb_pdf;
unsigned long parent_rate;
parent_rate = clk_get_rate(clk->parent);
usb_pdf = (CSCR() & CCM_CSCR_USB_MASK) >> CCM_CSCR_USB_OFFSET;
return parent_rate / (usb_pdf + 1U);
}
static unsigned long _clk_usb_round_rate(struct clk *clk,
unsigned long rate)
{
return _clk_generic_round_rate(clk, rate, 8);
}
static int _clk_usb_set_rate(struct clk *clk, unsigned long rate)
{
u32 reg;
u32 div;
unsigned long parent_rate;
parent_rate = clk_get_rate(clk->parent);
div = parent_rate / rate;
if (div > 8 || div < 1 || ((parent_rate / div) != rate))
return -EINVAL;
div--;
reg