504 lines
12 KiB
C
Executable File
504 lines
12 KiB
C
Executable File
/* Synaptics Register Mapped Interface (RMI4) I2C Physical Layer Driver.
|
|
* Copyright (c) 2007-2012, Synaptics Incorporated
|
|
*
|
|
* This software is licensed under the terms of the GNU General Public
|
|
* License version 2, as published by the Free Software Foundation, and
|
|
* may be copied, distributed, and modified under those terms.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
*/
|
|
#include <linux/kernel.h>
|
|
#include <linux/module.h>
|
|
#include <asm/unaligned.h>
|
|
#include <asm/siginfo.h>
|
|
#include <mach/cpufreq.h>
|
|
#include <linux/slab.h>
|
|
#include <linux/i2c.h>
|
|
#include <linux/interrupt.h>
|
|
#include <linux/delay.h>
|
|
#include <linux/input.h>
|
|
#include <linux/gpio.h>
|
|
#include <linux/uaccess.h>
|
|
#include <linux/signal.h>
|
|
#include "synaptics_i2c_rmi.h"
|
|
|
|
#include "Multiverse/GMvSystem.h"
|
|
|
|
#define GEUST_PAGES_TO_SERVICE (0x60)
|
|
#define HARDCODE_PDT
|
|
|
|
static ssize_t rmi_guest_sysfs_query_base_addr_show(struct device *dev,
|
|
struct device_attribute *attr, char *buf);
|
|
|
|
static ssize_t rmi_guest_sysfs_control_base_addr_show(struct device *dev,
|
|
struct device_attribute *attr, char *buf);
|
|
|
|
static ssize_t rmi_guest_sysfs_data_base_addr_show(struct device *dev,
|
|
struct device_attribute *attr, char *buf);
|
|
|
|
static ssize_t rmi_guest_sysfs_command_base_addr_show(struct device *dev,
|
|
struct device_attribute *attr, char *buf);
|
|
|
|
struct rmi_guest_handle {
|
|
unsigned char intr_mask;
|
|
unsigned char intr_reg_num;
|
|
unsigned short query_base_addr;
|
|
unsigned short control_base_addr;
|
|
unsigned short data_base_addr;
|
|
unsigned short command_base_addr;
|
|
struct synaptics_rmi4_data *rmi4_data;
|
|
struct synaptics_rmi4_exp_fn_ptr *fn_ptr;
|
|
struct kobject *sysfs_dir;
|
|
};
|
|
|
|
static struct device_attribute attrs[] = {
|
|
__ATTR(query_base_addr, S_IRUGO,
|
|
rmi_guest_sysfs_query_base_addr_show,
|
|
synaptics_rmi4_store_error),
|
|
__ATTR(control_base_addr, S_IRUGO,
|
|
rmi_guest_sysfs_control_base_addr_show,
|
|
synaptics_rmi4_store_error),
|
|
__ATTR(data_base_addr, S_IRUGO,
|
|
rmi_guest_sysfs_data_base_addr_show,
|
|
synaptics_rmi4_store_error),
|
|
__ATTR(command_base_addr, S_IRUGO,
|
|
rmi_guest_sysfs_command_base_addr_show,
|
|
synaptics_rmi4_store_error),
|
|
};
|
|
|
|
static struct rmi_guest_handle *rmiguest;
|
|
|
|
static ssize_t rmi_guest_sysfs_query_base_addr_show(struct device *dev,
|
|
struct device_attribute *attr, char *buf)
|
|
{
|
|
return snprintf(buf, PAGE_SIZE, "0x%04x\n",
|
|
rmiguest->query_base_addr);
|
|
}
|
|
|
|
static ssize_t rmi_guest_sysfs_control_base_addr_show(struct device *dev,
|
|
struct device_attribute *attr, char *buf)
|
|
{
|
|
return snprintf(buf, PAGE_SIZE, "0x%04x\n",
|
|
rmiguest->control_base_addr);
|
|
}
|
|
|
|
static ssize_t rmi_guest_sysfs_data_base_addr_show(struct device *dev,
|
|
struct device_attribute *attr, char *buf)
|
|
{
|
|
return snprintf(buf, PAGE_SIZE, "0x%04x\n",
|
|
rmiguest->data_base_addr);
|
|
}
|
|
|
|
static ssize_t rmi_guest_sysfs_command_base_addr_show(struct device *dev,
|
|
struct device_attribute *attr, char *buf)
|
|
{
|
|
return snprintf(buf, PAGE_SIZE, "0x%04x\n",
|
|
rmiguest->command_base_addr);
|
|
}
|
|
|
|
static int rmi_guest_scan_pdt(void)
|
|
{
|
|
int retval;
|
|
unsigned short ii;
|
|
unsigned short addr;
|
|
unsigned char page;
|
|
unsigned char intr_count = 0;
|
|
unsigned char intr_offset;
|
|
bool fguest_found = false;
|
|
struct synaptics_rmi4_fn_desc rmi_fd;
|
|
struct synaptics_rmi4_data *rmi4_data = rmiguest->rmi4_data;
|
|
|
|
for (page = 0; page <= GEUST_PAGES_TO_SERVICE; page++) {
|
|
for (ii = PDT_START; ii > PDT_END; ii -= PDT_ENTRY_SIZE) {
|
|
ii |= (page << 8);
|
|
|
|
retval = rmiguest->fn_ptr->read(rmiguest->rmi4_data,
|
|
ii,
|
|
(unsigned char *)&rmi_fd,
|
|
sizeof(rmi_fd));
|
|
if (retval < 0)
|
|
return retval;
|
|
|
|
ii &= ~(MASK_8BIT << 8);
|
|
|
|
if (!rmi_fd.fn_number)
|
|
break;
|
|
|
|
if (rmi_fd.fn_number == SYNAPTICS_RMI4_F60) {
|
|
fguest_found = true;
|
|
goto fguest_found;
|
|
}
|
|
|
|
intr_count += (rmi_fd.intr_src_count & MASK_3BIT);
|
|
}
|
|
}
|
|
|
|
#ifndef HARDCODE_PDT
|
|
if (!fguest_found) {
|
|
tsp_debug_err(true, &rmiguest->rmi4_data->i2c_client->dev,
|
|
"%s: Failed to find F60\n",
|
|
__func__);
|
|
return -EINVAL;
|
|
}
|
|
#endif
|
|
|
|
fguest_found:
|
|
rmiguest->query_base_addr = rmi_fd.query_base_addr | (page << 8);
|
|
rmiguest->control_base_addr = rmi_fd.ctrl_base_addr | (page << 8);
|
|
rmiguest->data_base_addr = rmi_fd.data_base_addr | (page << 8);
|
|
rmiguest->command_base_addr = rmi_fd.cmd_base_addr | (page << 8);
|
|
|
|
#ifdef HARDCODE_PDT
|
|
rmiguest->query_base_addr = 0x0000;
|
|
rmiguest->control_base_addr = 0x0000;
|
|
rmiguest->data_base_addr = 0x6000;
|
|
rmiguest->command_base_addr = 0x0000;
|
|
rmi_fd.intr_src_count = 1;
|
|
#endif
|
|
|
|
rmiguest->intr_reg_num = (intr_count + 7) / 8;
|
|
if (rmiguest->intr_reg_num != 0)
|
|
rmiguest->intr_reg_num -= 1;
|
|
|
|
rmiguest->intr_mask = 0;
|
|
intr_offset = intr_count % 8;
|
|
for (ii = intr_offset;
|
|
ii < ((rmi_fd.intr_src_count & MASK_3BIT) +
|
|
intr_offset);
|
|
ii++) {
|
|
rmiguest->intr_mask |= 1 << ii;
|
|
}
|
|
|
|
rmi4_data->intr_mask[0] |= rmiguest->intr_mask;
|
|
|
|
addr = rmi4_data->f01_ctrl_base_addr + 1;
|
|
|
|
retval = rmiguest->fn_ptr->write(rmi4_data,
|
|
addr,
|
|
&(rmi4_data->intr_mask[0]),
|
|
sizeof(rmi4_data->intr_mask[0]));
|
|
if (retval < 0)
|
|
return retval;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void rmi_guest_attn(struct synaptics_rmi4_data *rmi4_data,
|
|
unsigned char intr_mask)
|
|
{
|
|
if (!rmiguest)
|
|
return;
|
|
|
|
if (rmiguest->intr_mask & intr_mask) {
|
|
#ifdef USE_GUEST_THREAD
|
|
GMvBraneIsr();
|
|
#endif
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
static int rmi_guest_reinit(struct synaptics_rmi4_data *rmi4_data)
|
|
{
|
|
int retval;
|
|
|
|
retval = rmi_guest_scan_pdt();
|
|
if (retval < 0)
|
|
tsp_debug_err(true, &rmi4_data->i2c_client->dev,
|
|
"%s: Failed to find guest functionality\n",
|
|
__func__);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int rmi_guest_init(struct synaptics_rmi4_data *rmi4_data)
|
|
{
|
|
int retval;
|
|
unsigned char attr_count;
|
|
|
|
rmiguest = kzalloc(sizeof(*rmiguest), GFP_KERNEL);
|
|
if (!rmiguest) {
|
|
tsp_debug_err(true, &rmi4_data->i2c_client->dev,
|
|
"%s: Failed to alloc mem for rmiguest\n",
|
|
__func__);
|
|
retval = -ENOMEM;
|
|
goto err_rmi_guest;
|
|
}
|
|
|
|
rmiguest->fn_ptr = kzalloc(sizeof(*(rmiguest->fn_ptr)), GFP_KERNEL);
|
|
if (!rmiguest->fn_ptr) {
|
|
tsp_debug_err(true, &rmi4_data->i2c_client->dev,
|
|
"%s: Failed to alloc mem for fn_ptr\n",
|
|
__func__);
|
|
retval = -ENOMEM;
|
|
goto err_fn_ptr;
|
|
}
|
|
|
|
rmiguest->fn_ptr->read = rmi4_data->i2c_read;
|
|
rmiguest->fn_ptr->write = rmi4_data->i2c_write;
|
|
rmiguest->fn_ptr->enable = rmi4_data->irq_enable;
|
|
rmiguest->rmi4_data = rmi4_data;
|
|
|
|
retval = rmi_guest_scan_pdt();
|
|
if (retval < 0) {
|
|
retval = 0;
|
|
goto err_scan_pdt;
|
|
}
|
|
|
|
rmiguest->sysfs_dir = kobject_create_and_add("guest",
|
|
&rmi4_data->input_dev->dev.kobj);
|
|
if (!rmiguest->sysfs_dir) {
|
|
tsp_debug_err(true, &rmi4_data->i2c_client->dev,
|
|
"%s: Failed to create sysfs directory\n",
|
|
__func__);
|
|
retval = -ENODEV;
|
|
goto err_sysfs_dir;
|
|
}
|
|
|
|
for (attr_count = 0; attr_count < ARRAY_SIZE(attrs); attr_count++) {
|
|
retval = sysfs_create_file(rmiguest->sysfs_dir,
|
|
&attrs[attr_count].attr);
|
|
if (retval < 0) {
|
|
tsp_debug_err(true, &rmi4_data->input_dev->dev,
|
|
"%s: Failed to create sysfs attributes\n",
|
|
__func__);
|
|
retval = -ENODEV;
|
|
goto err_sysfs_attrs;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
|
|
err_sysfs_attrs:
|
|
for (attr_count--; attr_count >= 0; attr_count--)
|
|
sysfs_remove_file(rmiguest->sysfs_dir, &attrs[attr_count].attr);
|
|
|
|
kobject_put(rmiguest->sysfs_dir);
|
|
|
|
err_sysfs_dir:
|
|
err_scan_pdt:
|
|
kfree(rmiguest->fn_ptr);
|
|
|
|
err_fn_ptr:
|
|
kfree(rmiguest);
|
|
rmiguest = NULL;
|
|
|
|
err_rmi_guest:
|
|
return retval;
|
|
}
|
|
|
|
static void rmi_guest_remove(struct synaptics_rmi4_data *rmi4_data)
|
|
{
|
|
unsigned char attr_count;
|
|
|
|
if (!rmiguest)
|
|
goto exit;
|
|
|
|
for (attr_count = 0; attr_count < ARRAY_SIZE(attrs); attr_count++)
|
|
sysfs_remove_file(rmiguest->sysfs_dir, &attrs[attr_count].attr);
|
|
|
|
kobject_put(rmiguest->sysfs_dir);
|
|
|
|
kfree(rmiguest->fn_ptr);
|
|
kfree(rmiguest);
|
|
rmiguest = NULL;
|
|
|
|
exit:
|
|
return;
|
|
}
|
|
|
|
int rmi_guest_module_register(struct synaptics_rmi4_data *rmi4_data)
|
|
{
|
|
int retval;
|
|
|
|
retval = synaptics_rmi4_new_function(RMI_GUEST,
|
|
rmi4_data,
|
|
rmi_guest_init,
|
|
rmi_guest_reinit,
|
|
rmi_guest_remove,
|
|
rmi_guest_attn);
|
|
|
|
return retval;
|
|
}
|
|
|
|
#ifdef USE_GUEST_THREAD
|
|
|
|
#define DEB_NONE_LEVEL 0
|
|
#define DBG_STRING_LEVEL 1
|
|
#define DBG_DATA_LEVEL 2
|
|
|
|
#define MV_DBG_RESULT_STR_LEN 160
|
|
#define MV_DEBUG_STR_LEN 8
|
|
|
|
#define MV_DEBUG_PRNT_SCREEN(_dest, _temp, _length, fmt, ...) \
|
|
({ \
|
|
snprintf(_temp, _length, fmt, ## __VA_ARGS__); \
|
|
strcat(_dest, _temp); \
|
|
})
|
|
|
|
enum MV_ADD_VAL {
|
|
MV_ADDR_HEADER_RX,
|
|
MV_ADDR_HEADER_TX,
|
|
MV_ADDR_PAYLOAD_RX,
|
|
MV_ADDR_PAYLOAD_TX,
|
|
MV_ADDR_ACK_LSB,
|
|
MV_ADDR_ACK_MSB,
|
|
MV_ADDR_VER_LSB,
|
|
MV_ADDR_VER_MSB,
|
|
};
|
|
|
|
enum MV_PKT_ID_TYPE {
|
|
MV_PKT_ID_TYPE_BRANE,
|
|
MV_PKT_ID_TYPE_BASIC,
|
|
MV_PKT_ID_TYPE_START_SVC,
|
|
MV_PKT_ID_TYPE_STOP_SVC,
|
|
MV_PKT_ID_TYPE_DATA_SVC,
|
|
MV_PKT_ID_TYPE_MAX,
|
|
};
|
|
|
|
enum MV_PKT_HEADER {
|
|
MV_HEADER_PKT_SYNC,
|
|
MV_HEADER_PKT_ID,
|
|
MV_HEADER_PKT_ARG,
|
|
MV_HEADER_PKT_MAX,
|
|
/*...*/
|
|
};
|
|
|
|
enum MV_PKT_ACK {
|
|
MV_ACK_PKT_ID,
|
|
MV_ACK_PKT_ARG,
|
|
MV_ACK_PKT_MAX,
|
|
};
|
|
|
|
static char * MV_ADDR_STR[8] = {
|
|
"HDR_RX ",
|
|
"HDR_TX ",
|
|
"PLD_RX ",
|
|
"PLD_TX ",
|
|
"ACK_LSB",
|
|
"ACK_MSB",
|
|
"VER_LSB",
|
|
"VER_MSB"
|
|
};
|
|
|
|
static char * MV_PKT_ID_STR[5] = {
|
|
"BRANE",
|
|
"BASIC",
|
|
"START",
|
|
"STOP",
|
|
"DATA",
|
|
};
|
|
|
|
static char * MV_BASIC_PKT_ARG_STR[5] = {
|
|
"WAIT. ",
|
|
"ACK. ",
|
|
"NACK. ",
|
|
"RESET. ",
|
|
"SPEC. ",
|
|
};
|
|
|
|
static void debug_multiverse_pkt(u8 mode, uint16 u16Addr, void *pvData, sint32 s32Size)
|
|
{
|
|
char buffer[MV_DBG_RESULT_STR_LEN] = {0,};
|
|
char buffer_temp[MV_DEBUG_STR_LEN] = {0,};
|
|
int i;
|
|
unsigned char addr_id = u16Addr & 0xF;
|
|
unsigned char id_type = MV_PKT_ID_TYPE_MAX;
|
|
|
|
if (rmiguest->rmi4_data->guest_pkt_dbg_level >= DBG_DATA_LEVEL) {
|
|
for (i = 0; i < s32Size; i++) {
|
|
MV_DEBUG_PRNT_SCREEN(buffer, buffer_temp, MV_DEBUG_STR_LEN, "0x%02X ", ((unsigned char *)pvData)[i]);
|
|
}
|
|
goto out;
|
|
}
|
|
|
|
switch (addr_id) {
|
|
case MV_ADDR_HEADER_RX:
|
|
case MV_ADDR_HEADER_TX:
|
|
for (i = 0; i < MV_HEADER_PKT_MAX; i++) {
|
|
switch (i) {
|
|
case MV_HEADER_PKT_ID:
|
|
id_type = ((unsigned char *)pvData)[MV_HEADER_PKT_ID];
|
|
MV_DEBUG_PRNT_SCREEN(buffer, buffer_temp, MV_DEBUG_STR_LEN, " %s", MV_PKT_ID_STR[id_type]);
|
|
break;
|
|
case MV_HEADER_PKT_ARG:
|
|
if (id_type == MV_PKT_ID_TYPE_BASIC)
|
|
MV_DEBUG_PRNT_SCREEN(buffer, buffer_temp, MV_DEBUG_STR_LEN, " %s", MV_BASIC_PKT_ARG_STR[((unsigned char *)pvData)[MV_HEADER_PKT_ARG]]);
|
|
else
|
|
MV_DEBUG_PRNT_SCREEN(buffer, buffer_temp, MV_DEBUG_STR_LEN, " [0x%02X]", ((unsigned char *)pvData)[MV_HEADER_PKT_ARG]);
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
break;
|
|
case MV_ADDR_ACK_LSB:
|
|
for (i = 0; i < MV_ACK_PKT_MAX; i++) {
|
|
switch (i) {
|
|
case MV_ACK_PKT_ID:
|
|
id_type = ((unsigned char *)pvData)[MV_HEADER_PKT_ID];
|
|
MV_DEBUG_PRNT_SCREEN(buffer, buffer_temp, MV_DEBUG_STR_LEN, " %s", MV_PKT_ID_STR[((unsigned char *)pvData)[MV_ACK_PKT_ID]]);
|
|
break;
|
|
case MV_ACK_PKT_ARG:
|
|
if (id_type == MV_PKT_ID_TYPE_BASIC)
|
|
MV_DEBUG_PRNT_SCREEN(buffer, buffer_temp, MV_DEBUG_STR_LEN, " %s", MV_BASIC_PKT_ARG_STR[((unsigned char *)pvData)[MV_ACK_PKT_ARG]]);
|
|
else
|
|
MV_DEBUG_PRNT_SCREEN(buffer, buffer_temp, MV_DEBUG_STR_LEN, " [0x%02X]", ((unsigned char *)pvData)[MV_ACK_PKT_ARG]);
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
break;
|
|
default:
|
|
for (i = 0; i < s32Size; i++) {
|
|
MV_DEBUG_PRNT_SCREEN(buffer, buffer_temp, MV_DEBUG_STR_LEN, "0x%02X ", ((unsigned char *)pvData)[i]);
|
|
}
|
|
break;
|
|
}
|
|
|
|
out:
|
|
tsp_debug_err(true, &rmiguest->rmi4_data->i2c_client->dev,
|
|
"[PKT:(%s)]: 0x%04X [%s][%ld]: %s\n", mode ? "W" : "R",
|
|
u16Addr, MV_ADDR_STR[u16Addr & 0xF], s32Size, buffer);
|
|
}
|
|
#endif
|
|
|
|
void GMvGtReadI2cRegister(uint16 u16Addr, void *pvData, sint32 s32Size)
|
|
{
|
|
if( rmiguest )
|
|
{
|
|
if( rmiguest->fn_ptr && rmiguest->rmi4_data )
|
|
{
|
|
if( rmiguest->fn_ptr->read ) {
|
|
rmiguest->fn_ptr->read( rmiguest->rmi4_data, u16Addr, pvData, s32Size );
|
|
#ifdef USE_GUEST_THREAD
|
|
if (rmiguest->rmi4_data->guest_pkt_dbg_level)
|
|
debug_multiverse_pkt(0 ,u16Addr, pvData, s32Size);
|
|
#endif
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void GMvGtWriteI2cRegister(uint16 u16Addr, void *pvData, sint32 s32Size)
|
|
{
|
|
if( rmiguest )
|
|
{
|
|
if( rmiguest->fn_ptr && rmiguest->rmi4_data )
|
|
{
|
|
if( rmiguest->fn_ptr->write ) {
|
|
rmiguest->fn_ptr->write( rmiguest->rmi4_data, u16Addr, pvData, s32Size );
|
|
#ifdef USE_GUEST_THREAD
|
|
if (rmiguest->rmi4_data->guest_pkt_dbg_level)
|
|
debug_multiverse_pkt(1 ,u16Addr, pvData, s32Size);
|
|
#endif
|
|
}
|
|
}
|
|
}
|
|
}
|