clk-hfpll.c (5941B)
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. */ 20static 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 53static 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. */ 88static 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 105static 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 118static 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 128static 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 */ 148static 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 186static 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 199static int 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 0; 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 return 0; 223} 224 225static int hfpll_is_enabled(struct clk_hw *hw) 226{ 227 struct clk_hfpll *h = to_clk_hfpll(hw); 228 struct hfpll_data const *hd = h->d; 229 struct regmap *regmap = h->clkr.regmap; 230 u32 mode; 231 232 regmap_read(regmap, hd->mode_reg, &mode); 233 mode &= 0x7; 234 return mode == (PLL_BYPASSNL | PLL_RESET_N | PLL_OUTCTRL); 235} 236 237const struct clk_ops clk_ops_hfpll = { 238 .enable = clk_hfpll_enable, 239 .disable = clk_hfpll_disable, 240 .is_enabled = hfpll_is_enabled, 241 .round_rate = clk_hfpll_round_rate, 242 .set_rate = clk_hfpll_set_rate, 243 .recalc_rate = clk_hfpll_recalc_rate, 244 .init = clk_hfpll_init, 245}; 246EXPORT_SYMBOL_GPL(clk_ops_hfpll);