18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0+
28c2ecf20Sopenharmony_ci//
38c2ecf20Sopenharmony_ci// OWL divider clock driver
48c2ecf20Sopenharmony_ci//
58c2ecf20Sopenharmony_ci// Copyright (c) 2014 Actions Semi Inc.
68c2ecf20Sopenharmony_ci// Author: David Liu <liuwei@actions-semi.com>
78c2ecf20Sopenharmony_ci//
88c2ecf20Sopenharmony_ci// Copyright (c) 2018 Linaro Ltd.
98c2ecf20Sopenharmony_ci// Author: Manivannan Sadhasivam <manivannan.sadhasivam@linaro.org>
108c2ecf20Sopenharmony_ci
118c2ecf20Sopenharmony_ci#include <linux/clk-provider.h>
128c2ecf20Sopenharmony_ci#include <linux/regmap.h>
138c2ecf20Sopenharmony_ci
148c2ecf20Sopenharmony_ci#include "owl-divider.h"
158c2ecf20Sopenharmony_ci
168c2ecf20Sopenharmony_cilong owl_divider_helper_round_rate(struct owl_clk_common *common,
178c2ecf20Sopenharmony_ci				const struct owl_divider_hw *div_hw,
188c2ecf20Sopenharmony_ci				unsigned long rate,
198c2ecf20Sopenharmony_ci				unsigned long *parent_rate)
208c2ecf20Sopenharmony_ci{
218c2ecf20Sopenharmony_ci	return divider_round_rate(&common->hw, rate, parent_rate,
228c2ecf20Sopenharmony_ci				  div_hw->table, div_hw->width,
238c2ecf20Sopenharmony_ci				  div_hw->div_flags);
248c2ecf20Sopenharmony_ci}
258c2ecf20Sopenharmony_ci
268c2ecf20Sopenharmony_cistatic long owl_divider_round_rate(struct clk_hw *hw, unsigned long rate,
278c2ecf20Sopenharmony_ci				unsigned long *parent_rate)
288c2ecf20Sopenharmony_ci{
298c2ecf20Sopenharmony_ci	struct owl_divider *div = hw_to_owl_divider(hw);
308c2ecf20Sopenharmony_ci
318c2ecf20Sopenharmony_ci	return owl_divider_helper_round_rate(&div->common, &div->div_hw,
328c2ecf20Sopenharmony_ci					     rate, parent_rate);
338c2ecf20Sopenharmony_ci}
348c2ecf20Sopenharmony_ci
358c2ecf20Sopenharmony_ciunsigned long owl_divider_helper_recalc_rate(struct owl_clk_common *common,
368c2ecf20Sopenharmony_ci					 const struct owl_divider_hw *div_hw,
378c2ecf20Sopenharmony_ci					 unsigned long parent_rate)
388c2ecf20Sopenharmony_ci{
398c2ecf20Sopenharmony_ci	unsigned long val;
408c2ecf20Sopenharmony_ci	unsigned int reg;
418c2ecf20Sopenharmony_ci
428c2ecf20Sopenharmony_ci	regmap_read(common->regmap, div_hw->reg, &reg);
438c2ecf20Sopenharmony_ci	val = reg >> div_hw->shift;
448c2ecf20Sopenharmony_ci	val &= (1 << div_hw->width) - 1;
458c2ecf20Sopenharmony_ci
468c2ecf20Sopenharmony_ci	return divider_recalc_rate(&common->hw, parent_rate,
478c2ecf20Sopenharmony_ci				   val, div_hw->table,
488c2ecf20Sopenharmony_ci				   div_hw->div_flags,
498c2ecf20Sopenharmony_ci				   div_hw->width);
508c2ecf20Sopenharmony_ci}
518c2ecf20Sopenharmony_ci
528c2ecf20Sopenharmony_cistatic unsigned long owl_divider_recalc_rate(struct clk_hw *hw,
538c2ecf20Sopenharmony_ci					  unsigned long parent_rate)
548c2ecf20Sopenharmony_ci{
558c2ecf20Sopenharmony_ci	struct owl_divider *div = hw_to_owl_divider(hw);
568c2ecf20Sopenharmony_ci
578c2ecf20Sopenharmony_ci	return owl_divider_helper_recalc_rate(&div->common,
588c2ecf20Sopenharmony_ci					      &div->div_hw, parent_rate);
598c2ecf20Sopenharmony_ci}
608c2ecf20Sopenharmony_ci
618c2ecf20Sopenharmony_ciint owl_divider_helper_set_rate(const struct owl_clk_common *common,
628c2ecf20Sopenharmony_ci				const struct owl_divider_hw *div_hw,
638c2ecf20Sopenharmony_ci				unsigned long rate,
648c2ecf20Sopenharmony_ci				unsigned long parent_rate)
658c2ecf20Sopenharmony_ci{
668c2ecf20Sopenharmony_ci	unsigned long val;
678c2ecf20Sopenharmony_ci	unsigned int reg;
688c2ecf20Sopenharmony_ci
698c2ecf20Sopenharmony_ci	val = divider_get_val(rate, parent_rate, div_hw->table,
708c2ecf20Sopenharmony_ci			      div_hw->width, 0);
718c2ecf20Sopenharmony_ci
728c2ecf20Sopenharmony_ci	regmap_read(common->regmap, div_hw->reg, &reg);
738c2ecf20Sopenharmony_ci	reg &= ~GENMASK(div_hw->width + div_hw->shift - 1, div_hw->shift);
748c2ecf20Sopenharmony_ci
758c2ecf20Sopenharmony_ci	regmap_write(common->regmap, div_hw->reg,
768c2ecf20Sopenharmony_ci			  reg | (val << div_hw->shift));
778c2ecf20Sopenharmony_ci
788c2ecf20Sopenharmony_ci	return 0;
798c2ecf20Sopenharmony_ci}
808c2ecf20Sopenharmony_ci
818c2ecf20Sopenharmony_cistatic int owl_divider_set_rate(struct clk_hw *hw, unsigned long rate,
828c2ecf20Sopenharmony_ci				unsigned long parent_rate)
838c2ecf20Sopenharmony_ci{
848c2ecf20Sopenharmony_ci	struct owl_divider *div = hw_to_owl_divider(hw);
858c2ecf20Sopenharmony_ci
868c2ecf20Sopenharmony_ci	return owl_divider_helper_set_rate(&div->common, &div->div_hw,
878c2ecf20Sopenharmony_ci					rate, parent_rate);
888c2ecf20Sopenharmony_ci}
898c2ecf20Sopenharmony_ci
908c2ecf20Sopenharmony_ciconst struct clk_ops owl_divider_ops = {
918c2ecf20Sopenharmony_ci	.recalc_rate = owl_divider_recalc_rate,
928c2ecf20Sopenharmony_ci	.round_rate = owl_divider_round_rate,
938c2ecf20Sopenharmony_ci	.set_rate = owl_divider_set_rate,
948c2ecf20Sopenharmony_ci};
95