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 /* ----------------------------------- DSP/BIOS Bridge */
23 #include <dspbridge/dbdefs.h>
24 #include <dspbridge/drv.h>
25 #include <dspbridge/io_sm.h>
27 /* ----------------------------------- Platform Manager */
28 #include <dspbridge/brddefs.h>
29 #include <dspbridge/dev.h>
30 #include <dspbridge/iodefs.h>
32 /* ------------------------------------ Hardware Abstraction Layer */
36 #include <dspbridge/pwr_sh.h>
38 /* ----------------------------------- Bridge Driver */
39 #include <dspbridge/dspdeh.h>
40 #include <dspbridge/wdt.h>
42 /* ----------------------------------- specific to this file */
44 #include "_tiomap_pwr.h"
45 #include <mach-omap2/prm-regbits-34xx.h>
46 #include <mach-omap2/cm-regbits-34xx.h>
48 #define PWRSTST_TIMEOUT 200
51 * ======== handle_constraints_set ========
52 * Sets new DSP constraint
54 int handle_constraints_set(struct bridge_dev_context *dev_context,
57 #ifdef CONFIG_TIDSPBRIDGE_DVFS
59 struct dspbridge_platform_data *pdata =
60 omap_dspbridge_dev->dev.platform_data;
62 constraint_val = (u32 *) (pargs);
63 /* Read the target value requested by DSP */
64 dev_dbg(bridge, "OPP: %s opp requested = 0x%x\n", __func__,
65 (u32) *(constraint_val + 1));
67 /* Set the new opp value */
68 if (pdata->dsp_set_min_opp)
69 (*pdata->dsp_set_min_opp) ((u32) *(constraint_val + 1));
70 #endif /* #ifdef CONFIG_TIDSPBRIDGE_DVFS */
75 * ======== handle_hibernation_from_dsp ========
76 * Handle Hibernation requested from DSP
78 int handle_hibernation_from_dsp(struct bridge_dev_context *dev_context)
82 u16 timeout = PWRSTST_TIMEOUT / 10;
84 #ifdef CONFIG_TIDSPBRIDGE_DVFS
86 struct io_mgr *hio_mgr;
88 struct dspbridge_platform_data *pdata =
89 omap_dspbridge_dev->dev.platform_data;
91 pwr_state = (*pdata->dsp_prm_read)(OMAP3430_IVA2_MOD, OMAP2_PM_PWSTST) &
92 OMAP_POWERSTATEST_MASK;
93 /* Wait for DSP to move into OFF state */
94 while ((pwr_state != PWRDM_POWER_OFF) && --timeout) {
95 if (msleep_interruptible(10)) {
96 pr_err("Waiting for DSP OFF mode interrupted\n");
99 pwr_state = (*pdata->dsp_prm_read)(OMAP3430_IVA2_MOD,
100 OMAP2_PM_PWSTST) & OMAP_POWERSTATEST_MASK;
103 pr_err("%s: Timed out waiting for DSP off mode\n", __func__);
108 /* Save mailbox settings */
109 omap_mbox_save_ctx(dev_context->mbox);
111 /* Turn off DSP Peripheral clocks and DSP Load monitor timer */
112 status = dsp_clock_disable_all(dev_context->dsp_per_clks);
114 /* Disable wdt on hibernation. */
115 dsp_wdt_enable(false);
118 /* Update the Bridger Driver state */
119 dev_context->dw_brd_state = BRD_DSP_HIBERNATION;
120 #ifdef CONFIG_TIDSPBRIDGE_DVFS
122 dev_get_io_mgr(dev_context->hdev_obj, &hio_mgr);
124 status = DSP_EHANDLE;
127 io_sh_msetting(hio_mgr, SHM_GETOPP, &opplevel);
130 * Set the OPP to low level before moving to OFF
133 if (pdata->dsp_set_min_opp)
134 (*pdata->dsp_set_min_opp) (VDD1_OPP1);
136 #endif /* CONFIG_TIDSPBRIDGE_DVFS */
144 * ======== sleep_dsp ========
145 * Put DSP in low power consuming state.
147 int sleep_dsp(struct bridge_dev_context *dev_context, u32 dw_cmd,
152 #ifdef CONFIG_TIDSPBRIDGE_NTFY_PWRERR
153 struct deh_mgr *hdeh_mgr;
154 #endif /* CONFIG_TIDSPBRIDGE_NTFY_PWRERR */
155 u16 timeout = PWRSTST_TIMEOUT / 10;
156 u32 pwr_state, target_pwr_state;
157 struct dspbridge_platform_data *pdata =
158 omap_dspbridge_dev->dev.platform_data;
160 /* Check if sleep code is valid */
161 if ((dw_cmd != PWR_DEEPSLEEP) && (dw_cmd != PWR_EMERGENCYDEEPSLEEP))
164 switch (dev_context->dw_brd_state) {
166 omap_mbox_save_ctx(dev_context->mbox);
167 if (dsp_test_sleepstate == PWRDM_POWER_OFF) {
168 sm_interrupt_dsp(dev_context, MBX_PM_DSPHIBERNATE);
169 dev_dbg(bridge, "PM: %s - sent hibernate cmd to DSP\n",
171 target_pwr_state = PWRDM_POWER_OFF;
173 sm_interrupt_dsp(dev_context, MBX_PM_DSPRETENTION);
174 target_pwr_state = PWRDM_POWER_RET;
178 omap_mbox_save_ctx(dev_context->mbox);
179 if (dsp_test_sleepstate == PWRDM_POWER_OFF) {
180 sm_interrupt_dsp(dev_context, MBX_PM_DSPHIBERNATE);
181 target_pwr_state = PWRDM_POWER_OFF;
185 case BRD_HIBERNATION:
186 case BRD_DSP_HIBERNATION:
187 /* Already in Hibernation, so just return */
188 dev_dbg(bridge, "PM: %s - DSP already in hibernation\n",
192 dev_dbg(bridge, "PM: %s - Board in STOP state\n", __func__);
195 dev_dbg(bridge, "PM: %s - Bridge in Illegal state\n", __func__);
199 /* Get the PRCM DSP power domain status */
200 pwr_state = (*pdata->dsp_prm_read)(OMAP3430_IVA2_MOD, OMAP2_PM_PWSTST) &
201 OMAP_POWERSTATEST_MASK;
203 /* Wait for DSP to move into target power state */
204 while ((pwr_state != target_pwr_state) && --timeout) {
205 if (msleep_interruptible(10)) {
206 pr_err("Waiting for DSP to Suspend interrupted\n");
209 pwr_state = (*pdata->dsp_prm_read)(OMAP3430_IVA2_MOD,
210 OMAP2_PM_PWSTST) & OMAP_POWERSTATEST_MASK;
214 pr_err("%s: Timed out waiting for DSP off mode, state %x\n",
215 __func__, pwr_state);
216 #ifdef CONFIG_TIDSPBRIDGE_NTFY_PWRERR
217 dev_get_deh_mgr(dev_context->hdev_obj, &hdeh_mgr);
218 bridge_deh_notify(hdeh_mgr, DSP_PWRERROR, 0);
219 #endif /* CONFIG_TIDSPBRIDGE_NTFY_PWRERR */
222 /* Update the Bridger Driver state */
223 if (dsp_test_sleepstate == PWRDM_POWER_OFF)
224 dev_context->dw_brd_state = BRD_HIBERNATION;
226 dev_context->dw_brd_state = BRD_RETENTION;
228 /* Disable wdt on hibernation. */
229 dsp_wdt_enable(false);
231 /* Turn off DSP Peripheral clocks */
232 status = dsp_clock_disable_all(dev_context->dsp_per_clks);
235 #ifdef CONFIG_TIDSPBRIDGE_DVFS
236 else if (target_pwr_state == PWRDM_POWER_OFF) {
238 * Set the OPP to low level before moving to OFF mode
240 if (pdata->dsp_set_min_opp)
241 (*pdata->dsp_set_min_opp) (VDD1_OPP1);
243 #endif /* CONFIG_TIDSPBRIDGE_DVFS */
245 #endif /* CONFIG_PM */
250 * ======== wake_dsp ========
251 * Wake up DSP from sleep.
253 int wake_dsp(struct bridge_dev_context *dev_context, void *pargs)
258 /* Check the board state, if it is not 'SLEEP' then return */
259 if (dev_context->dw_brd_state == BRD_RUNNING ||
260 dev_context->dw_brd_state == BRD_STOPPED) {
261 /* The Device is in 'RET' or 'OFF' state and Bridge state is not
262 * 'SLEEP', this means state inconsistency, so return */
266 /* Send a wakeup message to DSP */
267 sm_interrupt_dsp(dev_context, MBX_PM_DSPWAKEUP);
269 /* Set the device state to RUNNIG */
270 dev_context->dw_brd_state = BRD_RUNNING;
271 #endif /* CONFIG_PM */
276 * ======== dsp_peripheral_clk_ctrl ========
277 * Enable/Disable the DSP peripheral clocks as needed..
279 int dsp_peripheral_clk_ctrl(struct bridge_dev_context *dev_context,
285 u32 clk_id_index = MBX_PM_MAX_RESOURCES;
287 u32 dsp_per_clks_before;
290 dsp_per_clks_before = dev_context->dsp_per_clks;
292 ext_clk = (u32) *((u32 *) pargs);
293 ext_clk_id = ext_clk & MBX_PM_CLK_IDMASK;
295 /* process the power message -- TODO, keep it in a separate function */
296 for (tmp_index = 0; tmp_index < MBX_PM_MAX_RESOURCES; tmp_index++) {
297 if (ext_clk_id == bpwr_clkid[tmp_index]) {
298 clk_id_index = tmp_index;
302 /* TODO -- Assert may be a too hard restriction here.. May be we should
303 * just return with failure when the CLK ID does not match */
304 /* DBC_ASSERT(clk_id_index < MBX_PM_MAX_RESOURCES); */
305 if (clk_id_index == MBX_PM_MAX_RESOURCES) {
306 /* return with a more meaningfull error code */
309 ext_clk_cmd = (ext_clk >> MBX_PM_CLK_CMDSHIFT) & MBX_PM_CLK_CMDMASK;
310 switch (ext_clk_cmd) {
311 case BPWR_DISABLE_CLOCK:
312 status = dsp_clk_disable(bpwr_clks[clk_id_index].clk);
313 dsp_clk_wakeup_event_ctrl(bpwr_clks[clk_id_index].clk_id,
316 (dev_context->dsp_per_clks) &=
317 (~((u32) (1 << bpwr_clks[clk_id_index].clk)));
320 case BPWR_ENABLE_CLOCK:
321 status = dsp_clk_enable(bpwr_clks[clk_id_index].clk);
322 dsp_clk_wakeup_event_ctrl(bpwr_clks[clk_id_index].clk_id, true);
324 (dev_context->dsp_per_clks) |=
325 (1 << bpwr_clks[clk_id_index].clk);
328 dev_dbg(bridge, "%s: Unsupported CMD\n", __func__);
329 /* unsupported cmd */
330 /* TODO -- provide support for AUTOIDLE Enable/Disable
337 * ========pre_scale_dsp========
338 * Sends prescale notification to DSP
341 int pre_scale_dsp(struct bridge_dev_context *dev_context, void *pargs)
343 #ifdef CONFIG_TIDSPBRIDGE_DVFS
347 voltage_domain = *((u32 *) pargs);
348 level = *((u32 *) pargs + 1);
350 dev_dbg(bridge, "OPP: %s voltage_domain = %x, level = 0x%x\n",
351 __func__, voltage_domain, level);
352 if ((dev_context->dw_brd_state == BRD_HIBERNATION) ||
353 (dev_context->dw_brd_state == BRD_RETENTION) ||
354 (dev_context->dw_brd_state == BRD_DSP_HIBERNATION)) {
355 dev_dbg(bridge, "OPP: %s IVA in sleep. No message to DSP\n");
357 } else if ((dev_context->dw_brd_state == BRD_RUNNING)) {
358 /* Send a prenotificatio to DSP */
359 dev_dbg(bridge, "OPP: %s sent notification to DSP\n", __func__);
360 sm_interrupt_dsp(dev_context, MBX_PM_SETPOINT_PRENOTIFY);
365 #endif /* #ifdef CONFIG_TIDSPBRIDGE_DVFS */
370 * ========post_scale_dsp========
371 * Sends postscale notification to DSP
374 int post_scale_dsp(struct bridge_dev_context *dev_context,
378 #ifdef CONFIG_TIDSPBRIDGE_DVFS
381 struct io_mgr *hio_mgr;
383 status = dev_get_io_mgr(dev_context->hdev_obj, &hio_mgr);
387 voltage_domain = *((u32 *) pargs);
388 level = *((u32 *) pargs + 1);
389 dev_dbg(bridge, "OPP: %s voltage_domain = %x, level = 0x%x\n",
390 __func__, voltage_domain, level);
391 if ((dev_context->dw_brd_state == BRD_HIBERNATION) ||
392 (dev_context->dw_brd_state == BRD_RETENTION) ||
393 (dev_context->dw_brd_state == BRD_DSP_HIBERNATION)) {
394 /* Update the OPP value in shared memory */
395 io_sh_msetting(hio_mgr, SHM_CURROPP, &level);
396 dev_dbg(bridge, "OPP: %s IVA in sleep. Wrote to shm\n",
398 } else if ((dev_context->dw_brd_state == BRD_RUNNING)) {
399 /* Update the OPP value in shared memory */
400 io_sh_msetting(hio_mgr, SHM_CURROPP, &level);
401 /* Send a post notification to DSP */
402 sm_interrupt_dsp(dev_context, MBX_PM_SETPOINT_POSTNOTIFY);
403 dev_dbg(bridge, "OPP: %s wrote to shm. Sent post notification "
404 "to DSP\n", __func__);
408 #endif /* #ifdef CONFIG_TIDSPBRIDGE_DVFS */
412 void dsp_clk_wakeup_event_ctrl(u32 clock_id, bool enable)
414 struct cfg_hostres *resources;
418 struct dev_object *hdev_object = NULL;
419 struct bridge_dev_context *bridge_context = NULL;
421 hdev_object = (struct dev_object *)drv_get_first_dev_object();
425 status = dev_get_bridge_context(hdev_object, &bridge_context);
429 resources = bridge_context->resources;
435 iva2_grpsel = readl(resources->dw_per_pm_base + 0xA8);
436 mpu_grpsel = readl(resources->dw_per_pm_base + 0xA4);
438 iva2_grpsel |= OMAP3430_GRPSEL_GPT5_MASK;
439 mpu_grpsel &= ~OMAP3430_GRPSEL_GPT5_MASK;
441 mpu_grpsel |= OMAP3430_GRPSEL_GPT5_MASK;
442 iva2_grpsel &= ~OMAP3430_GRPSEL_GPT5_MASK;
444 writel(iva2_grpsel, resources->dw_per_pm_base + 0xA8);
445 writel(mpu_grpsel, resources->dw_per_pm_base + 0xA4);
448 iva2_grpsel = readl(resources->dw_per_pm_base + 0xA8);
449 mpu_grpsel = readl(resources->dw_per_pm_base + 0xA4);
451 iva2_grpsel |= OMAP3430_GRPSEL_GPT6_MASK;
452 mpu_grpsel &= ~OMAP3430_GRPSEL_GPT6_MASK;
454 mpu_grpsel |= OMAP3430_GRPSEL_GPT6_MASK;
455 iva2_grpsel &= ~OMAP3430_GRPSEL_GPT6_MASK;
457 writel(iva2_grpsel, resources->dw_per_pm_base + 0xA8);
458 writel(mpu_grpsel, resources->dw_per_pm_base + 0xA4);
461 iva2_grpsel = readl(resources->dw_per_pm_base + 0xA8);
462 mpu_grpsel = readl(resources->dw_per_pm_base + 0xA4);
464 iva2_grpsel |= OMAP3430_GRPSEL_GPT7_MASK;
465 mpu_grpsel &= ~OMAP3430_GRPSEL_GPT7_MASK;
467 mpu_grpsel |= OMAP3430_GRPSEL_GPT7_MASK;
468 iva2_grpsel &= ~OMAP3430_GRPSEL_GPT7_MASK;
470 writel(iva2_grpsel, resources->dw_per_pm_base + 0xA8);
471 writel(mpu_grpsel, resources->dw_per_pm_base + 0xA4);
474 iva2_grpsel = readl(resources->dw_per_pm_base + 0xA8);
475 mpu_grpsel = readl(resources->dw_per_pm_base + 0xA4);
477 iva2_grpsel |= OMAP3430_GRPSEL_GPT8_MASK;
478 mpu_grpsel &= ~OMAP3430_GRPSEL_GPT8_MASK;
480 mpu_grpsel |= OMAP3430_GRPSEL_GPT8_MASK;
481 iva2_grpsel &= ~OMAP3430_GRPSEL_GPT8_MASK;
483 writel(iva2_grpsel, resources->dw_per_pm_base + 0xA8);
484 writel(mpu_grpsel, resources->dw_per_pm_base + 0xA4);
487 iva2_grpsel = readl(resources->dw_core_pm_base + 0xA8);
488 mpu_grpsel = readl(resources->dw_core_pm_base + 0xA4);
490 iva2_grpsel |= OMAP3430_GRPSEL_MCBSP1_MASK;
491 mpu_grpsel &= ~OMAP3430_GRPSEL_MCBSP1_MASK;
493 mpu_grpsel |= OMAP3430_GRPSEL_MCBSP1_MASK;
494 iva2_grpsel &= ~OMAP3430_GRPSEL_MCBSP1_MASK;
496 writel(iva2_grpsel, resources->dw_core_pm_base + 0xA8);
497 writel(mpu_grpsel, resources->dw_core_pm_base + 0xA4);
500 iva2_grpsel = readl(resources->dw_per_pm_base + 0xA8);
501 mpu_grpsel = readl(resources->dw_per_pm_base + 0xA4);
503 iva2_grpsel |= OMAP3430_GRPSEL_MCBSP2_MASK;
504 mpu_grpsel &= ~OMAP3430_GRPSEL_MCBSP2_MASK;
506 mpu_grpsel |= OMAP3430_GRPSEL_MCBSP2_MASK;
507 iva2_grpsel &= ~OMAP3430_GRPSEL_MCBSP2_MASK;
509 writel(iva2_grpsel, resources->dw_per_pm_base + 0xA8);
510 writel(mpu_grpsel, resources->dw_per_pm_base + 0xA4);
513 iva2_grpsel = readl(resources->dw_per_pm_base + 0xA8);
514 mpu_grpsel = readl(resources->dw_per_pm_base + 0xA4);
516 iva2_grpsel |= OMAP3430_GRPSEL_MCBSP3_MASK;
517 mpu_grpsel &= ~OMAP3430_GRPSEL_MCBSP3_MASK;
519 mpu_grpsel |= OMAP3430_GRPSEL_MCBSP3_MASK;
520 iva2_grpsel &= ~OMAP3430_GRPSEL_MCBSP3_MASK;
522 writel(iva2_grpsel, resources->dw_per_pm_base + 0xA8);
523 writel(mpu_grpsel, resources->dw_per_pm_base + 0xA4);
526 iva2_grpsel = readl(resources->dw_per_pm_base + 0xA8);
527 mpu_grpsel = readl(resources->dw_per_pm_base + 0xA4);
529 iva2_grpsel |= OMAP3430_GRPSEL_MCBSP4_MASK;
530 mpu_grpsel &= ~OMAP3430_GRPSEL_MCBSP4_MASK;
532 mpu_grpsel |= OMAP3430_GRPSEL_MCBSP4_MASK;
533 iva2_grpsel &= ~OMAP3430_GRPSEL_MCBSP4_MASK;
535 writel(iva2_grpsel, resources->dw_per_pm_base + 0xA8);
536 writel(mpu_grpsel, resources->dw_per_pm_base + 0xA4);
539 iva2_grpsel = readl(resources->dw_per_pm_base + 0xA8);
540 mpu_grpsel = readl(resources->dw_per_pm_base + 0xA4);
542 iva2_grpsel |= OMAP3430_GRPSEL_MCBSP5_MASK;
543 mpu_grpsel &= ~OMAP3430_GRPSEL_MCBSP5_MASK;
545 mpu_grpsel |= OMAP3430_GRPSEL_MCBSP5_MASK;
546 iva2_grpsel &= ~OMAP3430_GRPSEL_MCBSP5_MASK;
548 writel(iva2_grpsel, resources->dw_per_pm_base + 0xA8);
549 writel(mpu_grpsel, resources->dw_per_pm_base + 0xA4);