/*
 * pwr.c
 *
 * DSP-BIOS Bridge driver support functions for TI OMAP processors.
 *
 * PWR API for controlling DSP power states.
 *
 * Copyright (C) 2005-2006 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>

/*  ----------------------------------- This */
#include <dspbridge/pwr.h>

/*  ----------------------------------- Resource Manager */
#include <dspbridge/devdefs.h>
#include <dspbridge/drv.h>

/*  ----------------------------------- Platform Manager */
#include <dspbridge/dev.h>

/*  ----------------------------------- Link Driver */
#include <dspbridge/dspioctl.h>

/*
 *  ======== pwr_sleep_dsp ========
 *    Send command to DSP to enter sleep state.
 */
int pwr_sleep_dsp(const u32 sleep_code, const u32 timeout)
{
	struct bridge_drv_interface *intf_fxns;
	struct bridge_dev_context *dw_context;
	int status = -EPERM;
	struct dev_object *hdev_obj = NULL;
	u32 ioctlcode = 0;
	u32 arg = timeout;

	for (hdev_obj = (struct dev_object *)drv_get_first_dev_object();
	     hdev_obj != NULL;
	     hdev_obj =
	     (struct dev_object *)drv_get_next_dev_object((u32) hdev_obj)) {
		if (dev_get_bridge_context(hdev_obj,
						(struct bridge_dev_context **)
						   &dw_context)) {
			continue;
		}
		if (dev_get_intf_fxns(hdev_obj,
						(struct bridge_drv_interface **)
						&intf_fxns)) {
			continue;
		}
		if (sleep_code == PWR_DEEPSLEEP)
			ioctlcode = BRDIOCTL_DEEPSLEEP;
		else if (sleep_code == PWR_EMERGENCYDEEPSLEEP)
			ioctlcode = BRDIOCTL_EMERGENCYSLEEP;
		else
			status = -EINVAL;

		if (status != -EINVAL) {
			status = (*intf_fxns->dev_cntrl) (dw_context,
							      ioctlcode,
							      (void *)&arg);
		}
	}
	return status;
}

/*
 *  ======== pwr_wake_dsp ========
 *    Send command to DSP to wake it from sleep.
 */
int pwr_wake_dsp(const u32 timeout)
{
	struct bridge_drv_interface *intf_fxns;
	struct bridge_dev_context *dw_context;
	int status = -EPERM;
	struct dev_object *hdev_obj = NULL;
	u32 arg = timeout;

	for (hdev_obj = (struct dev_object *)drv_get_first_dev_object();
	     hdev_obj != NULL;
	     hdev_obj = (struct dev_object *)drv_get_next_dev_object
	     ((u32) hdev_obj)) {
		if (!(dev_get_bridge_context(hdev_obj,
						      (struct bridge_dev_context
						       **)&dw_context))) {
			if (!(dev_get_intf_fxns(hdev_obj,
			      (struct bridge_drv_interface **)&intf_fxns))) {
				status =
				    (*intf_fxns->dev_cntrl) (dw_context,
							BRDIOCTL_WAKEUP,
							(void *)&arg);
			}
		}
	}
	return status;
}

/*
 *  ======== pwr_pm_pre_scale========
 *    Sends pre-notification message to DSP.
 */
int pwr_pm_pre_scale(u16 voltage_domain, u32 level)
{
	struct bridge_drv_interface *intf_fxns;
	struct bridge_dev_context *dw_context;
	int status = -EPERM;
	struct dev_object *hdev_obj = NULL;
	u32 arg[2];

	arg[0] = voltage_domain;
	arg[1] = level;

	for (hdev_obj = (struct dev_object *)drv_get_first_dev_object();
	     hdev_obj != NULL;
	     hdev_obj = (struct dev_object *)drv_get_next_dev_object
	     ((u32) hdev_obj)) {
		if (!(dev_get_bridge_context(hdev_obj,
						      (struct bridge_dev_context
						       **)&dw_context))) {
			if (!(dev_get_intf_fxns(hdev_obj,
			      (struct bridge_drv_interface **)&intf_fxns))) {
				status =
				    (*intf_fxns->dev_cntrl) (dw_context,
						BRDIOCTL_PRESCALE_NOTIFY,
						(void *)&arg);
			}
		}
	}
	return status;
}

/*
 *  ======== pwr_pm_post_scale========
 *    Sends post-notification message to DSP.
 */
int pwr_pm_post_scale(u16 voltage_domain, u32 level)
{
	struct bridge_drv_interface *intf_fxns;
	struct bridge_dev_context *dw_context;
	int status = -EPERM;
	struct dev_object *hdev_obj = NULL;
	u32 arg[2];

	arg[0] = voltage_domain;
	arg[1] = level;

	for (hdev_obj = (struct dev_object *)drv_get_first_dev_object();
	     hdev_obj != NULL;
	     hdev_obj = (struct dev_object *)drv_get_next_dev_object
	     ((u32) hdev_obj)) {
		if (!(dev_get_bridge_context(hdev_obj,
						      (struct bridge_dev_context
						       **)&dw_context))) {
			if (!(dev_get_intf_fxns(hdev_obj,
			      (struct bridge_drv_interface **)&intf_fxns))) {
				status =
				    (*intf_fxns->dev_cntrl) (dw_context,
						BRDIOCTL_POSTSCALE_NOTIFY,
						(void *)&arg);
			}
		}
	}
	return status;

}