/* Copyright (c) 2016-2017, The Linux Foundation. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 2 and
 * only version 2 as published by the Free Software Foundation.
 *
 * 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.
 */

#define pr_fmt(fmt)	"FG: %s: " fmt, __func__

#include "fg-core.h"
#include "fg-reg.h"

/* Generic definitions */
#define RETRY_COUNT		3
#define BYTES_PER_SRAM_WORD	4

enum {
	FG_READ = 0,
	FG_WRITE,
};

static int fg_set_address(struct fg_chip *chip, u16 address)
{
	u8 buffer[2];
	int rc;

	buffer[0] = address & 0xFF;
	/* MSB has to be written zero */
	buffer[1] = 0;

	rc = fg_write(chip, MEM_IF_ADDR_LSB(chip), buffer, 2);
	if (rc < 0) {
		pr_err("failed to write to 0x%04X, rc=%d\n",
			MEM_IF_ADDR_LSB(chip), rc);
		return rc;
	}

	return rc;
}

static int fg_config_access_mode(struct fg_chip *chip, bool access, bool burst)
{
	int rc;
	u8 intf_ctl = 0;

	fg_dbg(chip, FG_SRAM_READ | FG_SRAM_WRITE, "access: %d burst: %d\n",
		access, burst);

	WARN_ON(burst && chip->use_ima_single_mode);
	intf_ctl = ((access == FG_WRITE) ? IMA_WR_EN_BIT : 0) |
			(burst ? MEM_ACS_BURST_BIT : 0);

	rc = fg_masked_write(chip, MEM_IF_IMA_CTL(chip), IMA_CTL_MASK,
			intf_ctl);
	if (rc < 0) {
		pr_err("failed to write to 0x%04x, rc=%d\n",
			MEM_IF_IMA_CTL(chip), rc);
		return -EIO;
	}

	return rc;
}

static int fg_run_iacs_clear_sequence(struct fg_chip *chip)
{
	u8 val, hw_sts, exp_sts;
	int rc, tries = 250;

	/*
	 * Values to write for running IACS clear sequence comes from
	 * hardware documentation.
	 */
	rc = fg_masked_write(chip, MEM_IF_IMA_CFG(chip),
			IACS_CLR_BIT | STATIC_CLK_EN_BIT,
			IACS_CLR_BIT | STATIC_CLK_EN_BIT);
	if (rc < 0) {
		pr_err("failed to write 0x%04x, rc=%d\n", MEM_IF_IMA_CFG(chip),
			rc);
		return rc;
	}

	rc = fg_config_access_mode(chip, FG_READ, false);
	if (rc < 0) {
		pr_err("failed to write to 0x%04x, rc=%d\n",
			MEM_IF_IMA_CTL(chip), rc);
		return rc;
	}

	rc = fg_masked_write(chip, MEM_IF_MEM_INTF_CFG(chip),
				MEM_ACCESS_REQ_BIT | IACS_SLCT_BIT,
				MEM_ACCESS_REQ_BIT | IACS_SLCT_BIT);
	if (rc < 0) {
		pr_err("failed to set ima_req_access bit rc=%d\n", rc);
		return rc;
	}

	/* Delay for the clock to reach FG */
	usleep_range(35, 40);

	while (1) {
		val = 0;
		rc = fg_write(chip, MEM_IF_ADDR_MSB(chip), &val, 1);
		if (rc < 0) {
			pr_err("failed to write 0x%04x, rc=%d\n",
				MEM_IF_ADDR_MSB(chip), rc);
			return rc;
		}

		val = 0;
		rc = fg_write(chip, MEM_IF_WR_DATA3(chip), &val, 1);
		if (rc < 0) {
			pr_err("failed to write 0x%04x, rc=%d\n",
				MEM_IF_WR_DATA3(chip), rc);
			return rc;
		}

		rc = fg_read(chip, MEM_IF_RD_DATA3(chip), &val, 1);
		if (rc < 0) {
			pr_err("failed to read 0x%04x, rc=%d\n",
				MEM_IF_RD_DATA3(chip), rc);
			return rc;
		}

		/* Delay for IMA hardware to clear */
		usleep_range(35, 40);

		rc = fg_read(chip, MEM_IF_IMA_HW_STS(chip), &hw_sts, 1);
		if (rc < 0) {
			pr_err("failed to read ima_hw_sts rc=%d\n", rc);
			return rc;
		}

		if (hw_sts != 0)
			continue;

		rc = fg_read(chip, MEM_IF_IMA_EXP_STS(chip), &exp_sts, 1);
		if (rc < 0) {
			pr_err("failed to read ima_exp_sts rc=%d\n", rc);
			return rc;
		}

		if (exp_sts == 0 || !(--tries))
			break;
	}

	if (!tries)
		pr_err("Failed to clear the error? hw_sts: %x exp_sts: %d\n",
			hw_sts, exp_sts);

	rc = fg_masked_write(chip, MEM_IF_IMA_CFG(chip), IACS_CLR_BIT, 0);
	if (rc < 0) {
		pr_err("failed to write 0x%04x, rc=%d\n", MEM_IF_IMA_CFG(chip),
			rc);
		return rc;
	}

	udelay(5);

	rc = fg_masked_write(chip, MEM_IF_MEM_INTF_CFG(chip),
				MEM_ACCESS_REQ_BIT | IACS_SLCT_BIT, 0);
	if (rc < 0) {
		pr_err("failed to write to 0x%04x, rc=%d\n",
			MEM_IF_MEM_INTF_CFG(chip), rc);
		return rc;
	}

	/* Delay before next transaction is attempted */
	usleep_range(35, 40);
	fg_dbg(chip, FG_SRAM_READ | FG_SRAM_WRITE, "IACS clear sequence complete\n");
	return rc;
}

int fg_clear_dma_errors_if_any(struct fg_chip *chip)
{
	int rc;
	u8 dma_sts;
	bool error_present;

	rc = fg_read(chip, MEM_IF_DMA_STS(chip), &dma_sts, 1);
	if (rc < 0) {
		pr_err("failed to read addr=0x%04x, rc=%d\n",
			MEM_IF_DMA_STS(chip), rc);
		return rc;
	}
	fg_dbg(chip, FG_STATUS, "dma_sts: %x\n", dma_sts);

	error_present = dma_sts & (DMA_WRITE_ERROR_BIT | DMA_READ_ERROR_BIT);
	rc = fg_masked_write(chip, MEM_IF_DMA_CTL(chip), DMA_CLEAR_LOG_BIT,
			error_present ? DMA_CLEAR_LOG_BIT : 0);
	if (rc < 0) {
		pr_err("failed to write addr=0x%04x, rc=%d\n",
			MEM_IF_DMA_CTL(chip), rc);
		return rc;
	}

	return 0;
}

int fg_clear_ima_errors_if_any(struct fg_chip *chip, bool check_hw_sts)
{
	int rc = 0;
	u8 err_sts, exp_sts = 0, hw_sts = 0;
	bool run_err_clr_seq = false;

	rc = fg_read(chip, MEM_IF_IMA_EXP_STS(chip), &exp_sts, 1);
	if (rc < 0) {
		pr_err("failed to read ima_exp_sts rc=%d\n", rc);
		return rc;
	}

	rc = fg_read(chip, MEM_IF_IMA_HW_STS(chip), &hw_sts, 1);
	if (rc < 0) {
		pr_err("failed to read ima_hw_sts rc=%d\n", rc);
		return rc;
	}

	rc = fg_read(chip, MEM_IF_IMA_ERR_STS(chip), &err_sts, 1);
	if (rc < 0) {
		pr_err("failed to read ima_err_sts rc=%d\n", rc);
		return rc;
	}

	fg_dbg(chip, FG_SRAM_READ | FG_SRAM_WRITE, "ima_err_sts=%x ima_exp_sts=%x ima_hw_sts=%x\n",
		err_sts, exp_sts, hw_sts);

	if (check_hw_sts) {
		/*
		 * Lower nibble should be equal to upper nibble before SRAM
		 * transactions begins from SW side. If they are unequal, then
		 * the error clear sequence should be run irrespective of IMA
		 * exception errors.
		 */
		if ((hw_sts & 0x0F) != hw_sts >> 4) {
			pr_err("IMA HW not in correct state, hw_sts=%x\n",
				hw_sts);
			run_err_clr_seq = true;
		}
	}

	if (exp_sts & (IACS_ERR_BIT | XCT_TYPE_ERR_BIT | DATA_RD_ERR_BIT |
		DATA_WR_ERR_BIT | ADDR_BURST_WRAP_BIT | ADDR_STABLE_ERR_BIT)) {
		pr_err("IMA exception bit set, exp_sts=%x\n", exp_sts);
		run_err_clr_seq = true;
	}

	if (run_err_clr_seq) {
		/* clear the error */
		rc = fg_run_iacs_clear_sequence(chip);
		if (rc < 0) {
			pr_err("failed to run iacs clear sequence rc=%d\n", rc);
			return rc;
		}

		/* Retry again as there was an error in the transaction */
		return -EAGAIN;
	}

	return rc;
}

static int fg_check_iacs_ready(struct fg_chip *chip)
{
	int rc = 0, tries = 250;
	u8 ima_opr_sts = 0;

	/*
	 * Additional delay to make sure IACS ready bit is set after
	 * Read/Write operation.
	 */

	usleep_range(30, 35);
	while (1) {
		rc = fg_read(chip, MEM_IF_IMA_OPR_STS(chip), &ima_opr_sts, 1);
		if (rc < 0) {
			pr_err("failed to read 0x%04x, rc=%d\n",
				MEM_IF_IMA_OPR_STS(chip), rc);
			return rc;
		}

		if (ima_opr_sts & IACS_RDY_BIT)
			break;

		if (!(--tries))
			break;

		/* delay for iacs_ready to be asserted */
		usleep_range(5000, 7000);
	}

	if (!tries) {
		pr_err("IACS_RDY not set, opr_sts: %d\n", ima_opr_sts);
		/* check for error condition */
		rc = fg_clear_ima_errors_if_any(chip, false);
		if (rc < 0) {
			if (rc != -EAGAIN)
				pr_err("Failed to check for ima errors rc=%d\n",
					rc);
			return rc;
		}

		return -EBUSY;
	}

	return 0;
}

static int __fg_interleaved_mem_write(struct fg_chip *chip, u16 address,
				int offset, u8 *val, int len)
{
	int rc = 0, i;
	u8 *ptr = val, byte_enable = 0, num_bytes = 0;

	fg_dbg(chip, FG_SRAM_WRITE, "length %d addr=%02X offset=%d\n", len,
		address, offset);

	while (len > 0) {
		num_bytes = (offset + len) > BYTES_PER_SRAM_WORD ?
				(BYTES_PER_SRAM_WORD - offset) : len;

		/* write to byte_enable */
		for (i = offset; i < (offset + num_bytes); i++)
			byte_enable |= BIT(i);

		rc = fg_write(chip, MEM_IF_IMA_BYTE_EN(chip), &byte_enable, 1);
		if (rc < 0) {
			pr_err("Unable to write to byte_en_reg rc=%d\n",
				rc);
			return rc;
		}

		/* write data */
		rc = fg_write(chip, MEM_IF_WR_DATA0(chip) + offset, ptr,
				num_bytes);
		if (rc < 0) {
			pr_err("failed to write to 0x%04x, rc=%d\n",
				MEM_IF_WR_DATA0(chip) + offset, rc);
			return rc;
		}

		/*
		 * The last-byte WR_DATA3 starts the write transaction.
		 * Write a dummy value to WR_DATA3 if it does not have
		 * valid data. This dummy data is not written to the
		 * SRAM as byte_en for WR_DATA3 is not set.
		 */
		if (!(byte_enable & BIT(3))) {
			u8 dummy_byte = 0x0;

			rc = fg_write(chip, MEM_IF_WR_DATA3(chip), &dummy_byte,
					1);
			if (rc < 0) {
				pr_err("failed to write dummy-data to WR_DATA3 rc=%d\n",
					rc);
				return rc;
			}
		}

		/* check for error condition */
		rc = fg_clear_ima_errors_if_any(chip, false);
		if (rc < 0) {
			if (rc == -EAGAIN)
				pr_err("IMA error cleared, address [%d %d] len %d\n",
					address, offset, len);
			else
				pr_err("Failed to check for ima errors rc=%d\n",
					rc);
			return rc;
		}

		ptr += num_bytes;
		len -= num_bytes;
		offset = byte_enable = 0;

		if (chip->use_ima_single_mode && len) {
			address++;
			rc = fg_set_address(chip, address);
			if (rc < 0) {
				pr_err("failed to set address rc = %d\n", rc);
				return rc;
			}
		}

		rc = fg_check_iacs_ready(chip);
		if (rc < 0) {
			pr_debug("IACS_RDY failed rc=%d\n", rc);
			return rc;
		}
	}

	return rc;
}

static int __fg_interleaved_mem_read(struct fg_chip *chip, u16 address,
				int offset, u8 *val, int len)
{
	int rc = 0, total_len;
	u8 *rd_data = val, num_bytes;
	char str[DEBUG_PRINT_BUFFER_SIZE];

	fg_dbg(chip, FG_SRAM_READ, "length %d addr=%02X\n", len, address);

	total_len = len;
	while (len > 0) {
		num_bytes = (offset + len) > BYTES_PER_SRAM_WORD ?
				(BYTES_PER_SRAM_WORD - offset) : len;
		rc = fg_read(chip, MEM_IF_RD_DATA0(chip) + offset, rd_data,
				num_bytes);
		if (rc < 0) {
			pr_err("failed to read 0x%04x, rc=%d\n",
				MEM_IF_RD_DATA0(chip) + offset, rc);
			return rc;
		}

		rd_data += num_bytes;
		len -= num_bytes;
		offset = 0;

		/* check for error condition */
		rc = fg_clear_ima_errors_if_any(chip, false);
		if (rc < 0) {
			if (rc == -EAGAIN)
				pr_err("IMA error cleared, address [%d %d] len %d\n",
					address, offset, len);
			else
				pr_err("Failed to check for ima errors rc=%d\n",
					rc);
			return rc;
		}

		if (chip->use_ima_single_mode) {
			if (len) {
				address++;
				rc = fg_set_address(chip, address);
				if (rc < 0) {
					pr_err("failed to set address rc = %d\n",
						rc);
					return rc;
				}
			}
		} else {
			if (len && len < BYTES_PER_SRAM_WORD) {
				/*
				 * Move to single mode. Changing address is not
				 * required here as it must be in burst mode.
				 * Address will get incremented internally by FG
				 * HW once the MSB of RD_DATA is read.
				 */
				rc = fg_config_access_mode(chip, FG_READ,
								false);
				if (rc < 0) {
					pr_err("failed to move to single mode rc=%d\n",
						rc);
					return -EIO;
				}
			}
		}

		rc = fg_check_iacs_ready(chip);
		if (rc < 0) {
			pr_debug("IACS_RDY failed rc=%d\n", rc);
			return rc;
		}
	}

	if (*chip->debug_mask & FG_SRAM_READ) {
		fill_string(str, DEBUG_PRINT_BUFFER_SIZE, val, total_len);
		pr_info("data read: %s\n", str);
	}

	return rc;
}

static int fg_get_mem_access_status(struct fg_chip *chip, bool *status)
{
	int rc;
	u8 mem_if_sts;

	rc = fg_read(chip, MEM_IF_MEM_INTF_CFG(chip), &mem_if_sts, 1);
	if (rc < 0) {
		pr_err("failed to read rif_mem status rc=%d\n", rc);
		return rc;
	}

	*status = mem_if_sts & MEM_ACCESS_REQ_BIT;
	return 0;
}

static bool is_mem_access_available(struct fg_chip *chip, int access)
{
	bool rif_mem_sts = true;
	int rc, time_count = 0;

	while (1) {
		rc = fg_get_mem_access_status(chip, &rif_mem_sts);
		if (rc < 0)
			return rc;

		/* This is an inverting logic */
		if (!rif_mem_sts)
			break;

		fg_dbg(chip, FG_SRAM_READ | FG_SRAM_WRITE, "MEM_ACCESS_REQ is not clear yet for IMA_%s\n",
			access ? "write" : "read");

		/*
		 * Try this no more than 4 times. If MEM_ACCESS_REQ is not
		 * clear, then return an error instead of waiting for it again.
		 */
		if  (time_count > 4) {
			pr_err("Tried 4 times(~16ms) polling MEM_ACCESS_REQ\n");
			return false;
		}

		/* Wait for 4ms before reading MEM_ACCESS_REQ again */
		usleep_range(4000, 4100);
		time_count++;
	}
	return true;
}

static int fg_interleaved_mem_config(struct fg_chip *chip, u8 *val,
		u16 address, int offset, int len, bool access)
{
	int rc = 0;
	bool burst_mode = false;

	if (!is_mem_access_available(chip, access))
		return -EBUSY;

	/* configure for IMA access */
	rc = fg_masked_write(chip, MEM_IF_MEM_INTF_CFG(chip),
				MEM_ACCESS_REQ_BIT | IACS_SLCT_BIT,
				MEM_ACCESS_REQ_BIT | IACS_SLCT_BIT);
	if (rc < 0) {
		pr_err("failed to set ima_req_access bit rc=%d\n", rc);
		return rc;
	}

	/* configure for the read/write, single/burst mode */
	burst_mode = chip->use_ima_single_mode ? false : ((offset + len) > 4);
	rc = fg_config_access_mode(chip, access, burst_mode);
	if (rc < 0) {
		pr_err("failed to set memory access rc = %d\n", rc);
		return rc;
	}

	rc = fg_check_iacs_ready(chip);
	if (rc < 0) {
		pr_err_ratelimited("IACS_RDY failed rc=%d\n", rc);
		return rc;
	}

	rc = fg_set_address(chip, address);
	if (rc < 0) {
		pr_err("failed to set address rc = %d\n", rc);
		return rc;
	}

	if (access == FG_READ) {
		rc = fg_check_iacs_ready(chip);
		if (rc < 0) {
			pr_debug("IACS_RDY failed rc=%d\n", rc);
			return rc;
		}
	}

	return rc;
}

static int fg_get_beat_count(struct fg_chip *chip, u8 *count)
{
	int rc;

	rc = fg_read(chip, MEM_IF_FG_BEAT_COUNT(chip), count, 1);
	*count &= BEAT_COUNT_MASK;
	return rc;
}

int fg_interleaved_mem_read(struct fg_chip *chip, u16 address, u8 offset,
				u8 *val, int len)
{
	int rc = 0, ret;
	u8 start_beat_count, end_beat_count, count = 0;
	bool retry = false;

	if (offset > 3) {
		pr_err("offset too large %d\n", offset);
		return -EINVAL;
	}

retry:
	if (count >= RETRY_COUNT) {
		pr_err("Tried %d times\n", RETRY_COUNT);
		retry = false;
		goto out;
	}

	rc = fg_interleaved_mem_config(chip, val, address, offset, len,
					FG_READ);
	if (rc < 0) {
		pr_err("failed to configure SRAM for IMA rc = %d\n", rc);
		count++;
		retry = true;
		goto out;
	}

	/* read the start beat count */
	rc = fg_get_beat_count(chip, &start_beat_count);
	if (rc < 0) {
		pr_err("failed to read beat count rc=%d\n", rc);
		count++;
		retry = true;
		goto out;
	}

	/* read data */
	rc = __fg_interleaved_mem_read(chip, address, offset, val, len);
	if (rc < 0) {
		count++;
		if (rc == -EAGAIN) {
			pr_err("IMA read failed retry_count = %d\n", count);
			goto retry;
		}
		pr_err("failed to read SRAM address rc = %d\n", rc);
		retry = true;
		goto out;
	}

	/* read the end beat count */
	rc = fg_get_beat_count(chip, &end_beat_count);
	if (rc < 0) {
		pr_err("failed to read beat count rc=%d\n", rc);
		count++;
		retry = true;
		goto out;
	}

	fg_dbg(chip, FG_SRAM_READ, "Start beat_count = %x End beat_count = %x\n",
		start_beat_count, end_beat_count);

	if (start_beat_count != end_beat_count) {
		fg_dbg(chip, FG_SRAM_READ, "Beat count(%d/%d) do not match - retry transaction\n",
			start_beat_count, end_beat_count);
		count++;
		retry = true;
	}
out:
	/* Release IMA access */
	ret = fg_masked_write(chip, MEM_IF_MEM_INTF_CFG(chip),
				MEM_ACCESS_REQ_BIT | IACS_SLCT_BIT, 0);
	if (rc < 0 && ret < 0) {
		pr_err("failed to reset IMA access bit ret = %d\n", ret);
		return ret;
	}

	if (retry) {
		retry = false;
		goto retry;
	}

	return rc;
}

int fg_interleaved_mem_write(struct fg_chip *chip, u16 address, u8 offset,
				u8 *val, int len, bool atomic_access)
{
	int rc = 0, ret;
	u8 start_beat_count, end_beat_count, count = 0;
	bool retry = false;

	if (offset > 3) {
		pr_err("offset too large %d\n", offset);
		return -EINVAL;
	}

retry:
	if (count >= RETRY_COUNT) {
		pr_err("Tried %d times\n", RETRY_COUNT);
		retry = false;
		goto out;
	}

	rc = fg_interleaved_mem_config(chip, val, address, offset, len,
					FG_WRITE);
	if (rc < 0) {
		pr_err("failed to configure SRAM for IMA rc = %d\n", rc);
		count++;
		retry = true;
		goto out;
	}

	/* read the start beat count */
	rc = fg_get_beat_count(chip, &start_beat_count);
	if (rc < 0) {
		pr_err("failed to read beat count rc=%d\n", rc);
		count++;
		retry = true;
		goto out;
	}

	/* write data */
	rc = __fg_interleaved_mem_write(chip, address, offset, val, len);
	if (rc < 0) {
		count++;
		if (rc == -EAGAIN) {
			pr_err("IMA write failed retry_count = %d\n", count);
			goto retry;
		}
		pr_err("failed to write SRAM address rc = %d\n", rc);
		retry = true;
		goto out;
	}

	/* read the end beat count */
	rc = fg_get_beat_count(chip, &end_beat_count);
	if (rc < 0) {
		pr_err("failed to read beat count rc=%d\n", rc);
		count++;
		retry = true;
		goto out;
	}

	if (atomic_access && start_beat_count != end_beat_count)
		pr_err("Start beat_count = %x End beat_count = %x\n",
			start_beat_count, end_beat_count);
out:
	/* Release IMA access */
	ret = fg_masked_write(chip, MEM_IF_MEM_INTF_CFG(chip),
				MEM_ACCESS_REQ_BIT | IACS_SLCT_BIT, 0);
	if (rc < 0 && ret < 0) {
		pr_err("failed to reset IMA access bit ret = %d\n", ret);
		return ret;
	}

	if (retry) {
		retry = false;
		goto retry;
	}

	/* Return the error we got before releasing memory access */
	return rc;
}

int fg_ima_init(struct fg_chip *chip)
{
	int rc;

	/*
	 * Change the FG_MEM_INT interrupt to track IACS_READY
	 * condition instead of end-of-transaction. This makes sure
	 * that the next transaction starts only after the hw is ready.
	 */
	rc = fg_masked_write(chip, MEM_IF_IMA_CFG(chip), IACS_INTR_SRC_SLCT_BIT,
				IACS_INTR_SRC_SLCT_BIT);
	if (rc < 0) {
		pr_err("failed to configure interrupt source %d\n", rc);
		return rc;
	}

	/* Clear DMA errors if any before clearing IMA errors */
	rc = fg_clear_dma_errors_if_any(chip);
	if (rc < 0) {
		pr_err("Error in checking DMA errors rc:%d\n", rc);
		return rc;
	}

	/* Clear IMA errors if any before SRAM transactions can begin */
	rc = fg_clear_ima_errors_if_any(chip, true);
	if (rc < 0 && rc != -EAGAIN) {
		pr_err("Error in checking IMA errors rc:%d\n", rc);
		return rc;
	}

	return 0;
}
