File Index
Files and directories
Sign in
FILE

clk-regmap.c

File preview with syntax highlighting.

linux-6.19.14/drivers/clk/meson/clk-regmap.c C
  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
 12int 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}
 51EXPORT_SYMBOL_NS_GPL(clk_regmap_init, "CLK_MESON");
 52
 53static 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
 65static int clk_regmap_gate_enable(struct clk_hw *hw)
 66{
 67	return clk_regmap_gate_endisable(hw, 1);
 68}
 69
 70static void clk_regmap_gate_disable(struct clk_hw *hw)
 71{
 72	clk_regmap_gate_endisable(hw, 0);
 73}
 74
 75static 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
 90const 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};
 96EXPORT_SYMBOL_NS_GPL(clk_regmap_gate_ops, "CLK_MESON");
 97
 98const struct clk_ops clk_regmap_gate_ro_ops = {
 99	.init		= clk_regmap_init,
100	.is_enabled = clk_regmap_gate_is_enabled,
101};
102EXPORT_SYMBOL_NS_GPL(clk_regmap_gate_ro_ops, "CLK_MESON");
103
104static 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
123static 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
148static 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
168const 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};
174EXPORT_SYMBOL_NS_GPL(clk_regmap_divider_ops, "CLK_MESON");
175
176const 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};
181EXPORT_SYMBOL_NS_GPL(clk_regmap_divider_ro_ops, "CLK_MESON");
182
183static 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
199static 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
210static 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
219const 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};
225EXPORT_SYMBOL_NS_GPL(clk_regmap_mux_ops, "CLK_MESON");
226
227const struct clk_ops clk_regmap_mux_ro_ops = {
228	.init		= clk_regmap_init,
229	.get_parent = clk_regmap_mux_get_parent,
230};
231EXPORT_SYMBOL_NS_GPL(clk_regmap_mux_ro_ops, "CLK_MESON");
232
233MODULE_DESCRIPTION("Amlogic regmap backed clock driver");
234MODULE_AUTHOR("Jerome Brunet <jbrunet@baylibre.com>");
235MODULE_LICENSE("GPL");
236MODULE_IMPORT_NS("CLK_MESON");
~/ Read-only file preview.