1// SPDX-License-Identifier: GPL-2.0 2/* 3 * Copyright (c) 2024 Neil Armstrong <neil.armstrong@linaro.org> 4 */ 5 6#include <linux/module.h> 7#include "vclk.h" 8 9/* The VCLK gate has a supplementary reset bit to pulse after ungating */ 10 11static inline struct meson_vclk_gate_data * 12clk_get_meson_vclk_gate_data(struct clk_regmap *clk) 13{ 14 return (struct meson_vclk_gate_data *)clk->data; 15} 16 17static int meson_vclk_gate_enable(struct clk_hw *hw) 18{ 19 struct clk_regmap *clk = to_clk_regmap(hw); 20 struct meson_vclk_gate_data *vclk = clk_get_meson_vclk_gate_data(clk); 21 22 meson_parm_write(clk->map, &vclk->enable, 1); 23 24 /* Do a reset pulse */ 25 meson_parm_write(clk->map, &vclk->reset, 1); 26 meson_parm_write(clk->map, &vclk->reset, 0); 27 28 return 0; 29} 30 31static void meson_vclk_gate_disable(struct clk_hw *hw) 32{ 33 struct clk_regmap *clk = to_clk_regmap(hw); 34 struct meson_vclk_gate_data *vclk = clk_get_meson_vclk_gate_data(clk); 35 36 meson_parm_write(clk->map, &vclk->enable, 0); 37} 38 39static int meson_vclk_gate_is_enabled(struct clk_hw *hw) 40{ 41 struct clk_regmap *clk = to_clk_regmap(hw); 42 struct meson_vclk_gate_data *vclk = clk_get_meson_vclk_gate_data(clk); 43 44 return meson_parm_read(clk->map, &vclk->enable); 45} 46 47const struct clk_ops meson_vclk_gate_ops = { 48 .enable = meson_vclk_gate_enable, 49 .disable = meson_vclk_gate_disable, 50 .is_enabled = meson_vclk_gate_is_enabled, 51}; 52EXPORT_SYMBOL_GPL(meson_vclk_gate_ops); 53 54/* The VCLK Divider has supplementary reset & enable bits */ 55 56static inline struct meson_vclk_div_data * 57clk_get_meson_vclk_div_data(struct clk_regmap *clk) 58{ 59 return (struct meson_vclk_div_data *)clk->data; 60} 61 62static unsigned long meson_vclk_div_recalc_rate(struct clk_hw *hw, 63 unsigned long prate) 64{ 65 struct clk_regmap *clk = to_clk_regmap(hw); 66 struct meson_vclk_div_data *vclk = clk_get_meson_vclk_div_data(clk); 67 68 return divider_recalc_rate(hw, prate, meson_parm_read(clk->map, &vclk->div), 69 vclk->table, vclk->flags, vclk->div.width); 70} 71 72static int meson_vclk_div_determine_rate(struct clk_hw *hw, 73 struct clk_rate_request *req) 74{ 75 struct clk_regmap *clk = to_clk_regmap(hw); 76 struct meson_vclk_div_data *vclk = clk_get_meson_vclk_div_data(clk); 77 78 return divider_determine_rate(hw, req, vclk->table, vclk->div.width, 79 vclk->flags); 80} 81 82static int meson_vclk_div_set_rate(struct clk_hw *hw, unsigned long rate, 83 unsigned long parent_rate) 84{ 85 struct clk_regmap *clk = to_clk_regmap(hw); 86 struct meson_vclk_div_data *vclk = clk_get_meson_vclk_div_data(clk); 87 int ret; 88 89 ret = divider_get_val(rate, parent_rate, vclk->table, vclk->div.width, 90 vclk->flags); 91 if (ret < 0) 92 return ret; 93 94 meson_parm_write(clk->map, &vclk->div, ret); 95 96 return 0; 97}; 98 99static int meson_vclk_div_enable(struct clk_hw *hw) 100{ 101 struct clk_regmap *clk = to_clk_regmap(hw); 102 struct meson_vclk_div_data *vclk = clk_get_meson_vclk_div_data(clk); 103 104 /* Unreset the divider when ungating */ 105 meson_parm_write(clk->map, &vclk->reset, 0); 106 meson_parm_write(clk->map, &vclk->enable, 1); 107 108 return 0; 109} 110 111static void meson_vclk_div_disable(struct clk_hw *hw) 112{ 113 struct clk_regmap *clk = to_clk_regmap(hw); 114 struct meson_vclk_div_data *vclk = clk_get_meson_vclk_div_data(clk); 115 116 /* Reset the divider when gating */ 117 meson_parm_write(clk->map, &vclk->enable, 0); 118 meson_parm_write(clk->map, &vclk->reset, 1); 119} 120 121static int meson_vclk_div_is_enabled(struct clk_hw *hw) 122{ 123 struct clk_regmap *clk = to_clk_regmap(hw); 124 struct meson_vclk_div_data *vclk = clk_get_meson_vclk_div_data(clk); 125 126 return meson_parm_read(clk->map, &vclk->enable); 127} 128 129const struct clk_ops meson_vclk_div_ops = { 130 .recalc_rate = meson_vclk_div_recalc_rate, 131 .determine_rate = meson_vclk_div_determine_rate, 132 .set_rate = meson_vclk_div_set_rate, 133 .enable = meson_vclk_div_enable, 134 .disable = meson_vclk_div_disable, 135 .is_enabled = meson_vclk_div_is_enabled, 136}; 137EXPORT_SYMBOL_GPL(meson_vclk_div_ops); 138 139MODULE_DESCRIPTION("Amlogic vclk clock driver"); 140MODULE_AUTHOR("Neil Armstrong <neil.armstrong@linaro.org>"); 141MODULE_LICENSE("GPL"); 142