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

Commit 02ba8635 authored by Taniya Das's avatar Taniya Das
Browse files

clk: qcom: clk-alpha-pll: Add support to be able to slew PLL



Few alpha PLLs would require to be able to slew in the same VCO mode, which
means the PLL would be able to update the new frequency L value without
turning it off. But to be support this feature the PLL needs to calibrated
at the mid of the VCO range and then enabled.

Add support for dynamic update in which the frequency can be changed
without turning off the PLL. and also Update list_register operation
to read the pll offset defined in PLL register map to avoid mismatch
alpha PLL.

Change-Id: Idbb374be8710cdcd80ab3c4ed57f89f00ed15d23
Signed-off-by: default avatarTaniya Das <tdas@codeaurora.org>
parent 1baf48e1
Loading
Loading
Loading
Loading
+373 −32
Original line number Diff line number Diff line
@@ -9,6 +9,7 @@
#include <linux/clk-provider.h>
#include <linux/regmap.h>
#include <linux/delay.h>
#include <linux/sched/clock.h>

#include "clk-alpha-pll.h"
#include "clk-debug.h"
@@ -23,6 +24,7 @@
# define PLL_LOCK_COUNT_MASK	0x3f
# define PLL_BIAS_COUNT_SHIFT	14
# define PLL_BIAS_COUNT_MASK	0x3f
#define PLL_LATCH_INTERFACE     BIT(11)
# define PLL_VOTE_FSM_ENA	BIT(20)
# define PLL_FSM_ENA		BIT(20)
# define PLL_VOTE_FSM_RESET	BIT(21)
@@ -291,12 +293,15 @@ static int wait_for_pll(struct clk_alpha_pll *pll, u32 mask, bool inverse,
	u32 val;
	int count;
	int ret;
	u64 time;
	const char *name = clk_hw_get_name(&pll->clkr.hw);

	ret = regmap_read(pll->clkr.regmap, PLL_MODE(pll), &val);
	if (ret)
		return ret;

	time = sched_clock();

	for (count = 100; count > 0; count--) {
		ret = regmap_read(pll->clkr.regmap, PLL_MODE(pll), &val);
		if (ret)
@@ -308,6 +313,9 @@ static int wait_for_pll(struct clk_alpha_pll *pll, u32 mask, bool inverse,

		udelay(1);
	}
	time = sched_clock() - time;

	pr_err("PLL lock bit detection total wait time: %lld ns\n", time);

	WARN(1, "%s failed to %s!\n", name, action);
	return -ETIMEDOUT;
@@ -337,14 +345,21 @@ static int wait_for_pll(struct clk_alpha_pll *pll, u32 mask, bool inverse,
#define wait_for_pll_update_ack_clear(pll) \
	wait_for_pll(pll, ALPHA_PLL_ACK_LATCH, 1, "update_ack_clear")

#define wait_for_pll_latch_ack(pll) \
	wait_for_pll(pll, ALPHA_PLL_ACK_LATCH, 0, "latch ack")

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

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

	if (pll_has_64bit_config(pll))
		regmap_write(regmap, PLL_CONFIG_CTL_U(pll),
@@ -353,25 +368,60 @@ void clk_alpha_pll_configure(struct clk_alpha_pll *pll, struct regmap *regmap,
	if (pll_alpha_width(pll) > 32)
		regmap_write(regmap, PLL_ALPHA_VAL_U(pll), config->alpha_hi);

	if (config->main_output_mask || config->aux_output_mask ||
		config->aux2_output_mask || config->early_output_mask ||
		config->pre_div_val || 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->pre_div_val;
	val |= config->post_div_val;
		val |= config->vco_val;
		val |= config->alpha_en_mask;
	val |= config->alpha_mode_mask;

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

		regmap_update_bits(regmap, PLL_USER_CTL(pll), mask, val);
	}

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

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

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

	if (config->test_ctl_mask) {
		mask = config->test_ctl_mask;
		val = config->test_ctl_val;
		regmap_update_bits(regmap, PLL_TEST_CTL(pll), 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_TEST_CTL_U(pll), mask, val);
	}
	if (pll->flags & SUPPORTS_DYNAMIC_UPDATE)
		regmap_update_bits(regmap, PLL_MODE(pll), PLL_UPDATE_BYPASS,
					PLL_UPDATE_BYPASS);

	if (pll->flags & SUPPORTS_FSM_MODE)
		qcom_pll_set_fsm_mode(regmap, PLL_MODE(pll), 6, 0);
@@ -610,6 +660,48 @@ clk_alpha_pll_recalc_rate(struct clk_hw *hw, unsigned long parent_rate)
	return alpha_pll_calc_rate(prate, l, a, alpha_width);
}

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_MODE(pll),
				PLL_UPDATE, PLL_UPDATE);

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

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

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

	ret = wait_for_pll_enable_lock(pll);
	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_update_latch(struct clk_alpha_pll *pll)
{
@@ -644,6 +736,9 @@ static int __clk_alpha_pll_update_latch(struct clk_alpha_pll *pll)
			return ret;
	}

	if (pll->flags & SUPPORTS_DYNAMIC_UPDATE)
		ret = wait_for_pll_enable_lock(pll);
	else
		ret = wait_for_pll_update_ack_clear(pll);
	if (ret)
		return ret;
@@ -669,17 +764,36 @@ static int __clk_alpha_pll_set_rate(struct clk_hw *hw, unsigned long rate,
				    int (*is_enabled)(struct clk_hw *))
{
	struct clk_alpha_pll *pll = to_clk_alpha_pll(hw);
	struct clk_alpha_pll_postdiv *pll_postdiv =
		to_clk_alpha_pll_postdiv(hw);
	const struct pll_vco *vco;
	const struct pll_vco_data *data;
	u32 l, alpha_width = pll_alpha_width(pll);
	u64 a;
	unsigned long rrate;

	rrate = alpha_pll_round_rate(rate, prate, &l, &a, alpha_width);
	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, alpha_width);
	vco = alpha_pll_find_vco(pll, rate);
	if (pll->vco_table && !vco) {
		pr_err("alpha pll not in a valid vco range\n");
		return -EINVAL;
	}

	/*
	 * 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->clkr.hw) &&
	    !(pll->flags & SUPPORTS_DYNAMIC_UPDATE))
		hw->init->ops->disable(hw);

	regmap_write(pll->clkr.regmap, PLL_L_VAL(pll), l);

	if (alpha_width > ALPHA_BITWIDTH)
@@ -696,9 +810,31 @@ static int __clk_alpha_pll_set_rate(struct clk_hw *hw, unsigned long rate,
				   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, PLL_USER_CTL(pll),
				PLL_POST_DIV_MASK(pll_postdiv)
				<< PLL_POST_DIV_SHIFT,
				data->post_div_val << PLL_POST_DIV_SHIFT);
		else
			regmap_update_bits(pll->clkr.regmap, PLL_USER_CTL(pll),
					PLL_POST_DIV_MASK(pll_postdiv)
					<< PLL_POST_DIV_SHIFT,
					0x0 << PLL_VCO_SHIFT);
	}

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

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

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

	return clk_alpha_pll_update_latch(pll, is_enabled);
}

@@ -724,6 +860,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, alpha_width);
	if (!pll->vco_table || alpha_pll_find_vco(pll, rate))
		return rate;
@@ -1603,12 +1742,16 @@ static void clk_alpha_pll_list_registers(struct seq_file *f, struct clk_hw *hw)
	int size, i, val;

	 static struct clk_register_data data[] = {
		{"PLL_MODE", 0x0},
		{"PLL_L_VAL", 0x4},
		{"PLL_ALPHA_VAL", 0x8},
		{"PLL_ALPHA_VAL_U", 0xC},
		{"PLL_USER_CTL", 0x10},
		{"PLL_CONFIG_CTL", 0x18},
		{"PLL_MODE", PLL_OFF_MODE},
		{"PLL_L_VAL", PLL_OFF_L_VAL},
		{"PLL_ALPHA_VAL", PLL_OFF_ALPHA_VAL},
		{"PLL_ALPHA_VAL_U", PLL_OFF_ALPHA_VAL_U},
		{"PLL_TEST_CTL", PLL_OFF_TEST_CTL},
		{"PLL_TEST_CTL_U", PLL_OFF_TEST_CTL_U},
		{"PLL_USER_CTL", PLL_OFF_USER_CTL},
		{"PLL_USER_CTL_U", PLL_OFF_USER_CTL_U},
		{"PLL_CONFIG_CTL", PLL_OFF_CONFIG_CTL},
		{"PLL_STATUS", PLL_OFF_STATUS},
	};

	static struct clk_register_data data1[] = {
@@ -1618,12 +1761,13 @@ static void clk_alpha_pll_list_registers(struct seq_file *f, struct clk_hw *hw)
	size = ARRAY_SIZE(data);

	for (i = 0; i < size; i++) {
		regmap_read(pll->clkr.regmap, pll->offset + data[i].offset,
					&val);
		regmap_read(pll->clkr.regmap,
				pll->offset + pll->regs[data[i].offset], &val);
		seq_printf(f, "%20s: 0x%.8x\n", data[i].name, val);
	}

	regmap_read(pll->clkr.regmap, pll->offset + data[0].offset, &val);
	regmap_read(pll->clkr.regmap, pll->offset +
					pll->regs[data[0].offset], &val);

	if (val & PLL_FSM_ENA) {
		regmap_read(pll->clkr.regmap, pll->clkr.enable_reg +
@@ -4110,3 +4254,200 @@ const struct clk_ops clk_agera_pll_ops = {
#endif
};
EXPORT_SYMBOL(clk_agera_pll_ops);

static int clk_alpha_pll_slew_update(struct clk_alpha_pll *pll)
{
	int ret = 0;
	u32 val;

	regmap_update_bits(pll->clkr.regmap, PLL_MODE(pll),
					PLL_UPDATE, PLL_UPDATE);
	regmap_read(pll->clkr.regmap, PLL_MODE(pll), &val);

	ret = wait_for_pll_update(pll);
	if (ret)
		return ret;
	/*
	 * HPG mandates a wait of at least 570ns before polling the LOCK
	 * detect bit. Have a delay of 1us just to be safe.
	 */
	mb();
	udelay(1);

	ret = wait_for_pll_enable_lock(pll);

	return ret;
}

static int clk_alpha_pll_slew_set_rate(struct clk_hw *hw, unsigned long rate,
			unsigned long parent_rate)
{
	struct clk_alpha_pll *pll = to_clk_alpha_pll(hw);
	unsigned long freq_hz;
	const struct pll_vco *curr_vco, *vco;
	u32 l, alpha_width = pll_alpha_width(pll);
	u64 a;

	freq_hz =  alpha_pll_round_rate(rate, parent_rate, &l, &a, alpha_width);
	if (freq_hz != rate) {
		pr_err("alpha_pll: Call clk_set_rate with rounded rates!\n");
		return -EINVAL;
	}

	curr_vco = alpha_pll_find_vco(pll, clk_hw_get_rate(hw));
	if (!curr_vco) {
		pr_err("alpha pll: not in a valid vco range\n");
		return -EINVAL;
	}

	vco = alpha_pll_find_vco(pll, freq_hz);
	if (!vco) {
		pr_err("alpha pll: not in a valid vco range\n");
		return -EINVAL;
	}

	/*
	 * Dynamic pll update will not support switching frequencies across
	 * vco ranges. In those cases fall back to normal alpha set rate.
	 */
	if (curr_vco->val != vco->val)
		return clk_alpha_pll_set_rate(hw, rate, parent_rate);

	a = a << (ALPHA_REG_BITWIDTH - ALPHA_BITWIDTH);

	regmap_write(pll->clkr.regmap, PLL_L_VAL(pll), l);
	regmap_write(pll->clkr.regmap, PLL_ALPHA_VAL(pll), a);
	regmap_write(pll->clkr.regmap, PLL_ALPHA_VAL_U(pll), a >> 32);

	/* Ensure that the write above goes through before proceeding. */
	mb();

	if (clk_hw_is_enabled(hw)) {
		if (pll->flags & SUPPORTS_DYNAMIC_UPDATE)
			clk_alpha_pll_dynamic_update(pll);
		else
			clk_alpha_pll_slew_update(pll);
	}

	return 0;
}

/*
 * Slewing plls should be bought up at frequency which is in the middle of the
 * desired VCO range. So after bringing up the pll at calibration freq, set it
 * back to desired frequency(that was set by previous clk_set_rate).
 */
static int clk_alpha_pll_calibrate(struct clk_hw *hw)
{
	unsigned long calibration_freq, freq_hz;
	struct clk_alpha_pll *pll = to_clk_alpha_pll(hw);
	struct clk_hw *parent;
	const struct pll_vco *vco;
	u64 a;
	u32 l, alpha_width = pll_alpha_width(pll);
	int rc;

	parent = clk_hw_get_parent(hw);
	if (!parent) {
		pr_err("alpha pll: no valid parent found\n");
		return -EINVAL;
	}

	vco = alpha_pll_find_vco(pll, clk_hw_get_rate(hw));
	if (!vco) {
		pr_err("alpha pll: not in a valid vco range\n");
		return -EINVAL;
	}

	/*
	 * As during slewing plls vco_sel won't be allowed to change, vco table
	 * should have only one entry table, i.e. index = 0, find the
	 * calibration frequency.
	 */
	calibration_freq = (pll->vco_table[0].min_freq +
					pll->vco_table[0].max_freq)/2;

	freq_hz = alpha_pll_round_rate(calibration_freq,
			clk_hw_get_rate(parent), &l, &a, alpha_width);
	if (freq_hz != calibration_freq) {
		pr_err("alpha_pll: call clk_set_rate with rounded rates!\n");
		return -EINVAL;
	}

	/* Setup PLL for calibration frequency */
	a <<= (ALPHA_REG_BITWIDTH - ALPHA_BITWIDTH);

	regmap_write(pll->clkr.regmap, PLL_L_VAL(pll), l);
	regmap_write(pll->clkr.regmap, PLL_ALPHA_VAL(pll), a);
	regmap_write(pll->clkr.regmap, PLL_ALPHA_VAL_U(pll), a >> 32);

	regmap_update_bits(pll->clkr.regmap, PLL_USER_CTL(pll),
				PLL_VCO_MASK << PLL_VCO_SHIFT,
				vco->val << PLL_VCO_SHIFT);

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

	/* Bringup the pll at calibration frequency */
	rc = clk_alpha_pll_enable(hw);
	if (rc) {
		pr_err("alpha pll calibration failed\n");
		return rc;
	}

	/*
	 * PLL is already running at calibration frequency.
	 * So slew pll to the previously set frequency.
	 */
	freq_hz = alpha_pll_round_rate(clk_hw_get_rate(hw),
			clk_hw_get_rate(parent), &l, &a, alpha_width);


	pr_debug("pll %s: setting back to required rate %lu, freq_hz %ld\n",
				hw->init->name, clk_hw_get_rate(hw), freq_hz);

	/* Setup the PLL for the new frequency */
	a <<= (ALPHA_REG_BITWIDTH - ALPHA_BITWIDTH);

	regmap_write(pll->clkr.regmap, PLL_L_VAL(pll), l);
	regmap_write(pll->clkr.regmap, PLL_ALPHA_VAL(pll), a);
	regmap_write(pll->clkr.regmap, PLL_ALPHA_VAL_U(pll), a >> 32);

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

	if (pll->flags & SUPPORTS_DYNAMIC_UPDATE)
		return clk_alpha_pll_dynamic_update(pll);
	else
		return clk_alpha_pll_slew_update(pll);
}

static int clk_alpha_pll_slew_enable(struct clk_hw *hw)
{
	int rc;

	rc = clk_alpha_pll_calibrate(hw);
	if (rc)
		return rc;

	rc = clk_alpha_pll_enable(hw);

	return rc;
}

const struct clk_ops clk_alpha_pll_slew_ops = {
	.prepare = clk_prepare_regmap,
	.unprepare = clk_unprepare_regmap,
	.pre_rate_change = clk_pre_change_regmap,
	.post_rate_change = clk_post_change_regmap,
	.enable = clk_alpha_pll_slew_enable,
	.disable = clk_alpha_pll_disable,
	.recalc_rate = clk_alpha_pll_recalc_rate,
	.round_rate = clk_alpha_pll_round_rate,
	.set_rate = clk_alpha_pll_slew_set_rate,
	.init = clk_alpha_pll_init,
	.debug_init = clk_common_debug_init,
#ifdef CONFIG_COMMON_CLK_QCOM_DEBUG
	.list_rate_vdd_level = clk_list_rate_vdd_level,
#endif
};
+20 −0
Original line number Diff line number Diff line
@@ -54,6 +54,11 @@ enum {

extern const u8 clk_alpha_pll_regs[CLK_ALPHA_PLL_TYPE_MAX][PLL_OFF_MAX_REGS];

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

struct pll_vco {
	unsigned long min_freq;
	unsigned long max_freq;
@@ -65,6 +70,7 @@ struct pll_vco {
 * @offset: base address of registers
 * @vco_table: array of VCO settings
 * @regs: alpha pll register map (see @clk_alpha_pll_regs)
 * @vco_data: array of VCO data settings like post div
 * @clkr: regmap clock handle
 */
struct clk_alpha_pll {
@@ -73,13 +79,24 @@ struct clk_alpha_pll {
	struct alpha_pll_config *config;
	const struct pll_vco *vco_table;
	size_t num_vco;

	const struct pll_vco_data *vco_data;
	size_t num_vco_data;

#define SUPPORTS_OFFLINE_REQ	BIT(0)
#define SUPPORTS_FSM_MODE	BIT(2)
	/*
	 * Some PLLs support dynamically updating their rate without disabling
	 * the PLL first. Set this flag to enable this support.
	 */

#define SUPPORTS_DYNAMIC_UPDATE	BIT(3)
#define SUPPORTS_FSM_LEGACY_MODE BIT(4)
#define SUPPORTS_SLEW           BIT(4)
	u8 flags;

	struct clk_regmap clkr;
	unsigned long min_supported_freq;
};

/**
@@ -127,6 +144,8 @@ struct alpha_pll_config {
	u32 pre_div_mask;
	u32 post_div_val;
	u32 post_div_mask;
	u32 test_ctl_mask;
	u32 test_ctl_hi_mask;
	u32 vco_val;
	u32 vco_mask;
	const u32 *custom_reg_offset;
@@ -155,6 +174,7 @@ extern const struct clk_ops clk_alpha_pll_zonda_5lpe_ops;
extern const struct clk_ops clk_alpha_pll_lucid_5lpe_ops;
extern const struct clk_ops clk_alpha_pll_fixed_lucid_5lpe_ops;
extern const struct clk_ops clk_alpha_pll_postdiv_lucid_5lpe_ops;
extern const struct clk_ops clk_alpha_pll_slew_ops;

extern const struct clk_ops clk_trion_fixed_pll_ops;
extern const struct clk_ops clk_trion_pll_postdiv_ops;