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 */
70extern struct ti_clock_dev ti_omap4_clk_devmap[];
71extern 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 */
86static struct ti_clock_dev *
87ti_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 */
140int
141ti_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 */
170int
171ti_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 */
214int
215ti_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 */
257int
258ti_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 */
301int
302ti_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 */
338int
339ti_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