Loading...
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 | /*
* 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.
*/
/* ----------------------------------- Host OS */
#include <dspbridge/host_os.h>
#include <linux/platform_data/dsp-omap.h>
/* ----------------------------------- DSP/BIOS Bridge */
#include <dspbridge/dbdefs.h>
#include <dspbridge/drv.h>
#include <dspbridge/io_sm.h>
/* ----------------------------------- Platform Manager */
#include <dspbridge/brddefs.h>
#include <dspbridge/dev.h>
#include <dspbridge/io.h>
/* ------------------------------------ Hardware Abstraction Layer */
#include <hw_defs.h>
#include <hw_mmu.h>
#include <dspbridge/pwr.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 omap_dsp_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 omap_dsp_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->brd_state = BRD_DSP_HIBERNATION;
#ifdef CONFIG_TIDSPBRIDGE_DVFS
status =
dev_get_io_mgr(dev_context->dev_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 omap_dsp_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->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->dev_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->brd_state = BRD_HIBERNATION;
else
dev_context->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->brd_state == BRD_RUNNING ||
dev_context->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->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 */
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->brd_state == BRD_HIBERNATION) ||
(dev_context->brd_state == BRD_RETENTION) ||
(dev_context->brd_state == BRD_DSP_HIBERNATION)) {
dev_dbg(bridge, "OPP: %s IVA in sleep. No message to DSP\n");
return 0;
} else if ((dev_context->brd_state == BRD_RUNNING)) {
/* Send a prenotification 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->dev_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->brd_state == BRD_HIBERNATION) ||
(dev_context->brd_state == BRD_RETENTION) ||
(dev_context->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->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->per_pm_base + 0xA8);
mpu_grpsel = readl(resources->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->per_pm_base + 0xA8);
writel(mpu_grpsel, resources->per_pm_base + 0xA4);
break;
case BPWR_GP_TIMER6:
iva2_grpsel = readl(resources->per_pm_base + 0xA8);
mpu_grpsel = readl(resources->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->per_pm_base + 0xA8);
writel(mpu_grpsel, resources->per_pm_base + 0xA4);
break;
case BPWR_GP_TIMER7:
iva2_grpsel = readl(resources->per_pm_base + 0xA8);
mpu_grpsel = readl(resources->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->per_pm_base + 0xA8);
writel(mpu_grpsel, resources->per_pm_base + 0xA4);
break;
case BPWR_GP_TIMER8:
iva2_grpsel = readl(resources->per_pm_base + 0xA8);
mpu_grpsel = readl(resources->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->per_pm_base + 0xA8);
writel(mpu_grpsel, resources->per_pm_base + 0xA4);
break;
case BPWR_MCBSP1:
iva2_grpsel = readl(resources->core_pm_base + 0xA8);
mpu_grpsel = readl(resources->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->core_pm_base + 0xA8);
writel(mpu_grpsel, resources->core_pm_base + 0xA4);
break;
case BPWR_MCBSP2:
iva2_grpsel = readl(resources->per_pm_base + 0xA8);
mpu_grpsel = readl(resources->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->per_pm_base + 0xA8);
writel(mpu_grpsel, resources->per_pm_base + 0xA4);
break;
case BPWR_MCBSP3:
iva2_grpsel = readl(resources->per_pm_base + 0xA8);
mpu_grpsel = readl(resources->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->per_pm_base + 0xA8);
writel(mpu_grpsel, resources->per_pm_base + 0xA4);
break;
case BPWR_MCBSP4:
iva2_grpsel = readl(resources->per_pm_base + 0xA8);
mpu_grpsel = readl(resources->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->per_pm_base + 0xA8);
writel(mpu_grpsel, resources->per_pm_base + 0xA4);
break;
case BPWR_MCBSP5:
iva2_grpsel = readl(resources->per_pm_base + 0xA8);
mpu_grpsel = readl(resources->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->per_pm_base + 0xA8);
writel(mpu_grpsel, resources->per_pm_base + 0xA4);
break;
}
}
|