1 /*-
2 * Copyright 2015 Alexander Kabaev <kan@FreeBSD.org>
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
13 *
14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24 * SUCH DAMAGE.
25 */
26
27 /*
28 * Ingenic JZ4780 CGU driver.
29 *
30 */
31
32 #include <sys/cdefs.h>
33 __FBSDID("$FreeBSD$");
34
35 #include <sys/param.h>
36 #include <sys/systm.h>
37 #include <sys/conf.h>
38 #include <sys/bus.h>
39 #include <sys/lock.h>
40 #include <sys/mutex.h>
41 #include <sys/resource.h>
42
43 #include <machine/bus.h>
44
45 #include <mips/ingenic/jz4780_clk.h>
46
47 /**********************************************************************
48 * JZ4780 PLL control register bit fields
49 **********************************************************************/
50 #define CGU_PLL_M_SHIFT 19
51 #define CGU_PLL_M_WIDTH 13
52
53 #define CGU_PLL_N_SHIFT 13
54 #define CGU_PLL_N_WIDTH 6
55
56 #define CGU_PLL_OD_SHIFT 9
57 #define CGU_PLL_OD_WIDTH 4
58
59 #define CGU_PLL_LOCK_SHIFT 6
60 #define CGU_PLL_LOCK_WIDTH 1
61
62 #define CGU_PLL_ON_SHIFT 4
63 #define CGU_PLL_ON_WIDTH 1
64
65 #define CGU_PLL_MODE_SHIFT 3
66 #define CGU_PLL_MODE_WIDTH 1
67
68 #define CGU_PLL_BP_SHIFT 1
69 #define CGU_PLL_BP_WIDTH 1
70
71 #define CGU_PLL_EN_SHIFT 0
72 #define CGU_PLL_EN_WIDTH 1
73
74 /* JZ4780 PLL clock */
75 static int jz4780_clk_pll_init(struct clknode *clk, device_t dev);
76 static int jz4780_clk_pll_recalc_freq(struct clknode *clk, uint64_t *freq);
77 static int jz4780_clk_pll_set_freq(struct clknode *clk, uint64_t fin,
78 uint64_t *fout, int flags, int *stop);
79
80 struct jz4780_clk_pll_sc {
81 struct mtx *clk_mtx;
82 struct resource *clk_res;
83 uint32_t clk_reg;
84 };
85
86 /*
87 * JZ4780 PLL clock methods
88 */
89 static clknode_method_t jz4780_clk_pll_methods[] = {
90 CLKNODEMETHOD(clknode_init, jz4780_clk_pll_init),
91 CLKNODEMETHOD(clknode_recalc_freq, jz4780_clk_pll_recalc_freq),
92 CLKNODEMETHOD(clknode_set_freq, jz4780_clk_pll_set_freq),
93
94 CLKNODEMETHOD_END
95 };
96 DEFINE_CLASS_1(jz4780_clk_pll, jz4780_clk_pll_class, jz4780_clk_pll_methods,
97 sizeof(struct jz4780_clk_pll_sc), clknode_class);
98
99 static int
100 jz4780_clk_pll_init(struct clknode *clk, device_t dev)
101 {
102 struct jz4780_clk_pll_sc *sc;
103 uint32_t reg;
104
105 sc = clknode_get_softc(clk);
106 CLK_LOCK(sc);
107 reg = CLK_RD_4(sc, sc->clk_reg);
108 CLK_WR_4(sc, sc->clk_reg, reg);
109 CLK_UNLOCK(sc);
110
111 clknode_init_parent_idx(clk, 0);
112 return (0);
113 }
114
115 static int
116 jz4780_clk_pll_recalc_freq(struct clknode *clk, uint64_t *freq)
117 {
118 struct jz4780_clk_pll_sc *sc;
119 uint32_t reg, m, n, od;
120
121 sc = clknode_get_softc(clk);
122 reg = CLK_RD_4(sc, sc->clk_reg);
123
124 /* Check for PLL enabled status */
125 if (REG_GET(reg, CGU_PLL_EN) == 0) {
126 *freq = 0;
127 return 0;
128 }
129
130 /* Return parent frequency if PPL is being bypassed */
131 if (REG_GET(reg, CGU_PLL_BP) != 0)
132 return 0;
133
134 m = REG_GET(reg, CGU_PLL_M) + 1;
135 n = REG_GET(reg, CGU_PLL_N) + 1;
136 od = REG_GET(reg, CGU_PLL_OD) + 1;
137
138 /* Sanity check values */
139 if (m == 0 || n == 0 || od == 0) {
140 *freq = 0;
141 return (EINVAL);
142 }
143
144 *freq = ((*freq / n) * m) / od;
145 return (0);
146 }
147
148 #define MHZ (1000 * 1000)
149 #define PLL_TIMEOUT 100
150
151 static int
152 jz4780_clk_pll_wait_lock(struct jz4780_clk_pll_sc *sc)
153 {
154 int i;
155
156 for (i = 0; i < PLL_TIMEOUT; i++) {
157 if (CLK_RD_4(sc, sc->clk_reg) & REG_VAL(CGU_PLL_LOCK, 1))
158 return (0);
159 DELAY(1000);
160 }
161 return (ETIMEDOUT);
162 }
163
164 static int
165 jz4780_clk_pll_set_freq(struct clknode *clk, uint64_t fin,
166 uint64_t *fout, int flags, int *stop)
167 {
168 struct jz4780_clk_pll_sc *sc;
169 uint32_t reg, m, n, od;
170 int rv;
171
172 sc = clknode_get_softc(clk);
173
174 /* Should be able to figure all clocks with m & n only */
175 od = 1;
176
177 m = MIN((uint32_t)(*fout / MHZ), (1u << CGU_PLL_M_WIDTH));
178 m = MIN(m, 1);
179
180 n = MIN((uint32_t)(fin / MHZ), (1u << CGU_PLL_N_WIDTH));
181 n = MIN(n, 1);
182
183 if (flags & CLK_SET_DRYRUN) {
184 if (((flags & (CLK_SET_ROUND_UP | CLK_SET_ROUND_DOWN)) == 0) &&
185 (*fout != (((fin / n) * m) / od)))
186 return (ERANGE);
187
188 *fout = ((fin / n) * m) / od;
189 return (0);
190 }
191
192 CLK_LOCK(sc);
193 reg = CLK_RD_4(sc, sc->clk_reg);
194
195 /* Set the calculated values */
196 reg = REG_SET(reg, CGU_PLL_M, m - 1);
197 reg = REG_SET(reg, CGU_PLL_N, n - 1);
198 reg = REG_SET(reg, CGU_PLL_OD, od - 1);
199
200 /* Enable the PLL */
201 reg = REG_SET(reg, CGU_PLL_EN, 1);
202 reg = REG_SET(reg, CGU_PLL_BP, 0);
203
204 /* Initiate the change */
205 CLK_WR_4(sc, sc->clk_reg, reg);
206
207 /* Wait for PLL to lock */
208 rv = jz4780_clk_pll_wait_lock(sc);
209 CLK_UNLOCK(sc);
210 if (rv != 0)
211 return (rv);
212
213 *fout = ((fin / n) * m) / od;
214 return (0);
215 }
216
217 int jz4780_clk_pll_register(struct clkdom *clkdom,
218 struct clknode_init_def *clkdef, struct mtx *dev_mtx,
219 struct resource *mem_res, uint32_t mem_reg)
220 {
221 struct clknode *clk;
222 struct jz4780_clk_pll_sc *sc;
223
224 clk = clknode_create(clkdom, &jz4780_clk_pll_class, clkdef);
225 if (clk == NULL)
226 return (1);
227
228 sc = clknode_get_softc(clk);
229 sc->clk_mtx = dev_mtx;
230 sc->clk_res = mem_res;
231 sc->clk_reg = mem_reg;
232 clknode_register(clkdom, clk);
233 return (0);
234 }
Cache object: 4835d619cafc9949471819bab934e284
|