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