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