1 // SPDX-License-Identifier: (GPL-2.0 OR MIT) 2 /* 3 * Copyright (c) 2018 BayLibre, SAS. 4 * Author: Jerome Brunet <jbrunet@baylibre.com> 5 */ 6 7 #include <linux/clk-provider.h> 8 #include "clkc.h" 9 10 #define phase_step(_width) (360 / (1 << (_width))) 11 12 static inline struct meson_clk_phase_data * 13 meson_clk_phase_data(struct clk_regmap *clk) 14 { 15 return (struct meson_clk_phase_data *)clk->data; 16 } 17 18 int meson_clk_degrees_from_val(unsigned int val, unsigned int width) 19 { 20 return phase_step(width) * val; 21 } 22 EXPORT_SYMBOL_GPL(meson_clk_degrees_from_val); 23 24 unsigned int meson_clk_degrees_to_val(int degrees, unsigned int width) 25 { 26 unsigned int val = DIV_ROUND_CLOSEST(degrees, phase_step(width)); 27 28 /* 29 * This last calculation is here for cases when degrees is rounded 30 * to 360, in which case val == (1 << width). 31 */ 32 return val % (1 << width); 33 } 34 EXPORT_SYMBOL_GPL(meson_clk_degrees_to_val); 35 36 static int meson_clk_phase_get_phase(struct clk_hw *hw) 37 { 38 struct clk_regmap *clk = to_clk_regmap(hw); 39 struct meson_clk_phase_data *phase = meson_clk_phase_data(clk); 40 unsigned int val; 41 42 val = meson_parm_read(clk->map, &phase->ph); 43 44 return meson_clk_degrees_from_val(val, phase->ph.width); 45 } 46 47 static int meson_clk_phase_set_phase(struct clk_hw *hw, int degrees) 48 { 49 struct clk_regmap *clk = to_clk_regmap(hw); 50 struct meson_clk_phase_data *phase = meson_clk_phase_data(clk); 51 unsigned int val; 52 53 val = meson_clk_degrees_to_val(degrees, phase->ph.width); 54 meson_parm_write(clk->map, &phase->ph, val); 55 56 return 0; 57 } 58 59 const struct clk_ops meson_clk_phase_ops = { 60 .get_phase = meson_clk_phase_get_phase, 61 .set_phase = meson_clk_phase_set_phase, 62 }; 63 EXPORT_SYMBOL_GPL(meson_clk_phase_ops); 64