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 11 static inline struct meson_vclk_gate_data * 12 clk_get_meson_vclk_gate_data(struct clk_regmap *clk) 13 { 14 return (struct meson_vclk_gate_data *)clk->data; 15 } 16 17 static 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 31 static 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 39 static 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 47 const struct clk_ops meson_vclk_gate_ops = { 48 .init = clk_regmap_init, 49 .enable = meson_vclk_gate_enable, 50 .disable = meson_vclk_gate_disable, 51 .is_enabled = meson_vclk_gate_is_enabled, 52 }; 53 EXPORT_SYMBOL_NS_GPL(meson_vclk_gate_ops, "CLK_MESON"); 54 55 /* The VCLK Divider has supplementary reset & enable bits */ 56 57 static inline struct meson_vclk_div_data * 58 clk_get_meson_vclk_div_data(struct clk_regmap *clk) 59 { 60 return (struct meson_vclk_div_data *)clk->data; 61 } 62 63 static unsigned long meson_vclk_div_recalc_rate(struct clk_hw *hw, 64 unsigned long prate) 65 { 66 struct clk_regmap *clk = to_clk_regmap(hw); 67 struct meson_vclk_div_data *vclk = clk_get_meson_vclk_div_data(clk); 68 69 return divider_recalc_rate(hw, prate, meson_parm_read(clk->map, &vclk->div), 70 vclk->table, vclk->flags, vclk->div.width); 71 } 72 73 static int meson_vclk_div_determine_rate(struct clk_hw *hw, 74 struct clk_rate_request *req) 75 { 76 struct clk_regmap *clk = to_clk_regmap(hw); 77 struct meson_vclk_div_data *vclk = clk_get_meson_vclk_div_data(clk); 78 79 return divider_determine_rate(hw, req, vclk->table, vclk->div.width, 80 vclk->flags); 81 } 82 83 static int meson_vclk_div_set_rate(struct clk_hw *hw, unsigned long rate, 84 unsigned long parent_rate) 85 { 86 struct clk_regmap *clk = to_clk_regmap(hw); 87 struct meson_vclk_div_data *vclk = clk_get_meson_vclk_div_data(clk); 88 int ret; 89 90 ret = divider_get_val(rate, parent_rate, vclk->table, vclk->div.width, 91 vclk->flags); 92 if (ret < 0) 93 return ret; 94 95 meson_parm_write(clk->map, &vclk->div, ret); 96 97 return 0; 98 }; 99 100 static int meson_vclk_div_enable(struct clk_hw *hw) 101 { 102 struct clk_regmap *clk = to_clk_regmap(hw); 103 struct meson_vclk_div_data *vclk = clk_get_meson_vclk_div_data(clk); 104 105 /* Unreset the divider when ungating */ 106 meson_parm_write(clk->map, &vclk->reset, 0); 107 meson_parm_write(clk->map, &vclk->enable, 1); 108 109 return 0; 110 } 111 112 static void meson_vclk_div_disable(struct clk_hw *hw) 113 { 114 struct clk_regmap *clk = to_clk_regmap(hw); 115 struct meson_vclk_div_data *vclk = clk_get_meson_vclk_div_data(clk); 116 117 /* Reset the divider when gating */ 118 meson_parm_write(clk->map, &vclk->enable, 0); 119 meson_parm_write(clk->map, &vclk->reset, 1); 120 } 121 122 static int meson_vclk_div_is_enabled(struct clk_hw *hw) 123 { 124 struct clk_regmap *clk = to_clk_regmap(hw); 125 struct meson_vclk_div_data *vclk = clk_get_meson_vclk_div_data(clk); 126 127 return meson_parm_read(clk->map, &vclk->enable); 128 } 129 130 const struct clk_ops meson_vclk_div_ops = { 131 .init = clk_regmap_init, 132 .recalc_rate = meson_vclk_div_recalc_rate, 133 .determine_rate = meson_vclk_div_determine_rate, 134 .set_rate = meson_vclk_div_set_rate, 135 .enable = meson_vclk_div_enable, 136 .disable = meson_vclk_div_disable, 137 .is_enabled = meson_vclk_div_is_enabled, 138 }; 139 EXPORT_SYMBOL_NS_GPL(meson_vclk_div_ops, "CLK_MESON"); 140 141 MODULE_DESCRIPTION("Amlogic vclk clock driver"); 142 MODULE_AUTHOR("Neil Armstrong <neil.armstrong@linaro.org>"); 143 MODULE_LICENSE("GPL"); 144 MODULE_IMPORT_NS("CLK_MESON"); 145