]> git.karo-electronics.de Git - mv-sheeva.git/blob - drivers/staging/tidspbridge/core/dsp-clock.c
staging: ti dspbridge: add core driver sources
[mv-sheeva.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 /*  ----------------------------------- Host OS */
20 #include <dspbridge/host_os.h>
21 #include <plat/dmtimer.h>
22 #include <plat/mcbsp.h>
23
24 /*  ----------------------------------- DSP/BIOS Bridge */
25 #include <dspbridge/std.h>
26 #include <dspbridge/dbdefs.h>
27 #include <dspbridge/cfg.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
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         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  */
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 #ifdef CONFIG_OMAP_MCBSP
150 static void mcbsp_clk_prepare(bool flag, u8 id)
151 {
152         struct cfg_hostres *resources;
153         struct dev_object *hdev_object = NULL;
154         struct bridge_dev_context *bridge_context = NULL;
155         u32 val;
156
157         hdev_object = (struct dev_object *)drv_get_first_dev_object();
158         if (!hdev_object)
159                 return;
160
161         dev_get_bridge_context(hdev_object, &bridge_context);
162         if (!bridge_context)
163                 return;
164
165         resources = bridge_context->resources;
166         if (!resources)
167                 return;
168
169         if (flag) {
170                 if (id == DSP_CLK_MCBSP1) {
171                         /* set MCBSP1_CLKS, on McBSP1 ON */
172                         val = __raw_readl(resources->dw_sys_ctrl_base + 0x274);
173                         val |= 1 << 2;
174                         __raw_writel(val, resources->dw_sys_ctrl_base + 0x274);
175                 } else if (id == DSP_CLK_MCBSP2) {
176                         /* set MCBSP2_CLKS, on McBSP2 ON */
177                         val = __raw_readl(resources->dw_sys_ctrl_base + 0x274);
178                         val |= 1 << 6;
179                         __raw_writel(val, resources->dw_sys_ctrl_base + 0x274);
180                 }
181         } else {
182                 if (id == DSP_CLK_MCBSP1) {
183                         /* clear MCBSP1_CLKS, on McBSP1 OFF */
184                         val = __raw_readl(resources->dw_sys_ctrl_base + 0x274);
185                         val &= ~(1 << 2);
186                         __raw_writel(val, resources->dw_sys_ctrl_base + 0x274);
187                 } else if (id == DSP_CLK_MCBSP2) {
188                         /* clear MCBSP2_CLKS, on McBSP2 OFF */
189                         val = __raw_readl(resources->dw_sys_ctrl_base + 0x274);
190                         val &= ~(1 << 6);
191                         __raw_writel(val, resources->dw_sys_ctrl_base + 0x274);
192                 }
193         }
194 }
195 #endif
196
197 /**
198  * dsp_gpt_wait_overflow - set gpt overflow and wait for fixed timeout
199  * @clk_id:      GP Timer clock id.
200  * @load:        Overflow value.
201  *
202  * Sets an overflow interrupt for the desired GPT waiting for a timeout
203  * of 5 msecs for the interrupt to occur.
204  */
205 void dsp_gpt_wait_overflow(short int clk_id, unsigned int load)
206 {
207         struct omap_dm_timer *gpt = timer[clk_id - 1];
208         unsigned long timeout;
209
210         if (!gpt)
211                 return;
212
213         /* Enable overflow interrupt */
214         omap_dm_timer_set_int_enable(gpt, OMAP_TIMER_INT_OVERFLOW);
215
216         /*
217          * Set counter value to overflow counter after
218          * one tick and start timer.
219          */
220         omap_dm_timer_set_load_start(gpt, 0, load);
221
222         /* Wait 80us for timer to overflow */
223         udelay(80);
224
225         timeout = msecs_to_jiffies(5);
226         /* Check interrupt status and wait for interrupt */
227         while (!(omap_dm_timer_read_status(gpt) & OMAP_TIMER_INT_OVERFLOW)) {
228                 if (time_is_after_jiffies(timeout)) {
229                         pr_err("%s: GPTimer interrupt failed\n", __func__);
230                         break;
231                 }
232         }
233 }
234
235 /*
236  *  ======== dsp_clk_enable ========
237  *  Purpose:
238  *      Enable Clock .
239  *
240  */
241 int dsp_clk_enable(IN enum dsp_clk_id clk_id)
242 {
243         int status = 0;
244
245         if (is_dsp_clk_active(dsp_clocks, clk_id)) {
246                 dev_err(bridge, "WARN: clock id %d already enabled\n", clk_id);
247                 goto out;
248         }
249
250         switch (get_clk_type(clk_id)) {
251         case IVA2_CLK:
252                 clk_enable(iva2_clk);
253                 break;
254         case GPT_CLK:
255                 timer[clk_id - 1] =
256                                 omap_dm_timer_request_specific(DMT_ID(clk_id));
257                 break;
258 #ifdef CONFIG_OMAP_MCBSP
259         case MCBSP_CLK:
260                 mcbsp_clk_prepare(true, clk_id);
261                 omap_mcbsp_set_io_type(MCBSP_ID(clk_id), OMAP_MCBSP_POLL_IO);
262                 omap_mcbsp_request(MCBSP_ID(clk_id));
263                 break;
264 #endif
265         case WDT_CLK:
266                 dev_err(bridge, "ERROR: DSP requested to enable WDT3 clk\n");
267                 break;
268         case SSI_CLK:
269                 clk_enable(ssi.sst_fck);
270                 clk_enable(ssi.ssr_fck);
271                 clk_enable(ssi.ick);
272
273                 /*
274                  * The SSI module need to configured not to have the Forced
275                  * idle for master interface. If it is set to forced idle,
276                  * the SSI module is transitioning to standby thereby causing
277                  * the client in the DSP hang waiting for the SSI module to
278                  * be active after enabling the clocks
279                  */
280                 ssi_clk_prepare(true);
281                 break;
282         default:
283                 dev_err(bridge, "Invalid clock id for enable\n");
284                 status = -EPERM;
285         }
286
287         if (DSP_SUCCEEDED(status))
288                 set_dsp_clk_active(&dsp_clocks, clk_id);
289
290 out:
291         return status;
292 }
293
294 /**
295  * dsp_clock_enable_all - Enable clocks used by the DSP
296  * @dev_context         Driver's device context strucure
297  *
298  * This function enables all the peripheral clocks that were requested by DSP.
299  */
300 u32 dsp_clock_enable_all(u32 dsp_per_clocks)
301 {
302         u32 clk_id;
303         u32 status = -EPERM;
304
305         for (clk_id = 0; clk_id < DSP_CLK_NOT_DEFINED; clk_id++) {
306                 if (is_dsp_clk_active(dsp_per_clocks, clk_id))
307                         status = dsp_clk_enable(clk_id);
308         }
309
310         return status;
311 }
312
313 /*
314  *  ======== dsp_clk_disable ========
315  *  Purpose:
316  *      Disable the clock.
317  *
318  */
319 int dsp_clk_disable(IN enum dsp_clk_id clk_id)
320 {
321         int status = 0;
322
323         if (!is_dsp_clk_active(dsp_clocks, clk_id)) {
324                 dev_err(bridge, "ERR: clock id %d already disabled\n", clk_id);
325                 goto out;
326         }
327
328         switch (get_clk_type(clk_id)) {
329         case IVA2_CLK:
330                 clk_disable(iva2_clk);
331                 break;
332         case GPT_CLK:
333                 omap_dm_timer_free(timer[clk_id - 1]);
334                 break;
335 #ifdef CONFIG_OMAP_MCBSP
336         case MCBSP_CLK:
337                 mcbsp_clk_prepare(false, clk_id);
338                 omap_mcbsp_free(MCBSP_ID(clk_id));
339                 break;
340 #endif
341         case WDT_CLK:
342                 dev_err(bridge, "ERROR: DSP requested to disable WDT3 clk\n");
343                 break;
344         case SSI_CLK:
345                 ssi_clk_prepare(false);
346                 ssi_clk_prepare(false);
347                 clk_disable(ssi.sst_fck);
348                 clk_disable(ssi.ssr_fck);
349                 clk_disable(ssi.ick);
350                 break;
351         default:
352                 dev_err(bridge, "Invalid clock id for disable\n");
353                 status = -EPERM;
354         }
355
356         if (DSP_SUCCEEDED(status))
357                 set_dsp_clk_inactive(&dsp_clocks, clk_id);
358
359 out:
360         return status;
361 }
362
363 /**
364  * dsp_clock_disable_all - Disable all active clocks
365  * @dev_context         Driver's device context structure
366  *
367  * This function disables all the peripheral clocks that were enabled by DSP.
368  * It is meant to be called only when DSP is entering hibernation or when DSP
369  * is in error state.
370  */
371 u32 dsp_clock_disable_all(u32 dsp_per_clocks)
372 {
373         u32 clk_id;
374         u32 status = -EPERM;
375
376         for (clk_id = 0; clk_id < DSP_CLK_NOT_DEFINED; clk_id++) {
377                 if (is_dsp_clk_active(dsp_per_clocks, clk_id))
378                         status = dsp_clk_disable(clk_id);
379         }
380
381         return status;
382 }
383
384 u32 dsp_clk_get_iva2_rate(void)
385 {
386         u32 clk_speed_khz;
387
388         clk_speed_khz = clk_get_rate(iva2_clk);
389         clk_speed_khz /= 1000;
390         dev_dbg(bridge, "%s: clk speed Khz = %d\n", __func__, clk_speed_khz);
391
392         return clk_speed_khz;
393 }
394
395 void ssi_clk_prepare(bool FLAG)
396 {
397         void __iomem *ssi_base;
398         unsigned int value;
399
400         ssi_base = ioremap(L4_34XX_BASE + OMAP_SSI_OFFSET, OMAP_SSI_SIZE);
401         if (!ssi_base) {
402                 pr_err("%s: error, SSI not configured\n", __func__);
403                 return;
404         }
405
406         if (FLAG) {
407                 /* Set Autoidle, SIDLEMode to smart idle, and MIDLEmode to
408                  * no idle
409                  */
410                 value = SSI_AUTOIDLE | SSI_SIDLE_SMARTIDLE | SSI_MIDLE_NOIDLE;
411         } else {
412                 /* Set Autoidle, SIDLEMode to forced idle, and MIDLEmode to
413                  * forced idle
414                  */
415                 value = SSI_AUTOIDLE;
416         }
417
418         __raw_writel(value, ssi_base + OMAP_SSI_SYSCONFIG_OFFSET);
419         iounmap(ssi_base);
420 }
421