/*
* linux/arch/arm/mach-omap2/clock.c
*
* Copyright (C) 2005-2008 Texas Instruments, Inc.
* Copyright (C) 2004-2008 Nokia Corporation
*
* Contacts:
* Richard Woodruff <r-woodruff2@ti.com>
* Paul Walmsley
*
* 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.
*/
#undef DEBUG
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/device.h>
#include <linux/list.h>
#include <linux/errno.h>
#include <linux/delay.h>
#include <linux/clk.h>
#include <linux/io.h>
#include <linux/bitops.h>
#include <plat/clock.h>
#include <plat/clockdomain.h>
#include <plat/cpu.h>
#include <plat/prcm.h>
#include <asm/div64.h>
#include <plat/sdrc.h>
#include "sdrc.h"
#include "clock.h"
#include "prm.h"
#include "prm-regbits-24xx.h"
#include "cm.h"
#include "cm-regbits-24xx.h"
#include "cm-regbits-34xx.h"
/* DPLL rate rounding: minimum DPLL multiplier, divider values */
#define DPLL_MIN_MULTIPLIER 1
#define DPLL_MIN_DIVIDER 1
/* Possible error results from _dpll_test_mult */
#define DPLL_MULT_UNDERFLOW -1
/*
* Scale factor to mitigate roundoff errors in DPLL rate rounding.
* The higher the scale factor, the greater the risk of arithmetic overflow,
* but the closer the rounded rate to the target rate. DPLL_SCALE_FACTOR
* must be a power of DPLL_SCALE_BASE.
*/
#define DPLL_SCALE_FACTOR 64
#define DPLL_SCALE_BASE 2
#define DPLL_ROUNDING_VAL ((DPLL_SCALE_BASE / 2) * \
(DPLL_SCALE_FACTOR / DPLL_SCALE_BASE))
/* DPLL valid Fint frequency band limits - from 34xx TRM Section 4.7.6.2 */
#define DPLL_FINT_BAND1_MIN 750000
#define DPLL_FINT_BAND1_MAX 2100000
#define DPLL_FINT_BAND2_MIN 7500000
#define DPLL_FINT_BAND2_MAX 21000000
/* _dpll_test_fint() return codes */
#define DPLL_FINT_UNDERFLOW -1
#define DPLL_FINT_INVALID -2
u8 cpu_mask;
/*-------------------------------------------------------------------------
* OMAP2/3/4 specific clock functions
*-------------------------------------------------------------------------*/
void omap2_init_dpll_parent(struct clk *clk)
{
u32 v;
struct dpll_data *dd;
dd = clk->dpll_data;
if (!dd)
return;
/* Return bypass rate if DPLL is bypassed */
v = __raw_readl(dd->control_reg);
v &= dd->enable_mask;
v >>= __ffs(dd->enable_mask);
/* Reparent in case the dpll is in bypass */
if (cpu_is_omap24xx()) {
if (v == OMAP2XXX_EN_DPLL_LPBYPASS ||
v == OMAP2XXX_EN_DPLL_FRBYPASS)
clk_reparent(clk, dd->clk_bypass);
} else if (cpu_is_omap34xx()) {
if (v == OMAP3XXX_EN_DPLL_LPBYPASS ||
v == OMAP3XXX_EN_DPLL_FRBYPASS)
clk_reparent(clk, dd->clk_bypass);
} else if (cpu_is_omap44xx()) {
if (v == OMAP4XXX_EN_DPLL_LPBYPASS ||
v == OMAP4XXX_EN_DPLL_FRBYPASS ||
v == OMAP4XXX_EN_DPLL_MNBYPASS)
clk_reparent(clk, dd->clk_bypass);
}
return;
}
/**
* _omap2xxx_clk_commit - commit clock parent/rate changes in hardware
* @clk: struct clk *
*
* If @clk has the DELAYED_APP flag set, meaning that parent/rate changes
* don't take effect until the VALID_CONFIG bit is written, write the
* VALID_CONFIG bit and wait for the write to complete. No return value.
*/
static void _omap2xxx_clk_commit(struct clk *clk)
{
if (!cpu_is_omap24xx())
return;
if (!(clk->flags & DELAYED_APP))
return;
prm_write_mod_reg(OMAP24XX_VALID_CONFIG, OMAP24XX_GR_MOD,
OMAP2_PRCM_CLKCFG_CTRL_OFFSET);
/* OCP barrier */
prm_read_mod_reg(OMAP24XX_GR_MOD, OMAP2_PRCM_CLKCFG_CTRL_OFFSET);
}
/*
* _dpll_test_fint - test whether an Fint value is valid for the DPLL
* @clk: DPLL struct clk to test
* @n: divider value (N) to test
*
* Tests whether a particular divider @n will result in a valid DPLL
* internal clock frequency Fint. See the 34xx TRM 4.7.6.2 "DPLL Jitter
* Correction". Returns 0 if OK, -1 if the enclosing loop can terminate
* (assuming that it is counting N upwards), or -2 if the enclosing loop
* should skip to the next iteration (again assuming N is increasing).
*/
static int _dpll_test_fint(struct clk *clk, u8 n)
{
struct dpll_data *dd;
long fint;
int ret = 0;
dd = clk->dpll_data;
/* DPLL divider must result in a valid jitter correction val */
fint = clk->parent->rate / (n + 1);
if (fint < DPLL_FINT_BAND1_MIN) {
pr_debug("rejecting n=%d due to Fint failure, "
"lowering max_divider\n", n);
dd->max_divider = n;
ret = DPLL_FINT_UNDERFLOW;
} else if (fint > DPLL_FINT_BAND1_MAX &&
fint < DPLL_FINT_BAND2_MIN) {
pr_debug("rejecting n=%d due to Fint failure\n", n);
ret = DPLL_FINT_INVALID;
} else if (fint > DPLL_FINT_BAND2_MAX) {
pr_debug("rejecting n=%d due to Fint failure, "
"boosting min_divider\n", n);
dd->min_divider = n;
ret = DPLL_FINT_INVALID;
}
return ret;
}
/**
* omap2_init_clk_clkdm - look up a clockdomain name, store pointer in clk
* @clk: OMAP clock struct ptr to use
*
* Convert a clockdomain name stored in a struct clk 'clk' into a
* clockdomain pointer, and save it into the struct clk. Intended to be