1 /*- 2 * SPDX-License-Identifier: BSD-2-Clause 3 * 4 * Copyright (c) 2024 Jari Sihvola <jsihv@gmx.com> 5 * Copyright (c) 2024 The FreeBSD Foundation 6 * 7 * Portions of this software were developed by Mitchell Horne 8 * <mhorne@FreeBSD.org> under sponsorship from the FreeBSD Foundation. 9 */ 10 11 #include <sys/param.h> 12 #include <sys/systm.h> 13 #include <sys/bus.h> 14 #include <sys/kernel.h> 15 #include <sys/module.h> 16 #include <sys/mutex.h> 17 18 #include <machine/bus.h> 19 20 #include <dev/fdt/simplebus.h> 21 #include <dev/ofw/ofw_bus.h> 22 #include <dev/ofw/ofw_bus_subr.h> 23 24 #include <dev/clk/clk.h> 25 #include <dev/clk/starfive/jh7110_clk.h> 26 #include <dev/clk/starfive/jh7110_clk_pll.h> 27 #include <dev/syscon/syscon.h> 28 29 #include <dt-bindings/clock/starfive,jh7110-crg.h> 30 31 #include "clkdev_if.h" 32 #include "syscon_if.h" 33 34 #define JH7110_SYS_SYSCON_SYSCFG24 0x18 35 #define JH7110_SYS_SYSCON_SYSCFG28 0x1c 36 #define JH7110_SYS_SYSCON_SYSCFG32 0x20 37 #define JH7110_SYS_SYSCON_SYSCFG36 0x24 38 #define JH7110_SYS_SYSCON_SYSCFG40 0x28 39 #define JH7110_SYS_SYSCON_SYSCFG44 0x2c 40 #define JH7110_SYS_SYSCON_SYSCFG48 0x30 41 #define JH7110_SYS_SYSCON_SYSCFG52 0x34 42 43 #define DEVICE_LOCK(_clk) \ 44 CLKDEV_DEVICE_LOCK(clknode_get_device(_clk)) 45 #define DEVICE_UNLOCK(_clk) \ 46 CLKDEV_DEVICE_UNLOCK(clknode_get_device(_clk)) 47 48 #define PLL_MASK_FILL(sc, id) \ 49 do { \ 50 sc->dacpd_mask = PLL## id ##_DACPD_MASK; \ 51 sc->dsmpd_mask = PLL## id ##_DSMPD_MASK; \ 52 sc->fbdiv_mask = PLL## id ##_FBDIV_MASK; \ 53 sc->frac_mask = PLL## id ##_FRAC_MASK; \ 54 sc->prediv_mask = PLL## id ##_PREDIV_MASK; \ 55 sc->postdiv1_mask = PLL## id ##_POSTDIV1_MASK; \ 56 } while (0) 57 58 #define PLL_SHIFT_FILL(sc, id) \ 59 do { \ 60 sc->dacpd_shift = PLL## id ##_DACPD_SHIFT; \ 61 sc->dsmpd_shift = PLL## id ##_DSMPD_SHIFT; \ 62 sc->fbdiv_shift = PLL## id ##_FBDIV_SHIFT; \ 63 sc->frac_shift = PLL## id ##_FRAC_SHIFT; \ 64 sc->prediv_shift = PLL## id ##_PREDIV_SHIFT; \ 65 sc->postdiv1_shift = PLL## id ##_POSTDIV1_SHIFT; \ 66 } while (0) 67 68 struct jh7110_clk_pll_softc { 69 struct mtx mtx; 70 struct clkdom *clkdom; 71 struct syscon *syscon; 72 }; 73 74 struct jh7110_pll_clknode_softc { 75 uint32_t dacpd_offset; 76 uint32_t dsmpd_offset; 77 uint32_t fbdiv_offset; 78 uint32_t frac_offset; 79 uint32_t prediv_offset; 80 uint32_t postdiv1_offset; 81 82 uint32_t dacpd_mask; 83 uint32_t dsmpd_mask; 84 uint32_t fbdiv_mask; 85 uint32_t frac_mask; 86 uint32_t prediv_mask; 87 uint32_t postdiv1_mask; 88 89 uint32_t dacpd_shift; 90 uint32_t dsmpd_shift; 91 uint32_t fbdiv_shift; 92 uint32_t frac_shift; 93 uint32_t prediv_shift; 94 uint32_t postdiv1_shift; 95 96 const struct jh7110_pll_syscon_value *syscon_arr; 97 int syscon_nitems; 98 }; 99 100 static const char *pll_parents[] = { "osc" }; 101 102 static struct jh7110_clk_def pll_out_clks[] = { 103 { 104 .clkdef.id = JH7110_PLLCLK_PLL0_OUT, 105 .clkdef.name = "pll0_out", 106 .clkdef.parent_names = pll_parents, 107 .clkdef.parent_cnt = nitems(pll_parents), 108 .clkdef.flags = CLK_NODE_STATIC_STRINGS, 109 }, 110 { 111 .clkdef.id = JH7110_PLLCLK_PLL1_OUT, 112 .clkdef.name = "pll1_out", 113 .clkdef.parent_names = pll_parents, 114 .clkdef.parent_cnt = nitems(pll_parents), 115 .clkdef.flags = CLK_NODE_STATIC_STRINGS, 116 }, 117 { 118 .clkdef.id = JH7110_PLLCLK_PLL2_OUT, 119 .clkdef.name = "pll2_out", 120 .clkdef.parent_names = pll_parents, 121 .clkdef.parent_cnt = nitems(pll_parents), 122 .clkdef.flags = CLK_NODE_STATIC_STRINGS, 123 }, 124 }; 125 126 static int jh7110_clk_pll_register(struct clkdom *clkdom, 127 struct jh7110_clk_def *clkdef); 128 129 static int 130 jh7110_clk_pll_recalc_freq(struct clknode *clk, uint64_t *freq) 131 { 132 struct jh7110_clk_pll_softc *sc; 133 struct jh7110_pll_clknode_softc *clk_sc; 134 uint32_t dacpd, dsmpd, fbdiv, prediv, postdiv1; 135 uint64_t frac, fcal = 0; 136 137 sc = device_get_softc(clknode_get_device(clk)); 138 clk_sc = clknode_get_softc(clk); 139 140 DEVICE_LOCK(clk); 141 142 dacpd = (SYSCON_READ_4(sc->syscon, clk_sc->dacpd_offset) & clk_sc->dacpd_mask) >> 143 clk_sc->dacpd_shift; 144 dsmpd = (SYSCON_READ_4(sc->syscon, clk_sc->dsmpd_offset) & clk_sc->dsmpd_mask) >> 145 clk_sc->dsmpd_shift; 146 fbdiv = (SYSCON_READ_4(sc->syscon, clk_sc->fbdiv_offset) & clk_sc->fbdiv_mask) >> 147 clk_sc->fbdiv_shift; 148 prediv = (SYSCON_READ_4(sc->syscon, clk_sc->prediv_offset) & clk_sc->prediv_mask) >> 149 clk_sc->prediv_shift; 150 postdiv1 = (SYSCON_READ_4(sc->syscon, clk_sc->postdiv1_offset) & 151 clk_sc->postdiv1_mask) >> clk_sc->postdiv1_shift; 152 frac = (SYSCON_READ_4(sc->syscon, clk_sc->frac_offset) & clk_sc->frac_mask) >> 153 clk_sc->frac_shift; 154 155 DEVICE_UNLOCK(clk); 156 157 /* dacpd and dsmpd both being 0 entails Fraction Multiple Mode */ 158 if (dacpd == 0 && dsmpd == 0) 159 fcal = frac * FRAC_PATR_SIZE / (1 << 24); 160 161 *freq = *freq / FRAC_PATR_SIZE * (fbdiv * FRAC_PATR_SIZE + fcal) / 162 prediv / (1 << postdiv1); 163 164 return (0); 165 } 166 167 static int 168 jh7110_clk_pll_set_freq(struct clknode *clk, uint64_t fin, uint64_t *fout, 169 int flags, int *done) 170 { 171 struct jh7110_clk_pll_softc *sc; 172 struct jh7110_pll_clknode_softc *clk_sc; 173 const struct jh7110_pll_syscon_value *syscon_val = NULL; 174 175 sc = device_get_softc(clknode_get_device(clk)); 176 clk_sc = clknode_get_softc(clk); 177 178 for (int i = 0; i != clk_sc->syscon_nitems; i++) { 179 if (*fout == clk_sc->syscon_arr[i].freq) { 180 syscon_val = &clk_sc->syscon_arr[i]; 181 } 182 } 183 184 if (syscon_val == NULL) { 185 printf("%s: tried to set an unknown frequency %ju for %s\n", 186 __func__, *fout, clknode_get_name(clk)); 187 return (EINVAL); 188 } 189 190 if ((flags & CLK_SET_DRYRUN) != 0) { 191 *done = 1; 192 return (0); 193 } 194 195 DEVICE_LOCK(clk); 196 197 SYSCON_MODIFY_4(sc->syscon, clk_sc->dacpd_offset, clk_sc->dacpd_mask, 198 syscon_val->dacpd << clk_sc->dacpd_shift & clk_sc->dacpd_mask); 199 SYSCON_MODIFY_4(sc->syscon, clk_sc->dsmpd_offset, clk_sc->dsmpd_mask, 200 syscon_val->dsmpd << clk_sc->dsmpd_shift & clk_sc->dsmpd_mask); 201 SYSCON_MODIFY_4(sc->syscon, clk_sc->prediv_offset, clk_sc->prediv_mask, 202 syscon_val->prediv << clk_sc->prediv_shift & clk_sc->prediv_mask); 203 SYSCON_MODIFY_4(sc->syscon, clk_sc->fbdiv_offset, clk_sc->fbdiv_mask, 204 syscon_val->fbdiv << clk_sc->fbdiv_shift & clk_sc->fbdiv_mask); 205 SYSCON_MODIFY_4(sc->syscon, clk_sc->postdiv1_offset, 206 clk_sc->postdiv1_mask, (syscon_val->postdiv1 >> 1) << 207 clk_sc->postdiv1_shift & clk_sc->postdiv1_mask); 208 209 if (!syscon_val->dacpd && !syscon_val->dsmpd) { 210 SYSCON_MODIFY_4(sc->syscon, clk_sc->frac_offset, clk_sc->frac_mask, 211 syscon_val->frac << clk_sc->frac_shift & clk_sc->frac_mask); 212 } 213 214 DEVICE_UNLOCK(clk); 215 216 *done = 1; 217 return (0); 218 } 219 220 static int 221 jh7110_clk_pll_init(struct clknode *clk, device_t dev) 222 { 223 clknode_init_parent_idx(clk, 0); 224 225 return (0); 226 } 227 228 static int 229 jh7110_clk_pll_probe(device_t dev) 230 { 231 if (!ofw_bus_status_okay(dev)) 232 return (ENXIO); 233 234 if (!ofw_bus_is_compatible(dev, "starfive,jh7110-pll")) 235 return (ENXIO); 236 237 device_set_desc(dev, "StarFive JH7110 PLL clock generator"); 238 239 return (BUS_PROBE_DEFAULT); 240 } 241 242 static int 243 jh7110_clk_pll_attach(device_t dev) 244 { 245 struct jh7110_clk_pll_softc *sc; 246 int error; 247 248 sc = device_get_softc(dev); 249 250 mtx_init(&sc->mtx, device_get_nameunit(dev), NULL, MTX_DEF); 251 252 sc->clkdom = clkdom_create(dev); 253 if (sc->clkdom == NULL) { 254 device_printf(dev, "Couldn't create clkdom\n"); 255 return (ENXIO); 256 } 257 258 error = syscon_get_by_ofw_node(dev, OF_parent(ofw_bus_get_node(dev)), 259 &sc->syscon); 260 if (error != 0) { 261 device_printf(dev, "Couldn't get syscon handle of parent\n"); 262 return (error); 263 } 264 265 for (int i = 0; i < nitems(pll_out_clks); i++) { 266 error = jh7110_clk_pll_register(sc->clkdom, &pll_out_clks[i]); 267 if (error != 0) 268 device_printf(dev, "Couldn't register clock %s: %d\n", 269 pll_out_clks[i].clkdef.name, error); 270 } 271 272 error = clkdom_finit(sc->clkdom); 273 if (error != 0) { 274 device_printf(dev, "clkdom_finit() returned %d\n", error); 275 } 276 277 if (bootverbose) 278 clkdom_dump(sc->clkdom); 279 280 return (0); 281 } 282 283 static void 284 jh7110_clk_pll_device_lock(device_t dev) 285 { 286 struct jh7110_clk_pll_softc *sc; 287 288 sc = device_get_softc(dev); 289 mtx_lock(&sc->mtx); 290 } 291 292 static void 293 jh7110_clk_pll_device_unlock(device_t dev) 294 { 295 struct jh7110_clk_pll_softc *sc; 296 297 sc = device_get_softc(dev); 298 mtx_unlock(&sc->mtx); 299 } 300 301 static clknode_method_t jh7110_pllnode_methods[] = { 302 /* Device interface */ 303 CLKNODEMETHOD(clknode_init, jh7110_clk_pll_init), 304 CLKNODEMETHOD(clknode_recalc_freq, jh7110_clk_pll_recalc_freq), 305 CLKNODEMETHOD(clknode_set_freq, jh7110_clk_pll_set_freq), 306 307 CLKNODEMETHOD_END 308 }; 309 310 static device_method_t jh7110_clk_pll_methods[] = { 311 /* Device interface */ 312 DEVMETHOD(device_probe, jh7110_clk_pll_probe), 313 DEVMETHOD(device_attach, jh7110_clk_pll_attach), 314 315 /* clkdev interface */ 316 DEVMETHOD(clkdev_device_lock, jh7110_clk_pll_device_lock), 317 DEVMETHOD(clkdev_device_unlock, jh7110_clk_pll_device_unlock), 318 319 DEVMETHOD_END 320 }; 321 322 DEFINE_CLASS_1(jh7110_pllnode, jh7110_pllnode_class, jh7110_pllnode_methods, 323 sizeof(struct jh7110_pll_clknode_softc), clknode_class); 324 DEFINE_CLASS_0(jh7110_clk_pll, jh7110_clk_pll_driver, jh7110_clk_pll_methods, 325 sizeof(struct jh7110_clk_pll_softc)); 326 EARLY_DRIVER_MODULE(jh7110_clk_pll, simplebus, jh7110_clk_pll_driver, 0, 0, 327 BUS_PASS_BUS + BUS_PASS_ORDER_EARLY); 328 MODULE_VERSION(jh7110_clk_pll, 1); 329 330 int 331 jh7110_clk_pll_register(struct clkdom *clkdom, struct jh7110_clk_def *clkdef) 332 { 333 struct clknode *clk = NULL; 334 struct jh7110_pll_clknode_softc *sc; 335 336 clk = clknode_create(clkdom, &jh7110_pllnode_class, &clkdef->clkdef); 337 if (clk == NULL) 338 return (1); 339 340 sc = clknode_get_softc(clk); 341 342 switch (clkdef->clkdef.id) { 343 case JH7110_PLLCLK_PLL0_OUT: 344 sc->syscon_arr = jh7110_pll0_syscon_freq; 345 sc->syscon_nitems = nitems(jh7110_pll0_syscon_freq); 346 PLL_MASK_FILL(sc, 0); 347 PLL_SHIFT_FILL(sc, 0); 348 sc->dacpd_offset = JH7110_SYS_SYSCON_SYSCFG24; 349 sc->dsmpd_offset = JH7110_SYS_SYSCON_SYSCFG24; 350 sc->fbdiv_offset = JH7110_SYS_SYSCON_SYSCFG28; 351 sc->frac_offset = JH7110_SYS_SYSCON_SYSCFG32; 352 sc->prediv_offset = JH7110_SYS_SYSCON_SYSCFG36; 353 sc->postdiv1_offset = JH7110_SYS_SYSCON_SYSCFG32; 354 break; 355 case JH7110_PLLCLK_PLL1_OUT: 356 sc->syscon_arr = jh7110_pll1_syscon_freq; 357 sc->syscon_nitems = nitems(jh7110_pll1_syscon_freq); 358 PLL_MASK_FILL(sc, 1); 359 PLL_SHIFT_FILL(sc, 1); 360 sc->dacpd_offset = JH7110_SYS_SYSCON_SYSCFG36; 361 sc->dsmpd_offset = JH7110_SYS_SYSCON_SYSCFG36; 362 sc->fbdiv_offset = JH7110_SYS_SYSCON_SYSCFG36; 363 sc->frac_offset = JH7110_SYS_SYSCON_SYSCFG40; 364 sc->prediv_offset = JH7110_SYS_SYSCON_SYSCFG44; 365 sc->postdiv1_offset = JH7110_SYS_SYSCON_SYSCFG40; 366 break; 367 case JH7110_PLLCLK_PLL2_OUT: 368 sc->syscon_arr = jh7110_pll2_syscon_freq; 369 sc->syscon_nitems = nitems(jh7110_pll2_syscon_freq); 370 PLL_MASK_FILL(sc, 2); 371 PLL_SHIFT_FILL(sc, 2); 372 sc->dacpd_offset = JH7110_SYS_SYSCON_SYSCFG44; 373 sc->dsmpd_offset = JH7110_SYS_SYSCON_SYSCFG44; 374 sc->fbdiv_offset = JH7110_SYS_SYSCON_SYSCFG44; 375 sc->frac_offset = JH7110_SYS_SYSCON_SYSCFG48; 376 sc->prediv_offset = JH7110_SYS_SYSCON_SYSCFG52; 377 sc->postdiv1_offset = JH7110_SYS_SYSCON_SYSCFG48; 378 break; 379 default: 380 return (EINVAL); 381 } 382 383 clknode_register(clkdom, clk); 384 385 return (0); 386 } 387