1 // SPDX-License-Identifier: GPL-2.0 2 /* 3 * Copyright (c) 2018 BayLibre, SAS. 4 * Author: Jerome Brunet <jbrunet@baylibre.com> 5 */ 6 7 #include <linux/device.h> 8 #include <linux/module.h> 9 #include <linux/mfd/syscon.h> 10 #include "clk-regmap.h" 11 12 int clk_regmap_init(struct clk_hw *hw) 13 { 14 struct clk_regmap *clk = to_clk_regmap(hw); 15 struct device_node *np, *parent_np; 16 struct device *dev; 17 18 /* Allow regmap to be preset as it was historically done */ 19 if (clk->map) 20 return 0; 21 22 /* 23 * FIXME: what follows couples the controller implementation 24 * and clk_regmap clock type. This situation is not desirable 25 * but temporary, until the controller is able to register 26 * a hook to initialize a clock type 27 */ 28 29 /* Check the usual dev enabled controller with an basic IO regmap */ 30 dev = clk_hw_get_dev(hw); 31 if (dev) { 32 clk->map = dev_get_regmap(dev, NULL); 33 if (clk->map) 34 return 0; 35 } 36 37 /* Move on to early and syscon based controllers */ 38 np = clk_hw_get_of_node(hw); 39 if (np) { 40 parent_np = of_get_parent(np); 41 clk->map = syscon_node_to_regmap(parent_np); 42 of_node_put(parent_np); 43 44 if (!IS_ERR_OR_NULL(clk->map)) 45 return 0; 46 } 47 48 /* Bail out if regmap can't be found */ 49 return -EINVAL; 50 } 51 EXPORT_SYMBOL_NS_GPL(clk_regmap_init, "CLK_MESON"); 52 53 static int clk_regmap_gate_endisable(struct clk_hw *hw, int enable) 54 { 55 struct clk_regmap *clk = to_clk_regmap(hw); 56 struct clk_regmap_gate_data *gate = clk_get_regmap_gate_data(clk); 57 int set = gate->flags & CLK_GATE_SET_TO_DISABLE ? 1 : 0; 58 59 set ^= enable; 60 61 return regmap_update_bits(clk->map, gate->offset, BIT(gate->bit_idx), 62 set ? BIT(gate->bit_idx) : 0); 63 } 64 65 static int clk_regmap_gate_enable(struct clk_hw *hw) 66 { 67 return clk_regmap_gate_endisable(hw, 1); 68 } 69 70 static void clk_regmap_gate_disable(struct clk_hw *hw) 71 { 72 clk_regmap_gate_endisable(hw, 0); 73 } 74 75 static int clk_regmap_gate_is_enabled(struct clk_hw *hw) 76 { 77 struct clk_regmap *clk = to_clk_regmap(hw); 78 struct clk_regmap_gate_data *gate = clk_get_regmap_gate_data(clk); 79 unsigned int val; 80 81 regmap_read(clk->map, gate->offset, &val); 82 if (gate->flags & CLK_GATE_SET_TO_DISABLE) 83 val ^= BIT(gate->bit_idx); 84 85 val &= BIT(gate->bit_idx); 86 87 return val ? 1 : 0; 88 } 89 90 const struct clk_ops clk_regmap_gate_ops = { 91 .init = clk_regmap_init, 92 .enable = clk_regmap_gate_enable, 93 .disable = clk_regmap_gate_disable, 94 .is_enabled = clk_regmap_gate_is_enabled, 95 }; 96 EXPORT_SYMBOL_NS_GPL(clk_regmap_gate_ops, "CLK_MESON"); 97 98 const struct clk_ops clk_regmap_gate_ro_ops = { 99 .init = clk_regmap_init, 100 .is_enabled = clk_regmap_gate_is_enabled, 101 }; 102 EXPORT_SYMBOL_NS_GPL(clk_regmap_gate_ro_ops, "CLK_MESON"); 103 104 static unsigned long clk_regmap_div_recalc_rate(struct clk_hw *hw, 105 unsigned long prate) 106 { 107 struct clk_regmap *clk = to_clk_regmap(hw); 108 struct clk_regmap_div_data *div = clk_get_regmap_div_data(clk); 109 unsigned int val; 110 int ret; 111 112 ret = regmap_read(clk->map, div->offset, &val); 113 if (ret) 114 /* Gives a hint that something is wrong */ 115 return 0; 116 117 val >>= div->shift; 118 val &= clk_div_mask(div->width); 119 return divider_recalc_rate(hw, prate, val, div->table, div->flags, 120 div->width); 121 } 122 123 static int clk_regmap_div_determine_rate(struct clk_hw *hw, 124 struct clk_rate_request *req) 125 { 126 struct clk_regmap *clk = to_clk_regmap(hw); 127 struct clk_regmap_div_data *div = clk_get_regmap_div_data(clk); 128 unsigned int val; 129 int ret; 130 131 /* if read only, just return current value */ 132 if (div->flags & CLK_DIVIDER_READ_ONLY) { 133 ret = regmap_read(clk->map, div->offset, &val); 134 if (ret) 135 return ret; 136 137 val >>= div->shift; 138 val &= clk_div_mask(div->width); 139 140 return divider_ro_determine_rate(hw, req, div->table, 141 div->width, div->flags, val); 142 } 143 144 return divider_determine_rate(hw, req, div->table, div->width, 145 div->flags); 146 } 147 148 static int clk_regmap_div_set_rate(struct clk_hw *hw, unsigned long rate, 149 unsigned long parent_rate) 150 { 151 struct clk_regmap *clk = to_clk_regmap(hw); 152 struct clk_regmap_div_data *div = clk_get_regmap_div_data(clk); 153 unsigned int val; 154 int ret; 155 156 ret = divider_get_val(rate, parent_rate, div->table, div->width, 157 div->flags); 158 if (ret < 0) 159 return ret; 160 161 val = (unsigned int)ret << div->shift; 162 return regmap_update_bits(clk->map, div->offset, 163 clk_div_mask(div->width) << div->shift, val); 164 }; 165 166 /* Would prefer clk_regmap_div_ro_ops but clashes with qcom */ 167 168 const struct clk_ops clk_regmap_divider_ops = { 169 .init = clk_regmap_init, 170 .recalc_rate = clk_regmap_div_recalc_rate, 171 .determine_rate = clk_regmap_div_determine_rate, 172 .set_rate = clk_regmap_div_set_rate, 173 }; 174 EXPORT_SYMBOL_NS_GPL(clk_regmap_divider_ops, "CLK_MESON"); 175 176 const struct clk_ops clk_regmap_divider_ro_ops = { 177 .init = clk_regmap_init, 178 .recalc_rate = clk_regmap_div_recalc_rate, 179 .determine_rate = clk_regmap_div_determine_rate, 180 }; 181 EXPORT_SYMBOL_NS_GPL(clk_regmap_divider_ro_ops, "CLK_MESON"); 182 183 static u8 clk_regmap_mux_get_parent(struct clk_hw *hw) 184 { 185 struct clk_regmap *clk = to_clk_regmap(hw); 186 struct clk_regmap_mux_data *mux = clk_get_regmap_mux_data(clk); 187 unsigned int val; 188 int ret; 189 190 ret = regmap_read(clk->map, mux->offset, &val); 191 if (ret) 192 return ret; 193 194 val >>= mux->shift; 195 val &= mux->mask; 196 return clk_mux_val_to_index(hw, mux->table, mux->flags, val); 197 } 198 199 static int clk_regmap_mux_set_parent(struct clk_hw *hw, u8 index) 200 { 201 struct clk_regmap *clk = to_clk_regmap(hw); 202 struct clk_regmap_mux_data *mux = clk_get_regmap_mux_data(clk); 203 unsigned int val = clk_mux_index_to_val(mux->table, mux->flags, index); 204 205 return regmap_update_bits(clk->map, mux->offset, 206 mux->mask << mux->shift, 207 val << mux->shift); 208 } 209 210 static int clk_regmap_mux_determine_rate(struct clk_hw *hw, 211 struct clk_rate_request *req) 212 { 213 struct clk_regmap *clk = to_clk_regmap(hw); 214 struct clk_regmap_mux_data *mux = clk_get_regmap_mux_data(clk); 215 216 return clk_mux_determine_rate_flags(hw, req, mux->flags); 217 } 218 219 const struct clk_ops clk_regmap_mux_ops = { 220 .init = clk_regmap_init, 221 .get_parent = clk_regmap_mux_get_parent, 222 .set_parent = clk_regmap_mux_set_parent, 223 .determine_rate = clk_regmap_mux_determine_rate, 224 }; 225 EXPORT_SYMBOL_NS_GPL(clk_regmap_mux_ops, "CLK_MESON"); 226 227 const struct clk_ops clk_regmap_mux_ro_ops = { 228 .init = clk_regmap_init, 229 .get_parent = clk_regmap_mux_get_parent, 230 }; 231 EXPORT_SYMBOL_NS_GPL(clk_regmap_mux_ro_ops, "CLK_MESON"); 232 233 MODULE_DESCRIPTION("Amlogic regmap backed clock driver"); 234 MODULE_AUTHOR("Jerome Brunet <jbrunet@baylibre.com>"); 235 MODULE_LICENSE("GPL"); 236 MODULE_IMPORT_NS("CLK_MESON"); 237