From 4defae99442a4a0491b15880195dafb2305cbcb7 Mon Sep 17 00:00:00 2001 From: David Dai Date: Fri, 13 Jul 2018 18:20:02 -0700 Subject: [PATCH 1/2] clk: qcom: clk-debug: Add custom measure operators for mccc_clk Since the generic way of using testclock to measure ddr mc clock is not available, rely on reading the period of the current mc clock frequency written to by DDRSS software. This does not provide the physical rate at which the clock is running, but a snapshot of the current configuration. Change-Id: I37ce2927be7712adc2b722ed0a26c8eaf4596dd2 Signed-off-by: David Dai --- drivers/clk/qcom/clk-debug.c | 46 +++++++++++++++++++++++++++++++++++- drivers/clk/qcom/clk-debug.h | 4 ++++ 2 files changed, 49 insertions(+), 1 deletion(-) diff --git a/drivers/clk/qcom/clk-debug.c b/drivers/clk/qcom/clk-debug.c index 2bcad2d0173a..c9ce3dd2e793 100644 --- a/drivers/clk/qcom/clk-debug.c +++ b/drivers/clk/qcom/clk-debug.c @@ -288,15 +288,54 @@ 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_debug_mux *meas = to_clk_measure(measure); + int index; + int dbg_cc; + int ret = 0; + u32 regval; + + mutex_lock(&clk_debug_lock); + + ret = clk_set_parent(measure->clk, hw->clk); + if (!ret) { + index = clk_debug_mux_get_parent(measure); + dbg_cc = meas->parent[index].dbg_cc; + + regmap_read(meas->regmap[dbg_cc], meas->period_offset, ®val); + 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; + int index; + struct clk_debug_mux *meas; if (IS_ERR_OR_NULL(measure)) { pr_err_once("Please check if `measure` clk is registered.\n"); return; } + meas = to_clk_measure(measure); ret = clk_set_parent(measure->clk, hw->clk); if (ret) { pr_debug("Unable to set %s as %s's parent, ret=%d\n", @@ -304,7 +343,12 @@ void clk_debug_measure_add(struct clk_hw *hw, struct dentry *dentry) return; } - debugfs_create_file("clk_measure", 0444, dentry, hw, + index = clk_debug_mux_get_parent(measure); + if (meas->parent[index].dbg_cc == MC_CC) + 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); diff --git a/drivers/clk/qcom/clk-debug.h b/drivers/clk/qcom/clk-debug.h index 6e23487751f3..3c45de8574f4 100644 --- a/drivers/clk/qcom/clk-debug.h +++ b/drivers/clk/qcom/clk-debug.h @@ -35,6 +35,7 @@ enum debug_cc { GPU_CC, VIDEO_CC, CPU_CC, + MC_CC, MAX_NUM_CC, }; @@ -101,6 +102,8 @@ 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 { @@ -116,6 +119,7 @@ struct clk_debug_mux { u32 src_sel_shift; u32 post_div_mask; u32 post_div_shift; + u32 period_offset; struct clk_hw hw; }; From 98d35a0028458b975b9206ea848b7b0e325177b4 Mon Sep 17 00:00:00 2001 From: David Dai Date: Sat, 20 Apr 2019 10:59:25 -0700 Subject: [PATCH 2/2] clk: qcom: clk-debug: Unflatten mux tree In order to circumvent the 256 parent limit allowed per clock and more accurately represent the mux topology, separate out each clock to their respective debug muxes and update the supporting functions that configure these muxes for measurement. Change-Id: I63e3a4565a61997757c375d2ebb1d01e82a37814 Signed-off-by: David Dai Signed-off-by: David Collins --- drivers/clk/qcom/clk-debug.c | 261 +++++++++++++++++++---------------- drivers/clk/qcom/clk-debug.h | 84 +++-------- 2 files changed, 162 insertions(+), 183 deletions(-) diff --git a/drivers/clk/qcom/clk-debug.c b/drivers/clk/qcom/clk-debug.c index c9ce3dd2e793..686ca87a9f3e 100644 --- a/drivers/clk/qcom/clk-debug.c +++ b/drivers/clk/qcom/clk-debug.c @@ -10,6 +10,7 @@ #include #include #include +#include #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, ®val); - 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, ®val); - 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, ®val); - 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, ®val); - 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; @@ -291,20 +275,22 @@ DEFINE_DEBUGFS_ATTRIBUTE(clk_measure_fops, clk_debug_measure_get, static int clk_debug_read_period(void *data, u64 *val) { struct clk_hw *hw = data; - struct clk_debug_mux *meas = to_clk_measure(measure); - int index; - int dbg_cc; + struct clk_hw *parent; + struct clk_debug_mux *mux; int ret = 0; u32 regval; mutex_lock(&clk_debug_lock); - ret = clk_set_parent(measure->clk, hw->clk); + ret = clk_find_and_set_parent(measure, hw); if (!ret) { - index = clk_debug_mux_get_parent(measure); - dbg_cc = meas->parent[index].dbg_cc; - - regmap_read(meas->regmap[dbg_cc], meas->period_offset, ®val); + 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, ®val); if (!regval) { pr_err("Error reading mccc period register, ret = %d\n", ret); @@ -327,8 +313,9 @@ DEFINE_SIMPLE_ATTRIBUTE(clk_read_period_fops, clk_debug_read_period, void clk_debug_measure_add(struct clk_hw *hw, struct dentry *dentry) { int ret; - int index; + 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"); @@ -336,15 +323,19 @@ void clk_debug_measure_add(struct clk_hw *hw, struct dentry *dentry) } meas = to_clk_measure(measure); - ret = clk_set_parent(measure->clk, hw->clk); + 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; } - index = clk_debug_mux_get_parent(measure); - if (meas->parent[index].dbg_cc == MC_CC) + 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 @@ -367,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); diff --git a/drivers/clk/qcom/clk-debug.h b/drivers/clk/qcom/clk-debug.h index 3c45de8574f4..84fb4aeec41c 100644 --- a/drivers/clk/qcom/clk-debug.h +++ b/drivers/clk/qcom/clk-debug.h @@ -4,8 +4,20 @@ #ifndef __QCOM_CLK_DEBUG_H__ #define __QCOM_CLK_DEBUG_H__ +#include #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,68 +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, - MC_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 @@ -107,18 +63,20 @@ struct clk_src { * @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; }; @@ -129,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