Linux Audio

Check our new training course

Loading...
Note: File does not exist in v3.1.
  1// SPDX-License-Identifier: GPL-2.0-or-later
  2/*
  3 *  Copyright (C) 2013 Boris BREZILLON <b.brezillon@overkiz.com>
  4 */
  5
  6#include <linux/clk-provider.h>
  7#include <linux/clkdev.h>
  8#include <linux/clk/at91_pmc.h>
  9#include <linux/of.h>
 10#include <linux/mfd/syscon.h>
 11#include <linux/regmap.h>
 12
 13#include "pmc.h"
 14
 15#define SAM9X5_USB_DIV_SHIFT	8
 16#define SAM9X5_USB_MAX_DIV	0xf
 17
 18#define RM9200_USB_DIV_SHIFT	28
 19#define RM9200_USB_DIV_TAB_SIZE	4
 20
 21#define SAM9X5_USBS_MASK	GENMASK(0, 0)
 22#define SAM9X60_USBS_MASK	GENMASK(1, 0)
 23
 24struct at91sam9x5_clk_usb {
 25	struct clk_hw hw;
 26	struct regmap *regmap;
 27	u32 usbs_mask;
 28	u8 num_parents;
 29};
 30
 31#define to_at91sam9x5_clk_usb(hw) \
 32	container_of(hw, struct at91sam9x5_clk_usb, hw)
 33
 34struct at91rm9200_clk_usb {
 35	struct clk_hw hw;
 36	struct regmap *regmap;
 37	u32 divisors[4];
 38};
 39
 40#define to_at91rm9200_clk_usb(hw) \
 41	container_of(hw, struct at91rm9200_clk_usb, hw)
 42
 43static unsigned long at91sam9x5_clk_usb_recalc_rate(struct clk_hw *hw,
 44						    unsigned long parent_rate)
 45{
 46	struct at91sam9x5_clk_usb *usb = to_at91sam9x5_clk_usb(hw);
 47	unsigned int usbr;
 48	u8 usbdiv;
 49
 50	regmap_read(usb->regmap, AT91_PMC_USB, &usbr);
 51	usbdiv = (usbr & AT91_PMC_OHCIUSBDIV) >> SAM9X5_USB_DIV_SHIFT;
 52
 53	return DIV_ROUND_CLOSEST(parent_rate, (usbdiv + 1));
 54}
 55
 56static int at91sam9x5_clk_usb_determine_rate(struct clk_hw *hw,
 57					     struct clk_rate_request *req)
 58{
 59	struct clk_hw *parent;
 60	long best_rate = -EINVAL;
 61	unsigned long tmp_rate;
 62	int best_diff = -1;
 63	int tmp_diff;
 64	int i;
 65
 66	for (i = 0; i < clk_hw_get_num_parents(hw); i++) {
 67		int div;
 68
 69		parent = clk_hw_get_parent_by_index(hw, i);
 70		if (!parent)
 71			continue;
 72
 73		for (div = 1; div < SAM9X5_USB_MAX_DIV + 2; div++) {
 74			unsigned long tmp_parent_rate;
 75
 76			tmp_parent_rate = req->rate * div;
 77			tmp_parent_rate = clk_hw_round_rate(parent,
 78							   tmp_parent_rate);
 79			if (!tmp_parent_rate)
 80				continue;
 81
 82			tmp_rate = DIV_ROUND_CLOSEST(tmp_parent_rate, div);
 83			if (tmp_rate < req->rate)
 84				tmp_diff = req->rate - tmp_rate;
 85			else
 86				tmp_diff = tmp_rate - req->rate;
 87
 88			if (best_diff < 0 || best_diff > tmp_diff) {
 89				best_rate = tmp_rate;
 90				best_diff = tmp_diff;
 91				req->best_parent_rate = tmp_parent_rate;
 92				req->best_parent_hw = parent;
 93			}
 94
 95			if (!best_diff || tmp_rate < req->rate)
 96				break;
 97		}
 98
 99		if (!best_diff)
100			break;
101	}
102
103	if (best_rate < 0)
104		return best_rate;
105
106	req->rate = best_rate;
107	return 0;
108}
109
110static int at91sam9x5_clk_usb_set_parent(struct clk_hw *hw, u8 index)
111{
112	struct at91sam9x5_clk_usb *usb = to_at91sam9x5_clk_usb(hw);
113
114	if (index >= usb->num_parents)
115		return -EINVAL;
116
117	regmap_update_bits(usb->regmap, AT91_PMC_USB, usb->usbs_mask, index);
118
119	return 0;
120}
121
122static u8 at91sam9x5_clk_usb_get_parent(struct clk_hw *hw)
123{
124	struct at91sam9x5_clk_usb *usb = to_at91sam9x5_clk_usb(hw);
125	unsigned int usbr;
126
127	regmap_read(usb->regmap, AT91_PMC_USB, &usbr);
128
129	return usbr & usb->usbs_mask;
130}
131
132static int at91sam9x5_clk_usb_set_rate(struct clk_hw *hw, unsigned long rate,
133				       unsigned long parent_rate)
134{
135	struct at91sam9x5_clk_usb *usb = to_at91sam9x5_clk_usb(hw);
136	unsigned long div;
137
138	if (!rate)
139		return -EINVAL;
140
141	div = DIV_ROUND_CLOSEST(parent_rate, rate);
142	if (div > SAM9X5_USB_MAX_DIV + 1 || !div)
143		return -EINVAL;
144
145	regmap_update_bits(usb->regmap, AT91_PMC_USB, AT91_PMC_OHCIUSBDIV,
146			   (div - 1) << SAM9X5_USB_DIV_SHIFT);
147
148	return 0;
149}
150
151static const struct clk_ops at91sam9x5_usb_ops = {
152	.recalc_rate = at91sam9x5_clk_usb_recalc_rate,
153	.determine_rate = at91sam9x5_clk_usb_determine_rate,
154	.get_parent = at91sam9x5_clk_usb_get_parent,
155	.set_parent = at91sam9x5_clk_usb_set_parent,
156	.set_rate = at91sam9x5_clk_usb_set_rate,
157};
158
159static int at91sam9n12_clk_usb_enable(struct clk_hw *hw)
160{
161	struct at91sam9x5_clk_usb *usb = to_at91sam9x5_clk_usb(hw);
162
163	regmap_update_bits(usb->regmap, AT91_PMC_USB, AT91_PMC_USBS,
164			   AT91_PMC_USBS);
165
166	return 0;
167}
168
169static void at91sam9n12_clk_usb_disable(struct clk_hw *hw)
170{
171	struct at91sam9x5_clk_usb *usb = to_at91sam9x5_clk_usb(hw);
172
173	regmap_update_bits(usb->regmap, AT91_PMC_USB, AT91_PMC_USBS, 0);
174}
175
176static int at91sam9n12_clk_usb_is_enabled(struct clk_hw *hw)
177{
178	struct at91sam9x5_clk_usb *usb = to_at91sam9x5_clk_usb(hw);
179	unsigned int usbr;
180
181	regmap_read(usb->regmap, AT91_PMC_USB, &usbr);
182
183	return usbr & AT91_PMC_USBS;
184}
185
186static const struct clk_ops at91sam9n12_usb_ops = {
187	.enable = at91sam9n12_clk_usb_enable,
188	.disable = at91sam9n12_clk_usb_disable,
189	.is_enabled = at91sam9n12_clk_usb_is_enabled,
190	.recalc_rate = at91sam9x5_clk_usb_recalc_rate,
191	.determine_rate = at91sam9x5_clk_usb_determine_rate,
192	.set_rate = at91sam9x5_clk_usb_set_rate,
193};
194
195static struct clk_hw * __init
196_at91sam9x5_clk_register_usb(struct regmap *regmap, const char *name,
197			     const char **parent_names, u8 num_parents,
198			     u32 usbs_mask)
199{
200	struct at91sam9x5_clk_usb *usb;
201	struct clk_hw *hw;
202	struct clk_init_data init;
203	int ret;
204
205	usb = kzalloc(sizeof(*usb), GFP_KERNEL);
206	if (!usb)
207		return ERR_PTR(-ENOMEM);
208
209	init.name = name;
210	init.ops = &at91sam9x5_usb_ops;
211	init.parent_names = parent_names;
212	init.num_parents = num_parents;
213	init.flags = CLK_SET_RATE_GATE | CLK_SET_PARENT_GATE |
214		     CLK_SET_RATE_PARENT;
215
216	usb->hw.init = &init;
217	usb->regmap = regmap;
218	usb->usbs_mask = usbs_mask;
219	usb->num_parents = num_parents;
220
221	hw = &usb->hw;
222	ret = clk_hw_register(NULL, &usb->hw);
223	if (ret) {
224		kfree(usb);
225		hw = ERR_PTR(ret);
226	}
227
228	return hw;
229}
230
231struct clk_hw * __init
232at91sam9x5_clk_register_usb(struct regmap *regmap, const char *name,
233			    const char **parent_names, u8 num_parents)
234{
235	return _at91sam9x5_clk_register_usb(regmap, name, parent_names,
236					    num_parents, SAM9X5_USBS_MASK);
237}
238
239struct clk_hw * __init
240sam9x60_clk_register_usb(struct regmap *regmap, const char *name,
241			 const char **parent_names, u8 num_parents)
242{
243	return _at91sam9x5_clk_register_usb(regmap, name, parent_names,
244					    num_parents, SAM9X60_USBS_MASK);
245}
246
247struct clk_hw * __init
248at91sam9n12_clk_register_usb(struct regmap *regmap, const char *name,
249			     const char *parent_name)
250{
251	struct at91sam9x5_clk_usb *usb;
252	struct clk_hw *hw;
253	struct clk_init_data init;
254	int ret;
255
256	usb = kzalloc(sizeof(*usb), GFP_KERNEL);
257	if (!usb)
258		return ERR_PTR(-ENOMEM);
259
260	init.name = name;
261	init.ops = &at91sam9n12_usb_ops;
262	init.parent_names = &parent_name;
263	init.num_parents = 1;
264	init.flags = CLK_SET_RATE_GATE | CLK_SET_RATE_PARENT;
265
266	usb->hw.init = &init;
267	usb->regmap = regmap;
268
269	hw = &usb->hw;
270	ret = clk_hw_register(NULL, &usb->hw);
271	if (ret) {
272		kfree(usb);
273		hw = ERR_PTR(ret);
274	}
275
276	return hw;
277}
278
279static unsigned long at91rm9200_clk_usb_recalc_rate(struct clk_hw *hw,
280						    unsigned long parent_rate)
281{
282	struct at91rm9200_clk_usb *usb = to_at91rm9200_clk_usb(hw);
283	unsigned int pllbr;
284	u8 usbdiv;
285
286	regmap_read(usb->regmap, AT91_CKGR_PLLBR, &pllbr);
287
288	usbdiv = (pllbr & AT91_PMC_USBDIV) >> RM9200_USB_DIV_SHIFT;
289	if (usb->divisors[usbdiv])
290		return parent_rate / usb->divisors[usbdiv];
291
292	return 0;
293}
294
295static long at91rm9200_clk_usb_round_rate(struct clk_hw *hw, unsigned long rate,
296					  unsigned long *parent_rate)
297{
298	struct at91rm9200_clk_usb *usb = to_at91rm9200_clk_usb(hw);
299	struct clk_hw *parent = clk_hw_get_parent(hw);
300	unsigned long bestrate = 0;
301	int bestdiff = -1;
302	unsigned long tmprate;
303	int tmpdiff;
304	int i = 0;
305
306	for (i = 0; i < RM9200_USB_DIV_TAB_SIZE; i++) {
307		unsigned long tmp_parent_rate;
308
309		if (!usb->divisors[i])
310			continue;
311
312		tmp_parent_rate = rate * usb->divisors[i];
313		tmp_parent_rate = clk_hw_round_rate(parent, tmp_parent_rate);
314		tmprate = DIV_ROUND_CLOSEST(tmp_parent_rate, usb->divisors[i]);
315		if (tmprate < rate)
316			tmpdiff = rate - tmprate;
317		else
318			tmpdiff = tmprate - rate;
319
320		if (bestdiff < 0 || bestdiff > tmpdiff) {
321			bestrate = tmprate;
322			bestdiff = tmpdiff;
323			*parent_rate = tmp_parent_rate;
324		}
325
326		if (!bestdiff)
327			break;
328	}
329
330	return bestrate;
331}
332
333static int at91rm9200_clk_usb_set_rate(struct clk_hw *hw, unsigned long rate,
334				       unsigned long parent_rate)
335{
336	int i;
337	struct at91rm9200_clk_usb *usb = to_at91rm9200_clk_usb(hw);
338	unsigned long div;
339
340	if (!rate)
341		return -EINVAL;
342
343	div = DIV_ROUND_CLOSEST(parent_rate, rate);
344
345	for (i = 0; i < RM9200_USB_DIV_TAB_SIZE; i++) {
346		if (usb->divisors[i] == div) {
347			regmap_update_bits(usb->regmap, AT91_CKGR_PLLBR,
348					   AT91_PMC_USBDIV,
349					   i << RM9200_USB_DIV_SHIFT);
350
351			return 0;
352		}
353	}
354
355	return -EINVAL;
356}
357
358static const struct clk_ops at91rm9200_usb_ops = {
359	.recalc_rate = at91rm9200_clk_usb_recalc_rate,
360	.round_rate = at91rm9200_clk_usb_round_rate,
361	.set_rate = at91rm9200_clk_usb_set_rate,
362};
363
364struct clk_hw * __init
365at91rm9200_clk_register_usb(struct regmap *regmap, const char *name,
366			    const char *parent_name, const u32 *divisors)
367{
368	struct at91rm9200_clk_usb *usb;
369	struct clk_hw *hw;
370	struct clk_init_data init;
371	int ret;
372
373	usb = kzalloc(sizeof(*usb), GFP_KERNEL);
374	if (!usb)
375		return ERR_PTR(-ENOMEM);
376
377	init.name = name;
378	init.ops = &at91rm9200_usb_ops;
379	init.parent_names = &parent_name;
380	init.num_parents = 1;
381	init.flags = CLK_SET_RATE_PARENT;
382
383	usb->hw.init = &init;
384	usb->regmap = regmap;
385	memcpy(usb->divisors, divisors, sizeof(usb->divisors));
386
387	hw = &usb->hw;
388	ret = clk_hw_register(NULL, &usb->hw);
389	if (ret) {
390		kfree(usb);
391		hw = ERR_PTR(ret);
392	}
393
394	return hw;
395}