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

Commit 98d35a00 authored by David Dai's avatar David Dai Committed by David Collins
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>
Signed-off-by: default avatarDavid Collins <collinsd@codeaurora.org>
parent 4defae99
Loading
Loading
Loading
Loading
+140 −121
Original line number Diff line number Diff line
@@ -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;

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