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, ®); 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, ®); 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