Merge branch 'for-4.2/ti-clk-move' of https://github.com/t-kristo/linux-pm into clk...
[firefly-linux-kernel-4.4.55.git] / drivers / clk / ti / autoidle.c
1 /*
2  * TI clock autoidle support
3  *
4  * Copyright (C) 2013 Texas Instruments, Inc.
5  *
6  * Tero Kristo <t-kristo@ti.com>
7  *
8  * This program is free software; you can redistribute it and/or modify
9  * it under the terms of the GNU General Public License version 2 as
10  * published by the Free Software Foundation.
11  *
12  * This program is distributed "as is" WITHOUT ANY WARRANTY of any
13  * kind, whether express or implied; without even the implied warranty
14  * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  * GNU General Public License for more details.
16  */
17
18 #include <linux/clk-provider.h>
19 #include <linux/slab.h>
20 #include <linux/io.h>
21 #include <linux/of.h>
22 #include <linux/of_address.h>
23 #include <linux/clk/ti.h>
24
25 #include "clock.h"
26
27 struct clk_ti_autoidle {
28         void __iomem            *reg;
29         u8                      shift;
30         u8                      flags;
31         const char              *name;
32         struct list_head        node;
33 };
34
35 #define AUTOIDLE_LOW            0x1
36
37 static LIST_HEAD(autoidle_clks);
38 static LIST_HEAD(clk_hw_omap_clocks);
39
40 /**
41  * omap2_clk_deny_idle - disable autoidle on an OMAP clock
42  * @clk: struct clk * to disable autoidle for
43  *
44  * Disable autoidle on an OMAP clock.
45  */
46 int omap2_clk_deny_idle(struct clk *clk)
47 {
48         struct clk_hw_omap *c;
49
50         if (__clk_get_flags(clk) & CLK_IS_BASIC)
51                 return -EINVAL;
52
53         c = to_clk_hw_omap(__clk_get_hw(clk));
54         if (c->ops && c->ops->deny_idle)
55                 c->ops->deny_idle(c);
56         return 0;
57 }
58
59 /**
60  * omap2_clk_allow_idle - enable autoidle on an OMAP clock
61  * @clk: struct clk * to enable autoidle for
62  *
63  * Enable autoidle on an OMAP clock.
64  */
65 int omap2_clk_allow_idle(struct clk *clk)
66 {
67         struct clk_hw_omap *c;
68
69         if (__clk_get_flags(clk) & CLK_IS_BASIC)
70                 return -EINVAL;
71
72         c = to_clk_hw_omap(__clk_get_hw(clk));
73         if (c->ops && c->ops->allow_idle)
74                 c->ops->allow_idle(c);
75         return 0;
76 }
77
78 static void _allow_autoidle(struct clk_ti_autoidle *clk)
79 {
80         u32 val;
81
82         val = ti_clk_ll_ops->clk_readl(clk->reg);
83
84         if (clk->flags & AUTOIDLE_LOW)
85                 val &= ~(1 << clk->shift);
86         else
87                 val |= (1 << clk->shift);
88
89         ti_clk_ll_ops->clk_writel(val, clk->reg);
90 }
91
92 static void _deny_autoidle(struct clk_ti_autoidle *clk)
93 {
94         u32 val;
95
96         val = ti_clk_ll_ops->clk_readl(clk->reg);
97
98         if (clk->flags & AUTOIDLE_LOW)
99                 val |= (1 << clk->shift);
100         else
101                 val &= ~(1 << clk->shift);
102
103         ti_clk_ll_ops->clk_writel(val, clk->reg);
104 }
105
106 /**
107  * _clk_generic_allow_autoidle_all - enable autoidle for all clocks
108  *
109  * Enables hardware autoidle for all registered DT clocks, which have
110  * the feature.
111  */
112 static void _clk_generic_allow_autoidle_all(void)
113 {
114         struct clk_ti_autoidle *c;
115
116         list_for_each_entry(c, &autoidle_clks, node)
117                 _allow_autoidle(c);
118 }
119
120 /**
121  * _clk_generic_deny_autoidle_all - disable autoidle for all clocks
122  *
123  * Disables hardware autoidle for all registered DT clocks, which have
124  * the feature.
125  */
126 static void _clk_generic_deny_autoidle_all(void)
127 {
128         struct clk_ti_autoidle *c;
129
130         list_for_each_entry(c, &autoidle_clks, node)
131                 _deny_autoidle(c);
132 }
133
134 /**
135  * of_ti_clk_autoidle_setup - sets up hardware autoidle for a clock
136  * @node: pointer to the clock device node
137  *
138  * Checks if a clock has hardware autoidle support or not (check
139  * for presence of 'ti,autoidle-shift' property in the device tree
140  * node) and sets up the hardware autoidle feature for the clock
141  * if available. If autoidle is available, the clock is also added
142  * to the autoidle list for later processing. Returns 0 on success,
143  * negative error value on failure.
144  */
145 int __init of_ti_clk_autoidle_setup(struct device_node *node)
146 {
147         u32 shift;
148         struct clk_ti_autoidle *clk;
149
150         /* Check if this clock has autoidle support or not */
151         if (of_property_read_u32(node, "ti,autoidle-shift", &shift))
152                 return 0;
153
154         clk = kzalloc(sizeof(*clk), GFP_KERNEL);
155
156         if (!clk)
157                 return -ENOMEM;
158
159         clk->shift = shift;
160         clk->name = node->name;
161         clk->reg = ti_clk_get_reg_addr(node, 0);
162
163         if (IS_ERR(clk->reg)) {
164                 kfree(clk);
165                 return -EINVAL;
166         }
167
168         if (of_property_read_bool(node, "ti,invert-autoidle-bit"))
169                 clk->flags |= AUTOIDLE_LOW;
170
171         list_add(&clk->node, &autoidle_clks);
172
173         return 0;
174 }
175
176 /**
177  * omap2_init_clk_hw_omap_clocks - initialize an OMAP clock
178  * @clk: struct clk * to initialize
179  *
180  * Add an OMAP clock @clk to the internal list of OMAP clocks.  Used
181  * temporarily for autoidle handling, until this support can be
182  * integrated into the common clock framework code in some way.  No
183  * return value.
184  */
185 void omap2_init_clk_hw_omap_clocks(struct clk *clk)
186 {
187         struct clk_hw_omap *c;
188
189         if (__clk_get_flags(clk) & CLK_IS_BASIC)
190                 return;
191
192         c = to_clk_hw_omap(__clk_get_hw(clk));
193         list_add(&c->node, &clk_hw_omap_clocks);
194 }
195
196 /**
197  * omap2_clk_enable_autoidle_all - enable autoidle on all OMAP clocks that
198  * support it
199  *
200  * Enable clock autoidle on all OMAP clocks that have allow_idle
201  * function pointers associated with them.  This function is intended
202  * to be temporary until support for this is added to the common clock
203  * code.  Returns 0.
204  */
205 int omap2_clk_enable_autoidle_all(void)
206 {
207         struct clk_hw_omap *c;
208
209         list_for_each_entry(c, &clk_hw_omap_clocks, node)
210                 if (c->ops && c->ops->allow_idle)
211                         c->ops->allow_idle(c);
212
213         _clk_generic_allow_autoidle_all();
214
215         return 0;
216 }
217
218 /**
219  * omap2_clk_disable_autoidle_all - disable autoidle on all OMAP clocks that
220  * support it
221  *
222  * Disable clock autoidle on all OMAP clocks that have allow_idle
223  * function pointers associated with them.  This function is intended
224  * to be temporary until support for this is added to the common clock
225  * code.  Returns 0.
226  */
227 int omap2_clk_disable_autoidle_all(void)
228 {
229         struct clk_hw_omap *c;
230
231         list_for_each_entry(c, &clk_hw_omap_clocks, node)
232                 if (c->ops && c->ops->deny_idle)
233                         c->ops->deny_idle(c);
234
235         _clk_generic_deny_autoidle_all();
236
237         return 0;
238 }