text: /*
   * 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;
  	}
  }
  