]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - sys/mips/ingenic/jz4780_clk_pll.c
ident(1): Normalizing date format
[FreeBSD/FreeBSD.git] / sys / mips / ingenic / jz4780_clk_pll.c
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 }