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

Commit d9226e13 authored by Vivek Aknurwar's avatar Vivek Aknurwar Committed by David Collins
Browse files

clk: qcom: clk-alpha-pll: Add support for controlling zonda pll



Zonda PLL is high frequency PLL clock generator. It supports
fine control of the digital controlled oscillator (DCO)
and clocked on rising edge of the reference.

Change-Id: I2a9726a0acbffcee1a52f5172b197bf125e60030
Signed-off-by: default avatarVivek Aknurwar <viveka@codeaurora.org>
Signed-off-by: default avatarDavid Collins <collinsd@codeaurora.org>
parent 6a578b72
Loading
Loading
Loading
Loading
+274 −28
Original line number Diff line number Diff line
// SPDX-License-Identifier: GPL-2.0
// SPDX-License-Identifier: GPL-2.0-only
/*
 * Copyright (c) 2015, 2018, The Linux Foundation. All rights reserved.
 * Copyright (c) 2015-2016, 2018, The Linux Foundation. All rights reserved.
 */

#include <linux/kernel.h>
@@ -116,6 +116,20 @@ const u8 clk_alpha_pll_regs[][PLL_OFF_MAX_REGS] = {
		[PLL_OFF_OPMODE] = 0x38,
		[PLL_OFF_ALPHA_VAL] = 0x40,
	},
	[CLK_ALPHA_PLL_TYPE_ZONDA] =  {
		[PLL_OFF_L_VAL] = 0x04,
		[PLL_OFF_ALPHA_VAL] = 0x08,
		[PLL_OFF_USER_CTL] = 0x0c,
		[PLL_OFF_CONFIG_CTL] = 0x10,
		[PLL_OFF_CONFIG_CTL_U] = 0x14,
		[PLL_OFF_CONFIG_CTL_U1] = 0x18,
		[PLL_OFF_TEST_CTL] = 0x1c,
		[PLL_OFF_TEST_CTL_U] = 0x20,
		[PLL_OFF_TEST_CTL_U1] = 0x24,
		[PLL_OFF_OPMODE] = 0x28,
		[PLL_OFF_STATUS] = 0x38,
	},

};
EXPORT_SYMBOL_GPL(clk_alpha_pll_regs);

@@ -144,6 +158,9 @@ EXPORT_SYMBOL_GPL(clk_alpha_pll_regs);
#define LUCID_PLL_CAL_VAL	0x44
#define LUCID_PCAL_DONE		BIT(26)

/* ZONDA PLL specific offsets */
#define ZONDA_PLL_OUT_MASK	0xF

#define pll_alpha_width(p)					\
		((PLL_ALPHA_VAL_U(p) - PLL_ALPHA_VAL(p) == 4) ?	\
				 ALPHA_REG_BITWIDTH : ALPHA_REG_16BIT_WIDTH)
@@ -754,6 +771,239 @@ static long alpha_pll_huayra_round_rate(struct clk_hw *hw, unsigned long rate,
	return alpha_huayra_pll_round_rate(rate, *prate, &l, &a);
}

static int clk_zonda_pll_is_enabled(struct clk_hw *hw)
{
	struct clk_alpha_pll *pll = to_clk_alpha_pll(hw);
	u32 mode_regval, opmode_regval;
	int ret;

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

	return (opmode_regval & PLL_OPMODE_RUN) &&
		(mode_regval & PLL_OUTCTRL);
}

void clk_zonda_pll_configure(struct clk_alpha_pll *pll, struct regmap *regmap,
				const struct alpha_pll_config *config)
{
	if (config->l)
		regmap_write(regmap, PLL_L_VAL(pll), config->l);

	if (config->alpha)
		regmap_write(regmap, PLL_ALPHA_VAL(pll), config->alpha);

	if (config->config_ctl_val)
		regmap_write(regmap, PLL_CONFIG_CTL(pll),
				config->config_ctl_val);

	if (config->config_ctl_hi_val)
		regmap_write(regmap, PLL_CONFIG_CTL_U(pll),
				config->config_ctl_hi_val);

	if (config->config_ctl_hi1_val)
		regmap_write(regmap, PLL_CONFIG_CTL_U1(pll),
				config->config_ctl_hi1_val);

	if (config->user_ctl_val)
		regmap_write(regmap, PLL_USER_CTL(pll),
				config->user_ctl_val);

	if (config->user_ctl_hi_val)
		regmap_write(regmap, PLL_USER_CTL_U(pll),
				config->user_ctl_hi_val);

	if (config->user_ctl_hi1_val)
		regmap_write(regmap, PLL_USER_CTL_U1(pll),
				config->user_ctl_hi1_val);

	if (config->test_ctl_val)
		regmap_write(regmap, PLL_TEST_CTL(pll),
				config->test_ctl_val);

	if (config->test_ctl_hi_val)
		regmap_write(regmap, PLL_TEST_CTL_U(pll),
				config->test_ctl_hi_val);

	if (config->test_ctl_hi1_val)
		regmap_write(regmap, PLL_TEST_CTL_U1(pll),
				config->test_ctl_hi1_val);

	regmap_update_bits(regmap, PLL_MODE(pll),
			 PLL_UPDATE_BYPASS,
			 PLL_UPDATE_BYPASS);

	/* Disable PLL output */
	regmap_update_bits(regmap, PLL_MODE(pll),
			 PLL_OUTCTRL, 0);

	/* Set operation mode to OFF */
	regmap_write(regmap, PLL_OPMODE(pll), PLL_OPMODE_STANDBY);

	/* PLL should be in OFF mode before continuing */
	wmb();

	/* Place the PLL in STANDBY mode */
	regmap_update_bits(regmap, PLL_MODE(pll),
				 PLL_RESET_N, PLL_RESET_N);
	pll->inited = true;
}

static int clk_zonda_pll_enable(struct clk_hw *hw)
{
	struct clk_alpha_pll *pll = to_clk_alpha_pll(hw);
	u32 val;
	int ret;

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

	/* If in FSM mode, just vote for it */
	if (val & PLL_VOTE_FSM_ENA) {
		ret = clk_enable_regmap(hw);
		if (ret)
			return ret;
		return wait_for_pll_enable_active(pll);
	}

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

	/* Get the PLL out of bypass mode */
	ret = regmap_update_bits(pll->clkr.regmap, PLL_MODE(pll),
						PLL_BYPASSNL, PLL_BYPASSNL);
	if (ret)
		return ret;

	/*
	 * H/W requires a 1us delay between disabling the bypass and
	 * de-asserting the reset.
	 */
	mb();
	udelay(1);

	ret = regmap_update_bits(pll->clkr.regmap, PLL_MODE(pll),
						 PLL_RESET_N, PLL_RESET_N);
	if (ret)
		return ret;

	/* Set operation mode to RUN */
	regmap_write(pll->clkr.regmap, PLL_OPMODE(pll),
						PLL_OPMODE_RUN);

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

	/* Enable the PLL outputs */
	ret = regmap_update_bits(pll->clkr.regmap, PLL_USER_CTL(pll),
				ZONDA_PLL_OUT_MASK, ZONDA_PLL_OUT_MASK);
	if (ret)
		return ret;

	/* Enable the global PLL outputs */
	ret = regmap_update_bits(pll->clkr.regmap, PLL_MODE(pll),
				 PLL_OUTCTRL, PLL_OUTCTRL);
	if (ret)
		return ret;

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

	return 0;
}

static void clk_zonda_pll_disable(struct clk_hw *hw)
{
	struct clk_alpha_pll *pll = to_clk_alpha_pll(hw);
	u32 val, mask, off = pll->offset;
	int ret;

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

	/* If in FSM mode, just unvote it */
	if (val & PLL_VOTE_FSM_ENA) {
		clk_disable_regmap(hw);
		return;
	}

	/* Disable the global PLL output */
	ret = regmap_update_bits(pll->clkr.regmap, PLL_MODE(pll),
							PLL_OUTCTRL, 0);
	if (ret)
		return;

	/* Disable the PLL outputs */
	ret = regmap_update_bits(pll->clkr.regmap, PLL_USER_CTL(pll),
					ZONDA_PLL_OUT_MASK, 0);

	/* Put the PLL in bypass and reset */
	mask = PLL_RESET_N | PLL_BYPASSNL;
	ret = regmap_update_bits(pll->clkr.regmap, PLL_MODE(pll), mask, 0);
	if (ret)
		return;

	/* Place the PLL mode in OFF state */
	regmap_write(pll->clkr.regmap, PLL_OPMODE(pll),
			0x0);
}

static int clk_zonda_pll_set_rate(struct clk_hw *hw, unsigned long rate,
				  unsigned long prate)
{
	struct clk_alpha_pll *pll = to_clk_alpha_pll(hw);
	unsigned long rrate;
	u32 l;
	u64 a;
	int ret;

	rrate = alpha_pll_round_rate(rate, prate, &l, &a, ALPHA_BITWIDTH);
	/*
	 * Due to a limited number of bits for fractional rate programming, the
	 * rounded up rate could be marginally higher than the requested rate.
	 */
	if (rrate > (rate + PLL_OUT_RATE_MARGIN) || rrate < rate) {
		pr_err("Requested rate (%lu) not matching the PLL's supported frequency (%lu)\n",
				rate, rrate);
		return -EINVAL;
	}

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

	/* Wait before polling for the frequency latch */
	udelay(5);

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

	/* Wait for PLL output to stabilize */
	udelay(100);
	return 0;
}

static unsigned long
clk_zonda_pll_recalc_rate(struct clk_hw *hw, unsigned long parent_rate)
{
	struct clk_alpha_pll *pll = to_clk_alpha_pll(hw);
	u32 l, frac;

	regmap_read(pll->clkr.regmap, PLL_L_VAL(pll), &l);
	regmap_read(pll->clkr.regmap, PLL_ALPHA_VAL(pll), &frac);

	return alpha_pll_calc_rate(parent_rate, l, frac, ALPHA_BITWIDTH);
}

const struct clk_ops clk_alpha_pll_ops = {
	.enable = clk_alpha_pll_enable,
	.disable = clk_alpha_pll_disable,
@@ -784,6 +1034,16 @@ const struct clk_ops clk_alpha_pll_hwfsm_ops = {
};
EXPORT_SYMBOL_GPL(clk_alpha_pll_hwfsm_ops);

const struct clk_ops clk_alpha_pll_zonda_ops = {
	.enable = clk_zonda_pll_enable,
	.disable = clk_zonda_pll_disable,
	.is_enabled = clk_zonda_pll_is_enabled,
	.recalc_rate = clk_zonda_pll_recalc_rate,
	.round_rate = clk_alpha_pll_round_rate,
	.set_rate = clk_zonda_pll_set_rate,
};
EXPORT_SYMBOL(clk_alpha_pll_zonda_ops);

static unsigned long
clk_alpha_pll_postdiv_recalc_rate(struct clk_hw *hw, unsigned long parent_rate)
{
@@ -1138,6 +1398,13 @@ const struct clk_ops clk_alpha_pll_postdiv_fabia_ops = {
};
EXPORT_SYMBOL_GPL(clk_alpha_pll_postdiv_fabia_ops);

const struct clk_ops clk_alpha_pll_postdiv_zonda_ops = {
	.recalc_rate = clk_alpha_pll_postdiv_fabia_recalc_rate,
	.round_rate = clk_alpha_pll_postdiv_fabia_round_rate,
	.set_rate = clk_alpha_pll_postdiv_fabia_set_rate,
};
EXPORT_SYMBOL(clk_alpha_pll_postdiv_zonda_ops);

static int lucid_pll_is_enabled(struct clk_alpha_pll *pll,
					struct regmap *regmap)
{
@@ -1153,27 +1420,17 @@ static int lucid_pll_is_enabled(struct clk_alpha_pll *pll,
		(mode_regval & PLL_OUTCTRL));
}

int clk_lucid_pll_configure(struct clk_alpha_pll *pll, struct regmap *regmap,
void clk_lucid_pll_configure(struct clk_alpha_pll *pll, struct regmap *regmap,
				const struct alpha_pll_config *config)
{
	int ret;

	if (lucid_pll_is_enabled(pll, regmap)) {
		pr_warn("PLL is already enabled. Skipping configuration.\n");
		return 0;
	}

	/*
	 * Disable the PLL if it's already been initialized. Not doing so might
	 * lead to the PLL running with the old frequency configuration.
	 */
	if (pll->inited) {
		ret = regmap_update_bits(regmap, PLL_MODE(pll),
		regmap_update_bits(regmap, PLL_MODE(pll),
						PLL_RESET_N, 0);
		if (ret)
			return ret;
	}

	if (config->l)
		regmap_write(regmap, PLL_L_VAL(pll), config->l);

@@ -1225,11 +1482,8 @@ int clk_lucid_pll_configure(struct clk_alpha_pll *pll, struct regmap *regmap,
				 PLL_UPDATE_BYPASS);

	/* Disable PLL output */
	ret = regmap_update_bits(regmap, PLL_MODE(pll),
	regmap_update_bits(regmap, PLL_MODE(pll),
					PLL_OUTCTRL, 0);
	if (ret)
		return ret;

	/* Set operation mode to OFF */
	regmap_write(regmap, PLL_OPMODE(pll), PLL_OPMODE_STANDBY);

@@ -1237,13 +1491,9 @@ int clk_lucid_pll_configure(struct clk_alpha_pll *pll, struct regmap *regmap,
	wmb();

	/* Place the PLL in STANDBY mode */
	ret = regmap_update_bits(regmap, PLL_MODE(pll),
	regmap_update_bits(regmap, PLL_MODE(pll),
				 PLL_RESET_N, PLL_RESET_N);
	if (ret)
		return ret;

	pll->inited = true;
	return 0;
}

static int alpha_pll_lucid_enable(struct clk_hw *hw)
@@ -1265,12 +1515,8 @@ static int alpha_pll_lucid_enable(struct clk_hw *hw)
	}

	if (unlikely(!pll->inited)) {
		ret = clk_lucid_pll_configure(pll, pll->clkr.regmap,
		clk_lucid_pll_configure(pll, pll->clkr.regmap,
						pll->config);
		if (ret) {
			pr_err("Failed to configure %s\n", clk_hw_get_name(hw));
			return ret;
		}
	}

	/* Set operation mode to RUN */
+10 −5
Original line number Diff line number Diff line
/* SPDX-License-Identifier: GPL-2.0 */
/* Copyright (c) 2015, 2018, The Linux Foundation. All rights reserved. */
/* SPDX-License-Identifier: GPL-2.0-only */
/* Copyright (c) 2015-2016, 2018, The Linux Foundation. All rights reserved. */

#ifndef __QCOM_CLK_ALPHA_PLL_H__
#define __QCOM_CLK_ALPHA_PLL_H__
@@ -14,6 +14,7 @@ enum {
	CLK_ALPHA_PLL_TYPE_BRAMMO,
	CLK_ALPHA_PLL_TYPE_FABIA,
	CLK_ALPHA_PLL_TYPE_LUCID,
	CLK_ALPHA_PLL_TYPE_ZONDA,
	CLK_ALPHA_PLL_TYPE_MAX,
};

@@ -58,7 +59,6 @@ struct clk_alpha_pll {
	const u8 *regs;
	struct alpha_pll_config *config;
	bool inited;

	const struct pll_vco *vco_table;
	size_t num_vco;
#define SUPPORTS_OFFLINE_REQ	BIT(0)
@@ -84,7 +84,6 @@ struct clk_alpha_pll_postdiv {
	u32 offset;
	u8 width;
	const u8 *regs;

	struct clk_regmap clkr;
	int post_div_shift;
	const struct clk_div_table *post_div_table;
@@ -133,10 +132,16 @@ extern const struct clk_ops clk_alpha_pll_lucid_ops;
extern const struct clk_ops clk_alpha_pll_fixed_lucid_ops;
extern const struct clk_ops clk_alpha_pll_postdiv_lucid_ops;

extern const struct clk_ops clk_alpha_pll_zonda_ops;
extern const struct clk_ops clk_alpha_pll_postdiv_zonda_ops;

void clk_alpha_pll_configure(struct clk_alpha_pll *pll, struct regmap *regmap,
			     const struct alpha_pll_config *config);
void clk_fabia_pll_configure(struct clk_alpha_pll *pll, struct regmap *regmap,
				const struct alpha_pll_config *config);
int clk_lucid_pll_configure(struct clk_alpha_pll *pll, struct regmap *regmap,
void clk_lucid_pll_configure(struct clk_alpha_pll *pll, struct regmap *regmap,
				const struct alpha_pll_config *config);
void clk_zonda_pll_configure(struct clk_alpha_pll *pll, struct regmap *regmap,
				const struct alpha_pll_config *config);

#endif