^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1) // SPDX-License-Identifier: GPL-2.0-or-later
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 2) /*
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 3) * Copyright (C) 2016 Maxime Ripard
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 4) * Maxime Ripard <maxime.ripard@free-electrons.com>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 5) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 6)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 7) #include <linux/clk-provider.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 8) #include <linux/io.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 9) #include <linux/spinlock.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 10)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 11) #include "ccu_phase.h"
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 12)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 13) static int ccu_phase_get_phase(struct clk_hw *hw)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 14) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 15) struct ccu_phase *phase = hw_to_ccu_phase(hw);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 16) struct clk_hw *parent, *grandparent;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 17) unsigned int parent_rate, grandparent_rate;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 18) u16 step, parent_div;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 19) u32 reg;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 20) u8 delay;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 21)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 22) reg = readl(phase->common.base + phase->common.reg);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 23) delay = (reg >> phase->shift);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 24) delay &= (1 << phase->width) - 1;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 25)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 26) if (!delay)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 27) return 180;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 28)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 29) /* Get our parent clock, it's the one that can adjust its rate */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 30) parent = clk_hw_get_parent(hw);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 31) if (!parent)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 32) return -EINVAL;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 33)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 34) /* And its rate */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 35) parent_rate = clk_hw_get_rate(parent);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 36) if (!parent_rate)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 37) return -EINVAL;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 38)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 39) /* Now, get our parent's parent (most likely some PLL) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 40) grandparent = clk_hw_get_parent(parent);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 41) if (!grandparent)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 42) return -EINVAL;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 43)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 44) /* And its rate */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 45) grandparent_rate = clk_hw_get_rate(grandparent);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 46) if (!grandparent_rate)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 47) return -EINVAL;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 48)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 49) /* Get our parent clock divider */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 50) parent_div = grandparent_rate / parent_rate;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 51)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 52) step = DIV_ROUND_CLOSEST(360, parent_div);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 53) return delay * step;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 54) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 55)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 56) static int ccu_phase_set_phase(struct clk_hw *hw, int degrees)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 57) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 58) struct ccu_phase *phase = hw_to_ccu_phase(hw);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 59) struct clk_hw *parent, *grandparent;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 60) unsigned int parent_rate, grandparent_rate;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 61) unsigned long flags;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 62) u32 reg;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 63) u8 delay;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 64)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 65) /* Get our parent clock, it's the one that can adjust its rate */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 66) parent = clk_hw_get_parent(hw);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 67) if (!parent)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 68) return -EINVAL;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 69)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 70) /* And its rate */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 71) parent_rate = clk_hw_get_rate(parent);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 72) if (!parent_rate)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 73) return -EINVAL;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 74)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 75) /* Now, get our parent's parent (most likely some PLL) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 76) grandparent = clk_hw_get_parent(parent);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 77) if (!grandparent)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 78) return -EINVAL;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 79)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 80) /* And its rate */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 81) grandparent_rate = clk_hw_get_rate(grandparent);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 82) if (!grandparent_rate)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 83) return -EINVAL;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 84)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 85) if (degrees != 180) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 86) u16 step, parent_div;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 87)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 88) /* Get our parent divider */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 89) parent_div = grandparent_rate / parent_rate;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 90)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 91) /*
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 92) * We can only outphase the clocks by multiple of the
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 93) * PLL's period.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 94) *
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 95) * Since our parent clock is only a divider, and the
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 96) * formula to get the outphasing in degrees is deg =
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 97) * 360 * delta / period
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 98) *
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 99) * If we simplify this formula, we can see that the
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 100) * only thing that we're concerned about is the number
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 101) * of period we want to outphase our clock from, and
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 102) * the divider set by our parent clock.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 103) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 104) step = DIV_ROUND_CLOSEST(360, parent_div);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 105) delay = DIV_ROUND_CLOSEST(degrees, step);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 106) } else {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 107) delay = 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 108) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 109)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 110) spin_lock_irqsave(phase->common.lock, flags);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 111) reg = readl(phase->common.base + phase->common.reg);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 112) reg &= ~GENMASK(phase->width + phase->shift - 1, phase->shift);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 113) writel(reg | (delay << phase->shift),
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 114) phase->common.base + phase->common.reg);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 115) spin_unlock_irqrestore(phase->common.lock, flags);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 116)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 117) return 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 118) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 119)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 120) const struct clk_ops ccu_phase_ops = {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 121) .get_phase = ccu_phase_get_phase,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 122) .set_phase = ccu_phase_set_phase,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 123) };