xref: /openbmc/linux/drivers/clk/actions/owl-pll.c (revision 4f2c0a4acffbec01079c28f839422e64ddeff004)
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, &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, &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, &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