1 /*- 2 * SPDX-License-Identifier: BSD-2-Clause 3 * 4 * Copyright (c) 2019 Emmanuel Vadot <manu@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 28 #include <sys/param.h> 29 #include <sys/systm.h> 30 #include <sys/bus.h> 31 32 #include <dev/clk/clk.h> 33 34 #include <dev/clk/allwinner/aw_clk.h> 35 #include <dev/clk/allwinner/aw_clk_np.h> 36 37 #include "clkdev_if.h" 38 39 /* 40 * clknode for clocks matching the formula : 41 * 42 * clk = clkin * n / p 43 * 44 */ 45 46 struct aw_clk_np_sc { 47 uint32_t offset; 48 49 struct aw_clk_factor n; 50 struct aw_clk_factor p; 51 52 uint32_t gate_shift; 53 uint32_t lock_shift; 54 uint32_t lock_retries; 55 56 uint32_t flags; 57 }; 58 59 #define WRITE4(_clk, off, val) \ 60 CLKDEV_WRITE_4(clknode_get_device(_clk), off, val) 61 #define READ4(_clk, off, val) \ 62 CLKDEV_READ_4(clknode_get_device(_clk), off, val) 63 #define DEVICE_LOCK(_clk) \ 64 CLKDEV_DEVICE_LOCK(clknode_get_device(_clk)) 65 #define DEVICE_UNLOCK(_clk) \ 66 CLKDEV_DEVICE_UNLOCK(clknode_get_device(_clk)) 67 68 static int 69 aw_clk_np_init(struct clknode *clk, device_t dev) 70 { 71 72 clknode_init_parent_idx(clk, 0); 73 return (0); 74 } 75 76 static int 77 aw_clk_np_set_gate(struct clknode *clk, bool enable) 78 { 79 struct aw_clk_np_sc *sc; 80 uint32_t val; 81 82 sc = clknode_get_softc(clk); 83 84 if ((sc->flags & AW_CLK_HAS_GATE) == 0) 85 return (0); 86 87 DEVICE_LOCK(clk); 88 READ4(clk, sc->offset, &val); 89 if (enable) 90 val |= (1 << sc->gate_shift); 91 else 92 val &= ~(1 << sc->gate_shift); 93 WRITE4(clk, sc->offset, val); 94 DEVICE_UNLOCK(clk); 95 96 return (0); 97 } 98 99 static uint64_t 100 aw_clk_np_find_best(struct aw_clk_np_sc *sc, uint64_t fparent, uint64_t *fout, 101 uint32_t *factor_n, uint32_t *factor_p) 102 { 103 uint64_t cur, best; 104 uint32_t n, p, max_n, max_p, min_n, min_p; 105 106 *factor_n = *factor_p = 0; 107 108 max_n = aw_clk_factor_get_max(&sc->n); 109 max_p = aw_clk_factor_get_max(&sc->p); 110 min_n = aw_clk_factor_get_min(&sc->n); 111 min_p = aw_clk_factor_get_min(&sc->p); 112 113 for (p = min_p; p <= max_p; ) { 114 for (n = min_n; n <= max_n; ) { 115 cur = fparent * n / p; 116 if (abs(*fout - cur) < abs(*fout - best)) { 117 best = cur; 118 *factor_n = n; 119 *factor_p = p; 120 } 121 122 n++; 123 } 124 p++; 125 } 126 127 return (best); 128 } 129 130 static int 131 aw_clk_np_set_freq(struct clknode *clk, uint64_t fparent, uint64_t *fout, 132 int flags, int *stop) 133 { 134 struct aw_clk_np_sc *sc; 135 uint64_t cur, best; 136 uint32_t val, n, p, best_n, best_p; 137 int retry; 138 139 sc = clknode_get_softc(clk); 140 141 best = cur = 0; 142 143 best = aw_clk_np_find_best(sc, fparent, fout, 144 &best_n, &best_p); 145 146 if ((flags & CLK_SET_DRYRUN) != 0) { 147 *fout = best; 148 *stop = 1; 149 return (0); 150 } 151 152 if ((best < *fout) && 153 ((flags & CLK_SET_ROUND_DOWN) == 0)) { 154 *stop = 1; 155 return (ERANGE); 156 } 157 if ((best > *fout) && 158 ((flags & CLK_SET_ROUND_UP) == 0)) { 159 *stop = 1; 160 return (ERANGE); 161 } 162 163 DEVICE_LOCK(clk); 164 READ4(clk, sc->offset, &val); 165 166 n = aw_clk_factor_get_value(&sc->n, best_n); 167 p = aw_clk_factor_get_value(&sc->p, best_p); 168 val &= ~sc->n.mask; 169 val &= ~sc->p.mask; 170 val |= n << sc->n.shift; 171 val |= p << sc->p.shift; 172 173 WRITE4(clk, sc->offset, val); 174 DEVICE_UNLOCK(clk); 175 176 if ((sc->flags & AW_CLK_HAS_LOCK) != 0) { 177 for (retry = 0; retry < sc->lock_retries; retry++) { 178 READ4(clk, sc->offset, &val); 179 if ((val & (1 << sc->lock_shift)) != 0) 180 break; 181 DELAY(1000); 182 } 183 } 184 185 *fout = best; 186 *stop = 1; 187 188 return (0); 189 } 190 191 static int 192 aw_clk_np_recalc(struct clknode *clk, uint64_t *freq) 193 { 194 struct aw_clk_np_sc *sc; 195 uint32_t val, n, p; 196 197 sc = clknode_get_softc(clk); 198 199 DEVICE_LOCK(clk); 200 READ4(clk, sc->offset, &val); 201 DEVICE_UNLOCK(clk); 202 203 n = aw_clk_get_factor(val, &sc->n); 204 p = aw_clk_get_factor(val, &sc->p); 205 206 *freq = *freq * n / p; 207 208 return (0); 209 } 210 211 static clknode_method_t aw_np_clknode_methods[] = { 212 /* Device interface */ 213 CLKNODEMETHOD(clknode_init, aw_clk_np_init), 214 CLKNODEMETHOD(clknode_set_gate, aw_clk_np_set_gate), 215 CLKNODEMETHOD(clknode_recalc_freq, aw_clk_np_recalc), 216 CLKNODEMETHOD(clknode_set_freq, aw_clk_np_set_freq), 217 CLKNODEMETHOD_END 218 }; 219 220 DEFINE_CLASS_1(aw_np_clknode, aw_np_clknode_class, aw_np_clknode_methods, 221 sizeof(struct aw_clk_np_sc), clknode_class); 222 223 int 224 aw_clk_np_register(struct clkdom *clkdom, struct aw_clk_np_def *clkdef) 225 { 226 struct clknode *clk; 227 struct aw_clk_np_sc *sc; 228 229 clk = clknode_create(clkdom, &aw_np_clknode_class, &clkdef->clkdef); 230 if (clk == NULL) 231 return (1); 232 233 sc = clknode_get_softc(clk); 234 235 sc->offset = clkdef->offset; 236 237 sc->n.shift = clkdef->n.shift; 238 sc->n.width = clkdef->n.width; 239 sc->n.mask = ((1 << sc->n.width) - 1) << sc->n.shift; 240 sc->n.value = clkdef->n.value; 241 sc->n.flags = clkdef->n.flags; 242 243 sc->p.shift = clkdef->p.shift; 244 sc->p.width = clkdef->p.width; 245 sc->p.mask = ((1 << sc->p.width) - 1) << sc->p.shift; 246 sc->p.value = clkdef->p.value; 247 sc->p.flags = clkdef->p.flags; 248 249 sc->gate_shift = clkdef->gate_shift; 250 251 sc->lock_shift = clkdef->lock_shift; 252 sc->lock_retries = clkdef->lock_retries; 253 254 sc->flags = clkdef->flags; 255 256 clknode_register(clkdom, clk); 257 258 return (0); 259 } 260