Skip to content

Commit 6ece3a9

Browse files
Nicolas Ferrealexandrebelloni
authored andcommitted
clk: at91: add audio pll clock driver
This new clock driver set allows to have a fractional divided clock that would generate a precise clock particularly suitable for audio applications. The main audio pll clock has two children clocks: one that is connected to the PMC, the other that can directly drive a pad. As these two routes have different enable bits and different dividers and divider formula, they are handled by two different drivers. Each of them would modify the rate of the main audio pll parent. Signed-off-by: Nicolas Ferre <nicolas.ferre@atmel.com> Signed-off-by: Alexandre Belloni <alexandre.belloni@free-electrons.com>
1 parent 69973b8 commit 6ece3a9

7 files changed

Lines changed: 716 additions & 0 deletions

File tree

Documentation/devicetree/bindings/clock/at91-clock.txt

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,16 @@ Required properties:
8181
"atmel,sama5d2-clk-generated":
8282
at91 generated clock
8383

84+
"atmel,sama5d2-clk-audio-pll-frac":
85+
at91 audio fractional pll
86+
87+
"atmel,sama5d2-clk-audio-pll-pad":
88+
at91 audio pll CLK_AUDIO output pin
89+
90+
"atmel,sama5d2-clk-audio-pll-pmc"
91+
at91 audio pll ouput on AUDIOPLLCLK that feeds the PMC
92+
and can be used by peripheral clock or generic clock
93+
8494
Required properties for SCKC node:
8595
- reg : defines the IO memory reserved for the SCKC.
8696
- #size-cells : shall be 0 (reg is used to encode clk id).

arch/arm/mach-at91/Kconfig

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ config SOC_SAMA5D2
1717
select HAVE_AT91_USB_CLK
1818
select HAVE_AT91_H32MX
1919
select HAVE_AT91_GENERATED_CLK
20+
select HAVE_AT91_AUDIO_PLL
2021
select PINCTRL_AT91PIO4
2122
help
2223
Select this if ou are using one of Atmel's SAMA5D2 family SoC.
@@ -114,6 +115,9 @@ config HAVE_AT91_H32MX
114115
config HAVE_AT91_GENERATED_CLK
115116
bool
116117

118+
config HAVE_AT91_AUDIO_PLL
119+
bool
120+
117121
config SOC_SAM_V4_V5
118122
bool
119123

drivers/clk/at91/Makefile

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@ obj-y += pmc.o sckc.o
66
obj-y += clk-slow.o clk-main.o clk-pll.o clk-plldiv.o clk-master.o
77
obj-y += clk-system.o clk-peripheral.o clk-programmable.o
88

9+
obj-$(CONFIG_HAVE_AT91_AUDIO_PLL) += clk-audio-pll.o
10+
obj-$(CONFIG_HAVE_AT91_AUDIO_PLL) += clk-audio-pll-pmc.o clk-audio-pll-pad.o
911
obj-$(CONFIG_HAVE_AT91_UTMI) += clk-utmi.o
1012
obj-$(CONFIG_HAVE_AT91_USB_CLK) += clk-usb.o
1113
obj-$(CONFIG_HAVE_AT91_SMD) += clk-smd.o
Lines changed: 238 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,238 @@
1+
/*
2+
* Copyright (C) 2016 Atmel Corporation,
3+
* Nicolas Ferre <nicolas.ferre@atmel.com>
4+
*
5+
* This program is free software; you can redistribute it and/or modify
6+
* it under the terms of the GNU General Public License as published by
7+
* the Free Software Foundation; either version 2 of the License, or
8+
* (at your option) any later version.
9+
*
10+
*/
11+
#include <linux/clk.h>
12+
#include <linux/clk-provider.h>
13+
#include <linux/clkdev.h>
14+
#include <linux/clk/at91_pmc.h>
15+
#include <linux/of.h>
16+
#include <linux/mfd/syscon.h>
17+
#include <linux/regmap.h>
18+
19+
#include "pmc.h"
20+
21+
/*
22+
* DOC: PAD output for fractional PLL clock for audio
23+
*
24+
* Traits of this clock:
25+
* enable - clk_enable writes qdpad (which is ext_div and (div2,div3)),
26+
* and enables PAD output
27+
* rate - rate is adjustable.
28+
* clk->rate = parent->rate / (ext_div * (div2,div3))
29+
* parent - fixed parent. No clk_set_parent support
30+
*/
31+
32+
#define AUDIO_PLL_FOUT_MIN 620000000
33+
#define AUDIO_PLL_FOUT_MAX 700000000
34+
#define AUDIO_PLL_REFERENCE_FOUT 660000000
35+
36+
#define AUDIO_PLL_QDPAD_MAX ((AT91_PMC_AUDIO_PLL_QDPAD_DIV_MASK >> \
37+
AT91_PMC_AUDIO_PLL_QDPAD_DIV_OFFSET) * \
38+
AT91_PMC_AUDIO_PLL_QDPAD_EXTDIV_MAX)
39+
#define AUDIO_PLL_QDPAD_EXTDIV_OFFSET (AT91_PMC_AUDIO_PLL_QDPAD_EXTDIV_OFFSET - \
40+
AT91_PMC_AUDIO_PLL_QDPAD_OFFSET)
41+
#define AUDIO_PLL_DIV2QD(div, ext_div) ((div) | ((ext_div) << \
42+
AUDIO_PLL_QDPAD_EXTDIV_OFFSET))
43+
#define AUDIO_PLL_QD2DIV(qd) ((qd) & (AT91_PMC_AUDIO_PLL_QDPAD_DIV_MASK >> \
44+
AT91_PMC_AUDIO_PLL_QDPAD_DIV_OFFSET))
45+
#define AUDIO_PLL_QD2EXTDIV(qd) (((qd) >> AUDIO_PLL_QDPAD_EXTDIV_OFFSET) \
46+
& (AT91_PMC_AUDIO_PLL_QDPAD_EXTDIV_MASK >> \
47+
AT91_PMC_AUDIO_PLL_QDPAD_EXTDIV_OFFSET))
48+
49+
struct clk_audio_pad {
50+
struct clk_hw hw;
51+
struct regmap *regmap;
52+
spinlock_t *lock;
53+
u8 qdpad;
54+
};
55+
56+
#define to_clk_audio_pad(hw) container_of(hw, struct clk_audio_pad, hw)
57+
58+
static int clk_audio_pll_pad_enable(struct clk_hw *hw)
59+
{
60+
struct clk_audio_pad *apad_ck = to_clk_audio_pad(hw);
61+
unsigned long flags;
62+
63+
spin_lock_irqsave(apad_ck->lock, flags);
64+
regmap_update_bits(apad_ck->regmap, AT91_PMC_AUDIO_PLL1,
65+
AT91_PMC_AUDIO_PLL_QDPAD_MASK,
66+
AT91_PMC_AUDIO_PLL_QDPAD(apad_ck->qdpad));
67+
regmap_update_bits(apad_ck->regmap, AT91_PMC_AUDIO_PLL0,
68+
AT91_PMC_AUDIO_PLL_PADEN, AT91_PMC_AUDIO_PLL_PADEN);
69+
spin_unlock_irqrestore(apad_ck->lock, flags);
70+
return 0;
71+
}
72+
73+
static void clk_audio_pll_pad_disable(struct clk_hw *hw)
74+
{
75+
struct clk_audio_pad *apad_ck = to_clk_audio_pad(hw);
76+
77+
regmap_update_bits(apad_ck->regmap, AT91_PMC_AUDIO_PLL0,
78+
AT91_PMC_AUDIO_PLL_PADEN, 0);
79+
}
80+
81+
static unsigned long clk_audio_pll_pad_recalc_rate(struct clk_hw *hw,
82+
unsigned long parent_rate)
83+
{
84+
struct clk_audio_pad *apad_ck = to_clk_audio_pad(hw);
85+
unsigned long apad_rate = 0;
86+
u8 tmp_div = AUDIO_PLL_QD2DIV(apad_ck->qdpad);
87+
u8 tmp_ext_div = AUDIO_PLL_QD2EXTDIV(apad_ck->qdpad);
88+
89+
if (tmp_div && tmp_ext_div)
90+
apad_rate = parent_rate / (tmp_div * tmp_ext_div);
91+
92+
pr_debug("A PLL/PAD: %s, apad_rate = %lu (div = %u, ext_div = %u)\n" ,
93+
__func__ , apad_rate, tmp_div, tmp_ext_div);
94+
95+
return apad_rate;
96+
}
97+
98+
static int clk_audio_pll_compute_qdpad(unsigned long q_rate, unsigned long rate,
99+
unsigned long *qd, u8 *div, u8 *ext_div)
100+
{
101+
unsigned long tmp_qd;
102+
unsigned long rem2, rem3;
103+
unsigned long ldiv, lext_div;;
104+
105+
if (!rate)
106+
return -EINVAL;
107+
108+
tmp_qd = q_rate / rate;
109+
if (!tmp_qd || tmp_qd > AUDIO_PLL_QDPAD_MAX)
110+
return -EINVAL;
111+
112+
if (tmp_qd <= AT91_PMC_AUDIO_PLL_QDPAD_EXTDIV_MAX) {
113+
ldiv = 1;
114+
lext_div = tmp_qd;
115+
} else {
116+
rem2 = tmp_qd % 2;
117+
rem3 = tmp_qd % 3;
118+
119+
if (rem3 == 0 ||
120+
tmp_qd > AT91_PMC_AUDIO_PLL_QDPAD_EXTDIV_MAX * 2 ||
121+
rem3 < rem2) {
122+
ldiv = 3;
123+
lext_div = tmp_qd / 3;
124+
} else {
125+
ldiv = 2;
126+
lext_div = tmp_qd >> 1;
127+
}
128+
}
129+
130+
pr_debug("A PLL/PAD: %s, qd = %lu (div = %lu, ext_div = %lu)\n" ,
131+
__func__ , ldiv * lext_div, ldiv, lext_div);
132+
133+
/* if we were given variable to store, we can provide them */
134+
if (qd)
135+
*qd = ldiv * lext_div;
136+
if (div && ext_div) {
137+
/* we can cast here as we verified the bounds just above */
138+
*div = (u8)ldiv;
139+
*ext_div = (u8)lext_div;
140+
}
141+
142+
return 0;
143+
}
144+
145+
static long clk_audio_pll_pad_round_rate(struct clk_hw *hw, unsigned long rate,
146+
unsigned long *parent_rate)
147+
{
148+
struct clk_hw *pclk = clk_hw_get_parent(hw);
149+
long best_rate = -EINVAL;
150+
unsigned long best_parent_rate = 0;
151+
unsigned long tmp_qd;
152+
153+
pr_debug("A PLL/PAD: %s, rate = %lu (parent_rate = %lu)\n" ,
154+
__func__ , rate, *parent_rate);
155+
156+
if (clk_audio_pll_compute_qdpad(AUDIO_PLL_REFERENCE_FOUT, rate,
157+
&tmp_qd, NULL, NULL))
158+
return -EINVAL;
159+
160+
best_parent_rate = clk_hw_round_rate(pclk, rate * tmp_qd);
161+
best_rate = best_parent_rate / tmp_qd;
162+
163+
pr_debug("A PLL/PAD: %s, best_rate = %ld, best_parent_rate = %lu\n",
164+
__func__, best_rate, best_parent_rate);
165+
166+
*parent_rate = best_parent_rate;
167+
return best_rate;
168+
}
169+
170+
static int clk_audio_pll_pad_set_rate(struct clk_hw *hw, unsigned long rate,
171+
unsigned long parent_rate)
172+
{
173+
struct clk_audio_pad *apad_ck = to_clk_audio_pad(hw);
174+
u8 tmp_div, tmp_ext_div;
175+
int ret;
176+
177+
pr_debug("A PLL/PAD: %s, rate = %lu (parent_rate = %lu)\n" ,
178+
__func__ , rate, parent_rate);
179+
180+
ret = clk_audio_pll_compute_qdpad(parent_rate, rate, NULL,
181+
&tmp_div, &tmp_ext_div);
182+
if (!ret)
183+
apad_ck->qdpad = AUDIO_PLL_DIV2QD(tmp_div, tmp_ext_div);
184+
185+
return ret;
186+
}
187+
188+
static const struct clk_ops audio_pll_pad_ops = {
189+
.enable = clk_audio_pll_pad_enable,
190+
.disable = clk_audio_pll_pad_disable,
191+
.recalc_rate = clk_audio_pll_pad_recalc_rate,
192+
.round_rate = clk_audio_pll_pad_round_rate,
193+
.set_rate = clk_audio_pll_pad_set_rate,
194+
};
195+
196+
static void __init of_sama5d2_clk_audio_pll_pad_setup(struct device_node *np)
197+
{
198+
struct clk_audio_pad *apad_ck;
199+
struct clk_init_data init;
200+
struct regmap *regmap;
201+
const char *parent_name;
202+
const char *name = np->name;
203+
int ret;
204+
205+
parent_name = of_clk_get_parent_name(np, 0);
206+
207+
of_property_read_string(np, "clock-output-names", &name);
208+
209+
regmap = syscon_node_to_regmap(of_get_parent(np));
210+
if (IS_ERR(regmap))
211+
return;
212+
213+
apad_ck = kzalloc(sizeof(*apad_ck), GFP_KERNEL);
214+
if (!apad_ck)
215+
return;
216+
217+
init.name = name;
218+
init.ops = &audio_pll_pad_ops;
219+
init.parent_names = (parent_name ? &parent_name : NULL);
220+
init.num_parents = 1;
221+
init.flags = CLK_SET_RATE_GATE |
222+
CLK_SET_PARENT_GATE | CLK_SET_RATE_PARENT;
223+
224+
apad_ck->hw.init = &init;
225+
apad_ck->regmap = regmap;
226+
apad_ck->lock = &pmc_pcr_lock;
227+
228+
ret = clk_hw_register(NULL, &apad_ck->hw);
229+
if (ret)
230+
kfree(apad_ck);
231+
else
232+
of_clk_add_hw_provider(np, of_clk_hw_simple_get, &apad_ck->hw);
233+
234+
return;
235+
}
236+
CLK_OF_DECLARE(of_sama5d2_clk_audio_pll_pad_setup,
237+
"atmel,sama5d2-clk-audio-pll-pad",
238+
of_sama5d2_clk_audio_pll_pad_setup);

0 commit comments

Comments
 (0)