xref: /freebsd/sys/arm/ti/ti_prcm.c (revision 5dae51da3da0cc94d17bd67b308fad304ebec7e0)
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 Management 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/resource.h>
56 #include <machine/intr.h>
57 
58 #include <arm/ti/ti_cpuid.h>
59 #include <arm/ti/ti_prcm.h>
60 
61 /**
62  *	ti_*_clk_devmap - Array of clock devices, should be defined one per SoC
63  *
64  *	This array is typically defined in one of the targeted *_prcm_clk.c
65  *	files and is specific to the given SoC platform.  Each entry in the array
66  *	corresponds to an individual clock device.
67  */
68 extern struct ti_clock_dev ti_omap4_clk_devmap[];
69 extern struct ti_clock_dev ti_am335x_clk_devmap[];
70 
71 /**
72  *	ti_prcm_clk_dev - returns a pointer to the clock device with given id
73  *	@clk: the ID of the clock device to get
74  *
75  *	Simply iterates through the clk_devmap global array and returns a pointer
76  *	to the clock device if found.
77  *
78  *	LOCKING:
79  *	None
80  *
81  *	RETURNS:
82  *	The pointer to the clock device on success, on failure NULL is returned.
83  */
84 static struct ti_clock_dev *
85 ti_prcm_clk_dev(clk_ident_t clk)
86 {
87 	struct ti_clock_dev *clk_dev;
88 
89 	/* Find the clock within the devmap - it's a bit inefficent having a for
90 	 * loop for this, but this function should only called when a driver is
91 	 * being activated so IMHO not a big issue.
92 	 */
93 	clk_dev = NULL;
94 	switch(ti_chip()) {
95 #ifdef SOC_OMAP4
96 	case CHIP_OMAP_4:
97 		clk_dev = &(ti_omap4_clk_devmap[0]);
98 		break;
99 #endif
100 #ifdef SOC_TI_AM335X
101 	case CHIP_AM335X:
102 		clk_dev = &(ti_am335x_clk_devmap[0]);
103 		break;
104 #endif
105 	}
106 	if (clk_dev == NULL)
107 		panic("No clock devmap found");
108 	while (clk_dev->id != INVALID_CLK_IDENT) {
109 		if (clk_dev->id == clk) {
110 			return (clk_dev);
111 		}
112 		clk_dev++;
113 	}
114 
115 	/* Sanity check we managed to find the clock */
116 	printf("ti_prcm: Failed to find clock device (%d)\n", clk);
117 	return (NULL);
118 }
119 
120 /**
121  *	ti_prcm_clk_valid - enables a clock for a particular module
122  *	@clk: identifier for the module to enable, see ti_prcm.h for a list
123  *	      of possible modules.
124  *	         Example: OMAP3_MODULE_MMC1_ICLK or OMAP3_MODULE_GPTIMER10_FCLK.
125  *
126  *	This function can enable either a functional or interface clock.
127  *
128  *	The real work done to enable the clock is really done in the callback
129  *	function associated with the clock, this function is simply a wrapper
130  *	around that.
131  *
132  *	LOCKING:
133  *	Internally locks the driver context.
134  *
135  *	RETURNS:
136  *	Returns 0 on success or positive error code on failure.
137  */
138 int
139 ti_prcm_clk_valid(clk_ident_t clk)
140 {
141 	int ret = 0;
142 
143 	if (ti_prcm_clk_dev(clk) == NULL)
144 		ret = EINVAL;
145 
146 	return (ret);
147 }
148 
149 
150 /**
151  *	ti_prcm_clk_enable - enables a clock for a particular module
152  *	@clk: identifier for the module to enable, see ti_prcm.h for a list
153  *	      of possible modules.
154  *	         Example: OMAP3_MODULE_MMC1_ICLK or OMAP3_MODULE_GPTIMER10_FCLK.
155  *
156  *	This function can enable either a functional or interface clock.
157  *
158  *	The real work done to enable the clock is really done in the callback
159  *	function associated with the clock, this function is simply a wrapper
160  *	around that.
161  *
162  *	LOCKING:
163  *	Internally locks the driver context.
164  *
165  *	RETURNS:
166  *	Returns 0 on success or positive error code on failure.
167  */
168 int
169 ti_prcm_clk_enable(clk_ident_t clk)
170 {
171 	struct ti_clock_dev *clk_dev;
172 	int ret;
173 
174 	/* Find the clock within the devmap - it's a bit inefficent having a for
175 	 * loop for this, but this function should only called when a driver is
176 	 * being activated so IMHO not a big issue.
177 	 */
178 	clk_dev = ti_prcm_clk_dev(clk);
179 
180 	/* Sanity check we managed to find the clock */
181 	if (clk_dev == NULL)
182 		return (EINVAL);
183 
184 	/* Activate the clock */
185 	if (clk_dev->clk_activate)
186 		ret = clk_dev->clk_activate(clk_dev);
187 	else
188 		ret = EINVAL;
189 
190 	return (ret);
191 }
192 
193 
194 /**
195  *	ti_prcm_clk_disable - disables a clock for a particular module
196  *	@clk: identifier for the module to enable, see ti_prcm.h for a list
197  *	      of possible modules.
198  *	         Example: OMAP3_MODULE_MMC1_ICLK or OMAP3_MODULE_GPTIMER10_FCLK.
199  *
200  *	This function can enable either a functional or interface clock.
201  *
202  *	The real work done to enable the clock is really done in the callback
203  *	function associated with the clock, this function is simply a wrapper
204  *	around that.
205  *
206  *	LOCKING:
207  *	Internally locks the driver context.
208  *
209  *	RETURNS:
210  *	Returns 0 on success or positive error code on failure.
211  */
212 int
213 ti_prcm_clk_disable(clk_ident_t clk)
214 {
215 	struct ti_clock_dev *clk_dev;
216 	int ret;
217 
218 	/* Find the clock within the devmap - it's a bit inefficent having a for
219 	 * loop for this, but this function should only called when a driver is
220 	 * being activated so IMHO not a big issue.
221 	 */
222 	clk_dev = ti_prcm_clk_dev(clk);
223 
224 	/* Sanity check we managed to find the clock */
225 	if (clk_dev == NULL)
226 		return (EINVAL);
227 
228 	/* Activate the clock */
229 	if (clk_dev->clk_deactivate)
230 		ret = clk_dev->clk_deactivate(clk_dev);
231 	else
232 		ret = EINVAL;
233 
234 	return (ret);
235 }
236 
237 /**
238  *	ti_prcm_clk_set_source - sets the source
239  *	@clk: identifier for the module to enable, see ti_prcm.h for a list
240  *	      of possible modules.
241  *	         Example: OMAP3_MODULE_MMC1_ICLK or OMAP3_MODULE_GPTIMER10_FCLK.
242  *
243  *	This function can enable either a functional or interface clock.
244  *
245  *	The real work done to enable the clock is really done in the callback
246  *	function associated with the clock, this function is simply a wrapper
247  *	around that.
248  *
249  *	LOCKING:
250  *	Internally locks the driver context.
251  *
252  *	RETURNS:
253  *	Returns 0 on success or positive error code on failure.
254  */
255 int
256 ti_prcm_clk_set_source(clk_ident_t clk, clk_src_t clksrc)
257 {
258 	struct ti_clock_dev *clk_dev;
259 	int ret;
260 
261 	/* Find the clock within the devmap - it's a bit inefficent having a for
262 	 * loop for this, but this function should only called when a driver is
263 	 * being activated so IMHO not a big issue.
264 	 */
265 	clk_dev = ti_prcm_clk_dev(clk);
266 
267 	/* Sanity check we managed to find the clock */
268 	if (clk_dev == NULL)
269 		return (EINVAL);
270 
271 	/* Activate the clock */
272 	if (clk_dev->clk_set_source)
273 		ret = clk_dev->clk_set_source(clk_dev, clksrc);
274 	else
275 		ret = EINVAL;
276 
277 	return (ret);
278 }
279 
280 
281 /**
282  *	ti_prcm_clk_get_source_freq - gets the source clock frequency
283  *	@clk: identifier for the module to enable, see ti_prcm.h for a list
284  *	      of possible modules.
285  *	@freq: pointer to an integer that upon return will contain the src freq
286  *
287  *	This function returns the frequency of the source clock.
288  *
289  *	The real work done to enable the clock is really done in the callback
290  *	function associated with the clock, this function is simply a wrapper
291  *	around that.
292  *
293  *	LOCKING:
294  *	Internally locks the driver context.
295  *
296  *	RETURNS:
297  *	Returns 0 on success or positive error code on failure.
298  */
299 int
300 ti_prcm_clk_get_source_freq(clk_ident_t clk, unsigned int *freq)
301 {
302 	struct ti_clock_dev *clk_dev;
303 	int ret;
304 
305 	/* Find the clock within the devmap - it's a bit inefficent having a for
306 	 * loop for this, but this function should only called when a driver is
307 	 * being activated so IMHO not a big issue.
308 	 */
309 	clk_dev = ti_prcm_clk_dev(clk);
310 
311 	/* Sanity check we managed to find the clock */
312 	if (clk_dev == NULL)
313 		return (EINVAL);
314 
315 	/* Get the source frequency of the clock */
316 	if (clk_dev->clk_get_source_freq)
317 		ret = clk_dev->clk_get_source_freq(clk_dev, freq);
318 	else
319 		ret = EINVAL;
320 
321 	return (ret);
322 }
323 
324 /**
325  *	ti_prcm_clk_set_source_freq - sets the source clock frequency as close to freq as possible
326  *	@clk: identifier for the module to enable, see ti_prcm.h for a list
327  *	      of possible modules.
328  *	@freq: requested freq
329  *
330  *	LOCKING:
331  *	Internally locks the driver context.
332  *
333  *	RETURNS:
334  *	Returns 0 on success or positive error code on failure.
335  */
336 int
337 ti_prcm_clk_set_source_freq(clk_ident_t clk, unsigned int freq)
338 {
339 	struct ti_clock_dev *clk_dev;
340 	int ret;
341 
342 	clk_dev = ti_prcm_clk_dev(clk);
343 
344 	/* Sanity check we managed to find the clock */
345 	if (clk_dev == NULL)
346 		return (EINVAL);
347 
348 	/* Get the source frequency of the clock */
349 	if (clk_dev->clk_set_source_freq)
350 		ret = clk_dev->clk_set_source_freq(clk_dev, freq);
351 	else
352 		ret = EINVAL;
353 
354 	return (ret);
355 }
356