xref: /freebsd/sys/arm/ti/ti_prcm.c (revision 955c8cbb4960e6cf3602de144b1b9154a5092968)
1 /*
2  * Copyright (c) 2010
3  *	Ben Gray <ben.r.gray@gmail.com>.
4  * All rights reserved.
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions
8  * are met:
9  * 1. Redistributions of source code must retain the above copyright
10  *    notice, this list of conditions and the following disclaimer.
11  * 2. Redistributions in binary form must reproduce the above copyright
12  *    notice, this list of conditions and the following disclaimer in the
13  *    documentation and/or other materials provided with the distribution.
14  * 3. All advertising materials mentioning features or use of this software
15  *    must display the following acknowledgement:
16  *	This product includes software developed by Ben Gray.
17  * 4. The name of the company nor the name of the author may be used to
18  *    endorse or promote products derived from this software without specific
19  *    prior written permission.
20  *
21  * THIS SOFTWARE IS PROVIDED BY BEN GRAY ``AS IS'' AND ANY EXPRESS OR
22  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
23  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
24  * IN NO EVENT SHALL BEN GRAY BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
25  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
26  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
27  * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
28  * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
29  * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
30  * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31  */
32 
33 /**
34  * Power, Reset and Clock Managment Module
35  *
36  * This is a very simple driver wrapper around the PRCM set of registers in
37  * the OMAP3 chip. It allows you to turn on and off things like the functional
38  * and interface clocks to the various on-chip modules.
39  *
40  */
41 #include <sys/cdefs.h>
42 __FBSDID("$FreeBSD$");
43 
44 #include <sys/param.h>
45 #include <sys/systm.h>
46 #include <sys/kernel.h>
47 #include <sys/module.h>
48 #include <sys/bus.h>
49 #include <sys/resource.h>
50 #include <sys/rman.h>
51 #include <sys/lock.h>
52 #include <sys/mutex.h>
53 
54 #include <machine/bus.h>
55 #include <machine/cpu.h>
56 #include <machine/cpufunc.h>
57 #include <machine/frame.h>
58 #include <machine/resource.h>
59 #include <machine/intr.h>
60 
61 #include <arm/ti/ti_prcm.h>
62 
63 /**
64  *	ti_clk_devmap - Array of clock devices, should be defined one per SoC
65  *
66  *	This array is typically defined in one of the targeted *_prcm_clk.c
67  *	files and is specific to the given SoC platform.  Each entry in the array
68  *	corresponds to an individual clock device.
69  */
70 extern struct ti_clock_dev ti_clk_devmap[];
71 
72 /**
73  *	ti_prcm_clk_dev - returns a pointer to the clock device with given id
74  *	@clk: the ID of the clock device to get
75  *
76  *	Simply iterates through the clk_devmap global array and returns a pointer
77  *	to the clock device if found.
78  *
79  *	LOCKING:
80  *	None
81  *
82  *	RETURNS:
83  *	The pointer to the clock device on success, on failure NULL is returned.
84  */
85 static struct ti_clock_dev *
86 ti_prcm_clk_dev(clk_ident_t clk)
87 {
88 	struct ti_clock_dev *clk_dev;
89 
90 	/* Find the clock within the devmap - it's a bit inefficent having a for
91 	 * loop for this, but this function should only called when a driver is
92 	 * being activated so IMHO not a big issue.
93 	 */
94 	clk_dev = &(ti_clk_devmap[0]);
95 	while (clk_dev->id != INVALID_CLK_IDENT) {
96 		if (clk_dev->id == clk) {
97 			return (clk_dev);
98 		}
99 		clk_dev++;
100 	}
101 
102 	/* Sanity check we managed to find the clock */
103 	printf("ti_prcm: Failed to find clock device (%d)\n", clk);
104 	return (NULL);
105 }
106 
107 /**
108  *	ti_prcm_clk_valid - enables a clock for a particular module
109  *	@clk: identifier for the module to enable, see ti_prcm.h for a list
110  *	      of possible modules.
111  *	         Example: OMAP3_MODULE_MMC1_ICLK or OMAP3_MODULE_GPTIMER10_FCLK.
112  *
113  *	This function can enable either a functional or interface clock.
114  *
115  *	The real work done to enable the clock is really done in the callback
116  *	function associated with the clock, this function is simply a wrapper
117  *	around that.
118  *
119  *	LOCKING:
120  *	Internally locks the driver context.
121  *
122  *	RETURNS:
123  *	Returns 0 on success or positive error code on failure.
124  */
125 int
126 ti_prcm_clk_valid(clk_ident_t clk)
127 {
128 	int ret = 0;
129 
130 	if (ti_prcm_clk_dev(clk) == NULL)
131 		ret = EINVAL;
132 
133 	return (ret);
134 }
135 
136 
137 /**
138  *	ti_prcm_clk_enable - enables a clock for a particular module
139  *	@clk: identifier for the module to enable, see ti_prcm.h for a list
140  *	      of possible modules.
141  *	         Example: OMAP3_MODULE_MMC1_ICLK or OMAP3_MODULE_GPTIMER10_FCLK.
142  *
143  *	This function can enable either a functional or interface clock.
144  *
145  *	The real work done to enable the clock is really done in the callback
146  *	function associated with the clock, this function is simply a wrapper
147  *	around that.
148  *
149  *	LOCKING:
150  *	Internally locks the driver context.
151  *
152  *	RETURNS:
153  *	Returns 0 on success or positive error code on failure.
154  */
155 int
156 ti_prcm_clk_enable(clk_ident_t clk)
157 {
158 	struct ti_clock_dev *clk_dev;
159 	int ret;
160 
161 	/* Find the clock within the devmap - it's a bit inefficent having a for
162 	 * loop for this, but this function should only called when a driver is
163 	 * being activated so IMHO not a big issue.
164 	 */
165 	clk_dev = ti_prcm_clk_dev(clk);
166 
167 	/* Sanity check we managed to find the clock */
168 	if (clk_dev == NULL)
169 		return (EINVAL);
170 
171 	/* Activate the clock */
172 	if (clk_dev->clk_activate)
173 		ret = clk_dev->clk_activate(clk_dev);
174 	else
175 		ret = EINVAL;
176 
177 	return (ret);
178 }
179 
180 
181 /**
182  *	ti_prcm_clk_disable - disables a clock for a particular module
183  *	@clk: identifier for the module to enable, see ti_prcm.h for a list
184  *	      of possible modules.
185  *	         Example: OMAP3_MODULE_MMC1_ICLK or OMAP3_MODULE_GPTIMER10_FCLK.
186  *
187  *	This function can enable either a functional or interface clock.
188  *
189  *	The real work done to enable the clock is really done in the callback
190  *	function associated with the clock, this function is simply a wrapper
191  *	around that.
192  *
193  *	LOCKING:
194  *	Internally locks the driver context.
195  *
196  *	RETURNS:
197  *	Returns 0 on success or positive error code on failure.
198  */
199 int
200 ti_prcm_clk_disable(clk_ident_t clk)
201 {
202 	struct ti_clock_dev *clk_dev;
203 	int ret;
204 
205 	/* Find the clock within the devmap - it's a bit inefficent having a for
206 	 * loop for this, but this function should only called when a driver is
207 	 * being activated so IMHO not a big issue.
208 	 */
209 	clk_dev = ti_prcm_clk_dev(clk);
210 
211 	/* Sanity check we managed to find the clock */
212 	if (clk_dev == NULL)
213 		return (EINVAL);
214 
215 	/* Activate the clock */
216 	if (clk_dev->clk_deactivate)
217 		ret = clk_dev->clk_deactivate(clk_dev);
218 	else
219 		ret = EINVAL;
220 
221 	return (ret);
222 }
223 
224 /**
225  *	ti_prcm_clk_set_source - sets the source
226  *	@clk: identifier for the module to enable, see ti_prcm.h for a list
227  *	      of possible modules.
228  *	         Example: OMAP3_MODULE_MMC1_ICLK or OMAP3_MODULE_GPTIMER10_FCLK.
229  *
230  *	This function can enable either a functional or interface clock.
231  *
232  *	The real work done to enable the clock is really done in the callback
233  *	function associated with the clock, this function is simply a wrapper
234  *	around that.
235  *
236  *	LOCKING:
237  *	Internally locks the driver context.
238  *
239  *	RETURNS:
240  *	Returns 0 on success or positive error code on failure.
241  */
242 int
243 ti_prcm_clk_set_source(clk_ident_t clk, clk_src_t clksrc)
244 {
245 	struct ti_clock_dev *clk_dev;
246 	int ret;
247 
248 	/* Find the clock within the devmap - it's a bit inefficent having a for
249 	 * loop for this, but this function should only called when a driver is
250 	 * being activated so IMHO not a big issue.
251 	 */
252 	clk_dev = ti_prcm_clk_dev(clk);
253 
254 	/* Sanity check we managed to find the clock */
255 	if (clk_dev == NULL)
256 		return (EINVAL);
257 
258 	/* Activate the clock */
259 	if (clk_dev->clk_set_source)
260 		ret = clk_dev->clk_set_source(clk_dev, clksrc);
261 	else
262 		ret = EINVAL;
263 
264 	return (ret);
265 }
266 
267 
268 /**
269  *	ti_prcm_clk_get_source_freq - gets the source clock frequency
270  *	@clk: identifier for the module to enable, see ti_prcm.h for a list
271  *	      of possible modules.
272  *	@freq: pointer to an integer that upon return will contain the src freq
273  *
274  *	This function returns the frequency of the source clock.
275  *
276  *	The real work done to enable the clock is really done in the callback
277  *	function associated with the clock, this function is simply a wrapper
278  *	around that.
279  *
280  *	LOCKING:
281  *	Internally locks the driver context.
282  *
283  *	RETURNS:
284  *	Returns 0 on success or positive error code on failure.
285  */
286 int
287 ti_prcm_clk_get_source_freq(clk_ident_t clk, unsigned int *freq)
288 {
289 	struct ti_clock_dev *clk_dev;
290 	int ret;
291 
292 	/* Find the clock within the devmap - it's a bit inefficent having a for
293 	 * loop for this, but this function should only called when a driver is
294 	 * being activated so IMHO not a big issue.
295 	 */
296 	clk_dev = ti_prcm_clk_dev(clk);
297 
298 	/* Sanity check we managed to find the clock */
299 	if (clk_dev == NULL)
300 		return (EINVAL);
301 
302 	/* Get the source frequency of the clock */
303 	if (clk_dev->clk_get_source_freq)
304 		ret = clk_dev->clk_get_source_freq(clk_dev, freq);
305 	else
306 		ret = EINVAL;
307 
308 	return (ret);
309 }
310