162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci// 362306a36Sopenharmony_ci// Spreadtrum divider clock driver 462306a36Sopenharmony_ci// 562306a36Sopenharmony_ci// Copyright (C) 2017 Spreadtrum, Inc. 662306a36Sopenharmony_ci// Author: Chunyan Zhang <chunyan.zhang@spreadtrum.com> 762306a36Sopenharmony_ci 862306a36Sopenharmony_ci#include <linux/clk-provider.h> 962306a36Sopenharmony_ci 1062306a36Sopenharmony_ci#include "div.h" 1162306a36Sopenharmony_ci 1262306a36Sopenharmony_cistatic long sprd_div_round_rate(struct clk_hw *hw, unsigned long rate, 1362306a36Sopenharmony_ci unsigned long *parent_rate) 1462306a36Sopenharmony_ci{ 1562306a36Sopenharmony_ci struct sprd_div *cd = hw_to_sprd_div(hw); 1662306a36Sopenharmony_ci 1762306a36Sopenharmony_ci return divider_round_rate(&cd->common.hw, rate, parent_rate, NULL, 1862306a36Sopenharmony_ci cd->div.width, 0); 1962306a36Sopenharmony_ci} 2062306a36Sopenharmony_ci 2162306a36Sopenharmony_ciunsigned long sprd_div_helper_recalc_rate(struct sprd_clk_common *common, 2262306a36Sopenharmony_ci const struct sprd_div_internal *div, 2362306a36Sopenharmony_ci unsigned long parent_rate) 2462306a36Sopenharmony_ci{ 2562306a36Sopenharmony_ci unsigned long val; 2662306a36Sopenharmony_ci unsigned int reg; 2762306a36Sopenharmony_ci 2862306a36Sopenharmony_ci regmap_read(common->regmap, common->reg, ®); 2962306a36Sopenharmony_ci val = reg >> div->shift; 3062306a36Sopenharmony_ci val &= (1 << div->width) - 1; 3162306a36Sopenharmony_ci 3262306a36Sopenharmony_ci return divider_recalc_rate(&common->hw, parent_rate, val, NULL, 0, 3362306a36Sopenharmony_ci div->width); 3462306a36Sopenharmony_ci} 3562306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(sprd_div_helper_recalc_rate); 3662306a36Sopenharmony_ci 3762306a36Sopenharmony_cistatic unsigned long sprd_div_recalc_rate(struct clk_hw *hw, 3862306a36Sopenharmony_ci unsigned long parent_rate) 3962306a36Sopenharmony_ci{ 4062306a36Sopenharmony_ci struct sprd_div *cd = hw_to_sprd_div(hw); 4162306a36Sopenharmony_ci 4262306a36Sopenharmony_ci return sprd_div_helper_recalc_rate(&cd->common, &cd->div, parent_rate); 4362306a36Sopenharmony_ci} 4462306a36Sopenharmony_ci 4562306a36Sopenharmony_ciint sprd_div_helper_set_rate(const struct sprd_clk_common *common, 4662306a36Sopenharmony_ci const struct sprd_div_internal *div, 4762306a36Sopenharmony_ci unsigned long rate, 4862306a36Sopenharmony_ci unsigned long parent_rate) 4962306a36Sopenharmony_ci{ 5062306a36Sopenharmony_ci unsigned long val; 5162306a36Sopenharmony_ci unsigned int reg; 5262306a36Sopenharmony_ci 5362306a36Sopenharmony_ci val = divider_get_val(rate, parent_rate, NULL, 5462306a36Sopenharmony_ci div->width, 0); 5562306a36Sopenharmony_ci 5662306a36Sopenharmony_ci regmap_read(common->regmap, common->reg, ®); 5762306a36Sopenharmony_ci reg &= ~GENMASK(div->width + div->shift - 1, div->shift); 5862306a36Sopenharmony_ci 5962306a36Sopenharmony_ci regmap_write(common->regmap, common->reg, 6062306a36Sopenharmony_ci reg | (val << div->shift)); 6162306a36Sopenharmony_ci 6262306a36Sopenharmony_ci return 0; 6362306a36Sopenharmony_ci 6462306a36Sopenharmony_ci} 6562306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(sprd_div_helper_set_rate); 6662306a36Sopenharmony_ci 6762306a36Sopenharmony_cistatic int sprd_div_set_rate(struct clk_hw *hw, unsigned long rate, 6862306a36Sopenharmony_ci unsigned long parent_rate) 6962306a36Sopenharmony_ci{ 7062306a36Sopenharmony_ci struct sprd_div *cd = hw_to_sprd_div(hw); 7162306a36Sopenharmony_ci 7262306a36Sopenharmony_ci return sprd_div_helper_set_rate(&cd->common, &cd->div, 7362306a36Sopenharmony_ci rate, parent_rate); 7462306a36Sopenharmony_ci} 7562306a36Sopenharmony_ci 7662306a36Sopenharmony_ciconst struct clk_ops sprd_div_ops = { 7762306a36Sopenharmony_ci .recalc_rate = sprd_div_recalc_rate, 7862306a36Sopenharmony_ci .round_rate = sprd_div_round_rate, 7962306a36Sopenharmony_ci .set_rate = sprd_div_set_rate, 8062306a36Sopenharmony_ci}; 8162306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(sprd_div_ops); 82