Lines Matching +full:speed +full:- +full:grade
1 // SPDX-License-Identifier: GPL-2.0
5 * Copyright (C) 2013 - 2021 Xilinx
14 #include <linux/clk-provider.h>
85 /* Divider limits, from UG572 Table 3-4 for Ultrascale+ */
109 #define div_mask(width) ((1 << (width)) - 1)
122 * struct clk_wzrd - Clock wizard private data structure
131 * @speed_grade: Speed grade of the device
147 * struct clk_wzrd_divider - clock divider specific to clk_wzrd
149 * @hw: handle between common and hardware-specific interfaces
181 /* maximum frequencies for input/output clocks per speed grade */
195 void __iomem *div_addr = divider->base + divider->offset; in clk_wzrd_recalc_rate_ver()
220 void __iomem *div_addr = divider->base + divider->offset; in clk_wzrd_recalc_rate()
223 val = readl(div_addr) >> divider->shift; in clk_wzrd_recalc_rate()
224 val &= div_mask(divider->width); in clk_wzrd_recalc_rate()
226 return divider_recalc_rate(hw, parent_rate, val, divider->table, in clk_wzrd_recalc_rate()
227 divider->flags, divider->width); in clk_wzrd_recalc_rate()
234 void __iomem *div_addr = divider->base + divider->offset; in clk_wzrd_ver_dynamic_reconfig()
239 spin_lock_irqsave(divider->lock, flags); in clk_wzrd_ver_dynamic_reconfig()
259 err = readl_poll_timeout_atomic(divider->base + WZRD_DR_STATUS_REG_OFFSET, in clk_wzrd_ver_dynamic_reconfig()
267 divider->base + WZRD_DR_INIT_VERSAL_OFFSET); in clk_wzrd_ver_dynamic_reconfig()
270 err = readl_poll_timeout_atomic(divider->base + WZRD_DR_STATUS_REG_OFFSET, in clk_wzrd_ver_dynamic_reconfig()
274 spin_unlock_irqrestore(divider->lock, flags); in clk_wzrd_ver_dynamic_reconfig()
282 void __iomem *div_addr = divider->base + divider->offset; in clk_wzrd_dynamic_reconfig()
287 spin_lock_irqsave(divider->lock, flags); in clk_wzrd_dynamic_reconfig()
299 err = readl_poll_timeout_atomic(divider->base + WZRD_DR_STATUS_REG_OFFSET, in clk_wzrd_dynamic_reconfig()
307 divider->base + WZRD_DR_INIT_REG_OFFSET); in clk_wzrd_dynamic_reconfig()
309 divider->base + WZRD_DR_INIT_REG_OFFSET); in clk_wzrd_dynamic_reconfig()
312 err = readl_poll_timeout_atomic(divider->base + WZRD_DR_STATUS_REG_OFFSET, in clk_wzrd_dynamic_reconfig()
316 spin_unlock_irqrestore(divider->lock, flags); in clk_wzrd_dynamic_reconfig()
357 diff = abs(freq - rate); in clk_wzrd_get_divisors_ver()
360 divider->m = m; in clk_wzrd_get_divisors_ver()
361 divider->d = d; in clk_wzrd_get_divisors_ver()
362 divider->o = o; in clk_wzrd_get_divisors_ver()
369 return -EBUSY; in clk_wzrd_get_divisors_ver()
395 diff = abs(freq - rate); in clk_wzrd_get_divisors()
398 divider->m = m; in clk_wzrd_get_divisors()
399 divider->d = d; in clk_wzrd_get_divisors()
400 divider->o = o; in clk_wzrd_get_divisors()
407 return -EBUSY; in clk_wzrd_get_divisors()
416 err = readl_poll_timeout_atomic(divider->base + WZRD_DR_STATUS_REG_OFFSET, value, in clk_wzrd_reconfig()
420 return -ETIMEDOUT; in clk_wzrd_reconfig()
425 return readl_poll_timeout_atomic(divider->base + WZRD_DR_STATUS_REG_OFFSET, value, in clk_wzrd_reconfig()
442 writel(0, divider->base + WZRD_CLK_CFG_REG(1, WZRD_CLKFBOUT_4)); in clk_wzrd_dynamic_ver_all_nolock()
444 m = divider->m; in clk_wzrd_dynamic_ver_all_nolock()
447 regval1 = readl(divider->base + WZRD_CLK_CFG_REG(1, in clk_wzrd_dynamic_ver_all_nolock()
455 writel(regval1, divider->base + WZRD_CLK_CFG_REG(1, in clk_wzrd_dynamic_ver_all_nolock()
458 writel(regval1, divider->base + WZRD_CLK_CFG_REG(1, in clk_wzrd_dynamic_ver_all_nolock()
461 value2 = divider->d; 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 writel(regval1, divider->base + WZRD_CLK_CFG_REG(1, WZRD_DIVCLK)); in clk_wzrd_dynamic_ver_all_nolock()
470 value = divider->o; in clk_wzrd_dynamic_ver_all_nolock()
472 regval1 = readl(divider->base + WZRD_CLK_CFG_REG(1, in clk_wzrd_dynamic_ver_all_nolock()
486 writel(regval1, divider->base + WZRD_CLK_CFG_REG(1, in clk_wzrd_dynamic_ver_all_nolock()
489 writel(regval, divider->base + WZRD_CLK_CFG_REG(1, in clk_wzrd_dynamic_ver_all_nolock()
491 div_addr = divider->base + WZRD_DR_INIT_VERSAL_OFFSET; in clk_wzrd_dynamic_ver_all_nolock()
509 vco_freq = DIV_ROUND_CLOSEST(parent_rate * divider->m, divider->d); in clk_wzrd_dynamic_all_nolock()
515 f = (pre - (clockout0_div * WZRD_FRAC_POINTS)); in clk_wzrd_dynamic_all_nolock()
521 writel(reg, divider->base + WZRD_CLK_CFG_REG(0, 2)); in clk_wzrd_dynamic_all_nolock()
523 reg = FIELD_PREP(WZRD_CLKFBOUT_MULT_MASK, divider->m) | in clk_wzrd_dynamic_all_nolock()
524 FIELD_PREP(WZRD_DIVCLK_DIVIDE_MASK, divider->d); in clk_wzrd_dynamic_all_nolock()
525 writel(reg, divider->base + WZRD_CLK_CFG_REG(0, 0)); in clk_wzrd_dynamic_all_nolock()
526 writel(divider->o, divider->base + WZRD_CLK_CFG_REG(0, 2)); in clk_wzrd_dynamic_all_nolock()
527 writel(0, divider->base + WZRD_CLK_CFG_REG(0, 3)); in clk_wzrd_dynamic_all_nolock()
528 div_addr = divider->base + WZRD_DR_INIT_REG_OFFSET; in clk_wzrd_dynamic_all_nolock()
539 spin_lock_irqsave(divider->lock, flags); in clk_wzrd_dynamic_all()
543 spin_unlock_irqrestore(divider->lock, flags); in clk_wzrd_dynamic_all()
555 spin_lock_irqsave(divider->lock, flags); in clk_wzrd_dynamic_all_ver()
559 spin_unlock_irqrestore(divider->lock, flags); in clk_wzrd_dynamic_all_ver()
570 reg = readl(divider->base + WZRD_CLK_CFG_REG(0, 0)); in clk_wzrd_recalc_rate_all()
573 reg = readl(divider->base + WZRD_CLK_CFG_REG(0, 2)); in clk_wzrd_recalc_rate_all()
578 return divider_recalc_rate(hw, parent_rate * m, div, divider->table, in clk_wzrd_recalc_rate_all()
579 divider->flags, divider->width); in clk_wzrd_recalc_rate_all()
589 edge = !!(readl(divider->base + WZRD_CLK_CFG_REG(1, WZRD_CLKFBOUT_1)) & in clk_wzrd_recalc_rate_all_ver()
592 reg = readl(divider->base + WZRD_CLK_CFG_REG(1, WZRD_CLKFBOUT_2)); in clk_wzrd_recalc_rate_all_ver()
600 regl = readl(divider->base + WZRD_CLK_CFG_REG(1, WZRD_CLKFBOUT_4)) & in clk_wzrd_recalc_rate_all_ver()
603 regl = readl(divider->base + WZRD_CLK_CFG_REG(1, WZRD_CLKFBOUT_3)) in clk_wzrd_recalc_rate_all_ver()
612 reg = readl(divider->base + WZRD_CLK_CFG_REG(1, WZRD_CLKOUT0_1)); in clk_wzrd_recalc_rate_all_ver()
617 reg = readl(divider->base + WZRD_CLK_CFG_REG(1, WZRD_CLKOUT0_2)); in clk_wzrd_recalc_rate_all_ver()
632 edged = !!(readl(divider->base + WZRD_CLK_CFG_REG(1, WZRD_DESKEW_2)) & in clk_wzrd_recalc_rate_all_ver()
634 reg = readl(divider->base + WZRD_CLK_CFG_REG(1, WZRD_DIVCLK)); in clk_wzrd_recalc_rate_all_ver()
644 return divider_recalc_rate(hw, parent_rate, div, divider->table, in clk_wzrd_recalc_rate_all_ver()
645 divider->flags, divider->width); in clk_wzrd_recalc_rate_all_ver()
660 m = divider->m; in clk_wzrd_round_rate_all()
661 d = divider->d; in clk_wzrd_round_rate_all()
662 o = divider->o; in clk_wzrd_round_rate_all()
665 int_freq = divider_recalc_rate(hw, *prate * m, div, divider->table, in clk_wzrd_round_rate_all()
666 divider->flags, divider->width); in clk_wzrd_round_rate_all()
705 void __iomem *div_addr = divider->base + divider->offset; in clk_wzrd_recalc_ratef()
708 div = val & div_mask(divider->width); in clk_wzrd_recalc_ratef()
721 void __iomem *div_addr = divider->base + divider->offset; in clk_wzrd_dynamic_reconfig_f()
727 f = (u32)(pre - (clockout0_div * 1000)); in clk_wzrd_dynamic_reconfig_f()
738 err = readl_poll_timeout(divider->base + WZRD_DR_STATUS_REG_OFFSET, value, in clk_wzrd_dynamic_reconfig_f()
746 divider->base + WZRD_DR_INIT_REG_OFFSET); in clk_wzrd_dynamic_reconfig_f()
748 divider->base + WZRD_DR_INIT_REG_OFFSET); in clk_wzrd_dynamic_reconfig_f()
751 return readl_poll_timeout(divider->base + WZRD_DR_STATUS_REG_OFFSET, value, in clk_wzrd_dynamic_reconfig_f()
785 return ERR_PTR(-ENOMEM); in clk_wzrd_register_divf()
795 div->base = base; in clk_wzrd_register_divf()
796 div->offset = offset; in clk_wzrd_register_divf()
797 div->shift = shift; in clk_wzrd_register_divf()
798 div->width = width; in clk_wzrd_register_divf()
799 div->flags = clk_divider_flags; in clk_wzrd_register_divf()
800 div->lock = lock; in clk_wzrd_register_divf()
801 div->hw.init = &init; in clk_wzrd_register_divf()
803 hw = &div->hw; in clk_wzrd_register_divf()
808 return hw->clk; in clk_wzrd_register_divf()
829 return ERR_PTR(-ENOMEM); in clk_wzrd_ver_register_divider()
842 div->base = base; in clk_wzrd_ver_register_divider()
843 div->offset = offset; in clk_wzrd_ver_register_divider()
844 div->shift = shift; in clk_wzrd_ver_register_divider()
845 div->width = width; in clk_wzrd_ver_register_divider()
846 div->flags = clk_divider_flags; in clk_wzrd_ver_register_divider()
847 div->lock = lock; in clk_wzrd_ver_register_divider()
848 div->hw.init = &init; in clk_wzrd_ver_register_divider()
850 hw = &div->hw; in clk_wzrd_ver_register_divider()
855 return hw->clk; in clk_wzrd_ver_register_divider()
875 return ERR_PTR(-ENOMEM); in clk_wzrd_register_divider()
888 div->base = base; in clk_wzrd_register_divider()
889 div->offset = offset; in clk_wzrd_register_divider()
890 div->shift = shift; in clk_wzrd_register_divider()
891 div->width = width; in clk_wzrd_register_divider()
892 div->flags = clk_divider_flags; in clk_wzrd_register_divider()
893 div->lock = lock; in clk_wzrd_register_divider()
894 div->hw.init = &init; in clk_wzrd_register_divider()
896 hw = &div->hw; in clk_wzrd_register_divider()
901 return hw->clk; in clk_wzrd_register_divider()
911 if (clk_wzrd->suspended) in clk_wzrd_clk_notifier()
914 if (ndata->clk == clk_wzrd->clk_in1) in clk_wzrd_clk_notifier()
915 max = clk_wzrd_max_freq[clk_wzrd->speed_grade - 1]; in clk_wzrd_clk_notifier()
916 else if (ndata->clk == clk_wzrd->axi_clk) in clk_wzrd_clk_notifier()
923 if (ndata->new_rate > max) in clk_wzrd_clk_notifier()
937 clk_disable_unprepare(clk_wzrd->axi_clk); in clk_wzrd_suspend()
938 clk_wzrd->suspended = true; in clk_wzrd_suspend()
948 ret = clk_prepare_enable(clk_wzrd->axi_clk); in clk_wzrd_resume()
954 clk_wzrd->suspended = false; in clk_wzrd_resume()
970 struct device_node *np = pdev->dev.of_node; in clk_wzrd_probe()
981 clk_wzrd = devm_kzalloc(&pdev->dev, sizeof(*clk_wzrd), GFP_KERNEL); in clk_wzrd_probe()
983 return -ENOMEM; in clk_wzrd_probe()
986 clk_wzrd->base = devm_platform_ioremap_resource(pdev, 0); in clk_wzrd_probe()
987 if (IS_ERR(clk_wzrd->base)) in clk_wzrd_probe()
988 return PTR_ERR(clk_wzrd->base); in clk_wzrd_probe()
990 ret = of_property_read_u32(np, "xlnx,speed-grade", &clk_wzrd->speed_grade); in clk_wzrd_probe()
992 if (clk_wzrd->speed_grade < 1 || clk_wzrd->speed_grade > 3) { in clk_wzrd_probe()
993 dev_warn(&pdev->dev, "invalid speed grade '%d'\n", in clk_wzrd_probe()
994 clk_wzrd->speed_grade); in clk_wzrd_probe()
995 clk_wzrd->speed_grade = 0; in clk_wzrd_probe()
999 clk_wzrd->clk_in1 = devm_clk_get(&pdev->dev, "clk_in1"); in clk_wzrd_probe()
1000 if (IS_ERR(clk_wzrd->clk_in1)) in clk_wzrd_probe()
1001 return dev_err_probe(&pdev->dev, PTR_ERR(clk_wzrd->clk_in1), in clk_wzrd_probe()
1004 clk_wzrd->axi_clk = devm_clk_get(&pdev->dev, "s_axi_aclk"); in clk_wzrd_probe()
1005 if (IS_ERR(clk_wzrd->axi_clk)) in clk_wzrd_probe()
1006 return dev_err_probe(&pdev->dev, PTR_ERR(clk_wzrd->axi_clk), in clk_wzrd_probe()
1008 ret = clk_prepare_enable(clk_wzrd->axi_clk); in clk_wzrd_probe()
1010 dev_err(&pdev->dev, "enabling s_axi_aclk failed\n"); in clk_wzrd_probe()
1013 rate = clk_get_rate(clk_wzrd->axi_clk); in clk_wzrd_probe()
1015 dev_err(&pdev->dev, "s_axi_aclk frequency (%lu) too high\n", in clk_wzrd_probe()
1017 ret = -EINVAL; in clk_wzrd_probe()
1021 data = device_get_match_data(&pdev->dev); in clk_wzrd_probe()
1023 is_versal = data->is_versal; in clk_wzrd_probe()
1025 ret = of_property_read_u32(np, "xlnx,nr-outputs", &nr_outputs); in clk_wzrd_probe()
1027 ret = -EINVAL; in clk_wzrd_probe()
1031 clkout_name = devm_kasprintf(&pdev->dev, GFP_KERNEL, "%s_out0", dev_name(&pdev->dev)); in clk_wzrd_probe()
1033 ret = -ENOMEM; in clk_wzrd_probe()
1039 clk_wzrd->clkout[0] = clk_wzrd_ver_register_divider in clk_wzrd_probe()
1040 (&pdev->dev, clkout_name, in clk_wzrd_probe()
1041 __clk_get_name(clk_wzrd->clk_in1), 0, in clk_wzrd_probe()
1042 clk_wzrd->base, WZRD_CLK_CFG_REG(is_versal, 3), in clk_wzrd_probe()
1051 edge = !!(readl(clk_wzrd->base + WZRD_CLK_CFG_REG(is_versal, 0)) & in clk_wzrd_probe()
1053 regl = (readl(clk_wzrd->base + WZRD_CLK_CFG_REG(is_versal, 1)) & in clk_wzrd_probe()
1055 regh = (readl(clk_wzrd->base + WZRD_CLK_CFG_REG(is_versal, 1)) & in clk_wzrd_probe()
1062 regl = readl(clk_wzrd->base + WZRD_CLK_CFG_REG(is_versal, 51)) & in clk_wzrd_probe()
1065 regl = readl(clk_wzrd->base + WZRD_CLK_CFG_REG(is_versal, 48)) & in clk_wzrd_probe()
1072 clk_wzrd->clkout[0] = clk_wzrd_register_divider in clk_wzrd_probe()
1073 (&pdev->dev, clkout_name, in clk_wzrd_probe()
1074 __clk_get_name(clk_wzrd->clk_in1), 0, in clk_wzrd_probe()
1075 clk_wzrd->base, WZRD_CLK_CFG_REG(is_versal, 3), in clk_wzrd_probe()
1083 reg = readl(clk_wzrd->base + WZRD_CLK_CFG_REG(is_versal, 0)); in clk_wzrd_probe()
1092 clk_name = devm_kasprintf(&pdev->dev, GFP_KERNEL, "%s_mul", dev_name(&pdev->dev)); in clk_wzrd_probe()
1094 ret = -ENOMEM; in clk_wzrd_probe()
1097 clk_wzrd->clks_internal[wzrd_clk_mul] = clk_register_fixed_factor in clk_wzrd_probe()
1098 (&pdev->dev, clk_name, in clk_wzrd_probe()
1099 __clk_get_name(clk_wzrd->clk_in1), in clk_wzrd_probe()
1101 if (IS_ERR(clk_wzrd->clks_internal[wzrd_clk_mul])) { in clk_wzrd_probe()
1102 dev_err(&pdev->dev, "unable to register fixed-factor clock\n"); in clk_wzrd_probe()
1103 ret = PTR_ERR(clk_wzrd->clks_internal[wzrd_clk_mul]); in clk_wzrd_probe()
1107 clk_name = devm_kasprintf(&pdev->dev, GFP_KERNEL, "%s_mul_div", dev_name(&pdev->dev)); in clk_wzrd_probe()
1109 ret = -ENOMEM; in clk_wzrd_probe()
1114 edged = !!(readl(clk_wzrd->base + WZRD_CLK_CFG_REG(is_versal, 20)) & in clk_wzrd_probe()
1116 regld = (readl(clk_wzrd->base + WZRD_CLK_CFG_REG(is_versal, 21)) & in clk_wzrd_probe()
1118 reghd = (readl(clk_wzrd->base + WZRD_CLK_CFG_REG(is_versal, 21)) & in clk_wzrd_probe()
1124 clk_mul_name = __clk_get_name(clk_wzrd->clks_internal[wzrd_clk_mul]); in clk_wzrd_probe()
1125 clk_wzrd->clks_internal[wzrd_clk_mul_div] = in clk_wzrd_probe()
1126 clk_register_fixed_factor(&pdev->dev, clk_name, in clk_wzrd_probe()
1129 ctrl_reg = clk_wzrd->base + WZRD_CLK_CFG_REG(is_versal, 0); in clk_wzrd_probe()
1130 clk_wzrd->clks_internal[wzrd_clk_mul_div] = clk_register_divider in clk_wzrd_probe()
1131 (&pdev->dev, clk_name, in clk_wzrd_probe()
1132 __clk_get_name(clk_wzrd->clks_internal[wzrd_clk_mul]), in clk_wzrd_probe()
1136 if (IS_ERR(clk_wzrd->clks_internal[wzrd_clk_mul_div])) { in clk_wzrd_probe()
1137 dev_err(&pdev->dev, "unable to register divider clock\n"); in clk_wzrd_probe()
1138 ret = PTR_ERR(clk_wzrd->clks_internal[wzrd_clk_mul_div]); in clk_wzrd_probe()
1143 for (i = nr_outputs - 1; i >= 0 ; i--) { in clk_wzrd_probe()
1144 clkout_name = devm_kasprintf(&pdev->dev, GFP_KERNEL, in clk_wzrd_probe()
1145 "%s_out%d", dev_name(&pdev->dev), i); in clk_wzrd_probe()
1147 ret = -ENOMEM; in clk_wzrd_probe()
1152 clk_wzrd->clkout[i] = clk_wzrd_ver_register_divider in clk_wzrd_probe()
1153 (&pdev->dev, in clk_wzrd_probe()
1155 clk_wzrd->base, in clk_wzrd_probe()
1164 clk_wzrd->clkout[i] = clk_wzrd_register_divf in clk_wzrd_probe()
1165 (&pdev->dev, clkout_name, clk_name, flags, clk_wzrd->base, in clk_wzrd_probe()
1172 clk_wzrd->clkout[i] = clk_wzrd_register_divider in clk_wzrd_probe()
1173 (&pdev->dev, clkout_name, clk_name, 0, clk_wzrd->base, in clk_wzrd_probe()
1180 if (IS_ERR(clk_wzrd->clkout[i])) { in clk_wzrd_probe()
1184 clk_unregister(clk_wzrd->clkout[j]); in clk_wzrd_probe()
1185 dev_err(&pdev->dev, in clk_wzrd_probe()
1187 ret = PTR_ERR(clk_wzrd->clkout[i]); in clk_wzrd_probe()
1193 clk_wzrd->clk_data.clks = clk_wzrd->clkout; in clk_wzrd_probe()
1194 clk_wzrd->clk_data.clk_num = ARRAY_SIZE(clk_wzrd->clkout); in clk_wzrd_probe()
1195 of_clk_add_provider(np, of_clk_src_onecell_get, &clk_wzrd->clk_data); in clk_wzrd_probe()
1197 if (clk_wzrd->speed_grade) { in clk_wzrd_probe()
1198 clk_wzrd->nb.notifier_call = clk_wzrd_clk_notifier; in clk_wzrd_probe()
1200 ret = clk_notifier_register(clk_wzrd->clk_in1, in clk_wzrd_probe()
1201 &clk_wzrd->nb); in clk_wzrd_probe()
1203 dev_warn(&pdev->dev, in clk_wzrd_probe()
1206 ret = clk_notifier_register(clk_wzrd->axi_clk, &clk_wzrd->nb); in clk_wzrd_probe()
1208 dev_warn(&pdev->dev, in clk_wzrd_probe()
1215 clk_unregister(clk_wzrd->clks_internal[1]); in clk_wzrd_probe()
1217 clk_unregister(clk_wzrd->clks_internal[0]); in clk_wzrd_probe()
1219 clk_disable_unprepare(clk_wzrd->axi_clk); in clk_wzrd_probe()
1229 of_clk_del_provider(pdev->dev.of_node); in clk_wzrd_remove()
1232 clk_unregister(clk_wzrd->clkout[i]); in clk_wzrd_remove()
1234 clk_unregister(clk_wzrd->clks_internal[i]); in clk_wzrd_remove()
1236 if (clk_wzrd->speed_grade) { in clk_wzrd_remove()
1237 clk_notifier_unregister(clk_wzrd->axi_clk, &clk_wzrd->nb); in clk_wzrd_remove()
1238 clk_notifier_unregister(clk_wzrd->clk_in1, &clk_wzrd->nb); in clk_wzrd_remove()
1241 clk_disable_unprepare(clk_wzrd->axi_clk); in clk_wzrd_remove()
1245 { .compatible = "xlnx,versal-clk-wizard", .data = &versal_data },
1246 { .compatible = "xlnx,clocking-wizard" },
1247 { .compatible = "xlnx,clocking-wizard-v5.2" },
1248 { .compatible = "xlnx,clocking-wizard-v6.0" },
1255 .name = "clk-wizard",