162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0+
262306a36Sopenharmony_ci//
362306a36Sopenharmony_ci// OWL pll clock driver
462306a36Sopenharmony_ci//
562306a36Sopenharmony_ci// Copyright (c) 2014 Actions Semi Inc.
662306a36Sopenharmony_ci// Author: David Liu <liuwei@actions-semi.com>
762306a36Sopenharmony_ci//
862306a36Sopenharmony_ci// Copyright (c) 2018 Linaro Ltd.
962306a36Sopenharmony_ci// Author: Manivannan Sadhasivam <manivannan.sadhasivam@linaro.org>
1062306a36Sopenharmony_ci
1162306a36Sopenharmony_ci#include <linux/clk-provider.h>
1262306a36Sopenharmony_ci#include <linux/slab.h>
1362306a36Sopenharmony_ci#include <linux/io.h>
1462306a36Sopenharmony_ci#include <linux/delay.h>
1562306a36Sopenharmony_ci
1662306a36Sopenharmony_ci#include "owl-pll.h"
1762306a36Sopenharmony_ci
1862306a36Sopenharmony_cistatic u32 owl_pll_calculate_mul(struct owl_pll_hw *pll_hw, unsigned long rate)
1962306a36Sopenharmony_ci{
2062306a36Sopenharmony_ci	u32 mul;
2162306a36Sopenharmony_ci
2262306a36Sopenharmony_ci	mul = DIV_ROUND_CLOSEST(rate, pll_hw->bfreq);
2362306a36Sopenharmony_ci	if (mul < pll_hw->min_mul)
2462306a36Sopenharmony_ci		mul = pll_hw->min_mul;
2562306a36Sopenharmony_ci	else if (mul > pll_hw->max_mul)
2662306a36Sopenharmony_ci		mul = pll_hw->max_mul;
2762306a36Sopenharmony_ci
2862306a36Sopenharmony_ci	return mul & mul_mask(pll_hw);
2962306a36Sopenharmony_ci}
3062306a36Sopenharmony_ci
3162306a36Sopenharmony_cistatic unsigned long _get_table_rate(const struct clk_pll_table *table,
3262306a36Sopenharmony_ci		unsigned int val)
3362306a36Sopenharmony_ci{
3462306a36Sopenharmony_ci	const struct clk_pll_table *clkt;
3562306a36Sopenharmony_ci
3662306a36Sopenharmony_ci	for (clkt = table; clkt->rate; clkt++)
3762306a36Sopenharmony_ci		if (clkt->val == val)
3862306a36Sopenharmony_ci			return clkt->rate;
3962306a36Sopenharmony_ci
4062306a36Sopenharmony_ci	return 0;
4162306a36Sopenharmony_ci}
4262306a36Sopenharmony_ci
4362306a36Sopenharmony_cistatic const struct clk_pll_table *_get_pll_table(
4462306a36Sopenharmony_ci		const struct clk_pll_table *table, unsigned long rate)
4562306a36Sopenharmony_ci{
4662306a36Sopenharmony_ci	const struct clk_pll_table *clkt;
4762306a36Sopenharmony_ci
4862306a36Sopenharmony_ci	for (clkt = table; clkt->rate; clkt++) {
4962306a36Sopenharmony_ci		if (clkt->rate == rate) {
5062306a36Sopenharmony_ci			table = clkt;
5162306a36Sopenharmony_ci			break;
5262306a36Sopenharmony_ci		} else if (clkt->rate < rate)
5362306a36Sopenharmony_ci			table = clkt;
5462306a36Sopenharmony_ci	}
5562306a36Sopenharmony_ci
5662306a36Sopenharmony_ci	return table;
5762306a36Sopenharmony_ci}
5862306a36Sopenharmony_ci
5962306a36Sopenharmony_cistatic long owl_pll_round_rate(struct clk_hw *hw, unsigned long rate,
6062306a36Sopenharmony_ci		unsigned long *parent_rate)
6162306a36Sopenharmony_ci{
6262306a36Sopenharmony_ci	struct owl_pll *pll = hw_to_owl_pll(hw);
6362306a36Sopenharmony_ci	struct owl_pll_hw *pll_hw = &pll->pll_hw;
6462306a36Sopenharmony_ci	const struct clk_pll_table *clkt;
6562306a36Sopenharmony_ci	u32 mul;
6662306a36Sopenharmony_ci
6762306a36Sopenharmony_ci	if (pll_hw->table) {
6862306a36Sopenharmony_ci		clkt = _get_pll_table(pll_hw->table, rate);
6962306a36Sopenharmony_ci		return clkt->rate;
7062306a36Sopenharmony_ci	}
7162306a36Sopenharmony_ci
7262306a36Sopenharmony_ci	/* fixed frequency */
7362306a36Sopenharmony_ci	if (pll_hw->width == 0)
7462306a36Sopenharmony_ci		return pll_hw->bfreq;
7562306a36Sopenharmony_ci
7662306a36Sopenharmony_ci	mul = owl_pll_calculate_mul(pll_hw, rate);
7762306a36Sopenharmony_ci
7862306a36Sopenharmony_ci	return pll_hw->bfreq * mul;
7962306a36Sopenharmony_ci}
8062306a36Sopenharmony_ci
8162306a36Sopenharmony_cistatic unsigned long owl_pll_recalc_rate(struct clk_hw *hw,
8262306a36Sopenharmony_ci		unsigned long parent_rate)
8362306a36Sopenharmony_ci{
8462306a36Sopenharmony_ci	struct owl_pll *pll = hw_to_owl_pll(hw);
8562306a36Sopenharmony_ci	struct owl_pll_hw *pll_hw = &pll->pll_hw;
8662306a36Sopenharmony_ci	const struct owl_clk_common *common = &pll->common;
8762306a36Sopenharmony_ci	u32 val;
8862306a36Sopenharmony_ci
8962306a36Sopenharmony_ci	if (pll_hw->table) {
9062306a36Sopenharmony_ci		regmap_read(common->regmap, pll_hw->reg, &val);
9162306a36Sopenharmony_ci
9262306a36Sopenharmony_ci		val = val >> pll_hw->shift;
9362306a36Sopenharmony_ci		val &= mul_mask(pll_hw);
9462306a36Sopenharmony_ci
9562306a36Sopenharmony_ci		return _get_table_rate(pll_hw->table, val);
9662306a36Sopenharmony_ci	}
9762306a36Sopenharmony_ci
9862306a36Sopenharmony_ci	/* fixed frequency */
9962306a36Sopenharmony_ci	if (pll_hw->width == 0)
10062306a36Sopenharmony_ci		return pll_hw->bfreq;
10162306a36Sopenharmony_ci
10262306a36Sopenharmony_ci	regmap_read(common->regmap, pll_hw->reg, &val);
10362306a36Sopenharmony_ci
10462306a36Sopenharmony_ci	val = val >> pll_hw->shift;
10562306a36Sopenharmony_ci	val &= mul_mask(pll_hw);
10662306a36Sopenharmony_ci
10762306a36Sopenharmony_ci	return pll_hw->bfreq * val;
10862306a36Sopenharmony_ci}
10962306a36Sopenharmony_ci
11062306a36Sopenharmony_cistatic int owl_pll_is_enabled(struct clk_hw *hw)
11162306a36Sopenharmony_ci{
11262306a36Sopenharmony_ci	struct owl_pll *pll = hw_to_owl_pll(hw);
11362306a36Sopenharmony_ci	struct owl_pll_hw *pll_hw = &pll->pll_hw;
11462306a36Sopenharmony_ci	const struct owl_clk_common *common = &pll->common;
11562306a36Sopenharmony_ci	u32 reg;
11662306a36Sopenharmony_ci
11762306a36Sopenharmony_ci	regmap_read(common->regmap, pll_hw->reg, &reg);
11862306a36Sopenharmony_ci
11962306a36Sopenharmony_ci	return !!(reg & BIT(pll_hw->bit_idx));
12062306a36Sopenharmony_ci}
12162306a36Sopenharmony_ci
12262306a36Sopenharmony_cistatic void owl_pll_set(const struct owl_clk_common *common,
12362306a36Sopenharmony_ci		       const struct owl_pll_hw *pll_hw, bool enable)
12462306a36Sopenharmony_ci{
12562306a36Sopenharmony_ci	u32 reg;
12662306a36Sopenharmony_ci
12762306a36Sopenharmony_ci	regmap_read(common->regmap, pll_hw->reg, &reg);
12862306a36Sopenharmony_ci
12962306a36Sopenharmony_ci	if (enable)
13062306a36Sopenharmony_ci		reg |= BIT(pll_hw->bit_idx);
13162306a36Sopenharmony_ci	else
13262306a36Sopenharmony_ci		reg &= ~BIT(pll_hw->bit_idx);
13362306a36Sopenharmony_ci
13462306a36Sopenharmony_ci	regmap_write(common->regmap, pll_hw->reg, reg);
13562306a36Sopenharmony_ci}
13662306a36Sopenharmony_ci
13762306a36Sopenharmony_cistatic int owl_pll_enable(struct clk_hw *hw)
13862306a36Sopenharmony_ci{
13962306a36Sopenharmony_ci	struct owl_pll *pll = hw_to_owl_pll(hw);
14062306a36Sopenharmony_ci	const struct owl_clk_common *common = &pll->common;
14162306a36Sopenharmony_ci
14262306a36Sopenharmony_ci	owl_pll_set(common, &pll->pll_hw, true);
14362306a36Sopenharmony_ci
14462306a36Sopenharmony_ci	return 0;
14562306a36Sopenharmony_ci}
14662306a36Sopenharmony_ci
14762306a36Sopenharmony_cistatic void owl_pll_disable(struct clk_hw *hw)
14862306a36Sopenharmony_ci{
14962306a36Sopenharmony_ci	struct owl_pll *pll = hw_to_owl_pll(hw);
15062306a36Sopenharmony_ci	const struct owl_clk_common *common = &pll->common;
15162306a36Sopenharmony_ci
15262306a36Sopenharmony_ci	owl_pll_set(common, &pll->pll_hw, false);
15362306a36Sopenharmony_ci}
15462306a36Sopenharmony_ci
15562306a36Sopenharmony_cistatic int owl_pll_set_rate(struct clk_hw *hw, unsigned long rate,
15662306a36Sopenharmony_ci		unsigned long parent_rate)
15762306a36Sopenharmony_ci{
15862306a36Sopenharmony_ci	struct owl_pll *pll = hw_to_owl_pll(hw);
15962306a36Sopenharmony_ci	struct owl_pll_hw *pll_hw = &pll->pll_hw;
16062306a36Sopenharmony_ci	const struct owl_clk_common *common = &pll->common;
16162306a36Sopenharmony_ci	const struct clk_pll_table *clkt;
16262306a36Sopenharmony_ci	u32 val, reg;
16362306a36Sopenharmony_ci
16462306a36Sopenharmony_ci	/* fixed frequency */
16562306a36Sopenharmony_ci	if (pll_hw->width == 0)
16662306a36Sopenharmony_ci		return 0;
16762306a36Sopenharmony_ci
16862306a36Sopenharmony_ci	if (pll_hw->table) {
16962306a36Sopenharmony_ci		clkt = _get_pll_table(pll_hw->table, rate);
17062306a36Sopenharmony_ci		val = clkt->val;
17162306a36Sopenharmony_ci	} else {
17262306a36Sopenharmony_ci		val = owl_pll_calculate_mul(pll_hw, rate);
17362306a36Sopenharmony_ci	}
17462306a36Sopenharmony_ci
17562306a36Sopenharmony_ci	regmap_read(common->regmap, pll_hw->reg, &reg);
17662306a36Sopenharmony_ci
17762306a36Sopenharmony_ci	reg &= ~mul_mask(pll_hw);
17862306a36Sopenharmony_ci	reg |= val << pll_hw->shift;
17962306a36Sopenharmony_ci
18062306a36Sopenharmony_ci	regmap_write(common->regmap, pll_hw->reg, reg);
18162306a36Sopenharmony_ci
18262306a36Sopenharmony_ci	udelay(pll_hw->delay);
18362306a36Sopenharmony_ci
18462306a36Sopenharmony_ci	return 0;
18562306a36Sopenharmony_ci}
18662306a36Sopenharmony_ci
18762306a36Sopenharmony_ciconst struct clk_ops owl_pll_ops = {
18862306a36Sopenharmony_ci	.enable = owl_pll_enable,
18962306a36Sopenharmony_ci	.disable = owl_pll_disable,
19062306a36Sopenharmony_ci	.is_enabled = owl_pll_is_enabled,
19162306a36Sopenharmony_ci	.round_rate = owl_pll_round_rate,
19262306a36Sopenharmony_ci	.recalc_rate = owl_pll_recalc_rate,
19362306a36Sopenharmony_ci	.set_rate = owl_pll_set_rate,
19462306a36Sopenharmony_ci};
195