Loading drivers/clk/qcom/clk-debug.c +96 −18 Original line number Diff line number Diff line Loading @@ -25,6 +25,7 @@ static DEFINE_MUTEX(clk_debug_lock); #define XO_DIV4_CNT_DONE BIT(25) #define CNT_EN BIT(20) #define MEASURE_CNT GENMASK(24, 0) #define CBCR_ENA BIT(0) /* Sample clock for 'ticks' reference clock ticks. */ static u32 run_measurement(unsigned int ticks, struct regmap *regmap, Loading Loading @@ -169,15 +170,6 @@ static int clk_debug_mux_set_parent(struct clk_hw *hw, u8 index) meas->parent[index].post_div_shift; regmap_write(meas->regmap[dbg_cc], meas->parent[index].post_div_offset, regval); /* Not all recursive muxes have a DEBUG clock. */ if (meas->parent[index].cbcr_offset != U32_MAX) { regmap_read(meas->regmap[dbg_cc], meas->parent[index].cbcr_offset, ®val); regval |= BIT(0); regmap_write(meas->regmap[dbg_cc], meas->parent[index].cbcr_offset, regval); } } /* Update the debug sel for GCC */ Loading @@ -194,11 +186,6 @@ static int clk_debug_mux_set_parent(struct clk_hw *hw, u8 index) meas->post_div_mask) << meas->post_div_shift; regmap_write(meas->regmap[GCC], meas->post_div_offset, regval); /* Turn on the GCC_DEBUG_CBCR */ regmap_read(meas->regmap[GCC], meas->cbcr_offset, ®val); regval |= BIT(0); regmap_write(meas->regmap[GCC], meas->cbcr_offset, regval); return 0; } Loading @@ -208,6 +195,44 @@ 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) { int dbg_cc = meas->parent[index].dbg_cc; 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); } static void disable_debug_clks(struct clk_debug_mux *meas, u8 index) { int dbg_cc = meas->parent[index].dbg_cc; 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, 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); } } static int clk_debug_measure_get(void *data, u64 *val) { struct clk_hw *hw = data, *par; Loading @@ -222,6 +247,8 @@ static int clk_debug_measure_get(void *data, u64 *val) 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); Loading @@ -235,33 +262,79 @@ static int clk_debug_measure_get(void *data, u64 *val) /* Accommodate for any pre-set dividers */ if (meas->parent[index].misc_div_val) *val *= meas->parent[index].misc_div_val; } else { 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) return -EINVAL; if (!par) { ret = -EINVAL; goto exit1; } 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); exit: mutex_unlock(&clk_debug_lock); return ret; } 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", Loading @@ -269,6 +342,11 @@ void clk_debug_measure_add(struct clk_hw *hw, struct dentry *dentry) return; } 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); } Loading drivers/clk/qcom/clk-debug.h +8 −1 Original line number Diff line number Diff line /* SPDX-License-Identifier: GPL-2.0 */ /* Copyright (c) 2016, The Linux Foundation. All rights reserved. */ /* Copyright (c) 2016, 2019, The Linux Foundation. All rights reserved. */ #ifndef __QCOM_CLK_DEBUG_H__ #define __QCOM_CLK_DEBUG_H__ Loading Loading @@ -35,6 +35,7 @@ enum debug_cc { GPU_CC, VIDEO_CC, CPU_CC, MC_CC, MAX_NUM_CC, }; Loading Loading @@ -88,6 +89,8 @@ struct clk_src { * @num_parents: number of parents * @regmap: regmaps of debug mux * @priv: private measure_clk_data to be used by debug mux * @en_mask: indicates the enable bit mask at global clock * controller debug mux. * @debug_offset: debug mux offset. * @post_div_offset: register with post-divider settings for the debug mux. * @cbcr_offset: branch register to turn on debug mux. Loading @@ -99,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 { Loading @@ -106,6 +111,7 @@ struct clk_debug_mux { int num_parents; struct regmap **regmap; void *priv; u32 en_mask; u32 debug_offset; u32 post_div_offset; u32 cbcr_offset; Loading @@ -113,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; }; Loading Loading
drivers/clk/qcom/clk-debug.c +96 −18 Original line number Diff line number Diff line Loading @@ -25,6 +25,7 @@ static DEFINE_MUTEX(clk_debug_lock); #define XO_DIV4_CNT_DONE BIT(25) #define CNT_EN BIT(20) #define MEASURE_CNT GENMASK(24, 0) #define CBCR_ENA BIT(0) /* Sample clock for 'ticks' reference clock ticks. */ static u32 run_measurement(unsigned int ticks, struct regmap *regmap, Loading Loading @@ -169,15 +170,6 @@ static int clk_debug_mux_set_parent(struct clk_hw *hw, u8 index) meas->parent[index].post_div_shift; regmap_write(meas->regmap[dbg_cc], meas->parent[index].post_div_offset, regval); /* Not all recursive muxes have a DEBUG clock. */ if (meas->parent[index].cbcr_offset != U32_MAX) { regmap_read(meas->regmap[dbg_cc], meas->parent[index].cbcr_offset, ®val); regval |= BIT(0); regmap_write(meas->regmap[dbg_cc], meas->parent[index].cbcr_offset, regval); } } /* Update the debug sel for GCC */ Loading @@ -194,11 +186,6 @@ static int clk_debug_mux_set_parent(struct clk_hw *hw, u8 index) meas->post_div_mask) << meas->post_div_shift; regmap_write(meas->regmap[GCC], meas->post_div_offset, regval); /* Turn on the GCC_DEBUG_CBCR */ regmap_read(meas->regmap[GCC], meas->cbcr_offset, ®val); regval |= BIT(0); regmap_write(meas->regmap[GCC], meas->cbcr_offset, regval); return 0; } Loading @@ -208,6 +195,44 @@ 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) { int dbg_cc = meas->parent[index].dbg_cc; 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); } static void disable_debug_clks(struct clk_debug_mux *meas, u8 index) { int dbg_cc = meas->parent[index].dbg_cc; 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, 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); } } static int clk_debug_measure_get(void *data, u64 *val) { struct clk_hw *hw = data, *par; Loading @@ -222,6 +247,8 @@ static int clk_debug_measure_get(void *data, u64 *val) 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); Loading @@ -235,33 +262,79 @@ static int clk_debug_measure_get(void *data, u64 *val) /* Accommodate for any pre-set dividers */ if (meas->parent[index].misc_div_val) *val *= meas->parent[index].misc_div_val; } else { 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) return -EINVAL; if (!par) { ret = -EINVAL; goto exit1; } 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); exit: mutex_unlock(&clk_debug_lock); return ret; } 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", Loading @@ -269,6 +342,11 @@ void clk_debug_measure_add(struct clk_hw *hw, struct dentry *dentry) return; } 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); } Loading
drivers/clk/qcom/clk-debug.h +8 −1 Original line number Diff line number Diff line /* SPDX-License-Identifier: GPL-2.0 */ /* Copyright (c) 2016, The Linux Foundation. All rights reserved. */ /* Copyright (c) 2016, 2019, The Linux Foundation. All rights reserved. */ #ifndef __QCOM_CLK_DEBUG_H__ #define __QCOM_CLK_DEBUG_H__ Loading Loading @@ -35,6 +35,7 @@ enum debug_cc { GPU_CC, VIDEO_CC, CPU_CC, MC_CC, MAX_NUM_CC, }; Loading Loading @@ -88,6 +89,8 @@ struct clk_src { * @num_parents: number of parents * @regmap: regmaps of debug mux * @priv: private measure_clk_data to be used by debug mux * @en_mask: indicates the enable bit mask at global clock * controller debug mux. * @debug_offset: debug mux offset. * @post_div_offset: register with post-divider settings for the debug mux. * @cbcr_offset: branch register to turn on debug mux. Loading @@ -99,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 { Loading @@ -106,6 +111,7 @@ struct clk_debug_mux { int num_parents; struct regmap **regmap; void *priv; u32 en_mask; u32 debug_offset; u32 post_div_offset; u32 cbcr_offset; Loading @@ -113,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; }; Loading