// SPDX-License-Identifier: GPL-2.0 /* * StarFive JH7110 PLL Clock Generator Driver * * Copyright (C) 2023 StarFive Technology Co., Ltd. * Copyright (C) 2023 Emil Renner Berthing * * This driver is about to register JH7110 PLL clock generator and support ops. * The JH7110 have three PLL clock, PLL0, PLL1 and PLL2. * Each PLL clocks work in integer mode or fraction mode by some dividers, * and the configuration registers and dividers are set in several syscon registers. * The formula for calculating frequency is: * Fvco = Fref * (NI + NF) / M / Q1 * Fref: OSC source clock rate * NI: integer frequency dividing ratio of feedback divider, set by fbdiv[11:0]. * NF: fractional frequency dividing ratio, set by frac[23:0]. NF = frac[23:0] / 2^24 = 0 ~ 0.999. * M: frequency dividing ratio of pre-divider, set by prediv[5:0]. * Q1: frequency dividing ratio of post divider, set by 2^postdiv1[1:0], eg. 1, 2, 4 or 8. */ #include #include #include #include #include #include #include #include #include /* this driver expects a 24MHz input frequency from the oscillator */ #define JH7110_PLL_OSC_RATE 24000000UL #define JH7110_PLL0_PD_OFFSET 0x18 #define JH7110_PLL0_DACPD_SHIFT 24 #define JH7110_PLL0_DACPD_MASK BIT(24) #define JH7110_PLL0_DSMPD_SHIFT 25 #define JH7110_PLL0_DSMPD_MASK BIT(25) #define JH7110_PLL0_FBDIV_OFFSET 0x1c #define JH7110_PLL0_FBDIV_SHIFT 0 #define JH7110_PLL0_FBDIV_MASK GENMASK(11, 0) #define JH7110_PLL0_FRAC_OFFSET 0x20 #define JH7110_PLL0_PREDIV_OFFSET 0x24 #define JH7110_PLL1_PD_OFFSET 0x24 #define JH7110_PLL1_DACPD_SHIFT 15 #define JH7110_PLL1_DACPD_MASK BIT(15) #define JH7110_PLL1_DSMPD_SHIFT 16 #define JH7110_PLL1_DSMPD_MASK BIT(16) #define JH7110_PLL1_FBDIV_OFFSET 0x24 #define JH7110_PLL1_FBDIV_SHIFT 17 #define JH7110_PLL1_FBDIV_MASK GENMASK(28, 17) #define JH7110_PLL1_FRAC_OFFSET 0x28 #define JH7110_PLL1_PREDIV_OFFSET 0x2c #define JH7110_PLL2_PD_OFFSET 0x2c #define JH7110_PLL2_DACPD_SHIFT 15 #define JH7110_PLL2_DACPD_MASK BIT(15) #define JH7110_PLL2_DSMPD_SHIFT 16 #define JH7110_PLL2_DSMPD_MASK BIT(16) #define JH7110_PLL2_FBDIV_OFFSET 0x2c #define JH7110_PLL2_FBDIV_SHIFT 17 #define JH7110_PLL2_FBDIV_MASK GENMASK(28, 17) #define JH7110_PLL2_FRAC_OFFSET 0x30 #define JH7110_PLL2_PREDIV_OFFSET 0x34 #define JH7110_PLL_FRAC_SHIFT 0 #define JH7110_PLL_FRAC_MASK GENMASK(23, 0) #define JH7110_PLL_POSTDIV1_SHIFT 28 #define JH7110_PLL_POSTDIV1_MASK GENMASK(29, 28) #define JH7110_PLL_PREDIV_SHIFT 0 #define JH7110_PLL_PREDIV_MASK GENMASK(5, 0) enum jh7110_pll_mode { JH7110_PLL_MODE_FRACTION, JH7110_PLL_MODE_INTEGER, }; struct jh7110_pll_preset { unsigned long freq; u32 frac; /* frac value should be decimals multiplied by 2^24 */ unsigned fbdiv : 12; /* fbdiv value should be 8 to 4095 */ unsigned prediv : 6; unsigned postdiv1 : 2; unsigned mode : 1; }; struct jh7110_pll_info { char *name; const struct jh7110_pll_preset *presets; unsigned int npresets; struct { unsigned int pd; unsigned int fbdiv; unsigned int frac; unsigned int prediv; } offsets; struct { u32 dacpd; u32 dsmpd; u32 fbdiv; } masks; struct { char dacpd; char dsmpd; char fbdiv; } shifts; }; #define _JH7110_PLL(_idx, _name, _presets) \ [_idx] = { \ .name = _name, \ .presets = _presets, \ .npresets = ARRAY_SIZE(_presets), \ .offsets = { \ .pd = JH7110_PLL##_idx##_PD_OFFSET, \ .fbdiv = JH7110_PLL##_idx##_FBDIV_OFFSET, \ .frac = JH7110_PLL##_idx##_FRAC_OFFSET, \ .prediv = JH7110_PLL##_idx##_PREDIV_OFFSET, \ }, \ .masks = { \ .dacpd = JH7110_PLL##_idx##_DACPD_MASK, \ .dsmpd = JH7110_PLL##_idx##_DSMPD_MASK, \ .fbdiv = JH7110_PLL##_idx##_FBDIV_MASK, \ }, \ .shifts = { \ .dacpd = JH7110_PLL##_idx##_DACPD_SHIFT, \ .dsmpd = JH7110_PLL##_idx##_DSMPD_SHIFT, \ .fbdiv = JH7110_PLL##_idx##_FBDIV_SHIFT, \ }, \ } #define JH7110_PLL(idx, name, presets) _JH7110_PLL(idx, name, presets) struct jh7110_pll_data { struct clk_hw hw; unsigned int idx; }; struct jh7110_pll_priv { struct device *dev; struct regmap *regmap; struct jh7110_pll_data pll[JH7110_PLLCLK_END]; }; struct jh7110_pll_regvals { u32 dacpd; u32 dsmpd; u32 fbdiv; u32 frac; u32 postdiv1; u32 prediv; }; /* * Because the pll frequency is relatively fixed, * it cannot be set arbitrarily, so it needs a specific configuration. * PLL0 frequency should be multiple of 125MHz (USB frequency). */ static const struct jh7110_pll_preset jh7110_pll0_presets[] = { { .freq = 375000000, .fbdiv = 125, .prediv = 8, .postdiv1 = 0, .mode = JH7110_PLL_MODE_INTEGER, }, { .freq = 500000000, .fbdiv = 125, .prediv = 6, .postdiv1 = 0, .mode = JH7110_PLL_MODE_INTEGER, }, { .freq = 625000000, .fbdiv = 625, .prediv = 24, .postdiv1 = 0, .mode = JH7110_PLL_MODE_INTEGER, }, { .freq = 750000000, .fbdiv = 125, .prediv = 4, .postdiv1 = 0, .mode = JH7110_PLL_MODE_INTEGER, }, { .freq = 875000000, .fbdiv = 875, .prediv = 24, .postdiv1 = 0, .mode = JH7110_PLL_MODE_INTEGER, }, { .freq = 1000000000, .fbdiv = 125, .prediv = 3, .postdiv1 = 0, .mode = JH7110_PLL_MODE_INTEGER, }, { .freq = 1250000000, .fbdiv = 625, .prediv = 12, .postdiv1 = 0, .mode = JH7110_PLL_MODE_INTEGER, }, { .freq = 1375000000, .fbdiv = 1375, .prediv = 24, .postdiv1 = 0, .mode = JH7110_PLL_MODE_INTEGER, }, { .freq = 1500000000, .fbdiv = 125, .prediv = 2, .postdiv1 = 0, .mode = JH7110_PLL_MODE_INTEGER, }, }; static const struct jh7110_pll_preset jh7110_pll1_presets[] = { { .freq = 1066000000, .fbdiv = 533, .prediv = 12, .postdiv1 = 0, .mode = JH7110_PLL_MODE_INTEGER, }, { .freq = 1200000000, .fbdiv = 50, .prediv = 1, .postdiv1 = 0, .mode = JH7110_PLL_MODE_INTEGER, }, { .freq = 1400000000, .fbdiv = 350, .prediv = 6, .postdiv1 = 0, .mode = JH7110_PLL_MODE_INTEGER, }, { .freq = 1600000000, .fbdiv = 200, .prediv = 3, .postdiv1 = 0, .mode = JH7110_PLL_MODE_INTEGER, }, }; static const struct jh7110_pll_preset jh7110_pll2_presets[] = { { .freq = 1188000000, .fbdiv = 99, .prediv = 2, .postdiv1 = 0, .mode = JH7110_PLL_MODE_INTEGER, }, { .freq = 1228800000, .fbdiv = 256, .prediv = 5, .postdiv1 = 0, .mode = JH7110_PLL_MODE_INTEGER, }, }; static const struct jh7110_pll_info jh7110_plls[JH7110_PLLCLK_END] = { JH7110_PLL(JH7110_PLLCLK_PLL0_OUT, "pll0_out", jh7110_pll0_presets), JH7110_PLL(JH7110_PLLCLK_PLL1_OUT, "pll1_out", jh7110_pll1_presets), JH7110_PLL(JH7110_PLLCLK_PLL2_OUT, "pll2_out", jh7110_pll2_presets), }; static struct jh7110_pll_data *jh7110_pll_data_from(struct clk_hw *hw) { return container_of(hw, struct jh7110_pll_data, hw); } static struct jh7110_pll_priv *jh7110_pll_priv_from(struct jh7110_pll_data *pll) { return container_of(pll, struct jh7110_pll_priv, pll[pll->idx]); } static void jh7110_pll_regvals_get(struct regmap *regmap, const struct jh7110_pll_info *info, struct jh7110_pll_regvals *ret) { u32 val; regmap_read(regmap, info->offsets.pd, &val); ret->dacpd = (val & info->masks.dacpd) >> info->shifts.dacpd; ret->dsmpd = (val & info->masks.dsmpd) >> info->shifts.dsmpd; regmap_read(regmap, info->offsets.fbdiv, &val); ret->fbdiv = (val & info->masks.fbdiv) >> info->shifts.fbdiv; regmap_read(regmap, info->offsets.frac, &val); ret->frac = (val & JH7110_PLL_FRAC_MASK) >> JH7110_PLL_FRAC_SHIFT; ret->postdiv1 = (val & JH7110_PLL_POSTDIV1_MASK) >> JH7110_PLL_POSTDIV1_SHIFT; regmap_read(regmap, info->offsets.prediv, &val); ret->prediv = (val & JH7110_PLL_PREDIV_MASK) >> JH7110_PLL_PREDIV_SHIFT; } static unsigned long jh7110_pll_recalc_rate(struct clk_hw *hw, unsigned long parent_rate) { struct jh7110_pll_data *pll = jh7110_pll_data_from(hw); struct jh7110_pll_priv *priv = jh7110_pll_priv_from(pll); struct jh7110_pll_regvals val; unsigned long rate; jh7110_pll_regvals_get(priv->regmap, &jh7110_plls[pll->idx], &val); /* * dacpd = dsmpd = 0: fraction mode * dacpd = dsmpd = 1: integer mode, frac value ignored * * rate = parent * (fbdiv + frac/2^24) / prediv / 2^postdiv1 * = (parent * fbdiv + parent * frac / 2^24) / (prediv * 2^postdiv1) */ if (val.dacpd == 0 && val.dsmpd == 0) rate = parent_rate * val.frac / (1UL << 24); else if (val.dacpd == 1 && val.dsmpd == 1) rate = 0; else return 0; rate += parent_rate * val.fbdiv; rate /= val.prediv << val.postdiv1; return rate; } static int jh7110_pll_determine_rate(struct clk_hw *hw, struct clk_rate_request *req) { struct jh7110_pll_data *pll = jh7110_pll_data_from(hw); const struct jh7110_pll_info *info = &jh7110_plls[pll->idx]; const struct jh7110_pll_preset *selected = &info->presets[0]; unsigned int idx; /* if the parent rate doesn't match our expectations the presets won't work */ if (req->best_parent_rate != JH7110_PLL_OSC_RATE) { req->rate = jh7110_pll_recalc_rate(hw, req->best_parent_rate); return 0; } /* find highest rate lower or equal to the requested rate */ for (idx = 1; idx < info->npresets; idx++) { const struct jh7110_pll_preset *val = &info->presets[idx]; if (req->rate < val->freq) break; selected = val; } req->rate = selected->freq; return 0; } static int jh7110_pll_set_rate(struct clk_hw *hw, unsigned long rate, unsigned long parent_rate) { struct jh7110_pll_data *pll = jh7110_pll_data_from(hw); struct jh7110_pll_priv *priv = jh7110_pll_priv_from(pll); const struct jh7110_pll_info *info = &jh7110_plls[pll->idx]; const struct jh7110_pll_preset *val; unsigned int idx; /* if the parent rate doesn't match our expectations the presets won't work */ if (parent_rate != JH7110_PLL_OSC_RATE) return -EINVAL; for (idx = 0, val = &info->presets[0]; idx < info->npresets; idx++, val++) { if (val->freq == rate) goto found; } return -EINVAL; found: if (val->mode == JH7110_PLL_MODE_FRACTION) regmap_update_bits(priv->regmap, info->offsets.frac, JH7110_PLL_FRAC_MASK, val->frac << JH7110_PLL_FRAC_SHIFT); regmap_update_bits(priv->regmap, info->offsets.pd, info->masks.dacpd, (u32)val->mode << info->shifts.dacpd); regmap_update_bits(priv->regmap, info->offsets.pd, info->masks.dsmpd, (u32)val->mode << info->shifts.dsmpd); regmap_update_bits(priv->regmap, info->offsets.prediv, JH7110_PLL_PREDIV_MASK, (u32)val->prediv << JH7110_PLL_PREDIV_SHIFT); regmap_update_bits(priv->regmap, info->offsets.fbdiv, info->masks.fbdiv, val->fbdiv << info->shifts.fbdiv); regmap_update_bits(priv->regmap, info->offsets.frac, JH7110_PLL_POSTDIV1_MASK, (u32)val->postdiv1 << JH7110_PLL_POSTDIV1_SHIFT); return 0; } #ifdef CONFIG_DEBUG_FS static int jh7110_pll_registers_read(struct seq_file *s, void *unused) { struct jh7110_pll_data *pll = s->private; struct jh7110_pll_priv *priv = jh7110_pll_priv_from(pll); struct jh7110_pll_regvals val; jh7110_pll_regvals_get(priv->regmap, &jh7110_plls[pll->idx], &val); seq_printf(s, "fbdiv=%u\n" "frac=%u\n" "prediv=%u\n" "postdiv1=%u\n" "dacpd=%u\n" "dsmpd=%u\n", val.fbdiv, val.frac, val.prediv, val.postdiv1, val.dacpd, val.dsmpd); return 0; } static int jh7110_pll_registers_open(struct inode *inode, struct file *f) { return single_open(f, jh7110_pll_registers_read, inode->i_private); } static const struct file_operations jh7110_pll_registers_ops = { .owner = THIS_MODULE, .open = jh7110_pll_registers_open, .release = single_release, .read = seq_read, .llseek = seq_lseek }; static void jh7110_pll_debug_init(struct clk_hw *hw, struct dentry *dentry) { struct jh7110_pll_data *pll = jh7110_pll_data_from(hw); debugfs_create_file("registers", 0400, dentry, pll, &jh7110_pll_registers_ops); } #else #define jh7110_pll_debug_init NULL #endif static const struct clk_ops jh7110_pll_ops = { .recalc_rate = jh7110_pll_recalc_rate, .determine_rate = jh7110_pll_determine_rate, .set_rate = jh7110_pll_set_rate, .debug_init = jh7110_pll_debug_init, }; static struct clk_hw *jh7110_pll_get(struct of_phandle_args *clkspec, void *data) { struct jh7110_pll_priv *priv = data; unsigned int idx = clkspec->args[0]; if (idx < JH7110_PLLCLK_END) return &priv->pll[idx].hw; return ERR_PTR(-EINVAL); } static int jh7110_pll_probe(struct platform_device *pdev) { struct jh7110_pll_priv *priv; unsigned int idx; int ret; priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL); if (!priv) return -ENOMEM; priv->dev = &pdev->dev; priv->regmap = syscon_node_to_regmap(priv->dev->of_node->parent); if (IS_ERR(priv->regmap)) return PTR_ERR(priv->regmap); for (idx = 0; idx < JH7110_PLLCLK_END; idx++) { struct clk_parent_data parents = { .index = 0, }; struct clk_init_data init = { .name = jh7110_plls[idx].name, .ops = &jh7110_pll_ops, .parent_data = &parents, .num_parents = 1, .flags = 0, }; struct jh7110_pll_data *pll = &priv->pll[idx]; pll->hw.init = &init; pll->idx = idx; ret = devm_clk_hw_register(&pdev->dev, &pll->hw); if (ret) return ret; } return devm_of_clk_add_hw_provider(&pdev->dev, jh7110_pll_get, priv); } static const struct of_device_id jh7110_pll_match[] = { { .compatible = "starfive,jh7110-pll" }, { /* sentinel */ } }; MODULE_DEVICE_TABLE(of, jh7110_pll_match); static struct platform_driver jh7110_pll_driver = { .driver = { .name = "clk-starfive-jh7110-pll", .of_match_table = jh7110_pll_match, }, }; builtin_platform_driver_probe(jh7110_pll_driver, jh7110_pll_probe);