blob: 9443fdd6fb4add50dc91b1698ebee211139a606b [file] [log] [blame]
/*
*
**************************************************************************
** STMicroelectronics **
**************************************************************************
** marco.cali@st.com **
**************************************************************************
* *
* 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
static void *client; /* /< bus client retrived by the OS and
* used to execute the bus transfers */
#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)
{
client = clt;
#ifdef I2C_INTERFACE
I2CSAD = ((struct i2c_client *)clt)->addr;
pr_info("openChannel: SAD: %02X\n", I2CSAD);
#else
pr_info("%s: spi_master: flags = %04X !\n", __func__,
((struct spi_device *)client)->master->flags);
pr_info("%s: spi_device: max_speed = %d chip select = %02X bits_per_words = %d mode = %04X !\n",
__func__, ((struct spi_device *)client)->max_speed_hz,
((struct spi_device *)client)->chip_select,
((struct spi_device *)client)->bits_per_word,
((struct spi_device *)client)->mode);
pr_info("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
/**
* Retrieve the pointer to the device struct of the IC
* @return a the device struct pointer if client was previously set
* or NULL in all the other cases
*/
struct device *getDev(void)
{
if (client != NULL)
return &(getClient()->dev);
else
return NULL;
}
#ifdef I2C_INTERFACE
/**
* Retrieve the pointer of the i2c_client struct representing the IC as i2c
* slave
* @return client if it was previously set or NULL in all the other cases
*/
struct i2c_client *getClient()
{
if (client != NULL)
return (struct i2c_client *)client;
else
return NULL;
}
#else
/**
* Retrieve the pointer of the spi_device struct representing the IC as spi
* slave
* @return client if it was previously set or NULL in all the other cases
*/
struct spi_device *getClient()
{
if (client != NULL)
return (struct spi_device *)client;
else
return NULL;
}
#endif
struct fts_ts_info *getDrvInfo(void)
{
struct device *dev = getDev();
struct fts_ts_info *info = NULL;
if (dev != NULL)
info = dev_get_drvdata(dev);
return info;
}
/****************** 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(u8 *outBuf, int byteToRead, bool dma_safe)
{
int ret = -1;
int retry = 0;
struct fts_ts_info *info = getDrvInfo();
#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)) {
pr_err("%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);
transfer[0].len = byteToRead;
transfer[0].delay_usecs = SPI_DELAY_CS;
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 (client == NULL)
return ERROR_BUS_O;
while (retry < I2C_RETRY && ret < OK) {
#ifdef I2C_INTERFACE
ret = i2c_transfer(getClient()->adapter, I2CMsg, 1);
#else
ret = spi_sync(getClient(), &msg);
#endif
retry++;
if (ret < OK)
mdelay(I2C_WAIT_BEFORE_RETRY);
/* pr_err("fts_writeCmd: attempt %d\n", retry); */
}
if (ret < 0) {
pr_err("%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(u8 *cmd, int cmdLength, u8 *outBuf,
int byteToRead, bool dma_safe)
{
int ret = -1;
int retry = 0;
struct fts_ts_info *info = getDrvInfo();
#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))) {
pr_err("%s: preallocated buffers are too small!\n", __func__);
return ERROR_ALLOC;
}
#ifdef I2C_INTERFACE
if (dma_safe == false) {
memcpy(info->io_write_buf, cmd, cmdLength);
cmd = info->io_write_buf;
}
/* 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
if (dma_safe == false) {
memcpy(info->io_write_buf, cmd, cmdLength);
cmd = info->io_write_buf;
}
spi_message_init(&msg);
transfer[0].len = cmdLength;
transfer[0].tx_buf = cmd;
transfer[0].rx_buf = NULL;
spi_message_add_tail(&transfer[0], &msg);
transfer[1].len = byteToRead;
transfer[1].delay_usecs = SPI_DELAY_CS;
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 (client == NULL)
return ERROR_BUS_O;
while (retry < I2C_RETRY && ret < OK) {
#ifdef I2C_INTERFACE
ret = i2c_transfer(getClient()->adapter, I2CMsg, 2);
#else
ret = spi_sync(getClient(), &msg);
#endif
retry++;
if (ret < OK)
mdelay(I2C_WAIT_BEFORE_RETRY);
}
if (ret < 0) {
pr_err("%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(u8 *cmd, int cmdLength, bool dma_safe)
{
int ret = -1;
int retry = 0;
struct fts_ts_info *info = getDrvInfo();
#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)) {
pr_err("%s: preallocated buffers are too small!\n", __func__);
return ERROR_ALLOC;
}
#ifdef I2C_INTERFACE
if (dma_safe == false) {
memcpy(info->io_write_buf, cmd, cmdLength);
cmd = info->io_write_buf;
}
I2CMsg[0].addr = (__u16)I2CSAD;
I2CMsg[0].flags = (__u16)0;
I2CMsg[0].len = (__u16)cmdLength;
I2CMsg[0].buf = (__u8 *)cmd;
#else
if (dma_safe == false) {
memcpy(info->io_write_buf, cmd, cmdLength);
cmd = info->io_write_buf;
}
spi_message_init(&msg);
transfer[0].len = cmdLength;
transfer[0].delay_usecs = SPI_DELAY_CS;
transfer[0].tx_buf = cmd;
transfer[0].rx_buf = NULL;
spi_message_add_tail(&transfer[0], &msg);
#endif
if (client == NULL)
return ERROR_BUS_O;
while (retry < I2C_RETRY && ret < OK) {
#ifdef I2C_INTERFACE
ret = i2c_transfer(getClient()->adapter, I2CMsg, 1);
#else
ret = spi_sync(getClient(), &msg);
#endif
retry++;
if (ret < OK)
mdelay(I2C_WAIT_BEFORE_RETRY);
/* pr_err("fts_writeCmd: attempt %d\n", retry); */
}
if (ret < 0) {
pr_err("%s: ERROR %08X\n", __func__, ERROR_BUS_W);
return ERROR_BUS_W;
}
return OK;
}
/**
* Write a FW command to the IC and check automatically the echo event
* @param cmd byte array containing the command to send
* @param cmdLength size of cmd
* @return OK if success, or an error code which specify the type of error
*/
static int fts_writeFwCmd_internal(u8 *cmd, int cmdLength, bool dma_safe)
{
int ret = -1;
int ret2 = -1;
int retry = 0;
struct fts_ts_info *info = getDrvInfo();
#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)) {
pr_err("%s: preallocated buffers are too small!\n", __func__);
return ERROR_ALLOC;
}
#ifdef I2C_INTERFACE
if (dma_safe == false) {
memcpy(info->io_write_buf, cmd, cmdLength);
cmd = info->io_write_buf;
}
I2CMsg[0].addr = (__u16)I2CSAD;
I2CMsg[0].flags = (__u16)0;
I2CMsg[0].len = (__u16)cmdLength;
I2CMsg[0].buf = (__u8 *)cmd;
#else
if (dma_safe == false) {
memcpy(info->io_write_buf, cmd, cmdLength);
cmd = info->io_write_buf;
}
spi_message_init(&msg);
transfer[0].len = cmdLength;
transfer[0].delay_usecs = SPI_DELAY_CS;
transfer[0].tx_buf = cmd;
transfer[0].rx_buf = NULL;
spi_message_add_tail(&transfer[0], &msg);
#endif
if (client == NULL)
return ERROR_BUS_O;
resetErrorList();
while (retry < I2C_RETRY && (ret < OK || ret2 < OK)) {
#ifdef I2C_INTERFACE
ret = i2c_transfer(getClient()->adapter, I2CMsg, 1);
#else
ret = spi_sync(getClient(), &msg);
#endif
retry++;
if (ret >= 0)
ret2 = checkEcho(cmd, cmdLength);
if (ret < OK || ret2 < OK)
mdelay(I2C_WAIT_BEFORE_RETRY);
/* pr_err("fts_writeCmd: attempt %d\n", retry); */
}
if (ret < 0) {
pr_err("fts_writeFwCmd: ERROR %08X\n", ERROR_BUS_W);
return ERROR_BUS_W;
}
if (ret2 < OK) {
pr_err("fts_writeFwCmd: check echo ERROR %08X\n", ret2);
return ret2;
}
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(u8 *writeCmd1, int writeCmdLength,
u8 *readCmd1, int readCmdLength,
u8 *outBuf, int byteToRead,
bool dma_safe)
{
int ret = -1;
int retry = 0;
struct fts_ts_info *info = getDrvInfo();
#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))) {
pr_err("%s: preallocated buffers are too small!\n", __func__);
return ERROR_ALLOC;
}
#ifdef I2C_INTERFACE
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;
}
/* 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
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;
}
spi_message_init(&msg);
transfer[0].len = writeCmdLength;
transfer[0].tx_buf = writeCmd1;
transfer[0].rx_buf = NULL;
spi_message_add_tail(&transfer[0], &msg);
transfer[1].len = readCmdLength;
transfer[1].tx_buf = readCmd1;
transfer[1].rx_buf = NULL;
spi_message_add_tail(&transfer[1], &msg);
transfer[2].len = byteToRead;
transfer[2].delay_usecs = SPI_DELAY_CS;
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 (client == NULL)
return ERROR_BUS_O;
while (retry < I2C_RETRY && ret < OK) {
#ifdef I2C_INTERFACE
ret = i2c_transfer(getClient()->adapter, I2CMsg, 3);
#else
ret = spi_sync(getClient(), &msg);
#endif
retry++;
if (ret < OK)
mdelay(I2C_WAIT_BEFORE_RETRY);
}
if (ret < 0) {
pr_err("%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(u8 *outBuf, int byteToRead)
{
return fts_read_internal(outBuf, byteToRead, false);
}
int fts_read_heap(u8 *outBuf, int byteToRead)
{
return fts_read_internal(outBuf, byteToRead, true);
}
int fts_writeRead(u8 *cmd, int cmdLength, u8 *outBuf, int byteToRead)
{
return fts_writeRead_internal(cmd, cmdLength, outBuf, byteToRead,
false);
}
int fts_writeRead_heap(u8 *cmd, int cmdLength, u8 *outBuf, int byteToRead)
{
return fts_writeRead_internal(cmd, cmdLength, outBuf, byteToRead, true);
}
int fts_write(u8 *cmd, int cmdLength)
{
return fts_write_internal(cmd, cmdLength, false);
}
int fts_write_heap(u8 *cmd, int cmdLength)
{
return fts_write_internal(cmd, cmdLength, true);
}
int fts_writeFwCmd(u8 *cmd, int cmdLength)
{
return fts_writeFwCmd_internal(cmd, cmdLength, false);
}
int fts_writeFwCmd_heap(u8 *cmd, int cmdLength)
{
return fts_writeFwCmd_internal(cmd, cmdLength, true);
}
int fts_writeThenWriteRead(u8 *writeCmd1, int writeCmdLength,
u8 *readCmd1, int readCmdLength,
u8 *outBuf, int byteToRead)
{
return fts_writeThenWriteRead_internal(writeCmd1, writeCmdLength,
readCmd1, readCmdLength,
outBuf, byteToRead, false);
}
int fts_writeThenWriteRead_heap(u8 *writeCmd1, int writeCmdLength,
u8 *readCmd1, int readCmdLength,
u8 *outBuf, int byteToRead)
{
return fts_writeThenWriteRead_internal(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(u8 cmd, AddrSize addrSize, u64 address, u8 *data,
int dataSize)
{
u8 *finalCmd;
int remaining = dataSize;
int toWrite = 0, i = 0;
struct fts_ts_info *info = getDrvInfo();
finalCmd = info->io_write_buf;
if (addrSize <= sizeof(u64)) {
while (remaining > 0) {
if (remaining >= WRITE_CHUNK) {
toWrite = WRITE_CHUNK;
remaining -= WRITE_CHUNK;
} else {
toWrite = remaining;
remaining = 0;
}
finalCmd[0] = cmd;
pr_debug("%s: addrSize = %d\n", __func__, addrSize);
for (i = 0; i < addrSize; i++) {
finalCmd[i + 1] = (u8)((address >> ((addrSize -
1 - i) *
8)) & 0xFF);
pr_debug("%s: cmd[%d] = %02X\n",
__func__, i + 1, finalCmd[i + 1]);
}
memcpy(&finalCmd[addrSize + 1], data, toWrite);
if (fts_write_heap(finalCmd, 1 + addrSize + toWrite)
< OK) {
pr_err(" %s: ERROR %08X\n",
__func__, ERROR_BUS_W);
return ERROR_BUS_W;
}
address += toWrite;
data += toWrite;
}
} else
pr_err("%s: address size bigger than max allowed %lu... ERROR %08X\n",
__func__, sizeof(u64), ERROR_OP_NOT_ALLOW);
return OK;
}
/**
* 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 hasDummyByte if the first byte of each reading is dummy (must be
* skipped)
* set to 1, otherwise if it is valid set to 0 (or any other value)
* @return OK if success or an error code which specify the type of error
*/
int fts_writeReadU8UX(u8 cmd, AddrSize addrSize, u64 address, u8 *outBuf,
int byteToRead, int hasDummyByte)
{
u8 *finalCmd;
u8 *buff;
int remaining = byteToRead;
int toRead = 0, i = 0;
struct fts_ts_info *info = getDrvInfo();
finalCmd = info->io_write_buf;
buff = info->io_read_buf;
while (remaining > 0) {
if (remaining >= READ_CHUNK) {
toRead = READ_CHUNK;
remaining -= READ_CHUNK;
} else {
toRead = remaining;
remaining = 0;
}
finalCmd[0] = cmd;
for (i = 0; i < addrSize; i++)
finalCmd[i + 1] = (u8)((address >> ((addrSize - 1 - i) *
8)) & 0xFF);
if (hasDummyByte == 1) {
if (fts_writeRead_heap(finalCmd, 1 + addrSize, buff,
toRead + 1) < OK) {
pr_err("%s: read error... ERROR %08X\n",
__func__, ERROR_BUS_WR);
return ERROR_BUS_WR;
}
memcpy(outBuf, buff + 1, toRead);
} else {
if (fts_writeRead_heap(finalCmd, 1 + addrSize, buff,
toRead) < OK) {
pr_err("%s: read error... ERROR %08X\n",
__func__, ERROR_BUS_WR);
return ERROR_BUS_WR;
}
memcpy(outBuf, buff, toRead);
}
address += toRead;
outBuf += toRead;
}
return OK;
}
/**
* 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(u8 cmd1, AddrSize addrSize1, u8 cmd2,
AddrSize addrSize2, u64 address, u8 *data,
int dataSize)
{
u8 *finalCmd1;
u8 *finalCmd2;
int remaining = dataSize;
int toWrite = 0, i = 0;
struct fts_ts_info *info = getDrvInfo();
finalCmd1 = info->io_write_buf;
finalCmd2 = info->io_extra_write_buf;
while (remaining > 0) {
if (remaining >= WRITE_CHUNK) {
toWrite = WRITE_CHUNK;
remaining -= WRITE_CHUNK;
} else {
toWrite = remaining;
remaining = 0;
}
finalCmd1[0] = cmd1;
for (i = 0; i < addrSize1; i++)
finalCmd1[i + 1] = (u8)((address >> ((addrSize1 +
addrSize2 - 1 -
i) * 8)) & 0xFF);
finalCmd2[0] = cmd2;
for (i = addrSize1; i < addrSize1 + addrSize2; i++)
finalCmd2[i - addrSize1 + 1] = (u8)((address >>
((addrSize1 +
addrSize2 - 1 -
i) * 8)) & 0xFF);
memcpy(&finalCmd2[addrSize2 + 1], data, toWrite);
if (fts_write_heap(finalCmd1, 1 + addrSize1) < OK) {
pr_err("%s: first write error... ERROR %08X\n",
__func__, ERROR_BUS_W);
return ERROR_BUS_W;
}
if (fts_write_heap(finalCmd2, 1 + addrSize2 + toWrite) < OK) {
pr_err("%s: second write error... ERROR %08X\n",
__func__, ERROR_BUS_W);
return ERROR_BUS_W;
}
address += toWrite;
data += toWrite;
}
return OK;
}
/**
* 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 hasDummyByte if the first byte of each reading is dummy (must be
* skipped) set to 1,
* otherwise if it is valid set to 0 (or any other value)
* @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(u8 cmd1, AddrSize addrSize1, u8 cmd2,
AddrSize addrSize2, u64 address, u8 *outBuf,
int byteToRead, int hasDummyByte)
{
u8 *finalCmd1;
u8 *finalCmd2;
u8 *buff;
int remaining = byteToRead;
int toRead = 0, i = 0;
struct fts_ts_info *info = getDrvInfo();
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;
remaining -= READ_CHUNK;
} else {
toRead = remaining;
remaining = 0;
}
finalCmd1[0] = cmd1;
for (i = 0; i < addrSize1; i++)
finalCmd1[i + 1] = (u8)((address >> ((addrSize1 +
addrSize2 - 1 -
i) * 8)) & 0xFF);
finalCmd2[0] = cmd2;
for (i = addrSize1; i < addrSize1 + addrSize2; i++)
finalCmd2[i - addrSize1 + 1] = (u8)((address >>
((addrSize1 +
addrSize2 - 1 -
i) * 8)) & 0xFF);
if (fts_write_heap(finalCmd1, 1 + addrSize1) < OK) {
pr_err("%s: first write error... ERROR %08X\n",
__func__, ERROR_BUS_W);
return ERROR_BUS_W;
}
if (hasDummyByte == 1) {
if (fts_writeRead_heap(finalCmd2, 1 + addrSize2, buff,
toRead + 1) < OK) {
pr_err("%s: read error... ERROR %08X\n",
__func__, ERROR_BUS_WR);
return ERROR_BUS_WR;
}
memcpy(outBuf, buff + 1, toRead);
} else {
if (fts_writeRead_heap(finalCmd2, 1 + addrSize2, buff,
toRead) < OK) {
pr_err("%s: read error... ERROR %08X\n",
__func__, ERROR_BUS_WR);
return ERROR_BUS_WR;
}
memcpy(outBuf, buff, toRead);
}
address += toRead;
outBuf += toRead;
}
return OK;
}