1 /*
2  * Copyright (C) 2016 Free Electrons
3  * Copyright (C) 2016 NextThing Co
4  *
5  * Maxime Ripard <maxime.ripard@free-electrons.com>
6  *
7  * This program is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU General Public License as
9  * published by the Free Software Foundation; either version 2 of
10  * the License, or (at your option) any later version.
11  */
12 
13 #include <linux/clk-provider.h>
14 #include <linux/regmap.h>
15 
16 #include "sun4i_tcon.h"
17 #include "sun4i_dotclock.h"
18 
19 struct sun4i_dclk {
20 	struct clk_hw		hw;
21 	struct regmap		*regmap;
22 	struct sun4i_tcon	*tcon;
23 };
24 
hw_to_dclk(struct clk_hw * hw)25 static inline struct sun4i_dclk *hw_to_dclk(struct clk_hw *hw)
26 {
27 	return container_of(hw, struct sun4i_dclk, hw);
28 }
29 
sun4i_dclk_disable(struct clk_hw * hw)30 static void sun4i_dclk_disable(struct clk_hw *hw)
31 {
32 	struct sun4i_dclk *dclk = hw_to_dclk(hw);
33 
34 	regmap_update_bits(dclk->regmap, SUN4I_TCON0_DCLK_REG,
35 			   BIT(SUN4I_TCON0_DCLK_GATE_BIT), 0);
36 }
37 
sun4i_dclk_enable(struct clk_hw * hw)38 static int sun4i_dclk_enable(struct clk_hw *hw)
39 {
40 	struct sun4i_dclk *dclk = hw_to_dclk(hw);
41 
42 	return regmap_update_bits(dclk->regmap, SUN4I_TCON0_DCLK_REG,
43 				  BIT(SUN4I_TCON0_DCLK_GATE_BIT),
44 				  BIT(SUN4I_TCON0_DCLK_GATE_BIT));
45 }
46 
sun4i_dclk_is_enabled(struct clk_hw * hw)47 static int sun4i_dclk_is_enabled(struct clk_hw *hw)
48 {
49 	struct sun4i_dclk *dclk = hw_to_dclk(hw);
50 	u32 val;
51 
52 	regmap_read(dclk->regmap, SUN4I_TCON0_DCLK_REG, &val);
53 
54 	return val & BIT(SUN4I_TCON0_DCLK_GATE_BIT);
55 }
56 
sun4i_dclk_recalc_rate(struct clk_hw * hw,unsigned long parent_rate)57 static unsigned long sun4i_dclk_recalc_rate(struct clk_hw *hw,
58 					    unsigned long parent_rate)
59 {
60 	struct sun4i_dclk *dclk = hw_to_dclk(hw);
61 	u32 val;
62 
63 	regmap_read(dclk->regmap, SUN4I_TCON0_DCLK_REG, &val);
64 
65 	val >>= SUN4I_TCON0_DCLK_DIV_SHIFT;
66 	val &= (1 << SUN4I_TCON0_DCLK_DIV_WIDTH) - 1;
67 
68 	if (!val)
69 		val = 1;
70 
71 	return parent_rate / val;
72 }
73 
sun4i_dclk_round_rate(struct clk_hw * hw,unsigned long rate,unsigned long * parent_rate)74 static long sun4i_dclk_round_rate(struct clk_hw *hw, unsigned long rate,
75 				  unsigned long *parent_rate)
76 {
77 	struct sun4i_dclk *dclk = hw_to_dclk(hw);
78 	struct sun4i_tcon *tcon = dclk->tcon;
79 	unsigned long best_parent = 0;
80 	u8 best_div = 1;
81 	int i;
82 
83 	for (i = tcon->dclk_min_div; i <= tcon->dclk_max_div; i++) {
84 		u64 ideal = (u64)rate * i;
85 		unsigned long rounded;
86 
87 		/*
88 		 * ideal has overflowed the max value that can be stored in an
89 		 * unsigned long, and every clk operation we might do on a
90 		 * truncated u64 value will give us incorrect results.
91 		 * Let's just stop there since bigger dividers will result in
92 		 * the same overflow issue.
93 		 */
94 		if (ideal > ULONG_MAX)
95 			goto out;
96 
97 		rounded = clk_hw_round_rate(clk_hw_get_parent(hw),
98 					    ideal);
99 
100 		if (rounded == ideal) {
101 			best_parent = rounded;
102 			best_div = i;
103 			goto out;
104 		}
105 
106 		if (abs(rate - rounded / i) <
107 		    abs(rate - best_parent / best_div)) {
108 			best_parent = rounded;
109 			best_div = i;
110 		}
111 	}
112 
113 out:
114 	*parent_rate = best_parent;
115 
116 	return best_parent / best_div;
117 }
118 
sun4i_dclk_set_rate(struct clk_hw * hw,unsigned long rate,unsigned long parent_rate)119 static int sun4i_dclk_set_rate(struct clk_hw *hw, unsigned long rate,
120 			       unsigned long parent_rate)
121 {
122 	struct sun4i_dclk *dclk = hw_to_dclk(hw);
123 	u8 div = parent_rate / rate;
124 
125 	return regmap_update_bits(dclk->regmap, SUN4I_TCON0_DCLK_REG,
126 				  GENMASK(6, 0), div);
127 }
128 
sun4i_dclk_get_phase(struct clk_hw * hw)129 static int sun4i_dclk_get_phase(struct clk_hw *hw)
130 {
131 	struct sun4i_dclk *dclk = hw_to_dclk(hw);
132 	u32 val;
133 
134 	regmap_read(dclk->regmap, SUN4I_TCON0_IO_POL_REG, &val);
135 
136 	val >>= 28;
137 	val &= 3;
138 
139 	return val * 120;
140 }
141 
sun4i_dclk_set_phase(struct clk_hw * hw,int degrees)142 static int sun4i_dclk_set_phase(struct clk_hw *hw, int degrees)
143 {
144 	struct sun4i_dclk *dclk = hw_to_dclk(hw);
145 	u32 val = degrees / 120;
146 
147 	val <<= 28;
148 
149 	regmap_update_bits(dclk->regmap, SUN4I_TCON0_IO_POL_REG,
150 			   GENMASK(29, 28),
151 			   val);
152 
153 	return 0;
154 }
155 
156 static const struct clk_ops sun4i_dclk_ops = {
157 	.disable	= sun4i_dclk_disable,
158 	.enable		= sun4i_dclk_enable,
159 	.is_enabled	= sun4i_dclk_is_enabled,
160 
161 	.recalc_rate	= sun4i_dclk_recalc_rate,
162 	.round_rate	= sun4i_dclk_round_rate,
163 	.set_rate	= sun4i_dclk_set_rate,
164 
165 	.get_phase	= sun4i_dclk_get_phase,
166 	.set_phase	= sun4i_dclk_set_phase,
167 };
168 
sun4i_dclk_create(struct device * dev,struct sun4i_tcon * tcon)169 int sun4i_dclk_create(struct device *dev, struct sun4i_tcon *tcon)
170 {
171 	const char *clk_name, *parent_name;
172 	struct clk_init_data init;
173 	struct sun4i_dclk *dclk;
174 	int ret;
175 
176 	parent_name = __clk_get_name(tcon->sclk0);
177 	ret = of_property_read_string_index(dev->of_node,
178 					    "clock-output-names", 0,
179 					    &clk_name);
180 	if (ret)
181 		return ret;
182 
183 	dclk = devm_kzalloc(dev, sizeof(*dclk), GFP_KERNEL);
184 	if (!dclk)
185 		return -ENOMEM;
186 	dclk->tcon = tcon;
187 
188 	init.name = clk_name;
189 	init.ops = &sun4i_dclk_ops;
190 	init.parent_names = &parent_name;
191 	init.num_parents = 1;
192 	init.flags = CLK_SET_RATE_PARENT;
193 
194 	dclk->regmap = tcon->regs;
195 	dclk->hw.init = &init;
196 
197 	tcon->dclk = clk_register(dev, &dclk->hw);
198 	if (IS_ERR(tcon->dclk))
199 		return PTR_ERR(tcon->dclk);
200 
201 	return 0;
202 }
203 EXPORT_SYMBOL(sun4i_dclk_create);
204 
sun4i_dclk_free(struct sun4i_tcon * tcon)205 int sun4i_dclk_free(struct sun4i_tcon *tcon)
206 {
207 	clk_unregister(tcon->dclk);
208 	return 0;
209 }
210 EXPORT_SYMBOL(sun4i_dclk_free);
211