2013-07-23 13:35:37 +07:00
|
|
|
/*
|
|
|
|
* OMAP APLL clock support
|
|
|
|
*
|
|
|
|
* Copyright (C) 2013 Texas Instruments, Inc.
|
|
|
|
*
|
|
|
|
* J Keerthy <j-keerthy@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.
|
|
|
|
*/
|
|
|
|
|
2015-06-20 05:00:46 +07:00
|
|
|
#include <linux/clk.h>
|
2013-07-23 13:35:37 +07:00
|
|
|
#include <linux/clk-provider.h>
|
|
|
|
#include <linux/module.h>
|
|
|
|
#include <linux/slab.h>
|
|
|
|
#include <linux/io.h>
|
|
|
|
#include <linux/err.h>
|
|
|
|
#include <linux/string.h>
|
|
|
|
#include <linux/log2.h>
|
|
|
|
#include <linux/of.h>
|
|
|
|
#include <linux/of_address.h>
|
|
|
|
#include <linux/clk/ti.h>
|
|
|
|
#include <linux/delay.h>
|
|
|
|
|
2015-03-05 02:02:05 +07:00
|
|
|
#include "clock.h"
|
|
|
|
|
2013-07-23 13:35:37 +07:00
|
|
|
#define APLL_FORCE_LOCK 0x1
|
|
|
|
#define APLL_AUTO_IDLE 0x2
|
|
|
|
#define MAX_APLL_WAIT_TRIES 1000000
|
|
|
|
|
|
|
|
#undef pr_fmt
|
|
|
|
#define pr_fmt(fmt) "%s: " fmt, __func__
|
|
|
|
|
|
|
|
static int dra7_apll_enable(struct clk_hw *hw)
|
|
|
|
{
|
|
|
|
struct clk_hw_omap *clk = to_clk_hw_omap(hw);
|
|
|
|
int r = 0, i = 0;
|
|
|
|
struct dpll_data *ad;
|
|
|
|
const char *clk_name;
|
|
|
|
u8 state = 1;
|
|
|
|
u32 v;
|
|
|
|
|
|
|
|
ad = clk->dpll_data;
|
|
|
|
if (!ad)
|
|
|
|
return -EINVAL;
|
|
|
|
|
2015-08-13 01:42:23 +07:00
|
|
|
clk_name = clk_hw_get_name(&clk->hw);
|
2013-07-23 13:35:37 +07:00
|
|
|
|
|
|
|
state <<= __ffs(ad->idlest_mask);
|
|
|
|
|
|
|
|
/* Check is already locked */
|
2017-02-09 16:24:37 +07:00
|
|
|
v = ti_clk_ll_ops->clk_readl(&ad->idlest_reg);
|
2013-07-23 13:35:37 +07:00
|
|
|
|
|
|
|
if ((v & ad->idlest_mask) == state)
|
|
|
|
return r;
|
|
|
|
|
2017-02-09 16:24:37 +07:00
|
|
|
v = ti_clk_ll_ops->clk_readl(&ad->control_reg);
|
2013-07-23 13:35:37 +07:00
|
|
|
v &= ~ad->enable_mask;
|
|
|
|
v |= APLL_FORCE_LOCK << __ffs(ad->enable_mask);
|
2017-02-09 16:24:37 +07:00
|
|
|
ti_clk_ll_ops->clk_writel(v, &ad->control_reg);
|
2013-07-23 13:35:37 +07:00
|
|
|
|
|
|
|
state <<= __ffs(ad->idlest_mask);
|
|
|
|
|
|
|
|
while (1) {
|
2017-02-09 16:24:37 +07:00
|
|
|
v = ti_clk_ll_ops->clk_readl(&ad->idlest_reg);
|
2013-07-23 13:35:37 +07:00
|
|
|
if ((v & ad->idlest_mask) == state)
|
|
|
|
break;
|
|
|
|
if (i > MAX_APLL_WAIT_TRIES)
|
|
|
|
break;
|
|
|
|
i++;
|
|
|
|
udelay(1);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (i == MAX_APLL_WAIT_TRIES) {
|
|
|
|
pr_warn("clock: %s failed transition to '%s'\n",
|
|
|
|
clk_name, (state) ? "locked" : "bypassed");
|
2014-05-19 18:25:48 +07:00
|
|
|
r = -EBUSY;
|
|
|
|
} else
|
2013-07-23 13:35:37 +07:00
|
|
|
pr_debug("clock: %s transition to '%s' in %d loops\n",
|
|
|
|
clk_name, (state) ? "locked" : "bypassed", i);
|
|
|
|
|
|
|
|
return r;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void dra7_apll_disable(struct clk_hw *hw)
|
|
|
|
{
|
|
|
|
struct clk_hw_omap *clk = to_clk_hw_omap(hw);
|
|
|
|
struct dpll_data *ad;
|
|
|
|
u8 state = 1;
|
|
|
|
u32 v;
|
|
|
|
|
|
|
|
ad = clk->dpll_data;
|
|
|
|
|
|
|
|
state <<= __ffs(ad->idlest_mask);
|
|
|
|
|
2017-02-09 16:24:37 +07:00
|
|
|
v = ti_clk_ll_ops->clk_readl(&ad->control_reg);
|
2013-07-23 13:35:37 +07:00
|
|
|
v &= ~ad->enable_mask;
|
|
|
|
v |= APLL_AUTO_IDLE << __ffs(ad->enable_mask);
|
2017-02-09 16:24:37 +07:00
|
|
|
ti_clk_ll_ops->clk_writel(v, &ad->control_reg);
|
2013-07-23 13:35:37 +07:00
|
|
|
}
|
|
|
|
|
|
|
|
static int dra7_apll_is_enabled(struct clk_hw *hw)
|
|
|
|
{
|
|
|
|
struct clk_hw_omap *clk = to_clk_hw_omap(hw);
|
|
|
|
struct dpll_data *ad;
|
|
|
|
u32 v;
|
|
|
|
|
|
|
|
ad = clk->dpll_data;
|
|
|
|
|
2017-02-09 16:24:37 +07:00
|
|
|
v = ti_clk_ll_ops->clk_readl(&ad->control_reg);
|
2013-07-23 13:35:37 +07:00
|
|
|
v &= ad->enable_mask;
|
|
|
|
|
|
|
|
v >>= __ffs(ad->enable_mask);
|
|
|
|
|
|
|
|
return v == APLL_AUTO_IDLE ? 0 : 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
static u8 dra7_init_apll_parent(struct clk_hw *hw)
|
|
|
|
{
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static const struct clk_ops apll_ck_ops = {
|
|
|
|
.enable = &dra7_apll_enable,
|
|
|
|
.disable = &dra7_apll_disable,
|
|
|
|
.is_enabled = &dra7_apll_is_enabled,
|
|
|
|
.get_parent = &dra7_init_apll_parent,
|
|
|
|
};
|
|
|
|
|
2017-11-06 14:43:16 +07:00
|
|
|
static void __init omap_clk_register_apll(void *user,
|
2013-07-23 13:35:37 +07:00
|
|
|
struct device_node *node)
|
|
|
|
{
|
2017-11-06 14:43:16 +07:00
|
|
|
struct clk_hw *hw = user;
|
2013-07-23 13:35:37 +07:00
|
|
|
struct clk_hw_omap *clk_hw = to_clk_hw_omap(hw);
|
|
|
|
struct dpll_data *ad = clk_hw->dpll_data;
|
|
|
|
struct clk *clk;
|
|
|
|
|
2016-02-20 18:24:26 +07:00
|
|
|
clk = of_clk_get(node, 0);
|
|
|
|
if (IS_ERR(clk)) {
|
2018-08-28 22:44:29 +07:00
|
|
|
pr_debug("clk-ref for %pOFn not ready, retry\n",
|
|
|
|
node);
|
2016-02-20 18:24:26 +07:00
|
|
|
if (!ti_clk_retry_init(node, hw, omap_clk_register_apll))
|
|
|
|
return;
|
|
|
|
|
|
|
|
goto cleanup;
|
|
|
|
}
|
2013-07-23 13:35:37 +07:00
|
|
|
|
2016-02-20 18:24:26 +07:00
|
|
|
ad->clk_ref = __clk_get_hw(clk);
|
|
|
|
|
|
|
|
clk = of_clk_get(node, 1);
|
|
|
|
if (IS_ERR(clk)) {
|
2018-08-28 22:44:29 +07:00
|
|
|
pr_debug("clk-bypass for %pOFn not ready, retry\n",
|
|
|
|
node);
|
2013-07-23 13:35:37 +07:00
|
|
|
if (!ti_clk_retry_init(node, hw, omap_clk_register_apll))
|
|
|
|
return;
|
|
|
|
|
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
2016-02-20 18:24:26 +07:00
|
|
|
ad->clk_bypass = __clk_get_hw(clk);
|
|
|
|
|
2019-01-15 16:15:15 +07:00
|
|
|
clk = ti_clk_register_omap_hw(NULL, &clk_hw->hw, node->name);
|
2013-07-23 13:35:37 +07:00
|
|
|
if (!IS_ERR(clk)) {
|
|
|
|
of_clk_add_provider(node, of_clk_src_simple_get, clk);
|
|
|
|
kfree(clk_hw->hw.init->parent_names);
|
|
|
|
kfree(clk_hw->hw.init);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
cleanup:
|
|
|
|
kfree(clk_hw->dpll_data);
|
|
|
|
kfree(clk_hw->hw.init->parent_names);
|
|
|
|
kfree(clk_hw->hw.init);
|
|
|
|
kfree(clk_hw);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void __init of_dra7_apll_setup(struct device_node *node)
|
|
|
|
{
|
|
|
|
struct dpll_data *ad = NULL;
|
|
|
|
struct clk_hw_omap *clk_hw = NULL;
|
|
|
|
struct clk_init_data *init = NULL;
|
|
|
|
const char **parent_names = NULL;
|
2017-02-09 16:24:37 +07:00
|
|
|
int ret;
|
2013-07-23 13:35:37 +07:00
|
|
|
|
|
|
|
ad = kzalloc(sizeof(*ad), GFP_KERNEL);
|
|
|
|
clk_hw = kzalloc(sizeof(*clk_hw), GFP_KERNEL);
|
|
|
|
init = kzalloc(sizeof(*init), GFP_KERNEL);
|
|
|
|
if (!ad || !clk_hw || !init)
|
|
|
|
goto cleanup;
|
|
|
|
|
|
|
|
clk_hw->dpll_data = ad;
|
|
|
|
clk_hw->hw.init = init;
|
|
|
|
|
|
|
|
init->name = node->name;
|
|
|
|
init->ops = &apll_ck_ops;
|
|
|
|
|
|
|
|
init->num_parents = of_clk_get_parent_count(node);
|
|
|
|
if (init->num_parents < 1) {
|
2018-08-28 22:44:29 +07:00
|
|
|
pr_err("dra7 apll %pOFn must have parent(s)\n", node);
|
2013-07-23 13:35:37 +07:00
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
treewide: kzalloc() -> kcalloc()
The kzalloc() function has a 2-factor argument form, kcalloc(). This
patch replaces cases of:
kzalloc(a * b, gfp)
with:
kcalloc(a * b, gfp)
as well as handling cases of:
kzalloc(a * b * c, gfp)
with:
kzalloc(array3_size(a, b, c), gfp)
as it's slightly less ugly than:
kzalloc_array(array_size(a, b), c, gfp)
This does, however, attempt to ignore constant size factors like:
kzalloc(4 * 1024, gfp)
though any constants defined via macros get caught up in the conversion.
Any factors with a sizeof() of "unsigned char", "char", and "u8" were
dropped, since they're redundant.
The Coccinelle script used for this was:
// Fix redundant parens around sizeof().
@@
type TYPE;
expression THING, E;
@@
(
kzalloc(
- (sizeof(TYPE)) * E
+ sizeof(TYPE) * E
, ...)
|
kzalloc(
- (sizeof(THING)) * E
+ sizeof(THING) * E
, ...)
)
// Drop single-byte sizes and redundant parens.
@@
expression COUNT;
typedef u8;
typedef __u8;
@@
(
kzalloc(
- sizeof(u8) * (COUNT)
+ COUNT
, ...)
|
kzalloc(
- sizeof(__u8) * (COUNT)
+ COUNT
, ...)
|
kzalloc(
- sizeof(char) * (COUNT)
+ COUNT
, ...)
|
kzalloc(
- sizeof(unsigned char) * (COUNT)
+ COUNT
, ...)
|
kzalloc(
- sizeof(u8) * COUNT
+ COUNT
, ...)
|
kzalloc(
- sizeof(__u8) * COUNT
+ COUNT
, ...)
|
kzalloc(
- sizeof(char) * COUNT
+ COUNT
, ...)
|
kzalloc(
- sizeof(unsigned char) * COUNT
+ COUNT
, ...)
)
// 2-factor product with sizeof(type/expression) and identifier or constant.
@@
type TYPE;
expression THING;
identifier COUNT_ID;
constant COUNT_CONST;
@@
(
- kzalloc
+ kcalloc
(
- sizeof(TYPE) * (COUNT_ID)
+ COUNT_ID, sizeof(TYPE)
, ...)
|
- kzalloc
+ kcalloc
(
- sizeof(TYPE) * COUNT_ID
+ COUNT_ID, sizeof(TYPE)
, ...)
|
- kzalloc
+ kcalloc
(
- sizeof(TYPE) * (COUNT_CONST)
+ COUNT_CONST, sizeof(TYPE)
, ...)
|
- kzalloc
+ kcalloc
(
- sizeof(TYPE) * COUNT_CONST
+ COUNT_CONST, sizeof(TYPE)
, ...)
|
- kzalloc
+ kcalloc
(
- sizeof(THING) * (COUNT_ID)
+ COUNT_ID, sizeof(THING)
, ...)
|
- kzalloc
+ kcalloc
(
- sizeof(THING) * COUNT_ID
+ COUNT_ID, sizeof(THING)
, ...)
|
- kzalloc
+ kcalloc
(
- sizeof(THING) * (COUNT_CONST)
+ COUNT_CONST, sizeof(THING)
, ...)
|
- kzalloc
+ kcalloc
(
- sizeof(THING) * COUNT_CONST
+ COUNT_CONST, sizeof(THING)
, ...)
)
// 2-factor product, only identifiers.
@@
identifier SIZE, COUNT;
@@
- kzalloc
+ kcalloc
(
- SIZE * COUNT
+ COUNT, SIZE
, ...)
// 3-factor product with 1 sizeof(type) or sizeof(expression), with
// redundant parens removed.
@@
expression THING;
identifier STRIDE, COUNT;
type TYPE;
@@
(
kzalloc(
- sizeof(TYPE) * (COUNT) * (STRIDE)
+ array3_size(COUNT, STRIDE, sizeof(TYPE))
, ...)
|
kzalloc(
- sizeof(TYPE) * (COUNT) * STRIDE
+ array3_size(COUNT, STRIDE, sizeof(TYPE))
, ...)
|
kzalloc(
- sizeof(TYPE) * COUNT * (STRIDE)
+ array3_size(COUNT, STRIDE, sizeof(TYPE))
, ...)
|
kzalloc(
- sizeof(TYPE) * COUNT * STRIDE
+ array3_size(COUNT, STRIDE, sizeof(TYPE))
, ...)
|
kzalloc(
- sizeof(THING) * (COUNT) * (STRIDE)
+ array3_size(COUNT, STRIDE, sizeof(THING))
, ...)
|
kzalloc(
- sizeof(THING) * (COUNT) * STRIDE
+ array3_size(COUNT, STRIDE, sizeof(THING))
, ...)
|
kzalloc(
- sizeof(THING) * COUNT * (STRIDE)
+ array3_size(COUNT, STRIDE, sizeof(THING))
, ...)
|
kzalloc(
- sizeof(THING) * COUNT * STRIDE
+ array3_size(COUNT, STRIDE, sizeof(THING))
, ...)
)
// 3-factor product with 2 sizeof(variable), with redundant parens removed.
@@
expression THING1, THING2;
identifier COUNT;
type TYPE1, TYPE2;
@@
(
kzalloc(
- sizeof(TYPE1) * sizeof(TYPE2) * COUNT
+ array3_size(COUNT, sizeof(TYPE1), sizeof(TYPE2))
, ...)
|
kzalloc(
- sizeof(TYPE1) * sizeof(THING2) * (COUNT)
+ array3_size(COUNT, sizeof(TYPE1), sizeof(TYPE2))
, ...)
|
kzalloc(
- sizeof(THING1) * sizeof(THING2) * COUNT
+ array3_size(COUNT, sizeof(THING1), sizeof(THING2))
, ...)
|
kzalloc(
- sizeof(THING1) * sizeof(THING2) * (COUNT)
+ array3_size(COUNT, sizeof(THING1), sizeof(THING2))
, ...)
|
kzalloc(
- sizeof(TYPE1) * sizeof(THING2) * COUNT
+ array3_size(COUNT, sizeof(TYPE1), sizeof(THING2))
, ...)
|
kzalloc(
- sizeof(TYPE1) * sizeof(THING2) * (COUNT)
+ array3_size(COUNT, sizeof(TYPE1), sizeof(THING2))
, ...)
)
// 3-factor product, only identifiers, with redundant parens removed.
@@
identifier STRIDE, SIZE, COUNT;
@@
(
kzalloc(
- (COUNT) * STRIDE * SIZE
+ array3_size(COUNT, STRIDE, SIZE)
, ...)
|
kzalloc(
- COUNT * (STRIDE) * SIZE
+ array3_size(COUNT, STRIDE, SIZE)
, ...)
|
kzalloc(
- COUNT * STRIDE * (SIZE)
+ array3_size(COUNT, STRIDE, SIZE)
, ...)
|
kzalloc(
- (COUNT) * (STRIDE) * SIZE
+ array3_size(COUNT, STRIDE, SIZE)
, ...)
|
kzalloc(
- COUNT * (STRIDE) * (SIZE)
+ array3_size(COUNT, STRIDE, SIZE)
, ...)
|
kzalloc(
- (COUNT) * STRIDE * (SIZE)
+ array3_size(COUNT, STRIDE, SIZE)
, ...)
|
kzalloc(
- (COUNT) * (STRIDE) * (SIZE)
+ array3_size(COUNT, STRIDE, SIZE)
, ...)
|
kzalloc(
- COUNT * STRIDE * SIZE
+ array3_size(COUNT, STRIDE, SIZE)
, ...)
)
// Any remaining multi-factor products, first at least 3-factor products,
// when they're not all constants...
@@
expression E1, E2, E3;
constant C1, C2, C3;
@@
(
kzalloc(C1 * C2 * C3, ...)
|
kzalloc(
- (E1) * E2 * E3
+ array3_size(E1, E2, E3)
, ...)
|
kzalloc(
- (E1) * (E2) * E3
+ array3_size(E1, E2, E3)
, ...)
|
kzalloc(
- (E1) * (E2) * (E3)
+ array3_size(E1, E2, E3)
, ...)
|
kzalloc(
- E1 * E2 * E3
+ array3_size(E1, E2, E3)
, ...)
)
// And then all remaining 2 factors products when they're not all constants,
// keeping sizeof() as the second factor argument.
@@
expression THING, E1, E2;
type TYPE;
constant C1, C2, C3;
@@
(
kzalloc(sizeof(THING) * C2, ...)
|
kzalloc(sizeof(TYPE) * C2, ...)
|
kzalloc(C1 * C2 * C3, ...)
|
kzalloc(C1 * C2, ...)
|
- kzalloc
+ kcalloc
(
- sizeof(TYPE) * (E2)
+ E2, sizeof(TYPE)
, ...)
|
- kzalloc
+ kcalloc
(
- sizeof(TYPE) * E2
+ E2, sizeof(TYPE)
, ...)
|
- kzalloc
+ kcalloc
(
- sizeof(THING) * (E2)
+ E2, sizeof(THING)
, ...)
|
- kzalloc
+ kcalloc
(
- sizeof(THING) * E2
+ E2, sizeof(THING)
, ...)
|
- kzalloc
+ kcalloc
(
- (E1) * E2
+ E1, E2
, ...)
|
- kzalloc
+ kcalloc
(
- (E1) * (E2)
+ E1, E2
, ...)
|
- kzalloc
+ kcalloc
(
- E1 * E2
+ E1, E2
, ...)
)
Signed-off-by: Kees Cook <keescook@chromium.org>
2018-06-13 04:03:40 +07:00
|
|
|
parent_names = kcalloc(init->num_parents, sizeof(char *), GFP_KERNEL);
|
2013-07-23 13:35:37 +07:00
|
|
|
if (!parent_names)
|
|
|
|
goto cleanup;
|
|
|
|
|
2015-07-07 10:59:06 +07:00
|
|
|
of_clk_parent_fill(node, parent_names, init->num_parents);
|
2013-07-23 13:35:37 +07:00
|
|
|
|
|
|
|
init->parent_names = parent_names;
|
|
|
|
|
2017-02-09 16:24:37 +07:00
|
|
|
ret = ti_clk_get_reg_addr(node, 0, &ad->control_reg);
|
|
|
|
ret |= ti_clk_get_reg_addr(node, 1, &ad->idlest_reg);
|
2013-07-23 13:35:37 +07:00
|
|
|
|
2017-02-09 16:24:37 +07:00
|
|
|
if (ret)
|
2013-07-23 13:35:37 +07:00
|
|
|
goto cleanup;
|
|
|
|
|
|
|
|
ad->idlest_mask = 0x1;
|
|
|
|
ad->enable_mask = 0x3;
|
|
|
|
|
|
|
|
omap_clk_register_apll(&clk_hw->hw, node);
|
|
|
|
return;
|
|
|
|
|
|
|
|
cleanup:
|
|
|
|
kfree(parent_names);
|
|
|
|
kfree(ad);
|
|
|
|
kfree(clk_hw);
|
|
|
|
kfree(init);
|
|
|
|
}
|
|
|
|
CLK_OF_DECLARE(dra7_apll_clock, "ti,dra7-apll-clock", of_dra7_apll_setup);
|
2014-02-24 21:06:34 +07:00
|
|
|
|
|
|
|
#define OMAP2_EN_APLL_LOCKED 0x3
|
|
|
|
#define OMAP2_EN_APLL_STOPPED 0x0
|
|
|
|
|
|
|
|
static int omap2_apll_is_enabled(struct clk_hw *hw)
|
|
|
|
{
|
|
|
|
struct clk_hw_omap *clk = to_clk_hw_omap(hw);
|
|
|
|
struct dpll_data *ad = clk->dpll_data;
|
|
|
|
u32 v;
|
|
|
|
|
2017-02-09 16:24:37 +07:00
|
|
|
v = ti_clk_ll_ops->clk_readl(&ad->control_reg);
|
2014-02-24 21:06:34 +07:00
|
|
|
v &= ad->enable_mask;
|
|
|
|
|
|
|
|
v >>= __ffs(ad->enable_mask);
|
|
|
|
|
|
|
|
return v == OMAP2_EN_APLL_LOCKED ? 1 : 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static unsigned long omap2_apll_recalc(struct clk_hw *hw,
|
|
|
|
unsigned long parent_rate)
|
|
|
|
{
|
|
|
|
struct clk_hw_omap *clk = to_clk_hw_omap(hw);
|
|
|
|
|
|
|
|
if (omap2_apll_is_enabled(hw))
|
|
|
|
return clk->fixed_rate;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int omap2_apll_enable(struct clk_hw *hw)
|
|
|
|
{
|
|
|
|
struct clk_hw_omap *clk = to_clk_hw_omap(hw);
|
|
|
|
struct dpll_data *ad = clk->dpll_data;
|
|
|
|
u32 v;
|
|
|
|
int i = 0;
|
|
|
|
|
2017-02-09 16:24:37 +07:00
|
|
|
v = ti_clk_ll_ops->clk_readl(&ad->control_reg);
|
2014-02-24 21:06:34 +07:00
|
|
|
v &= ~ad->enable_mask;
|
|
|
|
v |= OMAP2_EN_APLL_LOCKED << __ffs(ad->enable_mask);
|
2017-02-09 16:24:37 +07:00
|
|
|
ti_clk_ll_ops->clk_writel(v, &ad->control_reg);
|
2014-02-24 21:06:34 +07:00
|
|
|
|
|
|
|
while (1) {
|
2017-02-09 16:24:37 +07:00
|
|
|
v = ti_clk_ll_ops->clk_readl(&ad->idlest_reg);
|
2014-02-24 21:06:34 +07:00
|
|
|
if (v & ad->idlest_mask)
|
|
|
|
break;
|
|
|
|
if (i > MAX_APLL_WAIT_TRIES)
|
|
|
|
break;
|
|
|
|
i++;
|
|
|
|
udelay(1);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (i == MAX_APLL_WAIT_TRIES) {
|
|
|
|
pr_warn("%s failed to transition to locked\n",
|
2015-08-13 01:42:23 +07:00
|
|
|
clk_hw_get_name(&clk->hw));
|
2014-02-24 21:06:34 +07:00
|
|
|
return -EBUSY;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void omap2_apll_disable(struct clk_hw *hw)
|
|
|
|
{
|
|
|
|
struct clk_hw_omap *clk = to_clk_hw_omap(hw);
|
|
|
|
struct dpll_data *ad = clk->dpll_data;
|
|
|
|
u32 v;
|
|
|
|
|
2017-02-09 16:24:37 +07:00
|
|
|
v = ti_clk_ll_ops->clk_readl(&ad->control_reg);
|
2014-02-24 21:06:34 +07:00
|
|
|
v &= ~ad->enable_mask;
|
|
|
|
v |= OMAP2_EN_APLL_STOPPED << __ffs(ad->enable_mask);
|
2017-02-09 16:24:37 +07:00
|
|
|
ti_clk_ll_ops->clk_writel(v, &ad->control_reg);
|
2014-02-24 21:06:34 +07:00
|
|
|
}
|
|
|
|
|
2017-08-22 20:11:15 +07:00
|
|
|
static const struct clk_ops omap2_apll_ops = {
|
2014-02-24 21:06:34 +07:00
|
|
|
.enable = &omap2_apll_enable,
|
|
|
|
.disable = &omap2_apll_disable,
|
|
|
|
.is_enabled = &omap2_apll_is_enabled,
|
|
|
|
.recalc_rate = &omap2_apll_recalc,
|
|
|
|
};
|
|
|
|
|
|
|
|
static void omap2_apll_set_autoidle(struct clk_hw_omap *clk, u32 val)
|
|
|
|
{
|
|
|
|
struct dpll_data *ad = clk->dpll_data;
|
|
|
|
u32 v;
|
|
|
|
|
2017-02-09 16:24:37 +07:00
|
|
|
v = ti_clk_ll_ops->clk_readl(&ad->autoidle_reg);
|
2014-02-24 21:06:34 +07:00
|
|
|
v &= ~ad->autoidle_mask;
|
|
|
|
v |= val << __ffs(ad->autoidle_mask);
|
2017-02-09 16:24:37 +07:00
|
|
|
ti_clk_ll_ops->clk_writel(v, &ad->control_reg);
|
2014-02-24 21:06:34 +07:00
|
|
|
}
|
|
|
|
|
|
|
|
#define OMAP2_APLL_AUTOIDLE_LOW_POWER_STOP 0x3
|
|
|
|
#define OMAP2_APLL_AUTOIDLE_DISABLE 0x0
|
|
|
|
|
|
|
|
static void omap2_apll_allow_idle(struct clk_hw_omap *clk)
|
|
|
|
{
|
|
|
|
omap2_apll_set_autoidle(clk, OMAP2_APLL_AUTOIDLE_LOW_POWER_STOP);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void omap2_apll_deny_idle(struct clk_hw_omap *clk)
|
|
|
|
{
|
|
|
|
omap2_apll_set_autoidle(clk, OMAP2_APLL_AUTOIDLE_DISABLE);
|
|
|
|
}
|
|
|
|
|
2015-11-16 02:48:14 +07:00
|
|
|
static const struct clk_hw_omap_ops omap2_apll_hwops = {
|
2014-02-24 21:06:34 +07:00
|
|
|
.allow_idle = &omap2_apll_allow_idle,
|
|
|
|
.deny_idle = &omap2_apll_deny_idle,
|
|
|
|
};
|
|
|
|
|
|
|
|
static void __init of_omap2_apll_setup(struct device_node *node)
|
|
|
|
{
|
|
|
|
struct dpll_data *ad = NULL;
|
|
|
|
struct clk_hw_omap *clk_hw = NULL;
|
|
|
|
struct clk_init_data *init = NULL;
|
|
|
|
struct clk *clk;
|
|
|
|
const char *parent_name;
|
|
|
|
u32 val;
|
2017-02-09 16:24:37 +07:00
|
|
|
int ret;
|
2014-02-24 21:06:34 +07:00
|
|
|
|
2014-06-16 16:32:33 +07:00
|
|
|
ad = kzalloc(sizeof(*ad), GFP_KERNEL);
|
2014-02-24 21:06:34 +07:00
|
|
|
clk_hw = kzalloc(sizeof(*clk_hw), GFP_KERNEL);
|
|
|
|
init = kzalloc(sizeof(*init), GFP_KERNEL);
|
|
|
|
|
|
|
|
if (!ad || !clk_hw || !init)
|
|
|
|
goto cleanup;
|
|
|
|
|
|
|
|
clk_hw->dpll_data = ad;
|
|
|
|
clk_hw->hw.init = init;
|
|
|
|
init->ops = &omap2_apll_ops;
|
|
|
|
init->name = node->name;
|
|
|
|
clk_hw->ops = &omap2_apll_hwops;
|
|
|
|
|
|
|
|
init->num_parents = of_clk_get_parent_count(node);
|
|
|
|
if (init->num_parents != 1) {
|
2018-08-28 22:44:29 +07:00
|
|
|
pr_err("%pOFn must have one parent\n", node);
|
2014-02-24 21:06:34 +07:00
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
|
|
|
parent_name = of_clk_get_parent_name(node, 0);
|
|
|
|
init->parent_names = &parent_name;
|
|
|
|
|
|
|
|
if (of_property_read_u32(node, "ti,clock-frequency", &val)) {
|
2018-08-28 22:44:29 +07:00
|
|
|
pr_err("%pOFn missing clock-frequency\n", node);
|
2014-02-24 21:06:34 +07:00
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
clk_hw->fixed_rate = val;
|
|
|
|
|
|
|
|
if (of_property_read_u32(node, "ti,bit-shift", &val)) {
|
2018-08-28 22:44:29 +07:00
|
|
|
pr_err("%pOFn missing bit-shift\n", node);
|
2014-02-24 21:06:34 +07:00
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
|
|
|
clk_hw->enable_bit = val;
|
|
|
|
ad->enable_mask = 0x3 << val;
|
|
|
|
ad->autoidle_mask = 0x3 << val;
|
|
|
|
|
|
|
|
if (of_property_read_u32(node, "ti,idlest-shift", &val)) {
|
2018-08-28 22:44:29 +07:00
|
|
|
pr_err("%pOFn missing idlest-shift\n", node);
|
2014-02-24 21:06:34 +07:00
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
|
|
|
ad->idlest_mask = 1 << val;
|
|
|
|
|
2017-02-09 16:24:37 +07:00
|
|
|
ret = ti_clk_get_reg_addr(node, 0, &ad->control_reg);
|
|
|
|
ret |= ti_clk_get_reg_addr(node, 1, &ad->autoidle_reg);
|
|
|
|
ret |= ti_clk_get_reg_addr(node, 2, &ad->idlest_reg);
|
2014-02-24 21:06:34 +07:00
|
|
|
|
2017-02-09 16:24:37 +07:00
|
|
|
if (ret)
|
2014-02-24 21:06:34 +07:00
|
|
|
goto cleanup;
|
|
|
|
|
2019-01-15 16:15:15 +07:00
|
|
|
clk = ti_clk_register_omap_hw(NULL, &clk_hw->hw, node->name);
|
2014-02-24 21:06:34 +07:00
|
|
|
if (!IS_ERR(clk)) {
|
|
|
|
of_clk_add_provider(node, of_clk_src_simple_get, clk);
|
|
|
|
kfree(init);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
cleanup:
|
|
|
|
kfree(ad);
|
|
|
|
kfree(clk_hw);
|
|
|
|
kfree(init);
|
|
|
|
}
|
|
|
|
CLK_OF_DECLARE(omap2_apll_clock, "ti,omap2-apll-clock",
|
|
|
|
of_omap2_apll_setup);
|