162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci// 362306a36Sopenharmony_ci// Spreadtrum pll clock driver 462306a36Sopenharmony_ci// 562306a36Sopenharmony_ci// Copyright (C) 2015~2017 Spreadtrum, Inc. 662306a36Sopenharmony_ci// Author: Chunyan Zhang <chunyan.zhang@spreadtrum.com> 762306a36Sopenharmony_ci 862306a36Sopenharmony_ci#include <linux/delay.h> 962306a36Sopenharmony_ci#include <linux/err.h> 1062306a36Sopenharmony_ci#include <linux/regmap.h> 1162306a36Sopenharmony_ci#include <linux/slab.h> 1262306a36Sopenharmony_ci 1362306a36Sopenharmony_ci#include "pll.h" 1462306a36Sopenharmony_ci 1562306a36Sopenharmony_ci#define CLK_PLL_1M 1000000 1662306a36Sopenharmony_ci#define CLK_PLL_10M (CLK_PLL_1M * 10) 1762306a36Sopenharmony_ci 1862306a36Sopenharmony_ci#define pindex(pll, member) \ 1962306a36Sopenharmony_ci (pll->factors[member].shift / (8 * sizeof(pll->regs_num))) 2062306a36Sopenharmony_ci 2162306a36Sopenharmony_ci#define pshift(pll, member) \ 2262306a36Sopenharmony_ci (pll->factors[member].shift % (8 * sizeof(pll->regs_num))) 2362306a36Sopenharmony_ci 2462306a36Sopenharmony_ci#define pwidth(pll, member) \ 2562306a36Sopenharmony_ci pll->factors[member].width 2662306a36Sopenharmony_ci 2762306a36Sopenharmony_ci#define pmask(pll, member) \ 2862306a36Sopenharmony_ci ((pwidth(pll, member)) ? \ 2962306a36Sopenharmony_ci GENMASK(pwidth(pll, member) + pshift(pll, member) - 1, \ 3062306a36Sopenharmony_ci pshift(pll, member)) : 0) 3162306a36Sopenharmony_ci 3262306a36Sopenharmony_ci#define pinternal(pll, cfg, member) \ 3362306a36Sopenharmony_ci (cfg[pindex(pll, member)] & pmask(pll, member)) 3462306a36Sopenharmony_ci 3562306a36Sopenharmony_ci#define pinternal_val(pll, cfg, member) \ 3662306a36Sopenharmony_ci (pinternal(pll, cfg, member) >> pshift(pll, member)) 3762306a36Sopenharmony_ci 3862306a36Sopenharmony_cistatic inline unsigned int 3962306a36Sopenharmony_cisprd_pll_read(const struct sprd_pll *pll, u8 index) 4062306a36Sopenharmony_ci{ 4162306a36Sopenharmony_ci const struct sprd_clk_common *common = &pll->common; 4262306a36Sopenharmony_ci unsigned int val = 0; 4362306a36Sopenharmony_ci 4462306a36Sopenharmony_ci if (WARN_ON(index >= pll->regs_num)) 4562306a36Sopenharmony_ci return 0; 4662306a36Sopenharmony_ci 4762306a36Sopenharmony_ci regmap_read(common->regmap, common->reg + index * 4, &val); 4862306a36Sopenharmony_ci 4962306a36Sopenharmony_ci return val; 5062306a36Sopenharmony_ci} 5162306a36Sopenharmony_ci 5262306a36Sopenharmony_cistatic inline void 5362306a36Sopenharmony_cisprd_pll_write(const struct sprd_pll *pll, u8 index, 5462306a36Sopenharmony_ci u32 msk, u32 val) 5562306a36Sopenharmony_ci{ 5662306a36Sopenharmony_ci const struct sprd_clk_common *common = &pll->common; 5762306a36Sopenharmony_ci unsigned int offset, reg; 5862306a36Sopenharmony_ci int ret = 0; 5962306a36Sopenharmony_ci 6062306a36Sopenharmony_ci if (WARN_ON(index >= pll->regs_num)) 6162306a36Sopenharmony_ci return; 6262306a36Sopenharmony_ci 6362306a36Sopenharmony_ci offset = common->reg + index * 4; 6462306a36Sopenharmony_ci ret = regmap_read(common->regmap, offset, ®); 6562306a36Sopenharmony_ci if (!ret) 6662306a36Sopenharmony_ci regmap_write(common->regmap, offset, (reg & ~msk) | val); 6762306a36Sopenharmony_ci} 6862306a36Sopenharmony_ci 6962306a36Sopenharmony_cistatic unsigned long pll_get_refin(const struct sprd_pll *pll) 7062306a36Sopenharmony_ci{ 7162306a36Sopenharmony_ci u32 shift, mask, index, refin_id = 3; 7262306a36Sopenharmony_ci const unsigned long refin[4] = { 2, 4, 13, 26 }; 7362306a36Sopenharmony_ci 7462306a36Sopenharmony_ci if (pwidth(pll, PLL_REFIN)) { 7562306a36Sopenharmony_ci index = pindex(pll, PLL_REFIN); 7662306a36Sopenharmony_ci shift = pshift(pll, PLL_REFIN); 7762306a36Sopenharmony_ci mask = pmask(pll, PLL_REFIN); 7862306a36Sopenharmony_ci refin_id = (sprd_pll_read(pll, index) & mask) >> shift; 7962306a36Sopenharmony_ci if (refin_id > 3) 8062306a36Sopenharmony_ci refin_id = 3; 8162306a36Sopenharmony_ci } 8262306a36Sopenharmony_ci 8362306a36Sopenharmony_ci return refin[refin_id]; 8462306a36Sopenharmony_ci} 8562306a36Sopenharmony_ci 8662306a36Sopenharmony_cistatic u32 pll_get_ibias(u64 rate, const u64 *table) 8762306a36Sopenharmony_ci{ 8862306a36Sopenharmony_ci u32 i, num = table[0]; 8962306a36Sopenharmony_ci 9062306a36Sopenharmony_ci /* table[0] indicates the number of items in this table */ 9162306a36Sopenharmony_ci for (i = 0; i < num; i++) 9262306a36Sopenharmony_ci if (rate <= table[i + 1]) 9362306a36Sopenharmony_ci break; 9462306a36Sopenharmony_ci 9562306a36Sopenharmony_ci return i == num ? num - 1 : i; 9662306a36Sopenharmony_ci} 9762306a36Sopenharmony_ci 9862306a36Sopenharmony_cistatic unsigned long _sprd_pll_recalc_rate(const struct sprd_pll *pll, 9962306a36Sopenharmony_ci unsigned long parent_rate) 10062306a36Sopenharmony_ci{ 10162306a36Sopenharmony_ci u32 *cfg; 10262306a36Sopenharmony_ci u32 i, mask, regs_num = pll->regs_num; 10362306a36Sopenharmony_ci unsigned long rate, nint, kint = 0; 10462306a36Sopenharmony_ci u64 refin; 10562306a36Sopenharmony_ci u16 k1, k2; 10662306a36Sopenharmony_ci 10762306a36Sopenharmony_ci cfg = kcalloc(regs_num, sizeof(*cfg), GFP_KERNEL); 10862306a36Sopenharmony_ci if (!cfg) 10962306a36Sopenharmony_ci return parent_rate; 11062306a36Sopenharmony_ci 11162306a36Sopenharmony_ci for (i = 0; i < regs_num; i++) 11262306a36Sopenharmony_ci cfg[i] = sprd_pll_read(pll, i); 11362306a36Sopenharmony_ci 11462306a36Sopenharmony_ci refin = pll_get_refin(pll); 11562306a36Sopenharmony_ci 11662306a36Sopenharmony_ci if (pinternal(pll, cfg, PLL_PREDIV)) 11762306a36Sopenharmony_ci refin = refin * 2; 11862306a36Sopenharmony_ci 11962306a36Sopenharmony_ci if (pwidth(pll, PLL_POSTDIV) && 12062306a36Sopenharmony_ci ((pll->fflag == 1 && pinternal(pll, cfg, PLL_POSTDIV)) || 12162306a36Sopenharmony_ci (!pll->fflag && !pinternal(pll, cfg, PLL_POSTDIV)))) 12262306a36Sopenharmony_ci refin = refin / 2; 12362306a36Sopenharmony_ci 12462306a36Sopenharmony_ci if (!pinternal(pll, cfg, PLL_DIV_S)) { 12562306a36Sopenharmony_ci rate = refin * pinternal_val(pll, cfg, PLL_N) * CLK_PLL_10M; 12662306a36Sopenharmony_ci } else { 12762306a36Sopenharmony_ci nint = pinternal_val(pll, cfg, PLL_NINT); 12862306a36Sopenharmony_ci if (pinternal(pll, cfg, PLL_SDM_EN)) 12962306a36Sopenharmony_ci kint = pinternal_val(pll, cfg, PLL_KINT); 13062306a36Sopenharmony_ci 13162306a36Sopenharmony_ci mask = pmask(pll, PLL_KINT); 13262306a36Sopenharmony_ci 13362306a36Sopenharmony_ci k1 = pll->k1; 13462306a36Sopenharmony_ci k2 = pll->k2; 13562306a36Sopenharmony_ci rate = DIV_ROUND_CLOSEST_ULL(refin * kint * k1, 13662306a36Sopenharmony_ci ((mask >> __ffs(mask)) + 1)) * 13762306a36Sopenharmony_ci k2 + refin * nint * CLK_PLL_1M; 13862306a36Sopenharmony_ci } 13962306a36Sopenharmony_ci 14062306a36Sopenharmony_ci kfree(cfg); 14162306a36Sopenharmony_ci return rate; 14262306a36Sopenharmony_ci} 14362306a36Sopenharmony_ci 14462306a36Sopenharmony_ci#define SPRD_PLL_WRITE_CHECK(pll, i, mask, val) \ 14562306a36Sopenharmony_ci (((sprd_pll_read(pll, i) & mask) == val) ? 0 : (-EFAULT)) 14662306a36Sopenharmony_ci 14762306a36Sopenharmony_cistatic int _sprd_pll_set_rate(const struct sprd_pll *pll, 14862306a36Sopenharmony_ci unsigned long rate, 14962306a36Sopenharmony_ci unsigned long parent_rate) 15062306a36Sopenharmony_ci{ 15162306a36Sopenharmony_ci struct reg_cfg *cfg; 15262306a36Sopenharmony_ci int ret = 0; 15362306a36Sopenharmony_ci u32 mask, shift, width, ibias_val, index; 15462306a36Sopenharmony_ci u32 regs_num = pll->regs_num, i = 0; 15562306a36Sopenharmony_ci unsigned long kint, nint; 15662306a36Sopenharmony_ci u64 tmp, refin, fvco = rate; 15762306a36Sopenharmony_ci 15862306a36Sopenharmony_ci cfg = kcalloc(regs_num, sizeof(*cfg), GFP_KERNEL); 15962306a36Sopenharmony_ci if (!cfg) 16062306a36Sopenharmony_ci return -ENOMEM; 16162306a36Sopenharmony_ci 16262306a36Sopenharmony_ci refin = pll_get_refin(pll); 16362306a36Sopenharmony_ci 16462306a36Sopenharmony_ci mask = pmask(pll, PLL_PREDIV); 16562306a36Sopenharmony_ci index = pindex(pll, PLL_PREDIV); 16662306a36Sopenharmony_ci width = pwidth(pll, PLL_PREDIV); 16762306a36Sopenharmony_ci if (width && (sprd_pll_read(pll, index) & mask)) 16862306a36Sopenharmony_ci refin = refin * 2; 16962306a36Sopenharmony_ci 17062306a36Sopenharmony_ci mask = pmask(pll, PLL_POSTDIV); 17162306a36Sopenharmony_ci index = pindex(pll, PLL_POSTDIV); 17262306a36Sopenharmony_ci width = pwidth(pll, PLL_POSTDIV); 17362306a36Sopenharmony_ci cfg[index].msk = mask; 17462306a36Sopenharmony_ci if (width && ((pll->fflag == 1 && fvco <= pll->fvco) || 17562306a36Sopenharmony_ci (pll->fflag == 0 && fvco > pll->fvco))) 17662306a36Sopenharmony_ci cfg[index].val |= mask; 17762306a36Sopenharmony_ci 17862306a36Sopenharmony_ci if (width && fvco <= pll->fvco) 17962306a36Sopenharmony_ci fvco = fvco * 2; 18062306a36Sopenharmony_ci 18162306a36Sopenharmony_ci mask = pmask(pll, PLL_DIV_S); 18262306a36Sopenharmony_ci index = pindex(pll, PLL_DIV_S); 18362306a36Sopenharmony_ci cfg[index].val |= mask; 18462306a36Sopenharmony_ci cfg[index].msk |= mask; 18562306a36Sopenharmony_ci 18662306a36Sopenharmony_ci mask = pmask(pll, PLL_SDM_EN); 18762306a36Sopenharmony_ci index = pindex(pll, PLL_SDM_EN); 18862306a36Sopenharmony_ci cfg[index].val |= mask; 18962306a36Sopenharmony_ci cfg[index].msk |= mask; 19062306a36Sopenharmony_ci 19162306a36Sopenharmony_ci nint = do_div(fvco, refin * CLK_PLL_1M); 19262306a36Sopenharmony_ci mask = pmask(pll, PLL_NINT); 19362306a36Sopenharmony_ci index = pindex(pll, PLL_NINT); 19462306a36Sopenharmony_ci shift = pshift(pll, PLL_NINT); 19562306a36Sopenharmony_ci cfg[index].val |= (nint << shift) & mask; 19662306a36Sopenharmony_ci cfg[index].msk |= mask; 19762306a36Sopenharmony_ci 19862306a36Sopenharmony_ci mask = pmask(pll, PLL_KINT); 19962306a36Sopenharmony_ci index = pindex(pll, PLL_KINT); 20062306a36Sopenharmony_ci width = pwidth(pll, PLL_KINT); 20162306a36Sopenharmony_ci shift = pshift(pll, PLL_KINT); 20262306a36Sopenharmony_ci tmp = fvco - refin * nint * CLK_PLL_1M; 20362306a36Sopenharmony_ci tmp = do_div(tmp, 10000) * ((mask >> shift) + 1); 20462306a36Sopenharmony_ci kint = DIV_ROUND_CLOSEST_ULL(tmp, refin * 100); 20562306a36Sopenharmony_ci cfg[index].val |= (kint << shift) & mask; 20662306a36Sopenharmony_ci cfg[index].msk |= mask; 20762306a36Sopenharmony_ci 20862306a36Sopenharmony_ci ibias_val = pll_get_ibias(fvco, pll->itable); 20962306a36Sopenharmony_ci 21062306a36Sopenharmony_ci mask = pmask(pll, PLL_IBIAS); 21162306a36Sopenharmony_ci index = pindex(pll, PLL_IBIAS); 21262306a36Sopenharmony_ci shift = pshift(pll, PLL_IBIAS); 21362306a36Sopenharmony_ci cfg[index].val |= ibias_val << shift & mask; 21462306a36Sopenharmony_ci cfg[index].msk |= mask; 21562306a36Sopenharmony_ci 21662306a36Sopenharmony_ci for (i = 0; i < regs_num; i++) { 21762306a36Sopenharmony_ci if (cfg[i].msk) { 21862306a36Sopenharmony_ci sprd_pll_write(pll, i, cfg[i].msk, cfg[i].val); 21962306a36Sopenharmony_ci ret |= SPRD_PLL_WRITE_CHECK(pll, i, cfg[i].msk, 22062306a36Sopenharmony_ci cfg[i].val); 22162306a36Sopenharmony_ci } 22262306a36Sopenharmony_ci } 22362306a36Sopenharmony_ci 22462306a36Sopenharmony_ci if (!ret) 22562306a36Sopenharmony_ci udelay(pll->udelay); 22662306a36Sopenharmony_ci 22762306a36Sopenharmony_ci kfree(cfg); 22862306a36Sopenharmony_ci return ret; 22962306a36Sopenharmony_ci} 23062306a36Sopenharmony_ci 23162306a36Sopenharmony_cistatic unsigned long sprd_pll_recalc_rate(struct clk_hw *hw, 23262306a36Sopenharmony_ci unsigned long parent_rate) 23362306a36Sopenharmony_ci{ 23462306a36Sopenharmony_ci struct sprd_pll *pll = hw_to_sprd_pll(hw); 23562306a36Sopenharmony_ci 23662306a36Sopenharmony_ci return _sprd_pll_recalc_rate(pll, parent_rate); 23762306a36Sopenharmony_ci} 23862306a36Sopenharmony_ci 23962306a36Sopenharmony_cistatic int sprd_pll_set_rate(struct clk_hw *hw, 24062306a36Sopenharmony_ci unsigned long rate, 24162306a36Sopenharmony_ci unsigned long parent_rate) 24262306a36Sopenharmony_ci{ 24362306a36Sopenharmony_ci struct sprd_pll *pll = hw_to_sprd_pll(hw); 24462306a36Sopenharmony_ci 24562306a36Sopenharmony_ci return _sprd_pll_set_rate(pll, rate, parent_rate); 24662306a36Sopenharmony_ci} 24762306a36Sopenharmony_ci 24862306a36Sopenharmony_cistatic int sprd_pll_clk_prepare(struct clk_hw *hw) 24962306a36Sopenharmony_ci{ 25062306a36Sopenharmony_ci struct sprd_pll *pll = hw_to_sprd_pll(hw); 25162306a36Sopenharmony_ci 25262306a36Sopenharmony_ci udelay(pll->udelay); 25362306a36Sopenharmony_ci 25462306a36Sopenharmony_ci return 0; 25562306a36Sopenharmony_ci} 25662306a36Sopenharmony_ci 25762306a36Sopenharmony_cistatic long sprd_pll_round_rate(struct clk_hw *hw, unsigned long rate, 25862306a36Sopenharmony_ci unsigned long *prate) 25962306a36Sopenharmony_ci{ 26062306a36Sopenharmony_ci return rate; 26162306a36Sopenharmony_ci} 26262306a36Sopenharmony_ci 26362306a36Sopenharmony_ciconst struct clk_ops sprd_pll_ops = { 26462306a36Sopenharmony_ci .prepare = sprd_pll_clk_prepare, 26562306a36Sopenharmony_ci .recalc_rate = sprd_pll_recalc_rate, 26662306a36Sopenharmony_ci .round_rate = sprd_pll_round_rate, 26762306a36Sopenharmony_ci .set_rate = sprd_pll_set_rate, 26862306a36Sopenharmony_ci}; 26962306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(sprd_pll_ops); 270