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

Commit ae3669ac authored by Stephen Boyd's avatar Stephen Boyd
Browse files

clk: qcom: Add support for setting rates on PLLs



Some PLLs may require changing their rate at runtime. Add support
for these PLLs.

Signed-off-by: default avatarStephen Boyd <sboyd@codeaurora.org>
parent 50c6a503
Loading
Loading
Loading
Loading
+67 −1
Original line number Original line Diff line number Diff line
@@ -97,7 +97,7 @@ static unsigned long
clk_pll_recalc_rate(struct clk_hw *hw, unsigned long parent_rate)
clk_pll_recalc_rate(struct clk_hw *hw, unsigned long parent_rate)
{
{
	struct clk_pll *pll = to_clk_pll(hw);
	struct clk_pll *pll = to_clk_pll(hw);
	u32 l, m, n;
	u32 l, m, n, config;
	unsigned long rate;
	unsigned long rate;
	u64 tmp;
	u64 tmp;


@@ -116,13 +116,79 @@ clk_pll_recalc_rate(struct clk_hw *hw, unsigned long parent_rate)
		do_div(tmp, n);
		do_div(tmp, n);
		rate += tmp;
		rate += tmp;
	}
	}
	if (pll->post_div_width) {
		regmap_read(pll->clkr.regmap, pll->config_reg, &config);
		config >>= pll->post_div_shift;
		config &= BIT(pll->post_div_width) - 1;
		rate /= config + 1;
	}

	return rate;
	return rate;
}
}


static const
struct pll_freq_tbl *find_freq(const struct pll_freq_tbl *f, unsigned long rate)
{
	if (!f)
		return NULL;

	for (; f->freq; f++)
		if (rate <= f->freq)
			return f;

	return NULL;
}

static long
clk_pll_determine_rate(struct clk_hw *hw, unsigned long rate,
		       unsigned long *p_rate, struct clk **p)
{
	struct clk_pll *pll = to_clk_pll(hw);
	const struct pll_freq_tbl *f;

	f = find_freq(pll->freq_tbl, rate);
	if (!f)
		return clk_pll_recalc_rate(hw, *p_rate);

	return f->freq;
}

static int
clk_pll_set_rate(struct clk_hw *hw, unsigned long rate, unsigned long p_rate)
{
	struct clk_pll *pll = to_clk_pll(hw);
	const struct pll_freq_tbl *f;
	bool enabled;
	u32 mode;
	u32 enable_mask = PLL_OUTCTRL | PLL_BYPASSNL | PLL_RESET_N;

	f = find_freq(pll->freq_tbl, rate);
	if (!f)
		return -EINVAL;

	regmap_read(pll->clkr.regmap, pll->mode_reg, &mode);
	enabled = (mode & enable_mask) == enable_mask;

	if (enabled)
		clk_pll_disable(hw);

	regmap_update_bits(pll->clkr.regmap, pll->l_reg, 0x3ff, f->l);
	regmap_update_bits(pll->clkr.regmap, pll->m_reg, 0x7ffff, f->m);
	regmap_update_bits(pll->clkr.regmap, pll->n_reg, 0x7ffff, f->n);
	regmap_write(pll->clkr.regmap, pll->config_reg, f->ibits);

	if (enabled)
		clk_pll_enable(hw);

	return 0;
}

const struct clk_ops clk_pll_ops = {
const struct clk_ops clk_pll_ops = {
	.enable = clk_pll_enable,
	.enable = clk_pll_enable,
	.disable = clk_pll_disable,
	.disable = clk_pll_disable,
	.recalc_rate = clk_pll_recalc_rate,
	.recalc_rate = clk_pll_recalc_rate,
	.determine_rate = clk_pll_determine_rate,
	.set_rate = clk_pll_set_rate,
};
};
EXPORT_SYMBOL_GPL(clk_pll_ops);
EXPORT_SYMBOL_GPL(clk_pll_ops);


+20 −0
Original line number Original line Diff line number Diff line
@@ -17,6 +17,21 @@
#include <linux/clk-provider.h>
#include <linux/clk-provider.h>
#include "clk-regmap.h"
#include "clk-regmap.h"


/**
 * struct pll_freq_tbl - PLL frequency table
 * @l: L value
 * @m: M value
 * @n: N value
 * @ibits: internal values
 */
struct pll_freq_tbl {
	unsigned long freq;
	u16 l;
	u16 m;
	u16 n;
	u32 ibits;
};

/**
/**
 * struct clk_pll - phase locked loop (PLL)
 * struct clk_pll - phase locked loop (PLL)
 * @l_reg: L register
 * @l_reg: L register
@@ -26,6 +41,7 @@
 * @mode_reg: mode register
 * @mode_reg: mode register
 * @status_reg: status register
 * @status_reg: status register
 * @status_bit: ANDed with @status_reg to determine if PLL is enabled
 * @status_bit: ANDed with @status_reg to determine if PLL is enabled
 * @freq_tbl: PLL frequency table
 * @hw: handle between common and hardware-specific interfaces
 * @hw: handle between common and hardware-specific interfaces
 */
 */
struct clk_pll {
struct clk_pll {
@@ -36,6 +52,10 @@ struct clk_pll {
	u32	mode_reg;
	u32	mode_reg;
	u32	status_reg;
	u32	status_reg;
	u8	status_bit;
	u8	status_bit;
	u8	post_div_width;
	u8	post_div_shift;

	const struct pll_freq_tbl *freq_tbl;


	struct clk_regmap clkr;
	struct clk_regmap clkr;
};
};