xref: /openbmc/linux/drivers/clk/qcom/clk-regmap-divider.c (revision 12a26c298d2a8b1cab498533fa65198e49e3afd3)
14116076eSJosh Cartwright /*
24116076eSJosh Cartwright  * Copyright (c) 2014, The Linux Foundation. All rights reserved.
34116076eSJosh Cartwright  *
44116076eSJosh Cartwright  * This software is licensed under the terms of the GNU General Public
54116076eSJosh Cartwright  * License version 2, as published by the Free Software Foundation, and
64116076eSJosh Cartwright  * may be copied, distributed, and modified under those terms.
74116076eSJosh Cartwright  *
84116076eSJosh Cartwright  * This program is distributed in the hope that it will be useful,
94116076eSJosh Cartwright  * but WITHOUT ANY WARRANTY; without even the implied warranty of
104116076eSJosh Cartwright  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
114116076eSJosh Cartwright  * GNU General Public License for more details.
124116076eSJosh Cartwright  */
134116076eSJosh Cartwright 
144116076eSJosh Cartwright #include <linux/kernel.h>
154116076eSJosh Cartwright #include <linux/bitops.h>
164116076eSJosh Cartwright #include <linux/regmap.h>
174116076eSJosh Cartwright #include <linux/export.h>
184116076eSJosh Cartwright 
194116076eSJosh Cartwright #include "clk-regmap-divider.h"
204116076eSJosh Cartwright 
214116076eSJosh Cartwright static inline struct clk_regmap_div *to_clk_regmap_div(struct clk_hw *hw)
224116076eSJosh Cartwright {
234116076eSJosh Cartwright 	return container_of(to_clk_regmap(hw), struct clk_regmap_div, clkr);
244116076eSJosh Cartwright }
254116076eSJosh Cartwright 
264116076eSJosh Cartwright static long div_round_rate(struct clk_hw *hw, unsigned long rate,
274116076eSJosh Cartwright 			   unsigned long *prate)
284116076eSJosh Cartwright {
294116076eSJosh Cartwright 	struct clk_regmap_div *divider = to_clk_regmap_div(hw);
304116076eSJosh Cartwright 
314116076eSJosh Cartwright 	return divider_round_rate(hw, rate, prate, NULL, divider->width,
324116076eSJosh Cartwright 				  CLK_DIVIDER_ROUND_CLOSEST);
334116076eSJosh Cartwright }
344116076eSJosh Cartwright 
354116076eSJosh Cartwright static int div_set_rate(struct clk_hw *hw, unsigned long rate,
364116076eSJosh Cartwright 			unsigned long parent_rate)
374116076eSJosh Cartwright {
384116076eSJosh Cartwright 	struct clk_regmap_div *divider = to_clk_regmap_div(hw);
394116076eSJosh Cartwright 	struct clk_regmap *clkr = &divider->clkr;
404116076eSJosh Cartwright 	u32 div;
414116076eSJosh Cartwright 
424116076eSJosh Cartwright 	div = divider_get_val(rate, parent_rate, NULL, divider->width,
434116076eSJosh Cartwright 			      CLK_DIVIDER_ROUND_CLOSEST);
444116076eSJosh Cartwright 
454116076eSJosh Cartwright 	return regmap_update_bits(clkr->regmap, divider->reg,
464116076eSJosh Cartwright 				  (BIT(divider->width) - 1) << divider->shift,
474116076eSJosh Cartwright 				  div << divider->shift);
484116076eSJosh Cartwright }
494116076eSJosh Cartwright 
504116076eSJosh Cartwright static unsigned long div_recalc_rate(struct clk_hw *hw,
514116076eSJosh Cartwright 				     unsigned long parent_rate)
524116076eSJosh Cartwright {
534116076eSJosh Cartwright 	struct clk_regmap_div *divider = to_clk_regmap_div(hw);
544116076eSJosh Cartwright 	struct clk_regmap *clkr = &divider->clkr;
554116076eSJosh Cartwright 	u32 div;
564116076eSJosh Cartwright 
574116076eSJosh Cartwright 	regmap_read(clkr->regmap, divider->reg, &div);
584116076eSJosh Cartwright 	div >>= divider->shift;
594116076eSJosh Cartwright 	div &= BIT(divider->width) - 1;
604116076eSJosh Cartwright 
614116076eSJosh Cartwright 	return divider_recalc_rate(hw, parent_rate, div, NULL,
62*12a26c29SJerome Brunet 				   CLK_DIVIDER_ROUND_CLOSEST, divider->width);
634116076eSJosh Cartwright }
644116076eSJosh Cartwright 
654116076eSJosh Cartwright const struct clk_ops clk_regmap_div_ops = {
664116076eSJosh Cartwright 	.round_rate = div_round_rate,
674116076eSJosh Cartwright 	.set_rate = div_set_rate,
684116076eSJosh Cartwright 	.recalc_rate = div_recalc_rate,
694116076eSJosh Cartwright };
704116076eSJosh Cartwright EXPORT_SYMBOL_GPL(clk_regmap_div_ops);
71