Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

clk: qcom: Add support for High-Frequency PLLs (HFPLLs)

HFPLLs are the main frequency source for Krait CPU clocks. Add
support for changing the rate of these PLLs.

Signed-off-by: Stephen Boyd <sboyd@codeaurora.org>
Signed-off-by: Sricharan R <sricharan@codeaurora.org>
Tested-by: Craig Tatlor <ctatlor97@gmail.com>
Signed-off-by: Stephen Boyd <sboyd@kernel.org>

authored by

Stephen Boyd and committed by
Stephen Boyd
b3f2f106 36d68f64

+289
+1
drivers/clk/qcom/Makefile
··· 11 11 clk-qcom-y += clk-regmap-divider.o 12 12 clk-qcom-y += clk-regmap-mux.o 13 13 clk-qcom-y += clk-regmap-mux-div.o 14 + clk-qcom-y += clk-hfpll.o 14 15 clk-qcom-y += reset.o 15 16 clk-qcom-$(CONFIG_QCOM_GDSC) += gdsc.o 16 17
+244
drivers/clk/qcom/clk-hfpll.c
··· 1 + // SPDX-License-Identifier: GPL-2.0 2 + // Copyright (c) 2018, The Linux Foundation. All rights reserved. 3 + 4 + #include <linux/kernel.h> 5 + #include <linux/export.h> 6 + #include <linux/regmap.h> 7 + #include <linux/delay.h> 8 + #include <linux/err.h> 9 + #include <linux/clk-provider.h> 10 + #include <linux/spinlock.h> 11 + 12 + #include "clk-regmap.h" 13 + #include "clk-hfpll.h" 14 + 15 + #define PLL_OUTCTRL BIT(0) 16 + #define PLL_BYPASSNL BIT(1) 17 + #define PLL_RESET_N BIT(2) 18 + 19 + /* Initialize a HFPLL at a given rate and enable it. */ 20 + static void __clk_hfpll_init_once(struct clk_hw *hw) 21 + { 22 + struct clk_hfpll *h = to_clk_hfpll(hw); 23 + struct hfpll_data const *hd = h->d; 24 + struct regmap *regmap = h->clkr.regmap; 25 + 26 + if (likely(h->init_done)) 27 + return; 28 + 29 + /* Configure PLL parameters for integer mode. */ 30 + if (hd->config_val) 31 + regmap_write(regmap, hd->config_reg, hd->config_val); 32 + regmap_write(regmap, hd->m_reg, 0); 33 + regmap_write(regmap, hd->n_reg, 1); 34 + 35 + if (hd->user_reg) { 36 + u32 regval = hd->user_val; 37 + unsigned long rate; 38 + 39 + rate = clk_hw_get_rate(hw); 40 + 41 + /* Pick the right VCO. */ 42 + if (hd->user_vco_mask && rate > hd->low_vco_max_rate) 43 + regval |= hd->user_vco_mask; 44 + regmap_write(regmap, hd->user_reg, regval); 45 + } 46 + 47 + if (hd->droop_reg) 48 + regmap_write(regmap, hd->droop_reg, hd->droop_val); 49 + 50 + h->init_done = true; 51 + } 52 + 53 + static void __clk_hfpll_enable(struct clk_hw *hw) 54 + { 55 + struct clk_hfpll *h = to_clk_hfpll(hw); 56 + struct hfpll_data const *hd = h->d; 57 + struct regmap *regmap = h->clkr.regmap; 58 + u32 val; 59 + 60 + __clk_hfpll_init_once(hw); 61 + 62 + /* Disable PLL bypass mode. */ 63 + regmap_update_bits(regmap, hd->mode_reg, PLL_BYPASSNL, PLL_BYPASSNL); 64 + 65 + /* 66 + * H/W requires a 5us delay between disabling the bypass and 67 + * de-asserting the reset. Delay 10us just to be safe. 68 + */ 69 + udelay(10); 70 + 71 + /* De-assert active-low PLL reset. */ 72 + regmap_update_bits(regmap, hd->mode_reg, PLL_RESET_N, PLL_RESET_N); 73 + 74 + /* Wait for PLL to lock. */ 75 + if (hd->status_reg) { 76 + do { 77 + regmap_read(regmap, hd->status_reg, &val); 78 + } while (!(val & BIT(hd->lock_bit))); 79 + } else { 80 + udelay(60); 81 + } 82 + 83 + /* Enable PLL output. */ 84 + regmap_update_bits(regmap, hd->mode_reg, PLL_OUTCTRL, PLL_OUTCTRL); 85 + } 86 + 87 + /* Enable an already-configured HFPLL. */ 88 + static int clk_hfpll_enable(struct clk_hw *hw) 89 + { 90 + unsigned long flags; 91 + struct clk_hfpll *h = to_clk_hfpll(hw); 92 + struct hfpll_data const *hd = h->d; 93 + struct regmap *regmap = h->clkr.regmap; 94 + u32 mode; 95 + 96 + spin_lock_irqsave(&h->lock, flags); 97 + regmap_read(regmap, hd->mode_reg, &mode); 98 + if (!(mode & (PLL_BYPASSNL | PLL_RESET_N | PLL_OUTCTRL))) 99 + __clk_hfpll_enable(hw); 100 + spin_unlock_irqrestore(&h->lock, flags); 101 + 102 + return 0; 103 + } 104 + 105 + static void __clk_hfpll_disable(struct clk_hfpll *h) 106 + { 107 + struct hfpll_data const *hd = h->d; 108 + struct regmap *regmap = h->clkr.regmap; 109 + 110 + /* 111 + * Disable the PLL output, disable test mode, enable the bypass mode, 112 + * and assert the reset. 113 + */ 114 + regmap_update_bits(regmap, hd->mode_reg, 115 + PLL_BYPASSNL | PLL_RESET_N | PLL_OUTCTRL, 0); 116 + } 117 + 118 + static void clk_hfpll_disable(struct clk_hw *hw) 119 + { 120 + struct clk_hfpll *h = to_clk_hfpll(hw); 121 + unsigned long flags; 122 + 123 + spin_lock_irqsave(&h->lock, flags); 124 + __clk_hfpll_disable(h); 125 + spin_unlock_irqrestore(&h->lock, flags); 126 + } 127 + 128 + static long clk_hfpll_round_rate(struct clk_hw *hw, unsigned long rate, 129 + unsigned long *parent_rate) 130 + { 131 + struct clk_hfpll *h = to_clk_hfpll(hw); 132 + struct hfpll_data const *hd = h->d; 133 + unsigned long rrate; 134 + 135 + rate = clamp(rate, hd->min_rate, hd->max_rate); 136 + 137 + rrate = DIV_ROUND_UP(rate, *parent_rate) * *parent_rate; 138 + if (rrate > hd->max_rate) 139 + rrate -= *parent_rate; 140 + 141 + return rrate; 142 + } 143 + 144 + /* 145 + * For optimization reasons, assumes no downstream clocks are actively using 146 + * it. 147 + */ 148 + static int clk_hfpll_set_rate(struct clk_hw *hw, unsigned long rate, 149 + unsigned long parent_rate) 150 + { 151 + struct clk_hfpll *h = to_clk_hfpll(hw); 152 + struct hfpll_data const *hd = h->d; 153 + struct regmap *regmap = h->clkr.regmap; 154 + unsigned long flags; 155 + u32 l_val, val; 156 + bool enabled; 157 + 158 + l_val = rate / parent_rate; 159 + 160 + spin_lock_irqsave(&h->lock, flags); 161 + 162 + enabled = __clk_is_enabled(hw->clk); 163 + if (enabled) 164 + __clk_hfpll_disable(h); 165 + 166 + /* Pick the right VCO. */ 167 + if (hd->user_reg && hd->user_vco_mask) { 168 + regmap_read(regmap, hd->user_reg, &val); 169 + if (rate <= hd->low_vco_max_rate) 170 + val &= ~hd->user_vco_mask; 171 + else 172 + val |= hd->user_vco_mask; 173 + regmap_write(regmap, hd->user_reg, val); 174 + } 175 + 176 + regmap_write(regmap, hd->l_reg, l_val); 177 + 178 + if (enabled) 179 + __clk_hfpll_enable(hw); 180 + 181 + spin_unlock_irqrestore(&h->lock, flags); 182 + 183 + return 0; 184 + } 185 + 186 + static unsigned long clk_hfpll_recalc_rate(struct clk_hw *hw, 187 + unsigned long parent_rate) 188 + { 189 + struct clk_hfpll *h = to_clk_hfpll(hw); 190 + struct hfpll_data const *hd = h->d; 191 + struct regmap *regmap = h->clkr.regmap; 192 + u32 l_val; 193 + 194 + regmap_read(regmap, hd->l_reg, &l_val); 195 + 196 + return l_val * parent_rate; 197 + } 198 + 199 + static void clk_hfpll_init(struct clk_hw *hw) 200 + { 201 + struct clk_hfpll *h = to_clk_hfpll(hw); 202 + struct hfpll_data const *hd = h->d; 203 + struct regmap *regmap = h->clkr.regmap; 204 + u32 mode, status; 205 + 206 + regmap_read(regmap, hd->mode_reg, &mode); 207 + if (mode != (PLL_BYPASSNL | PLL_RESET_N | PLL_OUTCTRL)) { 208 + __clk_hfpll_init_once(hw); 209 + return; 210 + } 211 + 212 + if (hd->status_reg) { 213 + regmap_read(regmap, hd->status_reg, &status); 214 + if (!(status & BIT(hd->lock_bit))) { 215 + WARN(1, "HFPLL %s is ON, but not locked!\n", 216 + __clk_get_name(hw->clk)); 217 + clk_hfpll_disable(hw); 218 + __clk_hfpll_init_once(hw); 219 + } 220 + } 221 + } 222 + 223 + static int hfpll_is_enabled(struct clk_hw *hw) 224 + { 225 + struct clk_hfpll *h = to_clk_hfpll(hw); 226 + struct hfpll_data const *hd = h->d; 227 + struct regmap *regmap = h->clkr.regmap; 228 + u32 mode; 229 + 230 + regmap_read(regmap, hd->mode_reg, &mode); 231 + mode &= 0x7; 232 + return mode == (PLL_BYPASSNL | PLL_RESET_N | PLL_OUTCTRL); 233 + } 234 + 235 + const struct clk_ops clk_ops_hfpll = { 236 + .enable = clk_hfpll_enable, 237 + .disable = clk_hfpll_disable, 238 + .is_enabled = hfpll_is_enabled, 239 + .round_rate = clk_hfpll_round_rate, 240 + .set_rate = clk_hfpll_set_rate, 241 + .recalc_rate = clk_hfpll_recalc_rate, 242 + .init = clk_hfpll_init, 243 + }; 244 + EXPORT_SYMBOL_GPL(clk_ops_hfpll);
+44
drivers/clk/qcom/clk-hfpll.h
··· 1 + /* SPDX-License-Identifier: GPL-2.0 */ 2 + 3 + #ifndef __QCOM_CLK_HFPLL_H__ 4 + #define __QCOM_CLK_HFPLL_H__ 5 + 6 + #include <linux/clk-provider.h> 7 + #include <linux/spinlock.h> 8 + #include "clk-regmap.h" 9 + 10 + struct hfpll_data { 11 + u32 mode_reg; 12 + u32 l_reg; 13 + u32 m_reg; 14 + u32 n_reg; 15 + u32 user_reg; 16 + u32 droop_reg; 17 + u32 config_reg; 18 + u32 status_reg; 19 + u8 lock_bit; 20 + 21 + u32 droop_val; 22 + u32 config_val; 23 + u32 user_val; 24 + u32 user_vco_mask; 25 + unsigned long low_vco_max_rate; 26 + 27 + unsigned long min_rate; 28 + unsigned long max_rate; 29 + }; 30 + 31 + struct clk_hfpll { 32 + struct hfpll_data const *d; 33 + int init_done; 34 + 35 + struct clk_regmap clkr; 36 + spinlock_t lock; 37 + }; 38 + 39 + #define to_clk_hfpll(_hw) \ 40 + container_of(to_clk_regmap(_hw), struct clk_hfpll, clkr) 41 + 42 + extern const struct clk_ops clk_ops_hfpll; 43 + 44 + #endif