288 lines
7.3 KiB
C
288 lines
7.3 KiB
C
|
/* SPDX-License-Identifier: GPL-2.0 */
|
||
|
/*
|
||
|
* Copyright (C) 2016 MediaTek Inc.
|
||
|
*/
|
||
|
|
||
|
/************************************************************************
|
||
|
*
|
||
|
* File Name: focaltech_i2c.c
|
||
|
*
|
||
|
* Author: Focaltech Driver Team
|
||
|
*
|
||
|
* Created: 2016-08-04
|
||
|
*
|
||
|
* Abstract: i2c communication with TP
|
||
|
*
|
||
|
* Version: v1.0
|
||
|
*
|
||
|
* Revision History:
|
||
|
*
|
||
|
************************************************************************/
|
||
|
|
||
|
/*****************************************************************************
|
||
|
* Included header files
|
||
|
*****************************************************************************/
|
||
|
#include "focaltech_core.h"
|
||
|
|
||
|
/*****************************************************************************
|
||
|
* Private constant and macro definitions using #define
|
||
|
*****************************************************************************/
|
||
|
#define DMA_BUFFER_LENGTH 256
|
||
|
#define I2C_RETRY_NUMBER 3
|
||
|
|
||
|
/*****************************************************************************
|
||
|
* Private enumerations, structures and unions using typedef
|
||
|
*****************************************************************************/
|
||
|
|
||
|
/*****************************************************************************
|
||
|
* Static variables
|
||
|
*****************************************************************************/
|
||
|
|
||
|
#ifdef CONFIG_MTK_I2C_EXTENSION
|
||
|
u8 *g_dma_buff_va;
|
||
|
dma_addr_t g_dma_buff_pa;
|
||
|
#endif
|
||
|
|
||
|
/*****************************************************************************
|
||
|
* Global variable or extern global variabls/functions
|
||
|
*****************************************************************************/
|
||
|
|
||
|
/*****************************************************************************
|
||
|
* Static function prototypes
|
||
|
*****************************************************************************/
|
||
|
|
||
|
/*****************************************************************************
|
||
|
* functions body
|
||
|
*****************************************************************************/
|
||
|
#ifdef CONFIG_MTK_I2C_EXTENSION
|
||
|
int fts_read(u8 *cmd, u32 cmdlen, u8 *data, u32 datalen)
|
||
|
{
|
||
|
int ret = 0;
|
||
|
int i = 0;
|
||
|
struct i2c_client *client = NULL;
|
||
|
|
||
|
/* must have data when read */
|
||
|
if (!fts_data || !fts_data->client || !data || !datalen) {
|
||
|
FTS_ERROR("fts_data/client/data/datalen(%d) is invalid",
|
||
|
datalen);
|
||
|
return -EINVAL;
|
||
|
}
|
||
|
client = fts_data->client;
|
||
|
|
||
|
mutex_lock(&fts_data->bus_lock);
|
||
|
if ((writelen > 0) && (writelen <= DMA_BUFFER_LENGTH)) {
|
||
|
memcpy(g_dma_buff_va, cmd, cmdlen);
|
||
|
client->addr = ((client->addr & I2C_MASK_FLAG) | I2C_DMA_FLAG);
|
||
|
for (i = 0; i < I2C_RETRY_NUMBER; i++) {
|
||
|
ret = i2c_master_send(client,
|
||
|
(unsigned char *)g_dma_buff_pa,
|
||
|
writelen);
|
||
|
if (ret != writelen) {
|
||
|
FTS_ERROR(
|
||
|
"[IIC]: i2c_master_send failed, ret=%d!!",
|
||
|
ret);
|
||
|
} else
|
||
|
break;
|
||
|
}
|
||
|
client->addr = client->addr & I2C_MASK_FLAG & (~I2C_DMA_FLAG);
|
||
|
}
|
||
|
|
||
|
if ((readlen > 0) && (readlen <= DMA_BUFFER_LENGTH)) {
|
||
|
client->addr = ((client->addr & I2C_MASK_FLAG) | I2C_DMA_FLAG);
|
||
|
for (i = 0; i < I2C_RETRY_NUMBER; i++) {
|
||
|
ret = i2c_master_recv(client,
|
||
|
(unsigned char *)g_dma_buff_pa,
|
||
|
datalen);
|
||
|
if (ret != readlen)
|
||
|
FTS_ERROR("i2c_master_recv failed,ret=%d", ret);
|
||
|
else
|
||
|
break;
|
||
|
}
|
||
|
memcpy(data, g_dma_buff_va, readlen);
|
||
|
client->addr = client->addr & I2C_MASK_FLAG & (~I2C_DMA_FLAG);
|
||
|
} else
|
||
|
FTS_ERROR("i2c read len(%d) fail", readlen);
|
||
|
mutex_unlock(&fts_data->bus_lock);
|
||
|
return ret;
|
||
|
}
|
||
|
|
||
|
int fts_i2c_write(u8 *writebuf, int writelen)
|
||
|
{
|
||
|
int ret = 0;
|
||
|
int i = 0;
|
||
|
struct i2c_client *client = NULL;
|
||
|
|
||
|
if (!fts_data || !fts_data->client || !writebuf || !writelen) {
|
||
|
FTS_ERROR("fts_data/client/data/datalen(%d) is invalid",
|
||
|
writelen);
|
||
|
return -EINVAL;
|
||
|
}
|
||
|
client = fts_data->client;
|
||
|
|
||
|
mutex_lock(&fts_data->bus_lock);
|
||
|
if ((writelen > 0) && (writelen <= DMA_BUFFER_LENGTH)) {
|
||
|
memcpy(g_dma_buff_va, writebuf, writelen);
|
||
|
client->addr = ((client->addr & I2C_MASK_FLAG) | I2C_DMA_FLAG);
|
||
|
for (i = 0; i < I2C_RETRY_NUMBER; i++) {
|
||
|
ret = i2c_master_send(client,
|
||
|
(unsigned char *)g_dma_buff_pa,
|
||
|
writelen);
|
||
|
if (ret != writelen)
|
||
|
FTS_ERROR("i2c_master_send failed,ret=%d", ret);
|
||
|
else
|
||
|
break;
|
||
|
}
|
||
|
client->addr = client->addr & I2C_MASK_FLAG & (~I2C_DMA_FLAG);
|
||
|
} else
|
||
|
FTS_ERROR("i2c write len(%d) fail", writelen);
|
||
|
mutex_unlock(&fts_data->bus_lock);
|
||
|
|
||
|
return ret;
|
||
|
}
|
||
|
|
||
|
static void fts_i2c_msg_dma_alloct(void)
|
||
|
{
|
||
|
FTS_FUNC_ENTER();
|
||
|
if (g_dma_buff_va == NULL) {
|
||
|
fts_data->input_dev->dev.coherent_dma_mask = DMA_BIT_MASK(32);
|
||
|
g_dma_buff_va = (u8 *)dma_alloc_coherent(
|
||
|
&fts_data->input_dev->dev, DMA_BUFFER_LENGTH,
|
||
|
&g_dma_buff_pa, GFP_KERNEL);
|
||
|
|
||
|
if (!g_dma_buff_va)
|
||
|
FTS_ERROR("Allocate DMA I2C Buffer failed");
|
||
|
|
||
|
}
|
||
|
FTS_FUNC_EXIT();
|
||
|
}
|
||
|
|
||
|
static void fts_i2c_msg_dma_release(void)
|
||
|
{
|
||
|
FTS_FUNC_ENTER();
|
||
|
if (g_dma_buff_va) {
|
||
|
dma_free_coherent(NULL, DMA_BUFFER_LENGTH, g_dma_buff_va,
|
||
|
g_dma_buff_pa);
|
||
|
g_dma_buff_va = NULL;
|
||
|
g_dma_buff_pa = 0;
|
||
|
FTS_ERROR("[IIC]: Allocated DMA I2C Buffer release!!");
|
||
|
}
|
||
|
FTS_FUNC_EXIT();
|
||
|
}
|
||
|
#else
|
||
|
int fts_read(u8 *cmd, u32 cmdlen, u8 *data, u32 datalen)
|
||
|
{
|
||
|
int ret = 0;
|
||
|
int i = 0;
|
||
|
struct i2c_client *client = NULL;
|
||
|
struct i2c_msg msg_list[2];
|
||
|
struct i2c_msg *msg = NULL;
|
||
|
int msg_num = 0;
|
||
|
|
||
|
/* must have data when read */
|
||
|
if (!fts_data || !fts_data->client || !data || !datalen) {
|
||
|
FTS_ERROR("fts_data/client/data/datalen(%d) is invalid",
|
||
|
datalen);
|
||
|
return -EINVAL;
|
||
|
}
|
||
|
client = fts_data->client;
|
||
|
|
||
|
mutex_lock(&fts_data->bus_lock);
|
||
|
memset(&msg_list[0], 0, sizeof(struct i2c_msg));
|
||
|
memset(&msg_list[1], 0, sizeof(struct i2c_msg));
|
||
|
msg_list[0].addr = client->addr;
|
||
|
msg_list[0].flags = 0;
|
||
|
msg_list[0].len = cmdlen;
|
||
|
msg_list[0].buf = cmd;
|
||
|
msg_list[1].addr = client->addr;
|
||
|
msg_list[1].flags = I2C_M_RD;
|
||
|
msg_list[1].len = datalen;
|
||
|
msg_list[1].buf = data;
|
||
|
if (cmd && cmdlen) {
|
||
|
msg = &msg_list[0];
|
||
|
msg_num = 2;
|
||
|
} else {
|
||
|
msg = &msg_list[1];
|
||
|
msg_num = 1;
|
||
|
}
|
||
|
|
||
|
for (i = 0; i < I2C_RETRY_NUMBER; i++) {
|
||
|
ret = i2c_transfer(client->adapter, msg, msg_num);
|
||
|
if (ret < 0)
|
||
|
FTS_ERROR("i2c_transfer(read) fail,ret:%d", ret);
|
||
|
else
|
||
|
break;
|
||
|
|
||
|
}
|
||
|
|
||
|
mutex_unlock(&fts_data->bus_lock);
|
||
|
return ret;
|
||
|
}
|
||
|
|
||
|
int fts_write(u8 *writebuf, u32 writelen)
|
||
|
{
|
||
|
int ret = 0;
|
||
|
int i = 0;
|
||
|
struct i2c_client *client = NULL;
|
||
|
struct i2c_msg msgs;
|
||
|
|
||
|
if (!fts_data || !fts_data->client || !writebuf || !writelen) {
|
||
|
FTS_ERROR("fts_data/client/data/datalen(%d) is invalid",
|
||
|
writelen);
|
||
|
return -EINVAL;
|
||
|
}
|
||
|
client = fts_data->client;
|
||
|
|
||
|
mutex_lock(&fts_data->bus_lock);
|
||
|
memset(&msgs, 0, sizeof(struct i2c_msg));
|
||
|
msgs.addr = client->addr;
|
||
|
msgs.flags = 0;
|
||
|
msgs.len = writelen;
|
||
|
msgs.buf = writebuf;
|
||
|
for (i = 0; i < I2C_RETRY_NUMBER; i++) {
|
||
|
ret = i2c_transfer(client->adapter, &msgs, 1);
|
||
|
if (ret < 0)
|
||
|
FTS_ERROR("i2c_transfer(write) fail,reg:%d", ret);
|
||
|
else
|
||
|
break;
|
||
|
|
||
|
}
|
||
|
mutex_unlock(&fts_data->bus_lock);
|
||
|
return ret;
|
||
|
}
|
||
|
|
||
|
int fts_read_reg(u8 addr, u8 *value)
|
||
|
{
|
||
|
return fts_read(&addr, 1, value, 1);
|
||
|
}
|
||
|
|
||
|
int fts_write_reg(u8 addr, u8 value)
|
||
|
{
|
||
|
u8 buf[2] = {0};
|
||
|
|
||
|
buf[0] = addr;
|
||
|
buf[1] = value;
|
||
|
return fts_write(buf, sizeof(buf));
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
int fts_bus_init(struct fts_ts_data *ts_data)
|
||
|
{
|
||
|
FTS_FUNC_ENTER();
|
||
|
#ifdef CONFIG_MTK_I2C_EXTENSION
|
||
|
fts_i2c_msg_dma_alloct();
|
||
|
#endif
|
||
|
ts_data->bus_buf = NULL;
|
||
|
FTS_FUNC_EXIT();
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
int fts_bus_exit(struct fts_ts_data *ts_data)
|
||
|
{
|
||
|
FTS_FUNC_ENTER();
|
||
|
#ifdef CONFIG_MTK_I2C_EXTENSION
|
||
|
fts_i2c_msg_dma_release();
|
||
|
#endif
|
||
|
FTS_FUNC_EXIT();
|
||
|
return 0;
|
||
|
}
|