14fc1e47e97eff296c0a22eec032591f1bafefc0
[firefly-linux-kernel-4.4.55.git] / drivers / clk / rockchip / clk-pd.c
1 #include <linux/slab.h>
2
3 #include "clk-ops.h"
4 #include "clk-pd.h"
5
6
7 static LIST_HEAD(clk_pd_notifier_list);
8
9 static int __clk_pd_notify(struct clk *clk, unsigned long msg)
10 {
11         struct clk_pd_notifier *cn;
12         int ret = NOTIFY_DONE;
13
14         list_for_each_entry(cn, &clk_pd_notifier_list, node) {
15                 if (cn->clk == clk) {
16                         ret = srcu_notifier_call_chain(&cn->notifier_head, msg,
17                                         NULL);
18                         break;
19                 }
20         }
21
22         return ret;
23 }
24
25 int rk_clk_pd_notifier_register(struct clk *clk, struct notifier_block *nb)
26 {
27         struct clk_pd_notifier *cn;
28         int ret = -ENOMEM;
29
30         if (!clk || !nb)
31                 return -EINVAL;
32
33         //clk_prepare_lock();
34
35         /* search the list of notifiers for this clk */
36         list_for_each_entry(cn, &clk_pd_notifier_list, node)
37                 if (cn->clk == clk)
38                         break;
39
40         /* if clk wasn't in the notifier list, allocate new clk_notifier */
41         if (cn->clk != clk) {
42                 cn = kzalloc(sizeof(struct clk_pd_notifier), GFP_KERNEL);
43                 if (!cn)
44                         goto out;
45
46                 cn->clk = clk;
47                 srcu_init_notifier_head(&cn->notifier_head);
48
49                 list_add(&cn->node, &clk_pd_notifier_list);
50         }
51
52         ret = srcu_notifier_chain_register(&cn->notifier_head, nb);
53
54         //clk->notifier_count++;
55
56 out:
57         //clk_prepare_unlock();
58
59         return ret;
60 }
61 EXPORT_SYMBOL_GPL(rk_clk_pd_notifier_register);
62
63 int rk_clk_pd_notifier_unregister(struct clk *clk, struct notifier_block *nb)
64 {
65         struct clk_pd_notifier *cn = NULL;
66         int ret = -EINVAL;
67
68         if (!clk || !nb)
69                 return -EINVAL;
70
71         //clk_prepare_lock();
72
73         list_for_each_entry(cn, &clk_pd_notifier_list, node)
74                 if (cn->clk == clk)
75                         break;
76
77         if (cn->clk == clk) {
78                 ret = srcu_notifier_chain_unregister(&cn->notifier_head, nb);
79
80                 //clk->notifier_count--;
81
82                 /* XXX the notifier code should handle this better */
83                 if (!cn->notifier_head.head) {
84                         srcu_cleanup_notifier_head(&cn->notifier_head);
85                         list_del(&cn->node);
86                         kfree(cn);
87                 }
88
89         } else {
90                 ret = -ENOENT;
91         }
92
93         //clk_prepare_unlock();
94
95         return ret;
96 }
97 EXPORT_SYMBOL_GPL(rk_clk_pd_notifier_unregister);
98
99 static int clk_pd_endisable(struct clk_hw *hw, bool enable)
100 {
101         struct clk_pd *pd = to_clk_pd(hw);
102         unsigned long flags = 0;
103         int ret = 0;
104
105         if (pd->lock)
106                 spin_lock_irqsave(pd->lock, flags);
107
108         /* ret = rockchip_pmu_ops.set_power_domain(pd->id, enable); */
109
110         if (pd->lock)
111                 spin_unlock_irqrestore(pd->lock, flags);
112
113         return ret;     
114 }
115
116 static int clk_pd_enable(struct clk_hw *hw)
117 {
118         int ret = 0;
119
120         __clk_pd_notify(hw->clk, RK_CLK_PD_PRE_ENABLE);
121
122         ret = clk_pd_endisable(hw, true);
123
124         __clk_pd_notify(hw->clk, RK_CLK_PD_POST_ENABLE);
125
126         return ret;
127 }
128
129 static void clk_pd_disable(struct clk_hw *hw)
130 {
131         __clk_pd_notify(hw->clk, RK_CLK_PD_PRE_DISABLE);
132
133         clk_pd_endisable(hw, false);
134
135         __clk_pd_notify(hw->clk, RK_CLK_PD_POST_DISABLE);
136 }
137
138 /*
139 static int clk_pd_is_enabled(struct clk_hw *hw)
140 {
141         struct clk_pd *pd = to_clk_pd(hw);
142
143         return rockchip_pmu_ops.power_domain_is_on(pd->id);
144 }
145 */
146
147 static int clk_pd_prepare(struct clk_hw *hw)
148 {
149         __clk_pd_notify(hw->clk, RK_CLK_PD_PREPARE);
150
151         return 0;
152 }
153
154 static void clk_pd_unprepare(struct clk_hw *hw)
155 {
156         __clk_pd_notify(hw->clk, RK_CLK_PD_UNPREPARE);
157 }
158
159 const struct clk_ops clk_pd_ops = {
160         .prepare = clk_pd_prepare,
161         .unprepare = clk_pd_unprepare,
162         .enable = clk_pd_enable,
163         .disable = clk_pd_disable,
164         /*.is_enabled = clk_pd_is_enabled,*/
165 };
166
167 static int clk_pd_virt_enable(struct clk_hw *hw)
168 {
169         __clk_pd_notify(hw->clk, RK_CLK_PD_PRE_ENABLE);
170
171         __clk_pd_notify(hw->clk, RK_CLK_PD_POST_ENABLE);
172
173         return 0;
174 }
175
176 static void clk_pd_virt_disable(struct clk_hw *hw)
177 {
178         __clk_pd_notify(hw->clk, RK_CLK_PD_PRE_DISABLE);
179
180         __clk_pd_notify(hw->clk, RK_CLK_PD_POST_DISABLE);
181 }
182
183 const struct clk_ops clk_pd_virt_ops = {
184         .prepare = clk_pd_prepare,
185         .unprepare = clk_pd_unprepare,
186         .enable = clk_pd_virt_enable,
187         .disable = clk_pd_virt_disable,
188 };
189
190
191 struct clk *rk_clk_register_pd(struct device *dev, const char *name,
192                 const char *parent_name, unsigned long flags, 
193                 u32 pd_id, spinlock_t *lock)
194 {
195         struct clk_pd *pd;
196         struct clk *clk;
197         struct clk_init_data init;
198
199
200         /* allocate the pd */
201         pd = kzalloc(sizeof(struct clk_pd), GFP_KERNEL);
202         if (!pd) {
203                 clk_err("%s: could not allocate pd clk\n", __func__);
204                 return ERR_PTR(-ENOMEM);
205         }
206
207         init.name = name;
208         init.flags = flags | CLK_IS_BASIC;
209         init.parent_names = (parent_name ? &parent_name: NULL);
210         init.num_parents = (parent_name ? 1 : 0);
211
212         if(pd_id == CLK_PD_VIRT)
213                 init.ops = &clk_pd_virt_ops;
214         else
215                 init.ops = &clk_pd_ops;
216
217         /* struct clk_pd assignments */
218         pd->id= pd_id;
219         pd->lock = lock;
220         pd->hw.init = &init;
221
222         /* register the clock */
223         clk = clk_register(dev, &pd->hw);
224
225         if (IS_ERR(clk))
226                 kfree(pd);
227
228         return clk;
229 }
230