
#include <Library/RockchipPlatformLib.h>
#include "Soc.h"
#include <Library/DebugLib.h>
#include <Library/IoLib.h>
#include <Library/PcdLib.h>
#include <Library/SpiLib.h>
#include <Library/RK806.h>
#if 0
/* Not used or exisit register and configure */
#define NA			-1
#define BIT(n)			(1 << (n))
#define RK806_DBG		DEBUG_ERROR
/* rk806 buck*/
#define RK806_BUCK_ON_VSEL(n)		(0x1a + n - 1)
#define RK806_BUCK_SLP_VSEL(n)		(0x24 + n - 1)
#define RK806_BUCK_CONFIG(n)		(0x10 + n - 1)
#define RK806_BUCK_VSEL_MASK		0xff

/* RK806 LDO */
#define RK806_NLDO_ON_VSEL(n)		(0x43 + n - 1)
#define RK806_NLDO_SLP_VSEL(n)		(0x48 + n - 1)
#define RK806_NLDO_VSEL_MASK		0xff
#define RK806_PLDO_ON_VSEL(n)		(0x4e + n - 1)
#define RK806_PLDO_SLP_VSEL(n)		(0x54 + n - 1)
#define RK806_PLDO_VSEL_MASK		0xff

/* RK806 ENABLE */
#define RK806_POWER_EN(n)		(0x00 + n)
#define RK806_NLDO_EN(n)		(0x03 + n)
#define RK806_PLDO_EN(n)		(0x04 + n)
#define RK806_RAMP_RATE_MASK1		0xc0
#define RK806_RAMP_RATE_REG1(n)		(0x10 + n)
#define RK806_RAMP_RATE_REG1_8		0xeb
#define RK806_RAMP_RATE_REG9_10		0xea

#define RK806_RAMP_RATE_4LSB_PER_1CLK	0x00/* LDO 100mV/uS buck 50mV/us */
#define RK806_RAMP_RATE_2LSB_PER_1CLK	0x01/* LDO 50mV/uS buck 25mV/us */
#define RK806_RAMP_RATE_1LSB_PER_1CLK	0x02/* LDO 25mV/uS buck 12.5mV/us */
#define RK806_RAMP_RATE_1LSB_PER_2CLK	0x03/* LDO 12.5mV/uS buck 6.25mV/us */

#define RK806_RAMP_RATE_1LSB_PER_4CLK	0x04/* LDO 6.28/2mV/uS buck 3.125mV/us */
#define RK806_RAMP_RATE_1LSB_PER_8CLK	0x05/* LDO 3.12mV/uS buck 1.56mV/us */
#define RK806_RAMP_RATE_1LSB_PER_13CLK	0x06/* LDO 1.9mV/uS buck 961mV/us */
#define RK806_RAMP_RATE_1LSB_PER_32CLK	0x07/* LDO 0.78mV/uS buck 0.39mV/us */

#define RK806_PLDO0_2_MSK(pldo)		(BIT(pldo + 5))
#define RK806_PLDO0_2_SET(pldo)		(BIT(pldo + 1) | RK806_PLDO0_2_MSK(pldo))
#define RK806_PLDO0_2_CLR(pldo)		RK806_PLDO0_2_MSK(pldo)

#define RK806_CHIP_NAME			0x5A
#define RK806_CHIP_VER			0x5B

#define RK806_CMD_READ			0
#define RK806_CMD_WRITE			BIT(7)
#define RK806_CMD_CRC_EN		BIT(6)
#define RK806_CMD_CRC_DIS		0
#define RK806_CMD_LEN_MSK		0x0f
#define RK806_REG_H			0x00

#define RK806_SYS_CFG1			0x5f
#define RK806_SYS_CFG3			0x72
#define RK806_PWRON_KEY			0x76
#define RK806_INT_STS0			0x77
#define RK806_INT_MSK0			0x78
#define RK806_INT_STS1			0x79
#define RK806_INT_MSK1			0x7A
#define RK806_GPIO_INT_CONFIG		0x7B
#define RK806_IRQ_PWRON_FALL_MSK	BIT(0)
#define RK806_IRQ_PWRON_RISE_MSK	BIT(1)
#define RK806_DEV_OFF			BIT(0)
#define RK806_RST_MODE1			0x01
#define RK806_RST_MODE2			0x02
#define VERSION_AB			0x01

struct regulator_init_data {
	const char *supply_regulator;        /* or NULL for system supply */
	INT32 reg_id;
	INT32 init_voltage_mv;
};

struct rk8xx_reg_info {
	UINT32 min_uv;
	UINT32 step_uv;
	UINT8 vsel_reg;
	UINT8 vsel_sleep_reg;
	UINT8 config_reg;
	UINT8 vsel_mask;
	UINT8 min_sel;
	/* only for buck now */
	UINT8 max_sel;
	UINT8 range_num;
};

#define RK8XX_DESC_COM(_name, _reg_info, _ops) \
{						\
	.reg_info		= (_reg_info),	\
	.name	= (_reg_id),			\
	.ops		= _ops,			\
}

#define RK8XX_VOLTAGE_INIT(_id, _voltage) \
{		\
	.reg_id = (_id),\
	.init_voltage_mv	= (_voltage),\
}

/******************************************
8 -12: MASTER, SLAVE
4 -7: BUCK, NLDO, PLDO
0 -3:  num
******************************************/
#define MASTER	(0x0 << 8)
#define SLAVER	(0x1 << 8)
#define BUCK		(0x0 << 4)
#define NLDO		(0x1 << 4)
#define PLDO		(0x2 << 4)

enum master_num {
	MASTER_BUCK1 = (MASTER | BUCK | 0),
	MASTER_BUCK2 = (MASTER | BUCK | 1),
	MASTER_BUCK3 = (MASTER | BUCK | 2),
	MASTER_BUCK4 = (MASTER | BUCK | 3),
	MASTER_BUCK5 = (MASTER | BUCK | 4),
	MASTER_BUCK6 = (MASTER | BUCK | 5),
	MASTER_BUCK7 = (MASTER | BUCK | 6),
	MASTER_BUCK8 = (MASTER | BUCK | 7),
	MASTER_BUCK9 = (MASTER | BUCK | 8),
	MASTER_BUCK10 = (MASTER | BUCK | 9),

	MASTER_NLDO1 = (MASTER | NLDO | 0),
	MASTER_NLDO2 = (MASTER | NLDO | 1),
	MASTER_NLDO3 = (MASTER | NLDO | 2),
	MASTER_NLDO4 = (MASTER | NLDO | 3),
	MASTER_NLDO5 = (MASTER | NLDO | 4),

	MASTER_PLDO1 = (MASTER | PLDO | 0),
	MASTER_PLDO2 = (MASTER | PLDO | 1),
	MASTER_PLDO3 = (MASTER | PLDO | 2),
	MASTER_PLDO4 = (MASTER | PLDO | 3),
	MASTER_PLDO5 = (MASTER | PLDO | 4),
	MASTER_PLDO6 = (MASTER | PLDO | 5),
};

enum slaver_num {
	SLAVER_BUCK1 = (SLAVER | BUCK | 0),
	SLAVER_BUCK2 = (SLAVER | BUCK | 1),
	SLAVER_BUCK3 = (SLAVER | BUCK | 2),
	SLAVER_BUCK4 = (SLAVER | BUCK | 3),
	SLAVER_BUCK5 = (SLAVER | BUCK | 4),
	SLAVER_BUCK6 = (SLAVER | BUCK | 5),
	SLAVER_BUCK7 = (SLAVER | BUCK | 6),
	SLAVER_BUCK8 = (SLAVER | BUCK | 7),
	SLAVER_BUCK9 = (SLAVER | BUCK | 8),
	SLAVER_BUCK10 = (SLAVER | BUCK | 9),

	SLAVER_NLDO1 = (SLAVER | NLDO | 0),
	SLAVER_NLDO2 = (SLAVER | NLDO | 1),
	SLAVER_NLDO3 = (SLAVER | NLDO | 2),
	SLAVER_NLDO4 = (SLAVER | NLDO | 3),
	SLAVER_NLDO5 = (SLAVER | NLDO | 4),
            
	SLAVER_PLDO1 = (SLAVER | PLDO | 0),
	SLAVER_PLDO2 = (SLAVER | PLDO | 1),
	SLAVER_PLDO3 = (SLAVER | PLDO | 2),
	SLAVER_PLDO4 = (SLAVER | PLDO | 3),
	SLAVER_PLDO5 = (SLAVER | PLDO | 4),
	SLAVER_PLDO6 = (SLAVER | PLDO | 5),
};
#endif

struct SPI_HANDLE gSPI;

static const struct rk8xx_reg_info rk806_buck[] = {
	/* buck 1 */
	{  500000,   6250, RK806_BUCK_ON_VSEL(1), RK806_BUCK_SLP_VSEL(1), RK806_BUCK_CONFIG(1), RK806_BUCK_VSEL_MASK, 0x00, 0xa0, 3},
	{  1500000, 25000, RK806_BUCK_ON_VSEL(1), RK806_BUCK_SLP_VSEL(1), RK806_BUCK_CONFIG(1), RK806_BUCK_VSEL_MASK, 0xa1, 0xed, 3},
	{  3400000,     0, RK806_BUCK_ON_VSEL(1), RK806_BUCK_SLP_VSEL(1), RK806_BUCK_CONFIG(1), RK806_BUCK_VSEL_MASK, 0xee, 0xff, 3},
	/* buck 2 */
	{  500000,   6250, RK806_BUCK_ON_VSEL(2), RK806_BUCK_SLP_VSEL(2), RK806_BUCK_CONFIG(2), RK806_BUCK_VSEL_MASK, 0x00, 0xa0, 3},
	{  1500000, 25000, RK806_BUCK_ON_VSEL(2), RK806_BUCK_SLP_VSEL(2), RK806_BUCK_CONFIG(2), RK806_BUCK_VSEL_MASK, 0xa1, 0xed, 3},
	{  3400000,     0, RK806_BUCK_ON_VSEL(2), RK806_BUCK_SLP_VSEL(2), RK806_BUCK_CONFIG(2), RK806_BUCK_VSEL_MASK, 0xee, 0xff, 3},
	/* buck 3 */
	{  500000,   6250, RK806_BUCK_ON_VSEL(3), RK806_BUCK_SLP_VSEL(3), RK806_BUCK_CONFIG(3), RK806_BUCK_VSEL_MASK, 0x00, 0xa0, 3},
	{  1500000, 25000, RK806_BUCK_ON_VSEL(3), RK806_BUCK_SLP_VSEL(3), RK806_BUCK_CONFIG(3), RK806_BUCK_VSEL_MASK, 0xa1, 0xed, 3},
	{  3400000,     0, RK806_BUCK_ON_VSEL(3), RK806_BUCK_SLP_VSEL(3), RK806_BUCK_CONFIG(3), RK806_BUCK_VSEL_MASK, 0xee, 0xff, 3},
	/* buck 4 */
	{  500000,   6250, RK806_BUCK_ON_VSEL(4), RK806_BUCK_SLP_VSEL(4), RK806_BUCK_CONFIG(4), RK806_BUCK_VSEL_MASK, 0x00, 0xa0, 3},
	{  1500000, 25000, RK806_BUCK_ON_VSEL(4), RK806_BUCK_SLP_VSEL(4), RK806_BUCK_CONFIG(4), RK806_BUCK_VSEL_MASK, 0xa1, 0xed, 3},
	{  3400000,     0, RK806_BUCK_ON_VSEL(4), RK806_BUCK_SLP_VSEL(4), RK806_BUCK_CONFIG(4), RK806_BUCK_VSEL_MASK, 0xee, 0xff, 3},
	/* buck 5 */
	{  500000,   6250, RK806_BUCK_ON_VSEL(5), RK806_BUCK_SLP_VSEL(5), RK806_BUCK_CONFIG(5), RK806_BUCK_VSEL_MASK, 0x00, 0xa0, 3},
	{  1500000, 25000, RK806_BUCK_ON_VSEL(5), RK806_BUCK_SLP_VSEL(5), RK806_BUCK_CONFIG(5), RK806_BUCK_VSEL_MASK, 0xa1, 0xed, 3},
	{  3400000,     0, RK806_BUCK_ON_VSEL(5), RK806_BUCK_SLP_VSEL(5), RK806_BUCK_CONFIG(5), RK806_BUCK_VSEL_MASK, 0xee, 0xff, 3},
	/* buck 6 */
	{  500000,   6250, RK806_BUCK_ON_VSEL(6), RK806_BUCK_SLP_VSEL(6), RK806_BUCK_CONFIG(6), RK806_BUCK_VSEL_MASK, 0x00, 0xa0, 3},
	{  1500000, 25000, RK806_BUCK_ON_VSEL(6), RK806_BUCK_SLP_VSEL(6), RK806_BUCK_CONFIG(6), RK806_BUCK_VSEL_MASK, 0xa1, 0xed, 3},
	{  3400000,     0, RK806_BUCK_ON_VSEL(6), RK806_BUCK_SLP_VSEL(6), RK806_BUCK_CONFIG(6), RK806_BUCK_VSEL_MASK, 0xee, 0xff, 3},
	/* buck 7 */
	{  500000,   6250, RK806_BUCK_ON_VSEL(7), RK806_BUCK_SLP_VSEL(7), RK806_BUCK_CONFIG(7), RK806_BUCK_VSEL_MASK, 0x00, 0xa0, 3},
	{  1500000, 25000, RK806_BUCK_ON_VSEL(7), RK806_BUCK_SLP_VSEL(7), RK806_BUCK_CONFIG(7), RK806_BUCK_VSEL_MASK, 0xa1, 0xed, 3},
	{  3400000,     0, RK806_BUCK_ON_VSEL(7), RK806_BUCK_SLP_VSEL(7), RK806_BUCK_CONFIG(7), RK806_BUCK_VSEL_MASK, 0xee, 0xff, 3},
	/* buck 8 */
	{  500000,   6250, RK806_BUCK_ON_VSEL(8), RK806_BUCK_SLP_VSEL(8), RK806_BUCK_CONFIG(8), RK806_BUCK_VSEL_MASK, 0x00, 0xa0, 3},
	{  1500000, 25000, RK806_BUCK_ON_VSEL(8), RK806_BUCK_SLP_VSEL(8), RK806_BUCK_CONFIG(8), RK806_BUCK_VSEL_MASK, 0xa1, 0xed, 3},
	{  3400000,     0, RK806_BUCK_ON_VSEL(8), RK806_BUCK_SLP_VSEL(8), RK806_BUCK_CONFIG(8), RK806_BUCK_VSEL_MASK, 0xee, 0xff, 3},
	/* buck 9 */
	{  500000,   6250, RK806_BUCK_ON_VSEL(9), RK806_BUCK_SLP_VSEL(9), RK806_BUCK_CONFIG(9), RK806_BUCK_VSEL_MASK, 0x00, 0xa0, 3},
	{  1500000, 25000, RK806_BUCK_ON_VSEL(9), RK806_BUCK_SLP_VSEL(9), RK806_BUCK_CONFIG(9), RK806_BUCK_VSEL_MASK, 0xa1, 0xed, 3},
	{  3400000,     0, RK806_BUCK_ON_VSEL(9), RK806_BUCK_SLP_VSEL(9), RK806_BUCK_CONFIG(9), RK806_BUCK_VSEL_MASK, 0xee, 0xff, 3},
	/* buck 10 */
	{  500000,   6250, RK806_BUCK_ON_VSEL(10), RK806_BUCK_SLP_VSEL(10), RK806_BUCK_CONFIG(10), RK806_BUCK_VSEL_MASK, 0x00, 0xa0, 3},
	{  1500000, 25000, RK806_BUCK_ON_VSEL(10), RK806_BUCK_SLP_VSEL(10), RK806_BUCK_CONFIG(10), RK806_BUCK_VSEL_MASK, 0xa1, 0xed, 3},
	{  3400000,     0, RK806_BUCK_ON_VSEL(10), RK806_BUCK_SLP_VSEL(10), RK806_BUCK_CONFIG(10), RK806_BUCK_VSEL_MASK, 0xee, 0xff, 3},
};

static const struct rk8xx_reg_info rk806_nldo[] = {
	/* nldo1 */
	{  500000, 12500, RK806_NLDO_ON_VSEL(1), RK806_NLDO_SLP_VSEL(1), NA, RK806_NLDO_VSEL_MASK, 0x00, },
	{  3400000,    0, RK806_NLDO_ON_VSEL(1), RK806_NLDO_SLP_VSEL(1), NA, RK806_NLDO_VSEL_MASK, 0xE8, },
	/* nldo2 */
	{  500000, 12500, RK806_NLDO_ON_VSEL(2), RK806_NLDO_SLP_VSEL(2), NA, RK806_NLDO_VSEL_MASK, 0x00, },
	{  3400000,    0, RK806_NLDO_ON_VSEL(2), RK806_NLDO_SLP_VSEL(2), NA, RK806_NLDO_VSEL_MASK, 0xE8, },
	/* nldo3 */
	{  500000, 12500, RK806_NLDO_ON_VSEL(3), RK806_NLDO_SLP_VSEL(3), NA, RK806_NLDO_VSEL_MASK, 0x00, },
	{  3400000,    0, RK806_NLDO_ON_VSEL(3), RK806_NLDO_SLP_VSEL(3), NA, RK806_NLDO_VSEL_MASK, 0xE8, },
	/* nldo4 */
	{  500000, 12500, RK806_NLDO_ON_VSEL(4), RK806_NLDO_SLP_VSEL(4), NA, RK806_NLDO_VSEL_MASK, 0x00, },
	{  3400000,    0, RK806_NLDO_ON_VSEL(4), RK806_NLDO_SLP_VSEL(4), NA, RK806_NLDO_VSEL_MASK, 0xE8, },
	/* nldo5 */
	{  500000, 12500, RK806_NLDO_ON_VSEL(5), RK806_NLDO_SLP_VSEL(5), NA, RK806_NLDO_VSEL_MASK, 0x00, },
	{  3400000,    0, RK806_NLDO_ON_VSEL(5), RK806_NLDO_SLP_VSEL(5), NA, RK806_NLDO_VSEL_MASK, 0xE8, },
};

static const struct rk8xx_reg_info rk806_pldo[] = {
	/* pldo1 */
	{  500000, 12500, RK806_PLDO_ON_VSEL(1), RK806_PLDO_SLP_VSEL(1), NA, RK806_PLDO_VSEL_MASK, 0x00, },
	{  3400000,    0, RK806_PLDO_ON_VSEL(1), RK806_PLDO_SLP_VSEL(1), NA, RK806_PLDO_VSEL_MASK, 0xE8, },
	/* pldo2 */
	{  500000, 12500, RK806_PLDO_ON_VSEL(2), RK806_PLDO_SLP_VSEL(2), NA, RK806_PLDO_VSEL_MASK, 0x00, },
	{  3400000,    0, RK806_PLDO_ON_VSEL(2), RK806_PLDO_SLP_VSEL(2), NA, RK806_PLDO_VSEL_MASK, 0xE8, },
	/* pldo3 */
	{  500000, 12500, RK806_PLDO_ON_VSEL(3), RK806_PLDO_SLP_VSEL(3), NA, RK806_PLDO_VSEL_MASK, 0x00, },
	{  3400000,    0, RK806_PLDO_ON_VSEL(3), RK806_PLDO_SLP_VSEL(3), NA, RK806_PLDO_VSEL_MASK, 0xE8, },
	/* pldo4 */
	{  500000, 12500, RK806_PLDO_ON_VSEL(4), RK806_PLDO_SLP_VSEL(4), NA, RK806_PLDO_VSEL_MASK, 0x00, },
	{  3400000,    0, RK806_PLDO_ON_VSEL(4), RK806_PLDO_SLP_VSEL(4), NA, RK806_PLDO_VSEL_MASK, 0xE8, },
	/* pldo5 */
	{  500000, 12500, RK806_PLDO_ON_VSEL(5), RK806_PLDO_SLP_VSEL(5), NA, RK806_PLDO_VSEL_MASK, 0x00, },
	{  3400000,    0, RK806_PLDO_ON_VSEL(5), RK806_PLDO_SLP_VSEL(5), NA, RK806_PLDO_VSEL_MASK, 0xE8, },
	/* pldo6 */
	{  500000, 12500, RK806_PLDO_ON_VSEL(6), RK806_PLDO_SLP_VSEL(6), NA, RK806_PLDO_VSEL_MASK, 0x00, },
	{  3400000,    0, RK806_PLDO_ON_VSEL(6), RK806_PLDO_SLP_VSEL(6), NA, RK806_PLDO_VSEL_MASK, 0xE8, },
};

#if 0
static void io_mem_show(const char *label, unsigned long base, unsigned int start, unsigned int end)
{
  unsigned int val, offset = start, nr = 0;

  if (label)
    DEBUG ((DEBUG_ERROR, "%a:\n", label));

  DEBUG ((DEBUG_ERROR, "%08lx:  ", base + offset));
  for (offset = start; offset <= end; offset += 0x04) {
    if (nr >= 4) {
      DEBUG ((DEBUG_ERROR, "\n%08lx:  ", base + offset));
      nr = 0;
    }
    val = MmioRead32(base + offset);
    DEBUG ((DEBUG_ERROR, "%08lx ", val));
    nr++;
  }
  DEBUG ((DEBUG_ERROR, "\n"));
}
#endif

static RETURN_STATUS _spi_read(INT32 cs_id, UINT32 reg, UINT8 *buffer, INT32 len)
{
  UINT8 txbuf[8];
  RETURN_STATUS status;

  txbuf[0] = RK806_CMD_READ;
  txbuf[1] = reg;
  txbuf[2] = RK806_REG_H;

  SPI_SetCS(&gSPI, cs_id, 1);
  status = SPI_Configure(&gSPI, txbuf, NULL, 3);
  status = SPI_PioTransfer(&gSPI);
  SPI_Stop(&gSPI);
  status = SPI_Configure(&gSPI, NULL, buffer, 1);
  status = SPI_PioTransfer(&gSPI);
  SPI_Stop(&gSPI);
  SPI_SetCS(&gSPI, cs_id, 0);

  return status;
}

static RETURN_STATUS _spi_write(INT32 cs_id, UINT32 reg, const UINT8 *buffer, INT32 len)
{
  UINT8 txbuf[4];
  RETURN_STATUS status;

  txbuf[0] = RK806_CMD_WRITE;
  txbuf[1] = reg;
  txbuf[2] = RK806_REG_H;
  txbuf[3] = *buffer;
  SPI_SetCS(&gSPI, cs_id, 1);
  status = SPI_Configure(&gSPI, txbuf, NULL, 4);
  status = SPI_PioTransfer(&gSPI);
  
  SPI_Stop(&gSPI);
  SPI_SetCS(&gSPI, cs_id, 0);

  return status;
}

static RETURN_STATUS pmic_reg_read(INT32 cs_id,
			  UINT32 reg,
			  UINT8 *buffer,
			  INT32 len)
{
  RETURN_STATUS ret;

  ret = _spi_read(cs_id, reg, buffer, len);
  if (ret)
    DEBUG ((DEBUG_ERROR, "cs_id %d rk806 read reg(0x%x) error: %d buffer = %x\n",
           cs_id, reg, ret, buffer[0]));

  return ret;
}

static INT32 pmic_reg_write(INT32 cs_id,
			   UINT32 reg,
			   const UINT8 *buffer,
			   INT32 len)
{
  RETURN_STATUS ret;

  ret = _spi_write(cs_id, reg, buffer, len);
  if (ret)
    DEBUG ((DEBUG_ERROR, "cs_id %d rk806 write reg(0x%x) error: %d %x\n",
           cs_id, reg, ret, buffer[0]));

  return ret;
}

static RETURN_STATUS pmic_clrsetbits(INT32 cs_id, UINT32 reg, UINT32 clr, UINT32 set)
{
	UINT8 byte;
	RETURN_STATUS ret;

	ret = pmic_reg_read(cs_id, reg, &byte, 0x01);
	if (ret < 0)
		return ret;
	byte = (ret & ~clr) | set;

	 pmic_reg_write(cs_id, reg, &byte, 1);
	ret = pmic_reg_read(cs_id, reg, &byte, 0x01);
	return ret;
}
 
static const struct rk8xx_reg_info *get_buck_reg(INT32 num, INT32 uvolt)
{
	if (uvolt < 1500000)
		return &rk806_buck[num * 3 + 0];
	else if (uvolt < 3400000)
		return &rk806_buck[num * 3 + 1];
	else
		return &rk806_buck[num * 3 + 2];
}
 
static const struct rk8xx_reg_info *get_nldo_reg(INT32 num, INT32 uvolt)
{
	if (uvolt < 3400000)
		return &rk806_nldo[num * 2];
	else
		return &rk806_nldo[num * 2 + 1];
}

static const struct rk8xx_reg_info *get_pldo_reg(INT32 num, INT32 uvolt)
{
	if (uvolt < 3400000)
		return &rk806_pldo[num * 2];
	else
		return &rk806_pldo[num * 2 + 1];
}
static RETURN_STATUS buck_set_voltage(INT32 reg_id, INT32 uvolt)
{
	INT32 buck = reg_id & 0x0f;
	const struct rk8xx_reg_info *info = get_buck_reg(buck, uvolt);
	INT32 mask = info->vsel_mask;
	INT32 cs_id = (reg_id & 0xf00) >> 8;
	INT32 val;

	if (info->vsel_reg == NA)
		return RETURN_INVALID_PARAMETER;

	if (info->step_uv == 0)	/* Fixed voltage */
		val = info->min_sel;
	else
		val = ((uvolt - info->min_uv) / info->step_uv) + info->min_sel;

	DEBUG ((DEBUG_INFO, "%a: volt=%d, buck=%d, reg=0x%x, mask=0x%x, val=0x%x\n",
	      __func__, uvolt, buck + 1, info->vsel_reg, mask, val));

	return pmic_clrsetbits(cs_id, info->vsel_reg, mask, val);
}

static RETURN_STATUS nldo_set_voltage(INT32 reg_id, INT32 uvolt)
{
	INT32 ldo = reg_id & 0x0f;
	const struct rk8xx_reg_info *info = get_nldo_reg(ldo, uvolt);
	INT32 mask = info->vsel_mask;
	INT32 cs_id = (reg_id & 0xf00) >> 8;
	INT32 val;

	if (info->vsel_reg == NA)
		return RETURN_INVALID_PARAMETER;

	if (info->step_uv == 0)
		val = info->min_sel;
	else
		val = ((uvolt - info->min_uv) / info->step_uv) + info->min_sel;

	DEBUG ((DEBUG_INFO, "%a: volt=%d, ldo=%d, reg=0x%x, mask=0x%x, val=0x%x\n",
	      __func__, uvolt, ldo + 1, info->vsel_reg, mask, val));

	return pmic_clrsetbits(cs_id, info->vsel_reg, mask, val);
}

static RETURN_STATUS pldo_set_voltage(INT32 reg_id, INT32 uvolt)
{
	INT32 ldo = reg_id & 0x0f;
	const struct rk8xx_reg_info *info = get_pldo_reg(ldo, uvolt);
	INT32 mask = info->vsel_mask;
	INT32 cs_id = (reg_id & 0xf00) >> 8;
	INT32 val;

	if (info->vsel_reg == NA)
		return RETURN_INVALID_PARAMETER;

	if (info->step_uv == 0)
		val = info->min_sel;
	else
		val = ((uvolt - info->min_uv) / info->step_uv) + info->min_sel;

	DEBUG ((DEBUG_INFO, "%s: volt=%d, ldo=%d, reg=0x%x, mask=0x%x, val=0x%x\n",
	      __func__, uvolt, ldo + 1, info->vsel_reg, mask, val));

	return pmic_clrsetbits(cs_id, info->vsel_reg, mask, val);
}

static RETURN_STATUS buck_set_enable(INT32 reg_id, BOOLEAN enable)
{
	INT32 cs_id = (reg_id & 0xf00) >> 8;
	INT32 buck = reg_id & 0x0f;
	UINT8 value, en_reg;
	RETURN_STATUS ret;

	en_reg = RK806_POWER_EN(buck / 4);
	if (enable)
		value = ((1 << buck % 4) | (1 << (buck % 4 + 4)));
	else
		value = ((0 << buck % 4) | (1 << (buck % 4 + 4)));

	ret = pmic_reg_write(cs_id, en_reg, &value, 1);

	return ret;
}

static RETURN_STATUS nldo_set_enable(INT32 reg_id, BOOLEAN enable)
{
	INT32 cs_id = (reg_id & 0xf00) >> 8;
	INT32 ldo = reg_id & 0x0f;
	UINT8 value, en_reg;
	RETURN_STATUS ret;

		if (ldo < 4) {
			en_reg = RK806_NLDO_EN(0);
			if (enable)
				value = ((1 << ldo % 4) | (1 << (ldo % 4 + 4)));
			else
				value = ((0 << ldo % 4) | (1 << (ldo % 4 + 4)));
			ret =  pmic_reg_write(cs_id, en_reg, &value, 1);
		} else {
			en_reg = RK806_NLDO_EN(2);
			if (enable)
				value = 0x44;
			else
				value = 0x40;
			ret = pmic_reg_write(cs_id, en_reg, &value, 1);
		}

	return ret;
}

static RETURN_STATUS pldo_set_enable(INT32 reg_id, BOOLEAN enable)
{
	INT32 cs_id = (reg_id & 0xf00) >> 8;
	INT32 pldo = reg_id & 0x0f;
	UINT8 value, en_reg;
	RETURN_STATUS ret;

	if (pldo < 3) {
		en_reg = RK806_PLDO_EN(0);
		if (enable)
			value = RK806_PLDO0_2_SET(pldo);
		else
			value = RK806_PLDO0_2_CLR(pldo);
		ret =  pmic_reg_write(cs_id, en_reg, &value, 1);
	} else if (pldo == 3) {
		en_reg = RK806_PLDO_EN(1);
		if (enable) {
			value = ((1 << 0) | (1 << 4));
		} else {
			value = (1 << 4);
		}

		ret =  pmic_reg_write(cs_id, en_reg, &value, 1);
	} else if (pldo == 4) {
		en_reg = RK806_PLDO_EN(1);
		if (enable)
			value = ((1 << 1) | (1 << 5));
		else
			value = ((0 << 1) | (1 << 5));
		ret =  pmic_reg_write(cs_id, en_reg, &value, 1);
	} else if (pldo == 5) {
		en_reg = RK806_PLDO_EN(0);
		if (enable)
			value = ((1 << 0) | (1 << 4));
		else
			value = ((0 << 0) | (1 << 4));
		ret =  pmic_reg_write(cs_id, en_reg, &value, 1);
	}

	return ret;
}

void RK806RegulatorInit(struct regulator_init_data init_data)
{
	INT32 reg_id;
	INT32 init_voltage;

	init_voltage = init_data.init_voltage_mv;
	reg_id = init_data.reg_id;

	if ((reg_id & 0xf0) == BUCK) {
		buck_set_voltage(reg_id, init_voltage);
		buck_set_enable(reg_id, 1);
	} else if ((reg_id & 0xf0) == NLDO) {
		nldo_set_voltage(reg_id, init_voltage);
		nldo_set_enable(reg_id, 1);
	} if ((reg_id & 0xf0) == PLDO) {
		pldo_set_voltage(reg_id, init_voltage);
		pldo_set_enable(reg_id, 1);
	}
}

static
RETURN_STATUS
SpiCongig(struct SPI_HANDLE *pSPI)
{
  struct SPI_CONFIG *pSPIConfig = &pSPI->config;
  DEBUG ((DEBUG_INFO, "%a(%u): 0: %x\n", "SpiCongig", __LINE__, 0));

  /* Data width */
  pSPIConfig->nBytes = CR0_DATA_FRAME_SIZE_8BIT;

  /* CPOL */
  //pSPIConfig->clkPolarity = CR0_POLARITY_HIGH;
  pSPIConfig->clkPolarity = CR0_POLARITY_LOW;

  /* CPHA */
  //pSPIConfig->clkPhase = CR0_PHASE_2EDGE;
  pSPIConfig->clkPhase = CR0_PHASE_1EDGE;

  /* MSB or LSB */
  pSPIConfig->firstBit = CR0_FIRSTBIT_MSB;
  //pSPIConfig->firstBit = CR0_FIRSTBIT_LSB;

  /* Master or Slave */
  //pSPIConfig->opMode = CR0_OPM_SLAVE;
  pSPIConfig->opMode = CR0_OPM_MASTER;

  /* CSM cycles */
  pSPIConfig->csm = 0;

  pSPI->maxFreq = 24000000;
  pSPIConfig->speed = 2000000;

  if (pSPI->config.speed > HAL_SPI_MASTER_MAX_SCLK_OUT) {
    pSPI->config.speed = HAL_SPI_MASTER_MAX_SCLK_OUT;
  }

  return RETURN_SUCCESS;
}

RETURN_STATUS RK806Init(void)
{
  UINT32 base = FixedPcdGet32 (SpiRK806BaseAddr);

  Rk806SpiIomux();

  DEBUG ((DEBUG_INIT, "%a(%u): base: %x\n", "RK806Init", __LINE__, base));

  SPI_Init(&gSPI, base);
  SpiCongig(&gSPI);

  return RETURN_SUCCESS;
}
