xref: /openbmc/linux/drivers/clk/meson/clk-regmap.c (revision e4c5ef6b)
1ea11dda9SJerome Brunet // SPDX-License-Identifier: GPL-2.0
222f65a38SJerome Brunet /*
322f65a38SJerome Brunet  * Copyright (c) 2018 BayLibre, SAS.
422f65a38SJerome Brunet  * Author: Jerome Brunet <jbrunet@baylibre.com>
522f65a38SJerome Brunet  */
6ea11dda9SJerome Brunet 
7889c2b7eSJerome Brunet #include <linux/module.h>
8ea11dda9SJerome Brunet #include "clk-regmap.h"
9ea11dda9SJerome Brunet 
clk_regmap_gate_endisable(struct clk_hw * hw,int enable)10ea11dda9SJerome Brunet static int clk_regmap_gate_endisable(struct clk_hw *hw, int enable)
11ea11dda9SJerome Brunet {
12ea11dda9SJerome Brunet 	struct clk_regmap *clk = to_clk_regmap(hw);
13ea11dda9SJerome Brunet 	struct clk_regmap_gate_data *gate = clk_get_regmap_gate_data(clk);
14ea11dda9SJerome Brunet 	int set = gate->flags & CLK_GATE_SET_TO_DISABLE ? 1 : 0;
15ea11dda9SJerome Brunet 
16ea11dda9SJerome Brunet 	set ^= enable;
17ea11dda9SJerome Brunet 
18ea11dda9SJerome Brunet 	return regmap_update_bits(clk->map, gate->offset, BIT(gate->bit_idx),
19ea11dda9SJerome Brunet 				  set ? BIT(gate->bit_idx) : 0);
20ea11dda9SJerome Brunet }
21ea11dda9SJerome Brunet 
clk_regmap_gate_enable(struct clk_hw * hw)22ea11dda9SJerome Brunet static int clk_regmap_gate_enable(struct clk_hw *hw)
23ea11dda9SJerome Brunet {
24ea11dda9SJerome Brunet 	return clk_regmap_gate_endisable(hw, 1);
25ea11dda9SJerome Brunet }
26ea11dda9SJerome Brunet 
clk_regmap_gate_disable(struct clk_hw * hw)27ea11dda9SJerome Brunet static void clk_regmap_gate_disable(struct clk_hw *hw)
28ea11dda9SJerome Brunet {
29ea11dda9SJerome Brunet 	clk_regmap_gate_endisable(hw, 0);
30ea11dda9SJerome Brunet }
31ea11dda9SJerome Brunet 
clk_regmap_gate_is_enabled(struct clk_hw * hw)32ea11dda9SJerome Brunet static int clk_regmap_gate_is_enabled(struct clk_hw *hw)
33ea11dda9SJerome Brunet {
34ea11dda9SJerome Brunet 	struct clk_regmap *clk = to_clk_regmap(hw);
35ea11dda9SJerome Brunet 	struct clk_regmap_gate_data *gate = clk_get_regmap_gate_data(clk);
36ea11dda9SJerome Brunet 	unsigned int val;
37ea11dda9SJerome Brunet 
38ea11dda9SJerome Brunet 	regmap_read(clk->map, gate->offset, &val);
39ea11dda9SJerome Brunet 	if (gate->flags & CLK_GATE_SET_TO_DISABLE)
40ea11dda9SJerome Brunet 		val ^= BIT(gate->bit_idx);
41ea11dda9SJerome Brunet 
42ea11dda9SJerome Brunet 	val &= BIT(gate->bit_idx);
43ea11dda9SJerome Brunet 
44ea11dda9SJerome Brunet 	return val ? 1 : 0;
45ea11dda9SJerome Brunet }
46ea11dda9SJerome Brunet 
47ea11dda9SJerome Brunet const struct clk_ops clk_regmap_gate_ops = {
48ea11dda9SJerome Brunet 	.enable = clk_regmap_gate_enable,
49ea11dda9SJerome Brunet 	.disable = clk_regmap_gate_disable,
50ea11dda9SJerome Brunet 	.is_enabled = clk_regmap_gate_is_enabled,
51ea11dda9SJerome Brunet };
52ea11dda9SJerome Brunet EXPORT_SYMBOL_GPL(clk_regmap_gate_ops);
53ea11dda9SJerome Brunet 
543cf94c94SMartin Blumenstingl const struct clk_ops clk_regmap_gate_ro_ops = {
553cf94c94SMartin Blumenstingl 	.is_enabled = clk_regmap_gate_is_enabled,
563cf94c94SMartin Blumenstingl };
573cf94c94SMartin Blumenstingl EXPORT_SYMBOL_GPL(clk_regmap_gate_ro_ops);
583cf94c94SMartin Blumenstingl 
clk_regmap_div_recalc_rate(struct clk_hw * hw,unsigned long prate)59ea11dda9SJerome Brunet static unsigned long clk_regmap_div_recalc_rate(struct clk_hw *hw,
60ea11dda9SJerome Brunet 						unsigned long prate)
61ea11dda9SJerome Brunet {
62ea11dda9SJerome Brunet 	struct clk_regmap *clk = to_clk_regmap(hw);
63ea11dda9SJerome Brunet 	struct clk_regmap_div_data *div = clk_get_regmap_div_data(clk);
64ea11dda9SJerome Brunet 	unsigned int val;
65ea11dda9SJerome Brunet 	int ret;
66ea11dda9SJerome Brunet 
67ea11dda9SJerome Brunet 	ret = regmap_read(clk->map, div->offset, &val);
68ea11dda9SJerome Brunet 	if (ret)
69ea11dda9SJerome Brunet 		/* Gives a hint that something is wrong */
70ea11dda9SJerome Brunet 		return 0;
71ea11dda9SJerome Brunet 
72ea11dda9SJerome Brunet 	val >>= div->shift;
73ea11dda9SJerome Brunet 	val &= clk_div_mask(div->width);
74ea11dda9SJerome Brunet 	return divider_recalc_rate(hw, prate, val, div->table, div->flags,
75ea11dda9SJerome Brunet 				   div->width);
76ea11dda9SJerome Brunet }
77ea11dda9SJerome Brunet 
clk_regmap_div_determine_rate(struct clk_hw * hw,struct clk_rate_request * req)78*e4c5ef6bSMartin Blumenstingl static int clk_regmap_div_determine_rate(struct clk_hw *hw,
79*e4c5ef6bSMartin Blumenstingl 					 struct clk_rate_request *req)
80ea11dda9SJerome Brunet {
81ea11dda9SJerome Brunet 	struct clk_regmap *clk = to_clk_regmap(hw);
82ea11dda9SJerome Brunet 	struct clk_regmap_div_data *div = clk_get_regmap_div_data(clk);
83ea11dda9SJerome Brunet 	unsigned int val;
84ea11dda9SJerome Brunet 	int ret;
85ea11dda9SJerome Brunet 
86ea11dda9SJerome Brunet 	/* if read only, just return current value */
87ea11dda9SJerome Brunet 	if (div->flags & CLK_DIVIDER_READ_ONLY) {
88ea11dda9SJerome Brunet 		ret = regmap_read(clk->map, div->offset, &val);
89ea11dda9SJerome Brunet 		if (ret)
90*e4c5ef6bSMartin Blumenstingl 			return ret;
91ea11dda9SJerome Brunet 
92ea11dda9SJerome Brunet 		val >>= div->shift;
93ea11dda9SJerome Brunet 		val &= clk_div_mask(div->width);
94ea11dda9SJerome Brunet 
95*e4c5ef6bSMartin Blumenstingl 		return divider_ro_determine_rate(hw, req, div->table,
96ea11dda9SJerome Brunet 						 div->width, div->flags, val);
97ea11dda9SJerome Brunet 	}
98ea11dda9SJerome Brunet 
99*e4c5ef6bSMartin Blumenstingl 	return divider_determine_rate(hw, req, div->table, div->width,
100ea11dda9SJerome Brunet 				      div->flags);
101ea11dda9SJerome Brunet }
102ea11dda9SJerome Brunet 
clk_regmap_div_set_rate(struct clk_hw * hw,unsigned long rate,unsigned long parent_rate)103ea11dda9SJerome Brunet static int clk_regmap_div_set_rate(struct clk_hw *hw, unsigned long rate,
104ea11dda9SJerome Brunet 				   unsigned long parent_rate)
105ea11dda9SJerome Brunet {
106ea11dda9SJerome Brunet 	struct clk_regmap *clk = to_clk_regmap(hw);
107ea11dda9SJerome Brunet 	struct clk_regmap_div_data *div = clk_get_regmap_div_data(clk);
108ea11dda9SJerome Brunet 	unsigned int val;
109ea11dda9SJerome Brunet 	int ret;
110ea11dda9SJerome Brunet 
111ea11dda9SJerome Brunet 	ret = divider_get_val(rate, parent_rate, div->table, div->width,
112ea11dda9SJerome Brunet 			      div->flags);
113ea11dda9SJerome Brunet 	if (ret < 0)
114ea11dda9SJerome Brunet 		return ret;
115ea11dda9SJerome Brunet 
116ea11dda9SJerome Brunet 	val = (unsigned int)ret << div->shift;
117ea11dda9SJerome Brunet 	return regmap_update_bits(clk->map, div->offset,
118ea11dda9SJerome Brunet 				  clk_div_mask(div->width) << div->shift, val);
119ea11dda9SJerome Brunet };
120ea11dda9SJerome Brunet 
121ea11dda9SJerome Brunet /* Would prefer clk_regmap_div_ro_ops but clashes with qcom */
122ea11dda9SJerome Brunet 
123ea11dda9SJerome Brunet const struct clk_ops clk_regmap_divider_ops = {
124ea11dda9SJerome Brunet 	.recalc_rate = clk_regmap_div_recalc_rate,
125*e4c5ef6bSMartin Blumenstingl 	.determine_rate = clk_regmap_div_determine_rate,
126ea11dda9SJerome Brunet 	.set_rate = clk_regmap_div_set_rate,
127ea11dda9SJerome Brunet };
128ea11dda9SJerome Brunet EXPORT_SYMBOL_GPL(clk_regmap_divider_ops);
129ea11dda9SJerome Brunet 
130ea11dda9SJerome Brunet const struct clk_ops clk_regmap_divider_ro_ops = {
131ea11dda9SJerome Brunet 	.recalc_rate = clk_regmap_div_recalc_rate,
132*e4c5ef6bSMartin Blumenstingl 	.determine_rate = clk_regmap_div_determine_rate,
133ea11dda9SJerome Brunet };
134ea11dda9SJerome Brunet EXPORT_SYMBOL_GPL(clk_regmap_divider_ro_ops);
135ea11dda9SJerome Brunet 
clk_regmap_mux_get_parent(struct clk_hw * hw)136ea11dda9SJerome Brunet static u8 clk_regmap_mux_get_parent(struct clk_hw *hw)
137ea11dda9SJerome Brunet {
138ea11dda9SJerome Brunet 	struct clk_regmap *clk = to_clk_regmap(hw);
139ea11dda9SJerome Brunet 	struct clk_regmap_mux_data *mux = clk_get_regmap_mux_data(clk);
140ea11dda9SJerome Brunet 	unsigned int val;
141ea11dda9SJerome Brunet 	int ret;
142ea11dda9SJerome Brunet 
143ea11dda9SJerome Brunet 	ret = regmap_read(clk->map, mux->offset, &val);
144ea11dda9SJerome Brunet 	if (ret)
145ea11dda9SJerome Brunet 		return ret;
146ea11dda9SJerome Brunet 
147ea11dda9SJerome Brunet 	val >>= mux->shift;
148ea11dda9SJerome Brunet 	val &= mux->mask;
149ea11dda9SJerome Brunet 	return clk_mux_val_to_index(hw, mux->table, mux->flags, val);
150ea11dda9SJerome Brunet }
151ea11dda9SJerome Brunet 
clk_regmap_mux_set_parent(struct clk_hw * hw,u8 index)152ea11dda9SJerome Brunet static int clk_regmap_mux_set_parent(struct clk_hw *hw, u8 index)
153ea11dda9SJerome Brunet {
154ea11dda9SJerome Brunet 	struct clk_regmap *clk = to_clk_regmap(hw);
155ea11dda9SJerome Brunet 	struct clk_regmap_mux_data *mux = clk_get_regmap_mux_data(clk);
156ea11dda9SJerome Brunet 	unsigned int val = clk_mux_index_to_val(mux->table, mux->flags, index);
157ea11dda9SJerome Brunet 
158ea11dda9SJerome Brunet 	return regmap_update_bits(clk->map, mux->offset,
159ea11dda9SJerome Brunet 				  mux->mask << mux->shift,
160ea11dda9SJerome Brunet 				  val << mux->shift);
161ea11dda9SJerome Brunet }
162ea11dda9SJerome Brunet 
clk_regmap_mux_determine_rate(struct clk_hw * hw,struct clk_rate_request * req)1636cc1eb50SJerome Brunet static int clk_regmap_mux_determine_rate(struct clk_hw *hw,
1646cc1eb50SJerome Brunet 					 struct clk_rate_request *req)
1656cc1eb50SJerome Brunet {
1666cc1eb50SJerome Brunet 	struct clk_regmap *clk = to_clk_regmap(hw);
1676cc1eb50SJerome Brunet 	struct clk_regmap_mux_data *mux = clk_get_regmap_mux_data(clk);
1686cc1eb50SJerome Brunet 
1696cc1eb50SJerome Brunet 	return clk_mux_determine_rate_flags(hw, req, mux->flags);
1706cc1eb50SJerome Brunet }
1716cc1eb50SJerome Brunet 
172ea11dda9SJerome Brunet const struct clk_ops clk_regmap_mux_ops = {
173ea11dda9SJerome Brunet 	.get_parent = clk_regmap_mux_get_parent,
174ea11dda9SJerome Brunet 	.set_parent = clk_regmap_mux_set_parent,
1756cc1eb50SJerome Brunet 	.determine_rate = clk_regmap_mux_determine_rate,
176ea11dda9SJerome Brunet };
177ea11dda9SJerome Brunet EXPORT_SYMBOL_GPL(clk_regmap_mux_ops);
178ea11dda9SJerome Brunet 
179ea11dda9SJerome Brunet const struct clk_ops clk_regmap_mux_ro_ops = {
180ea11dda9SJerome Brunet 	.get_parent = clk_regmap_mux_get_parent,
181ea11dda9SJerome Brunet };
182ea11dda9SJerome Brunet EXPORT_SYMBOL_GPL(clk_regmap_mux_ro_ops);
183889c2b7eSJerome Brunet 
184889c2b7eSJerome Brunet MODULE_DESCRIPTION("Amlogic regmap backed clock driver");
185889c2b7eSJerome Brunet MODULE_AUTHOR("Jerome Brunet <jbrunet@baylibre.com>");
186889c2b7eSJerome Brunet MODULE_LICENSE("GPL v2");
187