Merge "clk: qcom: clk-debug: Unflatten mux tree"

This commit is contained in:
qctecmdr 2019-09-05 19:19:31 -07:00 • committed by Gerrit - the friendly Code Review server
commit a1f29ec6d7
2 changed files with 199 additions and 172 deletions

View file

@ -10,6 +10,7 @@
#include <linux/clk-provider.h>
#include <linux/of.h>
#include <linux/bitops.h>
#include <linux/mfd/syscon.h>
#include "clk-regmap.h"
#include "clk-debug.h"
@ -82,9 +83,9 @@ static unsigned long clk_debug_mux_measure_rate(struct clk_hw *hw)
spin_lock_irqsave(&clk_reg_lock, flags);
/* Enable CXO/4 and RINGOSC branch. */
regmap_read(meas->regmap[GCC], data->xo_div4_cbcr, &gcc_xo4_reg);
regmap_read(meas->regmap, data->xo_div4_cbcr, &gcc_xo4_reg);
gcc_xo4_reg |= BIT(0);
regmap_write(meas->regmap[GCC], data->xo_div4_cbcr, gcc_xo4_reg);
regmap_write(meas->regmap, data->xo_div4_cbcr, gcc_xo4_reg);
/*
* The ring oscillator counter will not reset if the measured clock
@ -94,15 +95,15 @@ static unsigned long clk_debug_mux_measure_rate(struct clk_hw *hw)
*/
/* Run a short measurement. (~1 ms) */
raw_count_short = run_measurement(SAMPLE_TICKS_1_MS, meas->regmap[GCC],
raw_count_short = run_measurement(SAMPLE_TICKS_1_MS, meas->regmap,
data->ctl_reg, data->status_reg);
/* Run a full measurement. (~14 ms) */
raw_count_full = run_measurement(SAMPLE_TICKS_14_MS, meas->regmap[GCC],
raw_count_full = run_measurement(SAMPLE_TICKS_14_MS, meas->regmap,
data->ctl_reg, data->status_reg);
gcc_xo4_reg &= ~BIT(0);
regmap_write(meas->regmap[GCC], data->xo_div4_cbcr, gcc_xo4_reg);
regmap_write(meas->regmap, data->xo_div4_cbcr, gcc_xo4_reg);
/* Return 0 if the clock is off. */
if (raw_count_full == raw_count_short)
@ -121,73 +122,63 @@ static unsigned long clk_debug_mux_measure_rate(struct clk_hw *hw)
return ret;
}
static int clk_find_and_set_parent(struct clk_hw *mux, struct clk_hw *clk)
{
int i;
if (!clk || !mux || !(mux->init->flags & CLK_IS_MEASURE))
return -EINVAL;
if (!clk_set_parent(mux->clk, clk->clk))
return 0;
for (i = 0; i < clk_hw_get_num_parents(mux); i++) {
struct clk_hw *parent = clk_hw_get_parent_by_index(mux, i);
if (!clk_find_and_set_parent(parent, clk))
return clk_set_parent(mux->clk, parent->clk);
}
return -EINVAL;
}
static u8 clk_debug_mux_get_parent(struct clk_hw *hw)
{
struct clk_debug_mux *meas = to_clk_measure(hw);
int i, num_parents = clk_hw_get_num_parents(hw);
struct clk_hw *hw_clk = clk_hw_get_parent(hw);
if (!hw_clk)
return 0;
for (i = 0; i < num_parents; i++) {
if (!strcmp(meas->parent[i].parents,
if (!strcmp(hw->init->parent_names[i],
clk_hw_get_name(hw_clk))) {
pr_debug("%s: clock parent - %s, index %d\n", __func__,
meas->parent[i].parents, i);
hw->init->parent_names[i], i);
return i;
}
}
return 0;
}
static int clk_debug_mux_set_parent(struct clk_hw *hw, u8 index)
{
struct clk_debug_mux *meas = to_clk_measure(hw);
u32 regval = 0;
int dbg_cc = 0;
struct clk_debug_mux *mux = to_clk_measure(hw);
int ret;
dbg_cc = meas->parent[index].dbg_cc;
if (!mux->mux_sels)
return 0;
if (dbg_cc != GCC) {
/* Update the recursive debug mux */
regmap_read(meas->regmap[dbg_cc],
meas->parent[index].mux_offset, &regval);
regval &= ~(meas->parent[index].mux_sel_mask <<
meas->parent[index].mux_sel_shift);
regval |= (meas->parent[index].dbg_cc_mux_sel &
meas->parent[index].mux_sel_mask) <<
meas->parent[index].mux_sel_shift;
regmap_write(meas->regmap[dbg_cc],
meas->parent[index].mux_offset, regval);
/* Update the debug sel for mux */
ret = regmap_update_bits(mux->regmap, mux->debug_offset,
mux->src_sel_mask,
mux->mux_sels[index] << mux->src_sel_shift);
if (ret)
return ret;
regmap_read(meas->regmap[dbg_cc],
meas->parent[index].post_div_offset, &regval);
regval &= ~(meas->parent[index].post_div_mask <<
meas->parent[index].post_div_shift);
regval |= ((meas->parent[index].post_div_val - 1) &
meas->parent[index].post_div_mask) <<
meas->parent[index].post_div_shift;
regmap_write(meas->regmap[dbg_cc],
meas->parent[index].post_div_offset, regval);
}
/* Update the debug sel for GCC */
regmap_read(meas->regmap[GCC], meas->debug_offset, &regval);
regval &= ~(meas->src_sel_mask << meas->src_sel_shift);
regval |= (meas->parent[index].prim_mux_sel & meas->src_sel_mask) <<
meas->src_sel_shift;
regmap_write(meas->regmap[GCC], meas->debug_offset, regval);
/* Set the GCC mux's post divider bits */
regmap_read(meas->regmap[GCC], meas->post_div_offset, &regval);
regval &= ~(meas->post_div_mask << meas->post_div_shift);
regval |= ((meas->parent[index].prim_mux_div_val - 1) &
meas->post_div_mask) << meas->post_div_shift;
regmap_write(meas->regmap[GCC], meas->post_div_offset, regval);
return 0;
/* Set the mux's post divider bits */
return regmap_update_bits(mux->regmap, mux->post_div_offset,
mux->post_div_mask,
(mux->post_div_val - 1) << mux->post_div_shift);
}
const struct clk_ops clk_debug_mux_ops = {
@ -196,90 +187,83 @@ const struct clk_ops clk_debug_mux_ops = {
};
EXPORT_SYMBOL(clk_debug_mux_ops);
static void enable_debug_clks(struct clk_debug_mux *meas, u8 index)
static void enable_debug_clks(struct clk_hw *mux)
{
int dbg_cc = meas->parent[index].dbg_cc;
struct clk_debug_mux *meas = to_clk_measure(mux);
struct clk_hw *parent;
if (!mux || !(mux->init->flags & CLK_IS_MEASURE))
return;
parent = clk_hw_get_parent(mux);
enable_debug_clks(parent);
meas->en_mask = meas->en_mask ? meas->en_mask : CBCR_ENA;
if (dbg_cc != GCC) {
/* Not all recursive muxes have a DEBUG clock. */
if (meas->parent[index].cbcr_offset != U32_MAX)
regmap_update_bits(meas->regmap[dbg_cc],
meas->parent[index].cbcr_offset,
meas->en_mask, meas->en_mask);
}
/* Turn on the GCC_DEBUG_CBCR */
regmap_update_bits(meas->regmap[GCC], meas->cbcr_offset,
meas->en_mask, meas->en_mask);
/* Not all muxes have a DEBUG clock. */
if (meas->cbcr_offset != U32_MAX)
regmap_update_bits(meas->regmap, meas->cbcr_offset,
meas->en_mask, meas->en_mask);
}
static void disable_debug_clks(struct clk_debug_mux *meas, u8 index)
static void disable_debug_clks(struct clk_hw *mux)
{
int dbg_cc = meas->parent[index].dbg_cc;
struct clk_debug_mux *meas = to_clk_measure(mux);
struct clk_hw *parent;
if (!mux || !(mux->init->flags & CLK_IS_MEASURE))
return;
meas->en_mask = meas->en_mask ? meas->en_mask : CBCR_ENA;
/* Turn off the GCC_DEBUG_CBCR */
regmap_update_bits(meas->regmap[GCC], meas->cbcr_offset,
if (meas->cbcr_offset != U32_MAX)
regmap_update_bits(meas->regmap, meas->cbcr_offset,
meas->en_mask, 0);
if (dbg_cc != GCC) {
if (meas->parent[index].cbcr_offset != U32_MAX)
regmap_update_bits(meas->regmap[dbg_cc],
meas->parent[index].cbcr_offset,
meas->en_mask, 0);
parent = clk_hw_get_parent(mux);
disable_debug_clks(parent);
}
static u32 get_mux_divs(struct clk_hw *mux)
{
struct clk_debug_mux *meas = to_clk_measure(mux);
struct clk_hw *parent;
u32 div_val;
if (!mux || !(mux->init->flags & CLK_IS_MEASURE))
return 1;
WARN_ON(!meas->post_div_val);
div_val = meas->post_div_val;
if (meas->pre_div_vals) {
int i = clk_debug_mux_get_parent(mux);
div_val *= meas->pre_div_vals[i];
}
parent = clk_hw_get_parent(mux);
return div_val * get_mux_divs(parent);
}
static int clk_debug_measure_get(void *data, u64 *val)
{
struct clk_hw *hw = data, *par;
struct clk_debug_mux *meas = to_clk_measure(measure);
int index;
struct clk_hw *hw = data;
int ret = 0;
unsigned long meas_rate, sw_rate;
mutex_lock(&clk_debug_lock);
ret = clk_set_parent(measure->clk, hw->clk);
if (!ret) {
par = measure;
index = clk_debug_mux_get_parent(measure);
enable_debug_clks(meas, index);
while (par && par != hw) {
if (par->init->ops->enable)
par->init->ops->enable(par);
par = clk_hw_get_parent(par);
}
*val = clk_debug_mux_measure_rate(measure);
if (meas->parent[index].dbg_cc != GCC)
*val *= meas->parent[index].post_div_val;
*val *= meas->parent[index].prim_mux_div_val;
/* Accommodate for any pre-set dividers */
if (meas->parent[index].misc_div_val)
*val *= meas->parent[index].misc_div_val;
} else {
ret = clk_find_and_set_parent(measure, hw);
if (ret) {
pr_err("Failed to set the debug mux's parent.\n");
goto exit;
}
meas_rate = clk_get_rate(hw->clk);
par = clk_hw_get_parent(measure);
if (!par) {
ret = -EINVAL;
goto exit1;
}
enable_debug_clks(measure);
*val = clk_debug_mux_measure_rate(measure);
sw_rate = clk_get_rate(par->clk);
if (sw_rate && meas_rate >= (sw_rate * 2))
*val *= DIV_ROUND_CLOSEST(meas_rate, sw_rate);
exit1:
disable_debug_clks(meas, index);
/* recursively calculate actual freq */
*val *= get_mux_divs(measure);
disable_debug_clks(measure);
exit:
mutex_unlock(&clk_debug_lock);
return ret;
@ -288,23 +272,74 @@ exit:
DEFINE_DEBUGFS_ATTRIBUTE(clk_measure_fops, clk_debug_measure_get,
NULL, "%lld\n");
static int clk_debug_read_period(void *data, u64 *val)
{
struct clk_hw *hw = data;
struct clk_hw *parent;
struct clk_debug_mux *mux;
int ret = 0;
u32 regval;
mutex_lock(&clk_debug_lock);
ret = clk_find_and_set_parent(measure, hw);
if (!ret) {
parent = clk_hw_get_parent(measure);
if (!parent) {
mutex_unlock(&clk_debug_lock);
return -EINVAL;
}
mux = to_clk_measure(parent);
regmap_read(mux->regmap, mux->period_offset, &regval);
if (!regval) {
pr_err("Error reading mccc period register, ret = %d\n",
ret);
mutex_unlock(&clk_debug_lock);
return 0;
}
*val = 1000000000000UL;
do_div(*val, regval);
} else {
pr_err("Failed to set the debug mux's parent.\n");
}
mutex_unlock(&clk_debug_lock);
return ret;
}
DEFINE_SIMPLE_ATTRIBUTE(clk_read_period_fops, clk_debug_read_period,
NULL, "%lld\n");
void clk_debug_measure_add(struct clk_hw *hw, struct dentry *dentry)
{
int ret;
struct clk_hw *parent;
struct clk_debug_mux *meas;
struct clk_debug_mux *meas_parent;
if (IS_ERR_OR_NULL(measure)) {
pr_err_once("Please check if `measure` clk is registered.\n");
return;
}
ret = clk_set_parent(measure->clk, hw->clk);
meas = to_clk_measure(measure);
ret = clk_find_and_set_parent(measure, hw);
if (ret) {
pr_debug("Unable to set %s as %s's parent, ret=%d\n",
clk_hw_get_name(hw), clk_hw_get_name(measure), ret);
return;
}
debugfs_create_file("clk_measure", 0444, dentry, hw,
parent = clk_hw_get_parent(measure);
if (!parent)
return;
meas_parent = to_clk_measure(parent);
if (parent->init->flags & CLK_IS_MEASURE && !meas_parent->mux_sels)
debugfs_create_file("clk_measure", 0444, dentry, hw,
&clk_read_period_fops);
else
debugfs_create_file("clk_measure", 0444, dentry, hw,
&clk_measure_fops);
}
EXPORT_SYMBOL(clk_debug_measure_add);
@ -323,3 +358,31 @@ int clk_debug_measure_register(struct clk_hw *hw)
}
EXPORT_SYMBOL(clk_debug_measure_register);
/**
* map_debug_bases - maps each debug mux based on phandle
* @pdev: the platform device used to find phandles
* @base: regmap base name used to look up phandle
* @mux: debug mux that requires a regmap
*
* This function attempts to look up and map a regmap for a debug mux
* using syscon_regmap_lookup_by_phandle if the base name property exists
* and assigns an appropriate regmap.
*
* Returns 0 on success, -EBADR when it can't find base name, -EERROR otherwise.
*/
int map_debug_bases(struct platform_device *pdev, const char *base,
struct clk_debug_mux *mux)
{
if (!of_get_property(pdev->dev.of_node, base, NULL))
return -EBADR;
mux->regmap = syscon_regmap_lookup_by_phandle(pdev->dev.of_node,
base);
if (IS_ERR(mux->regmap)) {
pr_err("Failed to map %s (ret=%ld)\n", base,
PTR_ERR(mux->regmap));
return PTR_ERR(mux->regmap);
}
return 0;
}
EXPORT_SYMBOL(map_debug_bases);

View file

@ -4,8 +4,20 @@
#ifndef __QCOM_CLK_DEBUG_H__
#define __QCOM_CLK_DEBUG_H__
#include <linux/platform_device.h>
#include "../clk.h"
/**
* struct mux_regmap_names - Structure of mux regmap mapping
* @mux: pointer to a clock debug mux
* @regmap_name: corresponding regmap name used to match a debug mux to
its regmap
*/
struct mux_regmap_names {
struct clk_debug_mux *mux;
const char *regmap_name;
};
/* Debugfs Measure Clocks */
/**
@ -24,67 +36,12 @@ struct measure_clk_data {
u32 xo_div4_cbcr;
};
/**
* List of Debug clock controllers.
*/
enum debug_cc {
GCC,
CAM_CC,
DISP_CC,
NPU_CC,
GPU_CC,
VIDEO_CC,
CPU_CC,
MAX_NUM_CC,
};
/**
* struct clk_src - Structure of clock source for debug mux
*
* @parents: clock name to be used as parent for debug mux.
* @prim_mux_sel: debug mux index at global clock controller.
* @prim_mux_div_val: PLL post-divider setting for the primary mux.
* @dbg_cc: indicates the clock controller for recursive debug
* clock controllers.
* @dbg_cc_mux_sel: indicates the debug mux index at recursive debug mux.
* @mux_sel_mask: indicates the mask for the mux selection.
* @mux_sel_shift: indicates the shift required for mux selection.
* @post_div_mask: indicates the post div mask to be used at recursive
* debug mux.
* @post_div_shift: indicates the shift required for post divider
* configuration.
* @post_div_val: indicates the post div value to be used at recursive
* debug mux.
* @mux_offset: the debug mux offset.
* @post_div_offset: register with post-divider settings for the debug mux.
* @cbcr_offset: branch register to turn on debug mux.
* @misc_div_val: includes any pre-set dividers in the measurement logic.
*/
struct clk_src {
const char *parents;
int prim_mux_sel;
u32 prim_mux_div_val;
enum debug_cc dbg_cc;
int dbg_cc_mux_sel;
u32 mux_sel_mask;
u32 mux_sel_shift;
u32 post_div_mask;
u32 post_div_shift;
u32 post_div_val;
u32 mux_offset;
u32 post_div_offset;
u32 cbcr_offset;
u32 misc_div_val;
};
#define MUX_SRC_LIST(...) \
.parent = (struct clk_src[]){__VA_ARGS__}, \
.num_parents = ARRAY_SIZE(((struct clk_src[]){__VA_ARGS__}))
/**
* struct clk_debug_mux - Structure of clock debug mux
*
* @parent: structure of clk_src
* @mux_sels: indicates the debug mux index at recursive debug mux.
* @pre_div_val: optional divider values for clocks that were pre-divided
before feeding into the debug muxes
* @num_parents: number of parents
* @regmap: regmaps of debug mux
* @priv: private measure_clk_data to be used by debug mux
@ -101,21 +58,26 @@ struct clk_src {
mux.
* @post_div_shift: indicates the shift required for post divider
selection in primary mux.
* @period_offset: offset of the period register used to read to determine
the mc clock period
* @hw: handle between common and hardware-specific interfaces.
*/
struct clk_debug_mux {
struct clk_src *parent;
int *mux_sels;
int *pre_div_vals;
int num_parents;
struct regmap **regmap;
struct regmap *regmap;
void *priv;
u32 en_mask;
u32 debug_offset;
u32 post_div_offset;
u32 cbcr_offset;
u32 src_sel_mask;
u32 src_sel_shift;
u32 post_div_offset;
u32 post_div_mask;
u32 post_div_shift;
u32 post_div_val;
u32 period_offset;
struct clk_hw hw;
};
@ -125,5 +87,7 @@ extern const struct clk_ops clk_debug_mux_ops;
int clk_debug_measure_register(struct clk_hw *hw);
void clk_debug_measure_add(struct clk_hw *hw, struct dentry *dentry);
int map_debug_bases(struct platform_device *pdev, const char *base,
struct clk_debug_mux *mux);
#endif