1 // SPDX-License-Identifier: GPL-2.0 2 // Copyright (c) 2018 BayLibre, SAS. 3 // Author: Jerome Brunet <jbrunet@baylibre.com> 4 5 #include "clk-regmap.h" 6 7 static int clk_regmap_gate_endisable(struct clk_hw *hw, int enable) 8 { 9 struct clk_regmap *clk = to_clk_regmap(hw); 10 struct clk_regmap_gate_data *gate = clk_get_regmap_gate_data(clk); 11 int set = gate->flags & CLK_GATE_SET_TO_DISABLE ? 1 : 0; 12 13 set ^= enable; 14 15 return regmap_update_bits(clk->map, gate->offset, BIT(gate->bit_idx), 16 set ? BIT(gate->bit_idx) : 0); 17 } 18 19 static int clk_regmap_gate_enable(struct clk_hw *hw) 20 { 21 return clk_regmap_gate_endisable(hw, 1); 22 } 23 24 static void clk_regmap_gate_disable(struct clk_hw *hw) 25 { 26 clk_regmap_gate_endisable(hw, 0); 27 } 28 29 static int clk_regmap_gate_is_enabled(struct clk_hw *hw) 30 { 31 struct clk_regmap *clk = to_clk_regmap(hw); 32 struct clk_regmap_gate_data *gate = clk_get_regmap_gate_data(clk); 33 unsigned int val; 34 35 regmap_read(clk->map, gate->offset, &val); 36 if (gate->flags & CLK_GATE_SET_TO_DISABLE) 37 val ^= BIT(gate->bit_idx); 38 39 val &= BIT(gate->bit_idx); 40 41 return val ? 1 : 0; 42 } 43 44 const struct clk_ops clk_regmap_gate_ops = { 45 .enable = clk_regmap_gate_enable, 46 .disable = clk_regmap_gate_disable, 47 .is_enabled = clk_regmap_gate_is_enabled, 48 }; 49 EXPORT_SYMBOL_GPL(clk_regmap_gate_ops); 50 51 static unsigned long clk_regmap_div_recalc_rate(struct clk_hw *hw, 52 unsigned long prate) 53 { 54 struct clk_regmap *clk = to_clk_regmap(hw); 55 struct clk_regmap_div_data *div = clk_get_regmap_div_data(clk); 56 unsigned int val; 57 int ret; 58 59 ret = regmap_read(clk->map, div->offset, &val); 60 if (ret) 61 /* Gives a hint that something is wrong */ 62 return 0; 63 64 val >>= div->shift; 65 val &= clk_div_mask(div->width); 66 return divider_recalc_rate(hw, prate, val, div->table, div->flags, 67 div->width); 68 } 69 70 static long clk_regmap_div_round_rate(struct clk_hw *hw, unsigned long rate, 71 unsigned long *prate) 72 { 73 struct clk_regmap *clk = to_clk_regmap(hw); 74 struct clk_regmap_div_data *div = clk_get_regmap_div_data(clk); 75 unsigned int val; 76 int ret; 77 78 /* if read only, just return current value */ 79 if (div->flags & CLK_DIVIDER_READ_ONLY) { 80 ret = regmap_read(clk->map, div->offset, &val); 81 if (ret) 82 /* Gives a hint that something is wrong */ 83 return 0; 84 85 val >>= div->shift; 86 val &= clk_div_mask(div->width); 87 88 return divider_ro_round_rate(hw, rate, prate, div->table, 89 div->width, div->flags, val); 90 } 91 92 return divider_round_rate(hw, rate, prate, div->table, div->width, 93 div->flags); 94 } 95 96 static int clk_regmap_div_set_rate(struct clk_hw *hw, unsigned long rate, 97 unsigned long parent_rate) 98 { 99 struct clk_regmap *clk = to_clk_regmap(hw); 100 struct clk_regmap_div_data *div = clk_get_regmap_div_data(clk); 101 unsigned int val; 102 int ret; 103 104 ret = divider_get_val(rate, parent_rate, div->table, div->width, 105 div->flags); 106 if (ret < 0) 107 return ret; 108 109 val = (unsigned int)ret << div->shift; 110 return regmap_update_bits(clk->map, div->offset, 111 clk_div_mask(div->width) << div->shift, val); 112 }; 113 114 /* Would prefer clk_regmap_div_ro_ops but clashes with qcom */ 115 116 const struct clk_ops clk_regmap_divider_ops = { 117 .recalc_rate = clk_regmap_div_recalc_rate, 118 .round_rate = clk_regmap_div_round_rate, 119 .set_rate = clk_regmap_div_set_rate, 120 }; 121 EXPORT_SYMBOL_GPL(clk_regmap_divider_ops); 122 123 const struct clk_ops clk_regmap_divider_ro_ops = { 124 .recalc_rate = clk_regmap_div_recalc_rate, 125 .round_rate = clk_regmap_div_round_rate, 126 }; 127 EXPORT_SYMBOL_GPL(clk_regmap_divider_ro_ops); 128 129 static u8 clk_regmap_mux_get_parent(struct clk_hw *hw) 130 { 131 struct clk_regmap *clk = to_clk_regmap(hw); 132 struct clk_regmap_mux_data *mux = clk_get_regmap_mux_data(clk); 133 unsigned int val; 134 int ret; 135 136 ret = regmap_read(clk->map, mux->offset, &val); 137 if (ret) 138 return ret; 139 140 val >>= mux->shift; 141 val &= mux->mask; 142 return clk_mux_val_to_index(hw, mux->table, mux->flags, val); 143 } 144 145 static int clk_regmap_mux_set_parent(struct clk_hw *hw, u8 index) 146 { 147 struct clk_regmap *clk = to_clk_regmap(hw); 148 struct clk_regmap_mux_data *mux = clk_get_regmap_mux_data(clk); 149 unsigned int val = clk_mux_index_to_val(mux->table, mux->flags, index); 150 151 return regmap_update_bits(clk->map, mux->offset, 152 mux->mask << mux->shift, 153 val << mux->shift); 154 } 155 156 const struct clk_ops clk_regmap_mux_ops = { 157 .get_parent = clk_regmap_mux_get_parent, 158 .set_parent = clk_regmap_mux_set_parent, 159 .determine_rate = __clk_mux_determine_rate, 160 }; 161 EXPORT_SYMBOL_GPL(clk_regmap_mux_ops); 162 163 const struct clk_ops clk_regmap_mux_ro_ops = { 164 .get_parent = clk_regmap_mux_get_parent, 165 }; 166 EXPORT_SYMBOL_GPL(clk_regmap_mux_ro_ops); 167