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

Commit 595ba272 authored by Linux Build Service Account's avatar Linux Build Service Account Committed by Gerrit - the friendly Code Review server
Browse files

Merge "clock-alpha-pll: Add FSM voting mode support"

parents a6f82f0a 85f788b7
Loading
Loading
Loading
Loading
+137 −14
Original line number Diff line number Diff line
@@ -25,12 +25,14 @@

#define MODE_REG(pll) (*pll->base + pll->offset + 0x0)
#define LOCK_REG(pll) (*pll->base + pll->offset + 0x0)
#define ACTIVE_REG(pll) (*pll->base + pll->offset + 0x0)
#define UPDATE_REG(pll) (*pll->base + pll->offset + 0x0)
#define L_REG(pll) (*pll->base + pll->offset + 0x4)
#define A_REG(pll) (*pll->base + pll->offset + 0x8)
#define VCO_REG(pll) (*pll->base + pll->offset + 0x10)
#define ALPHA_EN_REG(pll) (*pll->base + pll->offset + 0x10)
#define OUTPUT_REG(pll) (*pll->base + pll->offset + 0x10)
#define VOTE_REG(pll) (*pll->base + pll->fsm_reg_offset)

#define PLL_BYPASSNL 0x2
#define PLL_RESET_N  0x4
@@ -42,6 +44,13 @@
#define ALPHA_REG_BITWIDTH 40
#define ALPHA_BITWIDTH 32

/*
 * Enable/disable registers could be shared among PLLs when FSM voting
 * is used. This lock protects against potential race when multiple
 * PLLs are being enabled/disabled together.
 */
static DEFINE_SPINLOCK(alpha_pll_reg_lock);

static unsigned long compute_rate(struct alpha_pll_clk *pll,
				u32 l_val, u32 a_val)
{
@@ -60,10 +69,59 @@ static bool is_locked(struct alpha_pll_clk *pll)
	return (reg & mask) == mask;
}

static int alpha_pll_enable(struct clk *c)
static bool is_active(struct alpha_pll_clk *pll)
{
	u32 reg = readl_relaxed(ACTIVE_REG(pll));
	u32 mask = pll->masks->active_mask;
	return (reg & mask) == mask;
}

/*
 * Check active_flag if PLL is in FSM mode, otherwise check lock_det
 * bit. This function assumes PLLs are already configured to the
 * right mode.
 */
static bool update_finish(struct alpha_pll_clk *pll)
{
	if (pll->fsm_en_mask)
		return is_active(pll);
	else
		return is_locked(pll);
}

static int wait_for_update(struct alpha_pll_clk *pll)
{
	struct alpha_pll_clk *pll = to_alpha_pll_clk(c);
	int count;

	for (count = WAIT_MAX_LOOPS; count > 0; count--) {
		if (update_finish(pll))
			break;
		udelay(1);
	}

	if (!count) {
		pr_err("%s didn't lock after enabling it!\n", pll->c.dbg_name);
		return -EINVAL;
	}

	return 0;
}

static int __alpha_pll_vote_enable(struct alpha_pll_clk *pll)
{
	u32 ena;

	ena = readl_relaxed(VOTE_REG(pll));
	ena |= pll->fsm_en_mask;
	writel_relaxed(ena, VOTE_REG(pll));
	mb();

	return wait_for_update(pll);
}

static int __alpha_pll_enable(struct alpha_pll_clk *pll)
{
	int rc;
	u32 mode;

	mode  = readl_relaxed(MODE_REG(pll));
@@ -80,17 +138,9 @@ static int alpha_pll_enable(struct clk *c)
	mode |= PLL_RESET_N;
	writel_relaxed(mode, MODE_REG(pll));

	/* Wait for pll to lock. */
	for (count = WAIT_MAX_LOOPS; count > 0; count--) {
		if (is_locked(pll))
			break;
		udelay(1);
	}

	if (!count) {
		pr_err("%s didn't lock after enabling it!\n", c->dbg_name);
		return -EINVAL;
	}
	rc = wait_for_update(pll);
	if (rc < 0)
		return rc;

	/* Enable PLL output. */
	mode |= PLL_OUTCTRL;
@@ -101,9 +151,33 @@ static int alpha_pll_enable(struct clk *c)
	return 0;
}

static void alpha_pll_disable(struct clk *c)
static int alpha_pll_enable(struct clk *c)
{
	struct alpha_pll_clk *pll = to_alpha_pll_clk(c);
	unsigned long flags;
	int rc;

	spin_lock_irqsave(&alpha_pll_reg_lock, flags);
	if (pll->fsm_en_mask)
		rc = __alpha_pll_vote_enable(pll);
	else
		rc = __alpha_pll_enable(pll);
	spin_unlock_irqrestore(&alpha_pll_reg_lock, flags);

	return rc;
}

static void __alpha_pll_vote_disable(struct alpha_pll_clk *pll)
{
	u32 ena;

	ena = readl_relaxed(VOTE_REG(pll));
	ena &= ~pll->fsm_en_mask;
	writel_relaxed(ena, VOTE_REG(pll));
}

static void __alpha_pll_disable(struct alpha_pll_clk *pll)
{
	u32 mode;

	mode = readl_relaxed(MODE_REG(pll));
@@ -118,6 +192,19 @@ static void alpha_pll_disable(struct clk *c)
	writel_relaxed(mode, MODE_REG(pll));
}

static void alpha_pll_disable(struct clk *c)
{
	struct alpha_pll_clk *pll = to_alpha_pll_clk(c);
	unsigned long flags;

	spin_lock_irqsave(&alpha_pll_reg_lock, flags);
	if (pll->fsm_en_mask)
		__alpha_pll_vote_disable(pll);
	else
		__alpha_pll_disable(pll);
	spin_unlock_irqrestore(&alpha_pll_reg_lock, flags);
}

static u32 find_vco(struct alpha_pll_clk *pll, unsigned long rate)
{
	unsigned long i;
@@ -269,6 +356,38 @@ static void update_vco_tbl(struct alpha_pll_clk *pll)
	}
}

/*
 * Program bias count to be 0x6 (corresponds to 5us), and lock count
 * bits to 0 (check lock_det for locking).
 */
static void __set_fsm_mode(void __iomem *mode_reg)
{
	u32 regval = readl_relaxed(mode_reg);

	/* De-assert reset to FSM */
	regval &= ~BIT(21);
	writel_relaxed(regval, mode_reg);

	/* Program bias count */
	regval &= ~BM(19, 14);
	regval |= BVAL(19, 14, 0x6);
	writel_relaxed(regval, mode_reg);

	/* Program lock count */
	regval &= ~BM(13, 8);
	regval |= BVAL(13, 8, 0x0);
	writel_relaxed(regval, mode_reg);

	/* Enable PLL FSM voting */
	regval |= BIT(20);
	writel_relaxed(regval, mode_reg);
}

static bool is_fsm_mode(void __iomem *mode_reg)
{
	return !!(readl_relaxed(mode_reg) & BIT(20));
}

static enum handoff alpha_pll_handoff(struct clk *c)
{
	struct alpha_pll_clk *pll = to_alpha_pll_clk(c);
@@ -288,7 +407,11 @@ static enum handoff alpha_pll_handoff(struct clk *c)
			output_en |= pll->enable_config;
			writel_relaxed(output_en, OUTPUT_REG(pll));
		}
		if (pll->fsm_en_mask)
			__set_fsm_mode(MODE_REG(pll));
		return HANDOFF_DISABLED_CLK;
	} else if (pll->fsm_en_mask && !is_fsm_mode(MODE_REG(pll))) {
		WARN(1, "%s should be in FSM mode but is not\n", c->dbg_name);
	}

	l_val = readl_relaxed(L_REG(pll));
+11 −5
Original line number Diff line number Diff line
@@ -19,12 +19,13 @@
#include <linux/clk/msm-clk-provider.h>

struct alpha_pll_masks {
	u32 lock_mask;
	u32 update_mask;
	u32 vco_mask;
	u32 lock_mask;		/* lock_det bit */
	u32 active_mask;	/* active_flag in FSM mode */
	u32 update_mask;	/* update bit for dynamic update */
	u32 vco_mask;		/* vco_sel bits */
	u32 vco_shift;
	u32 alpha_en_mask;
	u32 output_mask;
	u32 alpha_en_mask;	/* alpha_en bit */
	u32 output_mask;	/* pllout_* bits */
};

struct alpha_pll_vco_tbl {
@@ -43,6 +44,11 @@ struct alpha_pll_clk {
	struct alpha_pll_masks *masks;
	void *const __iomem *base;
	const u32 offset;

	/* if fsm_en_mask is set, config PLL to FSM mode */
	const u32 fsm_reg_offset;
	const u32 fsm_en_mask;

	u32 enable_config;	/* bitmask of outputs to be enabled */

	struct alpha_pll_vco_tbl *vco_tbl;