diff options
Diffstat (limited to 'drivers/clk/meson')
-rw-r--r-- | drivers/clk/meson/Makefile | 1 | ||||
-rw-r--r-- | drivers/clk/meson/clk-phase.c | 63 | ||||
-rw-r--r-- | drivers/clk/meson/clkc.h | 8 |
3 files changed, 72 insertions, 0 deletions
diff --git a/drivers/clk/meson/Makefile b/drivers/clk/meson/Makefile index d0d13aeb369a..162e4a6f5c55 100644 --- a/drivers/clk/meson/Makefile +++ b/drivers/clk/meson/Makefile @@ -3,6 +3,7 @@ # obj-$(CONFIG_COMMON_CLK_AMLOGIC) += clk-pll.o clk-mpll.o clk-audio-divider.o +obj-$(CONFIG_COMMON_CLK_AMLOGIC) += clk-phase.o obj-$(CONFIG_COMMON_CLK_MESON_AO) += meson-aoclk.o obj-$(CONFIG_COMMON_CLK_MESON8B) += meson8b.o obj-$(CONFIG_COMMON_CLK_GXBB) += gxbb.o gxbb-aoclk.o gxbb-aoclk-32k.o diff --git a/drivers/clk/meson/clk-phase.c b/drivers/clk/meson/clk-phase.c new file mode 100644 index 000000000000..cba43748ce3d --- /dev/null +++ b/drivers/clk/meson/clk-phase.c @@ -0,0 +1,63 @@ +// SPDX-License-Identifier: (GPL-2.0 OR MIT) +/* + * Copyright (c) 2018 BayLibre, SAS. + * Author: Jerome Brunet <jbrunet@baylibre.com> + */ + +#include <linux/clk-provider.h> +#include "clkc.h" + +#define phase_step(_width) (360 / (1 << (_width))) + +static inline struct meson_clk_phase_data * +meson_clk_phase_data(struct clk_regmap *clk) +{ + return (struct meson_clk_phase_data *)clk->data; +} + +int meson_clk_degrees_from_val(unsigned int val, unsigned int width) +{ + return phase_step(width) * val; +} +EXPORT_SYMBOL_GPL(meson_clk_degrees_from_val); + +unsigned int meson_clk_degrees_to_val(int degrees, unsigned int width) +{ + unsigned int val = DIV_ROUND_CLOSEST(degrees, phase_step(width)); + + /* + * This last calculation is here for cases when degrees is rounded + * to 360, in which case val == (1 << width). + */ + return val % (1 << width); +} +EXPORT_SYMBOL_GPL(meson_clk_degrees_to_val); + +static int meson_clk_phase_get_phase(struct clk_hw *hw) +{ + struct clk_regmap *clk = to_clk_regmap(hw); + struct meson_clk_phase_data *phase = meson_clk_phase_data(clk); + unsigned int val; + + val = meson_parm_read(clk->map, &phase->ph); + + return meson_clk_degrees_from_val(val, phase->ph.width); +} + +static int meson_clk_phase_set_phase(struct clk_hw *hw, int degrees) +{ + struct clk_regmap *clk = to_clk_regmap(hw); + struct meson_clk_phase_data *phase = meson_clk_phase_data(clk); + unsigned int val; + + val = meson_clk_degrees_to_val(degrees, phase->ph.width); + meson_parm_write(clk->map, &phase->ph, val); + + return 0; +} + +const struct clk_ops meson_clk_phase_ops = { + .get_phase = meson_clk_phase_get_phase, + .set_phase = meson_clk_phase_set_phase, +}; +EXPORT_SYMBOL_GPL(meson_clk_phase_ops); diff --git a/drivers/clk/meson/clkc.h b/drivers/clk/meson/clkc.h index 2fb084330ee9..fd520ccdd1be 100644 --- a/drivers/clk/meson/clkc.h +++ b/drivers/clk/meson/clkc.h @@ -96,6 +96,13 @@ struct meson_clk_audio_div_data { u8 flags; }; +struct meson_clk_phase_data { + struct parm ph; +}; + +int meson_clk_degrees_from_val(unsigned int val, unsigned int width); +unsigned int meson_clk_degrees_to_val(int degrees, unsigned int width); + #define MESON_GATE(_name, _reg, _bit) \ struct clk_regmap _name = { \ .data = &(struct clk_regmap_gate_data){ \ @@ -119,5 +126,6 @@ extern const struct clk_ops meson_clk_mpll_ro_ops; extern const struct clk_ops meson_clk_mpll_ops; extern const struct clk_ops meson_clk_audio_divider_ro_ops; extern const struct clk_ops meson_clk_audio_divider_ops; +extern const struct clk_ops meson_clk_phase_ops; #endif /* __CLKC_H */ |