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.
此提交包含在:
@@ -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 *)®
|
||||
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 *)®
|
||||
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)
|
||||
|
新增問題並參考
封鎖使用者