2 * Copyright 2015 Alexander Kabaev <kan@FreeBSD.org>
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
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.
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
28 * Ingenic JZ4780 CGU driver.
32 #include <sys/cdefs.h>
33 __FBSDID("$FreeBSD$");
35 #include <sys/param.h>
36 #include <sys/systm.h>
40 #include <sys/mutex.h>
41 #include <sys/resource.h>
43 #include <machine/bus.h>
45 #include <mips/ingenic/jz4780_clk.h>
47 /**********************************************************************
48 * JZ4780 PLL control register bit fields
49 **********************************************************************/
50 #define CGU_PLL_M_SHIFT 19
51 #define CGU_PLL_M_WIDTH 13
53 #define CGU_PLL_N_SHIFT 13
54 #define CGU_PLL_N_WIDTH 6
56 #define CGU_PLL_OD_SHIFT 9
57 #define CGU_PLL_OD_WIDTH 4
59 #define CGU_PLL_LOCK_SHIFT 6
60 #define CGU_PLL_LOCK_WIDTH 1
62 #define CGU_PLL_ON_SHIFT 4
63 #define CGU_PLL_ON_WIDTH 1
65 #define CGU_PLL_MODE_SHIFT 3
66 #define CGU_PLL_MODE_WIDTH 1
68 #define CGU_PLL_BP_SHIFT 1
69 #define CGU_PLL_BP_WIDTH 1
71 #define CGU_PLL_EN_SHIFT 0
72 #define CGU_PLL_EN_WIDTH 1
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);
80 struct jz4780_clk_pll_sc {
82 struct resource *clk_res;
87 * JZ4780 PLL clock methods
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),
96 DEFINE_CLASS_1(jz4780_clk_pll, jz4780_clk_pll_class, jz4780_clk_pll_methods,
97 sizeof(struct jz4780_clk_pll_sc), clknode_class);
100 jz4780_clk_pll_init(struct clknode *clk, device_t dev)
102 struct jz4780_clk_pll_sc *sc;
105 sc = clknode_get_softc(clk);
107 reg = CLK_RD_4(sc, sc->clk_reg);
108 CLK_WR_4(sc, sc->clk_reg, reg);
111 clknode_init_parent_idx(clk, 0);
116 jz4780_clk_pll_recalc_freq(struct clknode *clk, uint64_t *freq)
118 struct jz4780_clk_pll_sc *sc;
119 uint32_t reg, m, n, od;
121 sc = clknode_get_softc(clk);
122 reg = CLK_RD_4(sc, sc->clk_reg);
124 /* Check for PLL enabled status */
125 if (REG_GET(reg, CGU_PLL_EN) == 0) {
130 /* Return parent frequency if PPL is being bypassed */
131 if (REG_GET(reg, CGU_PLL_BP) != 0)
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;
138 /* Sanity check values */
139 if (m == 0 || n == 0 || od == 0) {
144 *freq = ((*freq / n) * m) / od;
148 #define MHZ (1000 * 1000)
149 #define PLL_TIMEOUT 100
152 jz4780_clk_pll_wait_lock(struct jz4780_clk_pll_sc *sc)
156 for (i = 0; i < PLL_TIMEOUT; i++) {
157 if (CLK_RD_4(sc, sc->clk_reg) & REG_VAL(CGU_PLL_LOCK, 1))
165 jz4780_clk_pll_set_freq(struct clknode *clk, uint64_t fin,
166 uint64_t *fout, int flags, int *stop)
168 struct jz4780_clk_pll_sc *sc;
169 uint32_t reg, m, n, od;
172 sc = clknode_get_softc(clk);
174 /* Should be able to figure all clocks with m & n only */
177 m = MIN((uint32_t)(*fout / MHZ), (1u << CGU_PLL_M_WIDTH));
180 n = MIN((uint32_t)(fin / MHZ), (1u << CGU_PLL_N_WIDTH));
183 if (flags & CLK_SET_DRYRUN) {
184 if (((flags & (CLK_SET_ROUND_UP | CLK_SET_ROUND_DOWN)) == 0) &&
185 (*fout != (((fin / n) * m) / od)))
188 *fout = ((fin / n) * m) / od;
193 reg = CLK_RD_4(sc, sc->clk_reg);
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);
201 reg = REG_SET(reg, CGU_PLL_EN, 1);
202 reg = REG_SET(reg, CGU_PLL_BP, 0);
204 /* Initiate the change */
205 CLK_WR_4(sc, sc->clk_reg, reg);
207 /* Wait for PLL to lock */
208 rv = jz4780_clk_pll_wait_lock(sc);
213 *fout = ((fin / n) * m) / od;
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)
222 struct jz4780_clk_pll_sc *sc;
224 clk = clknode_create(clkdom, &jz4780_clk_pll_class, clkdef);
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);