1032 lines
22 KiB
C
1032 lines
22 KiB
C
/* Copyright (c) 2019-2025 Allwinner Technology Co., Ltd. ALL rights reserved.
|
||
*
|
||
* Allwinner is a trademark of Allwinner Technology Co.,Ltd., registered in
|
||
*the the People's Republic of China and other countries.
|
||
* All Allwinner Technology Co.,Ltd. trademarks are used with permission.
|
||
*
|
||
* DISCLAIMER
|
||
* THIRD PARTY LICENCES MAY BE REQUIRED TO IMPLEMENT THE SOLUTION/PRODUCT.
|
||
* IF YOU NEED TO INTEGRATE THIRD PARTY’S TECHNOLOGY (SONY, DTS, DOLBY, AVS OR MPEGLA, ETC.)
|
||
* IN ALLWINNERS’SDK OR PRODUCTS, YOU SHALL BE SOLELY RESPONSIBLE TO OBTAIN
|
||
* ALL APPROPRIATELY REQUIRED THIRD PARTY LICENCES.
|
||
* ALLWINNER SHALL HAVE NO WARRANTY, INDEMNITY OR OTHER OBLIGATIONS WITH RESPECT TO MATTERS
|
||
* COVERED UNDER ANY REQUIRED THIRD PARTY LICENSE.
|
||
* YOU ARE SOLELY RESPONSIBLE FOR YOUR USAGE OF THIRD PARTY’S TECHNOLOGY.
|
||
*
|
||
*
|
||
* THIS SOFTWARE IS PROVIDED BY ALLWINNER"AS IS" AND TO THE MAXIMUM EXTENT
|
||
* PERMITTED BY LAW, ALLWINNER EXPRESSLY DISCLAIMS ALL WARRANTIES OF ANY KIND,
|
||
* WHETHER EXPRESS, IMPLIED OR STATUTORY, INCLUDING WITHOUT LIMITATION REGARDING
|
||
* THE TITLE, NON-INFRINGEMENT, ACCURACY, CONDITION, COMPLETENESS, PERFORMANCE
|
||
* OR MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
|
||
* IN NO EVENT SHALL ALLWINNER BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
||
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||
* LOSS OF USE, DATA, OR PROFITS, OR BUSINESS INTERRUPTION)
|
||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
|
||
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
|
||
* OF THE POSSIBILITY OF SUCH DAMAGE.
|
||
*/
|
||
#include "ccu.h"
|
||
#include <stdlib.h>
|
||
#include <hal_clk.h>
|
||
#include <sunxi_hal_common.h>
|
||
|
||
static LIST_HEAD(clk_root_list);
|
||
|
||
const char *clk_hw_get_name(const struct clk_hw *hw)
|
||
{
|
||
if (!hw || !hw->core)
|
||
return NULL;
|
||
|
||
return hw->core->name;
|
||
}
|
||
|
||
unsigned long clk_hw_get_flags(const struct clk_hw *hw)
|
||
{
|
||
if (!hw || !hw->core)
|
||
return 0;
|
||
|
||
return hw->core->flags;
|
||
}
|
||
|
||
struct clk_core *clk_hw_get_core(const struct clk_hw *hw)
|
||
{
|
||
if (!hw || !hw->core)
|
||
return NULL;
|
||
|
||
return hw->core;
|
||
}
|
||
|
||
struct clk_core *clk_core_get_by_name(const char *name)
|
||
{
|
||
struct clk_core *core = NULL;
|
||
|
||
list_for_each_entry(core, &clk_root_list, node)
|
||
{
|
||
if (strcmp(name, core->name))
|
||
{
|
||
continue;
|
||
}
|
||
return core;
|
||
}
|
||
|
||
return NULL;
|
||
}
|
||
|
||
static struct clk_core *clk_core_get_by_pindex(struct clk_core *core, u8 p_index)
|
||
{
|
||
if (p_index > core->num_parents)
|
||
{
|
||
return NULL;
|
||
}
|
||
|
||
return clk_core_get_by_name(core->parents[p_index].name);
|
||
}
|
||
|
||
static void clk_core_fill_parent_index(struct clk_core *core, u8 index)
|
||
{
|
||
struct clk_parent_map *entry = &core->parents[index];
|
||
struct clk_core *parent = NULL;
|
||
|
||
if (entry->hw)
|
||
{
|
||
parent = entry->hw->core;
|
||
/*
|
||
* We have a direct reference but it isn't registered yet?
|
||
* Orphan it and let clk_reparent() update the orphan status
|
||
* when the parent is registered.
|
||
*/
|
||
}
|
||
else
|
||
{
|
||
parent = clk_core_get_by_pindex(core, index);
|
||
}
|
||
|
||
/* Only cache it if it's not an error */
|
||
if (parent)
|
||
{
|
||
entry->core = parent;
|
||
}
|
||
}
|
||
|
||
static struct clk_core *clk_core_get_parent_by_index(struct clk_core *core,
|
||
u8 index)
|
||
{
|
||
if (!core || index >= core->num_parents || !core->parents)
|
||
{
|
||
return NULL;
|
||
}
|
||
|
||
if (!core->parents[index].core)
|
||
{
|
||
clk_core_fill_parent_index(core, index);
|
||
}
|
||
return core->parents[index].core;
|
||
}
|
||
|
||
struct clk_hw *
|
||
clk_hw_get_parent_by_index(const struct clk_hw *hw, unsigned int index)
|
||
{
|
||
struct clk_core *parent;
|
||
|
||
parent = clk_core_get_parent_by_index(hw->core, index);
|
||
|
||
return !parent ? NULL : parent->hw;
|
||
}
|
||
|
||
unsigned int clk_hw_get_num_parents(const struct clk_hw *hw)
|
||
{
|
||
return hw->core->num_parents;
|
||
}
|
||
|
||
struct clk_hw *clk_hw_get_parent(const struct clk_hw *hw)
|
||
{
|
||
return hw->core->parent ? hw->core->parent->hw : NULL;
|
||
}
|
||
|
||
static u32 __clk_get_accuracy(struct clk_core *core)
|
||
{
|
||
if (!core)
|
||
{
|
||
return 0;
|
||
}
|
||
return core->accuracy;
|
||
}
|
||
|
||
static unsigned long clk_core_get_rate_nolock(struct clk_core *core)
|
||
{
|
||
if (!core)
|
||
{
|
||
return 0;
|
||
}
|
||
|
||
if (!core->num_parents || core->parent)
|
||
{
|
||
return core->rate;
|
||
}
|
||
|
||
/*
|
||
* Clk must have a parent because num_parents > 0 but the parent isn't
|
||
* known yet. Best to return 0 as the rate of this clk until we can
|
||
* properly recalc the rate based on the parent's rate.
|
||
*/
|
||
return 0;
|
||
}
|
||
|
||
static int clk_core_determine_round_nolock(struct clk_core *core,
|
||
struct clk_rate_request *req)
|
||
{
|
||
long rate;
|
||
|
||
if (!core)
|
||
{
|
||
return 0;
|
||
}
|
||
|
||
/*
|
||
* At this point, core protection will be disabled if
|
||
* - if the provider is not protected at all
|
||
* - if the calling consumer is the only one which has exclusivity
|
||
* over the provider
|
||
*/
|
||
if (core->ops->determine_rate)
|
||
{
|
||
return core->ops->determine_rate(core->hw, req);
|
||
}
|
||
else if (core->ops->round_rate)
|
||
{
|
||
rate = core->ops->round_rate(core->hw, req->rate,
|
||
&req->best_parent_rate);
|
||
if (rate < 0)
|
||
{
|
||
return rate;
|
||
}
|
||
|
||
req->rate = rate;
|
||
}
|
||
else
|
||
{
|
||
return -1;
|
||
}
|
||
|
||
return 0;
|
||
}
|
||
|
||
static void clk_core_init_rate_req(struct clk_core *const core,
|
||
struct clk_rate_request *req)
|
||
{
|
||
struct clk_core *parent;
|
||
|
||
if (!core || !req)
|
||
{
|
||
return;
|
||
}
|
||
|
||
parent = core->parent;
|
||
if (parent)
|
||
{
|
||
req->best_parent_hw = parent->hw;
|
||
req->best_parent_rate = parent->rate;
|
||
}
|
||
else
|
||
{
|
||
req->best_parent_hw = NULL;
|
||
req->best_parent_rate = 0;
|
||
}
|
||
}
|
||
|
||
static u8 clk_core_can_round(struct clk_core *const core)
|
||
{
|
||
return core->ops->determine_rate || core->ops->round_rate;
|
||
}
|
||
|
||
static int clk_core_round_rate_nolock(struct clk_core *core,
|
||
struct clk_rate_request *req)
|
||
{
|
||
if (!core)
|
||
{
|
||
req->rate = 0;
|
||
return 0;
|
||
}
|
||
|
||
clk_core_init_rate_req(core, req);
|
||
|
||
if (clk_core_can_round(core))
|
||
{
|
||
return clk_core_determine_round_nolock(core, req);
|
||
}
|
||
else if (core->flags & CLK_SET_RATE_PARENT)
|
||
{
|
||
return clk_core_round_rate_nolock(core->parent, req);
|
||
}
|
||
|
||
req->rate = core->rate;
|
||
return 0;
|
||
}
|
||
|
||
/**
|
||
* __clk_determine_rate - get the closest rate actually supported by a clock
|
||
* @hw: determine the rate of this clock
|
||
* @req: target rate request
|
||
*
|
||
* Useful for clk_ops such as .set_rate and .determine_rate.
|
||
*/
|
||
int __clk_determine_rate(struct clk_hw *hw, struct clk_rate_request *req)
|
||
{
|
||
if (!hw)
|
||
{
|
||
req->rate = 0;
|
||
return 0;
|
||
}
|
||
|
||
return clk_core_round_rate_nolock(hw->core, req);
|
||
}
|
||
|
||
static u8 mux_is_better_rate(unsigned long rate, unsigned long now,
|
||
unsigned long best, unsigned long flags)
|
||
{
|
||
if (flags & CLK_MUX_ROUND_CLOSEST)
|
||
{
|
||
return abs(now - rate) < abs(best - rate);
|
||
}
|
||
|
||
return now <= rate && now > best;
|
||
}
|
||
|
||
int clk_mux_determine_rate_flags(struct clk_hw *hw,
|
||
struct clk_rate_request *req,
|
||
unsigned long flags)
|
||
{
|
||
struct clk_core *core = hw->core, *parent, *best_parent = NULL;
|
||
int i, num_parents, ret;
|
||
unsigned long best = 0;
|
||
struct clk_rate_request parent_req = *req;
|
||
|
||
/* if NO_REPARENT flag set, pass through to current parent */
|
||
if (core->flags & CLK_SET_RATE_NO_REPARENT)
|
||
{
|
||
parent = core->parent;
|
||
if (core->flags & CLK_SET_RATE_PARENT)
|
||
{
|
||
ret = __clk_determine_rate(parent ? parent->hw : NULL,
|
||
&parent_req);
|
||
if (ret)
|
||
{
|
||
return ret;
|
||
}
|
||
|
||
best = parent_req.rate;
|
||
}
|
||
else if (parent)
|
||
{
|
||
best = clk_core_get_rate_nolock(parent);
|
||
}
|
||
else
|
||
{
|
||
best = clk_core_get_rate_nolock(core);
|
||
}
|
||
|
||
goto out;
|
||
}
|
||
|
||
/* find the parent that can provide the fastest rate <= rate */
|
||
num_parents = core->num_parents;
|
||
for (i = 0; i < num_parents; i++)
|
||
{
|
||
parent = clk_core_get_parent_by_index(core, i);
|
||
if (!parent)
|
||
{
|
||
continue;
|
||
}
|
||
|
||
if (core->flags & CLK_SET_RATE_PARENT)
|
||
{
|
||
parent_req = *req;
|
||
ret = __clk_determine_rate(parent->hw, &parent_req);
|
||
if (ret)
|
||
{
|
||
continue;
|
||
}
|
||
}
|
||
else
|
||
{
|
||
parent_req.rate = clk_core_get_rate_nolock(parent);
|
||
}
|
||
|
||
if (mux_is_better_rate(req->rate, parent_req.rate,
|
||
best, flags))
|
||
{
|
||
best_parent = parent;
|
||
best = parent_req.rate;
|
||
}
|
||
}
|
||
|
||
if (!best_parent)
|
||
{
|
||
return -1;
|
||
}
|
||
|
||
out:
|
||
if (best_parent)
|
||
{
|
||
req->best_parent_hw = best_parent->hw;
|
||
}
|
||
req->best_parent_rate = best;
|
||
req->rate = best;
|
||
|
||
return 0;
|
||
}
|
||
|
||
/*
|
||
* __clk_mux_determine_rate - clk_ops::determine_rate implementation for a mux type clk
|
||
* @hw: mux type clk to determine rate on
|
||
* @req: rate request, also used to return preferred parent and frequencies
|
||
*
|
||
* Helper for finding best parent to provide a given frequency. This can be used
|
||
* directly as a determine_rate callback (e.g. for a mux), or from a more
|
||
* complex clock that may combine a mux with other operations.
|
||
*
|
||
* Returns: 0 on success, -EERROR value on error
|
||
*/
|
||
int __clk_mux_determine_rate(struct clk_hw *hw,
|
||
struct clk_rate_request *req)
|
||
{
|
||
return clk_mux_determine_rate_flags(hw, req, 0);
|
||
}
|
||
|
||
static void clk_core_get_boundaries(struct clk_core *core,
|
||
unsigned long *min_rate,
|
||
unsigned long *max_rate)
|
||
{
|
||
*min_rate = core->min_rate;
|
||
*max_rate = core->max_rate;
|
||
}
|
||
|
||
unsigned long clk_hw_round_rate(struct clk_hw *hw, unsigned long rate)
|
||
{
|
||
int ret;
|
||
struct clk_rate_request req;
|
||
|
||
if (!hw)
|
||
{
|
||
return 0;
|
||
}
|
||
|
||
clk_core_get_boundaries(hw->core, &req.min_rate, &req.max_rate);
|
||
req.rate = rate;
|
||
|
||
ret = clk_core_round_rate_nolock(hw->core, &req);
|
||
if (ret)
|
||
{
|
||
return 0;
|
||
}
|
||
|
||
return req.rate;
|
||
}
|
||
|
||
static struct clk_core *__clk_init_parent(struct clk_core *core)
|
||
{
|
||
u8 index = 0;
|
||
|
||
if (!core->num_parents)
|
||
{
|
||
return NULL;
|
||
}
|
||
|
||
if (core->num_parents > 1 && core->ops->get_parent)
|
||
{
|
||
index = core->ops->get_parent(core->hw);
|
||
}
|
||
|
||
return clk_core_get_parent_by_index(core, index);
|
||
}
|
||
|
||
static int __clk_core_init(struct clk_core *core)
|
||
{
|
||
u32 rate;
|
||
int ret = -1;
|
||
|
||
if (!core)
|
||
{
|
||
return ret;
|
||
}
|
||
|
||
if (core->ops->init)
|
||
{
|
||
core->ops->init(core->hw);
|
||
}
|
||
|
||
core->parent = __clk_init_parent(core);
|
||
|
||
if (core->ops->recalc_accuracy)
|
||
{
|
||
core->accuracy = core->ops->recalc_accuracy(core->hw,
|
||
__clk_get_accuracy(core->parent));
|
||
}
|
||
else if (core->parent)
|
||
{
|
||
core->accuracy = core->parent->accuracy;
|
||
}
|
||
else
|
||
{
|
||
core->accuracy = 0;
|
||
}
|
||
|
||
if (core->ops->recalc_rate)
|
||
{
|
||
rate = core->ops->recalc_rate(core->hw, clk_core_get_rate(core->parent));
|
||
}
|
||
else if (core->parent)
|
||
{
|
||
rate = core->parent->rate;
|
||
}
|
||
else
|
||
{
|
||
rate = 0;
|
||
}
|
||
|
||
if (core->parent)
|
||
{
|
||
core->p_rate = core->parent->rate;
|
||
}
|
||
else
|
||
{
|
||
core->p_rate = 0;
|
||
}
|
||
|
||
core->rate = rate;
|
||
|
||
if (core->flags & CLK_IS_CRITICAL)
|
||
{
|
||
ret = clk_core_enable(core);
|
||
if (ret)
|
||
{
|
||
return ret;
|
||
}
|
||
}
|
||
|
||
return 0;
|
||
}
|
||
|
||
static int clk_core_populate_parent_map(struct clk_core *core,
|
||
const struct clk_init_data *init)
|
||
{
|
||
u8 num_parents = init->num_parents;
|
||
const char *const *parent_names = init->parent_names;
|
||
const struct clk_hw **parent_hws = init->parent_hws;
|
||
const struct clk_parent_data *parent_data = init->parent_data;
|
||
int i, ret = 0;
|
||
struct clk_parent_map *parents, *parent;
|
||
|
||
if (!num_parents)
|
||
{
|
||
return 0;
|
||
}
|
||
|
||
/*
|
||
* Avoid unnecessary string look-ups of clk_core's possible parents by
|
||
* having a cache of names/clk_hw pointers to clk_core pointers.
|
||
*/
|
||
parents = (struct clk_parent_map *)malloc(num_parents * sizeof(*parents));
|
||
core->parents = parents;
|
||
if (!parents)
|
||
{
|
||
return -1;
|
||
}
|
||
|
||
/* Copy everything over because it might be __initdata */
|
||
for (i = 0, parent = parents; i < num_parents; i++, parent++)
|
||
{
|
||
parent->index = -1;
|
||
parent->core = NULL;
|
||
parent->hw = NULL;
|
||
parent->name = NULL;
|
||
parent->fw_name = NULL;
|
||
if (parent_names)
|
||
{
|
||
if (!parent_names[i])
|
||
{
|
||
hal_log_err("invalid NULL in %s's .parent_names\n", core->name);
|
||
}
|
||
else
|
||
{
|
||
parent->name = parent_names[i];
|
||
}
|
||
}
|
||
else if (parent_data)
|
||
{
|
||
parent->hw = parent_data[i].hw;
|
||
parent->index = parent_data[i].index;
|
||
if (parent_data[i].name)
|
||
{
|
||
parent->name = parent_data[i].name;
|
||
}
|
||
else
|
||
{
|
||
parent->name = parent_data[i].fw_name;
|
||
}
|
||
|
||
}
|
||
else if (parent_hws)
|
||
{
|
||
parent->hw = parent_hws[i];
|
||
}
|
||
else
|
||
{
|
||
ret = -1;
|
||
hal_log_err("Must specify parents if num_parents > 0\n");
|
||
}
|
||
|
||
if (ret)
|
||
{
|
||
core->parents = NULL;
|
||
free(parents);
|
||
return ret;
|
||
}
|
||
}
|
||
|
||
return 0;
|
||
}
|
||
|
||
#if 0
|
||
static void clk_core_free_parent_map(struct clk_core *core)
|
||
{
|
||
if (!core->num_parents)
|
||
{
|
||
return;
|
||
}
|
||
|
||
free(core->parents);
|
||
}
|
||
#endif
|
||
|
||
int clk_hw_register(struct clk_hw *hw)
|
||
{
|
||
int ret = -1;
|
||
struct clk_core *core;
|
||
struct clk_init_data *init;
|
||
|
||
if (!hw)
|
||
{
|
||
return 0;
|
||
}
|
||
|
||
init = hw->init;
|
||
|
||
core = (struct clk_core *)malloc(sizeof(*core));
|
||
if (!core)
|
||
{
|
||
hal_log_err("out of memory\n");
|
||
return -1;
|
||
}
|
||
|
||
core->name = init->name;
|
||
core->num_parents = init->num_parents;
|
||
core->flags = init->flags;
|
||
core->hw = hw;
|
||
core->ops = hw->init->ops;
|
||
core->min_rate = 0;
|
||
core->max_rate = ULONG_MAX;
|
||
core->enable_count = 0;
|
||
|
||
core->clk = (struct clk *)malloc(sizeof(struct clk));
|
||
if (!core->clk)
|
||
{
|
||
hal_log_err("out of memory\n");
|
||
goto fail_clk;
|
||
}
|
||
core->clk->core = core;
|
||
core->clk->name = core->name;
|
||
core->clk->count = 0;
|
||
|
||
ret = clk_core_populate_parent_map(core, init);
|
||
if (ret)
|
||
{
|
||
goto fail_parents;
|
||
}
|
||
|
||
ret = __clk_core_init(core);
|
||
|
||
if (ret)
|
||
{
|
||
free(core);
|
||
return -1;
|
||
}
|
||
hw->core = core;
|
||
|
||
list_add_tail(&core->node, &clk_root_list);
|
||
|
||
return ret;
|
||
|
||
fail_parents:
|
||
free(core->clk);
|
||
core->clk = NULL;
|
||
|
||
fail_clk:
|
||
free(core);
|
||
core = NULL;
|
||
|
||
return ret;
|
||
}
|
||
|
||
int clk_hw_unregister(struct clk_hw *hw)
|
||
{
|
||
struct clk_core *core;
|
||
|
||
if (!hw || !hw->core)
|
||
{
|
||
return 0;
|
||
}
|
||
|
||
list_for_each_entry(core, &clk_root_list, node)
|
||
{
|
||
if (core->hw->type != hw->type)
|
||
{
|
||
continue;
|
||
}
|
||
if (core->hw->id != hw->id)
|
||
{
|
||
continue;
|
||
}
|
||
list_del(&core->node);
|
||
free(core->clk);
|
||
free(core);
|
||
return 0;
|
||
}
|
||
|
||
return 0;
|
||
}
|
||
|
||
struct clk_core *clk_core_get(hal_clk_type_t type, hal_clk_id_t id)
|
||
{
|
||
struct clk_core *core = NULL;
|
||
|
||
list_for_each_entry(core, &clk_root_list, node)
|
||
{
|
||
if (core->hw->type != type)
|
||
{
|
||
continue;
|
||
}
|
||
if (core->hw->id != id)
|
||
{
|
||
continue;
|
||
}
|
||
|
||
return core;
|
||
}
|
||
|
||
return NULL;
|
||
}
|
||
|
||
hal_clk_status_t clk_core_is_enabled(struct clk_core *core)
|
||
{
|
||
/*
|
||
* .is_enabled is only mandatory for clocks that gate
|
||
* fall back to software usage counter if .is_enabled is missing
|
||
*/
|
||
if (!core->ops->is_enabled)
|
||
{
|
||
return !!core->enable_count;
|
||
}
|
||
|
||
return core->ops->is_enabled(core->hw);
|
||
}
|
||
|
||
hal_clk_status_t clk_core_enable(struct clk_core *core)
|
||
{
|
||
int ret = 0;
|
||
|
||
if (!core)
|
||
{
|
||
return 0;
|
||
}
|
||
|
||
if (core->enable_count == 0)
|
||
{
|
||
ret = clk_core_enable(core->parent);
|
||
|
||
if (ret)
|
||
{
|
||
return ret;
|
||
}
|
||
|
||
if (core->ops->enable)
|
||
{
|
||
ret = core->ops->enable(core->hw);
|
||
}
|
||
|
||
if (ret)
|
||
{
|
||
clk_core_disable(core->parent);
|
||
return ret;
|
||
}
|
||
}
|
||
|
||
core->enable_count++;
|
||
return 0;
|
||
}
|
||
|
||
hal_clk_status_t clk_core_disable(struct clk_core *core)
|
||
{
|
||
if (!core)
|
||
{
|
||
return 0;
|
||
}
|
||
|
||
if (core->enable_count == 0)
|
||
{
|
||
return 0;
|
||
}
|
||
|
||
if (--core->enable_count > 0)
|
||
{
|
||
return 0;
|
||
}
|
||
|
||
if (core->ops->disable)
|
||
{
|
||
core->ops->disable(core->hw);
|
||
}
|
||
|
||
clk_core_disable(core->parent);
|
||
|
||
return 0;
|
||
}
|
||
|
||
u32 clk_core_round_rate(struct clk_core *core, u32 rate)
|
||
{
|
||
struct clk_rate_request req;
|
||
int ret;
|
||
|
||
if (!core)
|
||
{
|
||
return 0;
|
||
}
|
||
|
||
clk_core_get_boundaries(core, &req.min_rate, &req.max_rate);
|
||
req.rate = rate;
|
||
|
||
ret = clk_core_round_rate_nolock(core, &req);
|
||
|
||
if (ret)
|
||
{
|
||
return ret;
|
||
}
|
||
|
||
return req.rate;
|
||
}
|
||
|
||
u32 clk_core_recalc_rate(struct clk_core *core, struct clk_core *p_core)
|
||
{
|
||
u32 p_rate = 0;
|
||
|
||
if (!core)
|
||
{
|
||
return 0;
|
||
}
|
||
|
||
if (!p_core)
|
||
{
|
||
return core->rate;
|
||
}
|
||
|
||
clk_core_recalc_rate(p_core, p_core->parent);
|
||
|
||
p_rate = p_core->rate;
|
||
if (core->ops->recalc_rate)
|
||
{
|
||
return core->ops->recalc_rate(core->hw, p_rate);
|
||
}
|
||
|
||
return core->rate;
|
||
}
|
||
|
||
u32 clk_core_get_rate(struct clk_core *core)
|
||
{
|
||
if (!core)
|
||
{
|
||
return 0;
|
||
}
|
||
|
||
core->parent = __clk_init_parent(core);
|
||
|
||
return clk_core_recalc_rate(core, core->parent);
|
||
}
|
||
|
||
u32 clk_hw_get_rate(const struct clk_hw *hw)
|
||
{
|
||
return clk_core_get_rate(hw->core);
|
||
}
|
||
|
||
hal_clk_status_t clk_core_set_rate(struct clk_core *core, struct clk_core *p_core, unsigned long rate)
|
||
{
|
||
u8 ret = -1;
|
||
|
||
if (!core || !p_core)
|
||
{
|
||
hal_log_err("core or p_core is NULL\n");
|
||
return ret;
|
||
}
|
||
|
||
if (core->ops->set_rate)
|
||
{
|
||
ret = core->ops->set_rate(core->hw, rate, p_core->rate);
|
||
}
|
||
|
||
if (ret)
|
||
{
|
||
return ret;
|
||
}
|
||
|
||
core->rate = rate;
|
||
return ret;
|
||
}
|
||
|
||
hal_clk_status_t clk_hw_set_rate(struct clk_hw *hw, unsigned long rate)
|
||
{
|
||
struct clk_core *core, *p_core;
|
||
core = clk_hw_get_core(hw);
|
||
p_core = clk_core_get_parent(core);
|
||
|
||
return clk_core_set_rate(core, p_core, rate);
|
||
}
|
||
|
||
|
||
static int __clk_set_parent(struct clk_core *core, struct clk_core *parent,
|
||
u8 p_index)
|
||
{
|
||
int ret = 0;
|
||
|
||
/* change clock input source */
|
||
if (parent && core->ops->set_parent)
|
||
{
|
||
ret = core->ops->set_parent(core->hw, p_index);
|
||
}
|
||
|
||
return ret;
|
||
}
|
||
|
||
static int clk_fetch_parent_index(struct clk_core *core,
|
||
struct clk_core *parent)
|
||
{
|
||
int i;
|
||
|
||
if (!parent)
|
||
{
|
||
return -1;
|
||
}
|
||
|
||
for (i = 0; i < core->num_parents; i++)
|
||
{
|
||
/* Found it first try! */
|
||
if (core->parents[i].core)
|
||
{
|
||
if (core->parents[i].core == parent)
|
||
{
|
||
return i;
|
||
}
|
||
}
|
||
|
||
/* Something else is here, so keep looking */
|
||
if (core->parents[i].core)
|
||
{
|
||
continue;
|
||
}
|
||
|
||
/* Maybe core hasn't been cached but the hw is all we know? */
|
||
if (core->parents[i].hw)
|
||
{
|
||
if (core->parents[i].hw == parent->hw)
|
||
{
|
||
break;
|
||
}
|
||
|
||
/* Didn't match, but we're expecting a clk_hw */
|
||
continue;
|
||
}
|
||
|
||
/* Fallback to comparing globally unique names */
|
||
if (core->parents[i].name &&
|
||
!strcmp(parent->name, core->parents[i].name))
|
||
{
|
||
break;
|
||
}
|
||
}
|
||
|
||
if (i == core->num_parents)
|
||
{
|
||
return -1;
|
||
}
|
||
|
||
core->parents[i].core = parent;
|
||
return i;
|
||
}
|
||
|
||
struct clk_core *clk_core_get_parent(struct clk_core *core)
|
||
{
|
||
if (!core)
|
||
{
|
||
return NULL;
|
||
}
|
||
|
||
if (!core->parent)
|
||
core->parent = __clk_init_parent(core);
|
||
|
||
return core->parent;
|
||
}
|
||
|
||
hal_clk_status_t clk_core_set_parent(struct clk_core *core, struct clk_core *parent)
|
||
{
|
||
int ret = 0;
|
||
int p_index = 0;
|
||
uint32_t p_rate = 0;
|
||
|
||
if (!core)
|
||
{
|
||
return 0;
|
||
}
|
||
|
||
if (core->parent == parent)
|
||
{
|
||
return 0;
|
||
}
|
||
|
||
/* verify ops for for multi-parent clks */
|
||
if ((core->num_parents > 1) && (!core->ops->set_parent))
|
||
{
|
||
ret = -1;
|
||
goto out;
|
||
}
|
||
|
||
/* try finding the new parent index */
|
||
if (parent)
|
||
{
|
||
p_index = clk_fetch_parent_index(core, parent);
|
||
if (p_index < 0)
|
||
{
|
||
printf("%s: clk %s can not be parent of clk %s\n",
|
||
__func__, parent->name, core->name);
|
||
ret = p_index;
|
||
goto out;
|
||
}
|
||
p_rate = parent->rate;
|
||
}
|
||
|
||
/* do the re-parent */
|
||
ret = __clk_set_parent(core, parent, p_index);
|
||
|
||
/* propagate rate an accuracy recalculation accordingly */
|
||
if (!ret)
|
||
{
|
||
core->parent = parent;
|
||
core->p_rate = p_rate;
|
||
core->rate = clk_core_recalc_rate(core, parent);
|
||
}
|
||
|
||
out:
|
||
return ret;
|
||
}
|
||
|