| /* |
| * Copyright (c) 2012-2018, 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_CFG_CTL_HI_REG(x) (*(x)->base + (unsigned long) \ |
| (x)->config_ctl_hi_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) |
| |
| #define SPM_FORCE_EVENT 0x4 |
| |
| 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); |
| } |
| |
| const 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, |
| }; |
| |
| /* |
| * spm_event() -- Set/Clear SPM events |
| * PLL off sequence -- enable (1) |
| * Set L2_SPM_FORCE_EVENT_EN[bit] register to 1 |
| * Set L2_SPM_FORCE_EVENT[bit] register to 1 |
| * PLL on sequence -- enable (0) |
| * Clear L2_SPM_FORCE_EVENT[bit] register to 0 |
| * Clear L2_SPM_FORCE_EVENT_EN[bit] register to 0 |
| */ |
| static void spm_event(void __iomem *base, u32 offset, u32 bit, |
| bool enable) |
| { |
| uint32_t val; |
| |
| if (!base) |
| return; |
| |
| if (enable) { |
| /* L2_SPM_FORCE_EVENT_EN */ |
| val = readl_relaxed(base + offset); |
| val |= BIT(bit); |
| writel_relaxed(val, (base + offset)); |
| /* Ensure that the write above goes through. */ |
| mb(); |
| |
| /* L2_SPM_FORCE_EVENT */ |
| val = readl_relaxed(base + offset + SPM_FORCE_EVENT); |
| val |= BIT(bit); |
| writel_relaxed(val, (base + offset + SPM_FORCE_EVENT)); |
| /* Ensure that the write above goes through. */ |
| mb(); |
| } else { |
| /* L2_SPM_FORCE_EVENT */ |
| val = readl_relaxed(base + offset + SPM_FORCE_EVENT); |
| val &= ~BIT(bit); |
| writel_relaxed(val, (base + offset + SPM_FORCE_EVENT)); |
| /* Ensure that the write above goes through. */ |
| mb(); |
| |
| /* L2_SPM_FORCE_EVENT_EN */ |
| val = readl_relaxed(base + offset); |
| val &= ~BIT(bit); |
| writel_relaxed(val, (base + offset)); |
| /* Ensure that the write above goes through. */ |
| mb(); |
| } |
| } |
| |
| 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 void pll_wait_for_lock(struct pll_clk *pll) |
| { |
| int count; |
| u32 mode = readl_relaxed(PLL_MODE_REG(pll)); |
| u32 lockmask = pll->masks.lock_mask ?: PLL_LOCKED_BIT; |
| u32 status_reg, user_reg, l_reg, m_reg, n_reg, config_reg; |
| |
| /* 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)) { |
| mode = readl_relaxed(PLL_MODE_REG(pll)); |
| status_reg = readl_relaxed(PLL_STATUS_REG(pll)); |
| user_reg = readl_relaxed(PLL_CONFIG_REG(pll)); |
| config_reg = readl_relaxed(PLL_CFG_CTL_REG(pll)); |
| l_reg = readl_relaxed(PLL_L_REG(pll)); |
| m_reg = readl_relaxed(PLL_M_REG(pll)); |
| n_reg = readl_relaxed(PLL_N_REG(pll)); |
| pr_err("count = %d\n", (int)count); |
| pr_err("mode register is 0x%x\n", mode); |
| pr_err("status register is 0x%x\n", status_reg); |
| pr_err("user control register is 0x%x\n", user_reg); |
| pr_err("config control register is 0x%x\n", config_reg); |
| pr_err("L value register is 0x%x\n", l_reg); |
| pr_err("M value register is 0x%x\n", m_reg); |
| pr_err("N value control register is 0x%x\n", n_reg); |
| panic("PLL %s didn't lock after enabling it!\n", |
| pll->c.dbg_name); |
| } |
| } |
| |
| static int sr2_pll_clk_enable(struct clk *c) |
| { |
| unsigned long flags; |
| struct pll_clk *pll = to_pll_clk(c); |
| u32 mode = readl_relaxed(PLL_MODE_REG(pll)); |
| |
| spin_lock_irqsave(&pll_reg_lock, flags); |
| |
| spm_event(pll->spm_ctrl.spm_base, pll->spm_ctrl.offset, |
| pll->spm_ctrl.event_bit, false); |
| |
| /* 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)); |
| |
| pll_wait_for_lock(pll); |
| |
| /* 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; |
| } |
| |
| static int acpu_pll_clk_enable(struct clk *c) |
| { |
| unsigned long flags; |
| struct pll_clk *pll = to_pll_clk(c); |
| u32 mode = readl_relaxed(PLL_MODE_REG(pll)); |
| |
| spin_lock_irqsave(&pll_reg_lock, flags); |
| |
| spm_event(pll->spm_ctrl.spm_base, pll->spm_ctrl.offset, |
| pll->spm_ctrl.event_bit, false); |
| |
| /* 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)); |
| |
| /* PLL H/W requires a 50uSec delay before polling lock_detect. */ |
| mb(); |
| udelay(50); |
| |
| pll_wait_for_lock(pll); |
| |
| /* 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; |
| } |
| |
| 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->vals.config_ctl_hi_val) |
| writel_relaxed(pll->vals.config_ctl_hi_val, |
| PLL_CFG_CTL_HI_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)); |
| |
| if (!pll->test_ctl_dbg) { |
| /* 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; |
| 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 variable_rate_pll_clk_disable_hwfsm(struct clk *c) |
| { |
| struct pll_clk *pll = to_pll_clk(c); |
| u32 regval; |
| |
| /* Set test control bit to stay-in-CFA if necessary */ |
| if (pll->test_ctl_lo_reg && pll->pgm_test_ctl_enable) { |
| regval = readl_relaxed(PLL_TEST_CTL_LO_REG(pll)); |
| writel_relaxed(regval | BIT(16), |
| PLL_TEST_CTL_LO_REG(pll)); |
| } |
| |
| /* 8 reference clock cycle delay mandated by the HPG */ |
| udelay(1); |
| } |
| |
| static int variable_rate_pll_clk_enable_hwfsm(struct clk *c) |
| { |
| struct pll_clk *pll = to_pll_clk(c); |
| int count; |
| u32 lockmask = pll->masks.lock_mask ?: PLL_LOCKED_BIT; |
| unsigned long flags; |
| u32 regval; |
| |
| spin_lock_irqsave(&pll_reg_lock, flags); |
| |
| /* Clear test control bit if necessary */ |
| if (pll->test_ctl_lo_reg && pll->pgm_test_ctl_enable) { |
| regval = readl_relaxed(PLL_TEST_CTL_LO_REG(pll)); |
| regval &= ~BIT(16); |
| writel_relaxed(regval, PLL_TEST_CTL_LO_REG(pll)); |
| } |
| |
| /* Wait for 50us explicitly to avoid transient locks */ |
| udelay(50); |
| |
| 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); |
| |
| spin_unlock_irqrestore(&pll_reg_lock, flags); |
| |
| return 0; |
| } |
| |
| 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); |
| spm_event(pll->spm_ctrl.spm_base, pll->spm_ctrl.offset, |
| pll->spm_ctrl.event_bit, true); |
| __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); |
| |
| lval = readl_relaxed(PLL_L_REG(pll)); |
| if (!lval) |
| return HANDOFF_DISABLED_CLK; |
| |
| 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; |
| } |
| |
| if ((mode & mask) != mask) |
| 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 && c->rate != rate) |
| 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->ops->disable(c); |
| |
| writel_relaxed(l_val, PLL_L_REG(pll)); |
| |
| if (c->count && c->ops->enable) |
| 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); |
| } |
| |
| |
| const 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, |
| }; |
| |
| const 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, |
| }; |
| |
| const struct clk_ops clk_ops_acpu_pll = { |
| .enable = acpu_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, |
| }; |
| |
| const struct clk_ops clk_ops_variable_rate_pll_hwfsm = { |
| .enable = variable_rate_pll_clk_enable_hwfsm, |
| .disable = variable_rate_pll_clk_disable_hwfsm, |
| .set_rate = variable_rate_pll_set_rate, |
| .round_rate = variable_rate_pll_round_rate, |
| .handoff = variable_rate_pll_handoff, |
| }; |
| |
| const 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; |
| } |
| |
| const 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 int pll_sleep_clk_enable(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); |
| return 0; |
| } |
| |
| static void pll_sleep_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 enum handoff pll_sleep_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; |
| } |
| |
| /* |
| * This .ops is meant to be used by gpll0_sleep_clk_src. The aim is to utilise |
| * the h/w feature of sleep enable bit to denote if the PLL can be turned OFF |
| * once APPS goes to PC. gpll0_sleep_clk_src will be enabled only if there is a |
| * peripheral client using it and disabled if there is none. The current |
| * implementation of enable .ops clears the h/w bit of sleep enable while the |
| * disable .ops asserts it. |
| */ |
| |
| const struct clk_ops clk_ops_pll_sleep_vote = { |
| .enable = pll_sleep_clk_enable, |
| .disable = pll_sleep_clk_disable, |
| .handoff = pll_sleep_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) |
| 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) |
| 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); |