4 * DSP-BIOS Bridge driver support functions for TI OMAP processors.
6 * Implementation of DSP wake/sleep routines.
8 * Copyright (C) 2007-2008 Texas Instruments, Inc.
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.
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.
19 /* ----------------------------------- Host OS */
20 #include <dspbridge/host_os.h>
22 #include <linux/platform_data/dsp-omap.h>
24 /* ----------------------------------- DSP/BIOS Bridge */
25 #include <dspbridge/dbdefs.h>
26 #include <dspbridge/drv.h>
27 #include <dspbridge/io_sm.h>
29 /* ----------------------------------- Platform Manager */
30 #include <dspbridge/brddefs.h>
31 #include <dspbridge/dev.h>
32 #include <dspbridge/io.h>
34 /* ------------------------------------ Hardware Abstraction Layer */
38 #include <dspbridge/pwr.h>
40 /* ----------------------------------- Bridge Driver */
41 #include <dspbridge/dspdeh.h>
42 #include <dspbridge/wdt.h>
44 /* ----------------------------------- specific to this file */
46 #include "_tiomap_pwr.h"
47 #include <mach-omap2/prm-regbits-34xx.h>
48 #include <mach-omap2/cm-regbits-34xx.h>
50 #define PWRSTST_TIMEOUT 200
53 * ======== handle_constraints_set ========
54 * Sets new DSP constraint
56 int handle_constraints_set(struct bridge_dev_context *dev_context,
59 #ifdef CONFIG_TIDSPBRIDGE_DVFS
61 struct omap_dsp_platform_data *pdata =
62 omap_dspbridge_dev->dev.platform_data;
64 constraint_val = (u32 *) (pargs);
65 /* Read the target value requested by DSP */
66 dev_dbg(bridge, "OPP: %s opp requested = 0x%x\n", __func__,
67 (u32) *(constraint_val + 1));
69 /* Set the new opp value */
70 if (pdata->dsp_set_min_opp)
71 (*pdata->dsp_set_min_opp) ((u32) *(constraint_val + 1));
72 #endif /* #ifdef CONFIG_TIDSPBRIDGE_DVFS */
77 * ======== handle_hibernation_from_dsp ========
78 * Handle Hibernation requested from DSP
80 int handle_hibernation_from_dsp(struct bridge_dev_context *dev_context)
84 u16 timeout = PWRSTST_TIMEOUT / 10;
86 #ifdef CONFIG_TIDSPBRIDGE_DVFS
88 struct io_mgr *hio_mgr;
90 struct omap_dsp_platform_data *pdata =
91 omap_dspbridge_dev->dev.platform_data;
93 pwr_state = (*pdata->dsp_prm_read)(OMAP3430_IVA2_MOD, OMAP2_PM_PWSTST) &
94 OMAP_POWERSTATEST_MASK;
95 /* Wait for DSP to move into OFF state */
96 while ((pwr_state != PWRDM_POWER_OFF) && --timeout) {
97 if (msleep_interruptible(10)) {
98 pr_err("Waiting for DSP OFF mode interrupted\n");
101 pwr_state = (*pdata->dsp_prm_read)(OMAP3430_IVA2_MOD,
102 OMAP2_PM_PWSTST) & OMAP_POWERSTATEST_MASK;
105 pr_err("%s: Timed out waiting for DSP off mode\n", __func__);
110 /* Save mailbox settings */
111 omap_mbox_save_ctx(dev_context->mbox);
113 /* Turn off DSP Peripheral clocks and DSP Load monitor timer */
114 status = dsp_clock_disable_all(dev_context->dsp_per_clks);
116 /* Disable wdt on hibernation. */
117 dsp_wdt_enable(false);
120 /* Update the Bridger Driver state */
121 dev_context->brd_state = BRD_DSP_HIBERNATION;
122 #ifdef CONFIG_TIDSPBRIDGE_DVFS
124 dev_get_io_mgr(dev_context->dev_obj, &hio_mgr);
126 status = DSP_EHANDLE;
129 io_sh_msetting(hio_mgr, SHM_GETOPP, &opplevel);
132 * Set the OPP to low level before moving to OFF
135 if (pdata->dsp_set_min_opp)
136 (*pdata->dsp_set_min_opp) (VDD1_OPP1);
138 #endif /* CONFIG_TIDSPBRIDGE_DVFS */
146 * ======== sleep_dsp ========
147 * Put DSP in low power consuming state.
149 int sleep_dsp(struct bridge_dev_context *dev_context, u32 dw_cmd,
154 #ifdef CONFIG_TIDSPBRIDGE_NTFY_PWRERR
155 struct deh_mgr *hdeh_mgr;
156 #endif /* CONFIG_TIDSPBRIDGE_NTFY_PWRERR */
157 u16 timeout = PWRSTST_TIMEOUT / 10;
158 u32 pwr_state, target_pwr_state;
159 struct omap_dsp_platform_data *pdata =
160 omap_dspbridge_dev->dev.platform_data;
162 /* Check if sleep code is valid */
163 if ((dw_cmd != PWR_DEEPSLEEP) && (dw_cmd != PWR_EMERGENCYDEEPSLEEP))
166 switch (dev_context->brd_state) {
168 omap_mbox_save_ctx(dev_context->mbox);
169 if (dsp_test_sleepstate == PWRDM_POWER_OFF) {
170 sm_interrupt_dsp(dev_context, MBX_PM_DSPHIBERNATE);
171 dev_dbg(bridge, "PM: %s - sent hibernate cmd to DSP\n",
173 target_pwr_state = PWRDM_POWER_OFF;
175 sm_interrupt_dsp(dev_context, MBX_PM_DSPRETENTION);
176 target_pwr_state = PWRDM_POWER_RET;
180 omap_mbox_save_ctx(dev_context->mbox);
181 if (dsp_test_sleepstate == PWRDM_POWER_OFF) {
182 sm_interrupt_dsp(dev_context, MBX_PM_DSPHIBERNATE);
183 target_pwr_state = PWRDM_POWER_OFF;
187 case BRD_HIBERNATION:
188 case BRD_DSP_HIBERNATION:
189 /* Already in Hibernation, so just return */
190 dev_dbg(bridge, "PM: %s - DSP already in hibernation\n",
194 dev_dbg(bridge, "PM: %s - Board in STOP state\n", __func__);
197 dev_dbg(bridge, "PM: %s - Bridge in Illegal state\n", __func__);
201 /* Get the PRCM DSP power domain status */
202 pwr_state = (*pdata->dsp_prm_read)(OMAP3430_IVA2_MOD, OMAP2_PM_PWSTST) &
203 OMAP_POWERSTATEST_MASK;
205 /* Wait for DSP to move into target power state */
206 while ((pwr_state != target_pwr_state) && --timeout) {
207 if (msleep_interruptible(10)) {
208 pr_err("Waiting for DSP to Suspend interrupted\n");
211 pwr_state = (*pdata->dsp_prm_read)(OMAP3430_IVA2_MOD,
212 OMAP2_PM_PWSTST) & OMAP_POWERSTATEST_MASK;
216 pr_err("%s: Timed out waiting for DSP off mode, state %x\n",
217 __func__, pwr_state);
218 #ifdef CONFIG_TIDSPBRIDGE_NTFY_PWRERR
219 dev_get_deh_mgr(dev_context->dev_obj, &hdeh_mgr);
220 bridge_deh_notify(hdeh_mgr, DSP_PWRERROR, 0);
221 #endif /* CONFIG_TIDSPBRIDGE_NTFY_PWRERR */
224 /* Update the Bridger Driver state */
225 if (dsp_test_sleepstate == PWRDM_POWER_OFF)
226 dev_context->brd_state = BRD_HIBERNATION;
228 dev_context->brd_state = BRD_RETENTION;
230 /* Disable wdt on hibernation. */
231 dsp_wdt_enable(false);
233 /* Turn off DSP Peripheral clocks */
234 status = dsp_clock_disable_all(dev_context->dsp_per_clks);
237 #ifdef CONFIG_TIDSPBRIDGE_DVFS
238 else if (target_pwr_state == PWRDM_POWER_OFF) {
240 * Set the OPP to low level before moving to OFF mode
242 if (pdata->dsp_set_min_opp)
243 (*pdata->dsp_set_min_opp) (VDD1_OPP1);
245 #endif /* CONFIG_TIDSPBRIDGE_DVFS */
247 #endif /* CONFIG_PM */
252 * ======== wake_dsp ========
253 * Wake up DSP from sleep.
255 int wake_dsp(struct bridge_dev_context *dev_context, void *pargs)
260 /* Check the board state, if it is not 'SLEEP' then return */
261 if (dev_context->brd_state == BRD_RUNNING ||
262 dev_context->brd_state == BRD_STOPPED) {
263 /* The Device is in 'RET' or 'OFF' state and Bridge state is not
264 * 'SLEEP', this means state inconsistency, so return */
268 /* Send a wakeup message to DSP */
269 sm_interrupt_dsp(dev_context, MBX_PM_DSPWAKEUP);
271 /* Set the device state to RUNNIG */
272 dev_context->brd_state = BRD_RUNNING;
273 #endif /* CONFIG_PM */
278 * ======== dsp_peripheral_clk_ctrl ========
279 * Enable/Disable the DSP peripheral clocks as needed..
281 int dsp_peripheral_clk_ctrl(struct bridge_dev_context *dev_context,
287 u32 clk_id_index = MBX_PM_MAX_RESOURCES;
289 u32 dsp_per_clks_before;
292 dsp_per_clks_before = dev_context->dsp_per_clks;
294 ext_clk = (u32) *((u32 *) pargs);
295 ext_clk_id = ext_clk & MBX_PM_CLK_IDMASK;
297 /* process the power message -- TODO, keep it in a separate function */
298 for (tmp_index = 0; tmp_index < MBX_PM_MAX_RESOURCES; tmp_index++) {
299 if (ext_clk_id == bpwr_clkid[tmp_index]) {
300 clk_id_index = tmp_index;
304 /* TODO -- Assert may be a too hard restriction here.. May be we should
305 * just return with failure when the CLK ID does not match */
306 if (clk_id_index == MBX_PM_MAX_RESOURCES) {
307 /* return with a more meaningfull error code */
310 ext_clk_cmd = (ext_clk >> MBX_PM_CLK_CMDSHIFT) & MBX_PM_CLK_CMDMASK;
311 switch (ext_clk_cmd) {
312 case BPWR_DISABLE_CLOCK:
313 status = dsp_clk_disable(bpwr_clks[clk_id_index].clk);
314 dsp_clk_wakeup_event_ctrl(bpwr_clks[clk_id_index].clk_id,
317 (dev_context->dsp_per_clks) &=
318 (~((u32) (1 << bpwr_clks[clk_id_index].clk)));
321 case BPWR_ENABLE_CLOCK:
322 status = dsp_clk_enable(bpwr_clks[clk_id_index].clk);
323 dsp_clk_wakeup_event_ctrl(bpwr_clks[clk_id_index].clk_id, true);
325 (dev_context->dsp_per_clks) |=
326 (1 << bpwr_clks[clk_id_index].clk);
329 dev_dbg(bridge, "%s: Unsupported CMD\n", __func__);
330 /* unsupported cmd */
331 /* TODO -- provide support for AUTOIDLE Enable/Disable
338 * ========pre_scale_dsp========
339 * Sends prescale notification to DSP
342 int pre_scale_dsp(struct bridge_dev_context *dev_context, void *pargs)
344 #ifdef CONFIG_TIDSPBRIDGE_DVFS
348 voltage_domain = *((u32 *) pargs);
349 level = *((u32 *) pargs + 1);
351 dev_dbg(bridge, "OPP: %s voltage_domain = %x, level = 0x%x\n",
352 __func__, voltage_domain, level);
353 if ((dev_context->brd_state == BRD_HIBERNATION) ||
354 (dev_context->brd_state == BRD_RETENTION) ||
355 (dev_context->brd_state == BRD_DSP_HIBERNATION)) {
356 dev_dbg(bridge, "OPP: %s IVA in sleep. No message to DSP\n");
358 } else if ((dev_context->brd_state == BRD_RUNNING)) {
359 /* Send a prenotification to DSP */
360 dev_dbg(bridge, "OPP: %s sent notification to DSP\n", __func__);
361 sm_interrupt_dsp(dev_context, MBX_PM_SETPOINT_PRENOTIFY);
366 #endif /* #ifdef CONFIG_TIDSPBRIDGE_DVFS */
371 * ========post_scale_dsp========
372 * Sends postscale notification to DSP
375 int post_scale_dsp(struct bridge_dev_context *dev_context,
379 #ifdef CONFIG_TIDSPBRIDGE_DVFS
382 struct io_mgr *hio_mgr;
384 status = dev_get_io_mgr(dev_context->dev_obj, &hio_mgr);
388 voltage_domain = *((u32 *) pargs);
389 level = *((u32 *) pargs + 1);
390 dev_dbg(bridge, "OPP: %s voltage_domain = %x, level = 0x%x\n",
391 __func__, voltage_domain, level);
392 if ((dev_context->brd_state == BRD_HIBERNATION) ||
393 (dev_context->brd_state == BRD_RETENTION) ||
394 (dev_context->brd_state == BRD_DSP_HIBERNATION)) {
395 /* Update the OPP value in shared memory */
396 io_sh_msetting(hio_mgr, SHM_CURROPP, &level);
397 dev_dbg(bridge, "OPP: %s IVA in sleep. Wrote to shm\n",
399 } else if ((dev_context->brd_state == BRD_RUNNING)) {
400 /* Update the OPP value in shared memory */
401 io_sh_msetting(hio_mgr, SHM_CURROPP, &level);
402 /* Send a post notification to DSP */
403 sm_interrupt_dsp(dev_context, MBX_PM_SETPOINT_POSTNOTIFY);
404 dev_dbg(bridge, "OPP: %s wrote to shm. Sent post notification "
405 "to DSP\n", __func__);
409 #endif /* #ifdef CONFIG_TIDSPBRIDGE_DVFS */
413 void dsp_clk_wakeup_event_ctrl(u32 clock_id, bool enable)
415 struct cfg_hostres *resources;
419 struct dev_object *hdev_object = NULL;
420 struct bridge_dev_context *bridge_context = NULL;
422 hdev_object = (struct dev_object *)drv_get_first_dev_object();
426 status = dev_get_bridge_context(hdev_object, &bridge_context);
430 resources = bridge_context->resources;
436 iva2_grpsel = readl(resources->per_pm_base + 0xA8);
437 mpu_grpsel = readl(resources->per_pm_base + 0xA4);
439 iva2_grpsel |= OMAP3430_GRPSEL_GPT5_MASK;
440 mpu_grpsel &= ~OMAP3430_GRPSEL_GPT5_MASK;
442 mpu_grpsel |= OMAP3430_GRPSEL_GPT5_MASK;
443 iva2_grpsel &= ~OMAP3430_GRPSEL_GPT5_MASK;
445 writel(iva2_grpsel, resources->per_pm_base + 0xA8);
446 writel(mpu_grpsel, resources->per_pm_base + 0xA4);
449 iva2_grpsel = readl(resources->per_pm_base + 0xA8);
450 mpu_grpsel = readl(resources->per_pm_base + 0xA4);
452 iva2_grpsel |= OMAP3430_GRPSEL_GPT6_MASK;
453 mpu_grpsel &= ~OMAP3430_GRPSEL_GPT6_MASK;
455 mpu_grpsel |= OMAP3430_GRPSEL_GPT6_MASK;
456 iva2_grpsel &= ~OMAP3430_GRPSEL_GPT6_MASK;
458 writel(iva2_grpsel, resources->per_pm_base + 0xA8);
459 writel(mpu_grpsel, resources->per_pm_base + 0xA4);
462 iva2_grpsel = readl(resources->per_pm_base + 0xA8);
463 mpu_grpsel = readl(resources->per_pm_base + 0xA4);
465 iva2_grpsel |= OMAP3430_GRPSEL_GPT7_MASK;
466 mpu_grpsel &= ~OMAP3430_GRPSEL_GPT7_MASK;
468 mpu_grpsel |= OMAP3430_GRPSEL_GPT7_MASK;
469 iva2_grpsel &= ~OMAP3430_GRPSEL_GPT7_MASK;
471 writel(iva2_grpsel, resources->per_pm_base + 0xA8);
472 writel(mpu_grpsel, resources->per_pm_base + 0xA4);
475 iva2_grpsel = readl(resources->per_pm_base + 0xA8);
476 mpu_grpsel = readl(resources->per_pm_base + 0xA4);
478 iva2_grpsel |= OMAP3430_GRPSEL_GPT8_MASK;
479 mpu_grpsel &= ~OMAP3430_GRPSEL_GPT8_MASK;
481 mpu_grpsel |= OMAP3430_GRPSEL_GPT8_MASK;
482 iva2_grpsel &= ~OMAP3430_GRPSEL_GPT8_MASK;
484 writel(iva2_grpsel, resources->per_pm_base + 0xA8);
485 writel(mpu_grpsel, resources->per_pm_base + 0xA4);
488 iva2_grpsel = readl(resources->core_pm_base + 0xA8);
489 mpu_grpsel = readl(resources->core_pm_base + 0xA4);
491 iva2_grpsel |= OMAP3430_GRPSEL_MCBSP1_MASK;
492 mpu_grpsel &= ~OMAP3430_GRPSEL_MCBSP1_MASK;
494 mpu_grpsel |= OMAP3430_GRPSEL_MCBSP1_MASK;
495 iva2_grpsel &= ~OMAP3430_GRPSEL_MCBSP1_MASK;
497 writel(iva2_grpsel, resources->core_pm_base + 0xA8);
498 writel(mpu_grpsel, resources->core_pm_base + 0xA4);
501 iva2_grpsel = readl(resources->per_pm_base + 0xA8);
502 mpu_grpsel = readl(resources->per_pm_base + 0xA4);
504 iva2_grpsel |= OMAP3430_GRPSEL_MCBSP2_MASK;
505 mpu_grpsel &= ~OMAP3430_GRPSEL_MCBSP2_MASK;
507 mpu_grpsel |= OMAP3430_GRPSEL_MCBSP2_MASK;
508 iva2_grpsel &= ~OMAP3430_GRPSEL_MCBSP2_MASK;
510 writel(iva2_grpsel, resources->per_pm_base + 0xA8);
511 writel(mpu_grpsel, resources->per_pm_base + 0xA4);
514 iva2_grpsel = readl(resources->per_pm_base + 0xA8);
515 mpu_grpsel = readl(resources->per_pm_base + 0xA4);
517 iva2_grpsel |= OMAP3430_GRPSEL_MCBSP3_MASK;
518 mpu_grpsel &= ~OMAP3430_GRPSEL_MCBSP3_MASK;
520 mpu_grpsel |= OMAP3430_GRPSEL_MCBSP3_MASK;
521 iva2_grpsel &= ~OMAP3430_GRPSEL_MCBSP3_MASK;
523 writel(iva2_grpsel, resources->per_pm_base + 0xA8);
524 writel(mpu_grpsel, resources->per_pm_base + 0xA4);
527 iva2_grpsel = readl(resources->per_pm_base + 0xA8);
528 mpu_grpsel = readl(resources->per_pm_base + 0xA4);
530 iva2_grpsel |= OMAP3430_GRPSEL_MCBSP4_MASK;
531 mpu_grpsel &= ~OMAP3430_GRPSEL_MCBSP4_MASK;
533 mpu_grpsel |= OMAP3430_GRPSEL_MCBSP4_MASK;
534 iva2_grpsel &= ~OMAP3430_GRPSEL_MCBSP4_MASK;
536 writel(iva2_grpsel, resources->per_pm_base + 0xA8);
537 writel(mpu_grpsel, resources->per_pm_base + 0xA4);
540 iva2_grpsel = readl(resources->per_pm_base + 0xA8);
541 mpu_grpsel = readl(resources->per_pm_base + 0xA4);
543 iva2_grpsel |= OMAP3430_GRPSEL_MCBSP5_MASK;
544 mpu_grpsel &= ~OMAP3430_GRPSEL_MCBSP5_MASK;
546 mpu_grpsel |= OMAP3430_GRPSEL_MCBSP5_MASK;
547 iva2_grpsel &= ~OMAP3430_GRPSEL_MCBSP5_MASK;
549 writel(iva2_grpsel, resources->per_pm_base + 0xA8);
550 writel(mpu_grpsel, resources->per_pm_base + 0xA4);