blob: 94ef15589c36a89637f2d02065ef2e424f663361 [file] [log] [blame]
/*
* Copyright (c) 2012-2015, 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
* only version 2 as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
*/
#define pr_fmt(fmt) "%s: " fmt, __func__
#include <linux/kernel.h>
#include <linux/delay.h>
#include <linux/err.h>
#include <linux/clk.h>
#include <linux/io.h>
#include <linux/sched.h>
#include <soc/qcom/clock-pll.h>
#include <soc/qcom/msm-clock-controller.h>
#include "clock.h"
#define PLL_OUTCTRL BIT(0)
#define PLL_BYPASSNL BIT(1)
#define PLL_RESET_N BIT(2)
#define PLL_MODE_MASK BM(3, 0)
#define PLL_EN_REG(x) (*(x)->base + (unsigned long) (x)->en_reg)
#define PLL_STATUS_REG(x) (*(x)->base + (unsigned long) (x)->status_reg)
#define PLL_ALT_STATUS_REG(x) (*(x)->base + (unsigned long) \
(x)->alt_status_reg)
#define PLL_MODE_REG(x) (*(x)->base + (unsigned long) (x)->mode_reg)
#define PLL_L_REG(x) (*(x)->base + (unsigned long) (x)->l_reg)
#define PLL_M_REG(x) (*(x)->base + (unsigned long) (x)->m_reg)
#define PLL_N_REG(x) (*(x)->base + (unsigned long) (x)->n_reg)
#define PLL_CONFIG_REG(x) (*(x)->base + (unsigned long) (x)->config_reg)
#define PLL_ALPHA_REG(x) (*(x)->base + (unsigned long) (x)->alpha_reg)
#define PLL_CFG_ALT_REG(x) (*(x)->base + (unsigned long) \
(x)->config_alt_reg)
#define PLL_CFG_CTL_REG(x) (*(x)->base + (unsigned long) \
(x)->config_ctl_reg)
#define PLL_TEST_CTL_LO_REG(x) (*(x)->base + (unsigned long) \
(x)->test_ctl_lo_reg)
#define PLL_TEST_CTL_HI_REG(x) (*(x)->base + (unsigned long) \
(x)->test_ctl_hi_reg)
static DEFINE_SPINLOCK(pll_reg_lock);
#define ENABLE_WAIT_MAX_LOOPS 200
#define PLL_LOCKED_BIT BIT(16)
static int pll_vote_clk_enable(struct clk *c)
{
u32 ena, count;
unsigned long flags;
struct pll_vote_clk *pllv = to_pll_vote_clk(c);
spin_lock_irqsave(&pll_reg_lock, flags);
ena = readl_relaxed(PLL_EN_REG(pllv));
ena |= pllv->en_mask;
writel_relaxed(ena, PLL_EN_REG(pllv));
spin_unlock_irqrestore(&pll_reg_lock, flags);
/*
* Use a memory barrier since some PLL status registers are
* not within the same 1K segment as the voting registers.
*/
mb();
/* Wait for pll to enable. */
for (count = ENABLE_WAIT_MAX_LOOPS; count > 0; count--) {
if (readl_relaxed(PLL_STATUS_REG(pllv)) & pllv->status_mask)
return 0;
udelay(1);
}
WARN("PLL %s didn't enable after voting for it!\n", c->dbg_name);
return -ETIMEDOUT;
}
static void pll_vote_clk_disable(struct clk *c)
{
u32 ena;
unsigned long flags;
struct pll_vote_clk *pllv = to_pll_vote_clk(c);
spin_lock_irqsave(&pll_reg_lock, flags);
ena = readl_relaxed(PLL_EN_REG(pllv));
ena &= ~(pllv->en_mask);
writel_relaxed(ena, PLL_EN_REG(pllv));
spin_unlock_irqrestore(&pll_reg_lock, flags);
}
static int pll_vote_clk_is_enabled(struct clk *c)
{
struct pll_vote_clk *pllv = to_pll_vote_clk(c);
return !!(readl_relaxed(PLL_STATUS_REG(pllv)) & pllv->status_mask);
}
static enum handoff pll_vote_clk_handoff(struct clk *c)
{
struct pll_vote_clk *pllv = to_pll_vote_clk(c);
if (readl_relaxed(PLL_EN_REG(pllv)) & pllv->en_mask)
return HANDOFF_ENABLED_CLK;
return HANDOFF_DISABLED_CLK;
}
static void __iomem *pll_vote_clk_list_registers(struct clk *c, int n,
struct clk_register_data **regs, u32 *size)
{
struct pll_vote_clk *pllv = to_pll_vote_clk(c);
static struct clk_register_data data1[] = {
{"APPS_VOTE", 0x0},
};
if (n)
return ERR_PTR(-EINVAL);
*regs = data1;
*size = ARRAY_SIZE(data1);
return PLL_EN_REG(pllv);
}
struct clk_ops clk_ops_pll_vote = {
.enable = pll_vote_clk_enable,
.disable = pll_vote_clk_disable,
.is_enabled = pll_vote_clk_is_enabled,
.handoff = pll_vote_clk_handoff,
.list_registers = pll_vote_clk_list_registers,
};
static void __pll_config_reg(void __iomem *pll_config, struct pll_freq_tbl *f,
struct pll_config_masks *masks)
{
u32 regval;
regval = readl_relaxed(pll_config);
/* Enable the MN counter if used */
if (f->m_val)
regval |= masks->mn_en_mask;
/* Set pre-divider and post-divider values */
regval &= ~masks->pre_div_mask;
regval |= f->pre_div_val;
regval &= ~masks->post_div_mask;
regval |= f->post_div_val;
/* Select VCO setting */
regval &= ~masks->vco_mask;
regval |= f->vco_val;
/* Enable main output if it has not been enabled */
if (masks->main_output_mask && !(regval & masks->main_output_mask))
regval |= masks->main_output_mask;
writel_relaxed(regval, pll_config);
}
static int sr2_pll_clk_enable(struct clk *c)
{
unsigned long flags;
struct pll_clk *pll = to_pll_clk(c);
int ret = 0, count;
u32 mode = readl_relaxed(PLL_MODE_REG(pll));
u32 lockmask = pll->masks.lock_mask ?: PLL_LOCKED_BIT;
spin_lock_irqsave(&pll_reg_lock, flags);
/* Disable PLL bypass mode. */
mode |= PLL_BYPASSNL;
writel_relaxed(mode, PLL_MODE_REG(pll));
/*
* H/W requires a 5us delay between disabling the bypass and
* de-asserting the reset. Delay 10us just to be safe.
*/
mb();
udelay(10);
/* De-assert active-low PLL reset. */
mode |= PLL_RESET_N;
writel_relaxed(mode, PLL_MODE_REG(pll));
/* Wait for pll to lock. */
for (count = ENABLE_WAIT_MAX_LOOPS; count > 0; count--) {
if (readl_relaxed(PLL_STATUS_REG(pll)) & lockmask)
break;
udelay(1);
}
if (!(readl_relaxed(PLL_STATUS_REG(pll)) & lockmask))
pr_err("PLL %s didn't lock after enabling it!\n", c->dbg_name);
/* Enable PLL output. */
mode |= PLL_OUTCTRL;
writel_relaxed(mode, PLL_MODE_REG(pll));
/* Ensure that the write above goes through before returning. */
mb();
spin_unlock_irqrestore(&pll_reg_lock, flags);
return ret;
}
static void __variable_rate_pll_init(struct clk *c)
{
struct pll_clk *pll = to_pll_clk(c);
u32 regval;
regval = readl_relaxed(PLL_CONFIG_REG(pll));
if (pll->masks.post_div_mask) {
regval &= ~pll->masks.post_div_mask;
regval |= pll->vals.post_div_masked;
}
if (pll->masks.pre_div_mask) {
regval &= ~pll->masks.pre_div_mask;
regval |= pll->vals.pre_div_masked;
}
if (pll->masks.main_output_mask)
regval |= pll->masks.main_output_mask;
if (pll->masks.early_output_mask)
regval |= pll->masks.early_output_mask;
if (pll->vals.enable_mn)
regval |= pll->masks.mn_en_mask;
else
regval &= ~pll->masks.mn_en_mask;
writel_relaxed(regval, PLL_CONFIG_REG(pll));
regval = readl_relaxed(PLL_MODE_REG(pll));
if (pll->masks.apc_pdn_mask)
regval &= ~pll->masks.apc_pdn_mask;
writel_relaxed(regval, PLL_MODE_REG(pll));
writel_relaxed(pll->vals.alpha_val, PLL_ALPHA_REG(pll));
writel_relaxed(pll->vals.config_ctl_val, PLL_CFG_CTL_REG(pll));
if (pll->init_test_ctl) {
writel_relaxed(pll->vals.test_ctl_lo_val,
PLL_TEST_CTL_LO_REG(pll));
writel_relaxed(pll->vals.test_ctl_hi_val,
PLL_TEST_CTL_HI_REG(pll));
}
pll->inited = true;
}
static int variable_rate_pll_clk_enable(struct clk *c)
{
unsigned long flags;
struct pll_clk *pll = to_pll_clk(c);
int ret = 0, count;
u32 mode, testlo;
u32 lockmask = pll->masks.lock_mask ?: PLL_LOCKED_BIT;
u32 mode_lock;
u64 time;
bool early_lock = false;
spin_lock_irqsave(&pll_reg_lock, flags);
if (unlikely(!to_pll_clk(c)->inited))
__variable_rate_pll_init(c);
mode = readl_relaxed(PLL_MODE_REG(pll));
/* Set test control bits as required by HW doc */
if (pll->test_ctl_lo_reg && pll->vals.test_ctl_lo_val &&
pll->pgm_test_ctl_enable)
writel_relaxed(pll->vals.test_ctl_lo_val,
PLL_TEST_CTL_LO_REG(pll));
/* Enable test_ctl debug */
mode |= BIT(3);
writel_relaxed(mode, PLL_MODE_REG(pll));
testlo = readl_relaxed(PLL_TEST_CTL_LO_REG(pll));
testlo &= ~BM(7, 6);
testlo |= 0xC0;
writel_relaxed(testlo, PLL_TEST_CTL_LO_REG(pll));
/* Wait for the write to complete */
mb();
/* Disable PLL bypass mode. */
mode |= PLL_BYPASSNL;
writel_relaxed(mode, PLL_MODE_REG(pll));
/*
* H/W requires a 5us delay between disabling the bypass and
* de-asserting the reset. Use 10us to be sure.
*/
mb();
udelay(10);
/* De-assert active-low PLL reset. */
mode |= PLL_RESET_N;
writel_relaxed(mode, PLL_MODE_REG(pll));
/*
* 5us delay mandated by HPG. However, put in a 200us delay here.
* This is to address possible locking issues with the PLL exhibit
* early "transient" locks about 16us from this point. With this
* higher delay, we avoid running into those transients.
*/
mb();
udelay(200);
/* Clear test control bits */
if (pll->test_ctl_lo_reg && pll->vals.test_ctl_lo_val &&
pll->pgm_test_ctl_enable)
writel_relaxed(0x0, PLL_TEST_CTL_LO_REG(pll));
time = sched_clock();
/* Wait for pll to lock. */
for (count = ENABLE_WAIT_MAX_LOOPS; count > 0; count--) {
if (readl_relaxed(PLL_STATUS_REG(pll)) & lockmask) {
udelay(1);
/*
* Check again to be sure. This is to avoid
* breaking too early if there is a "transient"
* lock.
*/
if ((readl_relaxed(PLL_STATUS_REG(pll)) & lockmask))
break;
else
early_lock = true;
}
udelay(1);
}
time = sched_clock() - time;
mode_lock = readl_relaxed(PLL_STATUS_REG(pll));
if (!(mode_lock & lockmask)) {
pr_err("PLL lock bit detection total wait time: %lld ns", time);
pr_err("PLL %s didn't lock after enabling for L value 0x%x!\n",
c->dbg_name, readl_relaxed(PLL_L_REG(pll)));
pr_err("mode register is 0x%x\n",
readl_relaxed(PLL_STATUS_REG(pll)));
pr_err("user control register is 0x%x\n",
readl_relaxed(PLL_CONFIG_REG(pll)));
pr_err("config control register is 0x%x\n",
readl_relaxed(PLL_CFG_CTL_REG(pll)));
pr_err("test control high register is 0x%x\n",
readl_relaxed(PLL_TEST_CTL_HI_REG(pll)));
pr_err("test control low register is 0x%x\n",
readl_relaxed(PLL_TEST_CTL_LO_REG(pll)));
pr_err("early lock? %s\n", early_lock ? "yes" : "no");
testlo = readl_relaxed(PLL_TEST_CTL_LO_REG(pll));
testlo &= ~BM(7, 6);
writel_relaxed(testlo, PLL_TEST_CTL_LO_REG(pll));
/* Wait for the write to complete */
mb();
pr_err("test_ctl_lo = 0x%x, pll status is: 0x%x\n",
readl_relaxed(PLL_TEST_CTL_LO_REG(pll)),
readl_relaxed(PLL_ALT_STATUS_REG(pll)));
testlo = readl_relaxed(PLL_TEST_CTL_LO_REG(pll));
testlo &= ~BM(7, 6);
testlo |= 0x40;
writel_relaxed(testlo, PLL_TEST_CTL_LO_REG(pll));
/* Wait for the write to complete */
mb();
pr_err("test_ctl_lo = 0x%x, pll status is: 0x%x\n",
readl_relaxed(PLL_TEST_CTL_LO_REG(pll)),
readl_relaxed(PLL_ALT_STATUS_REG(pll)));
testlo = readl_relaxed(PLL_TEST_CTL_LO_REG(pll));
testlo &= ~BM(7, 6);
testlo |= 0x80;
writel_relaxed(testlo, PLL_TEST_CTL_LO_REG(pll));
/* Wait for the write to complete */
mb();
pr_err("test_ctl_lo = 0x%x, pll status is: 0x%x\n",
readl_relaxed(PLL_TEST_CTL_LO_REG(pll)),
readl_relaxed(PLL_ALT_STATUS_REG(pll)));
testlo = readl_relaxed(PLL_TEST_CTL_LO_REG(pll));
testlo &= ~BM(7, 6);
testlo |= 0xC0;
writel_relaxed(testlo, PLL_TEST_CTL_LO_REG(pll));
/* Wait for the write to complete */
mb();
pr_err("test_ctl_lo = 0x%x, pll status is: 0x%x\n",
readl_relaxed(PLL_TEST_CTL_LO_REG(pll)),
readl_relaxed(PLL_ALT_STATUS_REG(pll)));
panic("failed to lock %s PLL\n", c->dbg_name);
}
/* Enable PLL output. */
mode |= PLL_OUTCTRL;
writel_relaxed(mode, PLL_MODE_REG(pll));
/* Ensure that the write above goes through before returning. */
mb();
spin_unlock_irqrestore(&pll_reg_lock, flags);
return ret;
}
static void __pll_clk_enable_reg(void __iomem *mode_reg)
{
u32 mode = readl_relaxed(mode_reg);
/* Disable PLL bypass mode. */
mode |= PLL_BYPASSNL;
writel_relaxed(mode, mode_reg);
/*
* H/W requires a 5us delay between disabling the bypass and
* de-asserting the reset. Delay 10us just to be safe.
*/
mb();
udelay(10);
/* De-assert active-low PLL reset. */
mode |= PLL_RESET_N;
writel_relaxed(mode, mode_reg);
/* Wait until PLL is locked. */
mb();
udelay(50);
/* Enable PLL output. */
mode |= PLL_OUTCTRL;
writel_relaxed(mode, mode_reg);
/* Ensure that the write above goes through before returning. */
mb();
}
static int local_pll_clk_enable(struct clk *c)
{
unsigned long flags;
struct pll_clk *pll = to_pll_clk(c);
spin_lock_irqsave(&pll_reg_lock, flags);
__pll_clk_enable_reg(PLL_MODE_REG(pll));
spin_unlock_irqrestore(&pll_reg_lock, flags);
return 0;
}
static void __pll_clk_disable_reg(void __iomem *mode_reg)
{
u32 mode = readl_relaxed(mode_reg);
mode &= ~PLL_MODE_MASK;
writel_relaxed(mode, mode_reg);
}
static void local_pll_clk_disable(struct clk *c)
{
unsigned long flags;
struct pll_clk *pll = to_pll_clk(c);
/*
* Disable the PLL output, disable test mode, enable
* the bypass mode, and assert the reset.
*/
spin_lock_irqsave(&pll_reg_lock, flags);
__pll_clk_disable_reg(PLL_MODE_REG(pll));
spin_unlock_irqrestore(&pll_reg_lock, flags);
}
static enum handoff local_pll_clk_handoff(struct clk *c)
{
struct pll_clk *pll = to_pll_clk(c);
u32 mode = readl_relaxed(PLL_MODE_REG(pll));
u32 mask = PLL_BYPASSNL | PLL_RESET_N | PLL_OUTCTRL;
unsigned long parent_rate;
u32 lval, mval, nval, userval;
if ((mode & mask) != mask)
return HANDOFF_DISABLED_CLK;
/* Assume bootloaders configure PLL to c->rate */
if (c->rate)
return HANDOFF_ENABLED_CLK;
parent_rate = clk_get_rate(c->parent);
lval = readl_relaxed(PLL_L_REG(pll));
mval = readl_relaxed(PLL_M_REG(pll));
nval = readl_relaxed(PLL_N_REG(pll));
userval = readl_relaxed(PLL_CONFIG_REG(pll));
c->rate = parent_rate * lval;
if (pll->masks.mn_en_mask && userval) {
if (!nval)
nval = 1;
c->rate += (parent_rate * mval) / nval;
}
return HANDOFF_ENABLED_CLK;
}
static long local_pll_clk_round_rate(struct clk *c, unsigned long rate)
{
struct pll_freq_tbl *nf;
struct pll_clk *pll = to_pll_clk(c);
if (!pll->freq_tbl)
return -EINVAL;
for (nf = pll->freq_tbl; nf->freq_hz != PLL_FREQ_END; nf++)
if (nf->freq_hz >= rate)
return nf->freq_hz;
nf--;
return nf->freq_hz;
}
static int local_pll_clk_set_rate(struct clk *c, unsigned long rate)
{
struct pll_freq_tbl *nf;
struct pll_clk *pll = to_pll_clk(c);
unsigned long flags;
for (nf = pll->freq_tbl; nf->freq_hz != PLL_FREQ_END
&& nf->freq_hz != rate; nf++)
;
if (nf->freq_hz == PLL_FREQ_END)
return -EINVAL;
/*
* Ensure PLL is off before changing rate. For optimization reasons,
* assume no downstream clock is using actively using it.
*/
spin_lock_irqsave(&c->lock, flags);
if (c->count)
c->ops->disable(c);
writel_relaxed(nf->l_val, PLL_L_REG(pll));
writel_relaxed(nf->m_val, PLL_M_REG(pll));
writel_relaxed(nf->n_val, PLL_N_REG(pll));
__pll_config_reg(PLL_CONFIG_REG(pll), nf, &pll->masks);
if (c->count)
c->ops->enable(c);
spin_unlock_irqrestore(&c->lock, flags);
return 0;
}
static enum handoff variable_rate_pll_handoff(struct clk *c)
{
struct pll_clk *pll = to_pll_clk(c);
u32 mode = readl_relaxed(PLL_MODE_REG(pll));
u32 mask = PLL_BYPASSNL | PLL_RESET_N | PLL_OUTCTRL;
u32 lval;
pll->src_rate = clk_get_rate(c->parent);
if ((mode & mask) != mask)
return HANDOFF_DISABLED_CLK;
lval = readl_relaxed(PLL_L_REG(pll));
c->rate = pll->src_rate * lval;
if (c->rate > pll->max_rate || c->rate < pll->min_rate) {
WARN(1, "%s: Out of spec PLL", c->dbg_name);
return HANDOFF_DISABLED_CLK;
}
return HANDOFF_ENABLED_CLK;
}
static long variable_rate_pll_round_rate(struct clk *c, unsigned long rate)
{
struct pll_clk *pll = to_pll_clk(c);
if (!pll->src_rate)
return 0;
if (pll->no_prepared_reconfig && c->prepare_count)
return -EINVAL;
if (rate < pll->min_rate)
rate = pll->min_rate;
if (rate > pll->max_rate)
rate = pll->max_rate;
return min(pll->max_rate,
DIV_ROUND_UP(rate, pll->src_rate) * pll->src_rate);
}
/*
* For optimization reasons, assumes no downstream clocks are actively using
* it.
*/
static int variable_rate_pll_set_rate(struct clk *c, unsigned long rate)
{
struct pll_clk *pll = to_pll_clk(c);
unsigned long flags;
u32 l_val;
if (rate != variable_rate_pll_round_rate(c, rate))
return -EINVAL;
l_val = rate / pll->src_rate;
spin_lock_irqsave(&c->lock, flags);
if (c->count)
c->ops->disable(c);
writel_relaxed(l_val, PLL_L_REG(pll));
if (c->count)
c->ops->enable(c);
spin_unlock_irqrestore(&c->lock, flags);
return 0;
}
int sr_pll_clk_enable(struct clk *c)
{
u32 mode;
unsigned long flags;
struct pll_clk *pll = to_pll_clk(c);
spin_lock_irqsave(&pll_reg_lock, flags);
mode = readl_relaxed(PLL_MODE_REG(pll));
/* De-assert active-low PLL reset. */
mode |= PLL_RESET_N;
writel_relaxed(mode, PLL_MODE_REG(pll));
/*
* H/W requires a 5us delay between disabling the bypass and
* de-asserting the reset. Delay 10us just to be safe.
*/
mb();
udelay(10);
/* Disable PLL bypass mode. */
mode |= PLL_BYPASSNL;
writel_relaxed(mode, PLL_MODE_REG(pll));
/* Wait until PLL is locked. */
mb();
udelay(60);
/* Enable PLL output. */
mode |= PLL_OUTCTRL;
writel_relaxed(mode, PLL_MODE_REG(pll));
/* Ensure that the write above goes through before returning. */
mb();
spin_unlock_irqrestore(&pll_reg_lock, flags);
return 0;
}
int sr_hpm_lp_pll_clk_enable(struct clk *c)
{
unsigned long flags;
struct pll_clk *pll = to_pll_clk(c);
u32 count, mode;
int ret = 0;
spin_lock_irqsave(&pll_reg_lock, flags);
/* Disable PLL bypass mode and de-assert reset. */
mode = PLL_BYPASSNL | PLL_RESET_N;
writel_relaxed(mode, PLL_MODE_REG(pll));
/* Wait for pll to lock. */
for (count = ENABLE_WAIT_MAX_LOOPS; count > 0; count--) {
if (readl_relaxed(PLL_STATUS_REG(pll)) & PLL_LOCKED_BIT)
break;
udelay(1);
}
if (!(readl_relaxed(PLL_STATUS_REG(pll)) & PLL_LOCKED_BIT)) {
WARN("PLL %s didn't lock after enabling it!\n", c->dbg_name);
ret = -ETIMEDOUT;
goto out;
}
/* Enable PLL output. */
mode |= PLL_OUTCTRL;
writel_relaxed(mode, PLL_MODE_REG(pll));
/* Ensure the write above goes through before returning. */
mb();
out:
spin_unlock_irqrestore(&pll_reg_lock, flags);
return ret;
}
static void __iomem *variable_rate_pll_list_registers(struct clk *c, int n,
struct clk_register_data **regs, u32 *size)
{
struct pll_clk *pll = to_pll_clk(c);
static struct clk_register_data data[] = {
{"MODE", 0x0},
{"L", 0x4},
{"ALPHA", 0x8},
{"USER_CTL", 0x10},
{"CONFIG_CTL", 0x14},
{"STATUS", 0x1C},
};
if (n)
return ERR_PTR(-EINVAL);
*regs = data;
*size = ARRAY_SIZE(data);
return PLL_MODE_REG(pll);
}
static void __iomem *local_pll_clk_list_registers(struct clk *c, int n,
struct clk_register_data **regs, u32 *size)
{
/* Not compatible with 8960 & friends */
struct pll_clk *pll = to_pll_clk(c);
static struct clk_register_data data[] = {
{"MODE", 0x0},
{"L", 0x4},
{"M", 0x8},
{"N", 0xC},
{"USER", 0x10},
{"CONFIG", 0x14},
{"STATUS", 0x1C},
};
if (n)
return ERR_PTR(-EINVAL);
*regs = data;
*size = ARRAY_SIZE(data);
return PLL_MODE_REG(pll);
}
struct clk_ops clk_ops_local_pll = {
.enable = local_pll_clk_enable,
.disable = local_pll_clk_disable,
.set_rate = local_pll_clk_set_rate,
.handoff = local_pll_clk_handoff,
.list_registers = local_pll_clk_list_registers,
};
struct clk_ops clk_ops_sr2_pll = {
.enable = sr2_pll_clk_enable,
.disable = local_pll_clk_disable,
.set_rate = local_pll_clk_set_rate,
.round_rate = local_pll_clk_round_rate,
.handoff = local_pll_clk_handoff,
.list_registers = local_pll_clk_list_registers,
};
struct clk_ops clk_ops_variable_rate_pll = {
.enable = variable_rate_pll_clk_enable,
.disable = local_pll_clk_disable,
.set_rate = variable_rate_pll_set_rate,
.round_rate = variable_rate_pll_round_rate,
.handoff = variable_rate_pll_handoff,
.list_registers = variable_rate_pll_list_registers,
};
static DEFINE_SPINLOCK(soft_vote_lock);
static int pll_acpu_vote_clk_enable(struct clk *c)
{
int ret = 0;
unsigned long flags;
struct pll_vote_clk *pllv = to_pll_vote_clk(c);
spin_lock_irqsave(&soft_vote_lock, flags);
if (!*pllv->soft_vote)
ret = pll_vote_clk_enable(c);
if (ret == 0)
*pllv->soft_vote |= (pllv->soft_vote_mask);
spin_unlock_irqrestore(&soft_vote_lock, flags);
return ret;
}
static void pll_acpu_vote_clk_disable(struct clk *c)
{
unsigned long flags;
struct pll_vote_clk *pllv = to_pll_vote_clk(c);
spin_lock_irqsave(&soft_vote_lock, flags);
*pllv->soft_vote &= ~(pllv->soft_vote_mask);
if (!*pllv->soft_vote)
pll_vote_clk_disable(c);
spin_unlock_irqrestore(&soft_vote_lock, flags);
}
static enum handoff pll_acpu_vote_clk_handoff(struct clk *c)
{
if (pll_vote_clk_handoff(c) == HANDOFF_DISABLED_CLK)
return HANDOFF_DISABLED_CLK;
if (pll_acpu_vote_clk_enable(c))
return HANDOFF_DISABLED_CLK;
return HANDOFF_ENABLED_CLK;
}
struct clk_ops clk_ops_pll_acpu_vote = {
.enable = pll_acpu_vote_clk_enable,
.disable = pll_acpu_vote_clk_disable,
.is_enabled = pll_vote_clk_is_enabled,
.handoff = pll_acpu_vote_clk_handoff,
.list_registers = pll_vote_clk_list_registers,
};
static void __set_fsm_mode(void __iomem *mode_reg,
u32 bias_count, u32 lock_count)
{
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, bias_count);
writel_relaxed(regval, mode_reg);
/* Program lock count */
regval &= ~BM(13, 8);
regval |= BVAL(13, 8, lock_count);
writel_relaxed(regval, mode_reg);
/* Enable PLL FSM voting */
regval |= BIT(20);
writel_relaxed(regval, mode_reg);
}
static void __configure_alt_config(struct pll_alt_config config,
struct pll_config_regs *regs)
{
u32 regval;
regval = readl_relaxed(PLL_CFG_ALT_REG(regs));
if (config.mask) {
regval &= ~config.mask;
regval |= config.val;
}
writel_relaxed(regval, PLL_CFG_ALT_REG(regs));
}
void __configure_pll(struct pll_config *config,
struct pll_config_regs *regs, u32 ena_fsm_mode)
{
u32 regval;
writel_relaxed(config->l, PLL_L_REG(regs));
writel_relaxed(config->m, PLL_M_REG(regs));
writel_relaxed(config->n, PLL_N_REG(regs));
regval = readl_relaxed(PLL_CONFIG_REG(regs));
/* Enable the MN accumulator */
if (config->mn_ena_mask) {
regval &= ~config->mn_ena_mask;
regval |= config->mn_ena_val;
}
/* Enable the main output */
if (config->main_output_mask) {
regval &= ~config->main_output_mask;
regval |= config->main_output_val;
}
/* Enable the aux output */
if (config->aux_output_mask) {
regval &= ~config->aux_output_mask;
regval |= config->aux_output_val;
}
/* Set pre-divider and post-divider values */
regval &= ~config->pre_div_mask;
regval |= config->pre_div_val;
regval &= ~config->post_div_mask;
regval |= config->post_div_val;
/* Select VCO setting */
regval &= ~config->vco_mask;
regval |= config->vco_val;
if (config->add_factor_mask) {
regval &= ~config->add_factor_mask;
regval |= config->add_factor_val;
}
writel_relaxed(regval, PLL_CONFIG_REG(regs));
if (regs->config_alt_reg)
__configure_alt_config(config->alt_cfg, regs);
if (regs->config_ctl_reg)
writel_relaxed(config->cfg_ctl_val, PLL_CFG_CTL_REG(regs));
}
void configure_sr_pll(struct pll_config *config,
struct pll_config_regs *regs, u32 ena_fsm_mode)
{
__configure_pll(config, regs, ena_fsm_mode);
if (ena_fsm_mode)
__set_fsm_mode(PLL_MODE_REG(regs), 0x1, 0x8);
}
void configure_sr_hpm_lp_pll(struct pll_config *config,
struct pll_config_regs *regs, u32 ena_fsm_mode)
{
__configure_pll(config, regs, ena_fsm_mode);
if (ena_fsm_mode)
__set_fsm_mode(PLL_MODE_REG(regs), 0x1, 0x0);
}
static void *votable_pll_clk_dt_parser(struct device *dev,
struct device_node *np)
{
struct pll_vote_clk *v, *peer;
struct clk *c;
u32 val, rc;
phandle p;
struct msmclk_data *drv;
v = devm_kzalloc(dev, sizeof(*v), GFP_KERNEL);
if (!v) {
dt_err(np, "memory alloc failure\n");
return ERR_PTR(-ENOMEM);
}
drv = msmclk_parse_phandle(dev, np->parent->phandle);
if (IS_ERR_OR_NULL(drv))
return ERR_CAST(drv);
v->base = &drv->base;
rc = of_property_read_u32(np, "qcom,en-offset", (u32 *)&v->en_reg);
if (rc) {
dt_err(np, "missing qcom,en-offset dt property\n");
return ERR_PTR(-EINVAL);
}
rc = of_property_read_u32(np, "qcom,en-bit", &val);
if (rc) {
dt_err(np, "missing qcom,en-bit dt property\n");
return ERR_PTR(-EINVAL);
}
v->en_mask = BIT(val);
rc = of_property_read_u32(np, "qcom,status-offset",
(u32 *)&v->status_reg);
if (rc) {
dt_err(np, "missing qcom,status-offset dt property\n");
return ERR_PTR(-EINVAL);
}
rc = of_property_read_u32(np, "qcom,status-bit", &val);
if (rc) {
dt_err(np, "missing qcom,status-bit dt property\n");
return ERR_PTR(-EINVAL);
}
v->status_mask = BIT(val);
rc = of_property_read_u32(np, "qcom,pll-config-rate", &val);
if (rc) {
dt_err(np, "missing qcom,pll-config-rate dt property\n");
return ERR_PTR(-EINVAL);
}
v->c.rate = val;
if (of_device_is_compatible(np, "qcom,active-only-pll"))
v->soft_vote_mask = PLL_SOFT_VOTE_ACPU;
else if (of_device_is_compatible(np, "qcom,sleep-active-pll"))
v->soft_vote_mask = PLL_SOFT_VOTE_PRIMARY;
if (of_device_is_compatible(np, "qcom,votable-pll")) {
v->c.ops = &clk_ops_pll_vote;
return msmclk_generic_clk_init(dev, np, &v->c);
}
rc = of_property_read_phandle_index(np, "qcom,peer", 0, &p);
if (rc) {
dt_err(np, "missing qcom,peer dt property\n");
return ERR_PTR(-EINVAL);
}
c = msmclk_lookup_phandle(dev, p);
if (!IS_ERR_OR_NULL(c)) {
v->soft_vote = devm_kzalloc(dev, sizeof(*v->soft_vote),
GFP_KERNEL);
if (!v->soft_vote) {
dt_err(np, "memory alloc failure\n");
return ERR_PTR(-ENOMEM);
}
peer = to_pll_vote_clk(c);
peer->soft_vote = v->soft_vote;
}
v->c.ops = &clk_ops_pll_acpu_vote;
return msmclk_generic_clk_init(dev, np, &v->c);
}
MSMCLK_PARSER(votable_pll_clk_dt_parser, "qcom,active-only-pll", 0);
MSMCLK_PARSER(votable_pll_clk_dt_parser, "qcom,sleep-active-pll", 1);
MSMCLK_PARSER(votable_pll_clk_dt_parser, "qcom,votable-pll", 2);