Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/freebsd-src
Path: blob/main/sys/arm64/freescale/imx/clk/imx_clk_sscg_pll.c
39566 views
1
/*-
2
* SPDX-License-Identifier: BSD-2-Clause
3
*
4
* Copyright (c) 2020 Oleksandr Tymoshenko <[email protected]>
5
*
6
* Redistribution and use in source and binary forms, with or without
7
* modification, are permitted provided that the following conditions
8
* are met:
9
* 1. Redistributions of source code must retain the above copyright
10
* notice, this list of conditions and the following disclaimer.
11
* 2. Redistributions in binary form must reproduce the above copyright
12
* notice, this list of conditions and the following disclaimer in the
13
* documentation and/or other materials provided with the distribution.
14
*
15
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
16
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
17
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
18
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
19
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
20
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
21
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
22
* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
23
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25
* SUCH DAMAGE.
26
*/
27
28
#include <sys/param.h>
29
#include <sys/systm.h>
30
#include <sys/bus.h>
31
32
#include <dev/clk/clk.h>
33
34
#include <arm64/freescale/imx/clk/imx_clk_sscg_pll.h>
35
36
#include "clkdev_if.h"
37
38
struct imx_clk_sscg_pll_sc {
39
uint32_t offset;
40
};
41
42
#define WRITE4(_clk, off, val) \
43
CLKDEV_WRITE_4(clknode_get_device(_clk), off, val)
44
#define READ4(_clk, off, val) \
45
CLKDEV_READ_4(clknode_get_device(_clk), off, val)
46
#define DEVICE_LOCK(_clk) \
47
CLKDEV_DEVICE_LOCK(clknode_get_device(_clk))
48
#define DEVICE_UNLOCK(_clk) \
49
CLKDEV_DEVICE_UNLOCK(clknode_get_device(_clk))
50
51
#define CFG0 0x00
52
#define CFG0_PLL_LOCK (1 << 31)
53
#define CFG0_PD (1 << 7)
54
#define CFG0_BYPASS2 (1 << 5)
55
#define CFG0_BYPASS1 (1 << 4)
56
#define CFG1 0x04
57
#define CFG2 0x08
58
#define CFG2_DIVR1_MASK (7 << 25)
59
#define CFG2_DIVR1_SHIFT 25
60
#define CFG2_DIVR2_MASK (0x3f << 19)
61
#define CFG2_DIVR2_SHIFT 19
62
#define CFG2_DIVF1_MASK (0x3f << 13)
63
#define CFG2_DIVF1_SHIFT 13
64
#define CFG2_DIVF2_MASK (0x3f << 7)
65
#define CFG2_DIVF2_SHIFT 7
66
#define CFG2_DIV_MASK (0x3f << 1)
67
#define CFG2_DIV_SHIFT 1
68
69
#if 0
70
#define dprintf(format, arg...) \
71
printf("%s:(%s)" format, __func__, clknode_get_name(clk), arg)
72
#else
73
#define dprintf(format, arg...)
74
#endif
75
76
static int
77
imx_clk_sscg_pll_init(struct clknode *clk, device_t dev)
78
{
79
if (clknode_get_parents_num(clk) > 1) {
80
device_printf(clknode_get_device(clk),
81
"error: SSCG PLL does not support more than one parent yet\n");
82
return (EINVAL);
83
}
84
clknode_init_parent_idx(clk, 0);
85
86
return (0);
87
}
88
89
static int
90
imx_clk_sscg_pll_set_gate(struct clknode *clk, bool enable)
91
{
92
struct imx_clk_sscg_pll_sc *sc;
93
uint32_t cfg0;
94
int timeout;
95
96
sc = clknode_get_softc(clk);
97
98
DEVICE_LOCK(clk);
99
READ4(clk, sc->offset + CFG0, &cfg0);
100
if (enable)
101
cfg0 &= ~(CFG0_PD);
102
else
103
cfg0 |= CFG0_PD;
104
WRITE4(clk, sc->offset + CFG0, cfg0);
105
106
/* Reading lock */
107
if (enable) {
108
for (timeout = 1000; timeout; timeout--) {
109
READ4(clk, sc->offset + CFG0, &cfg0);
110
if (cfg0 & CFG0_PLL_LOCK)
111
break;
112
DELAY(1);
113
}
114
}
115
116
DEVICE_UNLOCK(clk);
117
118
return (0);
119
}
120
121
static int
122
imx_clk_sscg_pll_recalc(struct clknode *clk, uint64_t *freq)
123
{
124
struct imx_clk_sscg_pll_sc *sc;
125
uint32_t cfg0, cfg2;
126
int divr1, divr2, divf1, divf2, div;
127
128
sc = clknode_get_softc(clk);
129
130
DEVICE_LOCK(clk);
131
READ4(clk, sc->offset + CFG0, &cfg0);
132
READ4(clk, sc->offset + CFG2, &cfg2);
133
DEVICE_UNLOCK(clk);
134
135
/* PLL is bypassed */
136
if (cfg0 & CFG0_BYPASS2)
137
return (0);
138
139
divr1 = (cfg2 & CFG2_DIVR1_MASK) >> CFG2_DIVR1_SHIFT;
140
divr2 = (cfg2 & CFG2_DIVR2_MASK) >> CFG2_DIVR2_SHIFT;
141
divf1 = (cfg2 & CFG2_DIVF1_MASK) >> CFG2_DIVF1_SHIFT;
142
divf2 = (cfg2 & CFG2_DIVF2_MASK) >> CFG2_DIVF2_SHIFT;
143
div = (cfg2 & CFG2_DIV_MASK) >> CFG2_DIV_SHIFT;
144
145
if (cfg0 & CFG0_BYPASS1) {
146
*freq = *freq / ((divr2 + 1) * (div + 1));
147
return (0);
148
}
149
150
*freq *= 2 * (divf1 + 1) * (divf2 + 1);
151
*freq /= (divr1 + 1) * (divr2 + 1) * (div + 1);
152
153
return (0);
154
}
155
156
static clknode_method_t imx_clk_sscg_pll_clknode_methods[] = {
157
/* Device interface */
158
CLKNODEMETHOD(clknode_init, imx_clk_sscg_pll_init),
159
CLKNODEMETHOD(clknode_set_gate, imx_clk_sscg_pll_set_gate),
160
CLKNODEMETHOD(clknode_recalc_freq, imx_clk_sscg_pll_recalc),
161
CLKNODEMETHOD_END
162
};
163
164
DEFINE_CLASS_1(imx_clk_sscg_pll_clknode, imx_clk_sscg_pll_clknode_class,
165
imx_clk_sscg_pll_clknode_methods, sizeof(struct imx_clk_sscg_pll_sc),
166
clknode_class);
167
168
int
169
imx_clk_sscg_pll_register(struct clkdom *clkdom,
170
struct imx_clk_sscg_pll_def *clkdef)
171
{
172
struct clknode *clk;
173
struct imx_clk_sscg_pll_sc *sc;
174
175
clk = clknode_create(clkdom, &imx_clk_sscg_pll_clknode_class,
176
&clkdef->clkdef);
177
if (clk == NULL)
178
return (1);
179
180
sc = clknode_get_softc(clk);
181
182
sc->offset = clkdef->offset;
183
184
clknode_register(clkdom, clk);
185
186
return (0);
187
}
188
189