0001
0002
0003
0004
0005
0006
0007 #include <linux/clk-provider.h>
0008 #include <linux/io.h>
0009 #include <linux/spinlock.h>
0010
0011 #include "ccu_phase.h"
0012
0013 static int ccu_phase_get_phase(struct clk_hw *hw)
0014 {
0015 struct ccu_phase *phase = hw_to_ccu_phase(hw);
0016 struct clk_hw *parent, *grandparent;
0017 unsigned int parent_rate, grandparent_rate;
0018 u16 step, parent_div;
0019 u32 reg;
0020 u8 delay;
0021
0022 reg = readl(phase->common.base + phase->common.reg);
0023 delay = (reg >> phase->shift);
0024 delay &= (1 << phase->width) - 1;
0025
0026 if (!delay)
0027 return 180;
0028
0029
0030 parent = clk_hw_get_parent(hw);
0031 if (!parent)
0032 return -EINVAL;
0033
0034
0035 parent_rate = clk_hw_get_rate(parent);
0036 if (!parent_rate)
0037 return -EINVAL;
0038
0039
0040 grandparent = clk_hw_get_parent(parent);
0041 if (!grandparent)
0042 return -EINVAL;
0043
0044
0045 grandparent_rate = clk_hw_get_rate(grandparent);
0046 if (!grandparent_rate)
0047 return -EINVAL;
0048
0049
0050 parent_div = grandparent_rate / parent_rate;
0051
0052 step = DIV_ROUND_CLOSEST(360, parent_div);
0053 return delay * step;
0054 }
0055
0056 static int ccu_phase_set_phase(struct clk_hw *hw, int degrees)
0057 {
0058 struct ccu_phase *phase = hw_to_ccu_phase(hw);
0059 struct clk_hw *parent, *grandparent;
0060 unsigned int parent_rate, grandparent_rate;
0061 unsigned long flags;
0062 u32 reg;
0063 u8 delay;
0064
0065
0066 parent = clk_hw_get_parent(hw);
0067 if (!parent)
0068 return -EINVAL;
0069
0070
0071 parent_rate = clk_hw_get_rate(parent);
0072 if (!parent_rate)
0073 return -EINVAL;
0074
0075
0076 grandparent = clk_hw_get_parent(parent);
0077 if (!grandparent)
0078 return -EINVAL;
0079
0080
0081 grandparent_rate = clk_hw_get_rate(grandparent);
0082 if (!grandparent_rate)
0083 return -EINVAL;
0084
0085 if (degrees != 180) {
0086 u16 step, parent_div;
0087
0088
0089 parent_div = grandparent_rate / parent_rate;
0090
0091
0092
0093
0094
0095
0096
0097
0098
0099
0100
0101
0102
0103
0104 step = DIV_ROUND_CLOSEST(360, parent_div);
0105 delay = DIV_ROUND_CLOSEST(degrees, step);
0106 } else {
0107 delay = 0;
0108 }
0109
0110 spin_lock_irqsave(phase->common.lock, flags);
0111 reg = readl(phase->common.base + phase->common.reg);
0112 reg &= ~GENMASK(phase->width + phase->shift - 1, phase->shift);
0113 writel(reg | (delay << phase->shift),
0114 phase->common.base + phase->common.reg);
0115 spin_unlock_irqrestore(phase->common.lock, flags);
0116
0117 return 0;
0118 }
0119
0120 const struct clk_ops ccu_phase_ops = {
0121 .get_phase = ccu_phase_get_phase,
0122 .set_phase = ccu_phase_set_phase,
0123 };
0124 EXPORT_SYMBOL_NS_GPL(ccu_phase_ops, SUNXI_CCU);