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 36struct 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 57int 58jh7110_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 81int 82jh7110_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 104static int 105jh7110_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 127static int 128jh7110_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 154static int 155jh7110_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 182static int 183jh7110_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 210static int 211jh7110_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 236done: 237 *fout = divisor; 238 *done = 1; 239 240 return (0); 241} 242 243static 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 253DEFINE_CLASS_1(jh7110_clknode, jh7110_clknode_class, jh7110_clknode_methods, 254 sizeof(struct jh7110_clk_sc), clknode_class); 255 256int 257jh7110_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