1 /*- 2 * Copyright (c) 2019 Emmanuel Vadot <manu@freebsd.org> 3 * 4 * Redistribution and use in source and binary forms, with or without 5 * modification, are permitted provided that the following conditions 6 * are met: 7 * 1. Redistributions of source code must retain the above copyright 8 * notice, this list of conditions and the following disclaimer. 9 * 2. Redistributions in binary form must reproduce the above copyright 10 * notice, this list of conditions and the following disclaimer in the 11 * documentation and/or other materials provided with the distribution. 12 * 13 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 14 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 15 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 16 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 17 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 18 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 19 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED 20 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 21 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 22 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 23 * SUCH DAMAGE. 24 */ 25 26 #include <sys/param.h> 27 #include <sys/systm.h> 28 #include <sys/bus.h> 29 30 #include <dev/clk/clk.h> 31 32 #include <dev/clk/allwinner/aw_clk.h> 33 #include <dev/clk/allwinner/aw_clk_mipi.h> 34 35 #include "clkdev_if.h" 36 37 /* #define dprintf(format, arg...) printf("%s:(%s)" format, __func__, clknode_get_name(clk), arg) */ 38 #define dprintf(format, arg...) 39 40 /* 41 * clknode for PLL_MIPI : 42 * 43 * clk = (pll_video0 * n * k) / m when vfb_sel=0 44 * clk depend on sint_frac, sdiv2, s6p25_7p5, pll_feedback_div when vfb_sel=1 45 * 46 */ 47 48 struct aw_clk_mipi_sc { 49 uint32_t offset; 50 51 struct aw_clk_factor k; 52 struct aw_clk_factor m; 53 struct aw_clk_factor n; 54 55 uint64_t min_freq; 56 uint64_t max_freq; 57 58 uint32_t gate_shift; 59 uint32_t lock_shift; 60 uint32_t lock_retries; 61 62 uint32_t flags; 63 }; 64 65 #define WRITE4(_clk, off, val) \ 66 CLKDEV_WRITE_4(clknode_get_device(_clk), off, val) 67 #define READ4(_clk, off, val) \ 68 CLKDEV_READ_4(clknode_get_device(_clk), off, val) 69 #define DEVICE_LOCK(_clk) \ 70 CLKDEV_DEVICE_LOCK(clknode_get_device(_clk)) 71 #define DEVICE_UNLOCK(_clk) \ 72 CLKDEV_DEVICE_UNLOCK(clknode_get_device(_clk)) 73 74 #define LDO1_EN_SHIFT 23 75 #define LDO2_EN_SHIFT 22 76 #define VFB_SEL_SHIFT 16 77 78 static int 79 aw_clk_mipi_init(struct clknode *clk, device_t dev) 80 { 81 82 clknode_init_parent_idx(clk, 0); 83 return (0); 84 } 85 86 static int 87 aw_clk_mipi_set_gate(struct clknode *clk, bool enable) 88 { 89 struct aw_clk_mipi_sc *sc; 90 uint32_t val; 91 92 sc = clknode_get_softc(clk); 93 94 dprintf("%sabling gate\n", enable ? "En" : "Dis"); 95 DEVICE_LOCK(clk); 96 READ4(clk, sc->offset, &val); 97 if (enable) { 98 val |= (1 << sc->gate_shift); 99 val |= (1 << LDO1_EN_SHIFT); 100 val |= (1 << LDO2_EN_SHIFT); 101 } else { 102 val &= ~(1 << sc->gate_shift); 103 val &= ~(1 << LDO1_EN_SHIFT); 104 val &= ~(1 << LDO2_EN_SHIFT); 105 } 106 WRITE4(clk, sc->offset, val); 107 DEVICE_UNLOCK(clk); 108 109 return (0); 110 } 111 112 static uint64_t 113 aw_clk_mipi_find_best(struct aw_clk_mipi_sc *sc, uint64_t fparent, uint64_t *fout, 114 uint32_t *factor_k, uint32_t *factor_m, uint32_t *factor_n) 115 { 116 uint64_t cur, best; 117 uint32_t n, k, m; 118 119 best = 0; 120 *factor_n = 0; 121 *factor_k = 0; 122 *factor_m = 0; 123 124 for (n = aw_clk_factor_get_min(&sc->n); n <= aw_clk_factor_get_max(&sc->n); n++) { 125 for (k = aw_clk_factor_get_min(&sc->k); k <= aw_clk_factor_get_max(&sc->k); k++) { 126 for (m = aw_clk_factor_get_min(&sc->m); m <= aw_clk_factor_get_max(&sc->m); m++) { 127 cur = (fparent * n * k) / m; 128 if ((*fout - cur) < (*fout - best)) { 129 best = cur; 130 *factor_n = n; 131 *factor_k = k; 132 *factor_m = m; 133 } 134 if (best == *fout) 135 return (best); 136 } 137 } 138 } 139 140 return best; 141 } 142 143 static int 144 aw_clk_mipi_set_freq(struct clknode *clk, uint64_t fparent, uint64_t *fout, 145 int flags, int *stop) 146 { 147 struct aw_clk_mipi_sc *sc; 148 uint64_t best = 0; 149 uint32_t best_k, best_m, best_n; 150 uint32_t k, m, n; 151 uint32_t val; 152 uint32_t retry; 153 154 sc = clknode_get_softc(clk); 155 156 best = aw_clk_mipi_find_best(sc, fparent, fout, &best_k, &best_m, &best_n); 157 158 if (best < sc->min_freq || 159 best > sc->max_freq) { 160 printf("%s: Cannot set %ju for %s (min=%ju max=%ju)\n", 161 __func__, best, clknode_get_name(clk), 162 sc->min_freq, sc->max_freq); 163 return (ERANGE); 164 } 165 if ((flags & CLK_SET_DRYRUN) != 0) { 166 *fout = best; 167 *stop = 1; 168 return (0); 169 } 170 171 DEVICE_LOCK(clk); 172 READ4(clk, sc->offset, &val); 173 /* Disable clock during freq changes */ 174 val &= ~(1 << sc->gate_shift); 175 WRITE4(clk, sc->offset, val); 176 177 k = aw_clk_factor_get_value(&sc->k, best_k); 178 n = aw_clk_factor_get_value(&sc->n, best_n); 179 m = aw_clk_factor_get_value(&sc->m, best_m); 180 val &= ~sc->k.mask; 181 val &= ~sc->m.mask; 182 val &= ~sc->n.mask; 183 val |= k << sc->k.shift; 184 val |= m << sc->m.shift; 185 val |= n << sc->n.shift; 186 187 /* Write the clock changes */ 188 WRITE4(clk, sc->offset, val); 189 190 /* Enable clock now that we've change it */ 191 val |= 1 << sc->gate_shift; 192 WRITE4(clk, sc->offset, val); 193 DEVICE_UNLOCK(clk); 194 195 for (retry = 0; retry < sc->lock_retries; retry++) { 196 READ4(clk, sc->offset, &val); 197 if ((val & (1 << sc->lock_shift)) != 0) 198 break; 199 DELAY(1000); 200 } 201 202 *fout = best; 203 *stop = 1; 204 205 return (0); 206 } 207 208 static int 209 aw_clk_mipi_recalc(struct clknode *clk, uint64_t *freq) 210 { 211 struct aw_clk_mipi_sc *sc; 212 uint32_t val, m, n, k; 213 214 sc = clknode_get_softc(clk); 215 216 DEVICE_LOCK(clk); 217 READ4(clk, sc->offset, &val); 218 DEVICE_UNLOCK(clk); 219 220 k = aw_clk_get_factor(val, &sc->k); 221 m = aw_clk_get_factor(val, &sc->m); 222 n = aw_clk_get_factor(val, &sc->n); 223 224 *freq = (*freq * n * k) / m; 225 226 return (0); 227 } 228 229 static clknode_method_t aw_mipi_clknode_methods[] = { 230 /* Device interface */ 231 CLKNODEMETHOD(clknode_init, aw_clk_mipi_init), 232 CLKNODEMETHOD(clknode_set_gate, aw_clk_mipi_set_gate), 233 CLKNODEMETHOD(clknode_recalc_freq, aw_clk_mipi_recalc), 234 CLKNODEMETHOD(clknode_set_freq, aw_clk_mipi_set_freq), 235 CLKNODEMETHOD_END 236 }; 237 238 DEFINE_CLASS_1(aw_mipi_clknode, aw_mipi_clknode_class, aw_mipi_clknode_methods, 239 sizeof(struct aw_clk_mipi_sc), clknode_class); 240 241 int 242 aw_clk_mipi_register(struct clkdom *clkdom, struct aw_clk_mipi_def *clkdef) 243 { 244 struct clknode *clk; 245 struct aw_clk_mipi_sc *sc; 246 247 clk = clknode_create(clkdom, &aw_mipi_clknode_class, &clkdef->clkdef); 248 if (clk == NULL) 249 return (1); 250 251 sc = clknode_get_softc(clk); 252 253 sc->offset = clkdef->offset; 254 255 sc->k.shift = clkdef->k.shift; 256 sc->k.width = clkdef->k.width; 257 sc->k.mask = ((1 << sc->k.width) - 1) << sc->k.shift; 258 sc->k.value = clkdef->k.value; 259 sc->k.flags = clkdef->k.flags; 260 sc->k.min_value = clkdef->k.min_value; 261 262 sc->m.shift = clkdef->m.shift; 263 sc->m.width = clkdef->m.width; 264 sc->m.mask = ((1 << sc->m.width) - 1) << sc->m.shift; 265 sc->m.value = clkdef->m.value; 266 sc->m.flags = clkdef->m.flags; 267 sc->m.min_value = clkdef->m.min_value; 268 269 sc->n.shift = clkdef->n.shift; 270 sc->n.width = clkdef->n.width; 271 sc->n.mask = ((1 << sc->n.width) - 1) << sc->n.shift; 272 sc->n.value = clkdef->n.value; 273 sc->n.flags = clkdef->n.flags; 274 sc->n.min_value = clkdef->n.min_value; 275 276 sc->min_freq = clkdef->min_freq; 277 sc->max_freq = clkdef->max_freq; 278 279 sc->gate_shift = clkdef->gate_shift; 280 281 sc->lock_shift = clkdef->lock_shift; 282 sc->lock_retries = clkdef->lock_retries; 283 284 sc->flags = clkdef->flags; 285 286 clknode_register(clkdom, clk); 287 288 return (0); 289 } 290