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

Commit 6278f25f authored by Amit Nischal's avatar Amit Nischal
Browse files

clk: qcom: Add support to initialize & handle dynamic update for alpha plls



Add support to do initial configuration for alpha plls and votable
alpha PLLs need to have the fsm mode enabled as part of the
initialization using flag 'SUPPORTS_FSM_MODE'.

Alpha PLLs can support two kinds of input signals, normal and latched.
The normal input is directly passed to the core, while the latched input
requires a latch and acknowledge sequence to be performed for the
changed input to propagate.

Alpha PLLs can support dynamic update with both kind of input signals.
The ones which support this using a latched interface however need to
follow the latch/wait-for-ack sequence to be performed when the rate
changes. Mark these with a new flag 'SUPPORTS_DYNAMIC_UPDATE' to handle
this as part of clk_alpha_pll_set_rate().

PLLs could require post div to be set at runtime, add a vco_data which
could be used for these settings.

Change-Id: Ia0b9a2a52a3b33b7b68409c19c460d717eb5c1e2
Signed-off-by: default avatarAmit Nischal <anischal@codeaurora.org>
parent 4a91ea36
Loading
Loading
Loading
Loading
+216 −37
Original line number Diff line number Diff line
/*
 * Copyright (c) 2015, The Linux Foundation. All rights reserved.
 * Copyright (c) 2015-2016, The Linux Foundation. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 2 and
@@ -25,12 +25,19 @@
#define PLL_RESET_N		BIT(2)
#define PLL_LOCK_COUNT_SHIFT	8
#define PLL_LOCK_COUNT_MASK	0x3f
#define PLL_LOCK_COUNT_VAL	0x0
#define PLL_BIAS_COUNT_SHIFT	14
#define PLL_BIAS_COUNT_MASK	0x3f
#define PLL_BIAS_COUNT_VAL	0x6
#define PLL_LATCH_INTERFACE	BIT(11)
#define PLL_VOTE_FSM_ENA	BIT(20)
#define PLL_VOTE_FSM_RESET	BIT(21)
#define PLL_UPDATE		BIT(22)
#define PLL_HW_UPDATE_LOGIC_BYPASS	BIT(23)
#define PLL_ALPHA_EN		BIT(24)
#define PLL_ACTIVE_FLAG		BIT(30)
#define PLL_LOCK_DET		BIT(31)
#define PLL_ACK_LATCH		BIT(29)

#define PLL_L_VAL		0x04
#define PLL_ALPHA_VAL		0x08
@@ -39,7 +46,6 @@
#define PLL_USER_CTL		0x10
#define PLL_POST_DIV_SHIFT	8
#define PLL_POST_DIV_MASK	0xf
# define PLL_ALPHA_EN		BIT(24)
#define PLL_VCO_SHIFT		20
#define PLL_VCO_MASK		0x3

@@ -79,7 +85,7 @@ static int wait_for_pll(struct clk_alpha_pll *pll, u32 mask, bool inverse,
		ret = regmap_read(pll->clkr.regmap, off + PLL_MODE, &val);
		if (ret)
			return ret;
		if (inverse && (val & mask))
		if (inverse && !(val & mask))
			return 0;
		else if ((val & mask) == mask)
			return 0;
@@ -106,6 +112,10 @@ static int wait_for_pll_offline(struct clk_alpha_pll *pll, u32 mask)
	return wait_for_pll(pll, mask, 0, "offline");
}

static int wait_for_pll_latch_ack(struct clk_alpha_pll *pll, u32 mask)
{
	return wait_for_pll(pll, mask, 0, "latch_ack");
}

/* alpha pll with hwfsm support */

@@ -114,29 +124,105 @@ static int wait_for_pll_offline(struct clk_alpha_pll *pll, u32 mask)
#define PLL_OFFLINE_ACK		BIT(28)
#define PLL_ACTIVE_FLAG		BIT(30)

static void clk_alpha_set_fsm_mode(struct clk_alpha_pll *pll)
{
	u32 val;

	regmap_read(pll->clkr.regmap, pll->offset + PLL_MODE, &val);

	/* De-assert reset to FSM */
	val &= ~PLL_VOTE_FSM_RESET;

	/* Program bias count */
	val &= ~(PLL_BIAS_COUNT_MASK << PLL_BIAS_COUNT_SHIFT);
	val |= PLL_BIAS_COUNT_VAL << PLL_BIAS_COUNT_SHIFT;

	/* Program lock count */
	val &= ~(PLL_LOCK_COUNT_MASK << PLL_LOCK_COUNT_SHIFT);
	val |= PLL_LOCK_COUNT_VAL << PLL_LOCK_COUNT_SHIFT;

	/* Enable PLL FSM voting */
	val |= PLL_VOTE_FSM_ENA;

	regmap_write(pll->clkr.regmap, pll->offset + PLL_MODE, val);
}

void clk_alpha_pll_configure(struct clk_alpha_pll *pll, struct regmap *regmap,
				const struct pll_config *config)
{
	u32 val, mask;

	if (config->l)
		regmap_write(regmap, pll->offset + PLL_L_VAL,
						config->l);
	if (config->alpha)
		regmap_write(regmap, pll->offset + PLL_ALPHA_VAL,
						config->alpha);
	if (config->alpha_u)
		regmap_write(regmap, pll->offset + PLL_ALPHA_VAL_U,
						config->alpha_u);
	if (config->config_ctl_val)
		regmap_write(regmap, pll->offset + PLL_CONFIG_CTL,
				config->config_ctl_val);

	if (config->main_output_mask || config->aux_output_mask ||
		config->aux2_output_mask || config->early_output_mask ||
		config->vco_val || config->alpha_en_mask) {

		val = config->main_output_mask;
		val |= config->aux_output_mask;
		val |= config->aux2_output_mask;
		val |= config->early_output_mask;
	val |= config->post_div_val;
		val |= config->vco_val;
		val |= config->alpha_en_mask;

		mask = config->main_output_mask;
		mask |= config->aux_output_mask;
		mask |= config->aux2_output_mask;
		mask |= config->early_output_mask;
	mask |= config->post_div_mask;
		mask |= config->vco_mask;
		mask |= config->alpha_en_mask;

	regmap_update_bits(regmap, pll->offset + PLL_USER_CTL, mask, val);
		regmap_update_bits(regmap, pll->offset + PLL_USER_CTL,
					mask, val);
	}

	return;
	if (config->post_div_mask) {
		mask = config->post_div_mask;
		val = config->post_div_val;
		regmap_update_bits(regmap, pll->offset + PLL_USER_CTL,
					mask, val);
	}

	/* Do not bypass the latch interface */
	if (pll->flags & SUPPORTS_SLEW)
		regmap_update_bits(regmap, pll->offset + PLL_USER_CTL_U,
		PLL_LATCH_INTERFACE, (u32)~PLL_LATCH_INTERFACE);

	if (pll->flags & SUPPORTS_DYNAMIC_UPDATE) {
		regmap_update_bits(regmap, pll->offset + PLL_MODE,
				 PLL_HW_UPDATE_LOGIC_BYPASS,
				 PLL_HW_UPDATE_LOGIC_BYPASS);
	}

	if (config->test_ctl_lo_mask) {
		mask = config->test_ctl_lo_mask;
		val = config->test_ctl_lo_val;
		regmap_update_bits(regmap, pll->offset + PLL_TEST_CTL,
					mask, val);
	}

	if (config->test_ctl_hi_mask) {
		mask = config->test_ctl_hi_mask;
		val = config->test_ctl_hi_val;
		regmap_update_bits(regmap, pll->offset + PLL_TEST_CTL_U,
					mask, val);
	}

	if (pll->flags & SUPPORTS_FSM_MODE)
		clk_alpha_set_fsm_mode(pll);

	pll->inited = true;
}

static int clk_alpha_pll_hwfsm_enable(struct clk_hw *hw)
@@ -190,8 +276,9 @@ static void clk_alpha_pll_hwfsm_disable(struct clk_hw *hw)
				 PLL_FSM_ENA, 0);
	if (ret)
		return;

	wait_for_pll_disable(pll, PLL_ACTIVE_FLAG);
	return;

}

static int clk_alpha_pll_enable(struct clk_hw *hw)
@@ -202,6 +289,11 @@ static int clk_alpha_pll_enable(struct clk_hw *hw)

	off = pll->offset;

	if (unlikely(!pll->inited)) {
		clk_alpha_pll_configure(pll, pll->clkr.regmap,
					pll->config);
	}

	mask = PLL_OUTCTRL | PLL_RESET_N | PLL_BYPASSNL;
	ret = regmap_read(pll->clkr.regmap, off + PLL_MODE, &val);
	if (ret)
@@ -339,7 +431,53 @@ clk_alpha_pll_recalc_rate(struct clk_hw *hw, unsigned long parent_rate)
		a >>= ALPHA_REG_BITWIDTH - ALPHA_BITWIDTH;
	}

	return alpha_pll_calc_rate(prate, l, a);
	ctl >>= PLL_POST_DIV_SHIFT;
	ctl &= PLL_POST_DIV_MASK;

	return alpha_pll_calc_rate(prate, l, a) >> fls(ctl);
}

static int clk_alpha_pll_dynamic_update(struct clk_alpha_pll *pll)
{
	int ret;

	/* Latch the input to the PLL */
	regmap_update_bits(pll->clkr.regmap, pll->offset + PLL_MODE,
				PLL_UPDATE, PLL_UPDATE);

	/* Wait for 2 reference cycle before checking ACK bit */
	udelay(1);

	ret = wait_for_pll_latch_ack(pll, PLL_ACK_LATCH);
	if (ret)
		return ret;

	/* Return latch input to 0 */
	regmap_update_bits(pll->clkr.regmap, pll->offset + PLL_MODE,
				PLL_UPDATE, (u32)~PLL_UPDATE);

	ret = wait_for_pll_enable(pll, PLL_LOCK_DET);
	if (ret)
		return ret;

	return 0;
}

static const struct pll_vco_data
	*find_vco_data(const struct pll_vco_data *data,
			unsigned long rate, size_t size)
{
	int i;

	if (!data)
		return NULL;

	for (i = 0; i < size; i++) {
		if (rate == data[i].freq)
			return &data[i];
	}

	return &data[i - 1];
}

static int clk_alpha_pll_set_rate(struct clk_hw *hw, unsigned long rate,
@@ -347,16 +485,36 @@ static int clk_alpha_pll_set_rate(struct clk_hw *hw, unsigned long rate,
{
	struct clk_alpha_pll *pll = to_clk_alpha_pll(hw);
	const struct pll_vco *vco;
	const struct pll_vco_data *data;
	bool is_enabled;
	u32 l, off = pll->offset;
	u64 a;
	unsigned long rrate;

	rrate = alpha_pll_round_rate(rate, prate, &l, &a);

	if (rrate != rate) {
		pr_err("alpha_pll: Call clk_set_rate with rounded rates!\n");
		return -EINVAL;
	}

	rate = alpha_pll_round_rate(rate, prate, &l, &a);
	vco = alpha_pll_find_vco(pll, rate);
	vco = alpha_pll_find_vco(pll, rrate);
	if (!vco) {
		pr_err("alpha pll not in a valid vco range\n");
		return -EINVAL;
	}

	is_enabled = clk_hw_is_enabled(hw);

	/*
	* For PLLs that do not support dynamic programming (dynamic_update
	* is not set), ensure PLL is off before changing rate. For
	* optimization reasons, assume no downstream clock is actively
	* using it.
	*/
	if (is_enabled && !(pll->flags & SUPPORTS_DYNAMIC_UPDATE))
		hw->init->ops->disable(hw);

	a <<= (ALPHA_REG_BITWIDTH - ALPHA_BITWIDTH);

	regmap_write(pll->clkr.regmap, off + PLL_L_VAL, l);
@@ -367,9 +525,27 @@ static int clk_alpha_pll_set_rate(struct clk_hw *hw, unsigned long rate,
			   PLL_VCO_MASK << PLL_VCO_SHIFT,
			   vco->val << PLL_VCO_SHIFT);

	data = find_vco_data(pll->vco_data, rate, pll->num_vco_data);
	if (data) {
		if (data->freq == rate)
			regmap_update_bits(pll->clkr.regmap, off + PLL_USER_CTL,
				PLL_POST_DIV_MASK << PLL_POST_DIV_SHIFT,
				data->post_div_val << PLL_POST_DIV_SHIFT);
		else
			regmap_update_bits(pll->clkr.regmap, off + PLL_USER_CTL,
					PLL_POST_DIV_MASK << PLL_POST_DIV_SHIFT,
					0x0 << PLL_VCO_SHIFT);
	}

	regmap_update_bits(pll->clkr.regmap, off + PLL_USER_CTL, PLL_ALPHA_EN,
			   PLL_ALPHA_EN);

	if (is_enabled && (pll->flags & SUPPORTS_DYNAMIC_UPDATE))
		clk_alpha_pll_dynamic_update(pll);

	if (is_enabled && !(pll->flags & SUPPORTS_DYNAMIC_UPDATE))
		hw->init->ops->enable(hw);

	return 0;
}

@@ -381,6 +557,9 @@ static long clk_alpha_pll_round_rate(struct clk_hw *hw, unsigned long rate,
	u64 a;
	unsigned long min_freq, max_freq;

	if (rate < pll->min_supported_freq)
		return pll->min_supported_freq;

	rate = alpha_pll_round_rate(rate, *prate, &l, &a);
	if (alpha_pll_find_vco(pll, rate))
		return rate;
+22 −2
Original line number Diff line number Diff line
/*
 * Copyright (c) 2015, The Linux Foundation. All rights reserved.
 * Copyright (c) 2015-2016, The Linux Foundation. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 2 and
@@ -18,6 +18,11 @@
#include "clk-regmap.h"
#include "clk-pll.h"

struct pll_vco_data {
	unsigned long freq;
	u8 post_div_val;
};

struct pll_vco {
	unsigned long min_freq;
	unsigned long max_freq;
@@ -28,21 +33,36 @@ struct pll_vco {
 * struct clk_alpha_pll - phase locked loop (PLL)
 * @offset: base address of registers
 * @vco_table: array of VCO settings
 * @vco_data: array of VCO data settings like post div
 * @clkr: regmap clock handle
 */
struct clk_alpha_pll {
	u32 offset;
	struct pll_config *config;
	bool inited;

	const struct pll_vco *vco_table;
	size_t num_vco;

	const struct pll_vco_data *vco_data;
	size_t num_vco_data;

	u8 flags;
#define SUPPORTS_FSM_MODE	BIT(0)
	/* some PLLs support dynamically updating their rate
	 * without disabling the PLL first. Set this flag
	 * to enable this support.
	 */
#define SUPPORTS_DYNAMIC_UPDATE BIT(1)
#define SUPPORTS_SLEW		BIT(2)

	struct clk_regmap clkr;
	u32 config_ctl_val;
#define PLLOUT_MAIN	BIT(0)
#define PLLOUT_AUX	BIT(1)
#define PLLOUT_AUX2	BIT(2)
#define PLLOUT_EARLY	BIT(3)
	u32 pllout_flags;
	unsigned long min_supported_freq;
};

/**
+8 −1
Original line number Diff line number Diff line
/*
 * Copyright (c) 2013, The Linux Foundation. All rights reserved.
 * Copyright (c) 2013, 2016 The Linux Foundation. All rights reserved.
 *
 * This software is licensed under the terms of the GNU General Public
 * License version 2, as published by the Free Software Foundation, and
@@ -70,6 +70,8 @@ struct pll_config {
	u16 l;
	u32 m;
	u32 n;
	u32 alpha;
	u32 alpha_u;
	u32 vco_val;
	u32 vco_mask;
	u32 pre_div_val;
@@ -77,11 +79,16 @@ struct pll_config {
	u32 post_div_val;
	u32 post_div_mask;
	u32 mn_ena_mask;
	u32 alpha_en_mask;
	u32 main_output_mask;
	u32 aux_output_mask;
	u32 aux2_output_mask;
	u32 early_output_mask;
	u32 config_ctl_val;
	u32 test_ctl_lo_val;
	u32 test_ctl_lo_mask;
	u32 test_ctl_hi_val;
	u32 test_ctl_hi_mask;
};

void clk_pll_configure_sr(struct clk_pll *pll, struct regmap *regmap,