Path: blob/main/sys/dev/clk/starfive/jh7110_clk_pll.c
39537 views
/*-1* SPDX-License-Identifier: BSD-2-Clause2*3* Copyright (c) 2024 Jari Sihvola <[email protected]>4* Copyright (c) 2024 The FreeBSD Foundation5*6* Portions of this software were developed by Mitchell Horne7* <[email protected]> under sponsorship from the FreeBSD Foundation.8*/910#include <sys/param.h>11#include <sys/systm.h>12#include <sys/bus.h>13#include <sys/kernel.h>14#include <sys/module.h>15#include <sys/mutex.h>1617#include <machine/bus.h>1819#include <dev/fdt/simplebus.h>20#include <dev/ofw/ofw_bus.h>21#include <dev/ofw/ofw_bus_subr.h>2223#include <dev/clk/clk.h>24#include <dev/clk/starfive/jh7110_clk.h>25#include <dev/clk/starfive/jh7110_clk_pll.h>26#include <dev/syscon/syscon.h>2728#include <dt-bindings/clock/starfive,jh7110-crg.h>2930#include "clkdev_if.h"31#include "syscon_if.h"3233#define JH7110_SYS_SYSCON_SYSCFG24 0x1834#define JH7110_SYS_SYSCON_SYSCFG28 0x1c35#define JH7110_SYS_SYSCON_SYSCFG32 0x2036#define JH7110_SYS_SYSCON_SYSCFG36 0x2437#define JH7110_SYS_SYSCON_SYSCFG40 0x2838#define JH7110_SYS_SYSCON_SYSCFG44 0x2c39#define JH7110_SYS_SYSCON_SYSCFG48 0x3040#define JH7110_SYS_SYSCON_SYSCFG52 0x344142#define DEVICE_LOCK(_clk) \43CLKDEV_DEVICE_LOCK(clknode_get_device(_clk))44#define DEVICE_UNLOCK(_clk) \45CLKDEV_DEVICE_UNLOCK(clknode_get_device(_clk))4647#define PLL_MASK_FILL(sc, id) \48do { \49sc->dacpd_mask = PLL## id ##_DACPD_MASK; \50sc->dsmpd_mask = PLL## id ##_DSMPD_MASK; \51sc->fbdiv_mask = PLL## id ##_FBDIV_MASK; \52sc->frac_mask = PLL## id ##_FRAC_MASK; \53sc->prediv_mask = PLL## id ##_PREDIV_MASK; \54sc->postdiv1_mask = PLL## id ##_POSTDIV1_MASK; \55} while (0)5657#define PLL_SHIFT_FILL(sc, id) \58do { \59sc->dacpd_shift = PLL## id ##_DACPD_SHIFT; \60sc->dsmpd_shift = PLL## id ##_DSMPD_SHIFT; \61sc->fbdiv_shift = PLL## id ##_FBDIV_SHIFT; \62sc->frac_shift = PLL## id ##_FRAC_SHIFT; \63sc->prediv_shift = PLL## id ##_PREDIV_SHIFT; \64sc->postdiv1_shift = PLL## id ##_POSTDIV1_SHIFT; \65} while (0)6667struct jh7110_clk_pll_softc {68struct mtx mtx;69struct clkdom *clkdom;70struct syscon *syscon;71};7273struct jh7110_pll_clknode_softc {74uint32_t dacpd_offset;75uint32_t dsmpd_offset;76uint32_t fbdiv_offset;77uint32_t frac_offset;78uint32_t prediv_offset;79uint32_t postdiv1_offset;8081uint32_t dacpd_mask;82uint32_t dsmpd_mask;83uint32_t fbdiv_mask;84uint32_t frac_mask;85uint32_t prediv_mask;86uint32_t postdiv1_mask;8788uint32_t dacpd_shift;89uint32_t dsmpd_shift;90uint32_t fbdiv_shift;91uint32_t frac_shift;92uint32_t prediv_shift;93uint32_t postdiv1_shift;9495const struct jh7110_pll_syscon_value *syscon_arr;96int syscon_nitems;97};9899static const char *pll_parents[] = { "osc" };100101static struct jh7110_clk_def pll_out_clks[] = {102{103.clkdef.id = JH7110_PLLCLK_PLL0_OUT,104.clkdef.name = "pll0_out",105.clkdef.parent_names = pll_parents,106.clkdef.parent_cnt = nitems(pll_parents),107.clkdef.flags = CLK_NODE_STATIC_STRINGS,108},109{110.clkdef.id = JH7110_PLLCLK_PLL1_OUT,111.clkdef.name = "pll1_out",112.clkdef.parent_names = pll_parents,113.clkdef.parent_cnt = nitems(pll_parents),114.clkdef.flags = CLK_NODE_STATIC_STRINGS,115},116{117.clkdef.id = JH7110_PLLCLK_PLL2_OUT,118.clkdef.name = "pll2_out",119.clkdef.parent_names = pll_parents,120.clkdef.parent_cnt = nitems(pll_parents),121.clkdef.flags = CLK_NODE_STATIC_STRINGS,122},123};124125static int jh7110_clk_pll_register(struct clkdom *clkdom,126struct jh7110_clk_def *clkdef);127128static int129jh7110_clk_pll_recalc_freq(struct clknode *clk, uint64_t *freq)130{131struct jh7110_clk_pll_softc *sc;132struct jh7110_pll_clknode_softc *clk_sc;133uint32_t dacpd, dsmpd, fbdiv, prediv, postdiv1;134uint64_t frac, fcal = 0;135136sc = device_get_softc(clknode_get_device(clk));137clk_sc = clknode_get_softc(clk);138139DEVICE_LOCK(clk);140141dacpd = (SYSCON_READ_4(sc->syscon, clk_sc->dacpd_offset) & clk_sc->dacpd_mask) >>142clk_sc->dacpd_shift;143dsmpd = (SYSCON_READ_4(sc->syscon, clk_sc->dsmpd_offset) & clk_sc->dsmpd_mask) >>144clk_sc->dsmpd_shift;145fbdiv = (SYSCON_READ_4(sc->syscon, clk_sc->fbdiv_offset) & clk_sc->fbdiv_mask) >>146clk_sc->fbdiv_shift;147prediv = (SYSCON_READ_4(sc->syscon, clk_sc->prediv_offset) & clk_sc->prediv_mask) >>148clk_sc->prediv_shift;149postdiv1 = (SYSCON_READ_4(sc->syscon, clk_sc->postdiv1_offset) &150clk_sc->postdiv1_mask) >> clk_sc->postdiv1_shift;151frac = (SYSCON_READ_4(sc->syscon, clk_sc->frac_offset) & clk_sc->frac_mask) >>152clk_sc->frac_shift;153154DEVICE_UNLOCK(clk);155156/* dacpd and dsmpd both being 0 entails Fraction Multiple Mode */157if (dacpd == 0 && dsmpd == 0)158fcal = frac * FRAC_PATR_SIZE / (1 << 24);159160*freq = *freq / FRAC_PATR_SIZE * (fbdiv * FRAC_PATR_SIZE + fcal) /161prediv / (1 << postdiv1);162163return (0);164}165166static int167jh7110_clk_pll_set_freq(struct clknode *clk, uint64_t fin, uint64_t *fout,168int flags, int *done)169{170struct jh7110_clk_pll_softc *sc;171struct jh7110_pll_clknode_softc *clk_sc;172const struct jh7110_pll_syscon_value *syscon_val = NULL;173174sc = device_get_softc(clknode_get_device(clk));175clk_sc = clknode_get_softc(clk);176177for (int i = 0; i != clk_sc->syscon_nitems; i++) {178if (*fout == clk_sc->syscon_arr[i].freq) {179syscon_val = &clk_sc->syscon_arr[i];180}181}182183if (syscon_val == NULL) {184printf("%s: tried to set an unknown frequency %ju for %s\n",185__func__, *fout, clknode_get_name(clk));186return (EINVAL);187}188189if ((flags & CLK_SET_DRYRUN) != 0) {190*done = 1;191return (0);192}193194DEVICE_LOCK(clk);195196SYSCON_MODIFY_4(sc->syscon, clk_sc->dacpd_offset, clk_sc->dacpd_mask,197syscon_val->dacpd << clk_sc->dacpd_shift & clk_sc->dacpd_mask);198SYSCON_MODIFY_4(sc->syscon, clk_sc->dsmpd_offset, clk_sc->dsmpd_mask,199syscon_val->dsmpd << clk_sc->dsmpd_shift & clk_sc->dsmpd_mask);200SYSCON_MODIFY_4(sc->syscon, clk_sc->prediv_offset, clk_sc->prediv_mask,201syscon_val->prediv << clk_sc->prediv_shift & clk_sc->prediv_mask);202SYSCON_MODIFY_4(sc->syscon, clk_sc->fbdiv_offset, clk_sc->fbdiv_mask,203syscon_val->fbdiv << clk_sc->fbdiv_shift & clk_sc->fbdiv_mask);204SYSCON_MODIFY_4(sc->syscon, clk_sc->postdiv1_offset,205clk_sc->postdiv1_mask, (syscon_val->postdiv1 >> 1) <<206clk_sc->postdiv1_shift & clk_sc->postdiv1_mask);207208if (!syscon_val->dacpd && !syscon_val->dsmpd) {209SYSCON_MODIFY_4(sc->syscon, clk_sc->frac_offset, clk_sc->frac_mask,210syscon_val->frac << clk_sc->frac_shift & clk_sc->frac_mask);211}212213DEVICE_UNLOCK(clk);214215*done = 1;216return (0);217}218219static int220jh7110_clk_pll_init(struct clknode *clk, device_t dev)221{222clknode_init_parent_idx(clk, 0);223224return (0);225}226227static int228jh7110_clk_pll_probe(device_t dev)229{230if (!ofw_bus_status_okay(dev))231return (ENXIO);232233if (!ofw_bus_is_compatible(dev, "starfive,jh7110-pll"))234return (ENXIO);235236device_set_desc(dev, "StarFive JH7110 PLL clock generator");237238return (BUS_PROBE_DEFAULT);239}240241static int242jh7110_clk_pll_attach(device_t dev)243{244struct jh7110_clk_pll_softc *sc;245int error;246247sc = device_get_softc(dev);248249mtx_init(&sc->mtx, device_get_nameunit(dev), NULL, MTX_DEF);250251sc->clkdom = clkdom_create(dev);252if (sc->clkdom == NULL) {253device_printf(dev, "Couldn't create clkdom\n");254return (ENXIO);255}256257error = syscon_get_by_ofw_node(dev, OF_parent(ofw_bus_get_node(dev)),258&sc->syscon);259if (error != 0) {260device_printf(dev, "Couldn't get syscon handle of parent\n");261return (error);262}263264for (int i = 0; i < nitems(pll_out_clks); i++) {265error = jh7110_clk_pll_register(sc->clkdom, &pll_out_clks[i]);266if (error != 0)267device_printf(dev, "Couldn't register clock %s: %d\n",268pll_out_clks[i].clkdef.name, error);269}270271error = clkdom_finit(sc->clkdom);272if (error != 0) {273device_printf(dev, "clkdom_finit() returned %d\n", error);274}275276if (bootverbose)277clkdom_dump(sc->clkdom);278279return (0);280}281282static void283jh7110_clk_pll_device_lock(device_t dev)284{285struct jh7110_clk_pll_softc *sc;286287sc = device_get_softc(dev);288mtx_lock(&sc->mtx);289}290291static void292jh7110_clk_pll_device_unlock(device_t dev)293{294struct jh7110_clk_pll_softc *sc;295296sc = device_get_softc(dev);297mtx_unlock(&sc->mtx);298}299300static clknode_method_t jh7110_pllnode_methods[] = {301/* Device interface */302CLKNODEMETHOD(clknode_init, jh7110_clk_pll_init),303CLKNODEMETHOD(clknode_recalc_freq, jh7110_clk_pll_recalc_freq),304CLKNODEMETHOD(clknode_set_freq, jh7110_clk_pll_set_freq),305306CLKNODEMETHOD_END307};308309static device_method_t jh7110_clk_pll_methods[] = {310/* Device interface */311DEVMETHOD(device_probe, jh7110_clk_pll_probe),312DEVMETHOD(device_attach, jh7110_clk_pll_attach),313314/* clkdev interface */315DEVMETHOD(clkdev_device_lock, jh7110_clk_pll_device_lock),316DEVMETHOD(clkdev_device_unlock, jh7110_clk_pll_device_unlock),317318DEVMETHOD_END319};320321DEFINE_CLASS_1(jh7110_pllnode, jh7110_pllnode_class, jh7110_pllnode_methods,322sizeof(struct jh7110_pll_clknode_softc), clknode_class);323DEFINE_CLASS_0(jh7110_clk_pll, jh7110_clk_pll_driver, jh7110_clk_pll_methods,324sizeof(struct jh7110_clk_pll_softc));325EARLY_DRIVER_MODULE(jh7110_clk_pll, simplebus, jh7110_clk_pll_driver, 0, 0,326BUS_PASS_BUS + BUS_PASS_ORDER_EARLY);327MODULE_VERSION(jh7110_clk_pll, 1);328329int330jh7110_clk_pll_register(struct clkdom *clkdom, struct jh7110_clk_def *clkdef)331{332struct clknode *clk = NULL;333struct jh7110_pll_clknode_softc *sc;334335clk = clknode_create(clkdom, &jh7110_pllnode_class, &clkdef->clkdef);336if (clk == NULL)337return (1);338339sc = clknode_get_softc(clk);340341switch (clkdef->clkdef.id) {342case JH7110_PLLCLK_PLL0_OUT:343sc->syscon_arr = jh7110_pll0_syscon_freq;344sc->syscon_nitems = nitems(jh7110_pll0_syscon_freq);345PLL_MASK_FILL(sc, 0);346PLL_SHIFT_FILL(sc, 0);347sc->dacpd_offset = JH7110_SYS_SYSCON_SYSCFG24;348sc->dsmpd_offset = JH7110_SYS_SYSCON_SYSCFG24;349sc->fbdiv_offset = JH7110_SYS_SYSCON_SYSCFG28;350sc->frac_offset = JH7110_SYS_SYSCON_SYSCFG32;351sc->prediv_offset = JH7110_SYS_SYSCON_SYSCFG36;352sc->postdiv1_offset = JH7110_SYS_SYSCON_SYSCFG32;353break;354case JH7110_PLLCLK_PLL1_OUT:355sc->syscon_arr = jh7110_pll1_syscon_freq;356sc->syscon_nitems = nitems(jh7110_pll1_syscon_freq);357PLL_MASK_FILL(sc, 1);358PLL_SHIFT_FILL(sc, 1);359sc->dacpd_offset = JH7110_SYS_SYSCON_SYSCFG36;360sc->dsmpd_offset = JH7110_SYS_SYSCON_SYSCFG36;361sc->fbdiv_offset = JH7110_SYS_SYSCON_SYSCFG36;362sc->frac_offset = JH7110_SYS_SYSCON_SYSCFG40;363sc->prediv_offset = JH7110_SYS_SYSCON_SYSCFG44;364sc->postdiv1_offset = JH7110_SYS_SYSCON_SYSCFG40;365break;366case JH7110_PLLCLK_PLL2_OUT:367sc->syscon_arr = jh7110_pll2_syscon_freq;368sc->syscon_nitems = nitems(jh7110_pll2_syscon_freq);369PLL_MASK_FILL(sc, 2);370PLL_SHIFT_FILL(sc, 2);371sc->dacpd_offset = JH7110_SYS_SYSCON_SYSCFG44;372sc->dsmpd_offset = JH7110_SYS_SYSCON_SYSCFG44;373sc->fbdiv_offset = JH7110_SYS_SYSCON_SYSCFG44;374sc->frac_offset = JH7110_SYS_SYSCON_SYSCFG48;375sc->prediv_offset = JH7110_SYS_SYSCON_SYSCFG52;376sc->postdiv1_offset = JH7110_SYS_SYSCON_SYSCFG48;377break;378default:379return (EINVAL);380}381382clknode_register(clkdom, clk);383384return (0);385}386387388