Lines Matching +full:clocking +full:- +full:wizard
1 // SPDX-License-Identifier: GPL-2.0
3 * Xilinx 'Clocking Wizard' driver
5 * Copyright (C) 2013 - 2021 Xilinx
14 #include <linux/clk-provider.h>
56 /* Divider limits, from UG572 Table 3-4 for Ultrascale+ */
72 #define div_mask(width) ((1 << (width)) - 1)
85 * struct clk_wzrd - Clock wizard private data structure
110 * struct clk_wzrd_divider - clock divider specific to clk_wzrd
112 * @hw: handle between common and hardware-specific interfaces
154 void __iomem *div_addr = divider->base + divider->offset; in clk_wzrd_recalc_rate()
157 val = readl(div_addr) >> divider->shift; in clk_wzrd_recalc_rate()
158 val &= div_mask(divider->width); in clk_wzrd_recalc_rate()
160 return divider_recalc_rate(hw, parent_rate, val, divider->table, in clk_wzrd_recalc_rate()
161 divider->flags, divider->width); in clk_wzrd_recalc_rate()
171 void __iomem *div_addr = divider->base + divider->offset; in clk_wzrd_dynamic_reconfig()
173 if (divider->lock) in clk_wzrd_dynamic_reconfig()
174 spin_lock_irqsave(divider->lock, flags); in clk_wzrd_dynamic_reconfig()
176 __acquire(divider->lock); in clk_wzrd_dynamic_reconfig()
188 err = readl_poll_timeout(divider->base + WZRD_DR_STATUS_REG_OFFSET, in clk_wzrd_dynamic_reconfig()
196 divider->base + WZRD_DR_INIT_REG_OFFSET); in clk_wzrd_dynamic_reconfig()
198 divider->base + WZRD_DR_INIT_REG_OFFSET); in clk_wzrd_dynamic_reconfig()
201 err = readl_poll_timeout(divider->base + WZRD_DR_STATUS_REG_OFFSET, in clk_wzrd_dynamic_reconfig()
205 if (divider->lock) in clk_wzrd_dynamic_reconfig()
206 spin_unlock_irqrestore(divider->lock, flags); in clk_wzrd_dynamic_reconfig()
208 __release(divider->lock); in clk_wzrd_dynamic_reconfig()
239 diff = abs(freq - rate); in clk_wzrd_get_divisors()
242 divider->m = m; in clk_wzrd_get_divisors()
243 divider->d = d; in clk_wzrd_get_divisors()
244 divider->o = o; in clk_wzrd_get_divisors()
251 return -EBUSY; in clk_wzrd_get_divisors()
266 vco_freq = DIV_ROUND_CLOSEST(parent_rate * divider->m, divider->d); in clk_wzrd_dynamic_all_nolock()
272 f = (pre - (clockout0_div * WZRD_FRAC_POINTS)); in clk_wzrd_dynamic_all_nolock()
278 writel(reg, divider->base + WZRD_CLK_CFG_REG(2)); in clk_wzrd_dynamic_all_nolock()
280 reg = FIELD_PREP(WZRD_CLKFBOUT_MULT_MASK, divider->m) | in clk_wzrd_dynamic_all_nolock()
281 FIELD_PREP(WZRD_DIVCLK_DIVIDE_MASK, divider->d); in clk_wzrd_dynamic_all_nolock()
282 writel(reg, divider->base + WZRD_CLK_CFG_REG(0)); in clk_wzrd_dynamic_all_nolock()
283 writel(divider->o, divider->base + WZRD_CLK_CFG_REG(2)); in clk_wzrd_dynamic_all_nolock()
284 writel(0, divider->base + WZRD_CLK_CFG_REG(3)); in clk_wzrd_dynamic_all_nolock()
286 err = readl_poll_timeout(divider->base + WZRD_DR_STATUS_REG_OFFSET, value, in clk_wzrd_dynamic_all_nolock()
290 return -ETIMEDOUT; in clk_wzrd_dynamic_all_nolock()
294 divider->base + WZRD_DR_INIT_REG_OFFSET); in clk_wzrd_dynamic_all_nolock()
297 return readl_poll_timeout(divider->base + WZRD_DR_STATUS_REG_OFFSET, value, in clk_wzrd_dynamic_all_nolock()
309 spin_lock_irqsave(divider->lock, flags); in clk_wzrd_dynamic_all()
313 spin_unlock_irqrestore(divider->lock, flags); in clk_wzrd_dynamic_all()
324 reg = readl(divider->base + WZRD_CLK_CFG_REG(0)); in clk_wzrd_recalc_rate_all()
327 reg = readl(divider->base + WZRD_CLK_CFG_REG(2)); in clk_wzrd_recalc_rate_all()
332 return divider_recalc_rate(hw, parent_rate * m, div, divider->table, in clk_wzrd_recalc_rate_all()
333 divider->flags, divider->width); in clk_wzrd_recalc_rate_all()
348 m = divider->m; in clk_wzrd_round_rate_all()
349 d = divider->d; in clk_wzrd_round_rate_all()
350 o = divider->o; in clk_wzrd_round_rate_all()
353 int_freq = divider_recalc_rate(hw, *prate * m, div, divider->table, in clk_wzrd_round_rate_all()
354 divider->flags, divider->width); in clk_wzrd_round_rate_all()
381 void __iomem *div_addr = divider->base + divider->offset; in clk_wzrd_recalc_ratef()
384 div = val & div_mask(divider->width); in clk_wzrd_recalc_ratef()
397 void __iomem *div_addr = divider->base + divider->offset; in clk_wzrd_dynamic_reconfig_f()
403 f = (u32)(pre - (clockout0_div * 1000)); in clk_wzrd_dynamic_reconfig_f()
414 err = readl_poll_timeout(divider->base + WZRD_DR_STATUS_REG_OFFSET, value, in clk_wzrd_dynamic_reconfig_f()
422 divider->base + WZRD_DR_INIT_REG_OFFSET); in clk_wzrd_dynamic_reconfig_f()
424 divider->base + WZRD_DR_INIT_REG_OFFSET); in clk_wzrd_dynamic_reconfig_f()
427 return readl_poll_timeout(divider->base + WZRD_DR_STATUS_REG_OFFSET, value, in clk_wzrd_dynamic_reconfig_f()
461 return ERR_PTR(-ENOMEM); in clk_wzrd_register_divf()
471 div->base = base; in clk_wzrd_register_divf()
472 div->offset = offset; in clk_wzrd_register_divf()
473 div->shift = shift; in clk_wzrd_register_divf()
474 div->width = width; in clk_wzrd_register_divf()
475 div->flags = clk_divider_flags; in clk_wzrd_register_divf()
476 div->lock = lock; in clk_wzrd_register_divf()
477 div->hw.init = &init; in clk_wzrd_register_divf()
479 hw = &div->hw; in clk_wzrd_register_divf()
484 return hw->clk; in clk_wzrd_register_divf()
504 return ERR_PTR(-ENOMEM); in clk_wzrd_register_divider()
517 div->base = base; in clk_wzrd_register_divider()
518 div->offset = offset; in clk_wzrd_register_divider()
519 div->shift = shift; in clk_wzrd_register_divider()
520 div->width = width; in clk_wzrd_register_divider()
521 div->flags = clk_divider_flags; in clk_wzrd_register_divider()
522 div->lock = lock; in clk_wzrd_register_divider()
523 div->hw.init = &init; in clk_wzrd_register_divider()
525 hw = &div->hw; in clk_wzrd_register_divider()
530 return hw->clk; in clk_wzrd_register_divider()
540 if (clk_wzrd->suspended) in clk_wzrd_clk_notifier()
543 if (ndata->clk == clk_wzrd->clk_in1) in clk_wzrd_clk_notifier()
544 max = clk_wzrd_max_freq[clk_wzrd->speed_grade - 1]; in clk_wzrd_clk_notifier()
545 else if (ndata->clk == clk_wzrd->axi_clk) in clk_wzrd_clk_notifier()
552 if (ndata->new_rate > max) in clk_wzrd_clk_notifier()
566 clk_disable_unprepare(clk_wzrd->axi_clk); in clk_wzrd_suspend()
567 clk_wzrd->suspended = true; in clk_wzrd_suspend()
577 ret = clk_prepare_enable(clk_wzrd->axi_clk); in clk_wzrd_resume()
583 clk_wzrd->suspended = false; in clk_wzrd_resume()
600 struct device_node *np = pdev->dev.of_node; in clk_wzrd_probe()
604 clk_wzrd = devm_kzalloc(&pdev->dev, sizeof(*clk_wzrd), GFP_KERNEL); in clk_wzrd_probe()
606 return -ENOMEM; in clk_wzrd_probe()
609 clk_wzrd->base = devm_platform_ioremap_resource(pdev, 0); in clk_wzrd_probe()
610 if (IS_ERR(clk_wzrd->base)) in clk_wzrd_probe()
611 return PTR_ERR(clk_wzrd->base); in clk_wzrd_probe()
613 ret = of_property_read_u32(np, "xlnx,speed-grade", &clk_wzrd->speed_grade); in clk_wzrd_probe()
615 if (clk_wzrd->speed_grade < 1 || clk_wzrd->speed_grade > 3) { in clk_wzrd_probe()
616 dev_warn(&pdev->dev, "invalid speed grade '%d'\n", in clk_wzrd_probe()
617 clk_wzrd->speed_grade); in clk_wzrd_probe()
618 clk_wzrd->speed_grade = 0; in clk_wzrd_probe()
622 clk_wzrd->clk_in1 = devm_clk_get(&pdev->dev, "clk_in1"); in clk_wzrd_probe()
623 if (IS_ERR(clk_wzrd->clk_in1)) in clk_wzrd_probe()
624 return dev_err_probe(&pdev->dev, PTR_ERR(clk_wzrd->clk_in1), in clk_wzrd_probe()
627 clk_wzrd->axi_clk = devm_clk_get(&pdev->dev, "s_axi_aclk"); in clk_wzrd_probe()
628 if (IS_ERR(clk_wzrd->axi_clk)) in clk_wzrd_probe()
629 return dev_err_probe(&pdev->dev, PTR_ERR(clk_wzrd->axi_clk), in clk_wzrd_probe()
631 ret = clk_prepare_enable(clk_wzrd->axi_clk); in clk_wzrd_probe()
633 dev_err(&pdev->dev, "enabling s_axi_aclk failed\n"); in clk_wzrd_probe()
636 rate = clk_get_rate(clk_wzrd->axi_clk); in clk_wzrd_probe()
638 dev_err(&pdev->dev, "s_axi_aclk frequency (%lu) too high\n", in clk_wzrd_probe()
640 ret = -EINVAL; in clk_wzrd_probe()
644 ret = of_property_read_u32(np, "xlnx,nr-outputs", &nr_outputs); in clk_wzrd_probe()
646 ret = -EINVAL; in clk_wzrd_probe()
650 clkout_name = devm_kasprintf(&pdev->dev, GFP_KERNEL, "%s_out0", dev_name(&pdev->dev)); in clk_wzrd_probe()
652 ret = -ENOMEM; in clk_wzrd_probe()
657 clk_wzrd->clkout[0] = clk_wzrd_register_divider in clk_wzrd_probe()
658 (&pdev->dev, clkout_name, in clk_wzrd_probe()
659 __clk_get_name(clk_wzrd->clk_in1), 0, in clk_wzrd_probe()
660 clk_wzrd->base, WZRD_CLK_CFG_REG(3), in clk_wzrd_probe()
669 reg = readl(clk_wzrd->base + WZRD_CLK_CFG_REG(0)); in clk_wzrd_probe()
676 clk_name = devm_kasprintf(&pdev->dev, GFP_KERNEL, "%s_mul", dev_name(&pdev->dev)); in clk_wzrd_probe()
678 ret = -ENOMEM; in clk_wzrd_probe()
681 clk_wzrd->clks_internal[wzrd_clk_mul] = clk_register_fixed_factor in clk_wzrd_probe()
682 (&pdev->dev, clk_name, in clk_wzrd_probe()
683 __clk_get_name(clk_wzrd->clk_in1), in clk_wzrd_probe()
685 if (IS_ERR(clk_wzrd->clks_internal[wzrd_clk_mul])) { in clk_wzrd_probe()
686 dev_err(&pdev->dev, "unable to register fixed-factor clock\n"); in clk_wzrd_probe()
687 ret = PTR_ERR(clk_wzrd->clks_internal[wzrd_clk_mul]); in clk_wzrd_probe()
691 clk_name = devm_kasprintf(&pdev->dev, GFP_KERNEL, "%s_mul_div", dev_name(&pdev->dev)); in clk_wzrd_probe()
693 ret = -ENOMEM; in clk_wzrd_probe()
697 ctrl_reg = clk_wzrd->base + WZRD_CLK_CFG_REG(0); in clk_wzrd_probe()
699 clk_wzrd->clks_internal[wzrd_clk_mul_div] = clk_register_divider in clk_wzrd_probe()
700 (&pdev->dev, clk_name, in clk_wzrd_probe()
701 __clk_get_name(clk_wzrd->clks_internal[wzrd_clk_mul]), in clk_wzrd_probe()
704 if (IS_ERR(clk_wzrd->clks_internal[wzrd_clk_mul_div])) { in clk_wzrd_probe()
705 dev_err(&pdev->dev, "unable to register divider clock\n"); in clk_wzrd_probe()
706 ret = PTR_ERR(clk_wzrd->clks_internal[wzrd_clk_mul_div]); in clk_wzrd_probe()
711 for (i = nr_outputs - 1; i >= 0 ; i--) { in clk_wzrd_probe()
712 clkout_name = devm_kasprintf(&pdev->dev, GFP_KERNEL, in clk_wzrd_probe()
713 "%s_out%d", dev_name(&pdev->dev), i); in clk_wzrd_probe()
715 ret = -ENOMEM; in clk_wzrd_probe()
720 clk_wzrd->clkout[i] = clk_wzrd_register_divf in clk_wzrd_probe()
721 (&pdev->dev, clkout_name, in clk_wzrd_probe()
723 clk_wzrd->base, (WZRD_CLK_CFG_REG(2) + i * 12), in clk_wzrd_probe()
729 clk_wzrd->clkout[i] = clk_wzrd_register_divider in clk_wzrd_probe()
730 (&pdev->dev, clkout_name, in clk_wzrd_probe()
732 clk_wzrd->base, (WZRD_CLK_CFG_REG(2) + i * 12), in clk_wzrd_probe()
737 if (IS_ERR(clk_wzrd->clkout[i])) { in clk_wzrd_probe()
741 clk_unregister(clk_wzrd->clkout[j]); in clk_wzrd_probe()
742 dev_err(&pdev->dev, in clk_wzrd_probe()
744 ret = PTR_ERR(clk_wzrd->clkout[i]); in clk_wzrd_probe()
750 clk_wzrd->clk_data.clks = clk_wzrd->clkout; in clk_wzrd_probe()
751 clk_wzrd->clk_data.clk_num = ARRAY_SIZE(clk_wzrd->clkout); in clk_wzrd_probe()
752 of_clk_add_provider(np, of_clk_src_onecell_get, &clk_wzrd->clk_data); in clk_wzrd_probe()
754 if (clk_wzrd->speed_grade) { in clk_wzrd_probe()
755 clk_wzrd->nb.notifier_call = clk_wzrd_clk_notifier; in clk_wzrd_probe()
757 ret = clk_notifier_register(clk_wzrd->clk_in1, in clk_wzrd_probe()
758 &clk_wzrd->nb); in clk_wzrd_probe()
760 dev_warn(&pdev->dev, in clk_wzrd_probe()
763 ret = clk_notifier_register(clk_wzrd->axi_clk, &clk_wzrd->nb); in clk_wzrd_probe()
765 dev_warn(&pdev->dev, in clk_wzrd_probe()
772 clk_unregister(clk_wzrd->clks_internal[1]); in clk_wzrd_probe()
774 clk_unregister(clk_wzrd->clks_internal[0]); in clk_wzrd_probe()
776 clk_disable_unprepare(clk_wzrd->axi_clk); in clk_wzrd_probe()
786 of_clk_del_provider(pdev->dev.of_node); in clk_wzrd_remove()
789 clk_unregister(clk_wzrd->clkout[i]); in clk_wzrd_remove()
791 clk_unregister(clk_wzrd->clks_internal[i]); in clk_wzrd_remove()
793 if (clk_wzrd->speed_grade) { in clk_wzrd_remove()
794 clk_notifier_unregister(clk_wzrd->axi_clk, &clk_wzrd->nb); in clk_wzrd_remove()
795 clk_notifier_unregister(clk_wzrd->clk_in1, &clk_wzrd->nb); in clk_wzrd_remove()
798 clk_disable_unprepare(clk_wzrd->axi_clk); in clk_wzrd_remove()
802 { .compatible = "xlnx,clocking-wizard" },
803 { .compatible = "xlnx,clocking-wizard-v5.2" },
804 { .compatible = "xlnx,clocking-wizard-v6.0" },
811 .name = "clk-wizard",
822 MODULE_DESCRIPTION("Driver for the Xilinx Clocking Wizard IP core");