1 /*
2  * clk.c
3  *
4  * DSP-BIOS Bridge driver support functions for TI OMAP processors.
5  *
6  * Clock and Timer services.
7  *
8  * Copyright (C) 2005-2006 Texas Instruments, Inc.
9  *
10  * This package is free software; you can redistribute it and/or modify
11  * it under the terms of the GNU General Public License version 2 as
12  * published by the Free Software Foundation.
13  *
14  * THIS PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
15  * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
16  * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
17  */
18 
19 #include <linux/types.h>
20 
21 /*  ----------------------------------- Host OS */
22 #include <dspbridge/host_os.h>
23 #include <plat/dmtimer.h>
24 #include <plat/mcbsp.h>
25 
26 /*  ----------------------------------- DSP/BIOS Bridge */
27 #include <dspbridge/dbdefs.h>
28 #include <dspbridge/drv.h>
29 #include <dspbridge/dev.h>
30 #include "_tiomap.h"
31 
32 /*  ----------------------------------- This */
33 #include <dspbridge/clk.h>
34 
35 /*  ----------------------------------- Defines, Data Structures, Typedefs */
36 
37 #define OMAP_SSI_OFFSET			0x58000
38 #define OMAP_SSI_SIZE			0x1000
39 #define OMAP_SSI_SYSCONFIG_OFFSET	0x10
40 
41 #define SSI_AUTOIDLE			(1 << 0)
42 #define SSI_SIDLE_SMARTIDLE		(2 << 3)
43 #define SSI_MIDLE_NOIDLE		(1 << 12)
44 
45 /* Clk types requested by the dsp */
46 #define IVA2_CLK	0
47 #define GPT_CLK		1
48 #define WDT_CLK		2
49 #define MCBSP_CLK	3
50 #define SSI_CLK		4
51 
52 /* Bridge GPT id (1 - 4), DM Timer id (5 - 8) */
53 #define DMT_ID(id) ((id) + 4)
54 #define DM_TIMER_CLOCKS		4
55 
56 /* Bridge MCBSP id (6 - 10), OMAP Mcbsp id (0 - 4) */
57 #define MCBSP_ID(id) ((id) - 6)
58 
59 static struct omap_dm_timer *timer[4];
60 
61 struct clk *iva2_clk;
62 
63 struct dsp_ssi {
64 	struct clk *sst_fck;
65 	struct clk *ssr_fck;
66 	struct clk *ick;
67 };
68 
69 static struct dsp_ssi ssi;
70 
71 static u32 dsp_clocks;
72 
is_dsp_clk_active(u32 clk,u8 id)73 static inline u32 is_dsp_clk_active(u32 clk, u8 id)
74 {
75 	return clk & (1 << id);
76 }
77 
set_dsp_clk_active(u32 * clk,u8 id)78 static inline void set_dsp_clk_active(u32 *clk, u8 id)
79 {
80 	*clk |= (1 << id);
81 }
82 
set_dsp_clk_inactive(u32 * clk,u8 id)83 static inline void set_dsp_clk_inactive(u32 *clk, u8 id)
84 {
85 	*clk &= ~(1 << id);
86 }
87 
get_clk_type(u8 id)88 static s8 get_clk_type(u8 id)
89 {
90 	s8 type;
91 
92 	if (id == DSP_CLK_IVA2)
93 		type = IVA2_CLK;
94 	else if (id <= DSP_CLK_GPT8)
95 		type = GPT_CLK;
96 	else if (id == DSP_CLK_WDT3)
97 		type = WDT_CLK;
98 	else if (id <= DSP_CLK_MCBSP5)
99 		type = MCBSP_CLK;
100 	else if (id == DSP_CLK_SSI)
101 		type = SSI_CLK;
102 	else
103 		type = -1;
104 
105 	return type;
106 }
107 
108 /*
109  *  ======== dsp_clk_exit ========
110  *  Purpose:
111  *      Cleanup CLK module.
112  */
dsp_clk_exit(void)113 void dsp_clk_exit(void)
114 {
115 	int i;
116 
117 	dsp_clock_disable_all(dsp_clocks);
118 
119 	for (i = 0; i < DM_TIMER_CLOCKS; i++)
120 		omap_dm_timer_free(timer[i]);
121 
122 	clk_put(iva2_clk);
123 	clk_put(ssi.sst_fck);
124 	clk_put(ssi.ssr_fck);
125 	clk_put(ssi.ick);
126 }
127 
128 /*
129  *  ======== dsp_clk_init ========
130  *  Purpose:
131  *      Initialize CLK module.
132  */
dsp_clk_init(void)133 void dsp_clk_init(void)
134 {
135 	static struct platform_device dspbridge_device;
136 	int i, id;
137 
138 	dspbridge_device.dev.bus = &platform_bus_type;
139 
140 	for (i = 0, id = 5; i < DM_TIMER_CLOCKS; i++, id++)
141 		timer[i] = omap_dm_timer_request_specific(id);
142 
143 	iva2_clk = clk_get(&dspbridge_device.dev, "iva2_ck");
144 	if (IS_ERR(iva2_clk))
145 		dev_err(bridge, "failed to get iva2 clock %p\n", iva2_clk);
146 
147 	ssi.sst_fck = clk_get(&dspbridge_device.dev, "ssi_sst_fck");
148 	ssi.ssr_fck = clk_get(&dspbridge_device.dev, "ssi_ssr_fck");
149 	ssi.ick = clk_get(&dspbridge_device.dev, "ssi_ick");
150 
151 	if (IS_ERR(ssi.sst_fck) || IS_ERR(ssi.ssr_fck) || IS_ERR(ssi.ick))
152 		dev_err(bridge, "failed to get ssi: sst %p, ssr %p, ick %p\n",
153 					ssi.sst_fck, ssi.ssr_fck, ssi.ick);
154 }
155 
156 /**
157  * dsp_gpt_wait_overflow - set gpt overflow and wait for fixed timeout
158  * @clk_id:      GP Timer clock id.
159  * @load:        Overflow value.
160  *
161  * Sets an overflow interrupt for the desired GPT waiting for a timeout
162  * of 5 msecs for the interrupt to occur.
163  */
dsp_gpt_wait_overflow(short int clk_id,unsigned int load)164 void dsp_gpt_wait_overflow(short int clk_id, unsigned int load)
165 {
166 	struct omap_dm_timer *gpt = timer[clk_id - 1];
167 	unsigned long timeout;
168 
169 	if (!gpt)
170 		return;
171 
172 	/* Enable overflow interrupt */
173 	omap_dm_timer_set_int_enable(gpt, OMAP_TIMER_INT_OVERFLOW);
174 
175 	/*
176 	 * Set counter value to overflow counter after
177 	 * one tick and start timer.
178 	 */
179 	omap_dm_timer_set_load_start(gpt, 0, load);
180 
181 	/* Wait 80us for timer to overflow */
182 	udelay(80);
183 
184 	timeout = msecs_to_jiffies(5);
185 	/* Check interrupt status and wait for interrupt */
186 	while (!(omap_dm_timer_read_status(gpt) & OMAP_TIMER_INT_OVERFLOW)) {
187 		if (time_is_after_jiffies(timeout)) {
188 			pr_err("%s: GPTimer interrupt failed\n", __func__);
189 			break;
190 		}
191 	}
192 }
193 
194 /*
195  *  ======== dsp_clk_enable ========
196  *  Purpose:
197  *      Enable Clock .
198  *
199  */
dsp_clk_enable(enum dsp_clk_id clk_id)200 int dsp_clk_enable(enum dsp_clk_id clk_id)
201 {
202 	int status = 0;
203 
204 	if (is_dsp_clk_active(dsp_clocks, clk_id)) {
205 		dev_err(bridge, "WARN: clock id %d already enabled\n", clk_id);
206 		goto out;
207 	}
208 
209 	switch (get_clk_type(clk_id)) {
210 	case IVA2_CLK:
211 		clk_enable(iva2_clk);
212 		break;
213 	case GPT_CLK:
214 		status = omap_dm_timer_start(timer[clk_id - 1]);
215 		break;
216 #ifdef CONFIG_SND_OMAP_SOC_MCBSP
217 	case MCBSP_CLK:
218 		omap_mcbsp_request(MCBSP_ID(clk_id));
219 		omap2_mcbsp_set_clks_src(MCBSP_ID(clk_id), MCBSP_CLKS_PAD_SRC);
220 		break;
221 #endif
222 	case WDT_CLK:
223 		dev_err(bridge, "ERROR: DSP requested to enable WDT3 clk\n");
224 		break;
225 	case SSI_CLK:
226 		clk_enable(ssi.sst_fck);
227 		clk_enable(ssi.ssr_fck);
228 		clk_enable(ssi.ick);
229 
230 		/*
231 		 * The SSI module need to configured not to have the Forced
232 		 * idle for master interface. If it is set to forced idle,
233 		 * the SSI module is transitioning to standby thereby causing
234 		 * the client in the DSP hang waiting for the SSI module to
235 		 * be active after enabling the clocks
236 		 */
237 		ssi_clk_prepare(true);
238 		break;
239 	default:
240 		dev_err(bridge, "Invalid clock id for enable\n");
241 		status = -EPERM;
242 	}
243 
244 	if (!status)
245 		set_dsp_clk_active(&dsp_clocks, clk_id);
246 
247 out:
248 	return status;
249 }
250 
251 /**
252  * dsp_clock_enable_all - Enable clocks used by the DSP
253  * @dev_context		Driver's device context strucure
254  *
255  * This function enables all the peripheral clocks that were requested by DSP.
256  */
dsp_clock_enable_all(u32 dsp_per_clocks)257 u32 dsp_clock_enable_all(u32 dsp_per_clocks)
258 {
259 	u32 clk_id;
260 	u32 status = -EPERM;
261 
262 	for (clk_id = 0; clk_id < DSP_CLK_NOT_DEFINED; clk_id++) {
263 		if (is_dsp_clk_active(dsp_per_clocks, clk_id))
264 			status = dsp_clk_enable(clk_id);
265 	}
266 
267 	return status;
268 }
269 
270 /*
271  *  ======== dsp_clk_disable ========
272  *  Purpose:
273  *      Disable the clock.
274  *
275  */
dsp_clk_disable(enum dsp_clk_id clk_id)276 int dsp_clk_disable(enum dsp_clk_id clk_id)
277 {
278 	int status = 0;
279 
280 	if (!is_dsp_clk_active(dsp_clocks, clk_id)) {
281 		dev_err(bridge, "ERR: clock id %d already disabled\n", clk_id);
282 		goto out;
283 	}
284 
285 	switch (get_clk_type(clk_id)) {
286 	case IVA2_CLK:
287 		clk_disable(iva2_clk);
288 		break;
289 	case GPT_CLK:
290 		status = omap_dm_timer_stop(timer[clk_id - 1]);
291 		break;
292 #ifdef CONFIG_SND_OMAP_SOC_MCBSP
293 	case MCBSP_CLK:
294 		omap2_mcbsp_set_clks_src(MCBSP_ID(clk_id), MCBSP_CLKS_PRCM_SRC);
295 		omap_mcbsp_free(MCBSP_ID(clk_id));
296 		break;
297 #endif
298 	case WDT_CLK:
299 		dev_err(bridge, "ERROR: DSP requested to disable WDT3 clk\n");
300 		break;
301 	case SSI_CLK:
302 		ssi_clk_prepare(false);
303 		ssi_clk_prepare(false);
304 		clk_disable(ssi.sst_fck);
305 		clk_disable(ssi.ssr_fck);
306 		clk_disable(ssi.ick);
307 		break;
308 	default:
309 		dev_err(bridge, "Invalid clock id for disable\n");
310 		status = -EPERM;
311 	}
312 
313 	if (!status)
314 		set_dsp_clk_inactive(&dsp_clocks, clk_id);
315 
316 out:
317 	return status;
318 }
319 
320 /**
321  * dsp_clock_disable_all - Disable all active clocks
322  * @dev_context		Driver's device context structure
323  *
324  * This function disables all the peripheral clocks that were enabled by DSP.
325  * It is meant to be called only when DSP is entering hibernation or when DSP
326  * is in error state.
327  */
dsp_clock_disable_all(u32 dsp_per_clocks)328 u32 dsp_clock_disable_all(u32 dsp_per_clocks)
329 {
330 	u32 clk_id;
331 	u32 status = -EPERM;
332 
333 	for (clk_id = 0; clk_id < DSP_CLK_NOT_DEFINED; clk_id++) {
334 		if (is_dsp_clk_active(dsp_per_clocks, clk_id))
335 			status = dsp_clk_disable(clk_id);
336 	}
337 
338 	return status;
339 }
340 
dsp_clk_get_iva2_rate(void)341 u32 dsp_clk_get_iva2_rate(void)
342 {
343 	u32 clk_speed_khz;
344 
345 	clk_speed_khz = clk_get_rate(iva2_clk);
346 	clk_speed_khz /= 1000;
347 	dev_dbg(bridge, "%s: clk speed Khz = %d\n", __func__, clk_speed_khz);
348 
349 	return clk_speed_khz;
350 }
351 
ssi_clk_prepare(bool FLAG)352 void ssi_clk_prepare(bool FLAG)
353 {
354 	void __iomem *ssi_base;
355 	unsigned int value;
356 
357 	ssi_base = ioremap(L4_34XX_BASE + OMAP_SSI_OFFSET, OMAP_SSI_SIZE);
358 	if (!ssi_base) {
359 		pr_err("%s: error, SSI not configured\n", __func__);
360 		return;
361 	}
362 
363 	if (FLAG) {
364 		/* Set Autoidle, SIDLEMode to smart idle, and MIDLEmode to
365 		 * no idle
366 		 */
367 		value = SSI_AUTOIDLE | SSI_SIDLE_SMARTIDLE | SSI_MIDLE_NOIDLE;
368 	} else {
369 		/* Set Autoidle, SIDLEMode to forced idle, and MIDLEmode to
370 		 * forced idle
371 		 */
372 		value = SSI_AUTOIDLE;
373 	}
374 
375 	__raw_writel(value, ssi_base + OMAP_SSI_SYSCONFIG_OFFSET);
376 	iounmap(ssi_base);
377 }
378 
379