1 /*-
2 * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
3 *
4 * Copyright (c) 2020 Oleksandr Tymoshenko <gonzo@FreeBSD.org>
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 * $FreeBSD$
28 */
29
30 #include <sys/cdefs.h>
31 __FBSDID("$FreeBSD$");
32
33 #include <sys/param.h>
34 #include <sys/systm.h>
35 #include <sys/bus.h>
36
37 #include <dev/extres/clk/clk.h>
38
39 #include <arm64/freescale/imx/clk/imx_clk_frac_pll.h>
40
41 #include "clkdev_if.h"
42
43 struct imx_clk_frac_pll_sc {
44 uint32_t offset;
45 };
46
47 #define WRITE4(_clk, off, val) \
48 CLKDEV_WRITE_4(clknode_get_device(_clk), off, val)
49 #define READ4(_clk, off, val) \
50 CLKDEV_READ_4(clknode_get_device(_clk), off, val)
51 #define DEVICE_LOCK(_clk) \
52 CLKDEV_DEVICE_LOCK(clknode_get_device(_clk))
53 #define DEVICE_UNLOCK(_clk) \
54 CLKDEV_DEVICE_UNLOCK(clknode_get_device(_clk))
55
56 #define CFG0 0
57 #define CFG0_PLL_LOCK (1 << 31)
58 #define CFG0_PD (1 << 19)
59 #define CFG0_BYPASS (1 << 14)
60 #define CFG0_NEWDIV_VAL (1 << 12)
61 #define CFG0_NEWDIV_ACK (1 << 11)
62 #define CFG0_OUTPUT_DIV_MASK (0x1f << 0)
63 #define CFG0_OUTPUT_DIV_SHIFT 0
64 #define CFG1 4
65 #define CFG1_FRAC_DIV_MASK (0xffffff << 7)
66 #define CFG1_FRAC_DIV_SHIFT 7
67 #define CFG1_INT_DIV_MASK (0x7f << 0)
68 #define CFG1_INT_DIV_SHIFT 0
69
70 #if 0
71 #define dprintf(format, arg...) \
72 printf("%s:(%s)" format, __func__, clknode_get_name(clk), arg)
73 #else
74 #define dprintf(format, arg...)
75 #endif
76
77 static int
78 imx_clk_frac_pll_init(struct clknode *clk, device_t dev)
79 {
80
81 clknode_init_parent_idx(clk, 0);
82 return (0);
83 }
84
85 static int
86 imx_clk_frac_pll_set_gate(struct clknode *clk, bool enable)
87 {
88 struct imx_clk_frac_pll_sc *sc;
89 uint32_t cfg0;
90 int timeout;
91
92 sc = clknode_get_softc(clk);
93
94 DEVICE_LOCK(clk);
95 READ4(clk, sc->offset + CFG0, &cfg0);
96 if (enable)
97 cfg0 &= ~(CFG0_PD);
98 else
99 cfg0 |= CFG0_PD;
100 WRITE4(clk, sc->offset + CFG0, cfg0);
101
102 /* Wait for PLL to lock */
103 if (enable && ((cfg0 & CFG0_BYPASS) == 0)) {
104 for (timeout = 1000; timeout; timeout--) {
105 READ4(clk, sc->offset + CFG0, &cfg0);
106 if (cfg0 & CFG0_PLL_LOCK)
107 break;
108 DELAY(1);
109 }
110 }
111
112 DEVICE_UNLOCK(clk);
113
114 return (0);
115 }
116
117 static int
118 imx_clk_frac_pll_recalc(struct clknode *clk, uint64_t *freq)
119 {
120 struct imx_clk_frac_pll_sc *sc;
121 uint32_t cfg0, cfg1;
122 uint64_t div, divfi, divff, divf_val;
123
124 sc = clknode_get_softc(clk);
125
126 DEVICE_LOCK(clk);
127 READ4(clk, sc->offset + CFG0, &cfg0);
128 READ4(clk, sc->offset + CFG1, &cfg1);
129 DEVICE_UNLOCK(clk);
130
131 div = (cfg0 & CFG0_OUTPUT_DIV_MASK) >> CFG0_OUTPUT_DIV_SHIFT;
132 div = (div + 1) * 2;
133 divff = (cfg1 & CFG1_FRAC_DIV_MASK) >> CFG1_FRAC_DIV_SHIFT;
134 divfi = (cfg1 & CFG1_INT_DIV_MASK) >> CFG1_INT_DIV_SHIFT;
135
136 /* PLL is bypassed */
137 if (cfg0 & CFG0_BYPASS)
138 return (0);
139
140 divf_val = 1 + divfi + (divff/0x1000000);
141 *freq = *freq * 8 * divf_val / div;
142
143 return (0);
144 }
145
146 static clknode_method_t imx_clk_frac_pll_clknode_methods[] = {
147 /* Device interface */
148 CLKNODEMETHOD(clknode_init, imx_clk_frac_pll_init),
149 CLKNODEMETHOD(clknode_set_gate, imx_clk_frac_pll_set_gate),
150 CLKNODEMETHOD(clknode_recalc_freq, imx_clk_frac_pll_recalc),
151 CLKNODEMETHOD_END
152 };
153
154 DEFINE_CLASS_1(imx_clk_frac_pll_clknode, imx_clk_frac_pll_clknode_class,
155 imx_clk_frac_pll_clknode_methods, sizeof(struct imx_clk_frac_pll_sc),
156 clknode_class);
157
158 int
159 imx_clk_frac_pll_register(struct clkdom *clkdom,
160 struct imx_clk_frac_pll_def *clkdef)
161 {
162 struct clknode *clk;
163 struct imx_clk_frac_pll_sc *sc;
164
165 clk = clknode_create(clkdom, &imx_clk_frac_pll_clknode_class,
166 &clkdef->clkdef);
167 if (clk == NULL)
168 return (1);
169
170 sc = clknode_get_softc(clk);
171
172 sc->offset = clkdef->offset;
173
174 clknode_register(clkdom, clk);
175
176 return (0);
177 }
Cache object: 36bba440bd50fb05c8cd8bb33dd56608
|