pd: rk3368: add rk3368 power domain support (as pd clk)
[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 static int clk_pd_is_enabled(struct clk_hw *hw)
139 {
140         struct clk_pd *pd = to_clk_pd(hw);
141
142         return rockchip_pmu_ops.power_domain_is_on(pd->id);
143 }
144
145 static int clk_pd_prepare(struct clk_hw *hw)
146 {
147         __clk_pd_notify(hw->clk, RK_CLK_PD_PREPARE);
148
149         return 0;
150 }
151
152 static void clk_pd_unprepare(struct clk_hw *hw)
153 {
154         __clk_pd_notify(hw->clk, RK_CLK_PD_UNPREPARE);
155 }
156
157 const struct clk_ops clk_pd_ops = {
158         .prepare = clk_pd_prepare,
159         .unprepare = clk_pd_unprepare,
160         .enable = clk_pd_enable,
161         .disable = clk_pd_disable,
162         .is_enabled = clk_pd_is_enabled,
163 };
164
165 static int clk_pd_virt_enable(struct clk_hw *hw)
166 {
167         __clk_pd_notify(hw->clk, RK_CLK_PD_PRE_ENABLE);
168
169         __clk_pd_notify(hw->clk, RK_CLK_PD_POST_ENABLE);
170
171         return 0;
172 }
173
174 static void clk_pd_virt_disable(struct clk_hw *hw)
175 {
176         __clk_pd_notify(hw->clk, RK_CLK_PD_PRE_DISABLE);
177
178         __clk_pd_notify(hw->clk, RK_CLK_PD_POST_DISABLE);
179 }
180
181 const struct clk_ops clk_pd_virt_ops = {
182         .prepare = clk_pd_prepare,
183         .unprepare = clk_pd_unprepare,
184         .enable = clk_pd_virt_enable,
185         .disable = clk_pd_virt_disable,
186 };
187
188
189 struct clk *rk_clk_register_pd(struct device *dev, const char *name,
190                 const char *parent_name, unsigned long flags, 
191                 u32 pd_id, spinlock_t *lock)
192 {
193         struct clk_pd *pd;
194         struct clk *clk;
195         struct clk_init_data init;
196
197
198         /* allocate the pd */
199         pd = kzalloc(sizeof(struct clk_pd), GFP_KERNEL);
200         if (!pd) {
201                 clk_err("%s: could not allocate pd clk\n", __func__);
202                 return ERR_PTR(-ENOMEM);
203         }
204
205         init.name = name;
206         init.flags = flags | CLK_IS_BASIC;
207         init.parent_names = (parent_name ? &parent_name: NULL);
208         init.num_parents = (parent_name ? 1 : 0);
209
210         if(pd_id == CLK_PD_VIRT)
211                 init.ops = &clk_pd_virt_ops;
212         else
213                 init.ops = &clk_pd_ops;
214
215         /* struct clk_pd assignments */
216         pd->id= pd_id;
217         pd->lock = lock;
218         pd->hw.init = &init;
219
220         /* register the clock */
221         clk = clk_register(dev, &pd->hw);
222
223         if (IS_ERR(clk))
224                 kfree(pd);
225
226         return clk;
227 }
228