12792c37eSManivannan Sadhasivam // SPDX-License-Identifier: GPL-2.0+ 22792c37eSManivannan Sadhasivam // 32792c37eSManivannan Sadhasivam // OWL pll clock driver 42792c37eSManivannan Sadhasivam // 52792c37eSManivannan Sadhasivam // Copyright (c) 2014 Actions Semi Inc. 62792c37eSManivannan Sadhasivam // Author: David Liu <liuwei@actions-semi.com> 72792c37eSManivannan Sadhasivam // 82792c37eSManivannan Sadhasivam // Copyright (c) 2018 Linaro Ltd. 92792c37eSManivannan Sadhasivam // Author: Manivannan Sadhasivam <manivannan.sadhasivam@linaro.org> 102792c37eSManivannan Sadhasivam 112792c37eSManivannan Sadhasivam #include <linux/clk-provider.h> 122792c37eSManivannan Sadhasivam #include <linux/slab.h> 132792c37eSManivannan Sadhasivam #include <linux/io.h> 142792c37eSManivannan Sadhasivam #include <linux/delay.h> 152792c37eSManivannan Sadhasivam 162792c37eSManivannan Sadhasivam #include "owl-pll.h" 172792c37eSManivannan Sadhasivam 182792c37eSManivannan Sadhasivam static u32 owl_pll_calculate_mul(struct owl_pll_hw *pll_hw, unsigned long rate) 192792c37eSManivannan Sadhasivam { 202792c37eSManivannan Sadhasivam u32 mul; 212792c37eSManivannan Sadhasivam 222792c37eSManivannan Sadhasivam mul = DIV_ROUND_CLOSEST(rate, pll_hw->bfreq); 232792c37eSManivannan Sadhasivam if (mul < pll_hw->min_mul) 242792c37eSManivannan Sadhasivam mul = pll_hw->min_mul; 252792c37eSManivannan Sadhasivam else if (mul > pll_hw->max_mul) 262792c37eSManivannan Sadhasivam mul = pll_hw->max_mul; 272792c37eSManivannan Sadhasivam 28*bab79506SColin Ian King return mul & mul_mask(pll_hw); 292792c37eSManivannan Sadhasivam } 302792c37eSManivannan Sadhasivam 312792c37eSManivannan Sadhasivam static unsigned long _get_table_rate(const struct clk_pll_table *table, 322792c37eSManivannan Sadhasivam unsigned int val) 332792c37eSManivannan Sadhasivam { 342792c37eSManivannan Sadhasivam const struct clk_pll_table *clkt; 352792c37eSManivannan Sadhasivam 362792c37eSManivannan Sadhasivam for (clkt = table; clkt->rate; clkt++) 372792c37eSManivannan Sadhasivam if (clkt->val == val) 382792c37eSManivannan Sadhasivam return clkt->rate; 392792c37eSManivannan Sadhasivam 402792c37eSManivannan Sadhasivam return 0; 412792c37eSManivannan Sadhasivam } 422792c37eSManivannan Sadhasivam 432792c37eSManivannan Sadhasivam static const struct clk_pll_table *_get_pll_table( 442792c37eSManivannan Sadhasivam const struct clk_pll_table *table, unsigned long rate) 452792c37eSManivannan Sadhasivam { 462792c37eSManivannan Sadhasivam const struct clk_pll_table *clkt; 472792c37eSManivannan Sadhasivam 482792c37eSManivannan Sadhasivam for (clkt = table; clkt->rate; clkt++) { 492792c37eSManivannan Sadhasivam if (clkt->rate == rate) { 502792c37eSManivannan Sadhasivam table = clkt; 512792c37eSManivannan Sadhasivam break; 522792c37eSManivannan Sadhasivam } else if (clkt->rate < rate) 532792c37eSManivannan Sadhasivam table = clkt; 542792c37eSManivannan Sadhasivam } 552792c37eSManivannan Sadhasivam 562792c37eSManivannan Sadhasivam return table; 572792c37eSManivannan Sadhasivam } 582792c37eSManivannan Sadhasivam 592792c37eSManivannan Sadhasivam static long owl_pll_round_rate(struct clk_hw *hw, unsigned long rate, 602792c37eSManivannan Sadhasivam unsigned long *parent_rate) 612792c37eSManivannan Sadhasivam { 622792c37eSManivannan Sadhasivam struct owl_pll *pll = hw_to_owl_pll(hw); 632792c37eSManivannan Sadhasivam struct owl_pll_hw *pll_hw = &pll->pll_hw; 642792c37eSManivannan Sadhasivam const struct clk_pll_table *clkt; 652792c37eSManivannan Sadhasivam u32 mul; 662792c37eSManivannan Sadhasivam 672792c37eSManivannan Sadhasivam if (pll_hw->table) { 682792c37eSManivannan Sadhasivam clkt = _get_pll_table(pll_hw->table, rate); 692792c37eSManivannan Sadhasivam return clkt->rate; 702792c37eSManivannan Sadhasivam } 712792c37eSManivannan Sadhasivam 722792c37eSManivannan Sadhasivam /* fixed frequency */ 732792c37eSManivannan Sadhasivam if (pll_hw->width == 0) 742792c37eSManivannan Sadhasivam return pll_hw->bfreq; 752792c37eSManivannan Sadhasivam 762792c37eSManivannan Sadhasivam mul = owl_pll_calculate_mul(pll_hw, rate); 772792c37eSManivannan Sadhasivam 782792c37eSManivannan Sadhasivam return pll_hw->bfreq * mul; 792792c37eSManivannan Sadhasivam } 802792c37eSManivannan Sadhasivam 812792c37eSManivannan Sadhasivam static unsigned long owl_pll_recalc_rate(struct clk_hw *hw, 822792c37eSManivannan Sadhasivam unsigned long parent_rate) 832792c37eSManivannan Sadhasivam { 842792c37eSManivannan Sadhasivam struct owl_pll *pll = hw_to_owl_pll(hw); 852792c37eSManivannan Sadhasivam struct owl_pll_hw *pll_hw = &pll->pll_hw; 862792c37eSManivannan Sadhasivam const struct owl_clk_common *common = &pll->common; 872792c37eSManivannan Sadhasivam u32 val; 882792c37eSManivannan Sadhasivam 892792c37eSManivannan Sadhasivam if (pll_hw->table) { 902792c37eSManivannan Sadhasivam regmap_read(common->regmap, pll_hw->reg, &val); 912792c37eSManivannan Sadhasivam 922792c37eSManivannan Sadhasivam val = val >> pll_hw->shift; 932792c37eSManivannan Sadhasivam val &= mul_mask(pll_hw); 942792c37eSManivannan Sadhasivam 952792c37eSManivannan Sadhasivam return _get_table_rate(pll_hw->table, val); 962792c37eSManivannan Sadhasivam } 972792c37eSManivannan Sadhasivam 982792c37eSManivannan Sadhasivam /* fixed frequency */ 992792c37eSManivannan Sadhasivam if (pll_hw->width == 0) 1002792c37eSManivannan Sadhasivam return pll_hw->bfreq; 1012792c37eSManivannan Sadhasivam 1022792c37eSManivannan Sadhasivam regmap_read(common->regmap, pll_hw->reg, &val); 1032792c37eSManivannan Sadhasivam 1042792c37eSManivannan Sadhasivam val = val >> pll_hw->shift; 1052792c37eSManivannan Sadhasivam val &= mul_mask(pll_hw); 1062792c37eSManivannan Sadhasivam 1072792c37eSManivannan Sadhasivam return pll_hw->bfreq * val; 1082792c37eSManivannan Sadhasivam } 1092792c37eSManivannan Sadhasivam 1102792c37eSManivannan Sadhasivam static int owl_pll_is_enabled(struct clk_hw *hw) 1112792c37eSManivannan Sadhasivam { 1122792c37eSManivannan Sadhasivam struct owl_pll *pll = hw_to_owl_pll(hw); 1132792c37eSManivannan Sadhasivam struct owl_pll_hw *pll_hw = &pll->pll_hw; 1142792c37eSManivannan Sadhasivam const struct owl_clk_common *common = &pll->common; 1152792c37eSManivannan Sadhasivam u32 reg; 1162792c37eSManivannan Sadhasivam 1172792c37eSManivannan Sadhasivam regmap_read(common->regmap, pll_hw->reg, ®); 1182792c37eSManivannan Sadhasivam 1192792c37eSManivannan Sadhasivam return !!(reg & BIT(pll_hw->bit_idx)); 1202792c37eSManivannan Sadhasivam } 1212792c37eSManivannan Sadhasivam 1222792c37eSManivannan Sadhasivam static void owl_pll_set(const struct owl_clk_common *common, 1232792c37eSManivannan Sadhasivam const struct owl_pll_hw *pll_hw, bool enable) 1242792c37eSManivannan Sadhasivam { 1252792c37eSManivannan Sadhasivam u32 reg; 1262792c37eSManivannan Sadhasivam 1272792c37eSManivannan Sadhasivam regmap_read(common->regmap, pll_hw->reg, ®); 1282792c37eSManivannan Sadhasivam 1292792c37eSManivannan Sadhasivam if (enable) 1302792c37eSManivannan Sadhasivam reg |= BIT(pll_hw->bit_idx); 1312792c37eSManivannan Sadhasivam else 1322792c37eSManivannan Sadhasivam reg &= ~BIT(pll_hw->bit_idx); 1332792c37eSManivannan Sadhasivam 1342792c37eSManivannan Sadhasivam regmap_write(common->regmap, pll_hw->reg, reg); 1352792c37eSManivannan Sadhasivam } 1362792c37eSManivannan Sadhasivam 1372792c37eSManivannan Sadhasivam static int owl_pll_enable(struct clk_hw *hw) 1382792c37eSManivannan Sadhasivam { 1392792c37eSManivannan Sadhasivam struct owl_pll *pll = hw_to_owl_pll(hw); 1402792c37eSManivannan Sadhasivam const struct owl_clk_common *common = &pll->common; 1412792c37eSManivannan Sadhasivam 1422792c37eSManivannan Sadhasivam owl_pll_set(common, &pll->pll_hw, true); 1432792c37eSManivannan Sadhasivam 1442792c37eSManivannan Sadhasivam return 0; 1452792c37eSManivannan Sadhasivam } 1462792c37eSManivannan Sadhasivam 1472792c37eSManivannan Sadhasivam static void owl_pll_disable(struct clk_hw *hw) 1482792c37eSManivannan Sadhasivam { 1492792c37eSManivannan Sadhasivam struct owl_pll *pll = hw_to_owl_pll(hw); 1502792c37eSManivannan Sadhasivam const struct owl_clk_common *common = &pll->common; 1512792c37eSManivannan Sadhasivam 1522792c37eSManivannan Sadhasivam owl_pll_set(common, &pll->pll_hw, false); 1532792c37eSManivannan Sadhasivam } 1542792c37eSManivannan Sadhasivam 1552792c37eSManivannan Sadhasivam static int owl_pll_set_rate(struct clk_hw *hw, unsigned long rate, 1562792c37eSManivannan Sadhasivam unsigned long parent_rate) 1572792c37eSManivannan Sadhasivam { 1582792c37eSManivannan Sadhasivam struct owl_pll *pll = hw_to_owl_pll(hw); 1592792c37eSManivannan Sadhasivam struct owl_pll_hw *pll_hw = &pll->pll_hw; 1602792c37eSManivannan Sadhasivam const struct owl_clk_common *common = &pll->common; 1612792c37eSManivannan Sadhasivam const struct clk_pll_table *clkt; 1622792c37eSManivannan Sadhasivam u32 val, reg; 1632792c37eSManivannan Sadhasivam 1642792c37eSManivannan Sadhasivam /* fixed frequency */ 1652792c37eSManivannan Sadhasivam if (pll_hw->width == 0) 1662792c37eSManivannan Sadhasivam return 0; 1672792c37eSManivannan Sadhasivam 1682792c37eSManivannan Sadhasivam if (pll_hw->table) { 1692792c37eSManivannan Sadhasivam clkt = _get_pll_table(pll_hw->table, rate); 1702792c37eSManivannan Sadhasivam val = clkt->val; 1712792c37eSManivannan Sadhasivam } else { 1722792c37eSManivannan Sadhasivam val = owl_pll_calculate_mul(pll_hw, rate); 1732792c37eSManivannan Sadhasivam } 1742792c37eSManivannan Sadhasivam 1752792c37eSManivannan Sadhasivam regmap_read(common->regmap, pll_hw->reg, ®); 1762792c37eSManivannan Sadhasivam 1772792c37eSManivannan Sadhasivam reg &= ~mul_mask(pll_hw); 1782792c37eSManivannan Sadhasivam reg |= val << pll_hw->shift; 1792792c37eSManivannan Sadhasivam 1802792c37eSManivannan Sadhasivam regmap_write(common->regmap, pll_hw->reg, reg); 1812792c37eSManivannan Sadhasivam 1829831289fSManivannan Sadhasivam udelay(pll_hw->delay); 1832792c37eSManivannan Sadhasivam 1842792c37eSManivannan Sadhasivam return 0; 1852792c37eSManivannan Sadhasivam } 1862792c37eSManivannan Sadhasivam 1872792c37eSManivannan Sadhasivam const struct clk_ops owl_pll_ops = { 1882792c37eSManivannan Sadhasivam .enable = owl_pll_enable, 1892792c37eSManivannan Sadhasivam .disable = owl_pll_disable, 1902792c37eSManivannan Sadhasivam .is_enabled = owl_pll_is_enabled, 1912792c37eSManivannan Sadhasivam .round_rate = owl_pll_round_rate, 1922792c37eSManivannan Sadhasivam .recalc_rate = owl_pll_recalc_rate, 1932792c37eSManivannan Sadhasivam .set_rate = owl_pll_set_rate, 1942792c37eSManivannan Sadhasivam }; 195