1 // SPDX-License-Identifier: GPL-2.0-only 2 /* 3 * Copyright (c) 2014, The Linux Foundation. All rights reserved. 4 */ 5 6 #include <linux/kernel.h> 7 #include <linux/bitops.h> 8 #include <linux/regmap.h> 9 #include <linux/export.h> 10 11 #include "clk-regmap-divider.h" 12 13 static inline struct clk_regmap_div *to_clk_regmap_div(struct clk_hw *hw) 14 { 15 return container_of(to_clk_regmap(hw), struct clk_regmap_div, clkr); 16 } 17 18 static int div_ro_determine_rate(struct clk_hw *hw, 19 struct clk_rate_request *req) 20 { 21 struct clk_regmap_div *divider = to_clk_regmap_div(hw); 22 struct clk_regmap *clkr = ÷r->clkr; 23 u32 val; 24 25 regmap_read(clkr->regmap, divider->reg, &val); 26 val >>= divider->shift; 27 val &= BIT(divider->width) - 1; 28 29 req->rate = divider_ro_round_rate(hw, req->rate, 30 &req->best_parent_rate, NULL, 31 divider->width, 32 CLK_DIVIDER_ROUND_CLOSEST, val); 33 34 return 0; 35 } 36 37 static int div_determine_rate(struct clk_hw *hw, struct clk_rate_request *req) 38 { 39 struct clk_regmap_div *divider = to_clk_regmap_div(hw); 40 41 req->rate = divider_round_rate(hw, req->rate, &req->best_parent_rate, 42 NULL, 43 divider->width, 44 CLK_DIVIDER_ROUND_CLOSEST); 45 46 return 0; 47 } 48 49 static int div_set_rate(struct clk_hw *hw, unsigned long rate, 50 unsigned long parent_rate) 51 { 52 struct clk_regmap_div *divider = to_clk_regmap_div(hw); 53 struct clk_regmap *clkr = ÷r->clkr; 54 u32 div; 55 56 div = divider_get_val(rate, parent_rate, NULL, divider->width, 57 CLK_DIVIDER_ROUND_CLOSEST); 58 59 return regmap_update_bits(clkr->regmap, divider->reg, 60 (BIT(divider->width) - 1) << divider->shift, 61 div << divider->shift); 62 } 63 64 static unsigned long div_recalc_rate(struct clk_hw *hw, 65 unsigned long parent_rate) 66 { 67 struct clk_regmap_div *divider = to_clk_regmap_div(hw); 68 struct clk_regmap *clkr = ÷r->clkr; 69 u32 div; 70 71 regmap_read(clkr->regmap, divider->reg, &div); 72 div >>= divider->shift; 73 div &= BIT(divider->width) - 1; 74 75 return divider_recalc_rate(hw, parent_rate, div, NULL, 76 CLK_DIVIDER_ROUND_CLOSEST, divider->width); 77 } 78 79 const struct clk_ops clk_regmap_div_ops = { 80 .determine_rate = div_determine_rate, 81 .set_rate = div_set_rate, 82 .recalc_rate = div_recalc_rate, 83 }; 84 EXPORT_SYMBOL_GPL(clk_regmap_div_ops); 85 86 const struct clk_ops clk_regmap_div_ro_ops = { 87 .determine_rate = div_ro_determine_rate, 88 .recalc_rate = div_recalc_rate, 89 }; 90 EXPORT_SYMBOL_GPL(clk_regmap_div_ro_ops); 91