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
owl_pll_calculate_mul(struct owl_pll_hw * pll_hw,unsigned long rate)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
_get_table_rate(const struct clk_pll_table * table,unsigned int val)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
_get_pll_table(const struct clk_pll_table * table,unsigned long rate)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
owl_pll_round_rate(struct clk_hw * hw,unsigned long rate,unsigned long * parent_rate)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
owl_pll_recalc_rate(struct clk_hw * hw,unsigned long parent_rate)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
owl_pll_is_enabled(struct clk_hw * hw)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
owl_pll_set(const struct owl_clk_common * common,const struct owl_pll_hw * pll_hw,bool enable)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
owl_pll_enable(struct clk_hw * hw)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
owl_pll_disable(struct clk_hw * hw)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
owl_pll_set_rate(struct clk_hw * hw,unsigned long rate,unsigned long parent_rate)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