blob: e86ae32ae204d100c6949f4e43b3d79671212e9d [file] [log] [blame] [edit]
/*
*
**************************************************************************
** STMicroelectronics **
**************************************************************************
** [email protected] **
**************************************************************************
* *
* I2C/SPI Communication *
* *
**************************************************************************
**************************************************************************
*
*/
/*!
* \file ftsIO.c
* \brief Contains all the functions which handle with the I2C/SPI
*communication
*/
#include "ftsSoftware.h"
#include <linux/errno.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/string.h>
#include <stdarg.h>
#include <linux/delay.h>
#include <linux/ctype.h>
#include <linux/of_gpio.h>
#ifdef I2C_INTERFACE
#include <linux/i2c.h>
#include <linux/i2c-dev.h>
static u16 I2CSAD; /* /< slave address of the IC in the i2c bus */
#else
#include <linux/spi/spidev.h>
#endif
#include "ftsCore.h"
#include "ftsError.h"
#include "ftsHardware.h"
#include "ftsIO.h"
/**
* Initialize the static client variable of the fts_lib library in order
* to allow any i2c/spi transaction in the driver (Must be called in the probe)
* @param clt pointer to i2c_client or spi_device struct which identify the bus
* slave device
* @return OK
*/
int openChannel(void *clt)
{
#ifdef I2C_INTERFACE
I2CSAD = ((struct i2c_client *)clt)->addr;
dev_info(&((struct i2c_client *)clt)->dev, "openChannel: SAD: %02X\n",
I2CSAD);
#else
dev_info(&((struct spi_device *)clt)->dev,
"%s: spi_master: flags = %04X !\n", __func__,
((struct spi_device *)clt)->master->flags);
dev_info(&((struct spi_device *)clt)->dev,
"%s: spi_device: max_speed = %d chip select = %02X bits_per_words = %d mode = %04X !\n",
__func__, ((struct spi_device *)clt)->max_speed_hz,
((struct spi_device *)clt)->chip_select,
((struct spi_device *)clt)->bits_per_word,
((struct spi_device *)clt)->mode);
dev_info(&((struct spi_device *)clt)->dev, "openChannel: completed!\n");
#endif
return OK;
}
#ifdef I2C_INTERFACE
/**
* Change the I2C slave address which will be used during the transaction
* (For Debug Only)
* @param sad new slave address id
* @return OK
*/
int changeSAD(u8 sad)
{
I2CSAD = sad;
return OK;
}
#endif
/****************** New I2C API *********************/
/**
* Perform a direct bus read
* @param outBuf pointer of a byte array which should contain the byte read
* from the IC
* @param byteToRead number of bytes to read
* @return OK if success or an error code which specify the type of error
*/
static int fts_read_internal(struct fts_ts_info *info, u8 *outBuf,
int byteToRead, bool dma_safe)
{
int ret = -1;
int retry = 0;
#ifdef I2C_INTERFACE
struct i2c_msg I2CMsg[1];
#else
struct spi_message msg;
struct spi_transfer transfer[1] = { { 0 } };
#endif
if (dma_safe == false && byteToRead > sizeof(info->io_read_buf)) {
dev_err(info->dev, "%s: preallocated buffers are too small!\n", __func__);
return ERROR_ALLOC;
}
#ifdef I2C_INTERFACE
I2CMsg[0].addr = (__u16)I2CSAD;
I2CMsg[0].flags = (__u16)I2C_M_RD;
I2CMsg[0].len = (__u16)byteToRead;
if (dma_safe == false)
I2CMsg[0].buf = (__u8 *)info->io_read_buf;
else
I2CMsg[0].buf = (__u8 *)outBuf;
#else
spi_message_init(&msg);
if (info->dma_mode) {
transfer[0].len = spi_len_dma_align(byteToRead, 4);
transfer[0].bits_per_word = spi_bits_dma_align(byteToRead);
} else {
transfer[0].len = byteToRead;
}
transfer[0].delay.value = SPI_DELAY_CS;
transfer[0].delay.unit = SPI_DELAY_UNIT_USECS;
transfer[0].tx_buf = NULL;
if (dma_safe == false)
transfer[0].rx_buf = info->io_read_buf;
else
transfer[0].rx_buf = outBuf;
spi_message_add_tail(&transfer[0], &msg);
#endif
if (info->client == NULL)
return ERROR_BUS_O;
while (retry < I2C_RETRY && ret < OK) {
#ifdef I2C_INTERFACE
ret = i2c_transfer(info->client->adapter, I2CMsg, 1);
#else
ret = spi_sync(info->client, &msg);
#endif
retry++;
if (ret < OK)
mdelay(I2C_WAIT_BEFORE_RETRY);
/* dev_err(info->dev, "fts_writeCmd: attempt %d\n", retry); */
}
if (ret < 0) {
dev_err(info->dev, "%s: ERROR %08X\n", __func__, ERROR_BUS_R);
return ERROR_BUS_R;
}
if (dma_safe == false)
memcpy(outBuf, info->io_read_buf, byteToRead);
return OK;
}
/**
* Perform a bus write followed by a bus read without a stop condition
* @param cmd byte array containing the command to write
* @param cmdLength size of cmd
* @param outBuf pointer of a byte array which should contain the bytes read
* from the IC
* @param byteToRead number of bytes to read
* @return OK if success or an error code which specify the type of error
*/
static int fts_writeRead_internal(struct fts_ts_info *info, u8 *cmd,
int cmdLength, u8 *outBuf, int byteToRead,
bool dma_safe)
{
int ret = -1;
int retry = 0;
#ifdef I2C_INTERFACE
struct i2c_msg I2CMsg[2];
#else
struct spi_message msg;
struct spi_transfer transfer[2] = { { 0 }, { 0 } };
#endif
if (dma_safe == false && (cmdLength > sizeof(info->io_write_buf) ||
byteToRead > sizeof(info->io_read_buf))) {
dev_err(info->dev, "%s: preallocated buffers are too small!\n", __func__);
return ERROR_ALLOC;
}
if (dma_safe == false) {
memcpy(info->io_write_buf, cmd, cmdLength);
cmd = info->io_write_buf;
}
#ifdef I2C_INTERFACE
/* write msg */
I2CMsg[0].addr = (__u16)I2CSAD;
I2CMsg[0].flags = (__u16)0;
I2CMsg[0].len = (__u16)cmdLength;
I2CMsg[0].buf = (__u8 *)cmd;
/* read msg */
I2CMsg[1].addr = (__u16)I2CSAD;
I2CMsg[1].flags = I2C_M_RD;
I2CMsg[1].len = byteToRead;
if (dma_safe == false)
I2CMsg[1].buf = (__u8 *)info->io_read_buf;
else
I2CMsg[1].buf = (__u8 *)outBuf;
#else
spi_message_init(&msg);
if (info->dma_mode) {
transfer[0].len = spi_len_dma_align(cmdLength, 4);
transfer[0].bits_per_word = spi_bits_dma_align(cmdLength);
} else {
transfer[0].len = cmdLength;
}
transfer[0].tx_buf = cmd;
transfer[0].rx_buf = NULL;
spi_message_add_tail(&transfer[0], &msg);
if (info->dma_mode) {
transfer[1].len = spi_len_dma_align(byteToRead, 4);
transfer[1].bits_per_word = spi_bits_dma_align(byteToRead);
} else {
transfer[1].len = byteToRead;
}
transfer[1].delay.value = SPI_DELAY_CS;
transfer[1].delay.unit = SPI_DELAY_UNIT_USECS;
transfer[1].tx_buf = NULL;
if (dma_safe == false)
transfer[1].rx_buf = info->io_read_buf;
else
transfer[1].rx_buf = outBuf;
spi_message_add_tail(&transfer[1], &msg);
#endif
if (info->client == NULL)
return ERROR_BUS_O;
while (retry < I2C_RETRY && ret < OK) {
#ifdef I2C_INTERFACE
ret = i2c_transfer(info->client->adapter, I2CMsg, 2);
#else
ret = spi_sync(info->client, &msg);
#endif
retry++;
if (ret < OK)
mdelay(I2C_WAIT_BEFORE_RETRY);
}
if (ret < 0) {
dev_err(info->dev, "%s: ERROR %08X\n", __func__, ERROR_BUS_WR);
return ERROR_BUS_WR;
}
if (dma_safe == false)
memcpy(outBuf, info->io_read_buf, byteToRead);
return OK;
}
/**
* Perform a bus write
* @param cmd byte array containing the command to write
* @param cmdLength size of cmd
* @return OK if success or an error code which specify the type of error
*/
static int fts_write_internal(struct fts_ts_info *info, u8 *cmd, int cmdLength,
bool dma_safe)
{
int ret = -1;
int retry = 0;
#ifdef I2C_INTERFACE
struct i2c_msg I2CMsg[1];
#else
struct spi_message msg;
struct spi_transfer transfer[1] = { { 0 } };
#endif
if (dma_safe == false && cmdLength > sizeof(info->io_write_buf)) {
dev_err(info->dev, "%s: preallocated buffers are too small!\n", __func__);
return ERROR_ALLOC;
}
if (dma_safe == false) {
memcpy(info->io_write_buf, cmd, cmdLength);
cmd = info->io_write_buf;
}
#ifdef I2C_INTERFACE
I2CMsg[0].addr = (__u16)I2CSAD;
I2CMsg[0].flags = (__u16)0;
I2CMsg[0].len = (__u16)cmdLength;
I2CMsg[0].buf = (__u8 *)cmd;
#else
spi_message_init(&msg);
if (info->dma_mode) {
transfer[0].len = spi_len_dma_align(cmdLength, 4);
transfer[0].bits_per_word = spi_bits_dma_align(cmdLength);
} else {
transfer[0].len = cmdLength;
}
transfer[0].delay.value = SPI_DELAY_CS;
transfer[0].delay.unit = SPI_DELAY_UNIT_USECS;
transfer[0].tx_buf = cmd;
transfer[0].rx_buf = NULL;
spi_message_add_tail(&transfer[0], &msg);
#endif
if (info->client == NULL)
return ERROR_BUS_O;
while (retry < I2C_RETRY && ret < OK) {
#ifdef I2C_INTERFACE
ret = i2c_transfer(info->client->adapter, I2CMsg, 1);
#else
ret = spi_sync(info->client, &msg);
#endif
retry++;
if (ret < OK)
mdelay(I2C_WAIT_BEFORE_RETRY);
/* dev_err(info->dev, "fts_writeCmd: attempt %d\n", retry); */
}
if (ret < 0) {
dev_err(info->dev, "%s: ERROR %08X\n", __func__, ERROR_BUS_W);
return ERROR_BUS_W;
}
return OK;
}
/**
* Perform two bus write and one bus read without any stop condition
* In case of FTI this function is not supported and the same sequence
* can be achieved calling fts_write followed by an fts_writeRead.
* @param writeCmd1 byte array containing the first command to write
* @param writeCmdLength size of writeCmd1
* @param readCmd1 byte array containing the second command to write
* @param readCmdLength size of readCmd1
* @param outBuf pointer of a byte array which should contain the bytes read
* from the IC
* @param byteToRead number of bytes to read
* @return OK if success or an error code which specify the type of error
*/
static int fts_writeThenWriteRead_internal(struct fts_ts_info *info,
u8 *writeCmd1, int writeCmdLength,
u8 *readCmd1, int readCmdLength,
u8 *outBuf, int byteToRead,
bool dma_safe)
{
int ret = -1;
int retry = 0;
#ifdef I2C_INTERFACE
struct i2c_msg I2CMsg[3];
#else
struct spi_message msg;
struct spi_transfer transfer[3] = { { 0 }, { 0 }, { 0 } };
#endif
if (dma_safe == false && (writeCmdLength > sizeof(info->io_write_buf) ||
readCmdLength > sizeof(info->io_extra_write_buf) ||
byteToRead > sizeof(info->io_read_buf))) {
dev_err(info->dev, "%s: preallocated buffers are too small!\n", __func__);
return ERROR_ALLOC;
}
if (dma_safe == false) {
memcpy(info->io_write_buf, writeCmd1, writeCmdLength);
writeCmd1 = info->io_write_buf;
memcpy(info->io_extra_write_buf, readCmd1, readCmdLength);
readCmd1 = info->io_extra_write_buf;
}
#ifdef I2C_INTERFACE
/* write msg */
I2CMsg[0].addr = (__u16)I2CSAD;
I2CMsg[0].flags = (__u16)0;
I2CMsg[0].len = (__u16)writeCmdLength;
I2CMsg[0].buf = (__u8 *)writeCmd1;
/* write msg */
I2CMsg[1].addr = (__u16)I2CSAD;
I2CMsg[1].flags = (__u16)0;
I2CMsg[1].len = (__u16)readCmdLength;
I2CMsg[1].buf = (__u8 *)readCmd1;
/* read msg */
I2CMsg[2].addr = (__u16)I2CSAD;
I2CMsg[2].flags = I2C_M_RD;
I2CMsg[2].len = byteToRead;
if (dma_safe == false)
I2CMsg[2].buf = (__u8 *)info->io_read_buf;
else
I2CMsg[2].buf = (__u8 *)outBuf;
#else
spi_message_init(&msg);
if (info->dma_mode) {
transfer[0].len = spi_len_dma_align(writeCmdLength, 4);
transfer[0].bits_per_word = spi_bits_dma_align(writeCmdLength);
} else {
transfer[0].len = writeCmdLength;
}
transfer[0].tx_buf = writeCmd1;
transfer[0].rx_buf = NULL;
spi_message_add_tail(&transfer[0], &msg);
if (info->dma_mode) {
transfer[1].len = spi_len_dma_align(readCmdLength, 4);
transfer[1].bits_per_word = spi_bits_dma_align(readCmdLength);
} else {
transfer[1].len = readCmdLength;
}
transfer[1].tx_buf = readCmd1;
transfer[1].rx_buf = NULL;
spi_message_add_tail(&transfer[1], &msg);
if (info->dma_mode) {
transfer[2].len = spi_len_dma_align(byteToRead, 4);
transfer[2].bits_per_word = spi_bits_dma_align(byteToRead);
} else {
transfer[2].len = byteToRead;
}
transfer[2].delay.value = SPI_DELAY_CS;
transfer[2].delay.unit = SPI_DELAY_UNIT_USECS;
transfer[2].tx_buf = NULL;
if (dma_safe == false)
transfer[2].rx_buf = info->io_read_buf;
else
transfer[2].rx_buf = outBuf;
spi_message_add_tail(&transfer[2], &msg);
#endif
if (info->client == NULL)
return ERROR_BUS_O;
while (retry < I2C_RETRY && ret < OK) {
#ifdef I2C_INTERFACE
ret = i2c_transfer(info->client->adapter, I2CMsg, 3);
#else
ret = spi_sync(info->client, &msg);
#endif
retry++;
if (ret < OK)
mdelay(I2C_WAIT_BEFORE_RETRY);
}
if (ret < 0) {
dev_err(info->dev, "%s: ERROR %08X\n", __func__, ERROR_BUS_WR);
return ERROR_BUS_WR;
}
if (dma_safe == false)
memcpy(outBuf, info->io_read_buf, byteToRead);
return OK;
}
/* Wrapper API for i2c read and write */
int fts_read(struct fts_ts_info *info, u8 *outBuf, int byteToRead)
{
int ret;
mutex_lock(&info->io_mutex);
ret = fts_read_internal(info, outBuf, byteToRead, false);
mutex_unlock(&info->io_mutex);
return ret;
}
int fts_read_heap(struct fts_ts_info *info, u8 *outBuf, int byteToRead)
{
return fts_read_internal(info, outBuf, byteToRead, true);
}
int fts_writeRead(struct fts_ts_info *info, u8 *cmd, int cmdLength, u8 *outBuf,
int byteToRead)
{
int ret;
mutex_lock(&info->io_mutex);
ret = fts_writeRead_internal(info, cmd, cmdLength, outBuf, byteToRead,
false);
mutex_unlock(&info->io_mutex);
return ret;
}
int fts_writeRead_heap(struct fts_ts_info *info, u8 *cmd, int cmdLength,
u8 *outBuf, int byteToRead)
{
return fts_writeRead_internal(info, cmd, cmdLength, outBuf, byteToRead,
true);
}
int fts_write(struct fts_ts_info *info, u8 *cmd, int cmdLength)
{
int ret;
mutex_lock(&info->io_mutex);
ret = fts_write_internal(info, cmd, cmdLength, false);
mutex_unlock(&info->io_mutex);
return ret;
}
int fts_write_heap(struct fts_ts_info *info, u8 *cmd, int cmdLength)
{
return fts_write_internal(info, cmd, cmdLength, true);
}
int fts_writeFwCmd(struct fts_ts_info *info, u8 *cmd, int cmdLength)
{
int ret_write = 0;
int ret_echo = 0;
int retry = 0;
while (retry < I2C_RETRY) {
mutex_lock(&info->io_mutex);
ret_write = fts_write_internal(info, cmd, cmdLength, false);
mutex_unlock(&info->io_mutex);
if (ret_write >= OK) {
ret_echo = checkEcho(info, cmd, cmdLength);
if (ret_echo >= OK)
break;
}
retry++;
mdelay(I2C_WAIT_BEFORE_RETRY);
}
if (retry) {
dev_warn(info->dev, "%s: retry %d for cmd %*ph!",
__func__, retry, min(4, cmdLength), cmd);
}
if (ret_write < OK) {
dev_err(info->dev, "fts_writeFwCmd: ERROR %08X\n", ERROR_BUS_W);
return ERROR_BUS_W;
} else if (ret_echo < OK) {
dev_err(info->dev, "fts_writeFwCmd: check echo ERROR %08X\n", ret_echo);
return ret_echo;
}
return OK;
}
int fts_writeFwCmd_heap(struct fts_ts_info *info, u8 *cmd, int cmdLength)
{
int ret_write = 0;
int ret_echo = 0;
int retry = 0;
while (retry < I2C_RETRY) {
ret_write = fts_write_internal(info, cmd, cmdLength, true);
retry++;
if (ret_write >= OK) {
ret_echo = checkEcho(info, cmd, cmdLength);
if (ret_echo >= OK)
break;
}
mdelay(I2C_WAIT_BEFORE_RETRY);
}
if (ret_write < OK) {
dev_err(info->dev, "fts_writeFwCmd: ERROR %08X\n", ERROR_BUS_W);
return ERROR_BUS_W;
} else if (ret_echo < OK) {
dev_err(info->dev, "fts_writeFwCmd: check echo ERROR %08X\n", ret_echo);
return ret_echo;
}
return OK;
}
int fts_writeThenWriteRead(struct fts_ts_info *info, u8 *writeCmd1,
int writeCmdLength, u8 *readCmd1, int readCmdLength,
u8 *outBuf, int byteToRead)
{
int ret;
mutex_lock(&info->io_mutex);
ret = fts_writeThenWriteRead_internal(info, writeCmd1, writeCmdLength,
readCmd1, readCmdLength,
outBuf, byteToRead, false);
mutex_unlock(&info->io_mutex);
return ret;
}
int fts_writeThenWriteRead_heap(struct fts_ts_info *info, u8 *writeCmd1,
int writeCmdLength, u8 *readCmd1,
int readCmdLength, u8 *outBuf, int byteToRead)
{
return fts_writeThenWriteRead_internal(info, writeCmd1, writeCmdLength,
readCmd1, readCmdLength,
outBuf, byteToRead, true);
}
/**
* Perform a chunked write with one byte op code and 1 to 8 bytes address
* @param cmd byte containing the op code to write
* @param addrSize address size in byte
* @param address the starting address
* @param data pointer of a byte array which contain the bytes to write
* @param dataSize size of data
* @return OK if success or an error code which specify the type of error
*/
/* this function works only if the address is max 8 bytes */
int fts_writeU8UX(struct fts_ts_info *info, u8 cmd, AddrSize addrSize,
u64 address, u8 *data, int dataSize)
{
u8 *finalCmd;
u8 *p;
int remaining = dataSize;
int toWrite = 0, i = 0;
int ret = 0;
if(addrSize > sizeof(u64)) {
dev_err(info->dev, "%s: address size bigger than max allowed %lu. ERROR %08X\n",
__func__, sizeof(u64), ERROR_OP_NOT_ALLOW);
return ERROR_OP_NOT_ALLOW;
}
mutex_lock(&info->io_mutex);
finalCmd = info->io_write_buf;
while (remaining > 0) {
if (remaining > WRITE_CHUNK)
toWrite = WRITE_CHUNK;
else
toWrite = remaining;
finalCmd[0] = cmd;
dev_dbg(info->dev, "%s: addrSize = %d, address = %llX\n",
__func__, addrSize, address);
p = (u8 *)&address + addrSize - 1;
for (i = 0; i < addrSize; i++)
finalCmd[i + 1] = *p--;
memcpy(&finalCmd[addrSize + 1], data, toWrite);
ret = fts_write_heap(info, finalCmd, 1 + addrSize + toWrite);
if (ret < OK) {
ret = ERROR_BUS_W;
break;
}
address += toWrite;
data += toWrite;
remaining -= toWrite;
}
mutex_unlock(&info->io_mutex);
if (ret < OK)
dev_err(info->dev, " %s: ERROR %08X\n", __func__, ret);
return ret;
}
/**
* Perform a chunked write read with one byte op code and 1 to 8 bytes address
* and dummy byte support.
* @param cmd byte containing the op code to write
* @param addrSize address size in byte
* @param address the starting address
* @param outBuf pointer of a byte array which contain the bytes to read
* @param byteToRead number of bytes to read
* @param bytes_to_skip if need to skip the first byte of each reading,
* set to 1,
* otherwise if the first byte is valid set to 0.
* @return OK if success or an error code which specify the type of error
*/
int fts_writeReadU8UX(struct fts_ts_info *info, u8 cmd, AddrSize addrSize,
u64 address, u8 *outBuf, int byteToRead,
int bytes_to_skip)
{
u8 *finalCmd;
u8 *buff;
u8 *p;
int remaining = byteToRead;
int toRead = 0, i = 0;
int ret = 0;
if(addrSize > sizeof(u64)) {
dev_err(info->dev, "%s: address size bigger than max allowed %lu. ERROR %08X\n",
__func__, sizeof(u64), ERROR_OP_NOT_ALLOW);
return ERROR_OP_NOT_ALLOW;
}
mutex_lock(&info->io_mutex);
finalCmd = info->io_write_buf;
buff = info->io_read_buf;
while (remaining > 0) {
if (remaining > READ_CHUNK)
toRead = READ_CHUNK;
else
toRead = remaining;
finalCmd[0] = cmd;
dev_dbg(info->dev, "%s: addrSize = %d, address = %llX\n",
__func__, addrSize, address);
p = (u8 *)&address + addrSize - 1;
for (i = 0; i < addrSize; i++)
finalCmd[i + 1] = *p--;
ret = fts_writeRead_heap(info, finalCmd, 1 + addrSize,
buff, toRead + bytes_to_skip);
if (ret < OK) {
ret = ERROR_BUS_WR;
break;
}
memcpy(outBuf, buff + bytes_to_skip, toRead);
address += toRead;
outBuf += toRead;
remaining -= toRead;
}
mutex_unlock(&info->io_mutex);
if (ret < OK)
dev_err(info->dev, "%s: read error... ERROR %08X\n",
__func__, ret);
return ret;
}
/**
* Perform a chunked write followed by a second write with one byte op code
* for each write and 1 to 8 bytes address (the sum of the 2 address size of
* the two writes can not exceed 8 bytes)
* @param cmd1 byte containing the op code of first write
* @param addrSize1 address size in byte of first write
* @param cmd2 byte containing the op code of second write
* @param addrSize2 address size in byte of second write
* @param address the starting address
* @param data pointer of a byte array which contain the bytes to write
* @param dataSize size of data
* @return OK if success or an error code which specify the type of error
*/
/* this function works only if the sum of two addresses in the two commands is
* max 8 bytes */
int fts_writeU8UXthenWriteU8UX(struct fts_ts_info *info, u8 cmd1,
AddrSize addrSize1, u8 cmd2, AddrSize addrSize2,
u64 address, u8 *data, int dataSize)
{
u8 *finalCmd1;
u8 *finalCmd2;
u8 *p;
int remaining = dataSize;
int toWrite = 0, i = 0;
int ret = 0;
mutex_lock(&info->io_mutex);
finalCmd1 = info->io_write_buf;
finalCmd2 = info->io_extra_write_buf;
while (remaining > 0) {
if (remaining > WRITE_CHUNK)
toWrite = WRITE_CHUNK;
else
toWrite = remaining;
finalCmd1[0] = cmd1;
p = (u8 *)&address + addrSize1 + addrSize2 - 1;
for (i = 0; i < addrSize1; i++)
finalCmd1[i + 1] = *p--;
finalCmd2[0] = cmd2;
for (i = 0; i < addrSize2; i++)
finalCmd2[i + 1] = *p--;
memcpy(&finalCmd2[addrSize2 + 1], data, toWrite);
ret = fts_write_heap(info, finalCmd1, 1 + addrSize1);
if (ret < OK) {
ret = ERROR_BUS_W;
dev_err(info->dev, "%s: first write error. ERROR %08X\n",
__func__, ret);
break;
}
ret = fts_write_heap(info, finalCmd2, 1 + addrSize2 + toWrite);
if (ret < OK) {
ret = ERROR_BUS_W;
dev_err(info->dev, "%s: second write error. ERROR %08X\n",
__func__, ret);
break;
}
address += toWrite;
data += toWrite;
remaining -= toWrite;
}
mutex_unlock(&info->io_mutex);
return ret;
}
/**
* Perform a chunked write followed by a write read with one byte op code
* and 1 to 8 bytes address for each write and dummy byte support.
* @param cmd1 byte containing the op code of first write
* @param addrSize1 address size in byte of first write
* @param cmd2 byte containing the op code of second write read
* @param addrSize2 address size in byte of second write read
* @param address the starting address
* @param outBuf pointer of a byte array which contain the bytes to read
* @param byteToRead number of bytes to read
* @param bytes_to_skip if need to skip the first byte of each reading,
* set to 1,
* otherwise if the first byte is valid set to 0.
* @return OK if success or an error code which specify the type of error
*/
/* this function works only if the sum of two addresses in the two commands is
* max 8 bytes */
int fts_writeU8UXthenWriteReadU8UX(struct fts_ts_info *info, u8 cmd1,
AddrSize addrSize1, u8 cmd2,
AddrSize addrSize2, u64 address, u8 *outBuf,
int byteToRead, int bytes_to_skip)
{
u8 *finalCmd1;
u8 *finalCmd2;
u8 *buff;
u8 *p;
int remaining = byteToRead;
int toRead = 0, i = 0;
int ret = 0;
mutex_lock(&info->io_mutex);
finalCmd1 = info->io_write_buf;
finalCmd2 = info->io_extra_write_buf;
buff = info->io_read_buf;
while (remaining > 0) {
if (remaining > READ_CHUNK)
toRead = READ_CHUNK;
else
toRead = remaining;
finalCmd1[0] = cmd1;
p = (u8 *)&address + addrSize1 + addrSize2 - 1;
for (i = 0; i < addrSize1; i++)
finalCmd1[i + 1] = *p--;
finalCmd2[0] = cmd2;
for (i = 0; i < addrSize2; i++)
finalCmd2[i + 1] = *p--;
ret = fts_write_heap(info, finalCmd1, 1 + addrSize1);
if (ret < OK) {
ret = ERROR_BUS_W;
dev_err(info->dev, "%s: first write error. ERROR %08X\n",
__func__, ret);
break;
}
ret = fts_writeRead_heap(info, finalCmd2, 1 + addrSize2,
buff, toRead + bytes_to_skip);
if (ret < OK) {
ret = ERROR_BUS_WR;
dev_err(info->dev, "%s: read error. ERROR %08X\n",
__func__, ret);
break;
}
memcpy(outBuf, buff + bytes_to_skip, toRead);
address += toRead;
outBuf += toRead;
remaining -= toRead;
}
mutex_unlock(&info->io_mutex);
return ret;
}