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

Commit 7e7b8ca6 authored by Roger Quadros's avatar Roger Quadros Committed by Kishon Vijay Abraham I
Browse files

phy: ti: am654-serdes: Support all clksel values



Add support to select all 16 CLKSEL combinations that are shown in
"SerDes Reference Clock Distribution" in AM65 TRM.

Signed-off-by: default avatarRoger Quadros <rogerq@ti.com>
Signed-off-by: default avatarKishon Vijay Abraham I <kishon@ti.com>
parent 71e2f5c5
Loading
Loading
Loading
Loading
+83 −49
Original line number Diff line number Diff line
@@ -58,13 +58,14 @@

#define SERDES_NUM_CLOCKS	3

#define AM654_SERDES_CTRL_CLKSEL_MASK	GENMASK(7, 4)
#define AM654_SERDES_CTRL_CLKSEL_SHIFT	4

struct serdes_am654_clk_mux {
	struct clk_hw	hw;
	struct regmap	*regmap;
	unsigned int	reg;
	int		*table;
	u32		mask;
	u8		shift;
	int		clk_id;
	struct clk_init_data clk_data;
};

@@ -282,31 +283,52 @@ static const struct phy_ops ops = {
	.owner		= THIS_MODULE,
};

#define SERDES_NUM_MUX_COMBINATIONS 16

#define LICLK 0
#define EXT_REFCLK 1
#define RICLK 2

static const int
serdes_am654_mux_table[SERDES_NUM_MUX_COMBINATIONS][SERDES_NUM_CLOCKS] = {
	/*
	 * Each combination maps to one of
	 * "Figure 12-1986. SerDes Reference Clock Distribution"
	 * in TRM.
	 */
	 /* Parent of CMU refclk, Left output, Right output
	  * either of EXT_REFCLK, LICLK, RICLK
	  */
	{ EXT_REFCLK, EXT_REFCLK, EXT_REFCLK },	/* 0000 */
	{ RICLK, EXT_REFCLK, EXT_REFCLK },	/* 0001 */
	{ EXT_REFCLK, RICLK, LICLK },		/* 0010 */
	{ RICLK, RICLK, EXT_REFCLK },		/* 0011 */
	{ LICLK, EXT_REFCLK, EXT_REFCLK },	/* 0100 */
	{ EXT_REFCLK, EXT_REFCLK, EXT_REFCLK },	/* 0101 */
	{ LICLK, RICLK, LICLK },		/* 0110 */
	{ EXT_REFCLK, RICLK, LICLK },		/* 0111 */
	{ EXT_REFCLK, EXT_REFCLK, LICLK },	/* 1000 */
	{ RICLK, EXT_REFCLK, LICLK },		/* 1001 */
	{ EXT_REFCLK, RICLK, EXT_REFCLK },	/* 1010 */
	{ RICLK, RICLK, EXT_REFCLK },		/* 1011 */
	{ LICLK, EXT_REFCLK, LICLK },		/* 1100 */
	{ EXT_REFCLK, EXT_REFCLK, LICLK },	/* 1101 */
	{ LICLK, RICLK, EXT_REFCLK },		/* 1110 */
	{ EXT_REFCLK, RICLK, EXT_REFCLK },	/* 1111 */
};

static u8 serdes_am654_clk_mux_get_parent(struct clk_hw *hw)
{
	struct serdes_am654_clk_mux *mux = to_serdes_am654_clk_mux(hw);
	unsigned int num_parents = clk_hw_get_num_parents(hw);
	struct regmap *regmap = mux->regmap;
	unsigned int reg = mux->reg;
	unsigned int val;
	int i;

	regmap_read(regmap, reg, &val);
	val >>= mux->shift;
	val &= mux->mask;
	val &= AM654_SERDES_CTRL_CLKSEL_MASK;
	val >>= AM654_SERDES_CTRL_CLKSEL_SHIFT;

	for (i = 0; i < num_parents; i++)
		if (mux->table[i] == val)
			return i;

	/*
	 * No parent? This should never happen!
	 * Verify if we set a valid parent in serdes_am654_clk_register()
	 */
	WARN(1, "Failed to find the parent of %s clock\n", hw->init->name);

	/* Make the parent lookup to fail */
	return num_parents;
	return serdes_am654_mux_table[val][mux->clk_id];
}

static int serdes_am654_clk_mux_set_parent(struct clk_hw *hw, u8 index)
@@ -314,16 +336,52 @@ static int serdes_am654_clk_mux_set_parent(struct clk_hw *hw, u8 index)
	struct serdes_am654_clk_mux *mux = to_serdes_am654_clk_mux(hw);
	struct regmap *regmap = mux->regmap;
	unsigned int reg = mux->reg;
	int val;
	int clk_id = mux->clk_id;
	int parents[SERDES_NUM_CLOCKS];
	const int *p;
	u32 val;
	int found, i;
	int ret;

	val = mux->table[index];
	/* get existing setting */
	regmap_read(regmap, reg, &val);
	val &= AM654_SERDES_CTRL_CLKSEL_MASK;
	val >>= AM654_SERDES_CTRL_CLKSEL_SHIFT;

	for (i = 0; i < SERDES_NUM_CLOCKS; i++)
		parents[i] = serdes_am654_mux_table[val][i];

	/* change parent of this clock. others left intact */
	parents[clk_id] = index;

	if (val == -1)
	/* Find the match */
	for (val = 0; val < SERDES_NUM_MUX_COMBINATIONS; val++) {
		p = serdes_am654_mux_table[val];
		found = 1;
		for (i = 0; i < SERDES_NUM_CLOCKS; i++) {
			if (parents[i] != p[i]) {
				found = 0;
				break;
			}
		}

		if (found)
			break;
	}

	if (!found) {
		/*
		 * This can never happen, unless we missed
		 * a valid combination in serdes_am654_mux_table.
		 */
		WARN(1, "Failed to find the parent of %s clock\n",
		     hw->init->name);
		return -EINVAL;
	}

	val <<= mux->shift;
	ret = regmap_update_bits(regmap, reg, mux->mask << mux->shift, val);
	val <<= AM654_SERDES_CTRL_CLKSEL_SHIFT;
	ret = regmap_update_bits(regmap, reg, AM654_SERDES_CTRL_CLKSEL_MASK,
				 val);

	return ret;
}
@@ -333,21 +391,6 @@ static const struct clk_ops serdes_am654_clk_mux_ops = {
	.get_parent = serdes_am654_clk_mux_get_parent,
};

static int mux_table[SERDES_NUM_CLOCKS][3] = {
	/*
	 * The entries represent values for selecting between
	 * {left input, external reference clock, right input}
	 * Only one of Left Output or Right Output should be used since
	 * both left and right output clock uses the same bits and modifying
	 * one clock will impact the other.
	 */
	{ BIT(2),               0, BIT(0) }, /* Mux of CMU refclk */
	{     -1,          BIT(3), BIT(1) }, /* Mux of Left Output */
	{ BIT(1), BIT(3) | BIT(1),     -1 }, /* Mux of Right Output */
};

static int mux_mask[SERDES_NUM_CLOCKS] = { 0x5, 0xa, 0xa };

static int serdes_am654_clk_register(struct serdes_am654 *am654_phy,
				     const char *clock_name, int clock_num)
{
@@ -407,20 +450,11 @@ static int serdes_am654_clk_register(struct serdes_am654 *am654_phy,
	init->num_parents = num_parents;
	init->name = clock_name;

	mux->table = mux_table[clock_num];
	mux->regmap = regmap;
	mux->reg = reg;
	mux->shift = 4;
	mux->mask = mux_mask[clock_num];
	mux->clk_id = clock_num;
	mux->hw.init = init;

	/*
	 * setup a sane default so get_parent() call evaluates
	 * to a valid parent. Index 1 is the safest choice as
	 * the default as it is valid value for all of serdes's
	 * output clocks.
	 */
	serdes_am654_clk_mux_set_parent(&mux->hw, 1);
	clk = devm_clk_register(dev, &mux->hw);
	if (IS_ERR(clk))
		return PTR_ERR(clk);