Merge branch 'for-4.2/ti-clk-move' of https://github.com/t-kristo/linux-pm into clk-next

From Tero Kristo:
  "This pull request contains the TI clock driver set to move the
   clock implementations under clock driver. Some small portions of
   the clock driver code still remain under mach-omap2 after this,
   it should be decided whether this code is now obsolete and should
   be deleted or should someone try to fix it."

Slight merge conflicts with determine_rate prototype changes.
此提交包含在:
Stephen Boyd
2015-07-28 11:58:26 -07:00
當前提交 4d34105620
共有 39 個檔案被更改,包括 1323 行新增2385 行删除

查看文件

@@ -12,8 +12,7 @@ obj-y := id.o io.o control.o mux.o devices.o fb.o serial.o timer.o pm.o \
hwmod-common = omap_hwmod.o omap_hwmod_reset.o \
omap_hwmod_common_data.o
clock-common = clock.o clock_common_data.o \
clkt_dpll.o clkt_clksel.o
clock-common = clock.o
secure-common = omap-smc.o omap-secure.o
obj-$(CONFIG_ARCH_OMAP2) += $(omap-2-3-common) $(hwmod-common)
@@ -182,24 +181,17 @@ obj-$(CONFIG_SOC_DRA7XX) += $(clockdomain-common)
obj-$(CONFIG_SOC_DRA7XX) += clockdomains7xx_data.o
# Clock framework
obj-$(CONFIG_ARCH_OMAP2) += $(clock-common) clock2xxx.o
obj-$(CONFIG_ARCH_OMAP2) += $(clock-common)
obj-$(CONFIG_ARCH_OMAP2) += clkt2xxx_dpllcore.o
obj-$(CONFIG_ARCH_OMAP2) += clkt2xxx_virt_prcm_set.o
obj-$(CONFIG_ARCH_OMAP2) += clkt2xxx_dpll.o clkt_iclk.o
obj-$(CONFIG_SOC_OMAP2430) += clock2430.o
obj-$(CONFIG_ARCH_OMAP3) += $(clock-common) clock3xxx.o
obj-$(CONFIG_ARCH_OMAP3) += clock34xx.o clkt34xx_dpll3m2.o
obj-$(CONFIG_ARCH_OMAP3) += clock3517.o clock36xx.o
obj-$(CONFIG_ARCH_OMAP3) += dpll3xxx.o
obj-$(CONFIG_ARCH_OMAP3) += clkt_iclk.o
obj-$(CONFIG_ARCH_OMAP2) += clkt2xxx_dpll.o
obj-$(CONFIG_ARCH_OMAP3) += $(clock-common)
obj-$(CONFIG_ARCH_OMAP3) += clkt34xx_dpll3m2.o
obj-$(CONFIG_ARCH_OMAP4) += $(clock-common)
obj-$(CONFIG_ARCH_OMAP4) += dpll3xxx.o dpll44xx.o
obj-$(CONFIG_SOC_AM33XX) += $(clock-common) dpll3xxx.o
obj-$(CONFIG_SOC_AM33XX) += $(clock-common)
obj-$(CONFIG_SOC_OMAP5) += $(clock-common)
obj-$(CONFIG_SOC_OMAP5) += dpll3xxx.o dpll44xx.o
obj-$(CONFIG_SOC_DRA7XX) += $(clock-common)
obj-$(CONFIG_SOC_DRA7XX) += dpll3xxx.o dpll44xx.o
obj-$(CONFIG_SOC_AM43XX) += $(clock-common) dpll3xxx.o
obj-$(CONFIG_SOC_AM43XX) += $(clock-common)
# OMAP2 clock rate set data (old "OPP" data)
obj-$(CONFIG_SOC_OMAP2420) += opp2420_data.o

查看文件

@@ -23,12 +23,13 @@
#include "clock.h"
#include "clock3xxx.h"
#include "clock34xx.h"
#include "sdrc.h"
#include "sram.h"
#define CYCLES_PER_MHZ 1000000
struct clk *sdrc_ick_p, *arm_fck_p;
/*
* CORE DPLL (DPLL3) M2 divider rate programming functions
*
@@ -60,7 +61,9 @@ int omap3_core_dpll_m2_set_rate(struct clk_hw *hw, unsigned long rate,
if (!clk || !rate)
return -EINVAL;
validrate = omap2_clksel_round_rate_div(clk, rate, &new_div);
new_div = DIV_ROUND_UP(parent_rate, rate);
validrate = parent_rate / new_div;
if (validrate != rate)
return -EINVAL;

查看文件

@@ -1,466 +0,0 @@
/*
* clkt_clksel.c - OMAP2/3/4 clksel clock functions
*
* Copyright (C) 2005-2008 Texas Instruments, Inc.
* Copyright (C) 2004-2010 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.
*
*
* clksel clocks are clocks that do not have a fixed parent, or that
* can divide their parent's rate, or possibly both at the same time, based
* on the contents of a hardware register bitfield.
*
* All of the various mux and divider settings can be encoded into
* struct clksel* data structures, and then these can be autogenerated
* from some hardware database for each new chip generation. This
* should avoid the need to write, review, and validate a lot of new
* clock code for each new chip, since it can be exported from the SoC
* design flow. This is now done on OMAP4.
*
* The fusion of mux and divider clocks is a software creation. In
* hardware reality, the multiplexer (parent selection) and the
* divider exist separately. XXX At some point these clksel clocks
* should be split into "divider" clocks and "mux" clocks to better
* match the hardware.
*
* (The name "clksel" comes from the name of the corresponding
* register field in the OMAP2/3 family of SoCs.)
*
* XXX Currently these clocks are only used in the OMAP2/3/4 code, but
* many of the OMAP1 clocks should be convertible to use this
* mechanism.
*/
#undef DEBUG
#include <linux/kernel.h>
#include <linux/errno.h>
#include <linux/clk-provider.h>
#include <linux/io.h>
#include <linux/bug.h>
#include "clock.h"
/* Private functions */
/**
* _get_clksel_by_parent() - return clksel struct for a given clk & parent
* @clk: OMAP struct clk ptr to inspect
* @src_clk: OMAP struct clk ptr of the parent clk to search for
*
* Scan the struct clksel array associated with the clock to find
* the element associated with the supplied parent clock address.
* Returns a pointer to the struct clksel on success or NULL on error.
*/
static const struct clksel *_get_clksel_by_parent(struct clk_hw_omap *clk,
struct clk *src_clk)
{
const struct clksel *clks;
if (!src_clk)
return NULL;
for (clks = clk->clksel; clks->parent; clks++)
if (clks->parent == src_clk)
break; /* Found the requested parent */
if (!clks->parent) {
/* This indicates a data problem */
WARN(1, "clock: %s: could not find parent clock %s in clksel array\n",
__clk_get_name(clk->hw.clk), __clk_get_name(src_clk));
return NULL;
}
return clks;
}
/**
* _write_clksel_reg() - program a clock's clksel register in hardware
* @clk: struct clk * to program
* @v: clksel bitfield value to program (with LSB at bit 0)
*
* Shift the clksel register bitfield value @v to its appropriate
* location in the clksel register and write it in. This function
* will ensure that the write to the clksel_reg reaches its
* destination before returning -- important since PRM and CM register
* accesses can be quite slow compared to ARM cycles -- but does not
* take into account any time the hardware might take to switch the
* clock source.
*/
static void _write_clksel_reg(struct clk_hw_omap *clk, u32 field_val)
{
u32 v;
v = omap2_clk_readl(clk, clk->clksel_reg);
v &= ~clk->clksel_mask;
v |= field_val << __ffs(clk->clksel_mask);
omap2_clk_writel(v, clk, clk->clksel_reg);
v = omap2_clk_readl(clk, clk->clksel_reg); /* OCP barrier */
}
/**
* _clksel_to_divisor() - turn clksel field value into integer divider
* @clk: OMAP struct clk to use
* @field_val: register field value to find
*
* Given a struct clk of a rate-selectable clksel clock, and a register field
* value to search for, find the corresponding clock divisor. The register
* field value should be pre-masked and shifted down so the LSB is at bit 0
* before calling. Returns 0 on error or returns the actual integer divisor
* upon success.
*/
static u32 _clksel_to_divisor(struct clk_hw_omap *clk, u32 field_val)
{
const struct clksel *clks;
const struct clksel_rate *clkr;
struct clk *parent;
parent = __clk_get_parent(clk->hw.clk);
clks = _get_clksel_by_parent(clk, parent);
if (!clks)
return 0;
for (clkr = clks->rates; clkr->div; clkr++) {
if (!(clkr->flags & cpu_mask))
continue;
if (clkr->val == field_val)
break;
}
if (!clkr->div) {
/* This indicates a data error */
WARN(1, "clock: %s: could not find fieldval %d for parent %s\n",
__clk_get_name(clk->hw.clk), field_val,
__clk_get_name(parent));
return 0;
}
return clkr->div;
}
/**
* _divisor_to_clksel() - turn clksel integer divisor into a field value
* @clk: OMAP struct clk to use
* @div: integer divisor to search for
*
* Given a struct clk of a rate-selectable clksel clock, and a clock
* divisor, find the corresponding register field value. Returns the
* register field value _before_ left-shifting (i.e., LSB is at bit
* 0); or returns 0xFFFFFFFF (~0) upon error.
*/
static u32 _divisor_to_clksel(struct clk_hw_omap *clk, u32 div)
{
const struct clksel *clks;
const struct clksel_rate *clkr;
struct clk *parent;
/* should never happen */
WARN_ON(div == 0);
parent = __clk_get_parent(clk->hw.clk);
clks = _get_clksel_by_parent(clk, parent);
if (!clks)
return ~0;
for (clkr = clks->rates; clkr->div; clkr++) {
if (!(clkr->flags & cpu_mask))
continue;
if (clkr->div == div)
break;
}
if (!clkr->div) {
pr_err("clock: %s: could not find divisor %d for parent %s\n",
__clk_get_name(clk->hw.clk), div,
__clk_get_name(parent));
return ~0;
}
return clkr->val;
}
/**
* _read_divisor() - get current divisor applied to parent clock (from hdwr)
* @clk: OMAP struct clk to use.
*
* Read the current divisor register value for @clk that is programmed
* into the hardware, convert it into the actual divisor value, and
* return it; or return 0 on error.
*/
static u32 _read_divisor(struct clk_hw_omap *clk)
{
u32 v;
if (!clk->clksel || !clk->clksel_mask)
return 0;
v = omap2_clk_readl(clk, clk->clksel_reg);
v &= clk->clksel_mask;
v >>= __ffs(clk->clksel_mask);
return _clksel_to_divisor(clk, v);
}
/* Public functions */
/**
* omap2_clksel_round_rate_div() - find divisor for the given clock and rate
* @clk: OMAP struct clk to use
* @target_rate: desired clock rate
* @new_div: ptr to where we should store the divisor
*
* Finds 'best' divider value in an array based on the source and target
* rates. The divider array must be sorted with smallest divider first.
* This function is also used by the DPLL3 M2 divider code.
*
* Returns the rounded clock rate or returns 0xffffffff on error.
*/
u32 omap2_clksel_round_rate_div(struct clk_hw_omap *clk,
unsigned long target_rate,
u32 *new_div)
{
unsigned long test_rate;
const struct clksel *clks;
const struct clksel_rate *clkr;
u32 last_div = 0;
struct clk *parent;
unsigned long parent_rate;
const char *clk_name;
parent = __clk_get_parent(clk->hw.clk);
clk_name = __clk_get_name(clk->hw.clk);
parent_rate = __clk_get_rate(parent);
if (!clk->clksel || !clk->clksel_mask)
return ~0;
pr_debug("clock: clksel_round_rate_div: %s target_rate %ld\n",
clk_name, target_rate);
*new_div = 1;
clks = _get_clksel_by_parent(clk, parent);
if (!clks)
return ~0;
for (clkr = clks->rates; clkr->div; clkr++) {
if (!(clkr->flags & cpu_mask))
continue;
/* Sanity check */
if (clkr->div <= last_div)
pr_err("clock: %s: clksel_rate table not sorted\n",
clk_name);
last_div = clkr->div;
test_rate = parent_rate / clkr->div;
if (test_rate <= target_rate)
break; /* found it */
}
if (!clkr->div) {
pr_err("clock: %s: could not find divisor for target rate %ld for parent %s\n",
clk_name, target_rate, __clk_get_name(parent));
return ~0;
}
*new_div = clkr->div;
pr_debug("clock: new_div = %d, new_rate = %ld\n", *new_div,
(parent_rate / clkr->div));
return parent_rate / clkr->div;
}
/*
* Clocktype interface functions to the OMAP clock code
* (i.e., those used in struct clk field function pointers, etc.)
*/
/**
* omap2_clksel_find_parent_index() - return the array index of the current
* hardware parent of @hw
* @hw: struct clk_hw * to find the current hardware parent of
*
* Given a struct clk_hw pointer @hw to the 'hw' member of a struct
* clk_hw_omap record representing a source-selectable hardware clock,
* read the hardware register and determine what its parent is
* currently set to. Intended to be called only by the common clock
* framework struct clk_hw_ops.get_parent function pointer. Return
* the array index of this parent clock upon success -- there is no
* way to return an error, so if we encounter an error, just WARN()
* and pretend that we know that we're doing.
*/
u8 omap2_clksel_find_parent_index(struct clk_hw *hw)
{
struct clk_hw_omap *clk = to_clk_hw_omap(hw);
const struct clksel *clks;
const struct clksel_rate *clkr;
u32 r, found = 0;
struct clk *parent;
const char *clk_name;
int ret = 0, f = 0;
parent = __clk_get_parent(hw->clk);
clk_name = __clk_get_name(hw->clk);
/* XXX should be able to return an error */
WARN((!clk->clksel || !clk->clksel_mask),
"clock: %s: attempt to call on a non-clksel clock", clk_name);
r = omap2_clk_readl(clk, clk->clksel_reg) & clk->clksel_mask;
r >>= __ffs(clk->clksel_mask);
for (clks = clk->clksel; clks->parent && !found; clks++) {
for (clkr = clks->rates; clkr->div && !found; clkr++) {
if (!(clkr->flags & cpu_mask))
continue;
if (clkr->val == r) {
found = 1;
ret = f;
}
}
f++;
}
/* This indicates a data error */
WARN(!found, "clock: %s: init parent: could not find regval %0x\n",
clk_name, r);
return ret;
}
/**
* omap2_clksel_recalc() - function ptr to pass via struct clk .recalc field
* @clk: struct clk *
*
* This function is intended to be called only by the clock framework.
* Each clksel clock should have its struct clk .recalc field set to this
* function. Returns the clock's current rate, based on its parent's rate
* and its current divisor setting in the hardware.
*/
unsigned long omap2_clksel_recalc(struct clk_hw *hw, unsigned long parent_rate)
{
unsigned long rate;
u32 div = 0;
struct clk_hw_omap *clk = to_clk_hw_omap(hw);
if (!parent_rate)
return 0;
div = _read_divisor(clk);
if (!div)
rate = parent_rate;
else
rate = parent_rate / div;
pr_debug("%s: recalc'd %s's rate to %lu (div %d)\n", __func__,
__clk_get_name(hw->clk), rate, div);
return rate;
}
/**
* omap2_clksel_round_rate() - find rounded rate for the given clock and rate
* @clk: OMAP struct clk to use
* @target_rate: desired clock rate
*
* This function is intended to be called only by the clock framework.
* Finds best target rate based on the source clock and possible dividers.
* rates. The divider array must be sorted with smallest divider first.
*
* Returns the rounded clock rate or returns 0xffffffff on error.
*/
long omap2_clksel_round_rate(struct clk_hw *hw, unsigned long target_rate,
unsigned long *parent_rate)
{
struct clk_hw_omap *clk = to_clk_hw_omap(hw);
u32 new_div;
return omap2_clksel_round_rate_div(clk, target_rate, &new_div);
}
/**
* omap2_clksel_set_rate() - program clock rate in hardware
* @clk: struct clk * to program rate
* @rate: target rate to program
*
* This function is intended to be called only by the clock framework.
* Program @clk's rate to @rate in the hardware. The clock can be
* either enabled or disabled when this happens, although if the clock
* is enabled, some downstream devices may glitch or behave
* unpredictably when the clock rate is changed - this depends on the
* hardware. This function does not currently check the usecount of
* the clock, so if multiple drivers are using the clock, and the rate
* is changed, they will all be affected without any notification.
* Returns -EINVAL upon error, or 0 upon success.
*/
int omap2_clksel_set_rate(struct clk_hw *hw, unsigned long rate,
unsigned long parent_rate)
{
struct clk_hw_omap *clk = to_clk_hw_omap(hw);
u32 field_val, validrate, new_div = 0;
if (!clk->clksel || !clk->clksel_mask)
return -EINVAL;
validrate = omap2_clksel_round_rate_div(clk, rate, &new_div);
if (validrate != rate)
return -EINVAL;
field_val = _divisor_to_clksel(clk, new_div);
if (field_val == ~0)
return -EINVAL;
_write_clksel_reg(clk, field_val);
pr_debug("clock: %s: set rate to %ld\n", __clk_get_name(hw->clk),
__clk_get_rate(hw->clk));
return 0;
}
/*
* Clksel parent setting function - not passed in struct clk function
* pointer - instead, the OMAP clock code currently assumes that any
* parent-setting clock is a clksel clock, and calls
* omap2_clksel_set_parent() by default
*/
/**
* omap2_clksel_set_parent() - change a clock's parent clock
* @clk: struct clk * of the child clock
* @new_parent: struct clk * of the new parent clock
*
* This function is intended to be called only by the clock framework.
* Change the parent clock of clock @clk to @new_parent. This is
* intended to be used while @clk is disabled. This function does not
* currently check the usecount of the clock, so if multiple drivers
* are using the clock, and the parent is changed, they will all be
* affected without any notification. Returns -EINVAL upon error, or
* 0 upon success.
*/
int omap2_clksel_set_parent(struct clk_hw *hw, u8 field_val)
{
struct clk_hw_omap *clk = to_clk_hw_omap(hw);
if (!clk->clksel || !clk->clksel_mask)
return -EINVAL;
_write_clksel_reg(clk, field_val);
return 0;
}

查看文件

@@ -1,370 +0,0 @@
/*
* OMAP2/3/4 DPLL clock functions
*
* Copyright (C) 2005-2008 Texas Instruments, Inc.
* Copyright (C) 2004-2010 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/kernel.h>
#include <linux/errno.h>
#include <linux/clk-provider.h>
#include <linux/io.h>
#include <asm/div64.h>
#include "clock.h"
/* DPLL rate rounding: minimum DPLL multiplier, divider values */
#define DPLL_MIN_MULTIPLIER 2
#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 range for OMAP36xx and OMAP4xxx.
* From device data manual section 4.3 "DPLL and DLL Specifications".
*/
#define OMAP3PLUS_DPLL_FINT_JTYPE_MIN 500000
#define OMAP3PLUS_DPLL_FINT_JTYPE_MAX 2500000
/* _dpll_test_fint() return codes */
#define DPLL_FINT_UNDERFLOW -1
#define DPLL_FINT_INVALID -2
/* Private functions */
/*
* _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_hw_omap *clk, unsigned int n)
{
struct dpll_data *dd;
long fint, fint_min, fint_max;
int ret = 0;
dd = clk->dpll_data;
/* DPLL divider must result in a valid jitter correction val */
fint = __clk_get_rate(__clk_get_parent(clk->hw.clk)) / n;
if (dd->flags & DPLL_J_TYPE) {
fint_min = OMAP3PLUS_DPLL_FINT_JTYPE_MIN;
fint_max = OMAP3PLUS_DPLL_FINT_JTYPE_MAX;
} else {
fint_min = ti_clk_features.fint_min;
fint_max = ti_clk_features.fint_max;
}
if (!fint_min || !fint_max) {
WARN(1, "No fint limits available!\n");
return DPLL_FINT_INVALID;
}
if (fint < ti_clk_features.fint_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 > ti_clk_features.fint_max) {
pr_debug("rejecting n=%d due to Fint failure, boosting min_divider\n",
n);
dd->min_divider = n;
ret = DPLL_FINT_INVALID;
} else if (fint > ti_clk_features.fint_band1_max &&
fint < ti_clk_features.fint_band2_min) {
pr_debug("rejecting n=%d due to Fint failure\n", n);
ret = DPLL_FINT_INVALID;
}
return ret;
}
static unsigned long _dpll_compute_new_rate(unsigned long parent_rate,
unsigned int m, unsigned int n)
{
unsigned long long num;
num = (unsigned long long)parent_rate * m;
do_div(num, n);
return num;
}
/*
* _dpll_test_mult - test a DPLL multiplier value
* @m: pointer to the DPLL m (multiplier) value under test
* @n: current DPLL n (divider) value under test
* @new_rate: pointer to storage for the resulting rounded rate
* @target_rate: the desired DPLL rate
* @parent_rate: the DPLL's parent clock rate
*
* This code tests a DPLL multiplier value, ensuring that the
* resulting rate will not be higher than the target_rate, and that
* the multiplier value itself is valid for the DPLL. Initially, the
* integer pointed to by the m argument should be prescaled by
* multiplying by DPLL_SCALE_FACTOR. The code will replace this with
* a non-scaled m upon return. This non-scaled m will result in a
* new_rate as close as possible to target_rate (but not greater than
* target_rate) given the current (parent_rate, n, prescaled m)
* triple. Returns DPLL_MULT_UNDERFLOW in the event that the
* non-scaled m attempted to underflow, which can allow the calling
* function to bail out early; or 0 upon success.
*/
static int _dpll_test_mult(int *m, int n, unsigned long *new_rate,
unsigned long target_rate,
unsigned long parent_rate)
{
int r = 0, carry = 0;
/* Unscale m and round if necessary */
if (*m % DPLL_SCALE_FACTOR >= DPLL_ROUNDING_VAL)
carry = 1;
*m = (*m / DPLL_SCALE_FACTOR) + carry;
/*
* The new rate must be <= the target rate to avoid programming
* a rate that is impossible for the hardware to handle
*/
*new_rate = _dpll_compute_new_rate(parent_rate, *m, n);
if (*new_rate > target_rate) {
(*m)--;
*new_rate = 0;
}
/* Guard against m underflow */
if (*m < DPLL_MIN_MULTIPLIER) {
*m = DPLL_MIN_MULTIPLIER;
*new_rate = 0;
r = DPLL_MULT_UNDERFLOW;
}
if (*new_rate == 0)
*new_rate = _dpll_compute_new_rate(parent_rate, *m, n);
return r;
}
/**
* _omap2_dpll_is_in_bypass - check if DPLL is in bypass mode or not
* @v: bitfield value of the DPLL enable
*
* Checks given DPLL enable bitfield to see whether the DPLL is in bypass
* mode or not. Returns 1 if the DPLL is in bypass, 0 otherwise.
*/
static int _omap2_dpll_is_in_bypass(u32 v)
{
u8 mask, val;
mask = ti_clk_features.dpll_bypass_vals;
/*
* Each set bit in the mask corresponds to a bypass value equal
* to the bitshift. Go through each set-bit in the mask and
* compare against the given register value.
*/
while (mask) {
val = __ffs(mask);
mask ^= (1 << val);
if (v == val)
return 1;
}
return 0;
}
/* Public functions */
u8 omap2_init_dpll_parent(struct clk_hw *hw)
{
struct clk_hw_omap *clk = to_clk_hw_omap(hw);
u32 v;
struct dpll_data *dd;
dd = clk->dpll_data;
if (!dd)
return -EINVAL;
v = omap2_clk_readl(clk, dd->control_reg);
v &= dd->enable_mask;
v >>= __ffs(dd->enable_mask);
/* Reparent the struct clk in case the dpll is in bypass */
if (_omap2_dpll_is_in_bypass(v))
return 1;
return 0;
}
/**
* omap2_get_dpll_rate - returns the current DPLL CLKOUT rate
* @clk: struct clk * of a DPLL
*
* DPLLs can be locked or bypassed - basically, enabled or disabled.
* When locked, the DPLL output depends on the M and N values. When
* bypassed, on OMAP2xxx, the output rate is either the 32KiHz clock
* or sys_clk. Bypass rates on OMAP3 depend on the DPLL: DPLLs 1 and
* 2 are bypassed with dpll1_fclk and dpll2_fclk respectively
* (generated by DPLL3), while DPLL 3, 4, and 5 bypass rates are sys_clk.
* Returns the current DPLL CLKOUT rate (*not* CLKOUTX2) if the DPLL is
* locked, or the appropriate bypass rate if the DPLL is bypassed, or 0
* if the clock @clk is not a DPLL.
*/
unsigned long omap2_get_dpll_rate(struct clk_hw_omap *clk)
{
long long dpll_clk;
u32 dpll_mult, dpll_div, v;
struct dpll_data *dd;
dd = clk->dpll_data;
if (!dd)
return 0;
/* Return bypass rate if DPLL is bypassed */
v = omap2_clk_readl(clk, dd->control_reg);
v &= dd->enable_mask;
v >>= __ffs(dd->enable_mask);
if (_omap2_dpll_is_in_bypass(v))
return __clk_get_rate(dd->clk_bypass);
v = omap2_clk_readl(clk, dd->mult_div1_reg);
dpll_mult = v & dd->mult_mask;
dpll_mult >>= __ffs(dd->mult_mask);
dpll_div = v & dd->div1_mask;
dpll_div >>= __ffs(dd->div1_mask);
dpll_clk = (long long) __clk_get_rate(dd->clk_ref) * dpll_mult;
do_div(dpll_clk, dpll_div + 1);
return dpll_clk;
}
/* DPLL rate rounding code */
/**
* omap2_dpll_round_rate - round a target rate for an OMAP DPLL
* @clk: struct clk * for a DPLL
* @target_rate: desired DPLL clock rate
*
* Given a DPLL and a desired target rate, round the target rate to a
* possible, programmable rate for this DPLL. Attempts to select the
* minimum possible n. Stores the computed (m, n) in the DPLL's
* dpll_data structure so set_rate() will not need to call this
* (expensive) function again. Returns ~0 if the target rate cannot
* be rounded, or the rounded rate upon success.
*/
long omap2_dpll_round_rate(struct clk_hw *hw, unsigned long target_rate,
unsigned long *parent_rate)
{
struct clk_hw_omap *clk = to_clk_hw_omap(hw);
int m, n, r, scaled_max_m;
int min_delta_m = INT_MAX, min_delta_n = INT_MAX;
unsigned long scaled_rt_rp;
unsigned long new_rate = 0;
struct dpll_data *dd;
unsigned long ref_rate;
long delta;
long prev_min_delta = LONG_MAX;
const char *clk_name;
if (!clk || !clk->dpll_data)
return ~0;
dd = clk->dpll_data;
ref_rate = __clk_get_rate(dd->clk_ref);
clk_name = __clk_get_name(hw->clk);
pr_debug("clock: %s: starting DPLL round_rate, target rate %lu\n",
clk_name, target_rate);
scaled_rt_rp = target_rate / (ref_rate / DPLL_SCALE_FACTOR);
scaled_max_m = dd->max_multiplier * DPLL_SCALE_FACTOR;
dd->last_rounded_rate = 0;
for (n = dd->min_divider; n <= dd->max_divider; n++) {
/* Is the (input clk, divider) pair valid for the DPLL? */
r = _dpll_test_fint(clk, n);
if (r == DPLL_FINT_UNDERFLOW)
break;
else if (r == DPLL_FINT_INVALID)
continue;
/* Compute the scaled DPLL multiplier, based on the divider */
m = scaled_rt_rp * n;
/*
* Since we're counting n up, a m overflow means we
* can bail out completely (since as n increases in
* the next iteration, there's no way that m can
* increase beyond the current m)
*/
if (m > scaled_max_m)
break;
r = _dpll_test_mult(&m, n, &new_rate, target_rate,
ref_rate);
/* m can't be set low enough for this n - try with a larger n */
if (r == DPLL_MULT_UNDERFLOW)
continue;
/* skip rates above our target rate */
delta = target_rate - new_rate;
if (delta < 0)
continue;
if (delta < prev_min_delta) {
prev_min_delta = delta;
min_delta_m = m;
min_delta_n = n;
}
pr_debug("clock: %s: m = %d: n = %d: new_rate = %lu\n",
clk_name, m, n, new_rate);
if (delta == 0)
break;
}
if (prev_min_delta == LONG_MAX) {
pr_debug("clock: %s: cannot round to rate %lu\n",
clk_name, target_rate);
return ~0;
}
dd->last_rounded_m = min_delta_m;
dd->last_rounded_n = min_delta_n;
dd->last_rounded_rate = target_rate - prev_min_delta;
return dd->last_rounded_rate;
}

查看文件

@@ -1,68 +0,0 @@
/*
* OMAP2/3 interface clock control
*
* Copyright (C) 2011 Nokia Corporation
* 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/kernel.h>
#include <linux/clk-provider.h>
#include <linux/io.h>
#include "clock.h"
/* Register offsets */
#define CM_AUTOIDLE 0x30
#define CM_ICLKEN 0x10
/* Private functions */
/* XXX */
void omap2_clkt_iclk_allow_idle(struct clk_hw_omap *clk)
{
u32 v;
void __iomem *r;
r = (__force void __iomem *)
((__force u32)clk->enable_reg ^ (CM_AUTOIDLE ^ CM_ICLKEN));
v = omap2_clk_readl(clk, r);
v |= (1 << clk->enable_bit);
omap2_clk_writel(v, clk, r);
}
/* XXX */
void omap2_clkt_iclk_deny_idle(struct clk_hw_omap *clk)
{
u32 v;
void __iomem *r;
r = (__force void __iomem *)
((__force u32)clk->enable_reg ^ (CM_AUTOIDLE ^ CM_ICLKEN));
v = omap2_clk_readl(clk, r);
v &= ~(1 << clk->enable_bit);
omap2_clk_writel(v, clk, r);
}
/* Public data */
const struct clk_hw_omap_ops clkhwops_iclk = {
.allow_idle = omap2_clkt_iclk_allow_idle,
.deny_idle = omap2_clkt_iclk_deny_idle,
};
const struct clk_hw_omap_ops clkhwops_iclk_wait = {
.allow_idle = omap2_clkt_iclk_allow_idle,
.deny_idle = omap2_clkt_iclk_deny_idle,
.find_idlest = omap2_clk_dflt_find_idlest,
.find_companion = omap2_clk_dflt_find_companion,
};

查看文件

@@ -23,9 +23,7 @@
#include <linux/clk-provider.h>
#include <linux/io.h>
#include <linux/bitops.h>
#include <linux/regmap.h>
#include <linux/of_address.h>
#include <linux/bootmem.h>
#include <asm/cpu.h>
#include <trace/events/power.h>
@@ -40,19 +38,8 @@
#include "cm-regbits-34xx.h"
#include "common.h"
/*
* MAX_MODULE_ENABLE_WAIT: maximum of number of microseconds to wait
* for a module to indicate that it is no longer in idle
*/
#define MAX_MODULE_ENABLE_WAIT 100000
u16 cpu_mask;
/*
* Clock features setup. Used instead of CPU type checks.
*/
struct ti_clk_features ti_clk_features;
/* DPLL valid Fint frequency band limits - from 34xx TRM Section 4.7.6.2 */
#define OMAP3430_DPLL_FINT_BAND1_MIN 750000
#define OMAP3430_DPLL_FINT_BAND1_MAX 2100000
@@ -66,119 +53,24 @@ struct ti_clk_features ti_clk_features;
#define OMAP3PLUS_DPLL_FINT_MIN 32000
#define OMAP3PLUS_DPLL_FINT_MAX 52000000
/*
* clkdm_control: if true, then when a clock is enabled in the
* hardware, its clockdomain will first be enabled; and when a clock
* is disabled in the hardware, its clockdomain will be disabled
* afterwards.
*/
static bool clkdm_control = true;
static LIST_HEAD(clk_hw_omap_clocks);
struct clk_iomap {
struct regmap *regmap;
void __iomem *mem;
};
static struct clk_iomap *clk_memmaps[CLK_MAX_MEMMAPS];
static void clk_memmap_writel(u32 val, void __iomem *reg)
{
struct clk_omap_reg *r = (struct clk_omap_reg *)&reg;
struct clk_iomap *io = clk_memmaps[r->index];
if (io->regmap)
regmap_write(io->regmap, r->offset, val);
else
writel_relaxed(val, io->mem + r->offset);
}
static u32 clk_memmap_readl(void __iomem *reg)
{
u32 val;
struct clk_omap_reg *r = (struct clk_omap_reg *)&reg;
struct clk_iomap *io = clk_memmaps[r->index];
if (io->regmap)
regmap_read(io->regmap, r->offset, &val);
else
val = readl_relaxed(io->mem + r->offset);
return val;
}
void omap2_clk_writel(u32 val, struct clk_hw_omap *clk, void __iomem *reg)
{
if (WARN_ON_ONCE(!(clk->flags & MEMMAP_ADDRESSING)))
writel_relaxed(val, reg);
else
clk_memmap_writel(val, reg);
}
u32 omap2_clk_readl(struct clk_hw_omap *clk, void __iomem *reg)
{
if (WARN_ON_ONCE(!(clk->flags & MEMMAP_ADDRESSING)))
return readl_relaxed(reg);
else
return clk_memmap_readl(reg);
}
static struct ti_clk_ll_ops omap_clk_ll_ops = {
.clk_readl = clk_memmap_readl,
.clk_writel = clk_memmap_writel,
.clkdm_clk_enable = clkdm_clk_enable,
.clkdm_clk_disable = clkdm_clk_disable,
.cm_wait_module_ready = omap_cm_wait_module_ready,
.cm_split_idlest_reg = cm_split_idlest_reg,
};
/**
* omap2_clk_provider_init - initialize a clock provider
* @match_table: DT device table to match for devices to init
* @np: device node pointer for the this clock provider
* @index: index for the clock provider
+ @syscon: syscon regmap pointer
* @mem: iomem pointer for the clock provider memory area, only used if
* syscon is not provided
* omap2_clk_setup_ll_ops - setup clock driver low-level ops
*
* Initializes a clock provider module (CM/PRM etc.), registering
* the memory mapping at specified index and initializing the
* low level driver infrastructure. Returns 0 in success.
* Sets up clock driver low-level platform ops. These are needed
* for register accesses and various other misc platform operations.
* Returns 0 on success, -EBUSY if low level ops have been registered
* already.
*/
int __init omap2_clk_provider_init(struct device_node *np, int index,
struct regmap *syscon, void __iomem *mem)
int __init omap2_clk_setup_ll_ops(void)
{
struct clk_iomap *io;
ti_clk_ll_ops = &omap_clk_ll_ops;
io = kzalloc(sizeof(*io), GFP_KERNEL);
io->regmap = syscon;
io->mem = mem;
clk_memmaps[index] = io;
ti_dt_clk_init_provider(np, index);
return 0;
}
/**
* omap2_clk_legacy_provider_init - initialize a legacy clock provider
* @index: index for the clock provider
* @mem: iomem pointer for the clock provider memory area
*
* Initializes a legacy clock provider memory mapping.
*/
void __init omap2_clk_legacy_provider_init(int index, void __iomem *mem)
{
struct clk_iomap *io;
ti_clk_ll_ops = &omap_clk_ll_ops;
io = memblock_virt_alloc(sizeof(*io), 0);
io->mem = mem;
clk_memmaps[index] = io;
return ti_clk_setup_ll_ops(&omap_clk_ll_ops);
}
/*
@@ -187,77 +79,6 @@ void __init omap2_clk_legacy_provider_init(int index, void __iomem *mem)
/* Private functions */
/**
* _wait_idlest_generic - wait for a module to leave the idle state
* @clk: module clock to wait for (needed for register offsets)
* @reg: virtual address of module IDLEST register
* @mask: value to mask against to determine if the module is active
* @idlest: idle state indicator (0 or 1) for the clock
* @name: name of the clock (for printk)
*
* Wait for a module to leave idle, where its idle-status register is
* not inside the CM module. Returns 1 if the module left idle
* promptly, or 0 if the module did not leave idle before the timeout
* elapsed. XXX Deprecated - should be moved into drivers for the
* individual IP block that the IDLEST register exists in.
*/
static int _wait_idlest_generic(struct clk_hw_omap *clk, void __iomem *reg,
u32 mask, u8 idlest, const char *name)
{
int i = 0, ena = 0;
ena = (idlest) ? 0 : mask;
omap_test_timeout(((omap2_clk_readl(clk, reg) & mask) == ena),
MAX_MODULE_ENABLE_WAIT, i);
if (i < MAX_MODULE_ENABLE_WAIT)
pr_debug("omap clock: module associated with clock %s ready after %d loops\n",
name, i);
else
pr_err("omap clock: module associated with clock %s didn't enable in %d tries\n",
name, MAX_MODULE_ENABLE_WAIT);
return (i < MAX_MODULE_ENABLE_WAIT) ? 1 : 0;
};
/**
* _omap2_module_wait_ready - wait for an OMAP module to leave IDLE
* @clk: struct clk * belonging to the module
*
* If the necessary clocks for the OMAP hardware IP block that
* corresponds to clock @clk are enabled, then wait for the module to
* indicate readiness (i.e., to leave IDLE). This code does not
* belong in the clock code and will be moved in the medium term to
* module-dependent code. No return value.
*/
static void _omap2_module_wait_ready(struct clk_hw_omap *clk)
{
void __iomem *companion_reg, *idlest_reg;
u8 other_bit, idlest_bit, idlest_val, idlest_reg_id;
s16 prcm_mod;
int r;
/* Not all modules have multiple clocks that their IDLEST depends on */
if (clk->ops->find_companion) {
clk->ops->find_companion(clk, &companion_reg, &other_bit);
if (!(omap2_clk_readl(clk, companion_reg) & (1 << other_bit)))
return;
}
clk->ops->find_idlest(clk, &idlest_reg, &idlest_bit, &idlest_val);
r = cm_split_idlest_reg(idlest_reg, &prcm_mod, &idlest_reg_id);
if (r) {
/* IDLEST register not in the CM module */
_wait_idlest_generic(clk, idlest_reg, (1 << idlest_bit),
idlest_val, __clk_get_name(clk->hw.clk));
} else {
omap_cm_wait_module_ready(0, prcm_mod, idlest_reg_id,
idlest_bit);
};
}
/* Public functions */
/**
@@ -290,279 +111,6 @@ void omap2_init_clk_clkdm(struct clk_hw *hw)
}
}
/**
* omap2_clk_disable_clkdm_control - disable clkdm control on clk enable/disable
*
* Prevent the OMAP clock code from calling into the clockdomain code
* when a hardware clock in that clockdomain is enabled or disabled.
* Intended to be called at init time from omap*_clk_init(). No
* return value.
*/
void __init omap2_clk_disable_clkdm_control(void)
{
clkdm_control = false;
}
/**
* omap2_clk_dflt_find_companion - find companion clock to @clk
* @clk: struct clk * to find the companion clock of
* @other_reg: void __iomem ** to return the companion clock CM_*CLKEN va in
* @other_bit: u8 ** to return the companion clock bit shift in
*
* Note: We don't need special code here for INVERT_ENABLE for the
* time being since INVERT_ENABLE only applies to clocks enabled by
* CM_CLKEN_PLL
*
* Convert CM_ICLKEN* <-> CM_FCLKEN*. This conversion assumes it's
* just a matter of XORing the bits.
*
* Some clocks don't have companion clocks. For example, modules with
* only an interface clock (such as MAILBOXES) don't have a companion
* clock. Right now, this code relies on the hardware exporting a bit
* in the correct companion register that indicates that the
* nonexistent 'companion clock' is active. Future patches will
* associate this type of code with per-module data structures to
* avoid this issue, and remove the casts. No return value.
*/
void omap2_clk_dflt_find_companion(struct clk_hw_omap *clk,
void __iomem **other_reg, u8 *other_bit)
{
u32 r;
/*
* Convert CM_ICLKEN* <-> CM_FCLKEN*. This conversion assumes
* it's just a matter of XORing the bits.
*/
r = ((__force u32)clk->enable_reg ^ (CM_FCLKEN ^ CM_ICLKEN));
*other_reg = (__force void __iomem *)r;
*other_bit = clk->enable_bit;
}
/**
* omap2_clk_dflt_find_idlest - find CM_IDLEST reg va, bit shift for @clk
* @clk: struct clk * to find IDLEST info for
* @idlest_reg: void __iomem ** to return the CM_IDLEST va in
* @idlest_bit: u8 * to return the CM_IDLEST bit shift in
* @idlest_val: u8 * to return the idle status indicator
*
* Return the CM_IDLEST register address and bit shift corresponding
* to the module that "owns" this clock. This default code assumes
* that the CM_IDLEST bit shift is the CM_*CLKEN bit shift, and that
* the IDLEST register address ID corresponds to the CM_*CLKEN
* register address ID (e.g., that CM_FCLKEN2 corresponds to
* CM_IDLEST2). This is not true for all modules. No return value.
*/
void omap2_clk_dflt_find_idlest(struct clk_hw_omap *clk,
void __iomem **idlest_reg, u8 *idlest_bit, u8 *idlest_val)
{
u32 r;
r = (((__force u32)clk->enable_reg & ~0xf0) | 0x20);
*idlest_reg = (__force void __iomem *)r;
*idlest_bit = clk->enable_bit;
/*
* 24xx uses 0 to indicate not ready, and 1 to indicate ready.
* 34xx reverses this, just to keep us on our toes
* AM35xx uses both, depending on the module.
*/
*idlest_val = ti_clk_features.cm_idlest_val;
}
/**
* omap2_dflt_clk_enable - enable a clock in the hardware
* @hw: struct clk_hw * of the clock to enable
*
* Enable the clock @hw in the hardware. We first call into the OMAP
* clockdomain code to "enable" the corresponding clockdomain if this
* is the first enabled user of the clockdomain. Then program the
* hardware to enable the clock. Then wait for the IP block that uses
* this clock to leave idle (if applicable). Returns the error value
* from clkdm_clk_enable() if it terminated with an error, or -EINVAL
* if @hw has a null clock enable_reg, or zero upon success.
*/
int omap2_dflt_clk_enable(struct clk_hw *hw)
{
struct clk_hw_omap *clk;
u32 v;
int ret = 0;
clk = to_clk_hw_omap(hw);
if (clkdm_control && clk->clkdm) {
ret = clkdm_clk_enable(clk->clkdm, hw->clk);
if (ret) {
WARN(1, "%s: could not enable %s's clockdomain %s: %d\n",
__func__, __clk_get_name(hw->clk),
clk->clkdm->name, ret);
return ret;
}
}
if (unlikely(clk->enable_reg == NULL)) {
pr_err("%s: %s missing enable_reg\n", __func__,
__clk_get_name(hw->clk));
ret = -EINVAL;
goto err;
}
/* FIXME should not have INVERT_ENABLE bit here */
v = omap2_clk_readl(clk, clk->enable_reg);
if (clk->flags & INVERT_ENABLE)
v &= ~(1 << clk->enable_bit);
else
v |= (1 << clk->enable_bit);
omap2_clk_writel(v, clk, clk->enable_reg);
v = omap2_clk_readl(clk, clk->enable_reg); /* OCP barrier */
if (clk->ops && clk->ops->find_idlest)
_omap2_module_wait_ready(clk);
return 0;
err:
if (clkdm_control && clk->clkdm)
clkdm_clk_disable(clk->clkdm, hw->clk);
return ret;
}
/**
* omap2_dflt_clk_disable - disable a clock in the hardware
* @hw: struct clk_hw * of the clock to disable
*
* Disable the clock @hw in the hardware, and call into the OMAP
* clockdomain code to "disable" the corresponding clockdomain if all
* clocks/hwmods in that clockdomain are now disabled. No return
* value.
*/
void omap2_dflt_clk_disable(struct clk_hw *hw)
{
struct clk_hw_omap *clk;
u32 v;
clk = to_clk_hw_omap(hw);
if (!clk->enable_reg) {
/*
* 'independent' here refers to a clock which is not
* controlled by its parent.
*/
pr_err("%s: independent clock %s has no enable_reg\n",
__func__, __clk_get_name(hw->clk));
return;
}
v = omap2_clk_readl(clk, clk->enable_reg);
if (clk->flags & INVERT_ENABLE)
v |= (1 << clk->enable_bit);
else
v &= ~(1 << clk->enable_bit);
omap2_clk_writel(v, clk, clk->enable_reg);
/* No OCP barrier needed here since it is a disable operation */
if (clkdm_control && clk->clkdm)
clkdm_clk_disable(clk->clkdm, hw->clk);
}
/**
* omap2_clkops_enable_clkdm - increment usecount on clkdm of @hw
* @hw: struct clk_hw * of the clock being enabled
*
* Increment the usecount of the clockdomain of the clock pointed to
* by @hw; if the usecount is 1, the clockdomain will be "enabled."
* Only needed for clocks that don't use omap2_dflt_clk_enable() as
* their enable function pointer. Passes along the return value of
* clkdm_clk_enable(), -EINVAL if @hw is not associated with a
* clockdomain, or 0 if clock framework-based clockdomain control is
* not implemented.
*/
int omap2_clkops_enable_clkdm(struct clk_hw *hw)
{
struct clk_hw_omap *clk;
int ret = 0;
clk = to_clk_hw_omap(hw);
if (unlikely(!clk->clkdm)) {
pr_err("%s: %s: no clkdm set ?!\n", __func__,
__clk_get_name(hw->clk));
return -EINVAL;
}
if (unlikely(clk->enable_reg))
pr_err("%s: %s: should use dflt_clk_enable ?!\n", __func__,
__clk_get_name(hw->clk));
if (!clkdm_control) {
pr_err("%s: %s: clkfw-based clockdomain control disabled ?!\n",
__func__, __clk_get_name(hw->clk));
return 0;
}
ret = clkdm_clk_enable(clk->clkdm, hw->clk);
WARN(ret, "%s: could not enable %s's clockdomain %s: %d\n",
__func__, __clk_get_name(hw->clk), clk->clkdm->name, ret);
return ret;
}
/**
* omap2_clkops_disable_clkdm - decrement usecount on clkdm of @hw
* @hw: struct clk_hw * of the clock being disabled
*
* Decrement the usecount of the clockdomain of the clock pointed to
* by @hw; if the usecount is 0, the clockdomain will be "disabled."
* Only needed for clocks that don't use omap2_dflt_clk_disable() as their
* disable function pointer. No return value.
*/
void omap2_clkops_disable_clkdm(struct clk_hw *hw)
{
struct clk_hw_omap *clk;
clk = to_clk_hw_omap(hw);
if (unlikely(!clk->clkdm)) {
pr_err("%s: %s: no clkdm set ?!\n", __func__,
__clk_get_name(hw->clk));
return;
}
if (unlikely(clk->enable_reg))
pr_err("%s: %s: should use dflt_clk_disable ?!\n", __func__,
__clk_get_name(hw->clk));
if (!clkdm_control) {
pr_err("%s: %s: clkfw-based clockdomain control disabled ?!\n",
__func__, __clk_get_name(hw->clk));
return;
}
clkdm_clk_disable(clk->clkdm, hw->clk);
}
/**
* omap2_dflt_clk_is_enabled - is clock enabled in the hardware?
* @hw: struct clk_hw * to check
*
* Return 1 if the clock represented by @hw is enabled in the
* hardware, or 0 otherwise. Intended for use in the struct
* clk_ops.is_enabled function pointer.
*/
int omap2_dflt_clk_is_enabled(struct clk_hw *hw)
{
struct clk_hw_omap *clk = to_clk_hw_omap(hw);
u32 v;
v = omap2_clk_readl(clk, clk->enable_reg);
if (clk->flags & INVERT_ENABLE)
v ^= BIT(clk->enable_bit);
v &= BIT(clk->enable_bit);
return v ? 1 : 0;
}
static int __initdata mpurate;
/*
@@ -583,178 +131,6 @@ static int __init omap_clk_setup(char *str)
}
__setup("mpurate=", omap_clk_setup);
/**
* omap2_init_clk_hw_omap_clocks - initialize an OMAP clock
* @clk: struct clk * to initialize
*
* Add an OMAP clock @clk to the internal list of OMAP clocks. Used
* temporarily for autoidle handling, until this support can be
* integrated into the common clock framework code in some way. No
* return value.
*/
void omap2_init_clk_hw_omap_clocks(struct clk *clk)
{
struct clk_hw_omap *c;
if (__clk_get_flags(clk) & CLK_IS_BASIC)
return;
c = to_clk_hw_omap(__clk_get_hw(clk));
list_add(&c->node, &clk_hw_omap_clocks);
}
/**
* omap2_clk_enable_autoidle_all - enable autoidle on all OMAP clocks that
* support it
*
* Enable clock autoidle on all OMAP clocks that have allow_idle
* function pointers associated with them. This function is intended
* to be temporary until support for this is added to the common clock
* code. Returns 0.
*/
int omap2_clk_enable_autoidle_all(void)
{
struct clk_hw_omap *c;
list_for_each_entry(c, &clk_hw_omap_clocks, node)
if (c->ops && c->ops->allow_idle)
c->ops->allow_idle(c);
of_ti_clk_allow_autoidle_all();
return 0;
}
/**
* omap2_clk_disable_autoidle_all - disable autoidle on all OMAP clocks that
* support it
*
* Disable clock autoidle on all OMAP clocks that have allow_idle
* function pointers associated with them. This function is intended
* to be temporary until support for this is added to the common clock
* code. Returns 0.
*/
int omap2_clk_disable_autoidle_all(void)
{
struct clk_hw_omap *c;
list_for_each_entry(c, &clk_hw_omap_clocks, node)
if (c->ops && c->ops->deny_idle)
c->ops->deny_idle(c);
of_ti_clk_deny_autoidle_all();
return 0;
}
/**
* omap2_clk_deny_idle - disable autoidle on an OMAP clock
* @clk: struct clk * to disable autoidle for
*
* Disable autoidle on an OMAP clock.
*/
int omap2_clk_deny_idle(struct clk *clk)
{
struct clk_hw_omap *c;
if (__clk_get_flags(clk) & CLK_IS_BASIC)
return -EINVAL;
c = to_clk_hw_omap(__clk_get_hw(clk));
if (c->ops && c->ops->deny_idle)
c->ops->deny_idle(c);
return 0;
}
/**
* omap2_clk_allow_idle - enable autoidle on an OMAP clock
* @clk: struct clk * to enable autoidle for
*
* Enable autoidle on an OMAP clock.
*/
int omap2_clk_allow_idle(struct clk *clk)
{
struct clk_hw_omap *c;
if (__clk_get_flags(clk) & CLK_IS_BASIC)
return -EINVAL;
c = to_clk_hw_omap(__clk_get_hw(clk));
if (c->ops && c->ops->allow_idle)
c->ops->allow_idle(c);
return 0;
}
/**
* omap2_clk_enable_init_clocks - prepare & enable a list of clocks
* @clk_names: ptr to an array of strings of clock names to enable
* @num_clocks: number of clock names in @clk_names
*
* Prepare and enable a list of clocks, named by @clk_names. No
* return value. XXX Deprecated; only needed until these clocks are
* properly claimed and enabled by the drivers or core code that uses
* them. XXX What code disables & calls clk_put on these clocks?
*/
void omap2_clk_enable_init_clocks(const char **clk_names, u8 num_clocks)
{
struct clk *init_clk;
int i;
for (i = 0; i < num_clocks; i++) {
init_clk = clk_get(NULL, clk_names[i]);
if (WARN(IS_ERR(init_clk), "could not find init clock %s\n",
clk_names[i]))
continue;
clk_prepare_enable(init_clk);
}
}
const struct clk_hw_omap_ops clkhwops_wait = {
.find_idlest = omap2_clk_dflt_find_idlest,
.find_companion = omap2_clk_dflt_find_companion,
};
/**
* omap2_clk_switch_mpurate_at_boot - switch ARM MPU rate by boot-time argument
* @mpurate_ck_name: clk name of the clock to change rate
*
* Change the ARM MPU clock rate to the rate specified on the command
* line, if one was specified. @mpurate_ck_name should be
* "virt_prcm_set" on OMAP2xxx and "dpll1_ck" on OMAP34xx/OMAP36xx.
* XXX Does not handle voltage scaling - on OMAP2xxx this is currently
* handled by the virt_prcm_set clock, but this should be handled by
* the OPP layer. XXX This is intended to be handled by the OPP layer
* code in the near future and should be removed from the clock code.
* Returns -EINVAL if 'mpurate' is zero or if clk_set_rate() rejects
* the rate, -ENOENT if the struct clk referred to by @mpurate_ck_name
* cannot be found, or 0 upon success.
*/
int __init omap2_clk_switch_mpurate_at_boot(const char *mpurate_ck_name)
{
struct clk *mpurate_ck;
int r;
if (!mpurate)
return -EINVAL;
mpurate_ck = clk_get(NULL, mpurate_ck_name);
if (WARN(IS_ERR(mpurate_ck), "Failed to get %s.\n", mpurate_ck_name))
return -ENOENT;
r = clk_set_rate(mpurate_ck, mpurate);
if (r < 0) {
WARN(1, "clock: %s: unable to set MPU rate to %d: %d\n",
mpurate_ck_name, mpurate, r);
clk_put(mpurate_ck);
return -EINVAL;
}
calibrate_delay();
clk_put(mpurate_ck);
return 0;
}
/**
* omap2_clk_print_new_rates - print summary of current clock tree rates
* @hfclkin_ck_name: clk name for the off-chip HF oscillator
@@ -801,29 +177,30 @@ void __init omap2_clk_print_new_rates(const char *hfclkin_ck_name,
*/
void __init ti_clk_init_features(void)
{
struct ti_clk_features features = { 0 };
/* Fint setup for DPLLs */
if (cpu_is_omap3430()) {
ti_clk_features.fint_min = OMAP3430_DPLL_FINT_BAND1_MIN;
ti_clk_features.fint_max = OMAP3430_DPLL_FINT_BAND2_MAX;
ti_clk_features.fint_band1_max = OMAP3430_DPLL_FINT_BAND1_MAX;
ti_clk_features.fint_band2_min = OMAP3430_DPLL_FINT_BAND2_MIN;
features.fint_min = OMAP3430_DPLL_FINT_BAND1_MIN;
features.fint_max = OMAP3430_DPLL_FINT_BAND2_MAX;
features.fint_band1_max = OMAP3430_DPLL_FINT_BAND1_MAX;
features.fint_band2_min = OMAP3430_DPLL_FINT_BAND2_MIN;
} else {
ti_clk_features.fint_min = OMAP3PLUS_DPLL_FINT_MIN;
ti_clk_features.fint_max = OMAP3PLUS_DPLL_FINT_MAX;
features.fint_min = OMAP3PLUS_DPLL_FINT_MIN;
features.fint_max = OMAP3PLUS_DPLL_FINT_MAX;
}
/* Bypass value setup for DPLLs */
if (cpu_is_omap24xx()) {
ti_clk_features.dpll_bypass_vals |=
features.dpll_bypass_vals |=
(1 << OMAP2XXX_EN_DPLL_LPBYPASS) |
(1 << OMAP2XXX_EN_DPLL_FRBYPASS);
} else if (cpu_is_omap34xx()) {
ti_clk_features.dpll_bypass_vals |=
features.dpll_bypass_vals |=
(1 << OMAP3XXX_EN_DPLL_LPBYPASS) |
(1 << OMAP3XXX_EN_DPLL_FRBYPASS);
} else if (soc_is_am33xx() || cpu_is_omap44xx() || soc_is_am43xx() ||
soc_is_omap54xx() || soc_is_dra7xx()) {
ti_clk_features.dpll_bypass_vals |=
features.dpll_bypass_vals |=
(1 << OMAP4XXX_EN_DPLL_LPBYPASS) |
(1 << OMAP4XXX_EN_DPLL_FRBYPASS) |
(1 << OMAP4XXX_EN_DPLL_MNBYPASS);
@@ -831,7 +208,7 @@ void __init ti_clk_init_features(void)
/* Jitter correction only available on OMAP343X */
if (cpu_is_omap343x())
ti_clk_features.flags |= TI_CLK_DPLL_HAS_FREQSEL;
features.flags |= TI_CLK_DPLL_HAS_FREQSEL;
/* Idlest value for interface clocks.
* 24xx uses 0 to indicate not ready, and 1 to indicate ready.
@@ -839,11 +216,13 @@ void __init ti_clk_init_features(void)
* AM35xx uses both, depending on the module.
*/
if (cpu_is_omap24xx())
ti_clk_features.cm_idlest_val = OMAP24XX_CM_IDLEST_VAL;
features.cm_idlest_val = OMAP24XX_CM_IDLEST_VAL;
else if (cpu_is_omap34xx())
ti_clk_features.cm_idlest_val = OMAP34XX_CM_IDLEST_VAL;
features.cm_idlest_val = OMAP34XX_CM_IDLEST_VAL;
/* On OMAP3430 ES1.0, DPLL4 can't be re-programmed */
if (omap_rev() == OMAP3430_REV_ES1_0)
ti_clk_features.flags |= TI_CLK_DPLL4_DENY_REPROGRAM;
features.flags |= TI_CLK_DPLL4_DENY_REPROGRAM;
ti_clk_setup_features(&features);
}

查看文件

@@ -23,90 +23,6 @@
#include <linux/clk-provider.h>
#include <linux/clk/ti.h>
struct omap_clk {
u16 cpu;
struct clk_lookup lk;
};
#define CLK(dev, con, ck) \
{ \
.lk = { \
.dev_id = dev, \
.con_id = con, \
.clk = ck, \
}, \
}
struct clockdomain;
#define DEFINE_STRUCT_CLK(_name, _parent_array_name, _clkops_name) \
static struct clk_core _name##_core = { \
.name = #_name, \
.hw = &_name##_hw.hw, \
.parent_names = _parent_array_name, \
.num_parents = ARRAY_SIZE(_parent_array_name), \
.ops = &_clkops_name, \
}; \
static struct clk _name = { \
.core = &_name##_core, \
};
#define DEFINE_STRUCT_CLK_FLAGS(_name, _parent_array_name, \
_clkops_name, _flags) \
static struct clk_core _name##_core = { \
.name = #_name, \
.hw = &_name##_hw.hw, \
.parent_names = _parent_array_name, \
.num_parents = ARRAY_SIZE(_parent_array_name), \
.ops = &_clkops_name, \
.flags = _flags, \
}; \
static struct clk _name = { \
.core = &_name##_core, \
};
#define DEFINE_STRUCT_CLK_HW_OMAP(_name, _clkdm_name) \
static struct clk_hw_omap _name##_hw = { \
.hw = { \
.clk = &_name, \
}, \
.clkdm_name = _clkdm_name, \
};
#define DEFINE_CLK_OMAP_MUX(_name, _clkdm_name, _clksel, \
_clksel_reg, _clksel_mask, \
_parent_names, _ops) \
static struct clk _name; \
static struct clk_hw_omap _name##_hw = { \
.hw = { \
.clk = &_name, \
}, \
.clksel = _clksel, \
.clksel_reg = _clksel_reg, \
.clksel_mask = _clksel_mask, \
.clkdm_name = _clkdm_name, \
}; \
DEFINE_STRUCT_CLK(_name, _parent_names, _ops);
#define DEFINE_CLK_OMAP_MUX_GATE(_name, _clkdm_name, _clksel, \
_clksel_reg, _clksel_mask, \
_enable_reg, _enable_bit, \
_hwops, _parent_names, _ops) \
static struct clk _name; \
static struct clk_hw_omap _name##_hw = { \
.hw = { \
.clk = &_name, \
}, \
.ops = _hwops, \
.enable_reg = _enable_reg, \
.enable_bit = _enable_bit, \
.clksel = _clksel, \
.clksel_reg = _clksel_reg, \
.clksel_mask = _clksel_mask, \
.clkdm_name = _clkdm_name, \
}; \
DEFINE_STRUCT_CLK(_name, _parent_names, _ops);
/* struct clksel_rate.flags possibilities */
#define RATE_IN_242X (1 << 0)
#define RATE_IN_243X (1 << 1)
@@ -127,38 +43,6 @@ struct clockdomain;
/* RATE_IN_3430ES2PLUS_36XX includes 34xx/35xx with ES >=2, and all 36xx/37xx */
#define RATE_IN_3430ES2PLUS_36XX (RATE_IN_3430ES2PLUS | RATE_IN_36XX)
/**
* struct clksel_rate - register bitfield values corresponding to clk divisors
* @val: register bitfield value (shifted to bit 0)
* @div: clock divisor corresponding to @val
* @flags: (see "struct clksel_rate.flags possibilities" above)
*
* @val should match the value of a read from struct clk.clksel_reg
* AND'ed with struct clk.clksel_mask, shifted right to bit 0.
*
* @div is the divisor that should be applied to the parent clock's rate
* to produce the current clock's rate.
*/
struct clksel_rate {
u32 val;
u8 div;
u16 flags;
};
/**
* struct clksel - available parent clocks, and a pointer to their divisors
* @parent: struct clk * to a possible parent clock
* @rates: available divisors for this parent clock
*
* A struct clksel is always associated with one or more struct clks
* and one or more struct clksel_rates.
*/
struct clksel {
struct clk *parent;
const struct clksel_rate *rates;
};
/* CM_CLKSEL2_PLL.CORE_CLK_SRC bits (2XXX) */
#define CORE_CLK_SRC_32K 0x0
#define CORE_CLK_SRC_DPLL 0x1
@@ -180,105 +64,18 @@ struct clksel {
#define OMAP4XXX_EN_DPLL_FRBYPASS 0x6
#define OMAP4XXX_EN_DPLL_LOCKED 0x7
u32 omap3_dpll_autoidle_read(struct clk_hw_omap *clk);
void omap3_dpll_allow_idle(struct clk_hw_omap *clk);
void omap3_dpll_deny_idle(struct clk_hw_omap *clk);
void omap4_dpllmx_allow_gatectrl(struct clk_hw_omap *clk);
void omap4_dpllmx_deny_gatectrl(struct clk_hw_omap *clk);
void __init omap2_clk_disable_clkdm_control(void);
/* clkt_clksel.c public functions */
u32 omap2_clksel_round_rate_div(struct clk_hw_omap *clk,
unsigned long target_rate,
u32 *new_div);
u8 omap2_clksel_find_parent_index(struct clk_hw *hw);
unsigned long omap2_clksel_recalc(struct clk_hw *hw, unsigned long parent_rate);
long omap2_clksel_round_rate(struct clk_hw *hw, unsigned long target_rate,
unsigned long *parent_rate);
int omap2_clksel_set_rate(struct clk_hw *hw, unsigned long rate,
unsigned long parent_rate);
int omap2_clksel_set_parent(struct clk_hw *hw, u8 field_val);
/* clkt_iclk.c public functions */
extern void omap2_clkt_iclk_allow_idle(struct clk_hw_omap *clk);
extern void omap2_clkt_iclk_deny_idle(struct clk_hw_omap *clk);
unsigned long omap2_get_dpll_rate(struct clk_hw_omap *clk);
void omap2_clk_dflt_find_companion(struct clk_hw_omap *clk,
void __iomem **other_reg,
u8 *other_bit);
void omap2_clk_dflt_find_idlest(struct clk_hw_omap *clk,
void __iomem **idlest_reg,
u8 *idlest_bit, u8 *idlest_val);
int omap2_clk_enable_autoidle_all(void);
int omap2_clk_allow_idle(struct clk *clk);
int omap2_clk_deny_idle(struct clk *clk);
int omap2_clk_switch_mpurate_at_boot(const char *mpurate_ck_name);
void omap2_clk_print_new_rates(const char *hfclkin_ck_name,
const char *core_ck_name,
const char *mpu_ck_name);
u32 omap2_clk_readl(struct clk_hw_omap *clk, void __iomem *reg);
void omap2_clk_writel(u32 val, struct clk_hw_omap *clk, void __iomem *reg);
extern u16 cpu_mask;
/*
* Clock features setup. Used instead of CPU type checks.
*/
struct ti_clk_features {
u32 flags;
long fint_min;
long fint_max;
long fint_band1_max;
long fint_band2_min;
u8 dpll_bypass_vals;
u8 cm_idlest_val;
};
#define TI_CLK_DPLL_HAS_FREQSEL (1 << 0)
#define TI_CLK_DPLL4_DENY_REPROGRAM (1 << 1)
extern struct ti_clk_features ti_clk_features;
extern const struct clkops clkops_omap2_dflt_wait;
extern const struct clkops clkops_omap2_dflt;
extern struct clk_functions omap2_clk_functions;
extern const struct clksel_rate gpt_32k_rates[];
extern const struct clksel_rate gpt_sys_rates[];
extern const struct clksel_rate gfx_l3_rates[];
extern const struct clksel_rate dsp_ick_rates[];
extern const struct clk_hw_omap_ops clkhwops_iclk_wait;
extern const struct clk_hw_omap_ops clkhwops_wait;
extern const struct clk_hw_omap_ops clkhwops_omap3430es2_ssi_wait;
extern const struct clk_hw_omap_ops clkhwops_omap3430es2_dss_usbhost_wait;
extern const struct clk_hw_omap_ops clkhwops_omap3430es2_hsotgusb_wait;
extern const struct clk_hw_omap_ops clkhwops_am35xx_ipss_module_wait;
extern const struct clk_hw_omap_ops clkhwops_apll54;
extern const struct clk_hw_omap_ops clkhwops_apll96;
/* clksel_rate blocks shared between OMAP44xx and AM33xx */
extern const struct clksel_rate div_1_0_rates[];
extern const struct clksel_rate div3_1to4_rates[];
extern const struct clksel_rate div_1_1_rates[];
extern const struct clksel_rate div_1_2_rates[];
extern const struct clksel_rate div_1_3_rates[];
extern const struct clksel_rate div_1_4_rates[];
extern const struct clksel_rate div31_1to31_rates[];
extern int omap2_clkops_enable_clkdm(struct clk_hw *hw);
extern void omap2_clkops_disable_clkdm(struct clk_hw *hw);
struct regmap;
int __init omap2_clk_provider_init(struct device_node *np, int index,
struct regmap *syscon, void __iomem *mem);
void __init omap2_clk_legacy_provider_init(int index, void __iomem *mem);
int __init omap2_clk_setup_ll_ops(void);
void __init ti_clk_init_features(void);
#endif

查看文件

@@ -1,57 +0,0 @@
/*
* clock2430.c - OMAP2430-specific clock integration code
*
* Copyright (C) 2005-2008 Texas Instruments, Inc.
* Copyright (C) 2004-2010 Nokia Corporation
*
* Contacts:
* Richard Woodruff <r-woodruff2@ti.com>
* Paul Walmsley
*
* Based on earlier work by Tuukka Tikkanen, 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.
*/
#undef DEBUG
#include <linux/kernel.h>
#include <linux/clk.h>
#include <linux/io.h>
#include "soc.h"
#include "iomap.h"
#include "clock.h"
#include "clock2xxx.h"
#include "cm2xxx.h"
#include "cm-regbits-24xx.h"
/**
* omap2430_clk_i2chs_find_idlest - return CM_IDLEST info for 2430 I2CHS
* @clk: struct clk * being enabled
* @idlest_reg: void __iomem ** to store CM_IDLEST reg address into
* @idlest_bit: pointer to a u8 to store the CM_IDLEST bit shift into
* @idlest_val: pointer to a u8 to store the CM_IDLEST indicator
*
* OMAP2430 I2CHS CM_IDLEST bits are in CM_IDLEST1_CORE, but the
* CM_*CLKEN bits are in CM_{I,F}CLKEN2_CORE. This custom function
* passes back the correct CM_IDLEST register address for I2CHS
* modules. No return value.
*/
static void omap2430_clk_i2chs_find_idlest(struct clk_hw_omap *clk,
void __iomem **idlest_reg,
u8 *idlest_bit,
u8 *idlest_val)
{
*idlest_reg = OMAP2430_CM_REGADDR(CORE_MOD, CM_IDLEST);
*idlest_bit = clk->enable_bit;
*idlest_val = OMAP24XX_CM_IDLEST_VAL;
}
/* 2430 I2CHS has non-standard IDLEST register */
const struct clk_hw_omap_ops clkhwops_omap2430_i2chs_wait = {
.find_idlest = omap2430_clk_i2chs_find_idlest,
.find_companion = omap2_clk_dflt_find_companion,
};

查看文件

@@ -1,57 +0,0 @@
/*
* clock2xxx.c - OMAP2xxx-specific clock integration code
*
* Copyright (C) 2005-2008 Texas Instruments, Inc.
* Copyright (C) 2004-2010 Nokia Corporation
*
* Contacts:
* Richard Woodruff <r-woodruff2@ti.com>
* Paul Walmsley
*
* Based on earlier work by Tuukka Tikkanen, 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.
*/
#undef DEBUG
#include <linux/kernel.h>
#include <linux/errno.h>
#include <linux/clk.h>
#include <linux/io.h>
#include "soc.h"
#include "clock.h"
#include "clock2xxx.h"
#include "cm.h"
#include "cm-regbits-24xx.h"
struct clk_hw *dclk_hw;
/*
* Omap24xx specific clock functions
*/
/*
* Switch the MPU rate if specified on cmdline. We cannot do this
* early until cmdline is parsed. XXX This should be removed from the
* clock code and handled by the OPP layer code in the near future.
*/
static int __init omap2xxx_clk_arch_init(void)
{
int ret;
if (!cpu_is_omap24xx())
return 0;
ret = omap2_clk_switch_mpurate_at_boot("virt_prcm_set");
if (!ret)
omap2_clk_print_new_rates("sys_ck", "dpll_ck", "mpu_ck");
return ret;
}
omap_arch_initcall(omap2xxx_clk_arch_init);

查看文件

@@ -1,138 +0,0 @@
/*
* OMAP3-specific clock framework functions
*
* Copyright (C) 2007-2008 Texas Instruments, Inc.
* Copyright (C) 2007-2011 Nokia Corporation
*
* Paul Walmsley
* Jouni Högander
*
* Parts of this code are based on code written by
* Richard Woodruff, Tony Lindgren, Tuukka Tikkanen, Karthik Dasu,
* Russell King
*
* 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/kernel.h>
#include <linux/clk.h>
#include <linux/io.h>
#include "clock.h"
#include "clock34xx.h"
#include "cm3xxx.h"
#include "cm-regbits-34xx.h"
/**
* omap3430es2_clk_ssi_find_idlest - return CM_IDLEST info for SSI
* @clk: struct clk * being enabled
* @idlest_reg: void __iomem ** to store CM_IDLEST reg address into
* @idlest_bit: pointer to a u8 to store the CM_IDLEST bit shift into
* @idlest_val: pointer to a u8 to store the CM_IDLEST indicator
*
* The OMAP3430ES2 SSI target CM_IDLEST bit is at a different shift
* from the CM_{I,F}CLKEN bit. Pass back the correct info via
* @idlest_reg and @idlest_bit. No return value.
*/
static void omap3430es2_clk_ssi_find_idlest(struct clk_hw_omap *clk,
void __iomem **idlest_reg,
u8 *idlest_bit,
u8 *idlest_val)
{
u32 r;
r = (((__force u32)clk->enable_reg & ~0xf0) | 0x20);
*idlest_reg = (__force void __iomem *)r;
*idlest_bit = OMAP3430ES2_ST_SSI_IDLE_SHIFT;
*idlest_val = OMAP34XX_CM_IDLEST_VAL;
}
const struct clk_hw_omap_ops clkhwops_omap3430es2_ssi_wait = {
.find_idlest = omap3430es2_clk_ssi_find_idlest,
.find_companion = omap2_clk_dflt_find_companion,
};
const struct clk_hw_omap_ops clkhwops_omap3430es2_iclk_ssi_wait = {
.allow_idle = omap2_clkt_iclk_allow_idle,
.deny_idle = omap2_clkt_iclk_deny_idle,
.find_idlest = omap3430es2_clk_ssi_find_idlest,
.find_companion = omap2_clk_dflt_find_companion,
};
/**
* omap3430es2_clk_dss_usbhost_find_idlest - CM_IDLEST info for DSS, USBHOST
* @clk: struct clk * being enabled
* @idlest_reg: void __iomem ** to store CM_IDLEST reg address into
* @idlest_bit: pointer to a u8 to store the CM_IDLEST bit shift into
* @idlest_val: pointer to a u8 to store the CM_IDLEST indicator
*
* Some OMAP modules on OMAP3 ES2+ chips have both initiator and
* target IDLEST bits. For our purposes, we are concerned with the
* target IDLEST bits, which exist at a different bit position than
* the *CLKEN bit position for these modules (DSS and USBHOST) (The
* default find_idlest code assumes that they are at the same
* position.) No return value.
*/
static void omap3430es2_clk_dss_usbhost_find_idlest(struct clk_hw_omap *clk,
void __iomem **idlest_reg,
u8 *idlest_bit,
u8 *idlest_val)
{
u32 r;
r = (((__force u32)clk->enable_reg & ~0xf0) | 0x20);
*idlest_reg = (__force void __iomem *)r;
/* USBHOST_IDLE has same shift */
*idlest_bit = OMAP3430ES2_ST_DSS_IDLE_SHIFT;
*idlest_val = OMAP34XX_CM_IDLEST_VAL;
}
const struct clk_hw_omap_ops clkhwops_omap3430es2_dss_usbhost_wait = {
.find_idlest = omap3430es2_clk_dss_usbhost_find_idlest,
.find_companion = omap2_clk_dflt_find_companion,
};
const struct clk_hw_omap_ops clkhwops_omap3430es2_iclk_dss_usbhost_wait = {
.allow_idle = omap2_clkt_iclk_allow_idle,
.deny_idle = omap2_clkt_iclk_deny_idle,
.find_idlest = omap3430es2_clk_dss_usbhost_find_idlest,
.find_companion = omap2_clk_dflt_find_companion,
};
/**
* omap3430es2_clk_hsotgusb_find_idlest - return CM_IDLEST info for HSOTGUSB
* @clk: struct clk * being enabled
* @idlest_reg: void __iomem ** to store CM_IDLEST reg address into
* @idlest_bit: pointer to a u8 to store the CM_IDLEST bit shift into
* @idlest_val: pointer to a u8 to store the CM_IDLEST indicator
*
* The OMAP3430ES2 HSOTGUSB target CM_IDLEST bit is at a different
* shift from the CM_{I,F}CLKEN bit. Pass back the correct info via
* @idlest_reg and @idlest_bit. No return value.
*/
static void omap3430es2_clk_hsotgusb_find_idlest(struct clk_hw_omap *clk,
void __iomem **idlest_reg,
u8 *idlest_bit,
u8 *idlest_val)
{
u32 r;
r = (((__force u32)clk->enable_reg & ~0xf0) | 0x20);
*idlest_reg = (__force void __iomem *)r;
*idlest_bit = OMAP3430ES2_ST_HSOTGUSB_IDLE_SHIFT;
*idlest_val = OMAP34XX_CM_IDLEST_VAL;
}
const struct clk_hw_omap_ops clkhwops_omap3430es2_iclk_hsotgusb_wait = {
.allow_idle = omap2_clkt_iclk_allow_idle,
.deny_idle = omap2_clkt_iclk_deny_idle,
.find_idlest = omap3430es2_clk_hsotgusb_find_idlest,
.find_companion = omap2_clk_dflt_find_companion,
};
const struct clk_hw_omap_ops clkhwops_omap3430es2_hsotgusb_wait = {
.find_idlest = omap3430es2_clk_hsotgusb_find_idlest,
.find_companion = omap2_clk_dflt_find_companion,
};

查看文件

@@ -1,18 +0,0 @@
/*
* OMAP34xx clock function prototypes and macros
*
* Copyright (C) 2007-2010 Texas Instruments, Inc.
* Copyright (C) 2007-2011 Nokia Corporation
*/
#ifndef __ARCH_ARM_MACH_OMAP2_CLOCK34XX_H
#define __ARCH_ARM_MACH_OMAP2_CLOCK34XX_H
extern const struct clkops clkops_omap3430es2_ssi_wait;
extern const struct clkops clkops_omap3430es2_iclk_ssi_wait;
extern const struct clkops clkops_omap3430es2_hsotgusb_wait;
extern const struct clkops clkops_omap3430es2_iclk_hsotgusb_wait;
extern const struct clkops clkops_omap3430es2_dss_usbhost_wait;
extern const struct clkops clkops_omap3430es2_iclk_dss_usbhost_wait;
#endif

查看文件

@@ -1,118 +0,0 @@
/*
* OMAP3517/3505-specific clock framework functions
*
* Copyright (C) 2010 Texas Instruments, Inc.
* Copyright (C) 2011 Nokia Corporation
*
* Ranjith Lohithakshan
* Paul Walmsley
*
* Parts of this code are based on code written by
* Richard Woodruff, Tony Lindgren, Tuukka Tikkanen, Karthik Dasu,
* Russell King
*
* 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/kernel.h>
#include <linux/clk.h>
#include <linux/io.h>
#include "clock.h"
#include "clock3517.h"
#include "cm3xxx.h"
#include "cm-regbits-34xx.h"
/*
* In AM35xx IPSS, the {ICK,FCK} enable bits for modules are exported
* in the same register at a bit offset of 0x8. The EN_ACK for ICK is
* at an offset of 4 from ICK enable bit.
*/
#define AM35XX_IPSS_ICK_MASK 0xF
#define AM35XX_IPSS_ICK_EN_ACK_OFFSET 0x4
#define AM35XX_IPSS_ICK_FCK_OFFSET 0x8
#define AM35XX_IPSS_CLK_IDLEST_VAL 0
/**
* am35xx_clk_find_idlest - return clock ACK info for AM35XX IPSS
* @clk: struct clk * being enabled
* @idlest_reg: void __iomem ** to store CM_IDLEST reg address into
* @idlest_bit: pointer to a u8 to store the CM_IDLEST bit shift into
* @idlest_val: pointer to a u8 to store the CM_IDLEST indicator
*
* The interface clocks on AM35xx IPSS reflects the clock idle status
* in the enable register itsel at a bit offset of 4 from the enable
* bit. A value of 1 indicates that clock is enabled.
*/
static void am35xx_clk_find_idlest(struct clk_hw_omap *clk,
void __iomem **idlest_reg,
u8 *idlest_bit,
u8 *idlest_val)
{
*idlest_reg = (__force void __iomem *)(clk->enable_reg);
*idlest_bit = clk->enable_bit + AM35XX_IPSS_ICK_EN_ACK_OFFSET;
*idlest_val = AM35XX_IPSS_CLK_IDLEST_VAL;
}
/**
* am35xx_clk_find_companion - find companion clock to @clk
* @clk: struct clk * to find the companion clock of
* @other_reg: void __iomem ** to return the companion clock CM_*CLKEN va in
* @other_bit: u8 ** to return the companion clock bit shift in
*
* Some clocks don't have companion clocks. For example, modules with
* only an interface clock (such as HECC) don't have a companion
* clock. Right now, this code relies on the hardware exporting a bit
* in the correct companion register that indicates that the
* nonexistent 'companion clock' is active. Future patches will
* associate this type of code with per-module data structures to
* avoid this issue, and remove the casts. No return value.
*/
static void am35xx_clk_find_companion(struct clk_hw_omap *clk,
void __iomem **other_reg,
u8 *other_bit)
{
*other_reg = (__force void __iomem *)(clk->enable_reg);
if (clk->enable_bit & AM35XX_IPSS_ICK_MASK)
*other_bit = clk->enable_bit + AM35XX_IPSS_ICK_FCK_OFFSET;
else
*other_bit = clk->enable_bit - AM35XX_IPSS_ICK_FCK_OFFSET;
}
const struct clk_hw_omap_ops clkhwops_am35xx_ipss_module_wait = {
.find_idlest = am35xx_clk_find_idlest,
.find_companion = am35xx_clk_find_companion,
};
/**
* am35xx_clk_ipss_find_idlest - return CM_IDLEST info for IPSS
* @clk: struct clk * being enabled
* @idlest_reg: void __iomem ** to store CM_IDLEST reg address into
* @idlest_bit: pointer to a u8 to store the CM_IDLEST bit shift into
* @idlest_val: pointer to a u8 to store the CM_IDLEST indicator
*
* The IPSS target CM_IDLEST bit is at a different shift from the
* CM_{I,F}CLKEN bit. Pass back the correct info via @idlest_reg
* and @idlest_bit. No return value.
*/
static void am35xx_clk_ipss_find_idlest(struct clk_hw_omap *clk,
void __iomem **idlest_reg,
u8 *idlest_bit,
u8 *idlest_val)
{
u32 r;
r = (((__force u32)clk->enable_reg & ~0xf0) | 0x20);
*idlest_reg = (__force void __iomem *)r;
*idlest_bit = AM35XX_ST_IPSS_SHIFT;
*idlest_val = OMAP34XX_CM_IDLEST_VAL;
}
const struct clk_hw_omap_ops clkhwops_am35xx_ipss_wait = {
.allow_idle = omap2_clkt_iclk_allow_idle,
.deny_idle = omap2_clkt_iclk_deny_idle,
.find_idlest = am35xx_clk_ipss_find_idlest,
.find_companion = omap2_clk_dflt_find_companion,
};

查看文件

@@ -1,14 +0,0 @@
/*
* OMAP3517/3505 clock function prototypes and macros
*
* Copyright (C) 2010 Texas Instruments, Inc.
* Copyright (C) 2010 Nokia Corporation
*/
#ifndef __ARCH_ARM_MACH_OMAP2_CLOCK3517_H
#define __ARCH_ARM_MACH_OMAP2_CLOCK3517_H
extern const struct clkops clkops_am35xx_ipss_module_wait;
extern const struct clkops clkops_am35xx_ipss_wait;
#endif

查看文件

@@ -1,69 +0,0 @@
/*
* OMAP36xx-specific clkops
*
* Copyright (C) 2010 Texas Instruments, Inc.
* Copyright (C) 2010 Nokia Corporation
*
* Mike Turquette
* Vijaykumar GN
* Paul Walmsley
*
* Parts of this code are based on code written by
* Richard Woodruff, Tony Lindgren, Tuukka Tikkanen, Karthik Dasu,
* Russell King
*
* 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/kernel.h>
#include <linux/clk.h>
#include <linux/clk-provider.h>
#include <linux/io.h>
#include "clock.h"
#include "clock36xx.h"
#define to_clk_divider(_hw) container_of(_hw, struct clk_divider, hw)
/**
* omap36xx_pwrdn_clk_enable_with_hsdiv_restore - enable clocks suffering
* from HSDivider PWRDN problem Implements Errata ID: i556.
* @clk: DPLL output struct clk
*
* 3630 only: dpll3_m3_ck, dpll4_m2_ck, dpll4_m3_ck, dpll4_m4_ck,
* dpll4_m5_ck & dpll4_m6_ck dividers gets loaded with reset
* valueafter their respective PWRDN bits are set. Any dummy write
* (Any other value different from the Read value) to the
* corresponding CM_CLKSEL register will refresh the dividers.
*/
int omap36xx_pwrdn_clk_enable_with_hsdiv_restore(struct clk_hw *clk)
{
struct clk_divider *parent;
struct clk_hw *parent_hw;
u32 dummy_v, orig_v;
struct clk_hw_omap *omap_clk = to_clk_hw_omap(clk);
int ret;
/* Clear PWRDN bit of HSDIVIDER */
ret = omap2_dflt_clk_enable(clk);
parent_hw = __clk_get_hw(__clk_get_parent(clk->clk));
parent = to_clk_divider(parent_hw);
/* Restore the dividers */
if (!ret) {
orig_v = omap2_clk_readl(omap_clk, parent->reg);
dummy_v = orig_v;
/* Write any other value different from the Read value */
dummy_v ^= (1 << parent->shift);
omap2_clk_writel(dummy_v, omap_clk, parent->reg);
/* Write the original divider */
omap2_clk_writel(orig_v, omap_clk, parent->reg);
}
return ret;
}

查看文件

@@ -1,13 +0,0 @@
/*
* OMAP36xx clock function prototypes and macros
*
* Copyright (C) 2010 Texas Instruments, Inc.
* Copyright (C) 2010 Nokia Corporation
*/
#ifndef __ARCH_ARM_MACH_OMAP2_CLOCK36XX_H
#define __ARCH_ARM_MACH_OMAP2_CLOCK36XX_H
extern int omap36xx_pwrdn_clk_enable_with_hsdiv_restore(struct clk_hw *hw);
#endif

查看文件

@@ -1,135 +0,0 @@
/*
* OMAP3-specific clock framework functions
*
* Copyright (C) 2007-2008 Texas Instruments, Inc.
* Copyright (C) 2007-2010 Nokia Corporation
*
* Paul Walmsley
* Jouni Högander
*
* Parts of this code are based on code written by
* Richard Woodruff, Tony Lindgren, Tuukka Tikkanen, Karthik Dasu
*
* 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/kernel.h>
#include <linux/errno.h>
#include <linux/clk.h>
#include <linux/io.h>
#include "soc.h"
#include "clock.h"
#include "clock3xxx.h"
#include "prm2xxx_3xxx.h"
#include "prm-regbits-34xx.h"
#include "cm2xxx_3xxx.h"
#include "cm-regbits-34xx.h"
/*
* DPLL5_FREQ_FOR_USBHOST: USBHOST and USBTLL are the only clocks
* that are sourced by DPLL5, and both of these require this clock
* to be at 120 MHz for proper operation.
*/
#define DPLL5_FREQ_FOR_USBHOST 120000000
/* needed by omap3_core_dpll_m2_set_rate() */
struct clk *sdrc_ick_p, *arm_fck_p;
/**
* omap3_dpll4_set_rate - set rate for omap3 per-dpll
* @hw: clock to change
* @rate: target rate for clock
* @parent_rate: rate of the parent clock
*
* Check if the current SoC supports the per-dpll reprogram operation
* or not, and then do the rate change if supported. Returns -EINVAL
* if not supported, 0 for success, and potential error codes from the
* clock rate change.
*/
int omap3_dpll4_set_rate(struct clk_hw *hw, unsigned long rate,
unsigned long parent_rate)
{
/*
* According to the 12-5 CDP code from TI, "Limitation 2.5"
* on 3430ES1 prevents us from changing DPLL multipliers or dividers
* on DPLL4.
*/
if (ti_clk_features.flags & TI_CLK_DPLL4_DENY_REPROGRAM) {
pr_err("clock: DPLL4 cannot change rate due to silicon 'Limitation 2.5' on 3430ES1.\n");
return -EINVAL;
}
return omap3_noncore_dpll_set_rate(hw, rate, parent_rate);
}
/**
* omap3_dpll4_set_rate_and_parent - set rate and parent for omap3 per-dpll
* @hw: clock to change
* @rate: target rate for clock
* @parent_rate: rate of the parent clock
* @index: parent index, 0 - reference clock, 1 - bypass clock
*
* Check if the current SoC support the per-dpll reprogram operation
* or not, and then do the rate + parent change if supported. Returns
* -EINVAL if not supported, 0 for success, and potential error codes
* from the clock rate change.
*/
int omap3_dpll4_set_rate_and_parent(struct clk_hw *hw, unsigned long rate,
unsigned long parent_rate, u8 index)
{
if (ti_clk_features.flags & TI_CLK_DPLL4_DENY_REPROGRAM) {
pr_err("clock: DPLL4 cannot change rate due to silicon 'Limitation 2.5' on 3430ES1.\n");
return -EINVAL;
}
return omap3_noncore_dpll_set_rate_and_parent(hw, rate, parent_rate,
index);
}
void __init omap3_clk_lock_dpll5(void)
{
struct clk *dpll5_clk;
struct clk *dpll5_m2_clk;
dpll5_clk = clk_get(NULL, "dpll5_ck");
clk_set_rate(dpll5_clk, DPLL5_FREQ_FOR_USBHOST);
clk_prepare_enable(dpll5_clk);
/* Program dpll5_m2_clk divider for no division */
dpll5_m2_clk = clk_get(NULL, "dpll5_m2_ck");
clk_prepare_enable(dpll5_m2_clk);
clk_set_rate(dpll5_m2_clk, DPLL5_FREQ_FOR_USBHOST);
clk_disable_unprepare(dpll5_m2_clk);
clk_disable_unprepare(dpll5_clk);
return;
}
/* Common clock code */
/*
* Switch the MPU rate if specified on cmdline. We cannot do this
* early until cmdline is parsed. XXX This should be removed from the
* clock code and handled by the OPP layer code in the near future.
*/
static int __init omap3xxx_clk_arch_init(void)
{
int ret;
if (!cpu_is_omap34xx())
return 0;
ret = omap2_clk_switch_mpurate_at_boot("dpll1_ck");
if (!ret)
omap2_clk_print_new_rates("osc_sys_ck", "core_ck", "arm_fck");
return ret;
}
omap_arch_initcall(omap3xxx_clk_arch_init);

查看文件

@@ -1,20 +0,0 @@
/*
* OMAP4 clock function prototypes and macros
*
* Copyright (C) 2009 Texas Instruments, Inc.
* Copyright (C) 2010 Nokia Corporation
*/
#ifndef __ARCH_ARM_MACH_OMAP2_CLOCK44XX_H
#define __ARCH_ARM_MACH_OMAP2_CLOCK44XX_H
/*
* OMAP4430_REGM4XEN_MULT: If the CM_CLKMODE_DPLL_ABE.DPLL_REGM4XEN bit is
* set, then the DPLL's lock frequency is multiplied by 4 (OMAP4430 TRM
* vV Section 3.6.3.3.1 "DPLLs Output Clocks Parameters")
*/
#define OMAP4430_REGM4XEN_MULT 4
int omap4xxx_clk_init(void);
#endif

查看文件

@@ -1,115 +0,0 @@
/*
* linux/arch/arm/mach-omap2/clock_common_data.c
*
* Copyright (C) 2005-2009 Texas Instruments, Inc.
* Copyright (C) 2004-2009 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.
*
* This file contains clock data that is common to both the OMAP2xxx and
* OMAP3xxx clock definition files.
*/
#include "clock.h"
/* clksel_rate data common to 24xx/343x */
const struct clksel_rate gpt_32k_rates[] = {
{ .div = 1, .val = 0, .flags = RATE_IN_24XX | RATE_IN_3XXX },
{ .div = 0 }
};
const struct clksel_rate gpt_sys_rates[] = {
{ .div = 1, .val = 1, .flags = RATE_IN_24XX | RATE_IN_3XXX },
{ .div = 0 }
};
const struct clksel_rate gfx_l3_rates[] = {
{ .div = 1, .val = 1, .flags = RATE_IN_24XX | RATE_IN_3XXX },
{ .div = 2, .val = 2, .flags = RATE_IN_24XX | RATE_IN_3XXX },
{ .div = 3, .val = 3, .flags = RATE_IN_243X | RATE_IN_3XXX },
{ .div = 4, .val = 4, .flags = RATE_IN_243X | RATE_IN_3XXX },
{ .div = 0 }
};
const struct clksel_rate dsp_ick_rates[] = {
{ .div = 1, .val = 1, .flags = RATE_IN_24XX },
{ .div = 2, .val = 2, .flags = RATE_IN_24XX },
{ .div = 3, .val = 3, .flags = RATE_IN_243X },
{ .div = 0 },
};
/* clksel_rate blocks shared between OMAP44xx and AM33xx */
const struct clksel_rate div_1_0_rates[] = {
{ .div = 1, .val = 0, .flags = RATE_IN_4430 | RATE_IN_AM33XX },
{ .div = 0 },
};
const struct clksel_rate div3_1to4_rates[] = {
{ .div = 1, .val = 0, .flags = RATE_IN_4430 },
{ .div = 2, .val = 1, .flags = RATE_IN_4430 },
{ .div = 4, .val = 2, .flags = RATE_IN_4430 },
{ .div = 0 },
};
const struct clksel_rate div_1_1_rates[] = {
{ .div = 1, .val = 1, .flags = RATE_IN_4430 | RATE_IN_AM33XX },
{ .div = 0 },
};
const struct clksel_rate div_1_2_rates[] = {
{ .div = 1, .val = 2, .flags = RATE_IN_4430 | RATE_IN_AM33XX },
{ .div = 0 },
};
const struct clksel_rate div_1_3_rates[] = {
{ .div = 1, .val = 3, .flags = RATE_IN_4430 | RATE_IN_AM33XX },
{ .div = 0 },
};
const struct clksel_rate div_1_4_rates[] = {
{ .div = 1, .val = 4, .flags = RATE_IN_4430 | RATE_IN_AM33XX },
{ .div = 0 },
};
const struct clksel_rate div31_1to31_rates[] = {
{ .div = 1, .val = 1, .flags = RATE_IN_4430 | RATE_IN_AM33XX },
{ .div = 2, .val = 2, .flags = RATE_IN_4430 | RATE_IN_AM33XX },
{ .div = 3, .val = 3, .flags = RATE_IN_4430 | RATE_IN_AM33XX },
{ .div = 4, .val = 4, .flags = RATE_IN_4430 | RATE_IN_AM33XX },
{ .div = 5, .val = 5, .flags = RATE_IN_4430 | RATE_IN_AM33XX },
{ .div = 6, .val = 6, .flags = RATE_IN_4430 | RATE_IN_AM33XX },
{ .div = 7, .val = 7, .flags = RATE_IN_4430 | RATE_IN_AM33XX },
{ .div = 8, .val = 8, .flags = RATE_IN_4430 | RATE_IN_AM33XX },
{ .div = 9, .val = 9, .flags = RATE_IN_4430 | RATE_IN_AM33XX },
{ .div = 10, .val = 10, .flags = RATE_IN_4430 | RATE_IN_AM33XX },
{ .div = 11, .val = 11, .flags = RATE_IN_4430 | RATE_IN_AM33XX },
{ .div = 12, .val = 12, .flags = RATE_IN_4430 | RATE_IN_AM33XX },
{ .div = 13, .val = 13, .flags = RATE_IN_4430 | RATE_IN_AM33XX },
{ .div = 14, .val = 14, .flags = RATE_IN_4430 | RATE_IN_AM33XX },
{ .div = 15, .val = 15, .flags = RATE_IN_4430 | RATE_IN_AM33XX },
{ .div = 16, .val = 16, .flags = RATE_IN_4430 | RATE_IN_AM33XX },
{ .div = 17, .val = 17, .flags = RATE_IN_4430 | RATE_IN_AM33XX },
{ .div = 18, .val = 18, .flags = RATE_IN_4430 | RATE_IN_AM33XX },
{ .div = 19, .val = 19, .flags = RATE_IN_4430 | RATE_IN_AM33XX },
{ .div = 20, .val = 20, .flags = RATE_IN_4430 | RATE_IN_AM33XX },
{ .div = 21, .val = 21, .flags = RATE_IN_4430 | RATE_IN_AM33XX },
{ .div = 22, .val = 22, .flags = RATE_IN_4430 | RATE_IN_AM33XX },
{ .div = 23, .val = 23, .flags = RATE_IN_4430 | RATE_IN_AM33XX },
{ .div = 24, .val = 24, .flags = RATE_IN_4430 | RATE_IN_AM33XX },
{ .div = 25, .val = 25, .flags = RATE_IN_4430 | RATE_IN_AM33XX },
{ .div = 26, .val = 26, .flags = RATE_IN_4430 | RATE_IN_AM33XX },
{ .div = 27, .val = 27, .flags = RATE_IN_4430 | RATE_IN_AM33XX },
{ .div = 28, .val = 28, .flags = RATE_IN_4430 | RATE_IN_AM33XX },
{ .div = 29, .val = 29, .flags = RATE_IN_4430 | RATE_IN_AM33XX },
{ .div = 30, .val = 30, .flags = RATE_IN_4430 | RATE_IN_AM33XX },
{ .div = 31, .val = 31, .flags = RATE_IN_4430 | RATE_IN_AM33XX },
{ .div = 0 },
};

查看文件

@@ -1,813 +0,0 @@
/*
* OMAP3/4 - specific DPLL control functions
*
* Copyright (C) 2009-2010 Texas Instruments, Inc.
* Copyright (C) 2009-2010 Nokia Corporation
*
* Written by Paul Walmsley
* Testing and integration fixes by Jouni Högander
*
* 36xx support added by Vishwanath BS, Richard Woodruff, and Nishanth
* Menon
*
* Parts of this code are based on code written by
* Richard Woodruff, Tony Lindgren, Tuukka Tikkanen, Karthik Dasu
*
* 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/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 <linux/clkdev.h>
#include "clockdomain.h"
#include "clock.h"
/* CM_AUTOIDLE_PLL*.AUTO_* bit values */
#define DPLL_AUTOIDLE_DISABLE 0x0
#define DPLL_AUTOIDLE_LOW_POWER_STOP 0x1
#define MAX_DPLL_WAIT_TRIES 1000000
/* Private functions */
/* _omap3_dpll_write_clken - write clken_bits arg to a DPLL's enable bits */
static void _omap3_dpll_write_clken(struct clk_hw_omap *clk, u8 clken_bits)
{
const struct dpll_data *dd;
u32 v;
dd = clk->dpll_data;
v = omap2_clk_readl(clk, dd->control_reg);
v &= ~dd->enable_mask;
v |= clken_bits << __ffs(dd->enable_mask);
omap2_clk_writel(v, clk, dd->control_reg);
}
/* _omap3_wait_dpll_status: wait for a DPLL to enter a specific state */
static int _omap3_wait_dpll_status(struct clk_hw_omap *clk, u8 state)
{
const struct dpll_data *dd;
int i = 0;
int ret = -EINVAL;
const char *clk_name;
dd = clk->dpll_data;
clk_name = __clk_get_name(clk->hw.clk);
state <<= __ffs(dd->idlest_mask);
while (((omap2_clk_readl(clk, dd->idlest_reg) & dd->idlest_mask)
!= state) && i < MAX_DPLL_WAIT_TRIES) {
i++;
udelay(1);
}
if (i == MAX_DPLL_WAIT_TRIES) {
printk(KERN_ERR "clock: %s failed transition to '%s'\n",
clk_name, (state) ? "locked" : "bypassed");
} else {
pr_debug("clock: %s transition to '%s' in %d loops\n",
clk_name, (state) ? "locked" : "bypassed", i);
ret = 0;
}
return ret;
}
/* From 3430 TRM ES2 4.7.6.2 */
static u16 _omap3_dpll_compute_freqsel(struct clk_hw_omap *clk, u8 n)
{
unsigned long fint;
u16 f = 0;
fint = __clk_get_rate(clk->dpll_data->clk_ref) / n;
pr_debug("clock: fint is %lu\n", fint);
if (fint >= 750000 && fint <= 1000000)
f = 0x3;
else if (fint > 1000000 && fint <= 1250000)
f = 0x4;
else if (fint > 1250000 && fint <= 1500000)
f = 0x5;
else if (fint > 1500000 && fint <= 1750000)
f = 0x6;
else if (fint > 1750000 && fint <= 2100000)
f = 0x7;
else if (fint > 7500000 && fint <= 10000000)
f = 0xB;
else if (fint > 10000000 && fint <= 12500000)
f = 0xC;
else if (fint > 12500000 && fint <= 15000000)
f = 0xD;
else if (fint > 15000000 && fint <= 17500000)
f = 0xE;
else if (fint > 17500000 && fint <= 21000000)
f = 0xF;
else
pr_debug("clock: unknown freqsel setting for %d\n", n);
return f;
}
/*
* _omap3_noncore_dpll_lock - instruct a DPLL to lock and wait for readiness
* @clk: pointer to a DPLL struct clk
*
* Instructs a non-CORE DPLL to lock. Waits for the DPLL to report
* readiness before returning. Will save and restore the DPLL's
* autoidle state across the enable, per the CDP code. If the DPLL
* locked successfully, return 0; if the DPLL did not lock in the time
* allotted, or DPLL3 was passed in, return -EINVAL.
*/
static int _omap3_noncore_dpll_lock(struct clk_hw_omap *clk)
{
const struct dpll_data *dd;
u8 ai;
u8 state = 1;
int r = 0;
pr_debug("clock: locking DPLL %s\n", __clk_get_name(clk->hw.clk));
dd = clk->dpll_data;
state <<= __ffs(dd->idlest_mask);
/* Check if already locked */
if ((omap2_clk_readl(clk, dd->idlest_reg) & dd->idlest_mask) == state)
goto done;
ai = omap3_dpll_autoidle_read(clk);
if (ai)
omap3_dpll_deny_idle(clk);
_omap3_dpll_write_clken(clk, DPLL_LOCKED);
r = _omap3_wait_dpll_status(clk, 1);
if (ai)
omap3_dpll_allow_idle(clk);
done:
return r;
}
/*
* _omap3_noncore_dpll_bypass - instruct a DPLL to bypass and wait for readiness
* @clk: pointer to a DPLL struct clk
*
* Instructs a non-CORE DPLL to enter low-power bypass mode. In
* bypass mode, the DPLL's rate is set equal to its parent clock's
* rate. Waits for the DPLL to report readiness before returning.
* Will save and restore the DPLL's autoidle state across the enable,
* per the CDP code. If the DPLL entered bypass mode successfully,
* return 0; if the DPLL did not enter bypass in the time allotted, or
* DPLL3 was passed in, or the DPLL does not support low-power bypass,
* return -EINVAL.
*/
static int _omap3_noncore_dpll_bypass(struct clk_hw_omap *clk)
{
int r;
u8 ai;
if (!(clk->dpll_data->modes & (1 << DPLL_LOW_POWER_BYPASS)))
return -EINVAL;
pr_debug("clock: configuring DPLL %s for low-power bypass\n",
__clk_get_name(clk->hw.clk));
ai = omap3_dpll_autoidle_read(clk);
_omap3_dpll_write_clken(clk, DPLL_LOW_POWER_BYPASS);
r = _omap3_wait_dpll_status(clk, 0);
if (ai)
omap3_dpll_allow_idle(clk);
return r;
}
/*
* _omap3_noncore_dpll_stop - instruct a DPLL to stop
* @clk: pointer to a DPLL struct clk
*
* Instructs a non-CORE DPLL to enter low-power stop. Will save and
* restore the DPLL's autoidle state across the stop, per the CDP
* code. If DPLL3 was passed in, or the DPLL does not support
* low-power stop, return -EINVAL; otherwise, return 0.
*/
static int _omap3_noncore_dpll_stop(struct clk_hw_omap *clk)
{
u8 ai;
if (!(clk->dpll_data->modes & (1 << DPLL_LOW_POWER_STOP)))
return -EINVAL;
pr_debug("clock: stopping DPLL %s\n", __clk_get_name(clk->hw.clk));
ai = omap3_dpll_autoidle_read(clk);
_omap3_dpll_write_clken(clk, DPLL_LOW_POWER_STOP);
if (ai)
omap3_dpll_allow_idle(clk);
return 0;
}
/**
* _lookup_dco - Lookup DCO used by j-type DPLL
* @clk: pointer to a DPLL struct clk
* @dco: digital control oscillator selector
* @m: DPLL multiplier to set
* @n: DPLL divider to set
*
* See 36xx TRM section 3.5.3.3.3.2 "Type B DPLL (Low-Jitter)"
*
* XXX This code is not needed for 3430/AM35xx; can it be optimized
* out in non-multi-OMAP builds for those chips?
*/
static void _lookup_dco(struct clk_hw_omap *clk, u8 *dco, u16 m, u8 n)
{
unsigned long fint, clkinp; /* watch out for overflow */
clkinp = __clk_get_rate(__clk_get_parent(clk->hw.clk));
fint = (clkinp / n) * m;
if (fint < 1000000000)
*dco = 2;
else
*dco = 4;
}
/**
* _lookup_sddiv - Calculate sigma delta divider for j-type DPLL
* @clk: pointer to a DPLL struct clk
* @sd_div: target sigma-delta divider
* @m: DPLL multiplier to set
* @n: DPLL divider to set
*
* See 36xx TRM section 3.5.3.3.3.2 "Type B DPLL (Low-Jitter)"
*
* XXX This code is not needed for 3430/AM35xx; can it be optimized
* out in non-multi-OMAP builds for those chips?
*/
static void _lookup_sddiv(struct clk_hw_omap *clk, u8 *sd_div, u16 m, u8 n)
{
unsigned long clkinp, sd; /* watch out for overflow */
int mod1, mod2;
clkinp = __clk_get_rate(__clk_get_parent(clk->hw.clk));
/*
* target sigma-delta to near 250MHz
* sd = ceil[(m/(n+1)) * (clkinp_MHz / 250)]
*/
clkinp /= 100000; /* shift from MHz to 10*Hz for 38.4 and 19.2 */
mod1 = (clkinp * m) % (250 * n);
sd = (clkinp * m) / (250 * n);
mod2 = sd % 10;
sd /= 10;
if (mod1 || mod2)
sd++;
*sd_div = sd;
}
/*
* _omap3_noncore_dpll_program - set non-core DPLL M,N values directly
* @clk: struct clk * of DPLL to set
* @freqsel: FREQSEL value to set
*
* Program the DPLL with the last M, N values calculated, and wait for
* the DPLL to lock. Returns -EINVAL upon error, or 0 upon success.
*/
static int omap3_noncore_dpll_program(struct clk_hw_omap *clk, u16 freqsel)
{
struct dpll_data *dd = clk->dpll_data;
u8 dco, sd_div;
u32 v;
/* 3430 ES2 TRM: 4.7.6.9 DPLL Programming Sequence */
_omap3_noncore_dpll_bypass(clk);
/*
* Set jitter correction. Jitter correction applicable for OMAP343X
* only since freqsel field is no longer present on other devices.
*/
if (ti_clk_features.flags & TI_CLK_DPLL_HAS_FREQSEL) {
v = omap2_clk_readl(clk, dd->control_reg);
v &= ~dd->freqsel_mask;
v |= freqsel << __ffs(dd->freqsel_mask);
omap2_clk_writel(v, clk, dd->control_reg);
}
/* Set DPLL multiplier, divider */
v = omap2_clk_readl(clk, dd->mult_div1_reg);
/* Handle Duty Cycle Correction */
if (dd->dcc_mask) {
if (dd->last_rounded_rate >= dd->dcc_rate)
v |= dd->dcc_mask; /* Enable DCC */
else
v &= ~dd->dcc_mask; /* Disable DCC */
}
v &= ~(dd->mult_mask | dd->div1_mask);
v |= dd->last_rounded_m << __ffs(dd->mult_mask);
v |= (dd->last_rounded_n - 1) << __ffs(dd->div1_mask);
/* Configure dco and sd_div for dplls that have these fields */
if (dd->dco_mask) {
_lookup_dco(clk, &dco, dd->last_rounded_m, dd->last_rounded_n);
v &= ~(dd->dco_mask);
v |= dco << __ffs(dd->dco_mask);
}
if (dd->sddiv_mask) {
_lookup_sddiv(clk, &sd_div, dd->last_rounded_m,
dd->last_rounded_n);
v &= ~(dd->sddiv_mask);
v |= sd_div << __ffs(dd->sddiv_mask);
}
omap2_clk_writel(v, clk, dd->mult_div1_reg);
/* Set 4X multiplier and low-power mode */
if (dd->m4xen_mask || dd->lpmode_mask) {
v = omap2_clk_readl(clk, dd->control_reg);
if (dd->m4xen_mask) {
if (dd->last_rounded_m4xen)
v |= dd->m4xen_mask;
else
v &= ~dd->m4xen_mask;
}
if (dd->lpmode_mask) {
if (dd->last_rounded_lpmode)
v |= dd->lpmode_mask;
else
v &= ~dd->lpmode_mask;
}
omap2_clk_writel(v, clk, dd->control_reg);
}
/* We let the clock framework set the other output dividers later */
/* REVISIT: Set ramp-up delay? */
_omap3_noncore_dpll_lock(clk);
return 0;
}
/* Public functions */
/**
* omap3_dpll_recalc - recalculate DPLL rate
* @clk: DPLL struct clk
*
* Recalculate and propagate the DPLL rate.
*/
unsigned long omap3_dpll_recalc(struct clk_hw *hw, unsigned long parent_rate)
{
struct clk_hw_omap *clk = to_clk_hw_omap(hw);
return omap2_get_dpll_rate(clk);
}
/* Non-CORE DPLL (e.g., DPLLs that do not control SDRC) clock functions */
/**
* omap3_noncore_dpll_enable - instruct a DPLL to enter bypass or lock mode
* @clk: pointer to a DPLL struct clk
*
* Instructs a non-CORE DPLL to enable, e.g., to enter bypass or lock.
* The choice of modes depends on the DPLL's programmed rate: if it is
* the same as the DPLL's parent clock, it will enter bypass;
* otherwise, it will enter lock. This code will wait for the DPLL to
* indicate readiness before returning, unless the DPLL takes too long
* to enter the target state. Intended to be used as the struct clk's
* enable function. If DPLL3 was passed in, or the DPLL does not
* support low-power stop, or if the DPLL took too long to enter
* bypass or lock, return -EINVAL; otherwise, return 0.
*/
int omap3_noncore_dpll_enable(struct clk_hw *hw)
{
struct clk_hw_omap *clk = to_clk_hw_omap(hw);
int r;
struct dpll_data *dd;
struct clk_hw *parent;
dd = clk->dpll_data;
if (!dd)
return -EINVAL;
if (clk->clkdm) {
r = clkdm_clk_enable(clk->clkdm, hw->clk);
if (r) {
WARN(1,
"%s: could not enable %s's clockdomain %s: %d\n",
__func__, __clk_get_name(hw->clk),
clk->clkdm->name, r);
return r;
}
}
parent = __clk_get_hw(__clk_get_parent(hw->clk));
if (__clk_get_rate(hw->clk) == __clk_get_rate(dd->clk_bypass)) {
WARN_ON(parent != __clk_get_hw(dd->clk_bypass));
r = _omap3_noncore_dpll_bypass(clk);
} else {
WARN_ON(parent != __clk_get_hw(dd->clk_ref));
r = _omap3_noncore_dpll_lock(clk);
}
return r;
}
/**
* omap3_noncore_dpll_disable - instruct a DPLL to enter low-power stop
* @clk: pointer to a DPLL struct clk
*
* Instructs a non-CORE DPLL to enter low-power stop. This function is
* intended for use in struct clkops. No return value.
*/
void omap3_noncore_dpll_disable(struct clk_hw *hw)
{
struct clk_hw_omap *clk = to_clk_hw_omap(hw);
_omap3_noncore_dpll_stop(clk);
if (clk->clkdm)
clkdm_clk_disable(clk->clkdm, hw->clk);
}
/* Non-CORE DPLL rate set code */
/**
* omap3_noncore_dpll_determine_rate - determine rate for a DPLL
* @hw: pointer to the clock to determine rate for
* @req: target rate request
*
* Determines which DPLL mode to use for reaching a desired target rate.
* Checks whether the DPLL shall be in bypass or locked mode, and if
* locked, calculates the M,N values for the DPLL via round-rate.
* Returns a 0 on success, negative error value in failure.
*/
int omap3_noncore_dpll_determine_rate(struct clk_hw *hw,
struct clk_rate_request *req)
{
struct clk_hw_omap *clk = to_clk_hw_omap(hw);
struct dpll_data *dd;
if (!req->rate)
return -EINVAL;
dd = clk->dpll_data;
if (!dd)
return -EINVAL;
if (__clk_get_rate(dd->clk_bypass) == req->rate &&
(dd->modes & (1 << DPLL_LOW_POWER_BYPASS))) {
req->best_parent_hw = __clk_get_hw(dd->clk_bypass);
} else {
req->rate = omap2_dpll_round_rate(hw, req->rate,
&req->best_parent_rate);
req->best_parent_hw = __clk_get_hw(dd->clk_ref);
}
req->best_parent_rate = req->rate;
return 0;
}
/**
* omap3_noncore_dpll_set_parent - set parent for a DPLL clock
* @hw: pointer to the clock to set parent for
* @index: parent index to select
*
* Sets parent for a DPLL clock. This sets the DPLL into bypass or
* locked mode. Returns 0 with success, negative error value otherwise.
*/
int omap3_noncore_dpll_set_parent(struct clk_hw *hw, u8 index)
{
struct clk_hw_omap *clk = to_clk_hw_omap(hw);
int ret;
if (!hw)
return -EINVAL;
if (index)
ret = _omap3_noncore_dpll_bypass(clk);
else
ret = _omap3_noncore_dpll_lock(clk);
return ret;
}
/**
* omap3_noncore_dpll_set_rate - set rate for a DPLL clock
* @hw: pointer to the clock to set parent for
* @rate: target rate for the clock
* @parent_rate: rate of the parent clock
*
* Sets rate for a DPLL clock. First checks if the clock parent is
* reference clock (in bypass mode, the rate of the clock can't be
* changed) and proceeds with the rate change operation. Returns 0
* with success, negative error value otherwise.
*/
int omap3_noncore_dpll_set_rate(struct clk_hw *hw, unsigned long rate,
unsigned long parent_rate)
{
struct clk_hw_omap *clk = to_clk_hw_omap(hw);
struct dpll_data *dd;
u16 freqsel = 0;
int ret;
if (!hw || !rate)
return -EINVAL;
dd = clk->dpll_data;
if (!dd)
return -EINVAL;
if (__clk_get_hw(__clk_get_parent(hw->clk)) !=
__clk_get_hw(dd->clk_ref))
return -EINVAL;
if (dd->last_rounded_rate == 0)
return -EINVAL;
/* Freqsel is available only on OMAP343X devices */
if (ti_clk_features.flags & TI_CLK_DPLL_HAS_FREQSEL) {
freqsel = _omap3_dpll_compute_freqsel(clk, dd->last_rounded_n);
WARN_ON(!freqsel);
}
pr_debug("%s: %s: set rate: locking rate to %lu.\n", __func__,
__clk_get_name(hw->clk), rate);
ret = omap3_noncore_dpll_program(clk, freqsel);
return ret;
}
/**
* omap3_noncore_dpll_set_rate_and_parent - set rate and parent for a DPLL clock
* @hw: pointer to the clock to set rate and parent for
* @rate: target rate for the DPLL
* @parent_rate: clock rate of the DPLL parent
* @index: new parent index for the DPLL, 0 - reference, 1 - bypass
*
* Sets rate and parent for a DPLL clock. If new parent is the bypass
* clock, only selects the parent. Otherwise proceeds with a rate
* change, as this will effectively also change the parent as the
* DPLL is put into locked mode. Returns 0 with success, negative error
* value otherwise.
*/
int omap3_noncore_dpll_set_rate_and_parent(struct clk_hw *hw,
unsigned long rate,
unsigned long parent_rate,
u8 index)
{
int ret;
if (!hw || !rate)
return -EINVAL;
/*
* clk-ref at index[0], in which case we only need to set rate,
* the parent will be changed automatically with the lock sequence.
* With clk-bypass case we only need to change parent.
*/
if (index)
ret = omap3_noncore_dpll_set_parent(hw, index);
else
ret = omap3_noncore_dpll_set_rate(hw, rate, parent_rate);
return ret;
}
/* DPLL autoidle read/set code */
/**
* omap3_dpll_autoidle_read - read a DPLL's autoidle bits
* @clk: struct clk * of the DPLL to read
*
* Return the DPLL's autoidle bits, shifted down to bit 0. Returns
* -EINVAL if passed a null pointer or if the struct clk does not
* appear to refer to a DPLL.
*/
u32 omap3_dpll_autoidle_read(struct clk_hw_omap *clk)
{
const struct dpll_data *dd;
u32 v;
if (!clk || !clk->dpll_data)
return -EINVAL;
dd = clk->dpll_data;
if (!dd->autoidle_reg)
return -EINVAL;
v = omap2_clk_readl(clk, dd->autoidle_reg);
v &= dd->autoidle_mask;
v >>= __ffs(dd->autoidle_mask);
return v;
}
/**
* omap3_dpll_allow_idle - enable DPLL autoidle bits
* @clk: struct clk * of the DPLL to operate on
*
* Enable DPLL automatic idle control. This automatic idle mode
* switching takes effect only when the DPLL is locked, at least on
* OMAP3430. The DPLL will enter low-power stop when its downstream
* clocks are gated. No return value.
*/
void omap3_dpll_allow_idle(struct clk_hw_omap *clk)
{
const struct dpll_data *dd;
u32 v;
if (!clk || !clk->dpll_data)
return;
dd = clk->dpll_data;
if (!dd->autoidle_reg)
return;
/*
* REVISIT: CORE DPLL can optionally enter low-power bypass
* by writing 0x5 instead of 0x1. Add some mechanism to
* optionally enter this mode.
*/
v = omap2_clk_readl(clk, dd->autoidle_reg);
v &= ~dd->autoidle_mask;
v |= DPLL_AUTOIDLE_LOW_POWER_STOP << __ffs(dd->autoidle_mask);
omap2_clk_writel(v, clk, dd->autoidle_reg);
}
/**
* omap3_dpll_deny_idle - prevent DPLL from automatically idling
* @clk: struct clk * of the DPLL to operate on
*
* Disable DPLL automatic idle control. No return value.
*/
void omap3_dpll_deny_idle(struct clk_hw_omap *clk)
{
const struct dpll_data *dd;
u32 v;
if (!clk || !clk->dpll_data)
return;
dd = clk->dpll_data;
if (!dd->autoidle_reg)
return;
v = omap2_clk_readl(clk, dd->autoidle_reg);
v &= ~dd->autoidle_mask;
v |= DPLL_AUTOIDLE_DISABLE << __ffs(dd->autoidle_mask);
omap2_clk_writel(v, clk, dd->autoidle_reg);
}
/* Clock control for DPLL outputs */
/* Find the parent DPLL for the given clkoutx2 clock */
static struct clk_hw_omap *omap3_find_clkoutx2_dpll(struct clk_hw *hw)
{
struct clk_hw_omap *pclk = NULL;
struct clk *parent;
/* Walk up the parents of clk, looking for a DPLL */
do {
do {
parent = __clk_get_parent(hw->clk);
hw = __clk_get_hw(parent);
} while (hw && (__clk_get_flags(hw->clk) & CLK_IS_BASIC));
if (!hw)
break;
pclk = to_clk_hw_omap(hw);
} while (pclk && !pclk->dpll_data);
/* clk does not have a DPLL as a parent? error in the clock data */
if (!pclk) {
WARN_ON(1);
return NULL;
}
return pclk;
}
/**
* omap3_clkoutx2_recalc - recalculate DPLL X2 output virtual clock rate
* @clk: DPLL output struct clk
*
* Using parent clock DPLL data, look up DPLL state. If locked, set our
* rate to the dpll_clk * 2; otherwise, just use dpll_clk.
*/
unsigned long omap3_clkoutx2_recalc(struct clk_hw *hw,
unsigned long parent_rate)
{
const struct dpll_data *dd;
unsigned long rate;
u32 v;
struct clk_hw_omap *pclk = NULL;
if (!parent_rate)
return 0;
pclk = omap3_find_clkoutx2_dpll(hw);
if (!pclk)
return 0;
dd = pclk->dpll_data;
WARN_ON(!dd->enable_mask);
v = omap2_clk_readl(pclk, dd->control_reg) & dd->enable_mask;
v >>= __ffs(dd->enable_mask);
if ((v != OMAP3XXX_EN_DPLL_LOCKED) || (dd->flags & DPLL_J_TYPE))
rate = parent_rate;
else
rate = parent_rate * 2;
return rate;
}
int omap3_clkoutx2_set_rate(struct clk_hw *hw, unsigned long rate,
unsigned long parent_rate)
{
return 0;
}
long omap3_clkoutx2_round_rate(struct clk_hw *hw, unsigned long rate,
unsigned long *prate)
{
const struct dpll_data *dd;
u32 v;
struct clk_hw_omap *pclk = NULL;
if (!*prate)
return 0;
pclk = omap3_find_clkoutx2_dpll(hw);
if (!pclk)
return 0;
dd = pclk->dpll_data;
/* TYPE J does not have a clkoutx2 */
if (dd->flags & DPLL_J_TYPE) {
*prate = __clk_round_rate(__clk_get_parent(pclk->hw.clk), rate);
return *prate;
}
WARN_ON(!dd->enable_mask);
v = omap2_clk_readl(pclk, dd->control_reg) & dd->enable_mask;
v >>= __ffs(dd->enable_mask);
/* If in bypass, the rate is fixed to the bypass rate*/
if (v != OMAP3XXX_EN_DPLL_LOCKED)
return *prate;
if (__clk_get_flags(hw->clk) & CLK_SET_RATE_PARENT) {
unsigned long best_parent;
best_parent = (rate / 2);
*prate = __clk_round_rate(__clk_get_parent(hw->clk),
best_parent);
}
return *prate * 2;
}
/* OMAP3/4 non-CORE DPLL clkops */
const struct clk_hw_omap_ops clkhwops_omap3_dpll = {
.allow_idle = omap3_dpll_allow_idle,
.deny_idle = omap3_dpll_deny_idle,
};

查看文件

@@ -1,226 +0,0 @@
/*
* OMAP4-specific DPLL control functions
*
* Copyright (C) 2011 Texas Instruments, Inc.
* Rajendra Nayak
*
* 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/kernel.h>
#include <linux/errno.h>
#include <linux/clk.h>
#include <linux/io.h>
#include <linux/bitops.h>
#include "clock.h"
/*
* Maximum DPLL input frequency (FINT) and output frequency (FOUT) that
* can supported when using the DPLL low-power mode. Frequencies are
* defined in OMAP4430/60 Public TRM section 3.6.3.3.2 "Enable Control,
* Status, and Low-Power Operation Mode".
*/
#define OMAP4_DPLL_LP_FINT_MAX 1000000
#define OMAP4_DPLL_LP_FOUT_MAX 100000000
/*
* Bitfield declarations
*/
#define OMAP4430_DPLL_CLKOUT_GATE_CTRL_MASK (1 << 8)
#define OMAP4430_DPLL_CLKOUTX2_GATE_CTRL_MASK (1 << 10)
#define OMAP4430_DPLL_REGM4XEN_MASK (1 << 11)
/* Static rate multiplier for OMAP4 REGM4XEN clocks */
#define OMAP4430_REGM4XEN_MULT 4
void omap4_dpllmx_allow_gatectrl(struct clk_hw_omap *clk)
{
u32 v;
u32 mask;
if (!clk || !clk->clksel_reg)
return;
mask = clk->flags & CLOCK_CLKOUTX2 ?
OMAP4430_DPLL_CLKOUTX2_GATE_CTRL_MASK :
OMAP4430_DPLL_CLKOUT_GATE_CTRL_MASK;
v = omap2_clk_readl(clk, clk->clksel_reg);
/* Clear the bit to allow gatectrl */
v &= ~mask;
omap2_clk_writel(v, clk, clk->clksel_reg);
}
void omap4_dpllmx_deny_gatectrl(struct clk_hw_omap *clk)
{
u32 v;
u32 mask;
if (!clk || !clk->clksel_reg)
return;
mask = clk->flags & CLOCK_CLKOUTX2 ?
OMAP4430_DPLL_CLKOUTX2_GATE_CTRL_MASK :
OMAP4430_DPLL_CLKOUT_GATE_CTRL_MASK;
v = omap2_clk_readl(clk, clk->clksel_reg);
/* Set the bit to deny gatectrl */
v |= mask;
omap2_clk_writel(v, clk, clk->clksel_reg);
}
const struct clk_hw_omap_ops clkhwops_omap4_dpllmx = {
.allow_idle = omap4_dpllmx_allow_gatectrl,
.deny_idle = omap4_dpllmx_deny_gatectrl,
};
/**
* omap4_dpll_lpmode_recalc - compute DPLL low-power setting
* @dd: pointer to the dpll data structure
*
* Calculates if low-power mode can be enabled based upon the last
* multiplier and divider values calculated. If low-power mode can be
* enabled, then the bit to enable low-power mode is stored in the
* last_rounded_lpmode variable. This implementation is based upon the
* criteria for enabling low-power mode as described in the OMAP4430/60
* Public TRM section 3.6.3.3.2 "Enable Control, Status, and Low-Power
* Operation Mode".
*/
static void omap4_dpll_lpmode_recalc(struct dpll_data *dd)
{
long fint, fout;
fint = __clk_get_rate(dd->clk_ref) / (dd->last_rounded_n + 1);
fout = fint * dd->last_rounded_m;
if ((fint < OMAP4_DPLL_LP_FINT_MAX) && (fout < OMAP4_DPLL_LP_FOUT_MAX))
dd->last_rounded_lpmode = 1;
else
dd->last_rounded_lpmode = 0;
}
/**
* omap4_dpll_regm4xen_recalc - compute DPLL rate, considering REGM4XEN bit
* @clk: struct clk * of the DPLL to compute the rate for
*
* Compute the output rate for the OMAP4 DPLL represented by @clk.
* Takes the REGM4XEN bit into consideration, which is needed for the
* OMAP4 ABE DPLL. Returns the DPLL's output rate (before M-dividers)
* upon success, or 0 upon error.
*/
unsigned long omap4_dpll_regm4xen_recalc(struct clk_hw *hw,
unsigned long parent_rate)
{
struct clk_hw_omap *clk = to_clk_hw_omap(hw);
u32 v;
unsigned long rate;
struct dpll_data *dd;
if (!clk || !clk->dpll_data)
return 0;
dd = clk->dpll_data;
rate = omap2_get_dpll_rate(clk);
/* regm4xen adds a multiplier of 4 to DPLL calculations */
v = omap2_clk_readl(clk, dd->control_reg);
if (v & OMAP4430_DPLL_REGM4XEN_MASK)
rate *= OMAP4430_REGM4XEN_MULT;
return rate;
}
/**
* omap4_dpll_regm4xen_round_rate - round DPLL rate, considering REGM4XEN bit
* @clk: struct clk * of the DPLL to round a rate for
* @target_rate: the desired rate of the DPLL
*
* Compute the rate that would be programmed into the DPLL hardware
* for @clk if set_rate() were to be provided with the rate
* @target_rate. Takes the REGM4XEN bit into consideration, which is
* needed for the OMAP4 ABE DPLL. Returns the rounded rate (before
* M-dividers) upon success, -EINVAL if @clk is null or not a DPLL, or
* ~0 if an error occurred in omap2_dpll_round_rate().
*/
long omap4_dpll_regm4xen_round_rate(struct clk_hw *hw,
unsigned long target_rate,
unsigned long *parent_rate)
{
struct clk_hw_omap *clk = to_clk_hw_omap(hw);
struct dpll_data *dd;
long r;
if (!clk || !clk->dpll_data)
return -EINVAL;
dd = clk->dpll_data;
dd->last_rounded_m4xen = 0;
/*
* First try to compute the DPLL configuration for
* target rate without using the 4X multiplier.
*/
r = omap2_dpll_round_rate(hw, target_rate, NULL);
if (r != ~0)
goto out;
/*
* If we did not find a valid DPLL configuration, try again, but
* this time see if using the 4X multiplier can help. Enabling the
* 4X multiplier is equivalent to dividing the target rate by 4.
*/
r = omap2_dpll_round_rate(hw, target_rate / OMAP4430_REGM4XEN_MULT,
NULL);
if (r == ~0)
return r;
dd->last_rounded_rate *= OMAP4430_REGM4XEN_MULT;
dd->last_rounded_m4xen = 1;
out:
omap4_dpll_lpmode_recalc(dd);
return dd->last_rounded_rate;
}
/**
* omap4_dpll_regm4xen_determine_rate - determine rate for a DPLL
* @hw: pointer to the clock to determine rate for
* @req: target rate request
*
* Determines which DPLL mode to use for reaching a desired rate.
* Checks whether the DPLL shall be in bypass or locked mode, and if
* locked, calculates the M,N values for the DPLL via round-rate.
* Returns 0 on success and a negative error value otherwise.
*/
int omap4_dpll_regm4xen_determine_rate(struct clk_hw *hw,
struct clk_rate_request *req)
{
struct clk_hw_omap *clk = to_clk_hw_omap(hw);
struct dpll_data *dd;
if (!req->rate)
return -EINVAL;
dd = clk->dpll_data;
if (!dd)
return -EINVAL;
if (__clk_get_rate(dd->clk_bypass) == req->rate &&
(dd->modes & (1 << DPLL_LOW_POWER_BYPASS))) {
req->best_parent_hw = __clk_get_hw(dd->clk_bypass);
} else {
req->rate = omap4_dpll_regm4xen_round_rate(hw, req->rate,
&req->best_parent_rate);
req->best_parent_hw = __clk_get_hw(dd->clk_ref);
}
req->best_parent_rate = req->rate;
return 0;
}

查看文件

@@ -37,7 +37,6 @@
#include "clock.h"
#include "clock2xxx.h"
#include "clock3xxx.h"
#include "clock44xx.h"
#include "omap-pm.h"
#include "sdrc.h"
#include "control.h"
@@ -723,6 +722,8 @@ int __init omap_clk_init(void)
ti_clk_init_features();
omap2_clk_setup_ll_ops();
if (of_have_populated_dt()) {
ret = omap_control_init();
if (ret)