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 generic CGU clock 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#include <mips/ingenic/jz4780_regs.h> 47 48/* JZ4780 generic mux and div clocks implementation */ 49static int jz4780_clk_gen_init(struct clknode *clk, device_t dev); 50static int jz4780_clk_gen_recalc_freq(struct clknode *clk, uint64_t *freq); 51static int jz4780_clk_gen_set_freq(struct clknode *clk, uint64_t fin, 52 uint64_t *fout, int flags, int *stop); 53static int jz4780_clk_gen_set_gate(struct clknode *clk, bool enable); 54static int jz4780_clk_gen_set_mux(struct clknode *clk, int src); 55 56struct jz4780_clk_gen_sc { 57 struct mtx *clk_mtx; 58 struct resource *clk_res; 59 int clk_reg; 60 const struct jz4780_clk_descr *clk_descr; 61}; 62 63/* 64 * JZ4780 clock PLL clock methods 65 */ 66static clknode_method_t jz4780_clk_gen_methods[] = { 67 CLKNODEMETHOD(clknode_init, jz4780_clk_gen_init), 68 CLKNODEMETHOD(clknode_set_gate, jz4780_clk_gen_set_gate), 69 CLKNODEMETHOD(clknode_recalc_freq, jz4780_clk_gen_recalc_freq), 70 CLKNODEMETHOD(clknode_set_freq, jz4780_clk_gen_set_freq), 71 CLKNODEMETHOD(clknode_set_mux, jz4780_clk_gen_set_mux), 72 73 CLKNODEMETHOD_END 74}; 75DEFINE_CLASS_1(jz4780_clk_pll, jz4780_clk_gen_class, jz4780_clk_gen_methods, 76 sizeof(struct jz4780_clk_gen_sc), clknode_class); 77 78static inline unsigned 79mux_to_reg(unsigned src, unsigned map) 80{ 81 unsigned ret, bit; 82 83 bit = (1u << 3); 84 for (ret = 0; bit; ret++, bit >>= 1) { 85 if (map & bit) { 86 if (src-- == 0) 87 return (ret); 88 } 89 } 90 panic("mux_to_reg"); 91} 92 93static inline unsigned 94reg_to_mux(unsigned reg, unsigned map) 95{ 96 unsigned ret, bit; 97 98 bit = (1u << 3); 99 for (ret = 0; reg; reg--, bit >>= 1) 100 if (map & bit) 101 ret++; 102 return (ret); 103} 104 105static int 106jz4780_clk_gen_init(struct clknode *clk, device_t dev) 107{ 108 struct jz4780_clk_gen_sc *sc; 109 uint32_t reg, msk, parent_idx; 110 111 sc = clknode_get_softc(clk); 112 CLK_LOCK(sc); 113 /* Figure our parent out */ 114 if (sc->clk_descr->clk_type & CLK_MASK_MUX) { 115 msk = (1u << sc->clk_descr->clk_mux.mux_bits) - 1; 116 reg = CLK_RD_4(sc, sc->clk_descr->clk_mux.mux_reg); 117 reg = (reg >> sc->clk_descr->clk_mux.mux_shift) & msk; 118 parent_idx = reg_to_mux(reg, sc->clk_descr->clk_mux.mux_map); 119 } else 120 parent_idx = 0; 121 CLK_UNLOCK(sc); 122 123 clknode_init_parent_idx(clk, parent_idx); 124 return (0); 125} 126 127static int 128jz4780_clk_gen_recalc_freq(struct clknode *clk, uint64_t *freq) 129{ 130 struct jz4780_clk_gen_sc *sc; 131 uint32_t reg; 132 133 sc = clknode_get_softc(clk); 134 135 /* Calculate divisor frequency */ 136 if (sc->clk_descr->clk_type & CLK_MASK_DIV) { 137 uint32_t msk; 138 139 msk = (1u << sc->clk_descr->clk_div.div_bits) - 1; 140 reg = CLK_RD_4(sc, sc->clk_descr->clk_div.div_reg); 141 reg = (reg >> sc->clk_descr->clk_div.div_shift) & msk; 142 reg = (reg + 1) << sc->clk_descr->clk_div.div_lg; 143 *freq /= reg; 144 } 145 return (0); 146} 147 148#define DIV_TIMEOUT 100 149 150static int 151jz4780_clk_gen_set_freq(struct clknode *clk, uint64_t fin, 152 uint64_t *fout, int flags, int *stop) 153{ 154 struct jz4780_clk_gen_sc *sc; 155 uint64_t _fout; 156 uint32_t divider, div_reg, div_msk, reg, div_l, div_h; 157 int rv; 158 159 sc = clknode_get_softc(clk); 160 161 /* Find closest divider */ 162 div_l = howmany(fin, *fout); 163 div_h = fin / *fout; 164 divider = abs((int64_t)*fout - (fin / div_l)) < 165 abs((int64_t)*fout - (fin / div_h)) ? div_l : div_h; 166 167 /* Adjust for divider multiplier */ 168 div_reg = divider >> sc->clk_descr->clk_div.div_lg; 169 divider = div_reg << sc->clk_descr->clk_div.div_lg; 170 if (divider == 0) 171 divider = 1; 172 173 _fout = fin / divider; 174 175 /* Rounding */ 176 if ((flags & CLK_SET_ROUND_UP) && (*fout > _fout)) 177 div_reg--; 178 else if ((flags & CLK_SET_ROUND_DOWN) && (*fout < _fout)) 179 div_reg++; 180 if (div_reg == 0) 181 div_reg = 1; 182 183 div_msk = (1u << sc->clk_descr->clk_div.div_bits) - 1; 184 185 *stop = 1; 186 if (div_reg > div_msk + 1) { 187 *stop = 0; 188 div_reg = div_msk; 189 } 190 191 divider = (div_reg << sc->clk_descr->clk_div.div_lg); 192 div_reg--; 193 194 if ((flags & CLK_SET_DRYRUN) != 0) { 195 if (*stop != 0 && *fout != fin / divider && 196 (flags & (CLK_SET_ROUND_UP | CLK_SET_ROUND_DOWN)) == 0) 197 return (ERANGE); 198 *fout = fin / divider; 199 return (0); 200 } 201 202 CLK_LOCK(sc); 203 /* Apply the new divider value */ 204 reg = CLK_RD_4(sc, sc->clk_descr->clk_div.div_reg); 205 reg &= ~(div_msk << sc->clk_descr->clk_div.div_shift); 206 reg |= (div_reg << sc->clk_descr->clk_div.div_shift); 207 /* Set the change enable bit, it present */ 208 if (sc->clk_descr->clk_div.div_ce_bit >= 0) 209 reg |= (1u << sc->clk_descr->clk_div.div_ce_bit); 210 /* Clear stop bit, it present */ 211 if (sc->clk_descr->clk_div.div_st_bit >= 0) 212 reg &= ~(1u << sc->clk_descr->clk_div.div_st_bit); 213 /* Initiate the change */ 214 CLK_WR_4(sc, sc->clk_descr->clk_div.div_reg, reg); 215 216 /* Wait for busy bit to clear indicating the change is complete */ 217 rv = 0; 218 if (sc->clk_descr->clk_div.div_busy_bit >= 0) { 219 int i; 220 221 for (i = 0; i < DIV_TIMEOUT; i++) { 222 reg = CLK_RD_4(sc, sc->clk_descr->clk_div.div_reg); 223 if (!(reg & (1u << sc->clk_descr->clk_div.div_busy_bit))) 224 break; 225 DELAY(1000); 226 } 227 if (i == DIV_TIMEOUT) 228 rv = ETIMEDOUT; 229 } 230 CLK_UNLOCK(sc); 231 232 *fout = fin / divider; 233 return (rv); 234} 235 236static int 237jz4780_clk_gen_set_mux(struct clknode *clk, int src) 238{ 239 struct jz4780_clk_gen_sc *sc; 240 uint32_t reg, msk; 241 242 sc = clknode_get_softc(clk); 243 244 /* Only mux nodes are capable of being reparented */ 245 if (!(sc->clk_descr->clk_type & CLK_MASK_MUX)) 246 return (src ? EINVAL : 0); 247 248 msk = (1u << sc->clk_descr->clk_mux.mux_bits) - 1; 249 src = mux_to_reg(src & msk, sc->clk_descr->clk_mux.mux_map); 250 251 CLK_LOCK(sc); 252 reg = CLK_RD_4(sc, sc->clk_descr->clk_mux.mux_reg); 253 reg &= ~(msk << sc->clk_descr->clk_mux.mux_shift); 254 reg |= (src << sc->clk_descr->clk_mux.mux_shift); 255 CLK_WR_4(sc, sc->clk_descr->clk_mux.mux_reg, reg); 256 CLK_UNLOCK(sc); 257 258 return (0); 259} 260 261static int 262jz4780_clk_gen_set_gate(struct clknode *clk, bool enable) 263{ 264 struct jz4780_clk_gen_sc *sc; 265 uint32_t off, reg, bit; 266 267 sc = clknode_get_softc(clk); 268 269 /* Check is clock can be gated */ 270 if (sc->clk_descr->clk_gate_bit < 0) 271 return 0; 272 273 bit = sc->clk_descr->clk_gate_bit; 274 if (bit < 32) { 275 off = JZ_CLKGR0; 276 } else { 277 off = JZ_CLKGR1; 278 bit -= 32; 279 } 280 281 CLK_LOCK(sc); 282 reg = CLK_RD_4(sc, off); 283 if (enable) 284 reg &= ~(1u << bit); 285 else 286 reg |= (1u << bit); 287 CLK_WR_4(sc, off, reg); 288 CLK_UNLOCK(sc); 289 290 return (0); 291} 292 293int jz4780_clk_gen_register(struct clkdom *clkdom, 294 const struct jz4780_clk_descr *descr, struct mtx *dev_mtx, 295 struct resource *mem_res) 296{ 297 struct clknode_init_def clkdef; 298 struct clknode *clk; 299 struct jz4780_clk_gen_sc *sc; 300 301 clkdef.id = descr->clk_id; 302 clkdef.name = __DECONST(char *, descr->clk_name); 303 /* Silly const games to work around API deficiency */ 304 clkdef.parent_names = (const char **)(uintptr_t)&descr->clk_pnames[0]; 305 clkdef.flags = CLK_NODE_STATIC_STRINGS; 306 if (descr->clk_type & CLK_MASK_MUX) 307 clkdef.parent_cnt = __bitcount16(descr->clk_mux.mux_map); 308 else 309 clkdef.parent_cnt = 1; 310 311 clk = clknode_create(clkdom, &jz4780_clk_gen_class, &clkdef); 312 if (clk == NULL) 313 return (1); 314 315 sc = clknode_get_softc(clk); 316 sc->clk_mtx = dev_mtx; 317 sc->clk_res = mem_res; 318 sc->clk_descr = descr; 319 clknode_register(clkdom, clk); 320 321 return (0); 322} 323