xref: /openbmc/linux/drivers/clk/ti/autoidle.c (revision 349d03ff)
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 	struct clk_omap_reg	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 
39 /*
40  * we have some non-atomic read/write
41  * operations behind it, so lets
42  * take one lock for handling autoidle
43  * of all clocks
44  */
45 static DEFINE_SPINLOCK(autoidle_spinlock);
46 
47 static int _omap2_clk_deny_idle(struct clk_hw_omap *clk)
48 {
49 	if (clk->ops && clk->ops->deny_idle) {
50 		unsigned long irqflags;
51 
52 		spin_lock_irqsave(&autoidle_spinlock, irqflags);
53 		clk->autoidle_count++;
54 		if (clk->autoidle_count == 1)
55 			clk->ops->deny_idle(clk);
56 
57 		spin_unlock_irqrestore(&autoidle_spinlock, irqflags);
58 	}
59 	return 0;
60 }
61 
62 static int _omap2_clk_allow_idle(struct clk_hw_omap *clk)
63 {
64 	if (clk->ops && clk->ops->allow_idle) {
65 		unsigned long irqflags;
66 
67 		spin_lock_irqsave(&autoidle_spinlock, irqflags);
68 		clk->autoidle_count--;
69 		if (clk->autoidle_count == 0)
70 			clk->ops->allow_idle(clk);
71 
72 		spin_unlock_irqrestore(&autoidle_spinlock, irqflags);
73 	}
74 	return 0;
75 }
76 
77 /**
78  * omap2_clk_deny_idle - disable autoidle on an OMAP clock
79  * @clk: struct clk * to disable autoidle for
80  *
81  * Disable autoidle on an OMAP clock.
82  */
83 int omap2_clk_deny_idle(struct clk *clk)
84 {
85 	struct clk_hw *hw;
86 
87 	if (!clk)
88 		return -EINVAL;
89 
90 	hw = __clk_get_hw(clk);
91 
92 	if (omap2_clk_is_hw_omap(hw)) {
93 		struct clk_hw_omap *c = to_clk_hw_omap(hw);
94 
95 		return _omap2_clk_deny_idle(c);
96 	}
97 
98 	return -EINVAL;
99 }
100 
101 /**
102  * omap2_clk_allow_idle - enable autoidle on an OMAP clock
103  * @clk: struct clk * to enable autoidle for
104  *
105  * Enable autoidle on an OMAP clock.
106  */
107 int omap2_clk_allow_idle(struct clk *clk)
108 {
109 	struct clk_hw *hw;
110 
111 	if (!clk)
112 		return -EINVAL;
113 
114 	hw = __clk_get_hw(clk);
115 
116 	if (omap2_clk_is_hw_omap(hw)) {
117 		struct clk_hw_omap *c = to_clk_hw_omap(hw);
118 
119 		return _omap2_clk_allow_idle(c);
120 	}
121 
122 	return -EINVAL;
123 }
124 
125 static void _allow_autoidle(struct clk_ti_autoidle *clk)
126 {
127 	u32 val;
128 
129 	val = ti_clk_ll_ops->clk_readl(&clk->reg);
130 
131 	if (clk->flags & AUTOIDLE_LOW)
132 		val &= ~(1 << clk->shift);
133 	else
134 		val |= (1 << clk->shift);
135 
136 	ti_clk_ll_ops->clk_writel(val, &clk->reg);
137 }
138 
139 static void _deny_autoidle(struct clk_ti_autoidle *clk)
140 {
141 	u32 val;
142 
143 	val = ti_clk_ll_ops->clk_readl(&clk->reg);
144 
145 	if (clk->flags & AUTOIDLE_LOW)
146 		val |= (1 << clk->shift);
147 	else
148 		val &= ~(1 << clk->shift);
149 
150 	ti_clk_ll_ops->clk_writel(val, &clk->reg);
151 }
152 
153 /**
154  * _clk_generic_allow_autoidle_all - enable autoidle for all clocks
155  *
156  * Enables hardware autoidle for all registered DT clocks, which have
157  * the feature.
158  */
159 static void _clk_generic_allow_autoidle_all(void)
160 {
161 	struct clk_ti_autoidle *c;
162 
163 	list_for_each_entry(c, &autoidle_clks, node)
164 		_allow_autoidle(c);
165 }
166 
167 /**
168  * _clk_generic_deny_autoidle_all - disable autoidle for all clocks
169  *
170  * Disables hardware autoidle for all registered DT clocks, which have
171  * the feature.
172  */
173 static void _clk_generic_deny_autoidle_all(void)
174 {
175 	struct clk_ti_autoidle *c;
176 
177 	list_for_each_entry(c, &autoidle_clks, node)
178 		_deny_autoidle(c);
179 }
180 
181 /**
182  * of_ti_clk_autoidle_setup - sets up hardware autoidle for a clock
183  * @node: pointer to the clock device node
184  *
185  * Checks if a clock has hardware autoidle support or not (check
186  * for presence of 'ti,autoidle-shift' property in the device tree
187  * node) and sets up the hardware autoidle feature for the clock
188  * if available. If autoidle is available, the clock is also added
189  * to the autoidle list for later processing. Returns 0 on success,
190  * negative error value on failure.
191  */
192 int __init of_ti_clk_autoidle_setup(struct device_node *node)
193 {
194 	u32 shift;
195 	struct clk_ti_autoidle *clk;
196 	int ret;
197 
198 	/* Check if this clock has autoidle support or not */
199 	if (of_property_read_u32(node, "ti,autoidle-shift", &shift))
200 		return 0;
201 
202 	clk = kzalloc(sizeof(*clk), GFP_KERNEL);
203 
204 	if (!clk)
205 		return -ENOMEM;
206 
207 	clk->shift = shift;
208 	clk->name = ti_dt_clk_name(node);
209 	ret = ti_clk_get_reg_addr(node, 0, &clk->reg);
210 	if (ret) {
211 		kfree(clk);
212 		return ret;
213 	}
214 
215 	if (of_property_read_bool(node, "ti,invert-autoidle-bit"))
216 		clk->flags |= AUTOIDLE_LOW;
217 
218 	list_add(&clk->node, &autoidle_clks);
219 
220 	return 0;
221 }
222 
223 /**
224  * omap2_clk_enable_autoidle_all - enable autoidle on all OMAP clocks that
225  * support it
226  *
227  * Enable clock autoidle on all OMAP clocks that have allow_idle
228  * function pointers associated with them.  This function is intended
229  * to be temporary until support for this is added to the common clock
230  * code.  Returns 0.
231  */
232 int omap2_clk_enable_autoidle_all(void)
233 {
234 	int ret;
235 
236 	ret = omap2_clk_for_each(_omap2_clk_allow_idle);
237 	if (ret)
238 		return ret;
239 
240 	_clk_generic_allow_autoidle_all();
241 
242 	return 0;
243 }
244 
245 /**
246  * omap2_clk_disable_autoidle_all - disable autoidle on all OMAP clocks that
247  * support it
248  *
249  * Disable clock autoidle on all OMAP clocks that have allow_idle
250  * function pointers associated with them.  This function is intended
251  * to be temporary until support for this is added to the common clock
252  * code.  Returns 0.
253  */
254 int omap2_clk_disable_autoidle_all(void)
255 {
256 	int ret;
257 
258 	ret = omap2_clk_for_each(_omap2_clk_deny_idle);
259 	if (ret)
260 		return ret;
261 
262 	_clk_generic_deny_autoidle_all();
263 
264 	return 0;
265 }
266