1
2
3
4
5
6
7
8
9
10
11#include <linux/clk-provider.h>
12#include <linux/spinlock.h>
13
14#include "ccu_phase.h"
15
16static int ccu_phase_get_phase(struct clk_hw *hw)
17{
18 struct ccu_phase *phase = hw_to_ccu_phase(hw);
19 struct clk_hw *parent, *grandparent;
20 unsigned int parent_rate, grandparent_rate;
21 u16 step, parent_div;
22 u32 reg;
23 u8 delay;
24
25 reg = readl(phase->common.base + phase->common.reg);
26 delay = (reg >> phase->shift);
27 delay &= (1 << phase->width) - 1;
28
29 if (!delay)
30 return 180;
31
32
33 parent = clk_hw_get_parent(hw);
34 if (!parent)
35 return -EINVAL;
36
37
38 parent_rate = clk_hw_get_rate(parent);
39 if (!parent_rate)
40 return -EINVAL;
41
42
43 grandparent = clk_hw_get_parent(parent);
44 if (!grandparent)
45 return -EINVAL;
46
47
48 grandparent_rate = clk_hw_get_rate(grandparent);
49 if (!grandparent_rate)
50 return -EINVAL;
51
52
53 parent_div = grandparent_rate / parent_rate;
54
55 step = DIV_ROUND_CLOSEST(360, parent_div);
56 return delay * step;
57}
58
59static int ccu_phase_set_phase(struct clk_hw *hw, int degrees)
60{
61 struct ccu_phase *phase = hw_to_ccu_phase(hw);
62 struct clk_hw *parent, *grandparent;
63 unsigned int parent_rate, grandparent_rate;
64 unsigned long flags;
65 u32 reg;
66 u8 delay;
67
68
69 parent = clk_hw_get_parent(hw);
70 if (!parent)
71 return -EINVAL;
72
73
74 parent_rate = clk_hw_get_rate(parent);
75 if (!parent_rate)
76 return -EINVAL;
77
78
79 grandparent = clk_hw_get_parent(parent);
80 if (!grandparent)
81 return -EINVAL;
82
83
84 grandparent_rate = clk_hw_get_rate(grandparent);
85 if (!grandparent_rate)
86 return -EINVAL;
87
88 if (degrees != 180) {
89 u16 step, parent_div;
90
91
92 parent_div = grandparent_rate / parent_rate;
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107 step = DIV_ROUND_CLOSEST(360, parent_div);
108 delay = DIV_ROUND_CLOSEST(degrees, step);
109 } else {
110 delay = 0;
111 }
112
113 spin_lock_irqsave(phase->common.lock, flags);
114 reg = readl(phase->common.base + phase->common.reg);
115 reg &= ~GENMASK(phase->width + phase->shift - 1, phase->shift);
116 writel(reg | (delay << phase->shift),
117 phase->common.base + phase->common.reg);
118 spin_unlock_irqrestore(phase->common.lock, flags);
119
120 return 0;
121}
122
123const struct clk_ops ccu_phase_ops = {
124 .get_phase = ccu_phase_get_phase,
125 .set_phase = ccu_phase_set_phase,
126};
127