Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 43eef15a authored by David Dai's avatar David Dai
Browse files

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: default avatarDavid Dai <daidavid1@codeaurora.org>
parent 2cea1f57
Loading
Loading
Loading
Loading
+138 −122
Original line number Diff line number Diff line
@@ -9,6 +9,7 @@
#include <linux/clk-provider.h>
#include <linux/of.h>
#include <linux/bitops.h>
#include <linux/mfd/syscon.h>
#include <linux/msm-bus.h>

#include "clk-regmap.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;

	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, &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);

		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);
	}
	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, &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);
	if (!mux->mux_sels)
		return 0;

	/* 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);
	/* 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 = {
@@ -196,51 +187,69 @@ 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_hw *hw = data;
	struct clk_debug_mux *meas = to_clk_measure(measure);
	int index;
	int ret = 0;
	unsigned long meas_rate, sw_rate;

	mutex_lock(&clk_debug_lock);

@@ -251,42 +260,18 @@ static int clk_debug_measure_get(void *data, u64 *val)
	if (meas->bus_cl_id)
		msm_bus_scale_client_update_request(meas->bus_cl_id, 1);

	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:
	if (meas->bus_cl_id)
		msm_bus_scale_client_update_request(meas->bus_cl_id, 0);
@@ -300,20 +285,18 @@ 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, &regval);
		parent = clk_hw_get_parent(measure);
		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);
@@ -336,8 +319,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");
@@ -347,17 +331,20 @@ void clk_debug_measure_add(struct clk_hw *hw, struct dentry *dentry)
	meas = to_clk_measure(measure);
	if (meas->bus_cl_id)
		msm_bus_scale_client_update_request(meas->bus_cl_id, 1);
	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);
		goto err;
	}

	index = clk_debug_mux_get_parent(measure);
	if (meas->parent[index].dbg_cc == MC_CC)
	parent = clk_hw_get_parent(measure);
	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);
@@ -387,3 +374,32 @@ void clk_debug_bus_vote(struct clk_hw *hw, bool enable)
		msm_bus_scale_client_update_request(hw->init->bus_cl_id,
								enable);
}

/**
 * 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);
+22 −62
Original line number Diff line number Diff line
@@ -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,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;
	u32 bus_cl_id;
	struct clk_hw hw;
@@ -131,5 +89,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);
void clk_debug_bus_vote(struct clk_hw *hw, bool enable);
int map_debug_bases(struct platform_device *pdev, const char *base,
		    struct clk_debug_mux *mux);

#endif
+715 −575

File changed.

Preview size limit exceeded, changes collapsed.

+590 −540

File changed.

Preview size limit exceeded, changes collapsed.

+0 −35
Original line number Diff line number Diff line
@@ -3874,39 +3874,6 @@ static struct clk_branch gcc_video_xo_clk = {
	},
};

/* Measure-only clock for gcc_cfg_noc_ahb_clk. */
static struct clk_dummy measure_only_cnoc_clk = {
	.rrate = 1000,
	.hw.init = &(struct clk_init_data){
		.name = "measure_only_cnoc_clk",
		.ops = &clk_dummy_ops,
	},
};

/* Measure-only clock for gcc_ipa_2x_clk. */
static struct clk_dummy measure_only_ipa_2x_clk = {
	.rrate = 1000,
	.hw.init = &(struct clk_init_data){
		.name = "measure_only_ipa_2x_clk",
		.ops = &clk_dummy_ops,
	},
};

/* Measure-only clock for gcc_sys_noc_axi_clk. */
static struct clk_dummy measure_only_snoc_clk = {
	.rrate = 1000,
	.hw.init = &(struct clk_init_data){
		.name = "measure_only_snoc_clk",
		.ops = &clk_dummy_ops,
	},
};

struct clk_hw *gcc_kona_hws[] = {
	[MEASURE_ONLY_CNOC_CLK] = &measure_only_cnoc_clk.hw,
	[MEASURE_ONLY_IPA_2X_CLK] = &measure_only_ipa_2x_clk.hw,
	[MEASURE_ONLY_SNOC_CLK] = &measure_only_snoc_clk.hw,
};

static struct clk_regmap *gcc_kona_clocks[] = {
	[GCC_AGGRE_NOC_PCIE_TBU_CLK] = &gcc_aggre_noc_pcie_tbu_clk.clkr,
	[GCC_AGGRE_UFS_CARD_AXI_CLK] = &gcc_aggre_ufs_card_axi_clk.clkr,
@@ -4215,8 +4182,6 @@ static const struct qcom_cc_desc gcc_kona_desc = {
	.num_clks = ARRAY_SIZE(gcc_kona_clocks),
	.resets = gcc_kona_resets,
	.num_resets = ARRAY_SIZE(gcc_kona_resets),
	.hwclks = gcc_kona_hws,
	.num_hwclks = ARRAY_SIZE(gcc_kona_hws),
};

static const struct of_device_id gcc_kona_match_table[] = {
Loading