162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0+ 262306a36Sopenharmony_ci// 362306a36Sopenharmony_ci// OWL divider 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/regmap.h> 1362306a36Sopenharmony_ci 1462306a36Sopenharmony_ci#include "owl-divider.h" 1562306a36Sopenharmony_ci 1662306a36Sopenharmony_cilong owl_divider_helper_round_rate(struct owl_clk_common *common, 1762306a36Sopenharmony_ci const struct owl_divider_hw *div_hw, 1862306a36Sopenharmony_ci unsigned long rate, 1962306a36Sopenharmony_ci unsigned long *parent_rate) 2062306a36Sopenharmony_ci{ 2162306a36Sopenharmony_ci return divider_round_rate(&common->hw, rate, parent_rate, 2262306a36Sopenharmony_ci div_hw->table, div_hw->width, 2362306a36Sopenharmony_ci div_hw->div_flags); 2462306a36Sopenharmony_ci} 2562306a36Sopenharmony_ci 2662306a36Sopenharmony_cistatic long owl_divider_round_rate(struct clk_hw *hw, unsigned long rate, 2762306a36Sopenharmony_ci unsigned long *parent_rate) 2862306a36Sopenharmony_ci{ 2962306a36Sopenharmony_ci struct owl_divider *div = hw_to_owl_divider(hw); 3062306a36Sopenharmony_ci 3162306a36Sopenharmony_ci return owl_divider_helper_round_rate(&div->common, &div->div_hw, 3262306a36Sopenharmony_ci rate, parent_rate); 3362306a36Sopenharmony_ci} 3462306a36Sopenharmony_ci 3562306a36Sopenharmony_ciunsigned long owl_divider_helper_recalc_rate(struct owl_clk_common *common, 3662306a36Sopenharmony_ci const struct owl_divider_hw *div_hw, 3762306a36Sopenharmony_ci unsigned long parent_rate) 3862306a36Sopenharmony_ci{ 3962306a36Sopenharmony_ci unsigned long val; 4062306a36Sopenharmony_ci unsigned int reg; 4162306a36Sopenharmony_ci 4262306a36Sopenharmony_ci regmap_read(common->regmap, div_hw->reg, ®); 4362306a36Sopenharmony_ci val = reg >> div_hw->shift; 4462306a36Sopenharmony_ci val &= (1 << div_hw->width) - 1; 4562306a36Sopenharmony_ci 4662306a36Sopenharmony_ci return divider_recalc_rate(&common->hw, parent_rate, 4762306a36Sopenharmony_ci val, div_hw->table, 4862306a36Sopenharmony_ci div_hw->div_flags, 4962306a36Sopenharmony_ci div_hw->width); 5062306a36Sopenharmony_ci} 5162306a36Sopenharmony_ci 5262306a36Sopenharmony_cistatic unsigned long owl_divider_recalc_rate(struct clk_hw *hw, 5362306a36Sopenharmony_ci unsigned long parent_rate) 5462306a36Sopenharmony_ci{ 5562306a36Sopenharmony_ci struct owl_divider *div = hw_to_owl_divider(hw); 5662306a36Sopenharmony_ci 5762306a36Sopenharmony_ci return owl_divider_helper_recalc_rate(&div->common, 5862306a36Sopenharmony_ci &div->div_hw, parent_rate); 5962306a36Sopenharmony_ci} 6062306a36Sopenharmony_ci 6162306a36Sopenharmony_ciint owl_divider_helper_set_rate(const struct owl_clk_common *common, 6262306a36Sopenharmony_ci const struct owl_divider_hw *div_hw, 6362306a36Sopenharmony_ci unsigned long rate, 6462306a36Sopenharmony_ci unsigned long parent_rate) 6562306a36Sopenharmony_ci{ 6662306a36Sopenharmony_ci unsigned long val; 6762306a36Sopenharmony_ci unsigned int reg; 6862306a36Sopenharmony_ci 6962306a36Sopenharmony_ci val = divider_get_val(rate, parent_rate, div_hw->table, 7062306a36Sopenharmony_ci div_hw->width, 0); 7162306a36Sopenharmony_ci 7262306a36Sopenharmony_ci regmap_read(common->regmap, div_hw->reg, ®); 7362306a36Sopenharmony_ci reg &= ~GENMASK(div_hw->width + div_hw->shift - 1, div_hw->shift); 7462306a36Sopenharmony_ci 7562306a36Sopenharmony_ci regmap_write(common->regmap, div_hw->reg, 7662306a36Sopenharmony_ci reg | (val << div_hw->shift)); 7762306a36Sopenharmony_ci 7862306a36Sopenharmony_ci return 0; 7962306a36Sopenharmony_ci} 8062306a36Sopenharmony_ci 8162306a36Sopenharmony_cistatic int owl_divider_set_rate(struct clk_hw *hw, unsigned long rate, 8262306a36Sopenharmony_ci unsigned long parent_rate) 8362306a36Sopenharmony_ci{ 8462306a36Sopenharmony_ci struct owl_divider *div = hw_to_owl_divider(hw); 8562306a36Sopenharmony_ci 8662306a36Sopenharmony_ci return owl_divider_helper_set_rate(&div->common, &div->div_hw, 8762306a36Sopenharmony_ci rate, parent_rate); 8862306a36Sopenharmony_ci} 8962306a36Sopenharmony_ci 9062306a36Sopenharmony_ciconst struct clk_ops owl_divider_ops = { 9162306a36Sopenharmony_ci .recalc_rate = owl_divider_recalc_rate, 9262306a36Sopenharmony_ci .round_rate = owl_divider_round_rate, 9362306a36Sopenharmony_ci .set_rate = owl_divider_set_rate, 9462306a36Sopenharmony_ci}; 95