1 /*- 2 * SPDX-License-Identifier: BSD-2-Clause 3 * 4 * Copyright (c) 2019 Emmanuel Vadot <manu@freebsd.org> 5 * Copyright (c) 2022 Mitchell Horne <mhorne@FreeBSD.org> 6 * Copyright (c) 2024 Jari Sihvola <jsihv@gmx.com> 7 */ 8 9 #include <sys/param.h> 10 #include <sys/systm.h> 11 #include <sys/bus.h> 12 #include <sys/mutex.h> 13 #include <sys/rman.h> 14 15 #include <machine/bus.h> 16 #include <machine/intr.h> 17 #include <machine/resource.h> 18 19 #include <dev/clk/clk.h> 20 #include <dev/hwreset/hwreset.h> 21 22 #include <dt-bindings/clock/starfive,jh7110-crg.h> 23 24 #include <dev/clk/starfive/jh7110_clk.h> 25 26 #include "clkdev_if.h" 27 #include "hwreset_if.h" 28 29 #define JH7110_DIV_MASK 0xffffff 30 #define JH7110_MUX_SHIFT 24 31 #define JH7110_MUX_MASK 0x3f000000 32 #define JH7110_ENABLE_SHIFT 31 33 34 #define REG_SIZE 4 35 36 struct jh7110_clk_sc { 37 uint32_t offset; 38 uint32_t flags; 39 uint64_t d_max; 40 int id; 41 }; 42 43 #define DIV_ROUND_CLOSEST(n, d) (((n) + (d) / 2) / (d)) 44 45 #define READ4(_sc, _off) \ 46 bus_read_4(_sc->mem_res, _off) 47 #define WRITE4(_sc, _off, _val) \ 48 bus_write_4(_sc->mem_res, _off, _val) 49 50 #define DEVICE_LOCK(_clk) \ 51 CLKDEV_DEVICE_LOCK(clknode_get_device(_clk)) 52 #define DEVICE_UNLOCK(_clk) \ 53 CLKDEV_DEVICE_UNLOCK(clknode_get_device(_clk)) 54 55 /* Reset functions */ 56 57 int 58 jh7110_reset_assert(device_t dev, intptr_t id, bool assert) 59 { 60 struct jh7110_clkgen_softc *sc; 61 uint32_t regvalue, offset, bitmask = 1UL << id % 32; 62 63 sc = device_get_softc(dev); 64 offset = sc->reset_selector_offset + id / 32 * 4; 65 66 mtx_lock(&sc->mtx); 67 68 regvalue = READ4(sc, offset); 69 70 if (assert) 71 regvalue |= bitmask; 72 else 73 regvalue &= ~bitmask; 74 WRITE4(sc, offset, regvalue); 75 76 mtx_unlock(&sc->mtx); 77 78 return (0); 79 } 80 81 int 82 jh7110_reset_is_asserted(device_t dev, intptr_t id, bool *reset) 83 { 84 struct jh7110_clkgen_softc *sc; 85 uint32_t regvalue, offset, bitmask; 86 87 sc = device_get_softc(dev); 88 offset = sc->reset_status_offset + id / 32 * 4; 89 90 mtx_lock(&sc->mtx); 91 92 regvalue = READ4(sc, offset); 93 bitmask = 1UL << id % 32; 94 95 mtx_unlock(&sc->mtx); 96 97 *reset = (regvalue & bitmask) == 0; 98 99 return (0); 100 } 101 102 /* Clock functions */ 103 104 static int 105 jh7110_clk_init(struct clknode *clk, device_t dev) 106 { 107 struct jh7110_clkgen_softc *sc; 108 struct jh7110_clk_sc *sc_clk; 109 uint32_t reg; 110 int idx = 0; 111 112 sc = device_get_softc(clknode_get_device(clk)); 113 sc_clk = clknode_get_softc(clk); 114 115 if (sc_clk->flags & JH7110_CLK_HAS_MUX) { 116 DEVICE_LOCK(clk); 117 reg = READ4(sc, sc_clk->offset); 118 DEVICE_UNLOCK(clk); 119 idx = (reg & JH7110_MUX_MASK) >> JH7110_MUX_SHIFT; 120 } 121 122 clknode_init_parent_idx(clk, idx); 123 124 return (0); 125 } 126 127 static int 128 jh7110_clk_set_gate(struct clknode *clk, bool enable) 129 { 130 struct jh7110_clkgen_softc *sc; 131 struct jh7110_clk_sc *sc_clk; 132 uint32_t reg; 133 134 sc = device_get_softc(clknode_get_device(clk)); 135 sc_clk = clknode_get_softc(clk); 136 137 if ((sc_clk->flags & JH7110_CLK_HAS_GATE) == 0) 138 return (0); 139 140 DEVICE_LOCK(clk); 141 142 reg = READ4(sc, sc_clk->offset); 143 if (enable) 144 reg |= (1 << JH7110_ENABLE_SHIFT); 145 else 146 reg &= ~(1 << JH7110_ENABLE_SHIFT); 147 WRITE4(sc, sc_clk->offset, reg); 148 149 DEVICE_UNLOCK(clk); 150 151 return (0); 152 } 153 154 static int 155 jh7110_clk_set_mux(struct clknode *clk, int idx) 156 { 157 struct jh7110_clkgen_softc *sc; 158 struct jh7110_clk_sc *sc_clk; 159 uint32_t reg; 160 161 sc = device_get_softc(clknode_get_device(clk)); 162 sc_clk = clknode_get_softc(clk); 163 164 if ((sc_clk->flags & JH7110_CLK_HAS_MUX) == 0) 165 return (ENXIO); 166 167 /* Checking index size */ 168 if ((idx & (JH7110_MUX_MASK >> JH7110_MUX_SHIFT)) != idx) 169 return (EINVAL); 170 171 DEVICE_LOCK(clk); 172 173 reg = READ4(sc, sc_clk->offset) & ~JH7110_MUX_MASK; 174 reg |= idx << JH7110_MUX_SHIFT; 175 WRITE4(sc, sc_clk->offset, reg); 176 177 DEVICE_UNLOCK(clk); 178 179 return (0); 180 } 181 182 static int 183 jh7110_clk_recalc_freq(struct clknode *clk, uint64_t *freq) 184 { 185 struct jh7110_clkgen_softc *sc; 186 struct jh7110_clk_sc *sc_clk; 187 uint32_t divisor; 188 189 sc = device_get_softc(clknode_get_device(clk)); 190 sc_clk = clknode_get_softc(clk); 191 192 /* Returning error here causes panic */ 193 if ((sc_clk->flags & JH7110_CLK_HAS_DIV) == 0) 194 return (0); 195 196 DEVICE_LOCK(clk); 197 198 divisor = READ4(sc, sc_clk->offset) & JH7110_DIV_MASK; 199 200 DEVICE_UNLOCK(clk); 201 202 if (divisor) 203 *freq = *freq / divisor; 204 else 205 *freq = 0; 206 207 return (0); 208 } 209 210 static int 211 jh7110_clk_set_freq(struct clknode *clk, uint64_t fin, uint64_t *fout, 212 int flags, int *done) 213 { 214 struct jh7110_clkgen_softc *sc; 215 struct jh7110_clk_sc *sc_clk; 216 uint32_t divisor; 217 218 sc = device_get_softc(clknode_get_device(clk)); 219 sc_clk = clknode_get_softc(clk); 220 221 if ((sc_clk->flags & JH7110_CLK_HAS_DIV) == 0) 222 return (0); 223 224 divisor = MIN(MAX(DIV_ROUND_CLOSEST(fin, *fout), 1UL), sc_clk->d_max); 225 226 if (flags & CLK_SET_DRYRUN) 227 goto done; 228 229 DEVICE_LOCK(clk); 230 231 divisor |= READ4(sc, sc_clk->offset) & ~JH7110_DIV_MASK; 232 WRITE4(sc, sc_clk->offset, divisor); 233 234 DEVICE_UNLOCK(clk); 235 236 done: 237 *fout = divisor; 238 *done = 1; 239 240 return (0); 241 } 242 243 static clknode_method_t jh7110_clknode_methods[] = { 244 /* Device interface */ 245 CLKNODEMETHOD(clknode_init, jh7110_clk_init), 246 CLKNODEMETHOD(clknode_set_gate, jh7110_clk_set_gate), 247 CLKNODEMETHOD(clknode_set_mux, jh7110_clk_set_mux), 248 CLKNODEMETHOD(clknode_recalc_freq, jh7110_clk_recalc_freq), 249 CLKNODEMETHOD(clknode_set_freq, jh7110_clk_set_freq), 250 CLKNODEMETHOD_END 251 }; 252 253 DEFINE_CLASS_1(jh7110_clknode, jh7110_clknode_class, jh7110_clknode_methods, 254 sizeof(struct jh7110_clk_sc), clknode_class); 255 256 int 257 jh7110_clk_register(struct clkdom *clkdom, const struct jh7110_clk_def *clkdef) 258 { 259 struct clknode *clk; 260 struct jh7110_clk_sc *sc; 261 262 clk = clknode_create(clkdom, &jh7110_clknode_class, &clkdef->clkdef); 263 if (clk == NULL) 264 return (-1); 265 266 sc = clknode_get_softc(clk); 267 268 sc->offset = clkdef->clkdef.id * REG_SIZE; 269 270 sc->flags = clkdef->flags; 271 sc->id = clkdef->clkdef.id; 272 sc->d_max = clkdef->d_max; 273 274 clknode_register(clkdom, clk); 275 276 return (0); 277 } 278