blob: f8eb2f31d0935a1d0fd21ddf59d314f88bf1eb7c [file] [log] [blame]
/*
*
**************************************************************************
** STMicroelectronics **
**************************************************************************
* *
* I2C/SPI Communication *
* *
**************************************************************************
**************************************************************************
*
*/
/*!
* \file ftsIO.c
* \brief Contains all the functions which handle with the I2C/SPI
*communication
*/
#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>
#else
#include <linux/spi/spidev.h>
#endif
#include "fts_io.h"
#include "fts_error.h"
#include "../fts.h"
/*#define DEBUG_LOG*/
extern struct sys_info system_info;
static void *client; /* /< bus client retrieved by the OS and
* used to execute the bus transfers */
#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 *get_client()
{
if (client != NULL)
return (struct i2c_client *)client;
else
return NULL;
}
#else
/**
* Retrieve the pointer of the spi_client struct representing the IC as spi
*slave
* @return client if it was previously set or NULL in all the other cases
*/
struct spi_device *get_client()
{
if (client != NULL)
return (struct spi_device *)client;
else
return NULL;
}
#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 *get_dev(void)
{
if (client != NULL)
return &(get_client()->dev);
else
return NULL;
}
/**
* 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 open_channel(void *clt)
{
client = clt;
#ifndef I2C_INTERFACE
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("%s: openChannel: completed!\n", __func__);
#endif
return OK;
}
/**
* Perform a direct bus read
* @param out_buf pointer of a byte array which should contain the byte read
*from the IC
* @param byte_to_read number of bytes to read
* @return OK if success or an error code which specify the type of error
*/
int fts_read(u8 *out_buf, int byte_to_read)
{
int ret = -1;
int retry = 0;
#ifdef I2C_INTERFACE
struct i2c_msg I2CMsg[1];
I2CMsg[0].addr = (__u16)I2C_SAD;
I2CMsg[0].flags = (__u16)I2C_M_RD;
I2CMsg[0].len = (__u16)byte_to_read;
I2CMsg[0].buf = (__u8 *)out_buf;
#else
struct fts_ts_info *info = dev_get_drvdata(&(get_client()->dev));
struct spi_message msg;
struct spi_transfer transfer[1] = { { 0 } };
spi_message_init(&msg);
if (info && info->dma_mode) {
transfer[0].len = spi_len_dma_align(byte_to_read, 4);
transfer[0].bits_per_word = spi_bits_dma_align(byte_to_read);
} else {
transfer[0].len = byte_to_read;
}
transfer[0].delay_usecs = SPI_DELAY_CS;
transfer[0].tx_buf = NULL;
transfer[0].rx_buf = out_buf;
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(get_client()->adapter, I2CMsg, 1);
#else
ret = spi_sync(get_client(), &msg);
#endif
retry++;
if (ret < OK)
msleep(I2C_WAIT_BEFORE_RETRY);
}
if (ret < 0) {
pr_err("%s: ERROR %08X\n", __func__, ERROR_BUS_R);
return ERROR_BUS_R;
}
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 cmd_length size of cmd
* @param out_buf pointer of a byte array which should contain the bytes read
*from the IC
* @param byte_to_read number of bytes to read
* @return OK if success or an error code which specify the type of error
*/
int fts_write_read(u8 *cmd, int cmd_length, u8 *out_buf, int byte_to_read)
{
int ret = -1;
int retry = 0;
#ifdef I2C_INTERFACE
struct i2c_msg I2CMsg[2];
#ifdef DEBUG_LOG
int i = 0;
#endif
/* write msg */
I2CMsg[0].addr = (__u16)I2C_SAD;
I2CMsg[0].flags = (__u16)0;
I2CMsg[0].len = (__u16)cmd_length;
I2CMsg[0].buf = (__u8 *)cmd;
/* read msg */
I2CMsg[1].addr = (__u16)I2C_SAD;
I2CMsg[1].flags = I2C_M_RD;
I2CMsg[1].len = byte_to_read;
I2CMsg[1].buf = (__u8 *)out_buf;
#else
#ifdef DEBUG_LOG
int i = 0;
#endif
struct fts_ts_info *info = dev_get_drvdata(&(get_client()->dev));
struct spi_message msg;
struct spi_transfer transfer[2] = { { 0 }, { 0 } };
spi_message_init(&msg);
if (info && info->dma_mode) {
transfer[0].len = spi_len_dma_align(cmd_length, 4);
transfer[0].bits_per_word = spi_bits_dma_align(cmd_length);
} else {
transfer[0].len = cmd_length;
}
transfer[0].tx_buf = cmd;
transfer[0].rx_buf = NULL;
spi_message_add_tail(&transfer[0], &msg);
if (info && info->dma_mode) {
transfer[1].len = spi_len_dma_align(byte_to_read, 4);
transfer[1].bits_per_word = spi_bits_dma_align(byte_to_read);
} else {
transfer[1].len = byte_to_read;
}
transfer[1].delay_usecs = SPI_DELAY_CS;
transfer[1].tx_buf = NULL;
transfer[1].rx_buf = out_buf;
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(get_client()->adapter, I2CMsg, 2);
#else
ret = spi_sync(get_client(), &msg);
#endif
retry++;
if (ret < OK)
msleep(I2C_WAIT_BEFORE_RETRY);
}
#ifdef DEBUG_LOG
pr_info("%s: W: ", __func__);
for (i = 0; i < cmd_length; i++)
printk(KERN_CONT "%02X ", cmd[i]);
printk(KERN_CONT "R: ");
for (i = 0; i < byte_to_read; i++)
printk(KERN_CONT "%02X ", out_buf[i]);
printk(KERN_CONT "\n");
#endif
if (ret < 0) {
pr_err("%s: ERROR %08X\n", __func__, ERROR_BUS_WR);
return ERROR_BUS_WR;
}
return OK;
}
/**
* Perform a bus write
* @param cmd byte array containing the command to write
* @param cmd_length size of cmd
* @return OK if success or an error code which specify the type of error
*/
int fts_write(u8 *cmd, int cmd_length)
{
int ret = -1;
int retry = 0;
#ifdef I2C_INTERFACE
struct i2c_msg I2CMsg[1];
#ifdef DEBUG_LOG
int i = 0;
#endif
I2CMsg[0].addr = (__u16)I2C_SAD;
I2CMsg[0].flags = (__u16)0;
I2CMsg[0].len = (__u16)cmd_length;
I2CMsg[0].buf = (__u8 *)cmd;
#else
#ifdef DEBUG_LOG
int i = 0;
#endif
struct fts_ts_info *info = dev_get_drvdata(&(get_client()->dev));
struct spi_message msg;
struct spi_transfer transfer[1] = { { 0 } };
spi_message_init(&msg);
if (info && info->dma_mode) {
transfer[0].len = spi_len_dma_align(cmd_length, 4);
transfer[0].bits_per_word = spi_bits_dma_align(cmd_length);
} else {
transfer[0].len = cmd_length;
}
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) {
pr_err("%s: ERROR %08X\n", __func__, ERROR_BUS_O);
return ERROR_BUS_O;
}
while (retry < I2C_RETRY && ret < OK) {
#ifdef I2C_INTERFACE
ret = i2c_transfer(get_client()->adapter, I2CMsg, 1);
#else
ret = spi_sync(get_client(), &msg);
#endif
retry++;
if (ret < OK)
msleep(I2C_WAIT_BEFORE_RETRY);
}
#ifdef DEBUG_LOG
pr_info("%s: W: ", __func__);
for (i = 0; i < cmd_length; i++)
printk(KERN_CONT "%02X ", cmd[i]);
printk(KERN_CONT "\n");
#endif
if (ret < 0) {
pr_err("%s: ERROR %08X\n", __func__, ERROR_BUS_W);
return ERROR_BUS_W;
}
return OK;
}
/**
* 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 addr_size address size in byte
* @param address the starting address
* @param data pointer of a byte array which contain the bytes to write
* @param data_size size of data
* @return OK if success or an error code which specify the type of error
*/
int fts_write_u8ux(u8 cmd, addr_size_t addr_size, u64 address, u8 *data,
int data_size)
{
u8 *final_cmd = NULL;
u8 offset = 0;
int remaining = data_size;
int to_write = 0, i = 0;
if (cmd == FTS_CMD_NONE) {
final_cmd = kmalloc(ALIGN(sizeof(u8) *
(addr_size + WRITE_CHUNK), 4), GFP_KERNEL);
if (final_cmd == NULL) {
pr_err("%s: Error allocating memory\n", __func__);
return ERROR_BUS_W;
}
offset = 0;
} else {
final_cmd = kmalloc(ALIGN(sizeof(u8) *
(1 + addr_size + WRITE_CHUNK), 4), GFP_KERNEL);
if (final_cmd == NULL) {
pr_err("%s: Error allocating memory\n", __func__);
return ERROR_BUS_W;
}
offset = 1;
}
if (addr_size <= sizeof(u64)) {
while (remaining > 0) {
if (remaining >= WRITE_CHUNK) {
to_write = WRITE_CHUNK;
remaining -= WRITE_CHUNK;
} else {
to_write = remaining;
remaining = 0;
}
if (cmd != FTS_CMD_NONE) {
final_cmd[0] = cmd;
pr_debug("%s: cmd[0] = %02X\n",
__func__, final_cmd[0]);
}
pr_debug("%s: addr_size_t = %d\n", __func__,
addr_size);
for (i = 0; i < addr_size; i++) {
final_cmd[i + offset] =
(u8)((address >> ((addr_size - 1 - i) *
8)) & 0xFF);
pr_debug("%s: cmd[%d] = %02X\n", __func__,
i + offset, final_cmd[i + offset]);
}
for (i = 0; i < to_write; i++)
pr_debug("%s: data[%d] = %02X\n",
__func__, i, data[i]);
memcpy(&final_cmd[addr_size + offset], data, to_write);
if (fts_write(final_cmd, offset +
addr_size + to_write) < OK) {
pr_debug("%s: ERROR %08X\n",
__func__, ERROR_BUS_W);
kfree(final_cmd);
return ERROR_BUS_W;
}
address += to_write;
data += to_write;
}
} else
pr_err("%s: address size bigger than max allowed %ld... ERROR %08X\n",
__func__, sizeof(u64), ERROR_OP_NOT_ALLOW);
kfree(final_cmd);
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 addr_size address size in byte
* @param address the starting address
* @param out_buf pointer of a byte array which contain the bytes to read
* @param byte_to_read number of bytes to read
* @param has_dummy_byte 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_write_read_u8ux(u8 cmd, addr_size_t addr_size, u64 address,
u8 *out_buf, int byte_to_read, int has_dummy_byte)
{
u8 *final_cmd = NULL;
u8 offset = 0;
u8 *buff = NULL;
int remaining = byte_to_read;
int to_read = 0, i = 0;
buff = kmalloc(ALIGN(sizeof(u8) * (READ_CHUNK + 1), 4), GFP_KERNEL);
if (buff == NULL) {
pr_err("%s: Error allocating memory\n", __func__);
return ERROR_BUS_WR;
}
if (cmd == FTS_CMD_NONE) {
final_cmd = kmalloc(ALIGN(sizeof(u8) *
(addr_size + WRITE_CHUNK), 4), GFP_KERNEL);
if (final_cmd == NULL) {
pr_err("%s: Error allocating memory\n", __func__);
kfree(buff);
return ERROR_BUS_WR;
}
offset = 0;
} else {
final_cmd = kmalloc(ALIGN(sizeof(u8) *
(1 + addr_size + WRITE_CHUNK), 4), GFP_KERNEL);
if (final_cmd == NULL) {
pr_err("%s: Error allocating memory\n", __func__);
kfree(buff);
return ERROR_BUS_WR;
}
offset = 1;
}
while (remaining > 0) {
if (remaining >= READ_CHUNK) {
to_read = READ_CHUNK;
remaining -= READ_CHUNK;
} else {
to_read = remaining;
remaining = 0;
}
if (cmd != FTS_CMD_NONE) {
final_cmd[0] = cmd;
pr_debug("%s: cmd[0] = %02X\n",
__func__, final_cmd[0]);
}
for (i = 0; i < addr_size; i++) {
final_cmd[i + offset] =
(u8)((address >> ((addr_size - 1 - i) * 8)) & 0xFF);
pr_debug("%s: cmd[%d] = %02X\n",
__func__, i + offset, final_cmd[i + offset]);
}
if (has_dummy_byte == 1) {
if (fts_write_read(final_cmd, offset + addr_size,
buff, to_read + 1) < OK) {
pr_err("%s: read error... ERROR %08X\n",
__func__, ERROR_BUS_WR);
kfree(final_cmd);
kfree(buff);
return ERROR_BUS_WR;
}
memcpy(out_buf, buff + 1, to_read);
} else {
if (fts_write_read(final_cmd, offset + addr_size, buff,
to_read) < OK) {
pr_err("%s: read error... ERROR %08X\n",
__func__, ERROR_BUS_WR);
kfree(final_cmd);
kfree(buff);
return ERROR_BUS_WR;
}
memcpy(out_buf, buff, to_read);
}
address += to_read;
out_buf += to_read;
}
kfree(final_cmd);
kfree(buff);
return OK;
}
/** @addtogroup events_group
* @{
*/
/**
* Poll the FIFO looking for a specified event within a timeout.
* @param event_to_search pointer to an array of int where each element
* correspond to a byte of the event to find.
* If the element of the array has value -1, the byte of the event,
* in the same position of the element is ignored.
* @param event_bytes size of event_to_search
* @param read_data pointer to an array of byte which will contain the event
*found
* @param time_to_wait time to wait before going in timeout
* @return OK if success or an error code which specify the type of error
*/
int poll_for_event(int *event_to_search, int event_bytes, u8 *read_data,
int time_to_wait)
{
int i, find, retry, count_err;
char temp[128] = { 0 };
find = 0;
retry = 0;
count_err = 0;
msleep(TIMEOUT_RESOLUTION);
while (find != 1 && retry < time_to_wait &&
fts_read_fw_reg(FIFO_READ_ADDR, read_data, 8) >= OK) {
/* Log of errors */
if (read_data[0] == EVT_ID_ERROR) {
pr_err("%s: %s", __func__,
print_hex("ERROR EVENT = ", read_data,
FIFO_EVENT_SIZE, temp));
switch (read_data[1]) {
case EVT_TYPE_ERROR_ITO_FORCETOGND:
printk("{ITO:Force Short to GND Error}\n");
break;
case EVT_TYPE_ERROR_ITO_SENSETOGND:
printk("{ITO:Sense short to GND Error}\n");
break;
case EVT_TYPE_ERROR_ITO_FLTTOGND:
printk("{ITO:Float Pin short to GND Error}\n");
break;
case EVT_TYPE_ERROR_ITO_FORCETOVDD:
printk("{ITO:Force short to VDD Error}\n");
break;
case EVT_TYPE_ERROR_ITO_SENSETOVDD:
printk("{ITO:Sense short to VDD Error}\n");
break;
case EVT_TYPE_ERROR_ITO_FLTTOVDD:
printk("{ITO:Float Pin short to VDD Error}\n");
break;
case EVT_TYPE_ERROR_ITO_FORCE_P2P:
printk("{ITO:Force Pin to Pin Short Error}\n");
break;
case EVT_TYPE_ERROR_ITO_SENSE_P2P:
printk("{ITO:Sense Pin to Pin Short Error}\n");
break;
case EVT_TYPE_ERROR_ITO_FLT_P2P:
printk("{ITO:Sense Pin to Pin Short Error}\n");
break;
case EVT_TYPE_ERROR_ITO_FORCEOPEN:
printk("{ITO:Force Open Error}\n");
break;
case EVT_TYPE_ERROR_ITO_SENSEOPEN:
printk("{ITO:Sense Open Error}\n");
break;
default:
printk("\n");
break;
}
memset(temp, 0, 128);
count_err++;
} else {
if (read_data[0] != EVT_ID_NOEVENT) {
pr_info("%s: %s\n",
__func__, print_hex("READ EVENT = ",
read_data, FIFO_EVENT_SIZE, temp));
memset(temp, 0, 128);
}
if (read_data[0] == EVT_ID_CONTROLLER_READY &&
event_to_search[0] != EVT_ID_CONTROLLER_READY) {
pr_info("%s: Unmanned Controller Ready Event!"
" Setting reset flags...\n",
__func__);
}
}
find = 1;
for (i = 0; i < event_bytes; i++) {
if (event_to_search[i] != -1 &&
(int)read_data[i] != event_to_search[i]) {
find = 0;
break;
}
}
retry++;
msleep(TIMEOUT_RESOLUTION);
}
if ((retry >= time_to_wait) && find != 1) {
pr_err("%s: ERROR %08X\n", __func__, ERROR_TIMEOUT);
return ERROR_TIMEOUT;
} else if (find == 1) {
pr_info("%s: %s\n", __func__,
print_hex("FOUND EVENT = ",
read_data, FIFO_EVENT_SIZE, temp));
memset(temp, 0, 128);
/* kfree(temp); */
pr_debug("%s: Event found in (%d iterations)! Number of errors found = %d\n",
__func__, retry, count_err);
return count_err;
} else {
pr_err("%s: ERROR %08X\n", __func__, ERROR_BUS_R);
return ERROR_BUS_R;
}
}
/** @}*/
/**
* Print messages after converting data in hex format combined with label
* @param label message label to be printed
* @param buff data pointer of bytes to print in hex format
* @param count variable for data size
* @param result data pointer to print the combined message
*/
char *print_hex(char *label, u8 *buff, int count, u8 *result)
{
int i, offset;
offset = strlen(label);
strlcpy(result, label, offset + 1); /* +1 for terminator char */
for (i = 0; i < count; i++) {
snprintf(&result[offset], 4, "%02X ", buff[i]);
/* this append automatically a null terminator char */
offset += 3;
}
return result;
}
/**
* Convert an array of bytes to an array of u16 taking two bytes at time,
* src has LSB first.
* @param src pointer to the source byte array
* @param src_length size of src
* @param dst pointer to the destination array.
* @return the final size of dst (half of the source) or ERROR_OP_NOT_ALLOW
* if the size of src is not multiple of 2.
*/
int u8_to_u16n(u8 *src, int src_length, u16 *dst)
{
int i, j;
if (src_length % 2 != 0)
return ERROR_OP_NOT_ALLOW;
j = 0;
dst = (u16 *)kmalloc((src_length / 2) * sizeof(u16),
GFP_KERNEL);
for (i = 0; i < src_length; i += 2) {
dst[j] = ((src[i + 1] & 0x00FF) << 8) + (src[i] &
0x00FF);
j++;
}
return src_length / 2;
}
/**
* Convert an array of 2 bytes to a u16, src has LSB first (little endian).
* @param src pointer to the source byte array
* @param dst pointer to the destination u16.
* @return OK
*/
int u8_to_u16(u8 *src, u16 *dst)
{
*dst = (u16)(((src[1] & 0x00FF) << 8) + (src[0] & 0x00FF));
return OK;
}
/**
* Convert an array of 2 bytes to a u16, src has MSB first (big endian).
* @param src pointer to the source byte array
* @param dst pointer to the destination u16.
* @return OK
*/
int u8_to_u16_be(u8 *src, u16 *dst)
{
*dst = (u16)(((src[0] & 0x00FF) << 8) + (src[1] & 0x00FF));
return OK;
}
/**
* Convert an array of u16 to an array of u8, dst has MSB first (big endian).
* @param src pointer to the source array of u16
* @param src_length size of src
* @param dst pointer to the destination array of u8. This array should be free
* when no need anymore
* @return size of dst (src size multiply by 2)
*/
int u16_to_u8n_be(u16 *src, int src_length, u8 *dst)
{
int i, j;
dst = (u8 *)kmalloc((2 * src_length) * sizeof(u8), GFP_KERNEL);
j = 0;
for (i = 0; i < src_length; i++) {
dst[j] = (u8)(src[i] & 0xFF00) >> 8;
dst[j + 1] = (u8)(src[i] & 0x00FF);
j += 2;
}
return src_length * 2;
}
/**
* Convert a u16 to an array of 2 u8, dst has MSB first (big endian).
* @param src u16 to convert
* @param dst pointer to the destination array of 2 u8.
* @return OK
*/
int u16_to_u8_be(u16 src, u8 *dst)
{
dst[0] = (u8)((src & 0xFF00) >> 8);
dst[1] = (u8)(src & 0x00FF);
return OK;
}
/**
* Convert a u16 to an array of 2 u8, dst has LSB first (little endian).
* @param src u16 to convert
* @param dst pointer to the destination array of 2 u8.
* @return OK
*/
int u16_to_u8(u16 src, u8 *dst)
{
dst[1] = (u8)((src & 0xFF00) >> 8);
dst[0] = (u8)(src & 0x00FF);
return OK;
}
/**
* Convert an array of bytes to a u32, src has LSB first (little endian).
* @param src array of bytes to convert
* @param dst pointer to the destination u32 variable.
* @return OK
*/
int u8_to_u32(u8 *src, u32 *dst)
{
*dst = (u32)(((src[3] & 0xFF) << 24) + ((src[2] & 0xFF) << 16) +
((src[1] & 0xFF) << 8) + (src[0] & 0xFF));
return OK;
}
/**
* Convert an array of bytes to a u32, src has MSB first (big endian).
* @param src array of bytes to convert
* @param dst pointer to the destination u32 variable.
* @return OK
*/
int u8_to_u32_be(u8 *src, u32 *dst)
{
*dst = (u32)(((src[0] & 0xFF) << 24) + ((src[1] & 0xFF) << 16) +
((src[2] & 0xFF) << 8) + (src[3] & 0xFF));
return OK;
}
/**
* Convert a u32 to an array of 4 bytes, dst has LSB first (little endian).
* @param src u32 value to convert
* @param dst pointer to the destination array of 4 bytes.
* @return OK
*/
int u32_to_u8(u32 src, u8 *dst)
{
dst[3] = (u8)((src & 0xFF000000) >> 24);
dst[2] = (u8)((src & 0x00FF0000) >> 16);
dst[1] = (u8)((src & 0x0000FF00) >> 8);
dst[0] = (u8)(src & 0x000000FF);
return OK;
}
/**
* Convert a u32 to an array of 4 bytes, dst has MSB first (big endian).
* @param src u32 value to convert
* @param dst pointer to the destination array of 4 bytes.
* @return OK
*/
int u32_to_u8_be(u32 src, u8 *dst)
{
dst[0] = (u8)((src & 0xFF000000) >> 24);
dst[1] = (u8)((src & 0x00FF0000) >> 16);
dst[2] = (u8)((src & 0x0000FF00) >> 8);
dst[3] = (u8)(src & 0x000000FF);
return OK;
}
/**
* Convert an array of bytes to an u64, src has MSB first (big endian).
* @param src array of bytes
* @param dest pointer to the destination u64.
* @param size size of src (can be <= 8)
* @return OK if success or ERROR_OP_NOT_ALLOW if size exceed 8
*/
int u8_to_u64_be(u8 *src, u64 *dest, int size)
{
int i = 0;
if (size > sizeof(u64))
return ERROR_OP_NOT_ALLOW;
*dest = 0;
for (i = 0; i < size; i++)
*dest |= (u64)(src[i]) << ((size - 1 - i) * 8);
return OK;
}
/**
* Convert an u64 to an array of bytes, dest has MSB first (big endian).
* @param src value of u64
* @param dest pointer to the destination array of bytes.
* @param size size of src (can be <= 8)
* @return OK if success or ERROR_OP_NOT_ALLOW if size exceed 8
*/
int u64_to_u8_be(u64 src, u8 *dest, int size)
{
int i = 0;
if (size > sizeof(u64))
return ERROR_OP_NOT_ALLOW;
for (i = 0; i < size; i++)
dest[i] = (u8)((src >> ((size - 1 - i) * 8)) & 0xFF);
return OK;
}
/**
* Convert a value of an id in a bitmask with a 1 in the position of the value
*of the id
* @param id Value of the ID to convert
* @param mask pointer to the bitmask that will be updated with the value of id
* @param size dimension in bytes of mask
* @return OK if success or ERROR_OP_NOT_ALLOW if size of mask is not enough to
*contain ID
*/
int from_id_to_mask(u8 id, u8 *mask, int size)
{
if (((int)((id) / 8)) < size) {
pr_info("%s: ID = %d Index = %d Position = %d !\n",
__func__, id, ((int)((id) / 8)), (id % 8));
mask[((int)((id) / 8))] |= 0x01 << (id % 8);
return OK;
}
pr_err("%s: Bitmask too small! Impossible contain ID = %d %d>=%d! ERROR %08X\n",
__func__, id, ((int)((id) / 8)), size, ERROR_OP_NOT_ALLOW);
return ERROR_OP_NOT_ALLOW;
}
/**
* Perform a firmware register write.
* @param address varaiable to point register offset
* @param data address pointer to array of data bytes
* @param length size of data to write
* @return OK if success or an error code which specify the type of error
*/
int fts_write_fw_reg(u16 address, u8 *data, uint32_t length)
{
int res = 0;
#ifdef I2C_INTERFACE
res = fts_write_u8ux(FTS_CMD_NONE, FW_ADDR_SIZE, address, data, length);
#else
int remaining = length;
int to_write_now = 0;
int written_already = 0;
while (remaining > 0) {
/*FW chunk size limit for spi FW REG W is 128 bytes*/
to_write_now =
remaining > SPI_REG_W_CHUNK ? SPI_REG_W_CHUNK : remaining;
res = fts_write_u8ux(FTS_CMD_REG_SPI_W, FW_ADDR_SIZE,
address + written_already,
data + written_already, to_write_now);
remaining -= to_write_now;
written_already += to_write_now;
}
#endif
if (res < OK)
pr_err("%s: ERROR %08X\n", __func__, res);
return res;
}
/**
* Perform a firmware register read.
* @param address varaiable to point register offset
* @param read_data pointer to buffer to read data
* @param read_length size of data to read
* @return OK if success or an error code which specify the type of error
*/
int fts_read_fw_reg(u16 address, u8 *read_data, uint32_t read_length)
{
int res = 0;
#ifdef I2C_INTERFACE
res = fts_write_read_u8ux(FTS_CMD_NONE, FW_ADDR_SIZE,
address, read_data, read_length, DUMMY_BYTE);
#else
int remaining = read_length;
int to_read_now = 0;
int read_already = 0;
while (remaining > 0) {
/*FW chunk size limit for spi FW REG R is 4096 bytes*/
to_read_now =
remaining > SPI_REG_R_CHUNK ? SPI_REG_R_CHUNK : remaining;
res = fts_write_read_u8ux(FTS_CMD_REG_SPI_R, FW_ADDR_SIZE,
address + read_already, read_data + read_already,
to_read_now, DUMMY_BYTE);
remaining -= to_read_now;
read_already += to_read_now;
}
#endif
if (res < OK)
pr_err("%s: ERROR %08X\n", __func__, res);
return res;
}
/**
* Perform a hdm data write to frame buffer.
* @param address varaiable to framebuffer
* @param data address pointer to array of data bytes
* @param length size of data to write
* @return OK if success or an error code which specify the type of error
*/
int fts_write_hdm(u16 address, u8 *data, int length)
{
int res = 0;
#ifdef I2C_INTERFACE
res = fts_write_u8ux(FTS_CMD_NONE, FW_ADDR_SIZE, address, data, length);
#else
int remaining = length;
int to_write_now = 0;
int written_already = 0;
while (remaining > 0) {
/*FW chunk size limit for spi FW HDM W is 4096 bytes*/
to_write_now =
remaining > SPI_HDM_W_CHUNK ? SPI_HDM_W_CHUNK : remaining;
res = fts_write_u8ux(FTS_CMD_HDM_SPI_W, FW_ADDR_SIZE,
address + written_already,
data + written_already, to_write_now);
remaining -= to_write_now;
written_already += to_write_now;
}
#endif
if (res < OK)
pr_err("%s: ERROR %08X\n", __func__, res);
return res;
}
/**
* Perform a hdm data read from frame buffer
* after hdm request
* @param address varaiable to point register offset
* @param read_data pointer to buffer to read data
* @param read_length size of data to read
* @return OK if success or an error code which specify the type of error
*/
int fts_read_hdm(u16 address, u8 *read_data, uint32_t read_length)
{
int res = 0;
#ifdef I2C_INTERFACE
res = fts_write_read_u8ux(FTS_CMD_NONE, FW_ADDR_SIZE,
address, read_data, read_length, DUMMY_BYTE);
#else
int remaining = read_length;
int to_read_now = 0;
int read_already = 0;
while (remaining > 0) {
/*FW chunk size limit for spi FW HDM R is 4096 bytes*/
to_read_now =
remaining > SPI_HDM_R_CHUNK ? SPI_HDM_R_CHUNK : remaining;
res = fts_write_read_u8ux(FTS_CMD_HDM_SPI_R, FW_ADDR_SIZE,
address + read_already, read_data + read_already,
to_read_now, DUMMY_BYTE);
remaining -= to_read_now;
read_already += to_read_now;
}
#endif
if (res < OK)
pr_err("%s: ERROR %08X\n", __func__, res);
return res;
}
/**
* Perform a firmware register read to check bit has been reset
* as part of the fw request command in a time out period
* @param address variable to point register offset
* @param bit_to_set variable to check fw register bit
* @param time_to_wait time out in ms resolution
* @return OK if success or an error code which specify the type of error
*/
int poll_fw_reg_clear_status(u16 address, u8 bit_to_check, int time_to_wait)
{
int i = 0;
int res = 0;
u8 data = 0;
for (i = 0; i < time_to_wait; i++) {
msleep(TIMEOUT_RESOLUTION);
res = fts_read_fw_reg(address, &data, 1);
if (res < OK) {
pr_err("%s: ERROR %08X\n", __func__, res);
return res;
}
if ((data & (0x01 << bit_to_check)) == 0x00)
break;
}
if (i == time_to_wait) {
pr_err("%s: FW reg status timeout.. RegVal: %02X\n", __func__, data);
return ERROR_TIMEOUT;
}
return OK;
}
/**
* Perform a firmware request by setting the regsiter bits.
* conditional auto clear option for waiting for that bits to be
* cleared by fw based on status of operation
* @param address variable to point register offset
* @param bit_to_set variable to set fw register bit
* @param auto_clear flag to poll for the bit to clear after request
* @param time_to_wait variable to set time out for auto clear
* @return OK if success or an error code which specify the type of error
*/
int fts_fw_request(u16 address, u8 bit_to_set, u8 auto_clear,
int time_to_wait)
{
int res = 0;
u8 data = 0x00;
if (address == PI_ADDR) {
/* This is a SW WA to do sense-on and sense-off before FPI.
* TODO(b/241527933#comment4): Asking vendor to provide the
* final solution when they find the root cause.
*/
u8 sense_on;
pr_info("%s: sensing on and sense off before FPI.", __func__);
sense_on = 0x01;
fts_write_fw_reg(0x10, &sense_on, 1);
msleep(200);
sense_on = 0x00;
fts_write_fw_reg(0x10, &sense_on, 1);
msleep(20);
}
res = fts_read_fw_reg(address, &data, 1);
if (res < OK) {
pr_err("%s: ERROR %08X\n", __func__, res);
return res;
}
data = data | (0x01 << bit_to_set);
res = fts_write_fw_reg(address, &data, 1);
if (res < OK) {
pr_err("%s: ERROR %08X\n", __func__, res);
return res;
}
if (auto_clear) {
res = poll_fw_reg_clear_status(address, bit_to_set,
time_to_wait);
if (res < OK) {
pr_err("%s: ERROR %08X\n", __func__, res);
return res;
}
} else
msleep(TIMEOUT_RESOLUTION * time_to_wait);
return res;
}
/**
* Perform a firmware request to save the data from hdm to system
* conditional save_to_flash option for saving data to flash
* @param save_to_flash variable to set option for save to flash
* @return OK if success or an error code which specify the type of error
*/
int fts_hdm_write_request(u8 save_to_flash)
{
int res = 0;
res = fts_fw_request(HDM_WRITE_REQ_ADDR, 0, 1,
TIMEOUT_FW_REG_STATUS);
if (res < OK) {
pr_err("%s: ERROR %08X\n", __func__, res);
return res;
}
if (save_to_flash) {
res = fts_fw_request(FLASH_SAVE_ADDR, 7, 1,
TIMEOUT_FW_REG_STATUS);
if (res < OK) {
pr_err("%s: ERROR %08X\n", __func__, res);
return res;
}
}
return res;
}
/**
* Perform a firmware register write to request hdm data
* wait for fw to clear the hdm register or timeout
* @param type variable different type of hdm data
* @return OK if success or an error code which specify the type of error
*/
int fts_request_hdm(u8 type)
{
int res = 0;
u8 data = type;
u8 read_buff = 0;
int i = 0;
res = fts_write_fw_reg(HDM_REQ_ADDR, &data, 1);
if (res < OK) {
pr_err("%s: ERROR %08X\n", __func__, res);
return res;
}
for (i = 0; i < TIMEOUT_FW_REG_STATUS; i++) {
msleep(TIMEOUT_RESOLUTION);
res = fts_read_fw_reg(HDM_REQ_ADDR, &read_buff, 1);
if (res < OK) {
pr_err("%s: ERROR %08X\n", __func__, res);
return res;
}
if (read_buff == 0x00)
break;
}
if (i == TIMEOUT_FW_REG_STATUS) {
pr_err("%s: HDM Request timeout.. RegVal: %02X\n", __func__, read_buff);
return ERROR_TIMEOUT;
}
return OK;
}
/**
* Read the System errors of size 8 bytes from Firmware register
* and print it
* @return OK if success or an error code which specify the type of error
*/
int fts_read_sys_errors(void)
{
int res = 0;
u8 data[8] = {0x00};
int i = 0;
res = fts_read_fw_reg(SYS_ERROR_ADDR, data, 8);
if (res < OK) {
pr_err("%s: ERROR %08X\n", __func__, res);
return res;
}
pr_info("%s: system errors:\n", __func__);
for (; i < 8; i++)
pr_info("%s: 0x%04X: %02X\n", __func__, SYS_ERROR_ADDR + i, data[i]);
return res;
}
/**
* Perform hdm header data read.
* initiates a firmware register hdm data request
* followed by reading the header data based on
* request status
* @param type variable different type of hdm data
* @param header pointer to data bytes for header
* @return OK if success or an error code which specify the type of error
*/
int read_hdm_header(uint8_t type, u8 *header)
{
int res = OK;
res = fts_request_hdm(type);
if (res < OK) {
pr_err("%s: error requesting hdm: %02X\n", __func__, type);
return res;
}
res = fts_read_hdm(FRAME_BUFFER_ADDR, header, COMP_HEADER_SIZE);
if (res < OK) {
pr_err("%s: read total cx header ERROR %08X\n",
__func__, res);
return res;
}
pr_info("%s: type: %02X, cnt: %02X, len: %d words\n", __func__,
header[0], header[1], (u16)((header[3] << 8) + header[2]));
if ((header[0] != type) && header[1] != 0)
pr_err("%s: HDM request error %08X\n", __func__, ERROR_TIMEOUT);
return res;
}
/**
* Read and pack the frame data related to the nodes
* @param address address in memory when the frame data node start
* @param size amount of data to read
* @param frame pointer to an array of bytes which will contain the frame node
*data
* @return OK if success or an error code which specify the type of error
*/
int get_frame_data(u16 address, int size, short *frame)
{
int i, j, res;
u8 *data = (u8 *)kmalloc(size * sizeof(u8), GFP_KERNEL);
if (data == NULL) {
pr_err("%s: ERROR %08X\n", __func__, ERROR_ALLOC);
return ERROR_ALLOC;
}
res = fts_write_read_u8ux(FTS_CMD_HW_REG_R, BITS_32,
FRAME_BUFFER_ADDRESS + address, data, size, DUMMY_BYTE);
if (res < OK) {
pr_err("%s: ERROR %08X\n", __func__, ERROR_BUS_R);
kfree(data);
data = NULL;
return ERROR_BUS_R;
}
j = 0;
for (i = 0; i < size; i += 2) {
frame[j] = (short)((data[i + 1] << 8) + data[i]);
j++;
}
kfree(data);
data = NULL;
return OK;
}
/**
* Read a MS Frame from frame buffer memory
* @param type type of MS frame to read
* @param frame pointer to MutualSenseFrame variable which will contain the
* data
* @return OK if success or an error code which specify the type of error
*/
int get_ms_frame(ms_frame_type_t type, struct mutual_sense_frame *frame)
{
u16 offset;
int res, force_len, sense_len;
force_len = system_info.u8_scr_tx_len;
sense_len = system_info.u8_scr_rx_len;
if (force_len == 0x00 || sense_len == 0x00 ||
force_len == 0xFF || sense_len == 0xFF) {
pr_err("%s: number of channels not initialized ERROR %08X\n",
__func__, ERROR_CH_LEN);
return ERROR_CH_LEN | ERROR_GET_FRAME;
}
frame->node_data = NULL;
pr_info("%s: Starting to get frame %02X\n", __func__, type);
switch (type) {
case MS_RAW:
offset = system_info.u16_ms_scr_raw_addr;
break;
case MS_STRENGTH:
offset = system_info.u16_ms_scr_strength_addr;
break;
case MS_FILTER:
offset = system_info.u16_ms_scr_filter_addr;
break;
case MS_BASELINE:
offset = system_info.u16_ms_scr_baseline_addr;
break;
default:
pr_err("%s: Invalid type ERROR %08X\n",
__func__, ERROR_OP_NOT_ALLOW | ERROR_GET_FRAME);
return ERROR_OP_NOT_ALLOW | ERROR_GET_FRAME;
}
frame->node_data_size = (force_len * sense_len);
frame->header.force_node = force_len;
frame->header.sense_node = sense_len;
frame->header.type = type;
pr_info("%s: Force_len = %d Sense_len = %d Offset = %04X\n",
__func__, force_len, sense_len, offset);
frame->node_data = (short *)kmalloc(frame->node_data_size *
sizeof(short), GFP_KERNEL);
if (frame->node_data == NULL) {
pr_err("%s: ERROR %08X\n", __func__, ERROR_ALLOC | ERROR_GET_FRAME);
return ERROR_ALLOC | ERROR_GET_FRAME;
}
res = get_frame_data(offset, frame->node_data_size *
BYTES_PER_NODE, (frame->node_data));
if (res < OK) {
pr_err("%s: ERROR %08X\n", __func__, ERROR_GET_FRAME_DATA);
kfree(frame->node_data);
frame->node_data = NULL;
return res | ERROR_GET_FRAME_DATA | ERROR_GET_FRAME;
}
/* if you want to access one node i,j,
* compute the offset like: offset = i*columns + j = > frame[i, j] */
pr_info("%s: Frame acquired!\n", __func__);
return OK;
/* return the number of data put inside frame */
}
/**
* Read a SS Frame from frame buffer
* @param type type of SS frame to read
* @param frame pointer to SelfSenseFrame variable which will contain the data
* @return OK if success or an error code which specify the type of error
*/
int get_ss_frame(ss_frame_type_t type, struct self_sense_frame *frame)
{
u16 self_force_offset = 0;
u16 self_sense_offset = 0;
int res, force_len, sense_len, tmp_size;
force_len = system_info.u8_scr_tx_len;
sense_len = system_info.u8_scr_rx_len;
if (force_len == 0x00 || sense_len == 0x00 ||
force_len == 0xFF || sense_len == 0xFF) {
pr_err("%s: number of channels not initialized ERROR %08X\n",
__func__, ERROR_CH_LEN);
return ERROR_CH_LEN | ERROR_GET_FRAME;
}
frame->force_data = NULL;
frame->sense_data = NULL;
frame->header.force_node = force_len;
frame->header.sense_node = sense_len;
pr_info("%s: Starting to get frame %02X\n", __func__, type);
switch (type) {
case SS_RAW:
self_force_offset = system_info.u16_ss_tch_tx_raw_addr;
self_sense_offset = system_info.u16_ss_tch_rx_raw_addr;
break;
case SS_FILTER:
self_force_offset = system_info.u16_ss_tch_tx_filter_addr;
self_sense_offset = system_info.u16_ss_tch_rx_filter_addr;
break;
case SS_BASELINE:
self_force_offset = system_info.u16_ss_tch_tx_baseline_addr;
self_sense_offset = system_info.u16_ss_tch_rx_baseline_addr;
break;
case SS_STRENGTH:
self_force_offset = system_info.u16_ss_tch_tx_strength_addr;
self_sense_offset = system_info.u16_ss_tch_rx_strength_addr;
break;
case SS_DETECT_RAW:
self_force_offset = system_info.u16_ss_det_tx_raw_addr;
self_sense_offset = system_info.u16_ss_det_rx_raw_addr;
frame->header.force_node = (self_force_offset == 0) ?
0 : frame->header.force_node;
frame->header.sense_node = (self_sense_offset == 0) ?
0 : frame->header.sense_node;
break;
case SS_DETECT_STRENGTH:
self_force_offset = system_info.u16_ss_det_tx_strength_addr;
self_sense_offset = system_info.u16_ss_det_rx_strength_addr;
frame->header.force_node = (self_force_offset == 0) ?
0 : frame->header.force_node;
frame->header.sense_node = (self_sense_offset == 0) ?
0 : frame->header.sense_node;
break;
case SS_DETECT_BASELINE:
self_force_offset = system_info.u16_ss_det_tx_baseline_addr;
self_sense_offset = system_info.u16_ss_det_rx_baseline_addr;
frame->header.force_node = (self_force_offset == 0) ?
0 : frame->header.force_node;
frame->header.sense_node = (self_sense_offset == 0) ?
0 : frame->header.sense_node;
break;
case SS_DETECT_FILTER:
self_force_offset = system_info.u16_ss_det_tx_filter_addr;
self_sense_offset = system_info.u16_ss_det_rx_filter_addr;
frame->header.force_node = (self_force_offset == 0) ?
0 : frame->header.force_node;
frame->header.sense_node = (self_sense_offset == 0) ?
0 : frame->header.sense_node;
break;
default:
pr_err("%s: Invalid type ERROR %08X\n",
__func__, ERROR_OP_NOT_ALLOW | ERROR_GET_FRAME);
return ERROR_OP_NOT_ALLOW | ERROR_GET_FRAME;
}
frame->header.type = type;
pr_info("%s: Force_len = %d Sense_len = %d Offset_force = %04X Offset_sense = %04X\n",
__func__, frame->header.force_node, frame->header.sense_node,
self_force_offset, self_sense_offset);
tmp_size = frame->header.force_node * sizeof(short);
frame->force_data = kmalloc(tmp_size, GFP_KERNEL);
if (frame->force_data == NULL) {
pr_err("%s: ERROR %08X\n", __func__, ERROR_ALLOC | ERROR_GET_FRAME);
return ERROR_ALLOC | ERROR_GET_FRAME;
}
tmp_size = frame->header.sense_node * sizeof(short);
frame->sense_data = kmalloc(tmp_size, GFP_KERNEL);
if (frame->sense_data == NULL) {
pr_err("%s: ERROR %08X\n", __func__, ERROR_ALLOC | ERROR_GET_FRAME);
kfree(frame->force_data);
frame->force_data = NULL;
return ERROR_ALLOC | ERROR_GET_FRAME;
}
if (self_force_offset) {
res = get_frame_data(self_force_offset,
frame->header.force_node *
BYTES_PER_NODE, (frame->force_data));
if (res < OK) {
pr_err("%s: error while reading force data ERROR %08X\n",
__func__, ERROR_GET_FRAME_DATA);
kfree(frame->force_data);
frame->force_data = NULL;
kfree(frame->sense_data);
frame->sense_data = NULL;
return res | ERROR_GET_FRAME_DATA | ERROR_GET_FRAME;
}
}
if (self_sense_offset) {
res = get_frame_data(self_sense_offset,
frame->header.sense_node *
BYTES_PER_NODE, (frame->sense_data));
if (res < OK) {
pr_err("%s: error while reading force data ERROR %08X\n",
__func__, res | ERROR_GET_FRAME_DATA | ERROR_GET_FRAME);
kfree(frame->force_data);
frame->force_data = NULL;
kfree(frame->sense_data);
frame->sense_data = NULL;
return res | ERROR_GET_FRAME_DATA | ERROR_GET_FRAME;
}
}
pr_info("%s: Frame acquired!\n", __func__);
return OK;
}
/**
* Read a Sync Frame from frame buffer which contain MS and SS data collected
*for the same scan
* @param type type of Sync frame to read, possible values:
* LOAD_SYNC_FRAME_RAW, LOAD_SYNC_FRAME_FILTER, LOAD_SYNC_FRAME_BASELINE,
* LOAD_SYNC_FRAME_STRENGTH
* @param msFrame pointer to MutualSenseFrame variable which will contain the
*MS data
* @param ssFrame pointer to SelfSenseFrame variable which will contain the SS
*data
* @return OK if success or an error code which specify the type of error
*/
int get_sync_frame(u8 type, struct mutual_sense_frame *ms_frame,
struct self_sense_frame *ss_frame)
{
int res;
u8 header_data[SYNC_FRAME_HEADER_SIZE] = {0x00};
u32 address = 0;
u8 *sync_frame_data = NULL;
u64 sync_frame_size = 0;
int i = 0, j = 0;
int offset;
ms_frame->node_data = NULL;
ss_frame->force_data = NULL;
ss_frame->sense_data = NULL;
res = read_hdm_header(type, header_data);
if (res < OK) {
pr_err("%s: read hdm header error\n", __func__);
return res | ERROR_GET_FRAME;
}
ms_frame->header.force_node = ss_frame->header.force_node =
header_data[5];
ms_frame->header.sense_node = ss_frame->header.sense_node =
header_data[6];
ms_frame->header.type = type;
pr_info("%s: tx_count: %d rx_count: %d\n", __func__,
ms_frame->header.force_node, ms_frame->header.sense_node);
if (ms_frame->header.force_node == 0x00 ||
ms_frame->header.sense_node == 0x00 ||
ms_frame->header.force_node == 0xFF ||
ms_frame->header.sense_node == 0xFF) {
pr_err("%s: force/sense length cannot be empty.Invalid sync frame header\n",
__func__);
return ERROR_CH_LEN | ERROR_GET_FRAME;
}
sync_frame_size = (header_data[5] * header_data[6] * 2) +
(header_data[5] * 2) + (header_data[6] * 2);
pr_info("%s: sync frame size: %lld\n", __func__, (unsigned long long)sync_frame_size);
sync_frame_data = (u8 *)kmalloc(sync_frame_size *
sizeof(u8), GFP_KERNEL);
if (sync_frame_data == NULL) {
pr_err("%s: ERROR %08X\n", __func__, ERROR_ALLOC | ERROR_GET_FRAME);
return ERROR_ALLOC | ERROR_GET_FRAME;
}
address = FRAME_BUFFER_ADDR + SYNC_FRAME_HEADER_SIZE + header_data[4];
pr_info("%s: sync frame address: 0x%04X\n", __func__, address);
res = fts_read_hdm(address, sync_frame_data, sync_frame_size);
if (res < OK) {
pr_err("%s: sync frame read ERROR %08X\n", __func__, ERROR_ALLOC | ERROR_GET_FRAME);
kfree(sync_frame_data);
sync_frame_data = NULL;
return res | ERROR_ALLOC | ERROR_GET_FRAME;
}
ms_frame->node_data_size = ms_frame->header.force_node *
ms_frame->header.sense_node;
ms_frame->node_data = (short *)kmalloc(ms_frame->node_data_size *
sizeof(short), GFP_KERNEL);
if (ms_frame->node_data == NULL) {
pr_err("%s: ERROR %08X\n", __func__, ERROR_ALLOC | ERROR_GET_FRAME);
res = ERROR_ALLOC | ERROR_GET_FRAME;
goto goto_end;
}
offset = ms_frame->node_data_size * 2;
for (; i < offset; i += 2) {
ms_frame->node_data[j] =
(short)((sync_frame_data[i + 1] << 8) + sync_frame_data[i]);
j++;
}
ss_frame->force_data = (short *)kmalloc(ss_frame->header.force_node *
sizeof(short), GFP_KERNEL);
if (ss_frame->force_data == NULL) {
pr_err("%s: ERROR %08X\n", __func__, ERROR_ALLOC | ERROR_GET_FRAME);
res = ERROR_ALLOC | ERROR_GET_FRAME;
goto goto_end;
}
j = 0;
offset = ss_frame->header.force_node * 2 + i;
pr_info("%s: sync frame ss force: %d\n", __func__, i);
for (; i < offset; i += 2) {
ss_frame->force_data[j] =
(short)((sync_frame_data[i + 1] << 8) + sync_frame_data[i]);
j++;
}
ss_frame->sense_data = (short *)kmalloc(ss_frame->header.sense_node *
sizeof(short), GFP_KERNEL);
if (ss_frame->sense_data == NULL) {
pr_err("%s: ERROR %08X\n", __func__, ERROR_ALLOC | ERROR_GET_FRAME);
res = ERROR_ALLOC | ERROR_GET_FRAME;
goto goto_end;
}
offset = ss_frame->header.sense_node * 2 + i;
pr_info("%s: sync frame ss sense: %d\n", __func__, i);
j = 0;
for (; i < offset; i += 2) {
ss_frame->sense_data[j] =
(short)((sync_frame_data[i + 1] << 8) + sync_frame_data[i]);
j++;
}
goto_end:
if (sync_frame_data != NULL) {
kfree(sync_frame_data);
sync_frame_data = NULL;
}
if (res < OK) {
if (ms_frame->node_data != NULL) {
kfree(ms_frame->node_data);
ms_frame->node_data = NULL;
}
if (ss_frame->force_data != NULL) {
kfree(ss_frame->force_data);
ss_frame->force_data = NULL;
}
if (ss_frame->sense_data != NULL) {
kfree(ss_frame->sense_data);
ss_frame->sense_data = NULL;
}
pr_err("%s: Getting Sync Frame FAILED! ERROR %08X!\n", __func__, res);
} else
pr_info("%s: Getting Sync Frame Finished!!\n", __func__);
return res;
}
/**
* Perform all the steps to read the necessary info for MS Initialization data
* from the buffer and store it in a mutual_sense_cx_data variable
* @param type type of MS Initialization data to read
* @param ms_cx_data pointer to mutual_sense_cx_data variable which will contain the MS
* initialization data
* @return OK if success or an error code which specify the type of error
*/
int get_mutual_cx_data(u8 type, struct mutual_sense_cx_data *ms_cx_data)
{
int res;
u32 address = 0;
u8 header_data[COMP_HEADER_SIZE] = { 0x00 };
ms_cx_data->node_data = NULL;
if (!(type == HDM_REQ_CX_MS_TOUCH || type == HDM_REQ_CX_MS_LOW_POWER)) {
pr_err("%s: Choose a MS type of compensation data ERROR %08X\n",
__func__, ERROR_OP_NOT_ALLOW | ERROR_GET_CX);
return ERROR_OP_NOT_ALLOW | ERROR_GET_CX;
}
res = read_hdm_header(type, header_data);
if (res < OK) {
pr_err("%s: read hdm header error\n", __func__);
return res | ERROR_GET_CX;
}
ms_cx_data->header.force_node = header_data[4];
ms_cx_data->header.sense_node = header_data[5];
ms_cx_data->header.type = type;
pr_info("%s: tx_count: %d rx_count: %d\n", __func__,
ms_cx_data->header.force_node, ms_cx_data->header.sense_node);
if (ms_cx_data->header.force_node == 0x00 ||
ms_cx_data->header.sense_node == 0x00 ||
ms_cx_data->header.force_node == 0xFF ||
ms_cx_data->header.sense_node == 0xFF) {
pr_err("%s: force/sense length cannot be empty.Invalid header\n",
__func__);
return ERROR_CH_LEN | ERROR_GET_CX;
}
ms_cx_data->cx1 = header_data[8];
pr_info("%s: cx1: %d\n", __func__, ms_cx_data->cx1);
ms_cx_data->node_data_size = ms_cx_data->header.force_node *
ms_cx_data->header.sense_node;
address = FRAME_BUFFER_ADDR + COMP_HEADER_SIZE;
pr_info("%s: compensation data address: 0x%04X, size: %d\n",
__func__, address, ms_cx_data->node_data_size);
ms_cx_data->node_data = (i8 *)kmalloc(ms_cx_data->node_data_size *
(sizeof(i8)), GFP_KERNEL);
if (ms_cx_data->node_data == NULL) {
pr_err("%s: can not allocate node_data... ERROR %08X",
__func__, ERROR_ALLOC);
return ERROR_ALLOC;
}
res = fts_read_hdm(address, ms_cx_data->node_data,
ms_cx_data->node_data_size);
if (res < OK) {
pr_err("%s: sync frame read ERROR %08X\n",
__func__, ERROR_ALLOC | ERROR_GET_FRAME);
kfree(ms_cx_data->node_data);
ms_cx_data->node_data = NULL;
return ERROR_ALLOC | ERROR_GET_FRAME;
}
pr_info("%s: Read Mutual CX data done!!\n", __func__);
return OK;
}
/**
* Perform all the steps to read the necessary info for SS Initialization data
* from the buffer and store it in a self_sense_cx_data variable
* @param type type of SS Initialization data to read
* @param ss_cx_data pointer to self_sense_cx_data variable which will contain the SS
* initialization data
* @return OK if success or an error code which specify the type of error
*/
int get_self_cx_data(u8 type, struct self_sense_cx_data *ss_cx_data)
{
int res;
u32 address = 0;
u8 header_data[COMP_HEADER_SIZE] = { 0x00 };
int size = 0;
u8 *data = NULL;
ss_cx_data->ix2_tx = NULL;
ss_cx_data->ix2_rx = NULL;
ss_cx_data->cx2_tx = NULL;
ss_cx_data->cx2_rx = NULL;
if (!(type == HDM_REQ_CX_SS_TOUCH ||
type == HDM_REQ_CX_SS_TOUCH_IDLE)) {
pr_err("%s: Choose a SS type of compensation data ERROR %08X\n",
__func__, ERROR_OP_NOT_ALLOW | ERROR_GET_CX);
return ERROR_OP_NOT_ALLOW | ERROR_GET_CX;
}
res = read_hdm_header(type, header_data);
if (res < OK) {
pr_err("%s: read hdm header error\n", __func__);
return res | ERROR_GET_CX;
}
ss_cx_data->header.force_node = header_data[4];
ss_cx_data->header.sense_node = header_data[5];
ss_cx_data->header.type = type;
pr_info("%s: tx_count: %d rx_count: %d\n", __func__,
ss_cx_data->header.force_node,
ss_cx_data->header.sense_node);
if (ss_cx_data->header.force_node == 0x00 ||
ss_cx_data->header.sense_node == 0x00 ||
ss_cx_data->header.force_node == 0xFF ||
ss_cx_data->header.sense_node == 0xFF) {
pr_err("%s: force/sense length cannot be empty.Invalid header\n",
__func__);
return ERROR_CH_LEN | ERROR_GET_CX;
}
ss_cx_data->tx_ix0 = header_data[8];
ss_cx_data->rx_ix0 = header_data[9];
ss_cx_data->tx_ix1 = header_data[10];
ss_cx_data->rx_ix1 = header_data[11];
ss_cx_data->tx_max_n = header_data[12];
ss_cx_data->rx_max_n = header_data[13];
ss_cx_data->tx_cx1 = (i8)header_data[14];
ss_cx_data->rx_cx1 = (i8)header_data[15];
pr_info("%s: tx_ix1 = %d rx_ix1 = %d tx_cx1 = %d rx_cx1 = %d\n",
__func__, ss_cx_data->tx_ix1, ss_cx_data->rx_ix1,
ss_cx_data->tx_cx1, ss_cx_data->rx_cx1);
pr_info("%s: tx_max_n = %d rx_max_n = %d tx_ix0 = %d rx_ix0 = %d\n",
__func__, ss_cx_data->tx_max_n, ss_cx_data->rx_max_n,
ss_cx_data->tx_ix0, ss_cx_data->rx_ix0);
size = (ss_cx_data->header.force_node * 2) +
(ss_cx_data->header.sense_node * 2);
data = (u8 *)kmalloc(size * (sizeof(u8)), GFP_KERNEL);
if (data == NULL) {
pr_err("%s: ERROR %08X\n", __func__,
ERROR_ALLOC | ERROR_GET_FRAME);
return ERROR_ALLOC | ERROR_GET_CX;
}
address = FRAME_BUFFER_ADDR + COMP_HEADER_SIZE;
pr_info("%s: compensation data address: 0x%04X, size: %d\n",
__func__, address, size);
res = fts_read_hdm(address, data, size);
if (res < OK) {
pr_err("%s: sync frame read ERROR %08X\n",
__func__, ERROR_ALLOC | ERROR_GET_FRAME);
kfree(data);
data = NULL;
return ERROR_ALLOC | ERROR_GET_CX;
}
ss_cx_data->ix2_tx = (u8 *)kmalloc(ss_cx_data->header.force_node *
(sizeof(i8)), GFP_KERNEL);
if (ss_cx_data->ix2_tx == NULL) {
pr_err("%s: can not allocate node_data... ERROR %08X",
__func__, ERROR_ALLOC);
res = ERROR_ALLOC | ERROR_GET_CX;
goto goto_end;
}
ss_cx_data->ix2_rx = (u8 *)kmalloc(ss_cx_data->header.sense_node *
(sizeof(i8)), GFP_KERNEL);
if (ss_cx_data->ix2_rx == NULL) {
pr_err("%s: can not allocate node_data... ERROR %08X",
__func__, ERROR_ALLOC);
res = ERROR_ALLOC | ERROR_GET_CX;
goto goto_end;
}
ss_cx_data->cx2_tx = (i8 *)kmalloc(ss_cx_data->header.force_node *
(sizeof(i8)), GFP_KERNEL);
if (ss_cx_data->cx2_tx == NULL) {
pr_err("%s: can not allocate node_data... ERROR %08X",
__func__, ERROR_ALLOC);
res = ERROR_ALLOC | ERROR_GET_CX;
goto goto_end;
}
ss_cx_data->cx2_rx = (i8 *)kmalloc(ss_cx_data->header.sense_node *
(sizeof(i8)), GFP_KERNEL);
if (ss_cx_data->cx2_rx == NULL) {
pr_err("%s: can not allocate node_data... ERROR %08X",
__func__, ERROR_ALLOC);
res = ERROR_ALLOC | ERROR_GET_CX;
goto goto_end;
}
size = 0;
memcpy(ss_cx_data->ix2_tx, data, ss_cx_data->header.force_node);
size += ss_cx_data->header.force_node;
memcpy(ss_cx_data->ix2_rx, data + size, ss_cx_data->header.sense_node);
size += ss_cx_data->header.sense_node;
memcpy(ss_cx_data->cx2_tx, data + size, ss_cx_data->header.force_node);
size += ss_cx_data->header.force_node;
memcpy(ss_cx_data->cx2_rx, data + size, ss_cx_data->header.sense_node);
goto_end:
if (data != NULL) {
kfree(data);
data = NULL;
}
if (res < OK) {
if (ss_cx_data->ix2_tx != NULL) {
kfree(ss_cx_data->ix2_tx);
ss_cx_data->ix2_tx = NULL;
}
if (ss_cx_data->ix2_rx != NULL) {
kfree(ss_cx_data->ix2_rx);
ss_cx_data->ix2_rx = NULL;
}
if (ss_cx_data->cx2_tx != NULL) {
kfree(ss_cx_data->cx2_tx);
ss_cx_data->cx2_tx = NULL;
}
if (ss_cx_data->cx2_rx != NULL) {
kfree(ss_cx_data->cx2_rx);
ss_cx_data->cx2_rx = NULL;
}
} else
pr_info("%s: Read Self CX data done!!\n", __func__);
return res;
}
/**
* Perform all the steps to read the necessary info for TOT MS Initialization
* data from the buffer and store it in a mutual_total_cx_data variable
* @param type type of TOT MS Initialization data to read
* @param tot_ms_cx_data pointer to a mutual_total_cx_data variable
* which will contain the TOT MS initialization data
* @return OK if success or an error code which specify the type of error
*/
int get_mutual_total_cx_data(u8 type, struct mutual_total_cx_data *tot_ms_cx_data)
{
int res;
u32 address = 0;
int size = 0;
int i, j = 0;
u8 *data = NULL;
u8 header_data[COMP_HEADER_SIZE] = { 0x00 };
tot_ms_cx_data->node_data = NULL;
if (!(type == HDM_REQ_TOT_CX_MS_TOUCH ||
type == HDM_REQ_TOT_CX_MS_LOW_POWER)) {
pr_err("%s: Choose a MS total type of compensation data ERROR %08X\n",
__func__, ERROR_OP_NOT_ALLOW);
return ERROR_OP_NOT_ALLOW;
}
res = read_hdm_header(type, header_data);
if (res < OK) {
pr_err("%s: read hdm header error\n", __func__);
return res | ERROR_GET_CX;
}
tot_ms_cx_data->header.force_node = header_data[4];
tot_ms_cx_data->header.sense_node = header_data[5];
tot_ms_cx_data->header.type = type;
pr_info("%s: tx_count: %d rx_count: %d\n", __func__,
tot_ms_cx_data->header.force_node,
tot_ms_cx_data->header.sense_node);
if (tot_ms_cx_data->header.force_node == 0x00 ||
tot_ms_cx_data->header.sense_node == 0x00 ||
tot_ms_cx_data->header.force_node == 0xFF ||
tot_ms_cx_data->header.sense_node == 0xFF) {
pr_err("%s: force/sense length cannot be empty.. Invalid sysn frame header\n",
__func__);
return ERROR_CH_LEN | ERROR_GET_CX;
}
size = tot_ms_cx_data->header.force_node *
tot_ms_cx_data->header.sense_node * 2;
address = FRAME_BUFFER_ADDR + COMP_HEADER_SIZE;
pr_info("%s: compensation data address: 0x%04X, size: %d\n",
__func__, address, size);
data = (u8 *)kmalloc(size * sizeof(u8), GFP_KERNEL);
if (data == NULL) {
pr_err("%s: can not allocate node_data... ERROR %08X",
__func__, ERROR_ALLOC);
return ERROR_ALLOC | ERROR_GET_CX;
}
res = fts_read_hdm(address, data, size);
if (res < OK) {
pr_err("%s: Total Mutual CX read ERROR %08X\n", __func__,
ERROR_ALLOC | ERROR_GET_FRAME);
kfree(tot_ms_cx_data->node_data);
tot_ms_cx_data->node_data = NULL;
return ERROR_ALLOC | ERROR_GET_FRAME;
}
tot_ms_cx_data->node_data_size = size / 2;
tot_ms_cx_data->node_data = (short *)kmalloc(
tot_ms_cx_data->node_data_size *
(sizeof(short)), GFP_KERNEL);
if (tot_ms_cx_data->node_data == NULL) {
pr_err("%s: can not allocate node_data... ERROR %08X",
__func__, ERROR_ALLOC);
return ERROR_ALLOC;
}
for (i = 0; i < size; i += 2) {
tot_ms_cx_data->node_data[j] = (short)((data[i + 1] << 8) +
data[i]);
j++;
}
pr_info("%s: Read Mutual Total CX data done!!\n", __func__);
return OK;
}
/**
* Perform all the steps to read the necessary info for TOT SS Initialization
* data from the buffer and store it in a self_total_cx_data variable
* @param type type of TOT MS Initialization data to read
* @param tot_ss_cx_data pointer to a self_total_cx_data variable
* which will contain the TOT MS initialization data
* @return OK if success or an error code which specify the type of error
*/
int get_self_total_cx_data(u8 type, struct self_total_cx_data *tot_ss_cx_data)
{
int res;
u32 address = 0;
u8 header_data[COMP_HEADER_SIZE] = { 0x00 };
int size = 0;
int i, j = 0;
u8 *data = NULL;
tot_ss_cx_data->ix_tx = NULL;
tot_ss_cx_data->ix_rx = NULL;
if (!(type == HDM_REQ_TOT_IX_SS_TOUCH ||
type == HDM_REQ_TOT_IX_SS_TOUCH_IDLE)) {
pr_err("%s: Choose a SS type of compensation data ERROR %08X\n",
__func__, ERROR_OP_NOT_ALLOW | ERROR_GET_CX);
return ERROR_OP_NOT_ALLOW | ERROR_GET_CX;
}
res = read_hdm_header(type, header_data);
if (res < OK) {
pr_err("%s: read hdm header error\n", __func__);
return res | ERROR_GET_CX;
}
tot_ss_cx_data->header.force_node = header_data[4];
tot_ss_cx_data->header.sense_node = header_data[5];
tot_ss_cx_data->header.type = type;
pr_info("%s: tx_count: %d rx_count: %d\n", __func__,
tot_ss_cx_data->header.force_node,
tot_ss_cx_data->header.sense_node);
if (tot_ss_cx_data->header.force_node == 0x00 ||
tot_ss_cx_data->header.sense_node == 0x00 ||
tot_ss_cx_data->header.force_node == 0xFF ||
tot_ss_cx_data->header.sense_node == 0xFF) {
pr_err("%s: force/sense length cannot be empty.. Invalid sysn frame header\n",
__func__);
return ERROR_CH_LEN | ERROR_GET_CX;
}
size = (tot_ss_cx_data->header.force_node * 2) +
(tot_ss_cx_data->header.sense_node * 2);
data = (u8 *)kmalloc(size * (sizeof(u8)), GFP_KERNEL);
if (data == NULL) {
pr_err("%s: ERROR %08X\n", __func__, ERROR_ALLOC | ERROR_GET_FRAME);
return ERROR_ALLOC | ERROR_GET_CX;
}
address = FRAME_BUFFER_ADDR + COMP_HEADER_SIZE;
pr_info("%s: compensation data address: 0x%04X, size: %d\n",
__func__, address, size);
res = fts_read_hdm(address, data, size);
if (res < OK) {
pr_err("%s: self cx read ERROR %08X\n", __func__,
ERROR_ALLOC | ERROR_GET_FRAME);
kfree(data);
data = NULL;
return ERROR_ALLOC | ERROR_GET_CX;
}
tot_ss_cx_data->ix_tx = (u16 *)kmalloc(tot_ss_cx_data->header.force_node
* (sizeof(u16)), GFP_KERNEL);
if (tot_ss_cx_data->ix_tx == NULL) {
pr_err("%s: can not allocate node_data... ERROR %08X",
__func__, ERROR_ALLOC);
res = ERROR_ALLOC | ERROR_GET_CX;
goto goto_end;
}
tot_ss_cx_data->ix_rx = (u16 *)kmalloc(tot_ss_cx_data->header.sense_node
* (sizeof(u16)), GFP_KERNEL);
if (tot_ss_cx_data->ix_rx == NULL) {
pr_err("%s: can not allocate node_data... ERROR %08X",
__func__, ERROR_ALLOC);
res = ERROR_ALLOC | ERROR_GET_CX;
goto goto_end;
}
for (i = 0; i < (tot_ss_cx_data->header.force_node * 2); i += 2) {
tot_ss_cx_data->ix_tx[j] = (u16)((data[i + 1] << 8) + data[i]);
j++;
}
j = 0;
for (i = (tot_ss_cx_data->header.force_node * 2); i < size; i += 2) {
tot_ss_cx_data->ix_rx[j] = (u16)((data[i + 1] << 8) + data[i]);
j++;
}
goto_end:
if (data != NULL) {
kfree(data);
data = NULL;
}
if (res < OK) {
if (tot_ss_cx_data->ix_tx != NULL) {
kfree(tot_ss_cx_data->ix_tx);
tot_ss_cx_data->ix_tx = NULL;
}
if (tot_ss_cx_data->ix_rx != NULL) {
kfree(tot_ss_cx_data->ix_rx);
tot_ss_cx_data->ix_rx = NULL;
}
} else
pr_info("%s: Read Self CX data done!!\n", __func__);
return res;
}