diff options
Diffstat (limited to 'drivers/staging/tidspbridge/core/tiomap3430_pwr.c')
-rw-r--r-- | drivers/staging/tidspbridge/core/tiomap3430_pwr.c | 550 |
1 files changed, 550 insertions, 0 deletions
diff --git a/drivers/staging/tidspbridge/core/tiomap3430_pwr.c b/drivers/staging/tidspbridge/core/tiomap3430_pwr.c new file mode 100644 index 000000000000..b789f8fdd89b --- /dev/null +++ b/drivers/staging/tidspbridge/core/tiomap3430_pwr.c @@ -0,0 +1,550 @@ +/* + * tiomap_pwr.c + * + * DSP-BIOS Bridge driver support functions for TI OMAP processors. + * + * Implementation of DSP wake/sleep routines. + * + * Copyright (C) 2007-2008 Texas Instruments, Inc. + * + * This package is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * THIS PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED + * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. + */ + +/* ----------------------------------- DSP/BIOS Bridge */ +#include <dspbridge/dbdefs.h> +#include <dspbridge/cfg.h> +#include <dspbridge/drv.h> +#include <dspbridge/io_sm.h> + +/* ----------------------------------- Platform Manager */ +#include <dspbridge/brddefs.h> +#include <dspbridge/dev.h> +#include <dspbridge/iodefs.h> + +/* ------------------------------------ Hardware Abstraction Layer */ +#include <hw_defs.h> +#include <hw_mmu.h> + +#include <dspbridge/pwr_sh.h> + +/* ----------------------------------- Bridge Driver */ +#include <dspbridge/dspdeh.h> +#include <dspbridge/wdt.h> + +/* ----------------------------------- specific to this file */ +#include "_tiomap.h" +#include "_tiomap_pwr.h" +#include <mach-omap2/prm-regbits-34xx.h> +#include <mach-omap2/cm-regbits-34xx.h> + +#define PWRSTST_TIMEOUT 200 + +/* + * ======== handle_constraints_set ======== + * Sets new DSP constraint + */ +int handle_constraints_set(struct bridge_dev_context *dev_context, + void *pargs) +{ +#ifdef CONFIG_TIDSPBRIDGE_DVFS + u32 *constraint_val; + struct dspbridge_platform_data *pdata = + omap_dspbridge_dev->dev.platform_data; + + constraint_val = (u32 *) (pargs); + /* Read the target value requested by DSP */ + dev_dbg(bridge, "OPP: %s opp requested = 0x%x\n", __func__, + (u32) *(constraint_val + 1)); + + /* Set the new opp value */ + if (pdata->dsp_set_min_opp) + (*pdata->dsp_set_min_opp) ((u32) *(constraint_val + 1)); +#endif /* #ifdef CONFIG_TIDSPBRIDGE_DVFS */ + return 0; +} + +/* + * ======== handle_hibernation_from_dsp ======== + * Handle Hibernation requested from DSP + */ +int handle_hibernation_from_dsp(struct bridge_dev_context *dev_context) +{ + int status = 0; +#ifdef CONFIG_PM + u16 timeout = PWRSTST_TIMEOUT / 10; + u32 pwr_state; +#ifdef CONFIG_TIDSPBRIDGE_DVFS + u32 opplevel; + struct io_mgr *hio_mgr; +#endif + struct dspbridge_platform_data *pdata = + omap_dspbridge_dev->dev.platform_data; + + pwr_state = (*pdata->dsp_prm_read)(OMAP3430_IVA2_MOD, OMAP2_PM_PWSTST) & + OMAP_POWERSTATEST_MASK; + /* Wait for DSP to move into OFF state */ + while ((pwr_state != PWRDM_POWER_OFF) && --timeout) { + if (msleep_interruptible(10)) { + pr_err("Waiting for DSP OFF mode interrupted\n"); + return -EPERM; + } + pwr_state = (*pdata->dsp_prm_read)(OMAP3430_IVA2_MOD, + OMAP2_PM_PWSTST) & OMAP_POWERSTATEST_MASK; + } + if (timeout == 0) { + pr_err("%s: Timed out waiting for DSP off mode\n", __func__); + status = -ETIMEDOUT; + return status; + } else { + + /* Save mailbox settings */ + omap_mbox_save_ctx(dev_context->mbox); + + /* Turn off DSP Peripheral clocks and DSP Load monitor timer */ + status = dsp_clock_disable_all(dev_context->dsp_per_clks); + + /* Disable wdt on hibernation. */ + dsp_wdt_enable(false); + + if (!status) { + /* Update the Bridger Driver state */ + dev_context->dw_brd_state = BRD_DSP_HIBERNATION; +#ifdef CONFIG_TIDSPBRIDGE_DVFS + status = + dev_get_io_mgr(dev_context->hdev_obj, &hio_mgr); + if (!hio_mgr) { + status = DSP_EHANDLE; + return status; + } + io_sh_msetting(hio_mgr, SHM_GETOPP, &opplevel); + + /* + * Set the OPP to low level before moving to OFF + * mode + */ + if (pdata->dsp_set_min_opp) + (*pdata->dsp_set_min_opp) (VDD1_OPP1); + status = 0; +#endif /* CONFIG_TIDSPBRIDGE_DVFS */ + } + } +#endif + return status; +} + +/* + * ======== sleep_dsp ======== + * Put DSP in low power consuming state. + */ +int sleep_dsp(struct bridge_dev_context *dev_context, u32 dw_cmd, + void *pargs) +{ + int status = 0; +#ifdef CONFIG_PM +#ifdef CONFIG_TIDSPBRIDGE_NTFY_PWRERR + struct deh_mgr *hdeh_mgr; +#endif /* CONFIG_TIDSPBRIDGE_NTFY_PWRERR */ + u16 timeout = PWRSTST_TIMEOUT / 10; + u32 pwr_state, target_pwr_state; + struct dspbridge_platform_data *pdata = + omap_dspbridge_dev->dev.platform_data; + + /* Check if sleep code is valid */ + if ((dw_cmd != PWR_DEEPSLEEP) && (dw_cmd != PWR_EMERGENCYDEEPSLEEP)) + return -EINVAL; + + switch (dev_context->dw_brd_state) { + case BRD_RUNNING: + omap_mbox_save_ctx(dev_context->mbox); + if (dsp_test_sleepstate == PWRDM_POWER_OFF) { + sm_interrupt_dsp(dev_context, MBX_PM_DSPHIBERNATE); + dev_dbg(bridge, "PM: %s - sent hibernate cmd to DSP\n", + __func__); + target_pwr_state = PWRDM_POWER_OFF; + } else { + sm_interrupt_dsp(dev_context, MBX_PM_DSPRETENTION); + target_pwr_state = PWRDM_POWER_RET; + } + break; + case BRD_RETENTION: + omap_mbox_save_ctx(dev_context->mbox); + if (dsp_test_sleepstate == PWRDM_POWER_OFF) { + sm_interrupt_dsp(dev_context, MBX_PM_DSPHIBERNATE); + target_pwr_state = PWRDM_POWER_OFF; + } else + return 0; + break; + case BRD_HIBERNATION: + case BRD_DSP_HIBERNATION: + /* Already in Hibernation, so just return */ + dev_dbg(bridge, "PM: %s - DSP already in hibernation\n", + __func__); + return 0; + case BRD_STOPPED: + dev_dbg(bridge, "PM: %s - Board in STOP state\n", __func__); + return 0; + default: + dev_dbg(bridge, "PM: %s - Bridge in Illegal state\n", __func__); + return -EPERM; + } + + /* Get the PRCM DSP power domain status */ + pwr_state = (*pdata->dsp_prm_read)(OMAP3430_IVA2_MOD, OMAP2_PM_PWSTST) & + OMAP_POWERSTATEST_MASK; + + /* Wait for DSP to move into target power state */ + while ((pwr_state != target_pwr_state) && --timeout) { + if (msleep_interruptible(10)) { + pr_err("Waiting for DSP to Suspend interrupted\n"); + return -EPERM; + } + pwr_state = (*pdata->dsp_prm_read)(OMAP3430_IVA2_MOD, + OMAP2_PM_PWSTST) & OMAP_POWERSTATEST_MASK; + } + + if (!timeout) { + pr_err("%s: Timed out waiting for DSP off mode, state %x\n", + __func__, pwr_state); +#ifdef CONFIG_TIDSPBRIDGE_NTFY_PWRERR + dev_get_deh_mgr(dev_context->hdev_obj, &hdeh_mgr); + bridge_deh_notify(hdeh_mgr, DSP_PWRERROR, 0); +#endif /* CONFIG_TIDSPBRIDGE_NTFY_PWRERR */ + return -ETIMEDOUT; + } else { + /* Update the Bridger Driver state */ + if (dsp_test_sleepstate == PWRDM_POWER_OFF) + dev_context->dw_brd_state = BRD_HIBERNATION; + else + dev_context->dw_brd_state = BRD_RETENTION; + + /* Disable wdt on hibernation. */ + dsp_wdt_enable(false); + + /* Turn off DSP Peripheral clocks */ + status = dsp_clock_disable_all(dev_context->dsp_per_clks); + if (status) + return status; +#ifdef CONFIG_TIDSPBRIDGE_DVFS + else if (target_pwr_state == PWRDM_POWER_OFF) { + /* + * Set the OPP to low level before moving to OFF mode + */ + if (pdata->dsp_set_min_opp) + (*pdata->dsp_set_min_opp) (VDD1_OPP1); + } +#endif /* CONFIG_TIDSPBRIDGE_DVFS */ + } +#endif /* CONFIG_PM */ + return status; +} + +/* + * ======== wake_dsp ======== + * Wake up DSP from sleep. + */ +int wake_dsp(struct bridge_dev_context *dev_context, void *pargs) +{ + int status = 0; +#ifdef CONFIG_PM + + /* Check the board state, if it is not 'SLEEP' then return */ + if (dev_context->dw_brd_state == BRD_RUNNING || + dev_context->dw_brd_state == BRD_STOPPED) { + /* The Device is in 'RET' or 'OFF' state and Bridge state is not + * 'SLEEP', this means state inconsistency, so return */ + return 0; + } + + /* Send a wakeup message to DSP */ + sm_interrupt_dsp(dev_context, MBX_PM_DSPWAKEUP); + + /* Set the device state to RUNNIG */ + dev_context->dw_brd_state = BRD_RUNNING; +#endif /* CONFIG_PM */ + return status; +} + +/* + * ======== dsp_peripheral_clk_ctrl ======== + * Enable/Disable the DSP peripheral clocks as needed.. + */ +int dsp_peripheral_clk_ctrl(struct bridge_dev_context *dev_context, + void *pargs) +{ + u32 ext_clk = 0; + u32 ext_clk_id = 0; + u32 ext_clk_cmd = 0; + u32 clk_id_index = MBX_PM_MAX_RESOURCES; + u32 tmp_index; + u32 dsp_per_clks_before; + int status = 0; + + dsp_per_clks_before = dev_context->dsp_per_clks; + + ext_clk = (u32) *((u32 *) pargs); + ext_clk_id = ext_clk & MBX_PM_CLK_IDMASK; + + /* process the power message -- TODO, keep it in a separate function */ + for (tmp_index = 0; tmp_index < MBX_PM_MAX_RESOURCES; tmp_index++) { + if (ext_clk_id == bpwr_clkid[tmp_index]) { + clk_id_index = tmp_index; + break; + } + } + /* TODO -- Assert may be a too hard restriction here.. May be we should + * just return with failure when the CLK ID does not match */ + /* DBC_ASSERT(clk_id_index < MBX_PM_MAX_RESOURCES); */ + if (clk_id_index == MBX_PM_MAX_RESOURCES) { + /* return with a more meaningfull error code */ + return -EPERM; + } + ext_clk_cmd = (ext_clk >> MBX_PM_CLK_CMDSHIFT) & MBX_PM_CLK_CMDMASK; + switch (ext_clk_cmd) { + case BPWR_DISABLE_CLOCK: + status = dsp_clk_disable(bpwr_clks[clk_id_index].clk); + dsp_clk_wakeup_event_ctrl(bpwr_clks[clk_id_index].clk_id, + false); + if (!status) { + (dev_context->dsp_per_clks) &= + (~((u32) (1 << bpwr_clks[clk_id_index].clk))); + } + break; + case BPWR_ENABLE_CLOCK: + status = dsp_clk_enable(bpwr_clks[clk_id_index].clk); + dsp_clk_wakeup_event_ctrl(bpwr_clks[clk_id_index].clk_id, true); + if (!status) + (dev_context->dsp_per_clks) |= + (1 << bpwr_clks[clk_id_index].clk); + break; + default: + dev_dbg(bridge, "%s: Unsupported CMD\n", __func__); + /* unsupported cmd */ + /* TODO -- provide support for AUTOIDLE Enable/Disable + * commands */ + } + return status; +} + +/* + * ========pre_scale_dsp======== + * Sends prescale notification to DSP + * + */ +int pre_scale_dsp(struct bridge_dev_context *dev_context, void *pargs) +{ +#ifdef CONFIG_TIDSPBRIDGE_DVFS + u32 level; + u32 voltage_domain; + + voltage_domain = *((u32 *) pargs); + level = *((u32 *) pargs + 1); + + dev_dbg(bridge, "OPP: %s voltage_domain = %x, level = 0x%x\n", + __func__, voltage_domain, level); + if ((dev_context->dw_brd_state == BRD_HIBERNATION) || + (dev_context->dw_brd_state == BRD_RETENTION) || + (dev_context->dw_brd_state == BRD_DSP_HIBERNATION)) { + dev_dbg(bridge, "OPP: %s IVA in sleep. No message to DSP\n"); + return 0; + } else if ((dev_context->dw_brd_state == BRD_RUNNING)) { + /* Send a prenotificatio to DSP */ + dev_dbg(bridge, "OPP: %s sent notification to DSP\n", __func__); + sm_interrupt_dsp(dev_context, MBX_PM_SETPOINT_PRENOTIFY); + return 0; + } else { + return -EPERM; + } +#endif /* #ifdef CONFIG_TIDSPBRIDGE_DVFS */ + return 0; +} + +/* + * ========post_scale_dsp======== + * Sends postscale notification to DSP + * + */ +int post_scale_dsp(struct bridge_dev_context *dev_context, + void *pargs) +{ + int status = 0; +#ifdef CONFIG_TIDSPBRIDGE_DVFS + u32 level; + u32 voltage_domain; + struct io_mgr *hio_mgr; + + status = dev_get_io_mgr(dev_context->hdev_obj, &hio_mgr); + if (!hio_mgr) + return -EFAULT; + + voltage_domain = *((u32 *) pargs); + level = *((u32 *) pargs + 1); + dev_dbg(bridge, "OPP: %s voltage_domain = %x, level = 0x%x\n", + __func__, voltage_domain, level); + if ((dev_context->dw_brd_state == BRD_HIBERNATION) || + (dev_context->dw_brd_state == BRD_RETENTION) || + (dev_context->dw_brd_state == BRD_DSP_HIBERNATION)) { + /* Update the OPP value in shared memory */ + io_sh_msetting(hio_mgr, SHM_CURROPP, &level); + dev_dbg(bridge, "OPP: %s IVA in sleep. Wrote to shm\n", + __func__); + } else if ((dev_context->dw_brd_state == BRD_RUNNING)) { + /* Update the OPP value in shared memory */ + io_sh_msetting(hio_mgr, SHM_CURROPP, &level); + /* Send a post notification to DSP */ + sm_interrupt_dsp(dev_context, MBX_PM_SETPOINT_POSTNOTIFY); + dev_dbg(bridge, "OPP: %s wrote to shm. Sent post notification " + "to DSP\n", __func__); + } else { + status = -EPERM; + } +#endif /* #ifdef CONFIG_TIDSPBRIDGE_DVFS */ + return status; +} + +void dsp_clk_wakeup_event_ctrl(u32 clock_id, bool enable) +{ + struct cfg_hostres *resources; + int status = 0; + u32 iva2_grpsel; + u32 mpu_grpsel; + struct dev_object *hdev_object = NULL; + struct bridge_dev_context *bridge_context = NULL; + + hdev_object = (struct dev_object *)drv_get_first_dev_object(); + if (!hdev_object) + return; + + status = dev_get_bridge_context(hdev_object, &bridge_context); + if (!bridge_context) + return; + + resources = bridge_context->resources; + if (!resources) + return; + + switch (clock_id) { + case BPWR_GP_TIMER5: + iva2_grpsel = readl(resources->dw_per_pm_base + 0xA8); + mpu_grpsel = readl(resources->dw_per_pm_base + 0xA4); + if (enable) { + iva2_grpsel |= OMAP3430_GRPSEL_GPT5_MASK; + mpu_grpsel &= ~OMAP3430_GRPSEL_GPT5_MASK; + } else { + mpu_grpsel |= OMAP3430_GRPSEL_GPT5_MASK; + iva2_grpsel &= ~OMAP3430_GRPSEL_GPT5_MASK; + } + writel(iva2_grpsel, resources->dw_per_pm_base + 0xA8); + writel(mpu_grpsel, resources->dw_per_pm_base + 0xA4); + break; + case BPWR_GP_TIMER6: + iva2_grpsel = readl(resources->dw_per_pm_base + 0xA8); + mpu_grpsel = readl(resources->dw_per_pm_base + 0xA4); + if (enable) { + iva2_grpsel |= OMAP3430_GRPSEL_GPT6_MASK; + mpu_grpsel &= ~OMAP3430_GRPSEL_GPT6_MASK; + } else { + mpu_grpsel |= OMAP3430_GRPSEL_GPT6_MASK; + iva2_grpsel &= ~OMAP3430_GRPSEL_GPT6_MASK; + } + writel(iva2_grpsel, resources->dw_per_pm_base + 0xA8); + writel(mpu_grpsel, resources->dw_per_pm_base + 0xA4); + break; + case BPWR_GP_TIMER7: + iva2_grpsel = readl(resources->dw_per_pm_base + 0xA8); + mpu_grpsel = readl(resources->dw_per_pm_base + 0xA4); + if (enable) { + iva2_grpsel |= OMAP3430_GRPSEL_GPT7_MASK; + mpu_grpsel &= ~OMAP3430_GRPSEL_GPT7_MASK; + } else { + mpu_grpsel |= OMAP3430_GRPSEL_GPT7_MASK; + iva2_grpsel &= ~OMAP3430_GRPSEL_GPT7_MASK; + } + writel(iva2_grpsel, resources->dw_per_pm_base + 0xA8); + writel(mpu_grpsel, resources->dw_per_pm_base + 0xA4); + break; + case BPWR_GP_TIMER8: + iva2_grpsel = readl(resources->dw_per_pm_base + 0xA8); + mpu_grpsel = readl(resources->dw_per_pm_base + 0xA4); + if (enable) { + iva2_grpsel |= OMAP3430_GRPSEL_GPT8_MASK; + mpu_grpsel &= ~OMAP3430_GRPSEL_GPT8_MASK; + } else { + mpu_grpsel |= OMAP3430_GRPSEL_GPT8_MASK; + iva2_grpsel &= ~OMAP3430_GRPSEL_GPT8_MASK; + } + writel(iva2_grpsel, resources->dw_per_pm_base + 0xA8); + writel(mpu_grpsel, resources->dw_per_pm_base + 0xA4); + break; + case BPWR_MCBSP1: + iva2_grpsel = readl(resources->dw_core_pm_base + 0xA8); + mpu_grpsel = readl(resources->dw_core_pm_base + 0xA4); + if (enable) { + iva2_grpsel |= OMAP3430_GRPSEL_MCBSP1_MASK; + mpu_grpsel &= ~OMAP3430_GRPSEL_MCBSP1_MASK; + } else { + mpu_grpsel |= OMAP3430_GRPSEL_MCBSP1_MASK; + iva2_grpsel &= ~OMAP3430_GRPSEL_MCBSP1_MASK; + } + writel(iva2_grpsel, resources->dw_core_pm_base + 0xA8); + writel(mpu_grpsel, resources->dw_core_pm_base + 0xA4); + break; + case BPWR_MCBSP2: + iva2_grpsel = readl(resources->dw_per_pm_base + 0xA8); + mpu_grpsel = readl(resources->dw_per_pm_base + 0xA4); + if (enable) { + iva2_grpsel |= OMAP3430_GRPSEL_MCBSP2_MASK; + mpu_grpsel &= ~OMAP3430_GRPSEL_MCBSP2_MASK; + } else { + mpu_grpsel |= OMAP3430_GRPSEL_MCBSP2_MASK; + iva2_grpsel &= ~OMAP3430_GRPSEL_MCBSP2_MASK; + } + writel(iva2_grpsel, resources->dw_per_pm_base + 0xA8); + writel(mpu_grpsel, resources->dw_per_pm_base + 0xA4); + break; + case BPWR_MCBSP3: + iva2_grpsel = readl(resources->dw_per_pm_base + 0xA8); + mpu_grpsel = readl(resources->dw_per_pm_base + 0xA4); + if (enable) { + iva2_grpsel |= OMAP3430_GRPSEL_MCBSP3_MASK; + mpu_grpsel &= ~OMAP3430_GRPSEL_MCBSP3_MASK; + } else { + mpu_grpsel |= OMAP3430_GRPSEL_MCBSP3_MASK; + iva2_grpsel &= ~OMAP3430_GRPSEL_MCBSP3_MASK; + } + writel(iva2_grpsel, resources->dw_per_pm_base + 0xA8); + writel(mpu_grpsel, resources->dw_per_pm_base + 0xA4); + break; + case BPWR_MCBSP4: + iva2_grpsel = readl(resources->dw_per_pm_base + 0xA8); + mpu_grpsel = readl(resources->dw_per_pm_base + 0xA4); + if (enable) { + iva2_grpsel |= OMAP3430_GRPSEL_MCBSP4_MASK; + mpu_grpsel &= ~OMAP3430_GRPSEL_MCBSP4_MASK; + } else { + mpu_grpsel |= OMAP3430_GRPSEL_MCBSP4_MASK; + iva2_grpsel &= ~OMAP3430_GRPSEL_MCBSP4_MASK; + } + writel(iva2_grpsel, resources->dw_per_pm_base + 0xA8); + writel(mpu_grpsel, resources->dw_per_pm_base + 0xA4); + break; + case BPWR_MCBSP5: + iva2_grpsel = readl(resources->dw_per_pm_base + 0xA8); + mpu_grpsel = readl(resources->dw_per_pm_base + 0xA4); + if (enable) { + iva2_grpsel |= OMAP3430_GRPSEL_MCBSP5_MASK; + mpu_grpsel &= ~OMAP3430_GRPSEL_MCBSP5_MASK; + } else { + mpu_grpsel |= OMAP3430_GRPSEL_MCBSP5_MASK; + iva2_grpsel &= ~OMAP3430_GRPSEL_MCBSP5_MASK; + } + writel(iva2_grpsel, resources->dw_per_pm_base + 0xA8); + writel(mpu_grpsel, resources->dw_per_pm_base + 0xA4); + break; + } +} |