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