mirror of https://gitee.com/openkylin/linux.git
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.
This commit is contained in:
commit
4d34105620
|
@ -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,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 },
|
||||
};
|
|
@ -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)
|
||||
|
|
|
@ -1,16 +1,19 @@
|
|||
obj-y += clk.o autoidle.o clockdomain.o
|
||||
clk-common = dpll.o composite.o divider.o gate.o \
|
||||
fixed-factor.o mux.o apll.o
|
||||
obj-$(CONFIG_SOC_AM33XX) += $(clk-common) clk-33xx.o
|
||||
fixed-factor.o mux.o apll.o \
|
||||
clkt_dpll.o clkt_iclk.o clkt_dflt.o
|
||||
obj-$(CONFIG_SOC_AM33XX) += $(clk-common) clk-33xx.o dpll3xxx.o
|
||||
obj-$(CONFIG_SOC_TI81XX) += $(clk-common) fapll.o clk-816x.o
|
||||
obj-$(CONFIG_ARCH_OMAP2) += $(clk-common) interface.o clk-2xxx.o
|
||||
obj-$(CONFIG_ARCH_OMAP3) += $(clk-common) interface.o \
|
||||
clk-3xxx.o
|
||||
obj-$(CONFIG_ARCH_OMAP4) += $(clk-common) clk-44xx.o
|
||||
obj-$(CONFIG_SOC_OMAP5) += $(clk-common) clk-54xx.o
|
||||
clk-3xxx.o dpll3xxx.o
|
||||
obj-$(CONFIG_ARCH_OMAP4) += $(clk-common) clk-44xx.o \
|
||||
dpll3xxx.o dpll44xx.o
|
||||
obj-$(CONFIG_SOC_OMAP5) += $(clk-common) clk-54xx.o \
|
||||
dpll3xxx.o dpll44xx.o
|
||||
obj-$(CONFIG_SOC_DRA7XX) += $(clk-common) clk-7xx.o \
|
||||
clk-dra7-atl.o
|
||||
obj-$(CONFIG_SOC_AM43XX) += $(clk-common) clk-43xx.o
|
||||
clk-dra7-atl.o dpll3xxx.o dpll44xx.o
|
||||
obj-$(CONFIG_SOC_AM43XX) += $(clk-common) dpll3xxx.o clk-43xx.o
|
||||
|
||||
ifdef CONFIG_ATAGS
|
||||
obj-$(CONFIG_ARCH_OMAP3) += clk-3xxx-legacy.o
|
||||
|
|
|
@ -27,6 +27,8 @@
|
|||
#include <linux/clk/ti.h>
|
||||
#include <linux/delay.h>
|
||||
|
||||
#include "clock.h"
|
||||
|
||||
#define APLL_FORCE_LOCK 0x1
|
||||
#define APLL_AUTO_IDLE 0x2
|
||||
#define MAX_APLL_WAIT_TRIES 1000000
|
||||
|
|
|
@ -22,6 +22,8 @@
|
|||
#include <linux/of_address.h>
|
||||
#include <linux/clk/ti.h>
|
||||
|
||||
#include "clock.h"
|
||||
|
||||
struct clk_ti_autoidle {
|
||||
void __iomem *reg;
|
||||
u8 shift;
|
||||
|
@ -33,8 +35,47 @@ struct clk_ti_autoidle {
|
|||
#define AUTOIDLE_LOW 0x1
|
||||
|
||||
static LIST_HEAD(autoidle_clks);
|
||||
static LIST_HEAD(clk_hw_omap_clocks);
|
||||
|
||||
static void ti_allow_autoidle(struct clk_ti_autoidle *clk)
|
||||
/**
|
||||
* 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;
|
||||
}
|
||||
|
||||
static void _allow_autoidle(struct clk_ti_autoidle *clk)
|
||||
{
|
||||
u32 val;
|
||||
|
||||
|
@ -48,7 +89,7 @@ static void ti_allow_autoidle(struct clk_ti_autoidle *clk)
|
|||
ti_clk_ll_ops->clk_writel(val, clk->reg);
|
||||
}
|
||||
|
||||
static void ti_deny_autoidle(struct clk_ti_autoidle *clk)
|
||||
static void _deny_autoidle(struct clk_ti_autoidle *clk)
|
||||
{
|
||||
u32 val;
|
||||
|
||||
|
@ -63,31 +104,31 @@ static void ti_deny_autoidle(struct clk_ti_autoidle *clk)
|
|||
}
|
||||
|
||||
/**
|
||||
* of_ti_clk_allow_autoidle_all - enable autoidle for all clocks
|
||||
* _clk_generic_allow_autoidle_all - enable autoidle for all clocks
|
||||
*
|
||||
* Enables hardware autoidle for all registered DT clocks, which have
|
||||
* the feature.
|
||||
*/
|
||||
void of_ti_clk_allow_autoidle_all(void)
|
||||
static void _clk_generic_allow_autoidle_all(void)
|
||||
{
|
||||
struct clk_ti_autoidle *c;
|
||||
|
||||
list_for_each_entry(c, &autoidle_clks, node)
|
||||
ti_allow_autoidle(c);
|
||||
_allow_autoidle(c);
|
||||
}
|
||||
|
||||
/**
|
||||
* of_ti_clk_deny_autoidle_all - disable autoidle for all clocks
|
||||
* _clk_generic_deny_autoidle_all - disable autoidle for all clocks
|
||||
*
|
||||
* Disables hardware autoidle for all registered DT clocks, which have
|
||||
* the feature.
|
||||
*/
|
||||
void of_ti_clk_deny_autoidle_all(void)
|
||||
static void _clk_generic_deny_autoidle_all(void)
|
||||
{
|
||||
struct clk_ti_autoidle *c;
|
||||
|
||||
list_for_each_entry(c, &autoidle_clks, node)
|
||||
ti_deny_autoidle(c);
|
||||
_deny_autoidle(c);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -131,3 +172,67 @@ int __init of_ti_clk_autoidle_setup(struct device_node *node)
|
|||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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);
|
||||
|
||||
_clk_generic_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);
|
||||
|
||||
_clk_generic_deny_autoidle_all();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -19,6 +19,8 @@
|
|||
#include <linux/clk-provider.h>
|
||||
#include <linux/clk/ti.h>
|
||||
|
||||
#include "clock.h"
|
||||
|
||||
static struct ti_dt_clk omap2xxx_clks[] = {
|
||||
DT_CLK(NULL, "func_32k_ck", "func_32k_ck"),
|
||||
DT_CLK(NULL, "secure_32k_ck", "secure_32k_ck"),
|
||||
|
|
|
@ -19,6 +19,8 @@
|
|||
#include <linux/clk-provider.h>
|
||||
#include <linux/clk/ti.h>
|
||||
|
||||
#include "clock.h"
|
||||
|
||||
static struct ti_dt_clk am33xx_clks[] = {
|
||||
DT_CLK(NULL, "clk_32768_ck", "clk_32768_ck"),
|
||||
DT_CLK(NULL, "clk_rc32k_ck", "clk_rc32k_ck"),
|
||||
|
|
|
@ -19,6 +19,226 @@
|
|||
#include <linux/clk-provider.h>
|
||||
#include <linux/clk/ti.h>
|
||||
|
||||
#include "clock.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
|
||||
|
||||
#define OMAP3430ES2_ST_DSS_IDLE_SHIFT 1
|
||||
#define OMAP3430ES2_ST_HSOTGUSB_IDLE_SHIFT 5
|
||||
#define OMAP3430ES2_ST_SSI_IDLE_SHIFT 8
|
||||
|
||||
#define OMAP34XX_CM_IDLEST_VAL 1
|
||||
|
||||
/*
|
||||
* 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
|
||||
|
||||
#define AM35XX_ST_IPSS_SHIFT 5
|
||||
|
||||
/**
|
||||
* 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,
|
||||
};
|
||||
|
||||
/**
|
||||
* 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,
|
||||
};
|
||||
|
||||
static struct ti_dt_clk omap3xxx_clks[] = {
|
||||
DT_CLK(NULL, "apb_pclk", "dummy_apb_pclk"),
|
||||
|
@ -324,6 +544,30 @@ enum {
|
|||
OMAP3_SOC_OMAP3630,
|
||||
};
|
||||
|
||||
/**
|
||||
* omap3_clk_lock_dpll5 - locks DPLL5
|
||||
*
|
||||
* Locks DPLL5 to a pre-defined frequency. This is required for proper
|
||||
* operation of USB.
|
||||
*/
|
||||
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);
|
||||
}
|
||||
|
||||
static int __init omap3xxx_dt_clk_init(int soc_type)
|
||||
{
|
||||
if (soc_type == OMAP3_SOC_AM35XX || soc_type == OMAP3_SOC_OMAP3630 ||
|
||||
|
|
|
@ -19,6 +19,8 @@
|
|||
#include <linux/clk-provider.h>
|
||||
#include <linux/clk/ti.h>
|
||||
|
||||
#include "clock.h"
|
||||
|
||||
static struct ti_dt_clk am43xx_clks[] = {
|
||||
DT_CLK(NULL, "clk_32768_ck", "clk_32768_ck"),
|
||||
DT_CLK(NULL, "clk_rc32k_ck", "clk_rc32k_ck"),
|
||||
|
|
|
@ -16,6 +16,8 @@
|
|||
#include <linux/clkdev.h>
|
||||
#include <linux/clk/ti.h>
|
||||
|
||||
#include "clock.h"
|
||||
|
||||
/*
|
||||
* OMAP4 ABE DPLL default frequency. In OMAP4460 TRM version V, section
|
||||
* "3.6.3.2.3 CM1_ABE Clock Generator" states that the "DPLL_ABE_X2_CLK
|
||||
|
|
|
@ -17,6 +17,8 @@
|
|||
#include <linux/io.h>
|
||||
#include <linux/clk/ti.h>
|
||||
|
||||
#include "clock.h"
|
||||
|
||||
#define OMAP5_DPLL_ABE_DEFFREQ 98304000
|
||||
|
||||
/*
|
||||
|
|
|
@ -16,11 +16,12 @@
|
|||
#include <linux/clkdev.h>
|
||||
#include <linux/clk/ti.h>
|
||||
|
||||
#include "clock.h"
|
||||
|
||||
#define DRA7_DPLL_ABE_DEFFREQ 180633600
|
||||
#define DRA7_DPLL_GMAC_DEFFREQ 1000000000
|
||||
#define DRA7_DPLL_USB_DEFFREQ 960000000
|
||||
|
||||
|
||||
static struct ti_dt_clk dra7xx_clks[] = {
|
||||
DT_CLK(NULL, "atl_clkin0_ck", "atl_clkin0_ck"),
|
||||
DT_CLK(NULL, "atl_clkin1_ck", "atl_clkin1_ck"),
|
||||
|
|
|
@ -14,6 +14,8 @@
|
|||
#include <linux/clk-provider.h>
|
||||
#include <linux/clk/ti.h>
|
||||
|
||||
#include "clock.h"
|
||||
|
||||
static struct ti_dt_clk dm816x_clks[] = {
|
||||
DT_CLK(NULL, "sys_clkin", "sys_clkin_ck"),
|
||||
DT_CLK(NULL, "timer_sys_ck", "sys_clkin_ck"),
|
||||
|
|
|
@ -21,6 +21,8 @@
|
|||
#include <linux/of.h>
|
||||
#include <linux/of_address.h>
|
||||
#include <linux/list.h>
|
||||
#include <linux/regmap.h>
|
||||
#include <linux/bootmem.h>
|
||||
|
||||
#include "clock.h"
|
||||
|
||||
|
@ -30,6 +32,63 @@
|
|||
struct ti_clk_ll_ops *ti_clk_ll_ops;
|
||||
static struct device_node *clocks_node_ptr[CLK_MAX_MEMMAPS];
|
||||
|
||||
struct ti_clk_features ti_clk_features;
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
/**
|
||||
* ti_clk_setup_ll_ops - setup low level clock operations
|
||||
* @ops: low level clock ops descriptor
|
||||
*
|
||||
* Sets up low level clock operations for TI clock driver. This is used
|
||||
* to provide various callbacks for the clock driver towards platform
|
||||
* specific code. Returns 0 on success, -EBUSY if ll_ops have been
|
||||
* registered already.
|
||||
*/
|
||||
int ti_clk_setup_ll_ops(struct ti_clk_ll_ops *ops)
|
||||
{
|
||||
if (ti_clk_ll_ops) {
|
||||
pr_err("Attempt to register ll_ops multiple times.\n");
|
||||
return -EBUSY;
|
||||
}
|
||||
|
||||
ti_clk_ll_ops = ops;
|
||||
ops->clk_readl = clk_memmap_readl;
|
||||
ops->clk_writel = clk_memmap_writel;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* ti_dt_clocks_register - register DT alias clocks during boot
|
||||
* @oclks: list of clocks to register
|
||||
|
@ -138,28 +197,61 @@ void __iomem *ti_clk_get_reg_addr(struct device_node *node, int index)
|
|||
}
|
||||
|
||||
/**
|
||||
* ti_dt_clk_init_provider - init master clock provider
|
||||
* omap2_clk_provider_init - init master clock provider
|
||||
* @parent: master node
|
||||
* @index: internal index for clk_reg_ops
|
||||
* @syscon: syscon regmap pointer for accessing clock registers
|
||||
* @mem: iomem pointer for the clock provider memory area, only used if
|
||||
* syscon is not provided
|
||||
*
|
||||
* Initializes a master clock IP block. This basically sets up the
|
||||
* mapping from clocks node to the memory map index. All the clocks
|
||||
* are then initialized through the common of_clk_init call, and the
|
||||
* clocks will access their memory maps based on the node layout.
|
||||
* Returns 0 in success.
|
||||
*/
|
||||
void ti_dt_clk_init_provider(struct device_node *parent, int index)
|
||||
int __init omap2_clk_provider_init(struct device_node *parent, int index,
|
||||
struct regmap *syscon, void __iomem *mem)
|
||||
{
|
||||
struct device_node *clocks;
|
||||
struct clk_iomap *io;
|
||||
|
||||
/* get clocks for this parent */
|
||||
clocks = of_get_child_by_name(parent, "clocks");
|
||||
if (!clocks) {
|
||||
pr_err("%s missing 'clocks' child node.\n", parent->name);
|
||||
return;
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* add clocks node info */
|
||||
clocks_node_ptr[index] = clocks;
|
||||
|
||||
io = kzalloc(sizeof(*io), GFP_KERNEL);
|
||||
|
||||
io->regmap = syscon;
|
||||
io->mem = mem;
|
||||
|
||||
clk_memmaps[index] = io;
|
||||
|
||||
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;
|
||||
|
||||
io = memblock_virt_alloc(sizeof(*io), 0);
|
||||
|
||||
io->mem = mem;
|
||||
|
||||
clk_memmaps[index] = io;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -311,3 +403,50 @@ int __init ti_clk_register_legacy_clks(struct ti_clk_alias *clks)
|
|||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
/**
|
||||
* ti_clk_setup_features - setup clock features flags
|
||||
* @features: features definition to use
|
||||
*
|
||||
* Initializes the clock driver features flags based on platform
|
||||
* provided data. No return value.
|
||||
*/
|
||||
void __init ti_clk_setup_features(struct ti_clk_features *features)
|
||||
{
|
||||
memcpy(&ti_clk_features, features, sizeof(*features));
|
||||
}
|
||||
|
||||
/**
|
||||
* ti_clk_get_features - get clock driver features flags
|
||||
*
|
||||
* Get TI clock driver features description. Returns a pointer
|
||||
* to the current feature setup.
|
||||
*/
|
||||
const struct ti_clk_features *ti_clk_get_features(void)
|
||||
{
|
||||
return &ti_clk_features;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,316 @@
|
|||
/*
|
||||
* Default clock type
|
||||
*
|
||||
* Copyright (C) 2005-2008, 2015 Texas Instruments, Inc.
|
||||
* Copyright (C) 2004-2010 Nokia Corporation
|
||||
*
|
||||
* Contacts:
|
||||
* Richard Woodruff <r-woodruff2@ti.com>
|
||||
* Paul Walmsley
|
||||
* Tero Kristo <t-kristo@ti.com>
|
||||
*
|
||||
* 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 program is distributed "as is" WITHOUT ANY WARRANTY of any
|
||||
* kind, whether express or implied; without even the implied warranty
|
||||
* of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*/
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/clk-provider.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/clk/ti.h>
|
||||
#include <linux/delay.h>
|
||||
|
||||
#include "clock.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
|
||||
|
||||
/*
|
||||
* CM module register offsets, used for calculating the companion
|
||||
* register addresses.
|
||||
*/
|
||||
#define CM_FCLKEN 0x0000
|
||||
#define CM_ICLKEN 0x0010
|
||||
|
||||
/**
|
||||
* _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;
|
||||
|
||||
/* Wait until module enters enabled state */
|
||||
for (i = 0; i < MAX_MODULE_ENABLE_WAIT; i++) {
|
||||
if ((ti_clk_ll_ops->clk_readl(reg) & mask) == ena)
|
||||
break;
|
||||
udelay(1);
|
||||
}
|
||||
|
||||
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 (!(ti_clk_ll_ops->clk_readl(companion_reg) &
|
||||
(1 << other_bit)))
|
||||
return;
|
||||
}
|
||||
|
||||
clk->ops->find_idlest(clk, &idlest_reg, &idlest_bit, &idlest_val);
|
||||
r = ti_clk_ll_ops->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 {
|
||||
ti_clk_ll_ops->cm_wait_module_ready(0, prcm_mod, idlest_reg_id,
|
||||
idlest_bit);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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_get_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;
|
||||
bool clkdm_control;
|
||||
|
||||
if (ti_clk_get_features()->flags & TI_CLK_DISABLE_CLKDM_CONTROL)
|
||||
clkdm_control = false;
|
||||
else
|
||||
clkdm_control = true;
|
||||
|
||||
clk = to_clk_hw_omap(hw);
|
||||
|
||||
if (clkdm_control && clk->clkdm) {
|
||||
ret = ti_clk_ll_ops->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)) {
|
||||
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 = ti_clk_ll_ops->clk_readl(clk->enable_reg);
|
||||
if (clk->flags & INVERT_ENABLE)
|
||||
v &= ~(1 << clk->enable_bit);
|
||||
else
|
||||
v |= (1 << clk->enable_bit);
|
||||
ti_clk_ll_ops->clk_writel(v, clk->enable_reg);
|
||||
v = ti_clk_ll_ops->clk_readl(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)
|
||||
ti_clk_ll_ops->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 = ti_clk_ll_ops->clk_readl(clk->enable_reg);
|
||||
if (clk->flags & INVERT_ENABLE)
|
||||
v |= (1 << clk->enable_bit);
|
||||
else
|
||||
v &= ~(1 << clk->enable_bit);
|
||||
ti_clk_ll_ops->clk_writel(v, clk->enable_reg);
|
||||
/* No OCP barrier needed here since it is a disable operation */
|
||||
|
||||
if (!(ti_clk_get_features()->flags & TI_CLK_DISABLE_CLKDM_CONTROL) &&
|
||||
clk->clkdm)
|
||||
ti_clk_ll_ops->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 = ti_clk_ll_ops->clk_readl(clk->enable_reg);
|
||||
|
||||
if (clk->flags & INVERT_ENABLE)
|
||||
v ^= BIT(clk->enable_bit);
|
||||
|
||||
v &= BIT(clk->enable_bit);
|
||||
|
||||
return v ? 1 : 0;
|
||||
}
|
||||
|
||||
const struct clk_hw_omap_ops clkhwops_wait = {
|
||||
.find_idlest = omap2_clk_dflt_find_idlest,
|
||||
.find_companion = omap2_clk_dflt_find_companion,
|
||||
};
|
|
@ -18,6 +18,7 @@
|
|||
#include <linux/errno.h>
|
||||
#include <linux/clk-provider.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/clk/ti.h>
|
||||
|
||||
#include <asm/div64.h>
|
||||
|
||||
|
@ -80,8 +81,8 @@ static int _dpll_test_fint(struct clk_hw_omap *clk, unsigned int n)
|
|||
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;
|
||||
fint_min = ti_clk_get_features()->fint_min;
|
||||
fint_max = ti_clk_get_features()->fint_max;
|
||||
}
|
||||
|
||||
if (!fint_min || !fint_max) {
|
||||
|
@ -89,18 +90,18 @@ static int _dpll_test_fint(struct clk_hw_omap *clk, unsigned int n)
|
|||
return DPLL_FINT_INVALID;
|
||||
}
|
||||
|
||||
if (fint < ti_clk_features.fint_min) {
|
||||
if (fint < ti_clk_get_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) {
|
||||
} else if (fint > ti_clk_get_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) {
|
||||
} else if (fint > ti_clk_get_features()->fint_band1_max &&
|
||||
fint < ti_clk_get_features()->fint_band2_min) {
|
||||
pr_debug("rejecting n=%d due to Fint failure\n", n);
|
||||
ret = DPLL_FINT_INVALID;
|
||||
}
|
||||
|
@ -183,7 +184,7 @@ static int _omap2_dpll_is_in_bypass(u32 v)
|
|||
{
|
||||
u8 mask, val;
|
||||
|
||||
mask = ti_clk_features.dpll_bypass_vals;
|
||||
mask = ti_clk_get_features()->dpll_bypass_vals;
|
||||
|
||||
/*
|
||||
* Each set bit in the mask corresponds to a bypass value equal
|
||||
|
@ -211,7 +212,7 @@ u8 omap2_init_dpll_parent(struct clk_hw *hw)
|
|||
if (!dd)
|
||||
return -EINVAL;
|
||||
|
||||
v = omap2_clk_readl(clk, dd->control_reg);
|
||||
v = ti_clk_ll_ops->clk_readl(dd->control_reg);
|
||||
v &= dd->enable_mask;
|
||||
v >>= __ffs(dd->enable_mask);
|
||||
|
||||
|
@ -247,20 +248,20 @@ unsigned long omap2_get_dpll_rate(struct clk_hw_omap *clk)
|
|||
return 0;
|
||||
|
||||
/* Return bypass rate if DPLL is bypassed */
|
||||
v = omap2_clk_readl(clk, dd->control_reg);
|
||||
v = ti_clk_ll_ops->clk_readl(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);
|
||||
v = ti_clk_ll_ops->clk_readl(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;
|
||||
dpll_clk = (long long)__clk_get_rate(dd->clk_ref) * dpll_mult;
|
||||
do_div(dpll_clk, dpll_div + 1);
|
||||
|
||||
return dpll_clk;
|
||||
|
@ -281,7 +282,7 @@ unsigned long omap2_get_dpll_rate(struct clk_hw_omap *clk)
|
|||
* 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)
|
||||
unsigned long *parent_rate)
|
||||
{
|
||||
struct clk_hw_omap *clk = to_clk_hw_omap(hw);
|
||||
int m, n, r, scaled_max_m;
|
||||
|
@ -310,7 +311,6 @@ long omap2_dpll_round_rate(struct clk_hw *hw, unsigned long target_rate,
|
|||
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)
|
||||
|
@ -367,4 +367,3 @@ long omap2_dpll_round_rate(struct clk_hw *hw, unsigned long target_rate,
|
|||
|
||||
return dd->last_rounded_rate;
|
||||
}
|
||||
|
|
@ -0,0 +1,101 @@
|
|||
/*
|
||||
* 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 <linux/clk/ti.h>
|
||||
|
||||
#include "clock.h"
|
||||
|
||||
/* Register offsets */
|
||||
#define OMAP24XX_CM_FCLKEN2 0x04
|
||||
#define CM_AUTOIDLE 0x30
|
||||
#define CM_ICLKEN 0x10
|
||||
#define CM_IDLEST 0x20
|
||||
|
||||
#define OMAP24XX_CM_IDLEST_VAL 0
|
||||
|
||||
/* 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 = ti_clk_ll_ops->clk_readl(r);
|
||||
v |= (1 << clk->enable_bit);
|
||||
ti_clk_ll_ops->clk_writel(v, 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 = ti_clk_ll_ops->clk_readl(r);
|
||||
v &= ~(1 << clk->enable_bit);
|
||||
ti_clk_ll_ops->clk_writel(v, r);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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)
|
||||
{
|
||||
u32 r;
|
||||
|
||||
r = ((__force u32)clk->enable_reg ^ (OMAP24XX_CM_FCLKEN2 ^ CM_IDLEST));
|
||||
*idlest_reg = (__force void __iomem *)r;
|
||||
*idlest_bit = clk->enable_bit;
|
||||
*idlest_val = OMAP24XX_CM_IDLEST_VAL;
|
||||
}
|
||||
|
||||
/* 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,
|
||||
};
|
||||
|
||||
/* 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,
|
||||
};
|
|
@ -154,6 +154,35 @@ struct ti_clk_dpll {
|
|||
u8 recal_st_bit;
|
||||
};
|
||||
|
||||
/* Composite clock component types */
|
||||
enum {
|
||||
CLK_COMPONENT_TYPE_GATE = 0,
|
||||
CLK_COMPONENT_TYPE_DIVIDER,
|
||||
CLK_COMPONENT_TYPE_MUX,
|
||||
CLK_COMPONENT_TYPE_MAX,
|
||||
};
|
||||
|
||||
/**
|
||||
* struct ti_dt_clk - OMAP DT clock alias declarations
|
||||
* @lk: clock lookup definition
|
||||
* @node_name: clock DT node to map to
|
||||
*/
|
||||
struct ti_dt_clk {
|
||||
struct clk_lookup lk;
|
||||
char *node_name;
|
||||
};
|
||||
|
||||
#define DT_CLK(dev, con, name) \
|
||||
{ \
|
||||
.lk = { \
|
||||
.dev_id = dev, \
|
||||
.con_id = con, \
|
||||
}, \
|
||||
.node_name = name, \
|
||||
}
|
||||
|
||||
typedef void (*ti_of_clk_init_cb_t)(struct clk_hw *, struct device_node *);
|
||||
|
||||
struct clk *ti_clk_register_gate(struct ti_clk *setup);
|
||||
struct clk *ti_clk_register_interface(struct ti_clk *setup);
|
||||
struct clk *ti_clk_register_mux(struct ti_clk *setup);
|
||||
|
@ -169,4 +198,80 @@ void ti_clk_patch_legacy_clks(struct ti_clk **patch);
|
|||
struct clk *ti_clk_register_clk(struct ti_clk *setup);
|
||||
int ti_clk_register_legacy_clks(struct ti_clk_alias *clks);
|
||||
|
||||
void __iomem *ti_clk_get_reg_addr(struct device_node *node, int index);
|
||||
void ti_dt_clocks_register(struct ti_dt_clk *oclks);
|
||||
int ti_clk_retry_init(struct device_node *node, struct clk_hw *hw,
|
||||
ti_of_clk_init_cb_t func);
|
||||
int ti_clk_add_component(struct device_node *node, struct clk_hw *hw, int type);
|
||||
|
||||
void omap2_init_clk_hw_omap_clocks(struct clk *clk);
|
||||
int of_ti_clk_autoidle_setup(struct device_node *node);
|
||||
void omap2_clk_enable_init_clocks(const char **clk_names, u8 num_clocks);
|
||||
|
||||
extern const struct clk_hw_omap_ops clkhwops_omap3_dpll;
|
||||
extern const struct clk_hw_omap_ops clkhwops_omap4_dpllmx;
|
||||
extern const struct clk_hw_omap_ops clkhwops_wait;
|
||||
extern const struct clk_hw_omap_ops clkhwops_iclk;
|
||||
extern const struct clk_hw_omap_ops clkhwops_iclk_wait;
|
||||
extern const struct clk_hw_omap_ops clkhwops_omap2430_i2chs_wait;
|
||||
extern const struct clk_hw_omap_ops clkhwops_omap3430es2_dss_usbhost_wait;
|
||||
extern const struct clk_hw_omap_ops clkhwops_omap3430es2_iclk_hsotgusb_wait;
|
||||
extern const struct clk_hw_omap_ops clkhwops_omap3430es2_iclk_dss_usbhost_wait;
|
||||
extern const struct clk_hw_omap_ops clkhwops_omap3430es2_iclk_ssi_wait;
|
||||
extern const struct clk_hw_omap_ops clkhwops_am35xx_ipss_module_wait;
|
||||
extern const struct clk_hw_omap_ops clkhwops_am35xx_ipss_wait;
|
||||
|
||||
extern const struct clk_ops ti_clk_divider_ops;
|
||||
extern const struct clk_ops ti_clk_mux_ops;
|
||||
|
||||
int omap2_clkops_enable_clkdm(struct clk_hw *hw);
|
||||
void omap2_clkops_disable_clkdm(struct clk_hw *hw);
|
||||
|
||||
int omap2_dflt_clk_enable(struct clk_hw *hw);
|
||||
void omap2_dflt_clk_disable(struct clk_hw *hw);
|
||||
int omap2_dflt_clk_is_enabled(struct clk_hw *hw);
|
||||
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);
|
||||
|
||||
void omap2_clkt_iclk_allow_idle(struct clk_hw_omap *clk);
|
||||
void omap2_clkt_iclk_deny_idle(struct clk_hw_omap *clk);
|
||||
|
||||
u8 omap2_init_dpll_parent(struct clk_hw *hw);
|
||||
int omap3_noncore_dpll_enable(struct clk_hw *hw);
|
||||
void omap3_noncore_dpll_disable(struct clk_hw *hw);
|
||||
int omap3_noncore_dpll_set_parent(struct clk_hw *hw, u8 index);
|
||||
int omap3_noncore_dpll_set_rate(struct clk_hw *hw, unsigned long rate,
|
||||
unsigned long parent_rate);
|
||||
int omap3_noncore_dpll_set_rate_and_parent(struct clk_hw *hw,
|
||||
unsigned long rate,
|
||||
unsigned long parent_rate,
|
||||
u8 index);
|
||||
int omap3_noncore_dpll_determine_rate(struct clk_hw *hw,
|
||||
struct clk_rate_request *req);
|
||||
long omap2_dpll_round_rate(struct clk_hw *hw, unsigned long target_rate,
|
||||
unsigned long *parent_rate);
|
||||
unsigned long omap3_clkoutx2_recalc(struct clk_hw *hw,
|
||||
unsigned long parent_rate);
|
||||
|
||||
unsigned long omap3_dpll_recalc(struct clk_hw *hw, unsigned long parent_rate);
|
||||
int omap3_dpll4_set_rate(struct clk_hw *clk, unsigned long rate,
|
||||
unsigned long parent_rate);
|
||||
int omap3_dpll4_set_rate_and_parent(struct clk_hw *hw, unsigned long rate,
|
||||
unsigned long parent_rate, u8 index);
|
||||
void omap3_clk_lock_dpll5(void);
|
||||
|
||||
unsigned long omap4_dpll_regm4xen_recalc(struct clk_hw *hw,
|
||||
unsigned long parent_rate);
|
||||
long omap4_dpll_regm4xen_round_rate(struct clk_hw *hw,
|
||||
unsigned long target_rate,
|
||||
unsigned long *parent_rate);
|
||||
int omap4_dpll_regm4xen_determine_rate(struct clk_hw *hw,
|
||||
struct clk_rate_request *req);
|
||||
|
||||
extern struct ti_clk_ll_ops *ti_clk_ll_ops;
|
||||
|
||||
#endif
|
||||
|
|
|
@ -21,9 +21,87 @@
|
|||
#include <linux/of_address.h>
|
||||
#include <linux/clk/ti.h>
|
||||
|
||||
#include "clock.h"
|
||||
|
||||
#undef pr_fmt
|
||||
#define pr_fmt(fmt) "%s: " fmt, __func__
|
||||
|
||||
/**
|
||||
* 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 (ti_clk_get_features()->flags & TI_CLK_DISABLE_CLKDM_CONTROL) {
|
||||
pr_err("%s: %s: clkfw-based clockdomain control disabled ?!\n",
|
||||
__func__, __clk_get_name(hw->clk));
|
||||
return 0;
|
||||
}
|
||||
|
||||
ret = ti_clk_ll_ops->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 (ti_clk_get_features()->flags & TI_CLK_DISABLE_CLKDM_CONTROL) {
|
||||
pr_err("%s: %s: clkfw-based clockdomain control disabled ?!\n",
|
||||
__func__, __clk_get_name(hw->clk));
|
||||
return;
|
||||
}
|
||||
|
||||
ti_clk_ll_ops->clkdm_clk_disable(clk->clkdm, hw->clk);
|
||||
}
|
||||
|
||||
static void __init of_ti_clockdomain_setup(struct device_node *node)
|
||||
{
|
||||
struct clk *clk;
|
||||
|
|
|
@ -27,8 +27,8 @@
|
|||
#include <linux/io.h>
|
||||
#include <linux/bitops.h>
|
||||
#include <linux/clkdev.h>
|
||||
#include <linux/clk/ti.h>
|
||||
|
||||
#include "clockdomain.h"
|
||||
#include "clock.h"
|
||||
|
||||
/* CM_AUTOIDLE_PLL*.AUTO_* bit values */
|
||||
|
@ -37,6 +37,13 @@
|
|||
|
||||
#define MAX_DPLL_WAIT_TRIES 1000000
|
||||
|
||||
#define OMAP3XXX_EN_DPLL_LOCKED 0x7
|
||||
|
||||
/* Forward declarations */
|
||||
static u32 omap3_dpll_autoidle_read(struct clk_hw_omap *clk);
|
||||
static void omap3_dpll_deny_idle(struct clk_hw_omap *clk);
|
||||
static void omap3_dpll_allow_idle(struct clk_hw_omap *clk);
|
||||
|
||||
/* Private functions */
|
||||
|
||||
/* _omap3_dpll_write_clken - write clken_bits arg to a DPLL's enable bits */
|
||||
|
@ -47,10 +54,10 @@ static void _omap3_dpll_write_clken(struct clk_hw_omap *clk, u8 clken_bits)
|
|||
|
||||
dd = clk->dpll_data;
|
||||
|
||||
v = omap2_clk_readl(clk, dd->control_reg);
|
||||
v = ti_clk_ll_ops->clk_readl(dd->control_reg);
|
||||
v &= ~dd->enable_mask;
|
||||
v |= clken_bits << __ffs(dd->enable_mask);
|
||||
omap2_clk_writel(v, clk, dd->control_reg);
|
||||
ti_clk_ll_ops->clk_writel(v, dd->control_reg);
|
||||
}
|
||||
|
||||
/* _omap3_wait_dpll_status: wait for a DPLL to enter a specific state */
|
||||
|
@ -66,14 +73,14 @@ static int _omap3_wait_dpll_status(struct clk_hw_omap *clk, u8 state)
|
|||
|
||||
state <<= __ffs(dd->idlest_mask);
|
||||
|
||||
while (((omap2_clk_readl(clk, dd->idlest_reg) & dd->idlest_mask)
|
||||
while (((ti_clk_ll_ops->clk_readl(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",
|
||||
pr_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",
|
||||
|
@ -144,7 +151,8 @@ static int _omap3_noncore_dpll_lock(struct clk_hw_omap *clk)
|
|||
state <<= __ffs(dd->idlest_mask);
|
||||
|
||||
/* Check if already locked */
|
||||
if ((omap2_clk_readl(clk, dd->idlest_reg) & dd->idlest_mask) == state)
|
||||
if ((ti_clk_ll_ops->clk_readl(dd->idlest_reg) & dd->idlest_mask) ==
|
||||
state)
|
||||
goto done;
|
||||
|
||||
ai = omap3_dpll_autoidle_read(clk);
|
||||
|
@ -307,15 +315,15 @@ static int omap3_noncore_dpll_program(struct clk_hw_omap *clk, u16 freqsel)
|
|||
* 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);
|
||||
if (ti_clk_get_features()->flags & TI_CLK_DPLL_HAS_FREQSEL) {
|
||||
v = ti_clk_ll_ops->clk_readl(dd->control_reg);
|
||||
v &= ~dd->freqsel_mask;
|
||||
v |= freqsel << __ffs(dd->freqsel_mask);
|
||||
omap2_clk_writel(v, clk, dd->control_reg);
|
||||
ti_clk_ll_ops->clk_writel(v, dd->control_reg);
|
||||
}
|
||||
|
||||
/* Set DPLL multiplier, divider */
|
||||
v = omap2_clk_readl(clk, dd->mult_div1_reg);
|
||||
v = ti_clk_ll_ops->clk_readl(dd->mult_div1_reg);
|
||||
|
||||
/* Handle Duty Cycle Correction */
|
||||
if (dd->dcc_mask) {
|
||||
|
@ -342,11 +350,11 @@ static int omap3_noncore_dpll_program(struct clk_hw_omap *clk, u16 freqsel)
|
|||
v |= sd_div << __ffs(dd->sddiv_mask);
|
||||
}
|
||||
|
||||
omap2_clk_writel(v, clk, dd->mult_div1_reg);
|
||||
ti_clk_ll_ops->clk_writel(v, 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);
|
||||
v = ti_clk_ll_ops->clk_readl(dd->control_reg);
|
||||
|
||||
if (dd->m4xen_mask) {
|
||||
if (dd->last_rounded_m4xen)
|
||||
|
@ -362,7 +370,7 @@ static int omap3_noncore_dpll_program(struct clk_hw_omap *clk, u16 freqsel)
|
|||
v &= ~dd->lpmode_mask;
|
||||
}
|
||||
|
||||
omap2_clk_writel(v, clk, dd->control_reg);
|
||||
ti_clk_ll_ops->clk_writel(v, dd->control_reg);
|
||||
}
|
||||
|
||||
/* We let the clock framework set the other output dividers later */
|
||||
|
@ -417,12 +425,12 @@ int omap3_noncore_dpll_enable(struct clk_hw *hw)
|
|||
return -EINVAL;
|
||||
|
||||
if (clk->clkdm) {
|
||||
r = clkdm_clk_enable(clk->clkdm, hw->clk);
|
||||
r = ti_clk_ll_ops->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);
|
||||
clk->clkdm_name, r);
|
||||
return r;
|
||||
}
|
||||
}
|
||||
|
@ -453,10 +461,9 @@ void omap3_noncore_dpll_disable(struct clk_hw *hw)
|
|||
|
||||
_omap3_noncore_dpll_stop(clk);
|
||||
if (clk->clkdm)
|
||||
clkdm_clk_disable(clk->clkdm, hw->clk);
|
||||
ti_clk_ll_ops->clkdm_clk_disable(clk->clkdm, hw->clk);
|
||||
}
|
||||
|
||||
|
||||
/* Non-CORE DPLL rate set code */
|
||||
|
||||
/**
|
||||
|
@ -554,7 +561,7 @@ int omap3_noncore_dpll_set_rate(struct clk_hw *hw, unsigned long rate,
|
|||
return -EINVAL;
|
||||
|
||||
/* Freqsel is available only on OMAP343X devices */
|
||||
if (ti_clk_features.flags & TI_CLK_DPLL_HAS_FREQSEL) {
|
||||
if (ti_clk_get_features()->flags & TI_CLK_DPLL_HAS_FREQSEL) {
|
||||
freqsel = _omap3_dpll_compute_freqsel(clk, dd->last_rounded_n);
|
||||
WARN_ON(!freqsel);
|
||||
}
|
||||
|
@ -613,7 +620,7 @@ int omap3_noncore_dpll_set_rate_and_parent(struct clk_hw *hw,
|
|||
* -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)
|
||||
static u32 omap3_dpll_autoidle_read(struct clk_hw_omap *clk)
|
||||
{
|
||||
const struct dpll_data *dd;
|
||||
u32 v;
|
||||
|
@ -626,7 +633,7 @@ u32 omap3_dpll_autoidle_read(struct clk_hw_omap *clk)
|
|||
if (!dd->autoidle_reg)
|
||||
return -EINVAL;
|
||||
|
||||
v = omap2_clk_readl(clk, dd->autoidle_reg);
|
||||
v = ti_clk_ll_ops->clk_readl(dd->autoidle_reg);
|
||||
v &= dd->autoidle_mask;
|
||||
v >>= __ffs(dd->autoidle_mask);
|
||||
|
||||
|
@ -642,7 +649,7 @@ u32 omap3_dpll_autoidle_read(struct clk_hw_omap *clk)
|
|||
* 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)
|
||||
static void omap3_dpll_allow_idle(struct clk_hw_omap *clk)
|
||||
{
|
||||
const struct dpll_data *dd;
|
||||
u32 v;
|
||||
|
@ -660,11 +667,10 @@ void omap3_dpll_allow_idle(struct clk_hw_omap *clk)
|
|||
* by writing 0x5 instead of 0x1. Add some mechanism to
|
||||
* optionally enter this mode.
|
||||
*/
|
||||
v = omap2_clk_readl(clk, dd->autoidle_reg);
|
||||
v = ti_clk_ll_ops->clk_readl(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);
|
||||
|
||||
ti_clk_ll_ops->clk_writel(v, dd->autoidle_reg);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -673,7 +679,7 @@ void omap3_dpll_allow_idle(struct clk_hw_omap *clk)
|
|||
*
|
||||
* Disable DPLL automatic idle control. No return value.
|
||||
*/
|
||||
void omap3_dpll_deny_idle(struct clk_hw_omap *clk)
|
||||
static void omap3_dpll_deny_idle(struct clk_hw_omap *clk)
|
||||
{
|
||||
const struct dpll_data *dd;
|
||||
u32 v;
|
||||
|
@ -686,11 +692,10 @@ void omap3_dpll_deny_idle(struct clk_hw_omap *clk)
|
|||
if (!dd->autoidle_reg)
|
||||
return;
|
||||
|
||||
v = omap2_clk_readl(clk, dd->autoidle_reg);
|
||||
v = ti_clk_ll_ops->clk_readl(dd->autoidle_reg);
|
||||
v &= ~dd->autoidle_mask;
|
||||
v |= DPLL_AUTOIDLE_DISABLE << __ffs(dd->autoidle_mask);
|
||||
omap2_clk_writel(v, clk, dd->autoidle_reg);
|
||||
|
||||
ti_clk_ll_ops->clk_writel(v, dd->autoidle_reg);
|
||||
}
|
||||
|
||||
/* Clock control for DPLL outputs */
|
||||
|
@ -748,7 +753,7 @@ unsigned long omap3_clkoutx2_recalc(struct clk_hw *hw,
|
|||
|
||||
WARN_ON(!dd->enable_mask);
|
||||
|
||||
v = omap2_clk_readl(pclk, dd->control_reg) & dd->enable_mask;
|
||||
v = ti_clk_ll_ops->clk_readl(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;
|
||||
|
@ -757,57 +762,59 @@ unsigned long omap3_clkoutx2_recalc(struct clk_hw *hw,
|
|||
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,
|
||||
};
|
||||
|
||||
/**
|
||||
* 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_get_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_get_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);
|
||||
}
|
|
@ -14,6 +14,7 @@
|
|||
#include <linux/clk.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/bitops.h>
|
||||
#include <linux/clk/ti.h>
|
||||
|
||||
#include "clock.h"
|
||||
|
||||
|
@ -29,14 +30,14 @@
|
|||
/*
|
||||
* 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)
|
||||
#define OMAP4430_DPLL_CLKOUT_GATE_CTRL_MASK BIT(8)
|
||||
#define OMAP4430_DPLL_CLKOUTX2_GATE_CTRL_MASK BIT(10)
|
||||
#define OMAP4430_DPLL_REGM4XEN_MASK BIT(11)
|
||||
|
||||
/* Static rate multiplier for OMAP4 REGM4XEN clocks */
|
||||
#define OMAP4430_REGM4XEN_MULT 4
|
||||
|
||||
void omap4_dpllmx_allow_gatectrl(struct clk_hw_omap *clk)
|
||||
static void omap4_dpllmx_allow_gatectrl(struct clk_hw_omap *clk)
|
||||
{
|
||||
u32 v;
|
||||
u32 mask;
|
||||
|
@ -48,13 +49,13 @@ void omap4_dpllmx_allow_gatectrl(struct clk_hw_omap *clk)
|
|||
OMAP4430_DPLL_CLKOUTX2_GATE_CTRL_MASK :
|
||||
OMAP4430_DPLL_CLKOUT_GATE_CTRL_MASK;
|
||||
|
||||
v = omap2_clk_readl(clk, clk->clksel_reg);
|
||||
v = ti_clk_ll_ops->clk_readl(clk->clksel_reg);
|
||||
/* Clear the bit to allow gatectrl */
|
||||
v &= ~mask;
|
||||
omap2_clk_writel(v, clk, clk->clksel_reg);
|
||||
ti_clk_ll_ops->clk_writel(v, clk->clksel_reg);
|
||||
}
|
||||
|
||||
void omap4_dpllmx_deny_gatectrl(struct clk_hw_omap *clk)
|
||||
static void omap4_dpllmx_deny_gatectrl(struct clk_hw_omap *clk)
|
||||
{
|
||||
u32 v;
|
||||
u32 mask;
|
||||
|
@ -66,10 +67,10 @@ void omap4_dpllmx_deny_gatectrl(struct clk_hw_omap *clk)
|
|||
OMAP4430_DPLL_CLKOUTX2_GATE_CTRL_MASK :
|
||||
OMAP4430_DPLL_CLKOUT_GATE_CTRL_MASK;
|
||||
|
||||
v = omap2_clk_readl(clk, clk->clksel_reg);
|
||||
v = ti_clk_ll_ops->clk_readl(clk->clksel_reg);
|
||||
/* Set the bit to deny gatectrl */
|
||||
v |= mask;
|
||||
omap2_clk_writel(v, clk, clk->clksel_reg);
|
||||
ti_clk_ll_ops->clk_writel(v, clk->clksel_reg);
|
||||
}
|
||||
|
||||
const struct clk_hw_omap_ops clkhwops_omap4_dpllmx = {
|
||||
|
@ -112,7 +113,7 @@ static void omap4_dpll_lpmode_recalc(struct dpll_data *dd)
|
|||
* upon success, or 0 upon error.
|
||||
*/
|
||||
unsigned long omap4_dpll_regm4xen_recalc(struct clk_hw *hw,
|
||||
unsigned long parent_rate)
|
||||
unsigned long parent_rate)
|
||||
{
|
||||
struct clk_hw_omap *clk = to_clk_hw_omap(hw);
|
||||
u32 v;
|
||||
|
@ -127,7 +128,7 @@ unsigned long omap4_dpll_regm4xen_recalc(struct clk_hw *hw,
|
|||
rate = omap2_get_dpll_rate(clk);
|
||||
|
||||
/* regm4xen adds a multiplier of 4 to DPLL calculations */
|
||||
v = omap2_clk_readl(clk, dd->control_reg);
|
||||
v = ti_clk_ll_ops->clk_readl(dd->control_reg);
|
||||
if (v & OMAP4430_DPLL_REGM4XEN_MASK)
|
||||
rate *= OMAP4430_REGM4XEN_MULT;
|
||||
|
|
@ -22,6 +22,8 @@
|
|||
#include <linux/of_address.h>
|
||||
#include <linux/clk/ti.h>
|
||||
|
||||
#include "clock.h"
|
||||
|
||||
#undef pr_fmt
|
||||
#define pr_fmt(fmt) "%s: " fmt, __func__
|
||||
|
||||
|
|
|
@ -188,33 +188,6 @@ struct clk_hw_omap {
|
|||
/* DPLL Type and DCO Selection Flags */
|
||||
#define DPLL_J_TYPE 0x1
|
||||
|
||||
/* Composite clock component types */
|
||||
enum {
|
||||
CLK_COMPONENT_TYPE_GATE = 0,
|
||||
CLK_COMPONENT_TYPE_DIVIDER,
|
||||
CLK_COMPONENT_TYPE_MUX,
|
||||
CLK_COMPONENT_TYPE_MAX,
|
||||
};
|
||||
|
||||
/**
|
||||
* struct ti_dt_clk - OMAP DT clock alias declarations
|
||||
* @lk: clock lookup definition
|
||||
* @node_name: clock DT node to map to
|
||||
*/
|
||||
struct ti_dt_clk {
|
||||
struct clk_lookup lk;
|
||||
char *node_name;
|
||||
};
|
||||
|
||||
#define DT_CLK(dev, con, name) \
|
||||
{ \
|
||||
.lk = { \
|
||||
.dev_id = dev, \
|
||||
.con_id = con, \
|
||||
}, \
|
||||
.node_name = name, \
|
||||
}
|
||||
|
||||
/* Static memmap indices */
|
||||
enum {
|
||||
TI_CLKM_CM = 0,
|
||||
|
@ -225,8 +198,6 @@ enum {
|
|||
CLK_MAX_MEMMAPS
|
||||
};
|
||||
|
||||
typedef void (*ti_of_clk_init_cb_t)(struct clk_hw *, struct device_node *);
|
||||
|
||||
/**
|
||||
* struct clk_omap_reg - OMAP register declaration
|
||||
* @offset: offset from the master IP module base address
|
||||
|
@ -238,85 +209,56 @@ struct clk_omap_reg {
|
|||
};
|
||||
|
||||
/**
|
||||
* struct ti_clk_ll_ops - low-level register access ops for a clock
|
||||
* struct ti_clk_ll_ops - low-level ops for clocks
|
||||
* @clk_readl: pointer to register read function
|
||||
* @clk_writel: pointer to register write function
|
||||
* @clkdm_clk_enable: pointer to clockdomain enable function
|
||||
* @clkdm_clk_disable: pointer to clockdomain disable function
|
||||
* @cm_wait_module_ready: pointer to CM module wait ready function
|
||||
* @cm_split_idlest_reg: pointer to CM module function to split idlest reg
|
||||
*
|
||||
* Low-level register access ops are generally used by the basic clock types
|
||||
* (clk-gate, clk-mux, clk-divider etc.) to provide support for various
|
||||
* low-level hardware interfaces (direct MMIO, regmap etc.), but can also be
|
||||
* used by other hardware-specific clock drivers if needed.
|
||||
* Low-level ops are generally used by the basic clock types (clk-gate,
|
||||
* clk-mux, clk-divider etc.) to provide support for various low-level
|
||||
* hadrware interfaces (direct MMIO, regmap etc.), and is initialized
|
||||
* by board code. Low-level ops also contain some other platform specific
|
||||
* operations not provided directly by clock drivers.
|
||||
*/
|
||||
struct ti_clk_ll_ops {
|
||||
u32 (*clk_readl)(void __iomem *reg);
|
||||
void (*clk_writel)(u32 val, void __iomem *reg);
|
||||
int (*clkdm_clk_enable)(struct clockdomain *clkdm, struct clk *clk);
|
||||
int (*clkdm_clk_disable)(struct clockdomain *clkdm,
|
||||
struct clk *clk);
|
||||
int (*cm_wait_module_ready)(u8 part, s16 prcm_mod, u16 idlest_reg,
|
||||
u8 idlest_shift);
|
||||
int (*cm_split_idlest_reg)(void __iomem *idlest_reg, s16 *prcm_inst,
|
||||
u8 *idlest_reg_id);
|
||||
};
|
||||
|
||||
extern struct ti_clk_ll_ops *ti_clk_ll_ops;
|
||||
|
||||
extern const struct clk_ops ti_clk_divider_ops;
|
||||
extern const struct clk_ops ti_clk_mux_ops;
|
||||
|
||||
#define to_clk_hw_omap(_hw) container_of(_hw, struct clk_hw_omap, hw)
|
||||
|
||||
void omap2_init_clk_hw_omap_clocks(struct clk *clk);
|
||||
int omap3_noncore_dpll_enable(struct clk_hw *hw);
|
||||
void omap3_noncore_dpll_disable(struct clk_hw *hw);
|
||||
int omap3_noncore_dpll_set_parent(struct clk_hw *hw, u8 index);
|
||||
int omap3_noncore_dpll_set_rate(struct clk_hw *hw, unsigned long rate,
|
||||
unsigned long parent_rate);
|
||||
int omap3_noncore_dpll_set_rate_and_parent(struct clk_hw *hw,
|
||||
unsigned long rate,
|
||||
unsigned long parent_rate,
|
||||
u8 index);
|
||||
int omap3_noncore_dpll_determine_rate(struct clk_hw *hw,
|
||||
struct clk_rate_request *req);
|
||||
unsigned long omap4_dpll_regm4xen_recalc(struct clk_hw *hw,
|
||||
unsigned long parent_rate);
|
||||
long omap4_dpll_regm4xen_round_rate(struct clk_hw *hw,
|
||||
unsigned long target_rate,
|
||||
unsigned long *parent_rate);
|
||||
int omap4_dpll_regm4xen_determine_rate(struct clk_hw *hw,
|
||||
struct clk_rate_request *req);
|
||||
u8 omap2_init_dpll_parent(struct clk_hw *hw);
|
||||
unsigned long omap3_dpll_recalc(struct clk_hw *hw, unsigned long parent_rate);
|
||||
long omap2_dpll_round_rate(struct clk_hw *hw, unsigned long target_rate,
|
||||
unsigned long *parent_rate);
|
||||
void omap2_init_clk_clkdm(struct clk_hw *clk);
|
||||
unsigned long omap3_clkoutx2_recalc(struct clk_hw *hw,
|
||||
unsigned long parent_rate);
|
||||
int omap3_clkoutx2_set_rate(struct clk_hw *hw, unsigned long rate,
|
||||
unsigned long parent_rate);
|
||||
long omap3_clkoutx2_round_rate(struct clk_hw *hw, unsigned long rate,
|
||||
unsigned long *prate);
|
||||
int omap2_clkops_enable_clkdm(struct clk_hw *hw);
|
||||
void omap2_clkops_disable_clkdm(struct clk_hw *hw);
|
||||
int omap2_clk_disable_autoidle_all(void);
|
||||
void omap2_clk_enable_init_clocks(const char **clk_names, u8 num_clocks);
|
||||
int omap3_dpll4_set_rate(struct clk_hw *clk, unsigned long rate,
|
||||
unsigned long parent_rate);
|
||||
int omap3_dpll4_set_rate_and_parent(struct clk_hw *hw, unsigned long rate,
|
||||
unsigned long parent_rate, u8 index);
|
||||
int omap2_dflt_clk_enable(struct clk_hw *hw);
|
||||
void omap2_dflt_clk_disable(struct clk_hw *hw);
|
||||
int omap2_dflt_clk_is_enabled(struct clk_hw *hw);
|
||||
void omap3_clk_lock_dpll5(void);
|
||||
int omap2_clk_enable_autoidle_all(void);
|
||||
int omap2_clk_allow_idle(struct clk *clk);
|
||||
int omap2_clk_deny_idle(struct clk *clk);
|
||||
unsigned long omap2_dpllcore_recalc(struct clk_hw *hw,
|
||||
unsigned long parent_rate);
|
||||
int omap2_reprogram_dpllcore(struct clk_hw *clk, unsigned long rate,
|
||||
unsigned long parent_rate);
|
||||
void omap2xxx_clkt_dpllcore_init(struct clk_hw *hw);
|
||||
void omap2xxx_clkt_vps_init(void);
|
||||
unsigned long omap2_get_dpll_rate(struct clk_hw_omap *clk);
|
||||
|
||||
void __iomem *ti_clk_get_reg_addr(struct device_node *node, int index);
|
||||
void ti_dt_clocks_register(struct ti_dt_clk *oclks);
|
||||
void ti_dt_clk_init_provider(struct device_node *np, int index);
|
||||
void ti_dt_clk_init_retry_clks(void);
|
||||
void ti_dt_clockdomains_setup(void);
|
||||
int ti_clk_retry_init(struct device_node *node, struct clk_hw *hw,
|
||||
ti_of_clk_init_cb_t func);
|
||||
int of_ti_clk_autoidle_setup(struct device_node *node);
|
||||
int ti_clk_add_component(struct device_node *node, struct clk_hw *hw, int type);
|
||||
int ti_clk_setup_ll_ops(struct ti_clk_ll_ops *ops);
|
||||
|
||||
struct regmap;
|
||||
|
||||
int omap2_clk_provider_init(struct device_node *parent, int index,
|
||||
struct regmap *syscon, void __iomem *mem);
|
||||
void omap2_clk_legacy_provider_init(int index, void __iomem *mem);
|
||||
|
||||
int omap3430_dt_clk_init(void);
|
||||
int omap3630_dt_clk_init(void);
|
||||
|
@ -330,27 +272,24 @@ int am43xx_dt_clk_init(void);
|
|||
int omap2420_dt_clk_init(void);
|
||||
int omap2430_dt_clk_init(void);
|
||||
|
||||
#ifdef CONFIG_OF
|
||||
void of_ti_clk_allow_autoidle_all(void);
|
||||
void of_ti_clk_deny_autoidle_all(void);
|
||||
#else
|
||||
static inline void of_ti_clk_allow_autoidle_all(void) { }
|
||||
static inline void of_ti_clk_deny_autoidle_all(void) { }
|
||||
#endif
|
||||
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 BIT(0)
|
||||
#define TI_CLK_DPLL4_DENY_REPROGRAM BIT(1)
|
||||
#define TI_CLK_DISABLE_CLKDM_CONTROL BIT(2)
|
||||
|
||||
void ti_clk_setup_features(struct ti_clk_features *features);
|
||||
const struct ti_clk_features *ti_clk_get_features(void);
|
||||
|
||||
extern const struct clk_hw_omap_ops clkhwops_omap2xxx_dpll;
|
||||
extern const struct clk_hw_omap_ops clkhwops_omap2430_i2chs_wait;
|
||||
extern const struct clk_hw_omap_ops clkhwops_omap3_dpll;
|
||||
extern const struct clk_hw_omap_ops clkhwops_omap4_dpllmx;
|
||||
extern const struct clk_hw_omap_ops clkhwops_wait;
|
||||
extern const struct clk_hw_omap_ops clkhwops_omap3430es2_dss_usbhost_wait;
|
||||
extern const struct clk_hw_omap_ops clkhwops_am35xx_ipss_module_wait;
|
||||
extern const struct clk_hw_omap_ops clkhwops_am35xx_ipss_wait;
|
||||
extern const struct clk_hw_omap_ops clkhwops_iclk;
|
||||
extern const struct clk_hw_omap_ops clkhwops_iclk_wait;
|
||||
extern const struct clk_hw_omap_ops clkhwops_omap3430es2_iclk_ssi_wait;
|
||||
extern const struct clk_hw_omap_ops clkhwops_omap3430es2_iclk_dss_usbhost_wait;
|
||||
extern const struct clk_hw_omap_ops clkhwops_omap3430es2_iclk_hsotgusb_wait;
|
||||
|
||||
#ifdef CONFIG_ATAGS
|
||||
int omap3430_clk_legacy_init(void);
|
||||
|
|
Loading…
Reference in New Issue