blob: 707a297a075ff4f01e72398fb94e1ba019fbe85e [file] [log] [blame]
/*
* Driver for MultiSensors
*
*/
#include <linux/types.h>
#include <linux/mutex.h>
#include <linux/device.h>
#include <linux/spi/spi.h>
#include <linux/slab.h>
#include <linux/sysfs.h>
#include <linux/module.h>
#include <linux/cdev.h>
#include <linux/fs.h>
#include <linux/miscdevice.h> /* For handling misc devices */
#include <asm/uaccess.h>
#include <linux/kthread.h>
#include <linux/delay.h>
#include <linux/gpio.h>
#include <linux/of.h>
#include <linux/of_gpio.h>
#include <linux/of_irq.h>
#include <linux/irq.h>
#include <linux/interrupt.h>
#include <linux/unistd.h>
#include <linux/power_supply.h>
#include <linux/mfd/samsung/rtc-s2mp.h>
#include <linux/wakelock.h>
#include <linux/proc_fs.h>
#include "subcpu_battery.h"
#include "MSensorsDrv.h"
#define DRV_NAME "MultiSensors"
#define DRV_NAME_CD "MultiSensors_CD"
#define DRV_NAME_CD_01 "MultiSensors_CD_01"
#define MINOR_COUNT (1)
/* #define CFG_SUBCPU_LOG */
#define INC_INDEX(a, r) \
do { \
if ((a) < ((r)-1)) \
(a)++; \
else \
(a)=0; \
} while(0)
struct WriteDataBuf {
unsigned char m_data[WRITE_DATA_SIZE];
unsigned char m_used;
struct list_head bqueue;
};
static spinlock_t slock;
struct mutex mlock;
static struct WriteDataBuf WriteDataBuf[WRITE_DATABUFF_SIZE];
static struct list_head wd_queue;
static struct Msensors_state *g_st=0;
static dev_t dev_id;
static struct class *Msensors_class;
static struct device *class_dev;
static struct Msensors_data Msensors_data_buff[MSENSORS_DATA_MAX];
static unsigned int dataBuffReadIndex;
static unsigned int dataBuffWriteIndex;
static unsigned int PacketDataNum=0;
static unsigned char HeaderData[HEADER_DATA_SIZE] = {
0x00, 0x00, 0x00, 0x1e, 0x21, 0x80, 0x00, 0x05 }; /* 2015/1/1 0:0:0 */
static unsigned char Flg_driver_ready = 0;
static unsigned char Flg_driver_probed = 0;
static unsigned char Flg_driver_shutdown = 0;
static unsigned char SubReadData[SUB_COM_DATA_SIZE_GETDATA];
static wait_queue_head_t wait_ioctl;
static volatile int ioctl_complete;
static wait_queue_head_t wait_rd;
static wait_queue_head_t wait_subint;
static volatile int sub_main_int_occur;
static volatile int data_pushed;
static int64_t soc_time;
struct Msensors_state *get_msensors_state(void)
{
return g_st;
}
static struct WriteDataBuf *allocWbBuf(void)
{
int i;
unsigned long flags;
for (i = 0; i < WRITE_DATABUFF_SIZE; i++) {
spin_lock_irqsave(&slock, flags);
if (WriteDataBuf[i].m_used == 0) {
WriteDataBuf[i].m_used = 1;
spin_unlock_irqrestore(&slock, flags);
return &WriteDataBuf[i];
}
spin_unlock_irqrestore(&slock, flags);
}
return NULL;
}
static void freeWbBuf(struct WriteDataBuf *wb)
{
wb->m_used = 0;
}
ssize_t Msensors_Spi_Send( struct Msensors_state *st, char* send_buf, char* recv_buf, size_t count )
{
struct spi_message msg;
struct spi_transfer xfer;
unsigned char tx_type;
int ret;
memset(&xfer, 0, sizeof(xfer));
xfer.len = count;
xfer.speed_hz = 1000 * 1000;
xfer.tx_buf = send_buf;
xfer.rx_buf = recv_buf;
xfer.bits_per_word = 8;
tx_type = (unsigned char)send_buf[0];
spi_message_init(&msg);
spi_message_add_tail(&xfer, &msg);
ret = spi_sync(st->sdev, &msg);
if (ret < 0)
pr_info("%s: ret = %d\n", __func__, ret);
return ret;
}
static int Msensors_Open(struct inode *inode, struct file *file)
{
struct Msensors_state *st;
st = container_of(inode->i_cdev, struct Msensors_state, cdev);
dev_dbg(&st->sdev->dev, "Msensors_Open\n");
file->private_data = st;
return 0;
}
static int Msensors_Close(struct inode *inode, struct file *file)
{
struct Msensors_state *st;
st = file->private_data;
dev_dbg(&st->sdev->dev, "Msensors_Close\n");
return 0;
}
static int64_t getTimestamp(void)
{
struct timespec ts;
get_monotonic_boottime(&ts);
return timespec_to_ns(&ts);
}
void Msensors_SetTimestamp(void)
{
soc_time = getTimestamp();
}
int Msensors_PushData(unsigned char* write_buff)
{
struct WriteDataBuf *wb;
unsigned long flags;
int counter = 0;
if (g_st->fw.status == MSENSORS_FW_UP_UPDATING)
return -EPROBE_DEFER;
if (Flg_driver_probed == 0 || Flg_driver_shutdown) {
dev_info(&g_st->sdev->dev, "write command type:%02x id:%02x ignored.\n"
,write_buff[1] , write_buff[2]);
return -EPROBE_DEFER;
}
retry:
wb = allocWbBuf();
if (wb) {
memcpy(wb->m_data, &write_buff[0], WRITE_DATA_SIZE);
spin_lock_irqsave(&slock, flags);
list_add_tail(&wb->bqueue, &wd_queue);
spin_unlock_irqrestore(&slock, flags);
data_pushed = 1;
wake_up(&wait_subint);
} else if (Flg_driver_ready) {
msleep(10);
counter++;
if (counter < 10)
goto retry;
}
return 0;
}
static void sub_HeaderInfoProc(void)
{
unsigned char write_buff[WRITE_DATA_SIZE];
memset(write_buff, SUB_COM_SEND_DUMMY, WRITE_DATA_SIZE);
/* charger or battery property changed */
if (HeaderData[2] & (1 << SUB_ALERT_BIT_POWER_CHG1)) {
write_buff[0] = SUB_COM_TYPE_READ;
write_buff[1] = SUB_COM_GETID_POWER_PROP1;
write_buff[2] = 0xff;
Msensors_PushData(&write_buff[0]);
}
if (HeaderData[2] & (1 << SUB_ALERT_BIT_POWER_CHG2)) {
write_buff[0] = SUB_COM_TYPE_READ;
write_buff[1] = SUB_COM_GETID_POWER_PROP2;
write_buff[2] = 0xff;
Msensors_PushData(&write_buff[0]);
}
}
#define STSOBJ_NUM 5
static uint32_t cnt_num[STSOBJ_NUM]; // number of samples
static uint32_t cnt_idx[STSOBJ_NUM];
static int64_t meas_tri;
static void timestamp_estimate_ts(int64_t measTri)
{
meas_tri = measTri & ~(0x3FF);
}
static void timestamp_count(uint8_t *recv, int packetnum)
{
int i, type;
for (i = 0; i < STSOBJ_NUM; i++) {
cnt_num[i] = 0;
cnt_idx[i] = 0;
}
for (i = 0; i < packetnum; i++) {
type = *recv;
recv += (SUB_COM_DATA_SIZE_PACKET + SUB_COM_ID_SIZE);
if (type == 0)
continue;
else if ((type & 0xf) < MSENSORS_TYPE_BHA)
++cnt_num[(type & 0xf) - 1];
}
}
static int64_t get_timestamp(int type)
{
int64_t t;
int u;
++cnt_idx[type];
/* the lower 10 bit is used for the index of decrease */
u = cnt_num[type] - cnt_idx[type];
if ( u < 0 ) u = 0; /* not occure but safe*/
t = meas_tri + u;
return t;
}
static struct wake_lock wlock;
static int SensorReadThread(void *p)
{
struct Msensors_state *st = g_st;
unsigned char type;
unsigned char send_type;
int sensor_num;
int sensor_type;
int cnt;
int recv_index;
int next_recv_size;
unsigned char* recv_buf = &st->spi.recv_buf[0];
int64_t event_time;
uint32_t elapsed_time = 0;
unsigned long flags;
struct WriteDataBuf *wb;
while (!kthread_should_stop()) {
next_recv_size = SUB_COM_TYPE_SIZE;
send_type = SUB_COM_TYPE_RES_NOMAL;
memset(&st->spi.send_buf[0], SUB_COM_SEND_DUMMY, SPI_DATA_MAX);
/* Get Type */
type = recv_buf[SUB_COM_HEAD_INDEX_TYPE];
/* HEADER Information */
#ifdef CFG_SUBCPU_LOG
if (type == SUB_COM_TYPE_BIT_HEAD || type == SUB_COM_TYPE_SUBCPU_LOG) {
#else
if (type == SUB_COM_TYPE_BIT_HEAD) {
#endif
/* Get Header Data */
memcpy(&HeaderData[0], &recv_buf[0], HEADER_DATA_SIZE);
/* Sub Header Infomation Proc */
sub_HeaderInfoProc();
/* Get sensors data num */
sensor_num = recv_buf[SUB_COM_HEAD_INDEX_PACKET];
/* Calc sensors data size */
#ifdef CFG_SUBCPU_LOG
if (type == SUB_COM_TYPE_SUBCPU_LOG)
next_recv_size += sensor_num * 65;
else
#endif
next_recv_size += sensor_num * ( SUB_COM_DATA_SIZE_PACKET + SUB_COM_ID_SIZE );
PacketDataNum = sensor_num;
if (st->spi.pre_send_type==SUB_COM_TYPE_READ) {
next_recv_size += SUB_COM_DATA_SIZE_GETDATA + SUB_COM_ID_SIZE;
if(PacketDataNum == 0)
send_type = SUB_COM_TYPE_GETDATA;
else
send_type = SUB_COM_TYPE_SENSOR_GETDATA;
} else if(PacketDataNum) {
send_type = SUB_COM_TYPE_SENSOR;
}
} else if (type == SUB_COM_TYPE_FWUP_READY) { /* 0x81 */
pr_info("msensors fw update\n");
st->fw.status = MSENSORS_FW_UP_READY;
wake_up(&st->fw.wait);
wait_event(st->fw.wait,
st->fw.status == MSENSORS_FW_UP_WAIT_RESET);
st->spi.pre_send_type = SUB_COM_TYPE_RES_NOMAL;
} else {
/* Data */
recv_index = SUB_COM_DATA_INDEX_ID;
if ((type == SUB_COM_TYPE_GETDATA ) || /* Get Data Only */
(type == SUB_COM_TYPE_SENSOR_GETDATA )) { /* Get Data and Sensor Data */
if (recv_buf[recv_index] == SUB_COM_GETID_SUB_CPU_VER) {
Msensors_set_fw_version(st, &recv_buf[recv_index + 1]);
#ifdef CONFIG_SUBCPU_BATTERY
} else if (recv_buf[recv_index] == SUB_COM_GETID_POWER_PROP1) {
subcpu_battery_update_status1(recv_buf);
} else if (recv_buf[recv_index] == SUB_COM_GETID_POWER_PROP2) {
subcpu_battery_update_status2(recv_buf);
#endif
} else if (recv_buf[recv_index] < SUB_COM_GETID_NUM) {
memcpy(SubReadData, &recv_buf[recv_index + 1], SUB_COM_DATA_SIZE_GETDATA);
ioctl_complete = 1;
wake_up_interruptible(&wait_ioctl);
}
/* Get Data Proc */
recv_index += SUB_COM_DATA_SIZE_GETDATA + SUB_COM_ID_SIZE;
}
if ((type == SUB_COM_TYPE_SENSOR) || /* Sensor Data Only */
(type == SUB_COM_TYPE_SENSOR_GETDATA)) { /* Get Data and Sensor Data */
event_time = soc_time - elapsed_time * 1000000LL;
/* Sensor Data Proc */
if (recv_buf[recv_index] == MSENSORS_TYPE_TIMESTAMP)
wake_lock_timeout(&wlock, HZ/5);
#ifdef CFG_SUBCPU_LOG
if (recv_buf[recv_index] == MSENSORS_TYPE_SUBCPU_LOG) { // subcpu log
for (cnt= 0; cnt < PacketDataNum; cnt++) {
if (recv_buf[recv_index] != MSENSORS_TYPE_SUBCPU_LOG) {
pr_info("subcpu type invalid\n");
break;
}
recv_index++;
printk(KERN_DEBUG "[subcpu] %s", &recv_buf[recv_index]);
recv_index += 64;
}
} else {
#endif
timestamp_count(&recv_buf[recv_index], PacketDataNum);
timestamp_estimate_ts(getTimestamp());
for (cnt= 0; cnt < PacketDataNum; cnt++) {
sensor_type = recv_buf[recv_index++];
if (sensor_type == MSENSORS_TYPE_TIMESTAMP) {
elapsed_time = recv_buf[recv_index+3]<<24 | recv_buf[recv_index+2]<<16 |
recv_buf[recv_index+1]<<8 | recv_buf[recv_index];
event_time = soc_time - elapsed_time * 1000000LL;
} else {
if ((sensor_type & 0xf) >= MSENSORS_TYPE_LIGHT) {
Msensors_data_buff[dataBuffWriteIndex].timestamp = event_time;
} else {
Msensors_data_buff[dataBuffWriteIndex].timestamp =
get_timestamp((sensor_type & 0xf)-1);
}
Msensors_data_buff[dataBuffWriteIndex].sensor_type = sensor_type;
memcpy(&Msensors_data_buff[dataBuffWriteIndex].sensor_value[0],
&recv_buf[recv_index], 6);
spin_lock_irqsave(&slock, flags);
INC_INDEX(dataBuffWriteIndex, MSENSORS_DATA_MAX);
if (dataBuffWriteIndex == dataBuffReadIndex)
INC_INDEX(dataBuffReadIndex, MSENSORS_DATA_MAX);
spin_unlock_irqrestore(&slock, flags);
}
recv_index += SUB_COM_DATA_SIZE_PACKET;
}
wake_up_interruptible_sync(&wait_rd);
#ifdef CFG_SUBCPU_LOG
}
#endif
}
}
wb = NULL;
if (send_type == SUB_COM_TYPE_RES_NOMAL) {
next_recv_size += SUB_COM_HEAD_SIZE_SETDATA + SUB_COM_ID_SIZE;
retry_check_wq:
if (!list_empty(&wd_queue)) {
wb = list_first_entry(&wd_queue, struct WriteDataBuf, bqueue);
send_type = wb->m_data[0];
memcpy(&st->spi.send_buf[0], wb->m_data, WRITE_DATA_SIZE);
}
}
/* Next SPI Send Proc */
st->spi.send_buf[SUB_COM_HEAD_INDEX_TYPE] = send_type;
st->spi.pre_send_type = send_type;
if (send_type == SUB_COM_TYPE_RES_NOMAL) {
wait_event(wait_subint, sub_main_int_occur | Flg_driver_shutdown | data_pushed);
if (data_pushed) {
data_pushed = 0;
goto retry_check_wq;
}
} else {
if (sub_main_int_occur == 0)
gpio_set_value(g_st->main_sub_int, 1);
wait_event(wait_subint, sub_main_int_occur | Flg_driver_shutdown);
}
sub_main_int_occur = 0;
data_pushed = 0;
gpio_set_value(g_st->main_sub_int, 0);
if (Flg_driver_shutdown)
break;
while (!Flg_driver_ready)
msleep(10);
if (st->spi.send_buf[0] == SUB_COM_TYPE_WRITE &&
st->spi.send_buf[1] == SUB_COM_SETID_MAIN_STATUS &&
st->spi.send_buf[2] == 0x2) /* shutdown */
Flg_driver_shutdown = 1;
Msensors_SetTimestamp();
Msensors_Spi_Send(st, &st->spi.send_buf[0], &st->spi.recv_buf[0], next_recv_size);
if (wb) {
spin_lock_irqsave(&slock, flags);
list_del(&wb->bqueue);
spin_unlock_irqrestore(&slock, flags);
freeWbBuf(wb);
}
}
return 0;
}
void spi_send_wrapper_for_fwup(uint8_t *sendbuf, uint8_t *recvbuf, size_t count)
{
if (sub_main_int_occur == 0)
gpio_set_value(g_st->main_sub_int, 1);
wait_event(wait_subint, sub_main_int_occur);
sub_main_int_occur = 0;
gpio_set_value(g_st->main_sub_int, 0);
Msensors_Spi_Send(g_st, sendbuf, recvbuf, count);
}
int cyttsp5_get_palm_on(void);
#define MSENSORS_HANDLE_WRIST_TILT (0x09) /* Sensor Type Wrist Tilt */
static ssize_t Msensors_Read( struct file* file, char* buf, size_t count, loff_t* offset )
{
int cnt=0;
int buf_index=0;
int size = sizeof(struct Msensors_data);
int ret;
int data_num = count / sizeof(struct Msensors_data);
struct Msensors_state *st;
st = file->private_data;
retrywait:
/* Wait until dataBuffWriteIndex moved */
ret = wait_event_interruptible(wait_rd, dataBuffWriteIndex != dataBuffReadIndex);
if (ret < 0) {
return ret;
}
while (cnt < data_num) {
if (dataBuffWriteIndex == dataBuffReadIndex) {
if (cnt == 0)
goto retrywait;
else
break; /* no data */
}
if (cyttsp5_get_palm_on()) {
if (Msensors_data_buff[dataBuffReadIndex].sensor_type == MSENSORS_HANDLE_WRIST_TILT) {
INC_INDEX(dataBuffReadIndex, MSENSORS_DATA_MAX);
continue;
}
}
if (copy_to_user(buf+buf_index, &Msensors_data_buff[dataBuffReadIndex], size))
return -EFAULT;
buf_index += size;
cnt++;
INC_INDEX(dataBuffReadIndex, MSENSORS_DATA_MAX);
}
return buf_index;
}
void auo_h120bln017_notify_seglcd(int seglcd_on);
void auo_h120bln017_notify_2layer(int mode_2layer);
static ssize_t Msensors_Write(struct file* file, const char* buf, size_t count,
loff_t* offset)
{
struct Msensors_state *st;
int ret;
unsigned char write_buff[WRITE_DATA_SIZE];
st = file->private_data;
dev_dbg(&st->sdev->dev, "Msensors_Write Start count=%zu\n", count);
ret = copy_from_user(&write_buff[1], buf, count);
if (!ret) {
#ifdef CONFIG_BACKLIGHT_SUBCPU
if (write_buff[1] == SUB_COM_SETID_SEG_CMD) {
if (write_buff[2] == 0) { // SegLCD On/Off Control
auo_h120bln017_notify_seglcd((write_buff[3] & 0x80) ? 1 : 0);
} else if (write_buff[2] == 9) { // NotifyOnOff2layer
auo_h120bln017_notify_2layer(write_buff[3]);
goto finish;
}
}
#endif
write_buff[0] = SUB_COM_TYPE_WRITE; //0xA1
Msensors_PushData(&write_buff[0]);
}
finish:
return ret;
}
static int sub_read_command(uint8_t command)
{
int ret;
unsigned char write_buff[WRITE_DATA_SIZE];
write_buff[0] = SUB_COM_TYPE_READ;
write_buff[1] = command;
write_buff[2] = 0xff;
mutex_lock(&mlock);
ioctl_complete = 0;
Msensors_PushData(&write_buff[0]);
ret = wait_event_interruptible(wait_ioctl, ioctl_complete == 1);
mutex_unlock(&mlock);
return ret;
}
static void get_subcpu_version(uint8_t *buf)
{
buf[0] = g_st->fw.maj_ver;
buf[1] = g_st->fw.min_ver;
buf[2] = g_st->fw.revision;
}
static long Msensors_Ioctl(struct file *file, unsigned int cmd, unsigned long arg)
{
int ret;
uint8_t buf[3];
/* this function must be reentrant */
pr_info("%s: cmd %x\n", __func__, cmd);
if (cmd == IOC_GET_VERSION) {
get_subcpu_version(buf);
ret = copy_to_user((void __user *)arg, buf, 3);
} else if (cmd == IOC_ACCEL_ADJ) {
/* accelerometer factory adjustment */
ret = sub_read_command(SUB_COM_GETID_ACC_ADJ);
if (ret >= 0)
ret = copy_to_user((void __user *)arg, SubReadData, 3);
} else if (cmd == IOC_POWER_WARN) {
ret = sub_read_command(SUB_COM_GETID_POWER_WARN);
if (ret >= 0)
ret = copy_to_user((void __user *)arg, SubReadData, 2);
} else if (cmd == IOC_HEIGHT_CORR) {
ret = sub_read_command(SUB_COM_GETID_HEIGHT_CORR);
if (ret >= 0)
ret = copy_to_user((void __user *)arg, SubReadData, 4);
} else if (cmd == IOC_PRESSURE_CORR) {
ret = sub_read_command(SUB_COM_GETID_PRESSURE_CORR);
if (ret >= 0)
ret = copy_to_user((void __user *)arg, SubReadData, 4);
} else if (cmd == IOC_STEP_TODAY) {
ret = sub_read_command(SUB_COM_GETID_STEP_TODAY);
if (ret >= 0)
ret = copy_to_user((void __user *)arg, SubReadData, 4);
} else if (cmd == IOC_STEP_YESTERDAY) {
ret = sub_read_command(SUB_COM_GETID_STEP_YESTERDAY);
if (ret >= 0)
ret = copy_to_user((void __user *)arg, SubReadData, 4);
} else if (cmd == IOC_BATTERY_LOG_START) {
ret = sub_read_command(SUB_COM_GETID_BATTERY_LOG_START);
if (ret >= 0)
ret = copy_to_user((void __user *)arg, SubReadData, 5);
} else if (cmd == IOC_BATTERY_LOG_GET_DATA) {
ret = sub_read_command(SUB_COM_GETID_BATTERY_LOG_GET_DATA);
if (ret >= 0)
ret = copy_to_user((void __user *)arg, SubReadData, 6);
} else {
ret = -1;
pr_info("%s: unknown command %x\n", __func__, cmd);
}
return ret;
}
#ifdef CFG_SUBCPU_LOG
static void get_subcpu_log(void)
{
unsigned char write_buff[WRITE_DATA_SIZE];
write_buff[0] = SUB_COM_TYPE_WRITE;
write_buff[1] = SUB_COM_REQUEST_SUBCPU_LOG;
write_buff[2] = 0xff;
Msensors_PushData(&write_buff[0]);
}
#endif
static ssize_t read_i2c_status_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
sub_read_command(SUB_COM_GETID_I2C_STATUS);
return sprintf(buf, "i2c_status: %d\n", SubReadData[0]);
}
static ssize_t read_acc_offset_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
int16_t *offset;
sub_read_command(SUB_COM_GETID_ACC_READ_OFFSET);
offset = (int16_t *)SubReadData;
return sprintf(buf, "acc_offset: %d,%d,%d\n",
offset[0], offset[1], offset[2]);
}
static ssize_t read_acc_gain_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
uint16_t *gain;
sub_read_command(SUB_COM_GETID_ACC_READ_GAIN);
gain = (int16_t *)SubReadData;
return sprintf(buf, "acc_gain: %d,%d,%d\n",
gain[0], gain[1], gain[2]);
}
static struct device_attribute attributes[] = {
__ATTR(i2c_status, S_IRUGO, read_i2c_status_show, NULL),
__ATTR(acc_offset, S_IRUGO, read_acc_offset_show, NULL),
__ATTR(acc_gain, S_IRUGO, read_acc_gain_show, NULL),
};
static int add_sysfs_interfaces(struct device *dev)
{
int i;
for (i = 0; i < ARRAY_SIZE(attributes); i++)
if (device_create_file(dev, attributes + i))
goto undo;
return 0;
undo:
for (i--; i >= 0; i--)
device_remove_file(dev, attributes + i);
dev_err(dev, "%s: failed to create sysfs interface\n", __func__);
return -ENODEV;
}
static struct file_operations Msensors_fops = {
.owner = THIS_MODULE,
.open = Msensors_Open,
.release = Msensors_Close,
.read = Msensors_Read,
.write = Msensors_Write,
.unlocked_ioctl = Msensors_Ioctl,
.compat_ioctl = Msensors_Ioctl,
};
static void Msensors_init(struct Msensors_state *st)
{
memset(&st->spi.send_buf[0], SUB_COM_SEND_DUMMY, SPI_DATA_MAX);
st->spi.send_buf[0] = SUB_COM_TYPE_WRITE;
st->spi.send_buf[1] = SUB_COM_SETID_MAIN_STATUS;
st->spi.send_buf[2] = 0x0; /* Nomal */
st->spi.pre_send_type = st->spi.send_buf[0];
sub_main_int_occur = 0;
gpio_set_value(g_st->main_sub_int, 1);
wait_event(wait_subint, sub_main_int_occur);
sub_main_int_occur = 0;
gpio_set_value(g_st->main_sub_int, 0);
Msensors_Spi_Send(st, &st->spi.send_buf[0], &st->spi.recv_buf[0], WRITE_DATA_SIZE);
}
static irqreturn_t sub_main_int_wake_isr(int irq, void *dev)
{
sub_main_int_occur = 1;
wake_up(&wait_subint);
return IRQ_HANDLED;
}
static int subcpu_proc_show(struct seq_file *m, void *v)
{
uint16_t *p = (uint16_t *)SubReadData;
sub_read_command(SUB_COM_GETID_FG_VER);
seq_printf(m, "subcpu %02x.%02x.%02x\n",
g_st->fw.maj_ver, g_st->fw.min_ver, g_st->fw.revision);
seq_printf(m, "subcpu reset cause %02x\n", g_st->fw.subcpu_reset_cause);
seq_printf(m, "fg %04x, par %04x\n", p[0], p[1]);
return 0;
}
static int subcpu_proc_open(struct inode *inode, struct file *file)
{
return single_open(file, subcpu_proc_show, PDE_DATA(inode));
}
static const struct file_operations subcpu_proc_fops = {
.owner = THIS_MODULE,
.open = subcpu_proc_open,
.read = seq_read,
.llseek = seq_lseek,
.release = single_release,
};
static int Msensors_probe(struct spi_device *spi)
{
int ret, err;
int rc, irq;
struct Msensors_state *st;
dev_dbg(&spi->dev, "Msensors_probe Start\n");
init_waitqueue_head(&wait_rd);
init_waitqueue_head(&wait_subint);
init_waitqueue_head(&wait_ioctl);
spin_lock_init(&slock);
mutex_init(&mlock);
INIT_LIST_HEAD(&wd_queue);
wake_lock_init(&wlock, WAKE_LOCK_SUSPEND, "multisensors");
dataBuffReadIndex = 0;
dataBuffWriteIndex = 0;
memset(&HeaderData[0], 0x00, HEADER_DATA_SIZE);
st = kzalloc(sizeof *st, GFP_KERNEL);
if (st == NULL) {
ret = -ENOMEM;
goto error_ret;
}
err = alloc_chrdev_region(&dev_id,0,MINOR_COUNT,DRV_NAME_CD);
if (err < 0) {
dev_err(&spi->dev, "Msensors_probe alloc_chrdev_region fail\n");
ret = err;
goto error_free_dev;
}
cdev_init(&st->cdev,&Msensors_fops);
st->cdev.owner = THIS_MODULE;
st->cdev.ops = &Msensors_fops;
err = cdev_add(&st->cdev, dev_id, MINOR_COUNT);
if (err < 0) {
dev_err(&spi->dev, "Msensors_probe cdev_add fail\n");
ret = err;
goto error_free_dev;
}
Msensors_class = class_create(THIS_MODULE, DRV_NAME_CD);
class_dev = device_create(Msensors_class, &spi->dev, dev_id, NULL, DRV_NAME_CD_01);
st->sdev = spi_dev_get(spi);
spi_set_drvdata(spi, st);
spi->max_speed_hz = 1000 * 1000;
spi->mode = SPI_MODE_1; /* CPHA=1 CPOL=0 */
spi->bits_per_word = 8;
spi_setup(spi);
st->sub_main_int = of_get_named_gpio(spi->dev.of_node, "nsw,sub_main_int", 0);
pr_info("%s: sub_main_int = %d\n", __func__, st->sub_main_int);
st->main_sub_int = of_get_named_gpio(spi->dev.of_node, "nsw,main_sub_int", 0);
pr_info("%s: main_sub_int = %d\n", __func__, st->main_sub_int);
g_st = st;
rc = gpio_request(st->sub_main_int, "sub_main_int_gpio");
if (unlikely(rc)) {
return rc;
}
gpio_direction_input(st->sub_main_int);
irq = gpio_to_irq(st->sub_main_int);
ret = request_irq(irq, sub_main_int_wake_isr, IRQF_TRIGGER_RISING, "sub_main_int_wake", NULL);
if (ret) {
gpio_free(st->sub_main_int);
return ret;
}
rc = gpio_request_one(st->main_sub_int, GPIOF_OUT_INIT_LOW, "main_sub_int_gpio");
if (unlikely(rc)) {
return rc;
}
add_sysfs_interfaces(&spi->dev);
enable_irq_wake(irq);
Flg_driver_ready = 1;
Flg_driver_probed = 1;
Msensors_init(st);
st->spi.pre_send_type = 0;
st->spi.sensor_read_thread = kthread_run(SensorReadThread, st,DRV_NAME);
if (IS_ERR(st->spi.sensor_read_thread)) {
ret = PTR_ERR(st->spi.sensor_read_thread);
goto error_free_dev;
}
msensors_fw_up_init(st);
#ifdef CFG_SUBCPU_LOG
get_subcpu_log();
#endif
proc_create_data("subcpu", S_IRUGO, NULL, &subcpu_proc_fops, NULL);
dev_dbg(&spi->dev, "Msensors_probe End\n");
return 0;
error_free_dev:
kfree(st);
error_ret:
return ret;
}
static void Msensors_shutdown(struct spi_device *spi)
{
unsigned char write_buff[WRITE_DATA_SIZE];
int count = 0;
memset(write_buff, SUB_COM_SEND_DUMMY, WRITE_DATA_SIZE);
write_buff[0] = SUB_COM_TYPE_WRITE;
write_buff[1] = SUB_COM_SETID_MAIN_STATUS;
write_buff[2] = 0x3; /* prepare shutdown */
Msensors_PushData(&write_buff[0]);
while (!list_empty(&wd_queue) && count < 30) {
msleep(10);
count++;
}
count = 0;
write_buff[2] = 0x2; /* shutdown */
Msensors_PushData(&write_buff[0]);
while (!list_empty(&wd_queue) && count < 30) {
msleep(10);
count++;
}
Flg_driver_shutdown = 1;
}
static int Msensors_suspend(struct device *dev)
{
unsigned char write_buff[WRITE_DATA_SIZE];
int count = 0;
memset(write_buff, SUB_COM_SEND_DUMMY, WRITE_DATA_SIZE);
write_buff[0] = SUB_COM_TYPE_WRITE;
write_buff[1] = SUB_COM_SETID_MAIN_STATUS;
write_buff[2] = 0x1; /* suspend */
Msensors_PushData(&write_buff[0]);
while (!list_empty(&wd_queue) && count < 30) {
msleep(10);
count++;
}
Flg_driver_ready = 0;
return 0;
}
static void Msensors_complete(struct device *dev)
{
unsigned char write_buff[WRITE_DATA_SIZE];
Flg_driver_ready = 1;
memset(write_buff, SUB_COM_SEND_DUMMY, WRITE_DATA_SIZE);
write_buff[0] = SUB_COM_TYPE_WRITE;
write_buff[1] = SUB_COM_SETID_MAIN_STATUS;
write_buff[2] = 0x0; /* Nomal */
Msensors_PushData(&write_buff[0]);
}
static int Msensors_runtime_suspend(struct device *dev)
{
return 0;
}
static int Msensors_runtime_resume(struct device *dev)
{
return 0;
}
static const struct dev_pm_ops Msensors_pm = {
.suspend = Msensors_suspend,
.complete = Msensors_complete,
SET_RUNTIME_PM_OPS(Msensors_runtime_suspend,
Msensors_runtime_resume, NULL)
};
static struct of_device_id Msensors_match_table[] = {
{ .compatible = "MultiSensors,24",},
{ },
};
static struct spi_driver Msensors_driver = {
.driver = {
.name = DRV_NAME,
.owner = THIS_MODULE,
.pm = &Msensors_pm,
.of_match_table = Msensors_match_table,
},
.probe = Msensors_probe,
.shutdown = Msensors_shutdown,
};
module_spi_driver(Msensors_driver);
void SUB_VibratorSet(int timeout)
{
unsigned char write_buff[WRITE_DATA_SIZE];
memset(write_buff, SUB_COM_SEND_DUMMY, WRITE_DATA_SIZE);
write_buff[0] = SUB_COM_TYPE_WRITE;
write_buff[1] = SUB_COM_SETID_VIB_SET;
write_buff[2] = (timeout >> 0) & 0xFF; /* ms */
write_buff[3] = (timeout >> 8) & 0xFF; /* ms */
Msensors_PushData(&write_buff[0]);
}
int SUBCPU_rtc_read_time(uint8_t *data)
{
uint8_t *p;
uint32_t version;
version = g_st->fw.maj_ver << 16 | g_st->fw.min_ver << 8 | g_st->fw.revision;
if (version >= 0x150a00) {
sub_read_command(SUB_COM_GETID_RTC_DATE_TIME);
p = SubReadData;
} else {
p = &HeaderData[3];
}
data[RTC_YEAR] = (uint8_t)(p[0] >> 1);
data[RTC_MONTH] = (uint8_t)(((p[0] & 0x01) << 3) |
((p[1] & 0xE0) >> 5));
data[RTC_DATE] = (uint8_t)(p[1] & 0x1F);
data[RTC_WEEKDAY] = BIT((uint8_t)(p[2] >> 5));
data[RTC_HOUR] = (uint8_t)(p[2] & 0x1F);
data[RTC_MIN] = p[3];
data[RTC_SEC] = p[4];
return 0;
}
void SUBCPU_send_lowtemp_burnoff_enable(void)
{
unsigned char write_buff[WRITE_DATA_SIZE];
memset(write_buff, SUB_COM_SEND_DUMMY, WRITE_DATA_SIZE);
write_buff[0] = SUB_COM_TYPE_WRITE;
write_buff[1] = SUB_COM_SETID_LOWTEMP_BURNOFF;
write_buff[2] = 1;
Msensors_PushData(&write_buff[0]);
}
#ifdef CONFIG_BACKLIGHT_SUBCPU
static int last_brightness = -1;
int SUB_LCDBrightnessSet(unsigned char LCDBrightness)
{
unsigned char write_buff[WRITE_DATA_SIZE];
if (last_brightness == LCDBrightness)
return 0;
last_brightness = LCDBrightness;
memset(write_buff, SUB_COM_SEND_DUMMY, WRITE_DATA_SIZE);
write_buff[0] = SUB_COM_TYPE_WRITE;
write_buff[1] = SUB_COM_SETID_LCD_SET;
write_buff[2] = LCDBrightness; /* LCD brightness */
return Msensors_PushData(&write_buff[0]);
}
#endif
MODULE_AUTHOR("Casio");
MODULE_DESCRIPTION("MultiSensors driver");
MODULE_LICENSE("GPL v2");
MODULE_ALIAS("spi:" DRV_NAME);