/* 2012-09-16: File added and changed by Sony Corporation */
/*
 * icx_smu.c
 *
 * Copyright 2011 Sony Corporation.
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
 *
 */

#include <common.h>

#if defined(CONFIG_ICX) || defined(CONFIG_ICD)

#include <asm/errno.h>

#include "reg.h"
#include "icx_io.h"
#include "icx_smu.h"


/****************/
/*@ Definitions */
/****************/
#define SMU_REG_MASK		0xffff0000
#define SMU_REG_BIT_MASK	0x0000ffff
#define SMU_REG_MAX_BIT_LEN	32


/********************/
/*@ Global routines */
/********************/

/*-----------------------------------------------------------------------------
 * Function : smu_reset_control
 * Abstruct : Reset/Unreset a specified device
 * Input    : dev		: device info
 *          : onoff		: 0	: reset
 *			:			: 1 : unreset
 * Return   : 0         : success
 *          : -EINVAL   : invalid parameters
 *----------------------------------------------------------------------------*/
int smu_reset_control(u_int dev, u_char onoff)
{
	u_int offset, bit;
	u_int regval;
	u_int regaddr = SMU_S0_BASE;

	if ((onoff != SMU_RESETREQ_RESET) && (onoff != SMU_RESETREQ_UNRESET))
		return -EINVAL;

	bit    = dev & SMU_REG_BIT_MASK;
	offset = (dev & SMU_REG_MASK) >> 16;

	regaddr += offset;

	if ((regaddr < SMU_DSP_RSTCTRL) || (regaddr > SMU_USIBU5_RSTCTRL))
		return -EINVAL;

	regval = inl(regaddr);

	if (onoff == SMU_RESETREQ_UNRESET)
		regval |= bit;
	else
		regval &= ~bit;

	outl(regval, regaddr);

	return 0;
}

/*-----------------------------------------------------------------------------
 * Function	: smu_clockgate_control
 * Abstruct : Open a gated clock control
 * Input    : clkbit	: clock bit
 *          : onoff		: 0	: close
 *			:			: 1 : open
 * Return   : 0         : success
 *          : -EINVAL   : invalid parameters
 *----------------------------------------------------------------------------*/
int smu_clockgate_control(u_int clkbit, u_char onoff)
{
	u_int offset, bit;
	u_int regval;
	u_int regaddr = SMU_S0_BASE;

	if ((onoff != SMU_GCLKCTRL_CLOSE) && (onoff != SMU_GCLKCTRL_OPEN))
		return -EINVAL;

	bit    = clkbit & SMU_REG_BIT_MASK;
	offset = (clkbit & SMU_REG_MASK) >> 16;

	regaddr += offset;

	if ((regaddr < SMU_DSPGCLKCTRL) || (regaddr > SMU_USIBU5GCLKCTRL))
		return -EINVAL;

	regval = inl(regaddr);

	if (onoff == SMU_GCLKCTRL_OPEN)
		regval |= bit;
	else
		regval &= ~bit;

	outl(regval, regaddr);

	return 0;
}

/*-----------------------------------------------------------------------------
 * Function	: smu_autoclock_control
 * Abstruct : Auto clock control 
 * Input    : dev		: device info
 *          : onoff		: 0	: disable
 *			:			: 1 : enable
 * Return   : 0         : success
 *          : -EINVAL   : invalid parameters
 *----------------------------------------------------------------------------*/
int smu_autoclock_control(u_int dev, u_char onoff)
{
	u_int mask, bit;
	u_int regval;
	u_int regaddr;

	bit = dev & SMU_REG_BIT_MASK;
	if (bit >= SMU_REG_MAX_BIT_LEN)
		return -EINVAL;

	mask = 1 << bit;

	switch (dev & SMU_REG_MASK) {
    	case SMU_AHBCLKCTRL0_GROUP:
			regaddr = SMU_AHBCLKCTRL0;
			break;
		case SMU_AHBCLKCTRL1_GROUP:
			regaddr = SMU_AHBCLKCTRL1;
			break;
		case SMU_AHBCLKCTRL2_GROUP:
			regaddr = SMU_AHBCLKCTRL2;
			break;
		case SMU_AHBCLKCTRL3_GROUP:
			regaddr = SMU_AHBCLKCTRL3;
			break;
		case SMU_APBCLKCTRL0_GROUP:
			regaddr = SMU_APBCLKCTRL0;
			break;
		case SMU_APBCLKCTRL1_GROUP:
			regaddr = SMU_APBCLKCTRL1;
			break;
		case SMU_APBCLKCTRL2_GROUP:
			regaddr = SMU_APBCLKCTRL2;
			break;
		case SMU_CLKCTRL_GROUP:
			regaddr = SMU_CLKCTRL;
			break;
		case SMU_AVECLKCTRL_GROUP:
			regaddr = SMU_AVECLKCTRL;
			break;
		default :
			return -EINVAL;
	}

	regval = inl(regaddr);
	
	if (onoff == SMU_AUTOCLKCTRL_ENABLE)
		regval |= mask;
	else
		regval &= ~mask;

	outl(regval, regaddr);

	return 0;
}

#endif	/* CONFIG_ICX || CONFIG_ICD */ 
