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: stable/12/sys/arm/ti/ti_prcm.c 326258 2017-11-27 15:04:10Z pfg $");
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 *
ti_prcm_clk_dev(clk_ident_t clk)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
ti_prcm_clk_valid(clk_ident_t clk)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
ti_prcm_clk_enable(clk_ident_t clk)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
ti_prcm_clk_disable(clk_ident_t clk)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
ti_prcm_clk_set_source(clk_ident_t clk,clk_src_t clksrc)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
ti_prcm_clk_get_source_freq(clk_ident_t clk,unsigned int * freq)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
ti_prcm_clk_set_source_freq(clk_ident_t clk,unsigned int freq)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