1e3f05d3bSChunyan Zhang // SPDX-License-Identifier: GPL-2.0 2e3f05d3bSChunyan Zhang // 3e3f05d3bSChunyan Zhang // Spreadtrum divider clock driver 4e3f05d3bSChunyan Zhang // 5e3f05d3bSChunyan Zhang // Copyright (C) 2017 Spreadtrum, Inc. 6e3f05d3bSChunyan Zhang // Author: Chunyan Zhang <chunyan.zhang@spreadtrum.com> 7e3f05d3bSChunyan Zhang 8e3f05d3bSChunyan Zhang #include <linux/clk-provider.h> 9e3f05d3bSChunyan Zhang 10e3f05d3bSChunyan Zhang #include "div.h" 11e3f05d3bSChunyan Zhang 12e3f05d3bSChunyan Zhang static long sprd_div_round_rate(struct clk_hw *hw, unsigned long rate, 13e3f05d3bSChunyan Zhang unsigned long *parent_rate) 14e3f05d3bSChunyan Zhang { 15e3f05d3bSChunyan Zhang struct sprd_div *cd = hw_to_sprd_div(hw); 16e3f05d3bSChunyan Zhang 17*587dd448SStephen Boyd return divider_round_rate(&cd->common.hw, rate, parent_rate, NULL, 18*587dd448SStephen Boyd cd->div.width, 0); 19e3f05d3bSChunyan Zhang } 20e3f05d3bSChunyan Zhang 21e3f05d3bSChunyan Zhang unsigned long sprd_div_helper_recalc_rate(struct sprd_clk_common *common, 22e3f05d3bSChunyan Zhang const struct sprd_div_internal *div, 23e3f05d3bSChunyan Zhang unsigned long parent_rate) 24e3f05d3bSChunyan Zhang { 25e3f05d3bSChunyan Zhang unsigned long val; 26e3f05d3bSChunyan Zhang unsigned int reg; 27e3f05d3bSChunyan Zhang 28e3f05d3bSChunyan Zhang regmap_read(common->regmap, common->reg, ®); 29e3f05d3bSChunyan Zhang val = reg >> div->shift; 30e3f05d3bSChunyan Zhang val &= (1 << div->width) - 1; 31e3f05d3bSChunyan Zhang 32716d9b1dSStephen Boyd return divider_recalc_rate(&common->hw, parent_rate, val, NULL, 0, 33716d9b1dSStephen Boyd div->width); 34e3f05d3bSChunyan Zhang } 35e3f05d3bSChunyan Zhang EXPORT_SYMBOL_GPL(sprd_div_helper_recalc_rate); 36e3f05d3bSChunyan Zhang 37e3f05d3bSChunyan Zhang static unsigned long sprd_div_recalc_rate(struct clk_hw *hw, 38e3f05d3bSChunyan Zhang unsigned long parent_rate) 39e3f05d3bSChunyan Zhang { 40e3f05d3bSChunyan Zhang struct sprd_div *cd = hw_to_sprd_div(hw); 41e3f05d3bSChunyan Zhang 42e3f05d3bSChunyan Zhang return sprd_div_helper_recalc_rate(&cd->common, &cd->div, parent_rate); 43e3f05d3bSChunyan Zhang } 44e3f05d3bSChunyan Zhang 45e3f05d3bSChunyan Zhang int sprd_div_helper_set_rate(const struct sprd_clk_common *common, 46e3f05d3bSChunyan Zhang const struct sprd_div_internal *div, 47e3f05d3bSChunyan Zhang unsigned long rate, 48e3f05d3bSChunyan Zhang unsigned long parent_rate) 49e3f05d3bSChunyan Zhang { 50e3f05d3bSChunyan Zhang unsigned long val; 51e3f05d3bSChunyan Zhang unsigned int reg; 52e3f05d3bSChunyan Zhang 53e3f05d3bSChunyan Zhang val = divider_get_val(rate, parent_rate, NULL, 54e3f05d3bSChunyan Zhang div->width, 0); 55e3f05d3bSChunyan Zhang 56e3f05d3bSChunyan Zhang regmap_read(common->regmap, common->reg, ®); 57e3f05d3bSChunyan Zhang reg &= ~GENMASK(div->width + div->shift - 1, div->shift); 58e3f05d3bSChunyan Zhang 59e3f05d3bSChunyan Zhang regmap_write(common->regmap, common->reg, 60e3f05d3bSChunyan Zhang reg | (val << div->shift)); 61e3f05d3bSChunyan Zhang 62e3f05d3bSChunyan Zhang return 0; 63e3f05d3bSChunyan Zhang 64e3f05d3bSChunyan Zhang } 65e3f05d3bSChunyan Zhang EXPORT_SYMBOL_GPL(sprd_div_helper_set_rate); 66e3f05d3bSChunyan Zhang 67e3f05d3bSChunyan Zhang static int sprd_div_set_rate(struct clk_hw *hw, unsigned long rate, 68e3f05d3bSChunyan Zhang unsigned long parent_rate) 69e3f05d3bSChunyan Zhang { 70e3f05d3bSChunyan Zhang struct sprd_div *cd = hw_to_sprd_div(hw); 71e3f05d3bSChunyan Zhang 72e3f05d3bSChunyan Zhang return sprd_div_helper_set_rate(&cd->common, &cd->div, 73e3f05d3bSChunyan Zhang rate, parent_rate); 74e3f05d3bSChunyan Zhang } 75e3f05d3bSChunyan Zhang 76e3f05d3bSChunyan Zhang const struct clk_ops sprd_div_ops = { 77e3f05d3bSChunyan Zhang .recalc_rate = sprd_div_recalc_rate, 78e3f05d3bSChunyan Zhang .round_rate = sprd_div_round_rate, 79e3f05d3bSChunyan Zhang .set_rate = sprd_div_set_rate, 80e3f05d3bSChunyan Zhang }; 81e3f05d3bSChunyan Zhang EXPORT_SYMBOL_GPL(sprd_div_ops); 82