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