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