Loading drivers/clk/qcom/clk-debug.c +174 −111 Original line number Diff line number Diff line Loading @@ -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" Loading Loading @@ -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 Loading @@ -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) Loading @@ -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; dbg_cc = meas->parent[index].dbg_cc; 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); 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); } struct clk_debug_mux *mux = to_clk_measure(hw); int ret; /* 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); if (!mux->mux_sels) return 0; /* 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); /* 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; 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 = { Loading @@ -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, /* 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); } /* 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_hw *mux) { 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; if (meas->cbcr_offset != U32_MAX) regmap_update_bits(meas->regmap, meas->cbcr_offset, meas->en_mask, 0); parent = clk_hw_get_parent(mux); disable_debug_clks(parent); } static void disable_debug_clks(struct clk_debug_mux *meas, u8 index) static u32 get_mux_divs(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; u32 div_val; meas->en_mask = meas->en_mask ? meas->en_mask : CBCR_ENA; if (!mux || !(mux->init->flags & CLK_IS_MEASURE)) return 1; /* Turn off the GCC_DEBUG_CBCR */ regmap_update_bits(meas->regmap[GCC], meas->cbcr_offset, meas->en_mask, 0); WARN_ON(!meas->post_div_val); div_val = meas->post_div_val; 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); 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; Loading @@ -288,22 +272,73 @@ static int clk_debug_measure_get(void *data, u64 *val) 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, ®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; 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; } 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); } Loading @@ -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); drivers/clk/qcom/clk-debug.h +25 −61 Original line number Diff line number Diff line Loading @@ -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 */ /** Loading @@ -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 Loading @@ -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; }; Loading @@ -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 Loading
drivers/clk/qcom/clk-debug.c +174 −111 Original line number Diff line number Diff line Loading @@ -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" Loading Loading @@ -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 Loading @@ -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) Loading @@ -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; dbg_cc = meas->parent[index].dbg_cc; 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); 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); } struct clk_debug_mux *mux = to_clk_measure(hw); int ret; /* 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); if (!mux->mux_sels) return 0; /* 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); /* 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; 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 = { Loading @@ -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, /* 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); } /* 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_hw *mux) { 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; if (meas->cbcr_offset != U32_MAX) regmap_update_bits(meas->regmap, meas->cbcr_offset, meas->en_mask, 0); parent = clk_hw_get_parent(mux); disable_debug_clks(parent); } static void disable_debug_clks(struct clk_debug_mux *meas, u8 index) static u32 get_mux_divs(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; u32 div_val; meas->en_mask = meas->en_mask ? meas->en_mask : CBCR_ENA; if (!mux || !(mux->init->flags & CLK_IS_MEASURE)) return 1; /* Turn off the GCC_DEBUG_CBCR */ regmap_update_bits(meas->regmap[GCC], meas->cbcr_offset, meas->en_mask, 0); WARN_ON(!meas->post_div_val); div_val = meas->post_div_val; 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); 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; Loading @@ -288,22 +272,73 @@ static int clk_debug_measure_get(void *data, u64 *val) 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, ®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; 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; } 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); } Loading @@ -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);
drivers/clk/qcom/clk-debug.h +25 −61 Original line number Diff line number Diff line Loading @@ -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 */ /** Loading @@ -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 Loading @@ -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; }; Loading @@ -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