Lines Matching +full:versal +full:- +full:clk
1 // SPDX-License-Identifier: GPL-2.0
5 * Copyright (C) 2013 - 2021 Xilinx
13 #include <linux/clk.h>
14 #include <linux/clk-provider.h>
88 /* Divider limits, from UG572 Table 3-4 for Ultrascale+ */
112 #define div_mask(width) ((1 << (width)) - 1)
125 * struct clk_wzrd - Clock wizard private data structure
139 struct clk *clk_in1;
140 struct clk *axi_clk;
148 * struct clk_wzrd_divider - clock divider specific to clk_wzrd
150 * @hw: handle between common and hardware-specific interfaces
200 void __iomem *div_addr = divider->base + divider->offset; in clk_wzrd_recalc_rate_ver()
225 void __iomem *div_addr = divider->base + divider->offset; in clk_wzrd_recalc_rate()
228 val = readl(div_addr) >> divider->shift; in clk_wzrd_recalc_rate()
229 val &= div_mask(divider->width); in clk_wzrd_recalc_rate()
231 return divider_recalc_rate(hw, parent_rate, val, divider->table, in clk_wzrd_recalc_rate()
232 divider->flags, divider->width); in clk_wzrd_recalc_rate()
239 void __iomem *div_addr = divider->base + divider->offset; in clk_wzrd_ver_dynamic_reconfig()
244 spin_lock_irqsave(divider->lock, flags); in clk_wzrd_ver_dynamic_reconfig()
264 err = readl_poll_timeout_atomic(divider->base + WZRD_DR_STATUS_REG_OFFSET, in clk_wzrd_ver_dynamic_reconfig()
272 divider->base + WZRD_DR_INIT_VERSAL_OFFSET); in clk_wzrd_ver_dynamic_reconfig()
275 err = readl_poll_timeout_atomic(divider->base + WZRD_DR_STATUS_REG_OFFSET, in clk_wzrd_ver_dynamic_reconfig()
279 spin_unlock_irqrestore(divider->lock, flags); in clk_wzrd_ver_dynamic_reconfig()
287 void __iomem *div_addr = divider->base + divider->offset; in clk_wzrd_dynamic_reconfig()
292 spin_lock_irqsave(divider->lock, flags); in clk_wzrd_dynamic_reconfig()
304 err = readl_poll_timeout_atomic(divider->base + WZRD_DR_STATUS_REG_OFFSET, in clk_wzrd_dynamic_reconfig()
312 divider->base + WZRD_DR_INIT_REG_OFFSET); in clk_wzrd_dynamic_reconfig()
314 divider->base + WZRD_DR_INIT_REG_OFFSET); in clk_wzrd_dynamic_reconfig()
317 err = readl_poll_timeout_atomic(divider->base + WZRD_DR_STATUS_REG_OFFSET, in clk_wzrd_dynamic_reconfig()
321 spin_unlock_irqrestore(divider->lock, flags); in clk_wzrd_dynamic_reconfig()
362 diff = abs(freq - rate); in clk_wzrd_get_divisors_ver()
365 divider->m = m; in clk_wzrd_get_divisors_ver()
366 divider->d = d; in clk_wzrd_get_divisors_ver()
367 divider->o = o; in clk_wzrd_get_divisors_ver()
374 return -EBUSY; in clk_wzrd_get_divisors_ver()
381 u64 vco_freq, freq, diff, vcomin, vcomax, best_diff = -1ULL; in clk_wzrd_get_divisors()
403 diff = freq - rate; in clk_wzrd_get_divisors()
406 divider->m = m >> 3; in clk_wzrd_get_divisors()
407 divider->m_frac = (m - (divider->m << 3)) * 125; in clk_wzrd_get_divisors()
408 divider->d = d; in clk_wzrd_get_divisors()
409 divider->o = o >> 3; in clk_wzrd_get_divisors()
410 divider->o_frac = (o - (divider->o << 3)) * 125; in clk_wzrd_get_divisors()
414 return best_diff < WZRD_MIN_ERR ? 0 : -EBUSY; in clk_wzrd_get_divisors()
423 err = readl_poll_timeout_atomic(divider->base + WZRD_DR_STATUS_REG_OFFSET, value, in clk_wzrd_reconfig()
427 return -ETIMEDOUT; in clk_wzrd_reconfig()
432 return readl_poll_timeout_atomic(divider->base + WZRD_DR_STATUS_REG_OFFSET, value, in clk_wzrd_reconfig()
449 writel(0, divider->base + WZRD_CLK_CFG_REG(1, WZRD_CLKFBOUT_4)); in clk_wzrd_dynamic_ver_all_nolock()
451 m = divider->m; in clk_wzrd_dynamic_ver_all_nolock()
454 regval1 = readl(divider->base + WZRD_CLK_CFG_REG(1, in clk_wzrd_dynamic_ver_all_nolock()
462 writel(regval1, divider->base + WZRD_CLK_CFG_REG(1, in clk_wzrd_dynamic_ver_all_nolock()
465 writel(regval1, divider->base + WZRD_CLK_CFG_REG(1, in clk_wzrd_dynamic_ver_all_nolock()
468 value2 = divider->d; in clk_wzrd_dynamic_ver_all_nolock()
472 writel(regval1, divider->base + WZRD_CLK_CFG_REG(1, in clk_wzrd_dynamic_ver_all_nolock()
475 writel(regval1, divider->base + WZRD_CLK_CFG_REG(1, WZRD_DIVCLK)); in clk_wzrd_dynamic_ver_all_nolock()
477 value = divider->o; in clk_wzrd_dynamic_ver_all_nolock()
479 regval1 = readl(divider->base + WZRD_CLK_CFG_REG(1, in clk_wzrd_dynamic_ver_all_nolock()
493 writel(regval1, divider->base + WZRD_CLK_CFG_REG(1, in clk_wzrd_dynamic_ver_all_nolock()
496 writel(regval, divider->base + WZRD_CLK_CFG_REG(1, in clk_wzrd_dynamic_ver_all_nolock()
498 div_addr = divider->base + WZRD_DR_INIT_VERSAL_OFFSET; in clk_wzrd_dynamic_ver_all_nolock()
515 reg = FIELD_PREP(WZRD_CLKOUT_DIVIDE_MASK, divider->o) | in clk_wzrd_dynamic_all_nolock()
516 FIELD_PREP(WZRD_CLKOUT0_FRAC_MASK, divider->o_frac); in clk_wzrd_dynamic_all_nolock()
518 writel(reg, divider->base + WZRD_CLK_CFG_REG(0, 2)); in clk_wzrd_dynamic_all_nolock()
519 reg = FIELD_PREP(WZRD_CLKFBOUT_MULT_MASK, divider->m) | in clk_wzrd_dynamic_all_nolock()
520 FIELD_PREP(WZRD_CLKFBOUT_MULT_FRAC_MASK, divider->m_frac) | in clk_wzrd_dynamic_all_nolock()
521 FIELD_PREP(WZRD_DIVCLK_DIVIDE_MASK, divider->d); in clk_wzrd_dynamic_all_nolock()
522 writel(reg, divider->base + WZRD_CLK_CFG_REG(0, 0)); in clk_wzrd_dynamic_all_nolock()
523 writel(0, divider->base + WZRD_CLK_CFG_REG(0, 3)); in clk_wzrd_dynamic_all_nolock()
524 div_addr = divider->base + WZRD_DR_INIT_REG_OFFSET; in clk_wzrd_dynamic_all_nolock()
535 spin_lock_irqsave(divider->lock, flags); in clk_wzrd_dynamic_all()
539 spin_unlock_irqrestore(divider->lock, flags); in clk_wzrd_dynamic_all()
551 spin_lock_irqsave(divider->lock, flags); in clk_wzrd_dynamic_all_ver()
555 spin_unlock_irqrestore(divider->lock, flags); in clk_wzrd_dynamic_all_ver()
567 reg = readl(divider->base + WZRD_CLK_CFG_REG(0, 0)); in clk_wzrd_recalc_rate_all()
571 reg = readl(divider->base + WZRD_CLK_CFG_REG(0, 2)); in clk_wzrd_recalc_rate_all()
586 edge = !!(readl(divider->base + WZRD_CLK_CFG_REG(1, WZRD_CLKFBOUT_1)) & in clk_wzrd_recalc_rate_all_ver()
589 reg = readl(divider->base + WZRD_CLK_CFG_REG(1, WZRD_CLKFBOUT_2)); in clk_wzrd_recalc_rate_all_ver()
597 regl = readl(divider->base + WZRD_CLK_CFG_REG(1, WZRD_CLKFBOUT_4)) & in clk_wzrd_recalc_rate_all_ver()
600 regl = readl(divider->base + WZRD_CLK_CFG_REG(1, WZRD_CLKFBOUT_3)) in clk_wzrd_recalc_rate_all_ver()
609 reg = readl(divider->base + WZRD_CLK_CFG_REG(1, WZRD_CLKOUT0_1)); in clk_wzrd_recalc_rate_all_ver()
614 reg = readl(divider->base + WZRD_CLK_CFG_REG(1, WZRD_CLKOUT0_2)); in clk_wzrd_recalc_rate_all_ver()
629 edged = !!(readl(divider->base + WZRD_CLK_CFG_REG(1, WZRD_DESKEW_2)) & in clk_wzrd_recalc_rate_all_ver()
631 reg = readl(divider->base + WZRD_CLK_CFG_REG(1, WZRD_DIVCLK)); in clk_wzrd_recalc_rate_all_ver()
641 return divider_recalc_rate(hw, parent_rate, div, divider->table, in clk_wzrd_recalc_rate_all_ver()
642 divider->flags, divider->width); in clk_wzrd_recalc_rate_all_ver()
656 m = divider->m; in clk_wzrd_round_rate_all()
657 d = divider->d; in clk_wzrd_round_rate_all()
658 o = divider->o; in clk_wzrd_round_rate_all()
660 rate = div_u64(*prate * (m * 1000 + divider->m_frac), d * (o * 1000 + divider->o_frac)); in clk_wzrd_round_rate_all()
676 m = divider->m; in clk_wzrd_ver_round_rate_all()
677 d = divider->d; in clk_wzrd_ver_round_rate_all()
678 o = divider->o; in clk_wzrd_ver_round_rate_all()
681 int_freq = divider_recalc_rate(hw, *prate * m, div, divider->table, in clk_wzrd_ver_round_rate_all()
682 divider->flags, divider->width); in clk_wzrd_ver_round_rate_all()
721 void __iomem *div_addr = divider->base + divider->offset; in clk_wzrd_recalc_ratef()
724 div = val & div_mask(divider->width); in clk_wzrd_recalc_ratef()
737 void __iomem *div_addr = divider->base + divider->offset; in clk_wzrd_dynamic_reconfig_f()
743 f = (u32)(pre - (clockout0_div * 1000)); in clk_wzrd_dynamic_reconfig_f()
754 err = readl_poll_timeout(divider->base + WZRD_DR_STATUS_REG_OFFSET, value, in clk_wzrd_dynamic_reconfig_f()
762 divider->base + WZRD_DR_INIT_REG_OFFSET); in clk_wzrd_dynamic_reconfig_f()
764 divider->base + WZRD_DR_INIT_REG_OFFSET); in clk_wzrd_dynamic_reconfig_f()
767 return readl_poll_timeout(divider->base + WZRD_DR_STATUS_REG_OFFSET, value, in clk_wzrd_dynamic_reconfig_f()
801 return ERR_PTR(-ENOMEM); in clk_wzrd_register_divf()
811 div->base = base; in clk_wzrd_register_divf()
812 div->offset = offset; in clk_wzrd_register_divf()
813 div->shift = shift; in clk_wzrd_register_divf()
814 div->width = width; in clk_wzrd_register_divf()
815 div->flags = clk_divider_flags; in clk_wzrd_register_divf()
816 div->lock = lock; in clk_wzrd_register_divf()
817 div->hw.init = &init; in clk_wzrd_register_divf()
819 hw = &div->hw; in clk_wzrd_register_divf()
845 return ERR_PTR(-ENOMEM); in clk_wzrd_ver_register_divider()
858 div->base = base; in clk_wzrd_ver_register_divider()
859 div->offset = offset; in clk_wzrd_ver_register_divider()
860 div->shift = shift; in clk_wzrd_ver_register_divider()
861 div->width = width; in clk_wzrd_ver_register_divider()
862 div->flags = clk_divider_flags; in clk_wzrd_ver_register_divider()
863 div->lock = lock; in clk_wzrd_ver_register_divider()
864 div->hw.init = &init; in clk_wzrd_ver_register_divider()
866 hw = &div->hw; in clk_wzrd_ver_register_divider()
891 return ERR_PTR(-ENOMEM); in clk_wzrd_register_divider()
904 div->base = base; in clk_wzrd_register_divider()
905 div->offset = offset; in clk_wzrd_register_divider()
906 div->shift = shift; in clk_wzrd_register_divider()
907 div->width = width; in clk_wzrd_register_divider()
908 div->flags = clk_divider_flags; in clk_wzrd_register_divider()
909 div->lock = lock; in clk_wzrd_register_divider()
910 div->hw.init = &init; in clk_wzrd_register_divider()
912 hw = &div->hw; in clk_wzrd_register_divider()
927 if (clk_wzrd->suspended) in clk_wzrd_clk_notifier()
930 if (ndata->clk == clk_wzrd->clk_in1) in clk_wzrd_clk_notifier()
931 max = clk_wzrd_max_freq[clk_wzrd->speed_grade - 1]; in clk_wzrd_clk_notifier()
932 else if (ndata->clk == clk_wzrd->axi_clk) in clk_wzrd_clk_notifier()
939 if (ndata->new_rate > max) in clk_wzrd_clk_notifier()
953 clk_disable_unprepare(clk_wzrd->axi_clk); in clk_wzrd_suspend()
954 clk_wzrd->suspended = true; in clk_wzrd_suspend()
964 ret = clk_prepare_enable(clk_wzrd->axi_clk); in clk_wzrd_resume()
970 clk_wzrd->suspended = false; in clk_wzrd_resume()
996 is_versal = data->is_versal; in clk_wzrd_register_output_clocks()
1000 return -ENOMEM; in clk_wzrd_register_output_clocks()
1004 clk_wzrd->clk_data.hws[0] = clk_wzrd_ver_register_divider in clk_wzrd_register_output_clocks()
1006 __clk_get_name(clk_wzrd->clk_in1), 0, in clk_wzrd_register_output_clocks()
1007 clk_wzrd->base, WZRD_CLK_CFG_REG(is_versal, 3), in clk_wzrd_register_output_clocks()
1016 edge = !!(readl(clk_wzrd->base + WZRD_CLK_CFG_REG(is_versal, 0)) & in clk_wzrd_register_output_clocks()
1018 regl = (readl(clk_wzrd->base + WZRD_CLK_CFG_REG(is_versal, 1)) & in clk_wzrd_register_output_clocks()
1020 regh = (readl(clk_wzrd->base + WZRD_CLK_CFG_REG(is_versal, 1)) & in clk_wzrd_register_output_clocks()
1027 regl = readl(clk_wzrd->base + WZRD_CLK_CFG_REG(is_versal, 51)) & in clk_wzrd_register_output_clocks()
1030 regl = readl(clk_wzrd->base + WZRD_CLK_CFG_REG(is_versal, 48)) & in clk_wzrd_register_output_clocks()
1037 clk_wzrd->clk_data.hws[0] = clk_wzrd_register_divider in clk_wzrd_register_output_clocks()
1039 __clk_get_name(clk_wzrd->clk_in1), 0, in clk_wzrd_register_output_clocks()
1040 clk_wzrd->base, WZRD_CLK_CFG_REG(is_versal, 3), in clk_wzrd_register_output_clocks()
1048 reg = readl(clk_wzrd->base + WZRD_CLK_CFG_REG(is_versal, 0)); in clk_wzrd_register_output_clocks()
1059 return -ENOMEM; in clk_wzrd_register_output_clocks()
1060 clk_wzrd->clks_internal[wzrd_clk_mul] = devm_clk_hw_register_fixed_factor in clk_wzrd_register_output_clocks()
1062 __clk_get_name(clk_wzrd->clk_in1), in clk_wzrd_register_output_clocks()
1064 if (IS_ERR(clk_wzrd->clks_internal[wzrd_clk_mul])) { in clk_wzrd_register_output_clocks()
1065 dev_err(dev, "unable to register fixed-factor clock\n"); in clk_wzrd_register_output_clocks()
1066 return PTR_ERR(clk_wzrd->clks_internal[wzrd_clk_mul]); in clk_wzrd_register_output_clocks()
1071 return -ENOMEM; in clk_wzrd_register_output_clocks()
1074 edged = !!(readl(clk_wzrd->base + WZRD_CLK_CFG_REG(is_versal, 20)) & in clk_wzrd_register_output_clocks()
1076 regld = (readl(clk_wzrd->base + WZRD_CLK_CFG_REG(is_versal, 21)) & in clk_wzrd_register_output_clocks()
1078 reghd = (readl(clk_wzrd->base + WZRD_CLK_CFG_REG(is_versal, 21)) & in clk_wzrd_register_output_clocks()
1084 clk_mul_name = clk_hw_get_name(clk_wzrd->clks_internal[wzrd_clk_mul]); in clk_wzrd_register_output_clocks()
1085 clk_wzrd->clks_internal[wzrd_clk_mul_div] = in clk_wzrd_register_output_clocks()
1088 ctrl_reg = clk_wzrd->base + WZRD_CLK_CFG_REG(is_versal, 0); in clk_wzrd_register_output_clocks()
1089 clk_wzrd->clks_internal[wzrd_clk_mul_div] = devm_clk_hw_register_divider in clk_wzrd_register_output_clocks()
1091 clk_hw_get_name(clk_wzrd->clks_internal[wzrd_clk_mul]), in clk_wzrd_register_output_clocks()
1095 if (IS_ERR(clk_wzrd->clks_internal[wzrd_clk_mul_div])) { in clk_wzrd_register_output_clocks()
1097 return PTR_ERR(clk_wzrd->clks_internal[wzrd_clk_mul_div]); in clk_wzrd_register_output_clocks()
1101 for (i = nr_outputs - 1; i >= 0 ; i--) { in clk_wzrd_register_output_clocks()
1104 return -ENOMEM; in clk_wzrd_register_output_clocks()
1107 clk_wzrd->clk_data.hws[i] = clk_wzrd_ver_register_divider in clk_wzrd_register_output_clocks()
1110 clk_wzrd->base, in clk_wzrd_register_output_clocks()
1119 clk_wzrd->clk_data.hws[i] = clk_wzrd_register_divf in clk_wzrd_register_output_clocks()
1120 (dev, clkout_name, clk_name, flags, clk_wzrd->base, in clk_wzrd_register_output_clocks()
1127 clk_wzrd->clk_data.hws[i] = clk_wzrd_register_divider in clk_wzrd_register_output_clocks()
1128 (dev, clkout_name, clk_name, 0, clk_wzrd->base, in clk_wzrd_register_output_clocks()
1135 if (IS_ERR(clk_wzrd->clk_data.hws[i])) { in clk_wzrd_register_output_clocks()
1137 return PTR_ERR(clk_wzrd->clk_data.hws[i]); in clk_wzrd_register_output_clocks()
1146 struct device_node *np = pdev->dev.of_node; in clk_wzrd_probe()
1152 ret = of_property_read_u32(np, "xlnx,nr-outputs", &nr_outputs); in clk_wzrd_probe()
1154 return -EINVAL; in clk_wzrd_probe()
1156 clk_wzrd = devm_kzalloc(&pdev->dev, struct_size(clk_wzrd, clk_data.hws, nr_outputs), in clk_wzrd_probe()
1159 return -ENOMEM; in clk_wzrd_probe()
1162 clk_wzrd->base = devm_platform_ioremap_resource(pdev, 0); in clk_wzrd_probe()
1163 if (IS_ERR(clk_wzrd->base)) in clk_wzrd_probe()
1164 return PTR_ERR(clk_wzrd->base); in clk_wzrd_probe()
1166 clk_wzrd->axi_clk = devm_clk_get_enabled(&pdev->dev, "s_axi_aclk"); in clk_wzrd_probe()
1167 if (IS_ERR(clk_wzrd->axi_clk)) in clk_wzrd_probe()
1168 return dev_err_probe(&pdev->dev, PTR_ERR(clk_wzrd->axi_clk), in clk_wzrd_probe()
1170 rate = clk_get_rate(clk_wzrd->axi_clk); in clk_wzrd_probe()
1172 dev_err(&pdev->dev, "s_axi_aclk frequency (%lu) too high\n", rate); in clk_wzrd_probe()
1173 return -EINVAL; in clk_wzrd_probe()
1176 if (!of_property_present(np, "xlnx,static-config")) { in clk_wzrd_probe()
1177 ret = of_property_read_u32(np, "xlnx,speed-grade", &clk_wzrd->speed_grade); in clk_wzrd_probe()
1179 if (clk_wzrd->speed_grade < 1 || clk_wzrd->speed_grade > 3) { in clk_wzrd_probe()
1180 dev_warn(&pdev->dev, "invalid speed grade '%d'\n", in clk_wzrd_probe()
1181 clk_wzrd->speed_grade); in clk_wzrd_probe()
1182 clk_wzrd->speed_grade = 0; in clk_wzrd_probe()
1186 clk_wzrd->clk_in1 = devm_clk_get(&pdev->dev, "clk_in1"); in clk_wzrd_probe()
1187 if (IS_ERR(clk_wzrd->clk_in1)) in clk_wzrd_probe()
1188 return dev_err_probe(&pdev->dev, PTR_ERR(clk_wzrd->clk_in1), in clk_wzrd_probe()
1191 ret = clk_wzrd_register_output_clocks(&pdev->dev, nr_outputs); in clk_wzrd_probe()
1195 clk_wzrd->clk_data.num = nr_outputs; in clk_wzrd_probe()
1196 ret = devm_of_clk_add_hw_provider(&pdev->dev, of_clk_hw_onecell_get, in clk_wzrd_probe()
1197 &clk_wzrd->clk_data); in clk_wzrd_probe()
1199 dev_err(&pdev->dev, "unable to register clock provider\n"); in clk_wzrd_probe()
1203 if (clk_wzrd->speed_grade) { in clk_wzrd_probe()
1204 clk_wzrd->nb.notifier_call = clk_wzrd_clk_notifier; in clk_wzrd_probe()
1206 ret = devm_clk_notifier_register(&pdev->dev, clk_wzrd->clk_in1, in clk_wzrd_probe()
1207 &clk_wzrd->nb); in clk_wzrd_probe()
1209 dev_warn(&pdev->dev, in clk_wzrd_probe()
1212 ret = devm_clk_notifier_register(&pdev->dev, clk_wzrd->axi_clk, in clk_wzrd_probe()
1213 &clk_wzrd->nb); in clk_wzrd_probe()
1215 dev_warn(&pdev->dev, in clk_wzrd_probe()
1224 { .compatible = "xlnx,versal-clk-wizard", .data = &versal_data },
1225 { .compatible = "xlnx,clocking-wizard" },
1226 { .compatible = "xlnx,clocking-wizard-v5.2" },
1227 { .compatible = "xlnx,clocking-wizard-v6.0" },
1234 .name = "clk-wizard",