/*
 * 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/gic.h>
#include <soc/mcu.h>

static u8 mvmap2315_calc_checksum(const void  *data, u32 size)
{
	u8  csum;
	const u8 *bytes = data;
	int i;

	for (i = csum = 0; i < size; i++)
		csum += bytes[i];

	return (~csum) & 0xFF;
}

void mcu_irq(void)
{
	printk(BIOS_DEBUG, "waiting for MCU msg...\n");
	while (!(read32(&mvmap2315_bcm_gicd->ispendr2) &
	      MVMAP2315_MCU_IPC_IRQ))
		;
}

void send_mcu_msg(void *msg, u32 size)
{
	printk(BIOS_DEBUG, "sending msg to MCU...\n");
	write32(&mvmap2315_sp_ipc->wdr0, (u32)msg);
	setbits_le32(&mvmap2315_sp_ipc->isrw, MVMAP2315_IPC_IRQSET_MSGSENT);
}

void *receive_mcu_msg(void)
{
	void *msg;

	printk(BIOS_DEBUG, "receiving msg to MCU...\n");

	write32(&mvmap2315_sp_ipc->dummy, 0xA5A5A5A5);

	switch (read32(&mvmap2315_sp_ipc->iir)) {
	case MSG_READY:
		msg = (void *)(read32(&mvmap2315_sp_ipc->rdr0)
			       + MVMAP2315_MCU_MSG_OFFSET);
		setbits_le32(&mvmap2315_sp_ipc->icr,
			     MVMAP2315_IPC_IRQCLR_MSGREADY);
		setbits_le32(&mvmap2315_sp_ipc->isrw, MVMAP2315_IPC_IRQACK);
		break;
	case MSG_RECEIVED:
		msg = NULL;
		setbits_le32(&mvmap2315_sp_ipc->icr,
			     MVMAP2315_IPC_IRQCLR_MSGRECEIVED);
		break;
	case MSG_READY_RECEIVED:
		msg = (void *)(read32(&mvmap2315_sp_ipc->rdr0)
			       + MVMAP2315_MCU_MSG_OFFSET);
		setbits_le32(&mvmap2315_sp_ipc->icr,
			     MVMAP2315_IPC_IRQCLR_MSGREADY);
		setbits_le32(&mvmap2315_sp_ipc->icr,
			     MVMAP2315_IPC_IRQCLR_MSGRECEIVED);
		setbits_le32(&mvmap2315_sp_ipc->isrw, MVMAP2315_IPC_IRQACK);

		break;
	default:
		msg = NULL;
		write32(&mvmap2315_sp_ipc->icr, read32(&mvmap2315_sp_ipc->iir));
		break;
	}

	return msg;
}

void sned_hash_msg(u8 subcmd)
{
	printk(BIOS_DEBUG, "requesting MCU hash...\n");

	mvmap2315_mcu_msg_buff->protocol_version = 0x3;
	mvmap2315_mcu_msg_buff->cmd_id = 0x2A;
	mvmap2315_mcu_msg_buff->reserved0 = 0x0;
	mvmap2315_mcu_msg_buff->length = 76;
	mvmap2315_mcu_msg_buff->csum = 0;
	mvmap2315_mcu_msg_buff->hash_subcmd = subcmd;
	mvmap2315_mcu_msg_buff->hash_type = 0;
	mvmap2315_mcu_msg_buff->nonce_size = 0;
	mvmap2315_mcu_msg_buff->reserved1 = 0;
	mvmap2315_mcu_msg_buff->offset = 0;
	mvmap2315_mcu_msg_buff->size = 32;
	mvmap2315_mcu_msg_buff->csum = mvmap2315_calc_checksum(
				       (void *)mvmap2315_mcu_msg_buff,
				       sizeof(struct sp_hash_request_msg));

	send_mcu_msg((void *)mvmap2315_mcu_msg_buff,
		     sizeof(struct sp_hash_request_msg));
}

void *receive_hash_msg_respond(void)
{
	struct mcu_hash_msg *mvmap2315_hash_msg_response;

	printk(BIOS_DEBUG, "waiting for MCU hash response msg...\n");

	mcu_irq();

	mvmap2315_hash_msg_response = receive_mcu_msg();

	mcu_irq();

	receive_mcu_msg();

	return mvmap2315_hash_msg_response;
}