/*
 * ----------------------------------------------------------------------------
 * drivers/nfc/st95hf/spi.c function definitions for SPI communication
 * ----------------------------------------------------------------------------
 * Copyright (C) 2015 STMicroelectronics Pvt. Ltd. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms and conditions of the GNU General Public License,
 * 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.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, see <http://www.gnu.org/licenses/>.
 */

#include "spi.h"

/* Function to send user provided buffer to ST95HF through SPI */
int st95hf_spi_send(struct st95hf_spi_context *spicontext,
		    unsigned char *buffertx,
		    int datalen,
		    enum req_type reqtype)
{
	struct spi_message m;
	int result = 0;
	struct spi_device *spidev = spicontext->spidev;
	struct spi_transfer tx_transfer = {
		.tx_buf = buffertx,
		.len = datalen,
	};

	mutex_lock(&spicontext->spi_lock);

	if (reqtype == SYNC) {
		spicontext->req_issync = true;
		reinit_completion(&spicontext->done);
	} else {
		spicontext->req_issync = false;
	}

	spi_message_init(&m);
	spi_message_add_tail(&tx_transfer, &m);

	result = spi_sync(spidev, &m);
	if (result) {
		dev_err(&spidev->dev, "error: sending cmd to st95hf using SPI = %d\n",
			result);
		mutex_unlock(&spicontext->spi_lock);
		return result;
	}

	/* return for asynchronous or no-wait case */
	if (reqtype == ASYNC) {
		mutex_unlock(&spicontext->spi_lock);
		return 0;
	}

	result = wait_for_completion_timeout(&spicontext->done,
					     msecs_to_jiffies(1000));
	/* check for timeout or success */
	if (!result) {
		dev_err(&spidev->dev, "error: response not ready timeout\n");
		result = -ETIMEDOUT;
	} else {
		result = 0;
	}

	mutex_unlock(&spicontext->spi_lock);

	return result;
}
EXPORT_SYMBOL_GPL(st95hf_spi_send);

/* Function to Receive command Response */
int st95hf_spi_recv_response(struct st95hf_spi_context *spicontext,
			     unsigned char *receivebuff)
{
	int len = 0;
	struct spi_transfer tx_takedata;
	struct spi_message m;
	struct spi_device *spidev = spicontext->spidev;
	unsigned char readdata_cmd = ST95HF_COMMAND_RECEIVE;
	struct spi_transfer t[2] = {
		{.tx_buf = &readdata_cmd, .len = 1,},
		{.rx_buf = receivebuff, .len = 2, .cs_change = 1,},
	};

	int ret = 0;

	memset(&tx_takedata, 0x0, sizeof(struct spi_transfer));

	mutex_lock(&spicontext->spi_lock);

	/* First spi transfer to know the length of valid data */
	spi_message_init(&m);
	spi_message_add_tail(&t[0], &m);
	spi_message_add_tail(&t[1], &m);

	ret = spi_sync(spidev, &m);
	if (ret) {
		dev_err(&spidev->dev, "spi_recv_resp, data length error = %d\n",
			ret);
		mutex_unlock(&spicontext->spi_lock);
		return ret;
	}

	/* As 2 bytes are already read */
	len = 2;

	/* Support of long frame */
	if (receivebuff[0] & 0x60)
		len += (((receivebuff[0] & 0x60) >> 5) << 8) | receivebuff[1];
	else
		len += receivebuff[1];

	/* Now make a transfer to read only relevant bytes */
	tx_takedata.rx_buf = &receivebuff[2];
	tx_takedata.len = len - 2;

	spi_message_init(&m);
	spi_message_add_tail(&tx_takedata, &m);

	ret = spi_sync(spidev, &m);

	mutex_unlock(&spicontext->spi_lock);
	if (ret) {
		dev_err(&spidev->dev, "spi_recv_resp, data read error = %d\n",
			ret);
		return ret;
	}

	return len;
}
EXPORT_SYMBOL_GPL(st95hf_spi_recv_response);

int st95hf_spi_recv_echo_res(struct st95hf_spi_context *spicontext,
			     unsigned char *receivebuff)
{
	unsigned char readdata_cmd = ST95HF_COMMAND_RECEIVE;
	struct spi_transfer t[2] = {
		{.tx_buf = &readdata_cmd, .len = 1,},
		{.rx_buf = receivebuff, .len = 1,},
	};
	struct spi_message m;
	struct spi_device *spidev = spicontext->spidev;
	int ret = 0;

	mutex_lock(&spicontext->spi_lock);

	spi_message_init(&m);
	spi_message_add_tail(&t[0], &m);
	spi_message_add_tail(&t[1], &m);
	ret = spi_sync(spidev, &m);

	mutex_unlock(&spicontext->spi_lock);

	if (ret)
		dev_err(&spidev->dev, "recv_echo_res, data read error = %d\n",
			ret);

	return ret;
}
EXPORT_SYMBOL_GPL(st95hf_spi_recv_echo_res);
