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