/*
 * This file is part of the coreboot project.
 *
 * Copyright (C) 2016 Marvell, Inc.
 *
 * 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; version 2 of the License.
 *
 * 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.
 */

#include <stddef.h>
#include <stdint.h>
#include <stdlib.h>

#include <arch/io.h>
#include <console/console.h>
#include <soc/apmu.h>
#include <soc/clock.h>

void apmu_start(void)
{
	u32 reg;

	printk(BIOS_DEBUG, "APMU init...\n");

	/* TODO: remove in real silicon */
	write32(&mvmap2315_apmu_pwrctl->scratch0, 0x900dc0de);

	/* Dedicate LCM area to the APMU */
	reg = MVMAP2315_LCM_START_BANK |
	      MVMAP2315_LCM_END_BANK |
	      MVMAP2315_LCM_DIRACCESS_EN;
	write32(&mvmap2315_lcm_regs->lcm_access, reg);

	/* Taking APMU out of reset */
	setbits_le32(&mvmap2315_apmu_clk->apaonclk_apmucpu_clkgenconfig,
		     MVMAP2315_APMU_CLK_EN);

	setbits_le32(&mvmap2315_apmu_clk->apaonclk_apmucpu_clkgenconfig,
		     MVMAP2315_APMU_CLK_RSTN);
}

static int apmu_sending_cmd(u32 cmd)
{
	int success;
	u32 timeout = 0;

	printk(BIOS_DEBUG, "sending cmd: %x\n", cmd);

	/* Send a sync command */
	write32(&mvmap2315_apmu_pwrctl->pwrcmdfifo,
		cmd | MVMAP2315_APMU_PWRCMDFIFO_DONEIRQ);

	while (!(read32(&mvmap2315_apmu_pwrctl->intstatus) &
	       (MVMAP2315_APMU_ISTCLR_DONE |
	       MVMAP2315_APMU_ISTCLR_ERROR)) &&
	       (timeout < MVMAP2315_APMU_PWRCMDFIFO_TIMEOUT))
		timeout++;

	if (timeout >= MVMAP2315_APMU_PWRCMDFIFO_TIMEOUT) {
		printk(BIOS_DEBUG, "cmd Timed out\n");
		success = -2;
	}

	if (read32(&mvmap2315_apmu_pwrctl->intstatus) &
					MVMAP2315_APMU_ISTCLR_DONE) {
		printk(BIOS_DEBUG, "Completed cmd: %x\n",
		       read32(&mvmap2315_apmu_pwrctl->doneirq));

		setbits_le32(&mvmap2315_apmu_pwrctl->intstatusclear,
			     MVMAP2315_APMU_ISTCLR_DONE);
		success = 0;

	} else if (read32(&mvmap2315_apmu_pwrctl->intstatus) &
					MVMAP2315_APMU_ISTCLR_ERROR) {
		printk(BIOS_DEBUG, "Error cmd: %x\n",
		       read32(&mvmap2315_apmu_pwrctl->errorirq));

		setbits_le32(&mvmap2315_apmu_pwrctl->intstatusclear,
			     MVMAP2315_APMU_ISTCLR_ERROR);

		success = -1;
	}

	return success;
}

int apmu_set_dev(u32 dev, u32 state)
{
	u32 cmd;

	cmd = MVMAP2315_APMU_PWRCMDFIFO_DONEIRQ;
	cmd |= MVMAP2315_APMU_OPCODE_PWRST << MVMAP2315_APMU_OPCODE_SHIFT;
	cmd |= state << MVMAP2315_APMU_DEV_STATE_SHIFT;
	cmd |= dev << MVMAP2315_APMU_DEV_OBJECT_SHIFT;

	return apmu_sending_cmd(cmd);
}

int apmu_set_pll(u32 dev, u32 state, u32 freq)
{
	u32 cmd;

	cmd = MVMAP2315_APMU_PWRCMDFIFO_DONEIRQ;
	cmd |= MVMAP2315_APMU_OPCODE_PWRST << MVMAP2315_APMU_OPCODE_SHIFT;
	cmd |= freq << MVMAP2315_APMU_DEV_FREQ_SHIFT;
	cmd |= state << MVMAP2315_APMU_DEV_STATE_SHIFT;
	cmd |= dev << MVMAP2315_APMU_DEV_OBJECT_SHIFT;

	return apmu_sending_cmd(cmd);
}


int apmu_set_clk(u32 clk, u32 state, u32 div, u32 src)
{
	u32 cmd;

	cmd = MVMAP2315_APMU_PWRCMDFIFO_DONEIRQ;
	cmd |= MVMAP2315_APMU_OPCODE_CLKST << MVMAP2315_APMU_OPCODE_SHIFT;
	cmd |= state << MVMAP2315_APMU_CLK_STATE_SHIFT;
	cmd |= div << MVMAP2315_APMU_CLK_DIV_SHIFT;
	cmd |= src << MVMAP2315_APMU_CLK_SOURCE_SHIFT;
	cmd |= clk << MVMAP2315_APMU_CLK_OBJECT_SHIFT;

	return apmu_sending_cmd(cmd);
}