18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: (GPL-2.0 OR MIT) 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Copyright (c) 2019 BayLibre, SAS. 48c2ecf20Sopenharmony_ci * Author: Neil Armstrong <narmstrong@baylibre.com> 58c2ecf20Sopenharmony_ci */ 68c2ecf20Sopenharmony_ci 78c2ecf20Sopenharmony_ci#include <linux/clk-provider.h> 88c2ecf20Sopenharmony_ci#include <linux/module.h> 98c2ecf20Sopenharmony_ci 108c2ecf20Sopenharmony_ci#include "clk-regmap.h" 118c2ecf20Sopenharmony_ci#include "clk-cpu-dyndiv.h" 128c2ecf20Sopenharmony_ci 138c2ecf20Sopenharmony_cistatic inline struct meson_clk_cpu_dyndiv_data * 148c2ecf20Sopenharmony_cimeson_clk_cpu_dyndiv_data(struct clk_regmap *clk) 158c2ecf20Sopenharmony_ci{ 168c2ecf20Sopenharmony_ci return (struct meson_clk_cpu_dyndiv_data *)clk->data; 178c2ecf20Sopenharmony_ci} 188c2ecf20Sopenharmony_ci 198c2ecf20Sopenharmony_cistatic unsigned long meson_clk_cpu_dyndiv_recalc_rate(struct clk_hw *hw, 208c2ecf20Sopenharmony_ci unsigned long prate) 218c2ecf20Sopenharmony_ci{ 228c2ecf20Sopenharmony_ci struct clk_regmap *clk = to_clk_regmap(hw); 238c2ecf20Sopenharmony_ci struct meson_clk_cpu_dyndiv_data *data = meson_clk_cpu_dyndiv_data(clk); 248c2ecf20Sopenharmony_ci 258c2ecf20Sopenharmony_ci return divider_recalc_rate(hw, prate, 268c2ecf20Sopenharmony_ci meson_parm_read(clk->map, &data->div), 278c2ecf20Sopenharmony_ci NULL, 0, data->div.width); 288c2ecf20Sopenharmony_ci} 298c2ecf20Sopenharmony_ci 308c2ecf20Sopenharmony_cistatic long meson_clk_cpu_dyndiv_round_rate(struct clk_hw *hw, 318c2ecf20Sopenharmony_ci unsigned long rate, 328c2ecf20Sopenharmony_ci unsigned long *prate) 338c2ecf20Sopenharmony_ci{ 348c2ecf20Sopenharmony_ci struct clk_regmap *clk = to_clk_regmap(hw); 358c2ecf20Sopenharmony_ci struct meson_clk_cpu_dyndiv_data *data = meson_clk_cpu_dyndiv_data(clk); 368c2ecf20Sopenharmony_ci 378c2ecf20Sopenharmony_ci return divider_round_rate(hw, rate, prate, NULL, data->div.width, 0); 388c2ecf20Sopenharmony_ci} 398c2ecf20Sopenharmony_ci 408c2ecf20Sopenharmony_cistatic int meson_clk_cpu_dyndiv_set_rate(struct clk_hw *hw, unsigned long rate, 418c2ecf20Sopenharmony_ci unsigned long parent_rate) 428c2ecf20Sopenharmony_ci{ 438c2ecf20Sopenharmony_ci struct clk_regmap *clk = to_clk_regmap(hw); 448c2ecf20Sopenharmony_ci struct meson_clk_cpu_dyndiv_data *data = meson_clk_cpu_dyndiv_data(clk); 458c2ecf20Sopenharmony_ci unsigned int val; 468c2ecf20Sopenharmony_ci int ret; 478c2ecf20Sopenharmony_ci 488c2ecf20Sopenharmony_ci ret = divider_get_val(rate, parent_rate, NULL, data->div.width, 0); 498c2ecf20Sopenharmony_ci if (ret < 0) 508c2ecf20Sopenharmony_ci return ret; 518c2ecf20Sopenharmony_ci 528c2ecf20Sopenharmony_ci val = (unsigned int)ret << data->div.shift; 538c2ecf20Sopenharmony_ci 548c2ecf20Sopenharmony_ci /* Write the SYS_CPU_DYN_ENABLE bit before changing the divider */ 558c2ecf20Sopenharmony_ci meson_parm_write(clk->map, &data->dyn, 1); 568c2ecf20Sopenharmony_ci 578c2ecf20Sopenharmony_ci /* Update the divider while removing the SYS_CPU_DYN_ENABLE bit */ 588c2ecf20Sopenharmony_ci return regmap_update_bits(clk->map, data->div.reg_off, 598c2ecf20Sopenharmony_ci SETPMASK(data->div.width, data->div.shift) | 608c2ecf20Sopenharmony_ci SETPMASK(data->dyn.width, data->dyn.shift), 618c2ecf20Sopenharmony_ci val); 628c2ecf20Sopenharmony_ci}; 638c2ecf20Sopenharmony_ci 648c2ecf20Sopenharmony_ciconst struct clk_ops meson_clk_cpu_dyndiv_ops = { 658c2ecf20Sopenharmony_ci .recalc_rate = meson_clk_cpu_dyndiv_recalc_rate, 668c2ecf20Sopenharmony_ci .round_rate = meson_clk_cpu_dyndiv_round_rate, 678c2ecf20Sopenharmony_ci .set_rate = meson_clk_cpu_dyndiv_set_rate, 688c2ecf20Sopenharmony_ci}; 698c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(meson_clk_cpu_dyndiv_ops); 708c2ecf20Sopenharmony_ci 718c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Amlogic CPU Dynamic Clock divider"); 728c2ecf20Sopenharmony_ciMODULE_AUTHOR("Neil Armstrong <narmstrong@baylibre.com>"); 738c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL v2"); 74