]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - sys/arm/ti/ti_prcm.c
MFV: r363292
[FreeBSD/FreeBSD.git] / sys / arm / ti / ti_prcm.c
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 }