1771 lines
50 KiB
C
1771 lines
50 KiB
C
/*
|
|
* m1120.c - Linux kernel modules for hall switch
|
|
*
|
|
* Copyright (C) 2013 Seunghwan Park <seunghwan.park@magnachip.com>
|
|
* Copyright (C) 2014 MagnaChip Semiconductor.
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation; either version 2 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* 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.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program; if not, write to the Free Software
|
|
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
|
*/
|
|
|
|
#include <linux/delay.h>
|
|
#include <linux/errno.h>
|
|
#include <linux/i2c.h>
|
|
#include <linux/init.h>
|
|
#include <linux/input.h>
|
|
#include <linux/kernel.h>
|
|
#include <linux/module.h>
|
|
#include <linux/mutex.h>
|
|
#include <linux/types.h>
|
|
#include <linux/slab.h>
|
|
#include <linux/workqueue.h>
|
|
#include <linux/miscdevice.h>
|
|
#include <asm/uaccess.h>
|
|
#include <linux/gpio.h>
|
|
#include <linux/irq.h>
|
|
#include <linux/interrupt.h>
|
|
#include <linux/fs.h>
|
|
#include <linux/uaccess.h>
|
|
#include "m1120.h"
|
|
|
|
///////////
|
|
//
|
|
// struct
|
|
/*
|
|
typedef sturct {
|
|
int type;
|
|
int min;
|
|
int max;
|
|
} m1120_sts_cfg_t;
|
|
stscfg
|
|
stscfg_init
|
|
*/
|
|
|
|
/* ********************************************************* */
|
|
/* customer config */
|
|
/* ********************************************************* */
|
|
//#define M1120_DBG_ENABLE // for debugging
|
|
#define M1120_DETECTION_MODE M1120_DETECTION_MODE_INTERRUPT // M1120_DETECTION_MODE_POLLING
|
|
#define M1120_INTERRUPT_TYPE M1120_VAL_INTSRS_INTTYPE_BESIDE
|
|
#define M1120_SENSITIVITY_TYPE M1120_VAL_INTSRS_SRS_10BIT_0_017mT
|
|
#define M1120_PERSISTENCE_COUNT M1120_VAL_PERSINT_COUNT(5)
|
|
#define M1120_OPERATION_FREQUENCY M1120_VAL_OPF_FREQ_10HZ
|
|
#define M1120_OPERATION_RESOLUTION M1120_VAL_OPF_BIT_10
|
|
#define M1120_DETECT_RANGE_HIGH (60)
|
|
#define M1120_DETECT_RANGE_LOW (50)
|
|
#define M1120_RESULT_STATUS_NUM (3)
|
|
#define M1120_RESULT_STATUS_A (0x01) // result status A close register(0)
|
|
#define M1120_RESULT_STATUS_B (0x02) // result status B stand
|
|
#define M1120_RESULT_STATUS_C (0x03) // result status C lift
|
|
#define M1120_RESULT_STATUS_D (0x04) // result status D camera1
|
|
#define M1120_RESULT_STATUS_E (0x05) // result status E open1
|
|
#define M1120_RESULT_STATUS_F (0x06) // result status F camera2
|
|
#define M1120_RESULT_STATUS_G (0x07) // result status G open2
|
|
#define M1120_RESULT_STATUS_UNKNOWN (0xFF) // result status Unknown
|
|
#define M1120_EVENT_TYPE EV_KEY //EV_ABS // EV_KEY
|
|
#define M1120_EVENT_CODE ABS_X // KEY_F1
|
|
#define M1120_EVENT_DATA_CAPABILITY_MIN (-32768)
|
|
#define M1120_EVENT_DATA_CAPABILITY_MAX (32767)
|
|
|
|
|
|
/* ********************************************************* */
|
|
/* debug macro */
|
|
/* ********************************************************* */
|
|
#ifdef M1120_DBG_ENABLE
|
|
#define dbg(fmt, args...) printk("[M1120-DBG] %s(L%04d) : " fmt "\n", __func__, __LINE__, ##args)
|
|
#define dbgn(fmt, args...) printk(fmt, ##args)
|
|
#else
|
|
#define dbg(fmt, args...)
|
|
#define dbgn(fmt, args...)
|
|
#endif // M1120_DBG_ENABLE
|
|
#define dbg_func_in() dbg("[M1120-DBG-F.IN] %s", __func__)
|
|
#define dbg_func_out() dbg("[M1120-DBG-F.OUT] %s", __func__)
|
|
#define dbg_line() dbg("[LINE] %d(%s)", __LINE__, __func__)
|
|
/* ********************************************************* */
|
|
|
|
|
|
/* ********************************************************* */
|
|
/* error display macro */
|
|
/* ********************************************************* */
|
|
#define mxerr(pdev, fmt, args...) \
|
|
dev_err(pdev, "[M1120-ERR] %s(L%04d) : " fmt "\n", __func__, __LINE__, ##args)
|
|
#define mxinfo(pdev, fmt, args...) \
|
|
dev_info(pdev, "[M1120-INFO] %s(L%04d) : " fmt "\n", __func__, __LINE__, ##args)
|
|
/* ********************************************************* */
|
|
|
|
//extern void *m1120_platform_data(void *info);
|
|
|
|
|
|
/* ********************************************************* */
|
|
/* static variable */
|
|
/* ********************************************************* */
|
|
static m1120_data_t *p_m1120_data;
|
|
/* ********************************************************* */
|
|
|
|
|
|
/* ********************************************************* */
|
|
/* function protyps */
|
|
/* ********************************************************* */
|
|
/* i2c interface */
|
|
static int m1120_i2c_read(struct i2c_client *client, u8 reg, u8* rdata, u8 len);
|
|
static int m1120_i2c_get_reg(struct i2c_client *client, u8 reg, u8* rdata);
|
|
static int m1120_i2c_write(struct i2c_client *client, u8 reg, u8* wdata, u8 len);
|
|
static int m1120_i2c_set_reg(struct i2c_client *client, u8 reg, u8 wdata);
|
|
/* vdd / vid power control */
|
|
static int m1120_set_power(struct device *dev, bool on);
|
|
/* scheduled work */
|
|
static void m1120_work_func(struct work_struct *work);
|
|
/* interrupt handler */
|
|
static irqreturn_t m1120_irq_handler(int irq, void *dev_id);
|
|
/* configuring or getting configured status */
|
|
static void m1120_get_reg(struct device *dev, int* regdata);
|
|
static void m1120_set_reg(struct device *dev, int* regdata);
|
|
static int m1120_get_enable(struct device *dev);
|
|
static void m1120_set_enable(struct device *dev, int enable);
|
|
static int m1120_get_delay(struct device *dev);
|
|
static void m1120_set_delay(struct device *dev, int delay);
|
|
static int m1120_get_debug(struct device *dev);
|
|
static void m1120_set_debug(struct device *dev, int debug);
|
|
static int m1120_clear_interrupt(struct device *dev);
|
|
static int m1120_update_interrupt_threshold(struct device *dev, short raw);
|
|
static int m1120_set_operation_mode(struct device *dev, int mode);
|
|
static int m1120_set_detection_mode(struct device *dev, u8 mode);
|
|
static int m1120_init_device(struct device *dev);
|
|
static int m1120_reset_device(struct device *dev);
|
|
static int m1120_set_calibration(struct device *dev);
|
|
static int m1120_get_calibrated_data(struct device *dev, int* data);
|
|
static int m1120_measure(m1120_data_t *p_data, short *raw);
|
|
static int m1120_get_result_status(m1120_data_t* p_data, int raw);
|
|
/* ********************************************************* */
|
|
|
|
static int irq_status = 2;
|
|
/******/
|
|
static int close;
|
|
static int stand;
|
|
static int lift;
|
|
static int open_a;
|
|
static int open_b;
|
|
static int cal_data;
|
|
|
|
/*****/
|
|
|
|
static int last_state = 0, current_state;
|
|
|
|
/* ********************************************************* */
|
|
/* functions for i2c interface */
|
|
/* ********************************************************* */
|
|
#define M1120_I2C_BUF_SIZE (17)
|
|
static int m1120_i2c_read(struct i2c_client* client, u8 reg, u8* rdata, u8 len)
|
|
{
|
|
int rc;
|
|
|
|
struct i2c_msg msg[] = {
|
|
{
|
|
.addr = client->addr,
|
|
.flags = 0,
|
|
.len = 1,
|
|
.buf = ®,
|
|
},
|
|
{
|
|
.addr = client->addr,
|
|
.flags = I2C_M_RD,
|
|
.len = len,
|
|
.buf = rdata,
|
|
},
|
|
};
|
|
|
|
if ( client == NULL ) {
|
|
mxerr(&client->dev, "client is NULL");
|
|
return -ENODEV;
|
|
}
|
|
|
|
rc = i2c_transfer(client->adapter, msg, 2);
|
|
if(rc<0) {
|
|
mxerr(&client->dev, "i2c_transfer was failed(%d)", rc);
|
|
return rc;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int m1120_i2c_get_reg(struct i2c_client *client, u8 reg, u8* rdata)
|
|
{
|
|
return m1120_i2c_read(client, reg, rdata, 1);
|
|
}
|
|
|
|
static int m1120_i2c_write(struct i2c_client* client, u8 reg, u8* wdata, u8 len)
|
|
{
|
|
m1120_data_t *p_data = i2c_get_clientdata(client);
|
|
u8 buf[M1120_I2C_BUF_SIZE];
|
|
int rc;
|
|
int i;
|
|
struct i2c_msg msg[] = {
|
|
{
|
|
.addr = client->addr,
|
|
.flags = 0,
|
|
.len = len+1,
|
|
.buf = buf,
|
|
},
|
|
};
|
|
|
|
if ( client == NULL ) {
|
|
printk("[ERROR] %s : i2c client is NULL.\n", __func__);
|
|
return -ENODEV;
|
|
}
|
|
|
|
buf[0] = reg;
|
|
if (len > M1120_I2C_BUF_SIZE) {
|
|
mxerr(&client->dev, "i2c buffer size must be less than %d", M1120_I2C_BUF_SIZE);
|
|
return -EIO;
|
|
}
|
|
for( i=0 ; i<len; i++ ) buf[i+1] = wdata[i];
|
|
|
|
rc = i2c_transfer(client->adapter, msg, 1);
|
|
if(rc< 0) {
|
|
mxerr(&client->dev, "i2c_transfer was failed (%d)", rc);
|
|
return rc;
|
|
}
|
|
|
|
if(len==1) {
|
|
switch(reg){
|
|
case M1120_REG_PERSINT:
|
|
p_data->reg.map.persint = wdata[0];
|
|
break;
|
|
case M1120_REG_INTSRS:
|
|
p_data->reg.map.intsrs = wdata[0];
|
|
break;
|
|
case M1120_REG_LTHL:
|
|
p_data->reg.map.lthl = wdata[0];
|
|
break;
|
|
case M1120_REG_LTHH:
|
|
p_data->reg.map.lthh = wdata[0];
|
|
break;
|
|
case M1120_REG_HTHL:
|
|
p_data->reg.map.hthl = wdata[0];
|
|
break;
|
|
case M1120_REG_HTHH:
|
|
p_data->reg.map.hthh = wdata[0];
|
|
break;
|
|
case M1120_REG_I2CDIS:
|
|
p_data->reg.map.i2cdis = wdata[0];
|
|
break;
|
|
case M1120_REG_SRST:
|
|
p_data->reg.map.srst = wdata[0];
|
|
msleep(1);
|
|
break;
|
|
case M1120_REG_OPF:
|
|
p_data->reg.map.opf = wdata[0];
|
|
break;
|
|
}
|
|
}
|
|
|
|
for(i=0; i<len; i++) dbg("reg=0x%02X data=0x%02X", buf[0]+(u8)i, buf[i+1]);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int m1120_i2c_set_reg(struct i2c_client *client, u8 reg, u8 wdata)
|
|
{
|
|
return m1120_i2c_write(client, reg, &wdata, sizeof(wdata));
|
|
}
|
|
|
|
/* ********************************************************* */
|
|
|
|
|
|
|
|
/* ********************************************************* */
|
|
/* vdd / vid power control */
|
|
/* ********************************************************* */
|
|
static int m1120_set_power(struct device *dev, bool on)
|
|
{
|
|
struct i2c_client *client = to_i2c_client(dev);
|
|
|
|
if(on) {
|
|
// to do for vdd power up
|
|
mxinfo(&client->dev, "vdd power up");
|
|
|
|
msleep(5); // wait 5ms
|
|
dbg("waiting 5ms after vdd power up");
|
|
|
|
// to do vid power up
|
|
mxinfo(&client->dev, "vid power up");
|
|
|
|
msleep(10); // wait 10ms
|
|
dbg("waiting 10ms after vid power up");
|
|
} else {
|
|
// to do for vid power down
|
|
mxinfo(&client->dev, "vid power down");
|
|
|
|
// to do for vdd power down
|
|
mxinfo(&client->dev, "vdd power down");
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
/* ********************************************************* */
|
|
|
|
/* ********************************************************* */
|
|
/* functions for scheduling */
|
|
/* ********************************************************* */
|
|
static void m1120_work_func(struct work_struct *work)
|
|
{
|
|
m1120_data_t* p_data = container_of((struct delayed_work *)work, m1120_data_t, work);
|
|
unsigned long delay = msecs_to_jiffies(m1120_get_delay(&p_data->client->dev));
|
|
short raw = 0;
|
|
int err = 0;
|
|
|
|
dbg_func_in();
|
|
err = m1120_measure(p_data, &raw);
|
|
|
|
if(!err) {
|
|
|
|
if(p_data->reg.map.intsrs & M1120_DETECTION_MODE_INTERRUPT) {
|
|
p_data->last_data = m1120_get_result_status(p_data, raw);
|
|
} else {
|
|
p_data->last_data = (int)raw;
|
|
}
|
|
|
|
#if (M1120_EVENT_TYPE == EV_ABS)
|
|
printk("-------------hall (%d)\n", p_data->last_data);
|
|
input_report_abs(p_data->input_dev, M1120_EVENT_CODE, p_data->last_data);
|
|
#elif (M1120_EVENT_TYPE == EV_KEY)
|
|
input_report_key(p_data->input_dev, M1120_EVENT_CODE, p_data->last_data);
|
|
if(p_data->last_data == M1120_RESULT_STATUS_A){
|
|
input_report_key(p_data->input_dev, KEY_ANGLE_0, 1);
|
|
input_sync(p_data->input_dev);
|
|
input_report_key(p_data->input_dev, KEY_ANGLE_0, 0);
|
|
input_sync(p_data->input_dev);
|
|
printk("-------------hall (%d)\n", p_data->last_data);
|
|
}else if(p_data->last_data == M1120_RESULT_STATUS_D){
|
|
input_report_key(p_data->input_dev, KEY_ANGLE_90, 1);
|
|
input_sync(p_data->input_dev);
|
|
input_report_key(p_data->input_dev, KEY_ANGLE_90, 0);
|
|
input_sync(p_data->input_dev);
|
|
printk("-------------hall (%d)\n", p_data->last_data);
|
|
}else if(p_data->last_data == M1120_RESULT_STATUS_G){
|
|
input_report_key(p_data->input_dev, KEY_ANGLE_180, 1);
|
|
input_sync(p_data->input_dev);
|
|
input_report_key(p_data->input_dev, KEY_ANGLE_180, 0);
|
|
input_sync(p_data->input_dev);
|
|
printk("-------------hall (%d)\n", p_data->last_data);
|
|
}else if((p_data->last_data == M1120_RESULT_STATUS_C) || (p_data->last_data == M1120_RESULT_STATUS_E)){
|
|
input_report_key(p_data->input_dev, KEY_ANGLE_CAMERA, 1);
|
|
input_sync(p_data->input_dev);
|
|
input_report_key(p_data->input_dev, KEY_ANGLE_CAMERA, 0);
|
|
input_sync(p_data->input_dev);
|
|
printk("-------------hall (%d)\n", p_data->last_data);
|
|
}
|
|
current_state = p_data->last_data;
|
|
if((last_state == M1120_RESULT_STATUS_A)&&(current_state != M1120_RESULT_STATUS_A)){
|
|
input_report_key(p_data->input_dev, KEY_ANGLE_OPEN, 1);
|
|
input_sync(p_data->input_dev);
|
|
input_report_key(p_data->input_dev, KEY_ANGLE_OPEN, 0);
|
|
input_sync(p_data->input_dev);
|
|
printk("-------------hall open\n");
|
|
}
|
|
last_state = p_data->last_data;
|
|
|
|
|
|
#else
|
|
#error ("[ERR] M1120_EVENT_TYPE is not defined.")
|
|
#endif
|
|
|
|
input_sync(p_data->input_dev);
|
|
}
|
|
|
|
if( p_data->reg.map.intsrs & M1120_DETECTION_MODE_INTERRUPT) {
|
|
dbg("run update_interrupt_threshold");
|
|
m1120_update_interrupt_threshold(&p_data->client->dev, raw);
|
|
} else {
|
|
schedule_delayed_work(&p_data->work, delay);
|
|
dbg("run schedule_delayed_work");
|
|
}
|
|
}
|
|
/* ********************************************************* */
|
|
|
|
|
|
/* ********************************************************* */
|
|
/* functions for interrupt handler */
|
|
/* ********************************************************* */
|
|
static irqreturn_t m1120_irq_handler(int irq, void *dev_id)
|
|
{
|
|
dbg_func_in();
|
|
dbg("run schedule_delayed_work");
|
|
if(p_m1120_data != NULL) {
|
|
dbg("run schedule_delayed_work");
|
|
schedule_delayed_work(&p_m1120_data->work, 0);
|
|
}
|
|
return IRQ_HANDLED;
|
|
}
|
|
/* ********************************************************* */
|
|
|
|
|
|
/* ********************************************************* */
|
|
/* functions for configuring or getting configured status */
|
|
/* ********************************************************* */
|
|
|
|
static void m1120_get_reg(struct device *dev, int* regdata)
|
|
{
|
|
struct i2c_client *client = to_i2c_client(dev);
|
|
int err;
|
|
|
|
u8 rega = (((*regdata) >> 8) & 0xFF);
|
|
u8 regd = 0;
|
|
err = m1120_i2c_get_reg(client, rega, ®d);
|
|
|
|
*regdata = 0;
|
|
*regdata |= (err==0) ? 0x0000 : 0xFF00;
|
|
*regdata |= regd;
|
|
}
|
|
|
|
static void m1120_set_reg(struct device *dev, int* regdata)
|
|
{
|
|
struct i2c_client *client = to_i2c_client(dev);
|
|
int err;
|
|
|
|
u8 rega = (((*regdata) >> 8) & 0xFF);
|
|
u8 regd = *regdata&0xFF;
|
|
err = m1120_i2c_set_reg(client, rega, regd);
|
|
|
|
*regdata = 0;
|
|
*regdata |= (err==0) ? 0x0000 : 0xFF00;
|
|
}
|
|
|
|
|
|
static int m1120_get_enable(struct device *dev)
|
|
{
|
|
struct i2c_client *client = to_i2c_client(dev);
|
|
m1120_data_t *p_data = i2c_get_clientdata(client);
|
|
|
|
return atomic_read(&p_data->atm.enable);
|
|
}
|
|
|
|
static void m1120_set_enable(struct device *dev, int enable)
|
|
{
|
|
struct i2c_client *client = to_i2c_client(dev);
|
|
m1120_data_t *p_data = i2c_get_clientdata(client);
|
|
int delay = m1120_get_delay(dev);
|
|
|
|
mutex_lock(&p_data->mtx.enable);
|
|
|
|
if (enable) { /* enable if state will be changed */
|
|
if (!atomic_cmpxchg(&p_data->atm.enable, 0, 1)) {
|
|
m1120_set_detection_mode(dev, p_data->reg.map.intsrs & M1120_DETECTION_MODE_INTERRUPT);
|
|
m1120_set_operation_mode(&p_m1120_data->client->dev, OPERATION_MODE_MEASUREMENT);
|
|
if( ! (p_data->reg.map.intsrs & M1120_DETECTION_MODE_INTERRUPT)) {
|
|
schedule_delayed_work(&p_data->work, msecs_to_jiffies(delay));
|
|
}
|
|
}
|
|
} else { /* disable if state will be changed */
|
|
if (atomic_cmpxchg(&p_data->atm.enable, 1, 0)) {
|
|
cancel_delayed_work_sync(&p_data->work);
|
|
m1120_set_operation_mode(&p_m1120_data->client->dev, OPERATION_MODE_POWERDOWN);
|
|
}
|
|
}
|
|
atomic_set(&p_data->atm.enable, enable);
|
|
|
|
mutex_unlock(&p_data->mtx.enable);
|
|
}
|
|
|
|
static int m1120_get_delay(struct device *dev)
|
|
{
|
|
struct i2c_client *client = to_i2c_client(dev);
|
|
m1120_data_t *p_data = i2c_get_clientdata(client);
|
|
|
|
int delay = 0;
|
|
|
|
delay = atomic_read(&p_data->atm.delay);
|
|
|
|
return delay;
|
|
}
|
|
|
|
static void m1120_set_delay(struct device *dev, int delay)
|
|
{
|
|
struct i2c_client *client = to_i2c_client(dev);
|
|
m1120_data_t *p_data = i2c_get_clientdata(client);
|
|
|
|
if(delay<M1120_DELAY_MIN) delay = M1120_DELAY_MIN;
|
|
atomic_set(&p_data->atm.delay, delay);
|
|
|
|
mutex_lock(&p_data->mtx.enable);
|
|
|
|
if (m1120_get_enable(dev)) {
|
|
if( ! (p_data->reg.map.intsrs & M1120_DETECTION_MODE_INTERRUPT)) {
|
|
cancel_delayed_work_sync(&p_data->work);
|
|
schedule_delayed_work(&p_data->work, msecs_to_jiffies(delay));
|
|
}
|
|
}
|
|
|
|
mutex_unlock(&p_data->mtx.enable);
|
|
}
|
|
|
|
static int m1120_get_debug(struct device *dev)
|
|
{
|
|
struct i2c_client *client = to_i2c_client(dev);
|
|
m1120_data_t *p_data = i2c_get_clientdata(client);
|
|
|
|
return atomic_read(&p_data->atm.debug);
|
|
}
|
|
|
|
static void m1120_set_debug(struct device *dev, int debug)
|
|
{
|
|
struct i2c_client *client = to_i2c_client(dev);
|
|
m1120_data_t *p_data = i2c_get_clientdata(client);
|
|
|
|
atomic_set(&p_data->atm.debug, debug);
|
|
}
|
|
|
|
static int m1120_clear_interrupt(struct device *dev)
|
|
{
|
|
struct i2c_client *client = to_i2c_client(dev);
|
|
m1120_data_t *p_data = i2c_get_clientdata(client);
|
|
int ret = 0;
|
|
|
|
ret = m1120_i2c_set_reg(p_data->client, M1120_REG_PERSINT, p_data->reg.map.persint | 0x01);
|
|
|
|
return ret;
|
|
}
|
|
|
|
void m1120_convdata_short_to_2byte(u8 opf, short x, unsigned char *hbyte, unsigned char *lbyte)
|
|
{
|
|
if( (opf & M1120_VAL_OPF_BIT_8) == M1120_VAL_OPF_BIT_8) {
|
|
/* 8 bit resolution */
|
|
if(x<-128) x=-128;
|
|
else if(x>127) x=127;
|
|
|
|
if(x>=0) {
|
|
*lbyte = x & 0x7F;
|
|
} else {
|
|
*lbyte = ( (0x80 - (x*(-1))) & 0x7F ) | 0x80;
|
|
}
|
|
*hbyte = 0x00;
|
|
} else {
|
|
/* 10 bit resolution */
|
|
if(x<-512) x=-512;
|
|
else if(x>511) x=511;
|
|
|
|
if(x>=0) {
|
|
*lbyte = x & 0xFF;
|
|
*hbyte = (((x&0x100)>>8)&0x01) << 6;
|
|
} else {
|
|
*lbyte = (0x0200 - (x*(-1))) & 0xFF;
|
|
*hbyte = ((((0x0200 - (x*(-1))) & 0x100)>>8)<<6) | 0x80;
|
|
}
|
|
}
|
|
}
|
|
|
|
short m1120_convdata_2byte_to_short(u8 opf, unsigned char hbyte, unsigned char lbyte)
|
|
{
|
|
short x;
|
|
|
|
if( (opf & M1120_VAL_OPF_BIT_8) == M1120_VAL_OPF_BIT_8) {
|
|
/* 8 bit resolution */
|
|
x = lbyte & 0x7F;
|
|
if(lbyte & 0x80) {
|
|
x -= 0x80;
|
|
}
|
|
} else {
|
|
/* 10 bit resolution */
|
|
x = ( ( (hbyte & 0x40) >> 6) << 8 ) | lbyte;
|
|
if(hbyte&0x80) {
|
|
x -= 0x200;
|
|
}
|
|
}
|
|
|
|
return x;
|
|
}
|
|
|
|
static int m1120_update_interrupt_threshold(struct device *dev, short raw)
|
|
{
|
|
struct i2c_client *client = to_i2c_client(dev);
|
|
m1120_data_t *p_data = i2c_get_clientdata(client);
|
|
u8 lthh=0, lthl=0, hthh=0, hthl=0;
|
|
int err = -1;
|
|
int h_status; //digital hall current status.
|
|
|
|
if(p_data->reg.map.intsrs & M1120_DETECTION_MODE_INTERRUPT) {
|
|
|
|
dbg("reg.map.intsrs = 0x%02X", p_data->reg.map.intsrs);
|
|
if(p_data->reg.map.intsrs & M1120_VAL_INTSRS_INTTYPE_WITHIN) {
|
|
// to do another condition
|
|
} else {
|
|
dbg("BESIDE raw = %d", raw);
|
|
#if (M1120_RESULT_STATUS_NUM==3)
|
|
if(p_data->stscfg_init) {
|
|
m1120_convdata_short_to_2byte(p_data->reg.map.opf, 511, &hthh, &hthl);
|
|
m1120_convdata_short_to_2byte(p_data->reg.map.opf, 510, <hh, <hl);
|
|
p_data->stscfg_init = 0;
|
|
/*
|
|
* } else {
|
|
if (m1120_get_result_status(p_data, (int)raw) == p_data->stscfg[0].type) {
|
|
m1120_convdata_short_to_2byte(p_data->reg.map.opf, 511, &hthh, &hthl);
|
|
m1120_convdata_short_to_2byte(p_data->reg.map.opf, (short)p_data->stscfg[1].max, <hh, <hl);
|
|
} else if (m1120_get_result_status(p_data, (int)raw) == p_data->stscfg[1].type) {
|
|
m1120_convdata_short_to_2byte(p_data->reg.map.opf, (short)p_data->stscfg[0].min, &hthh, &hthl);
|
|
m1120_convdata_short_to_2byte(p_data->reg.map.opf, (short)p_data->stscfg[2].max, <hh, <hl);
|
|
} else if (m1120_get_result_status(p_data, (int)raw) == p_data->stscfg[2].type) {
|
|
m1120_convdata_short_to_2byte(p_data->reg.map.opf, (short)p_data->stscfg[1].min, &hthh, &hthl);
|
|
m1120_convdata_short_to_2byte(p_data->reg.map.opf, -511, <hh, <hl);
|
|
}
|
|
}
|
|
*/
|
|
/**************1 Added for lenovo requirement--liulf2***************/
|
|
} else {
|
|
h_status = m1120_get_result_status(p_data, (int)raw) - 1;
|
|
m1120_convdata_short_to_2byte(p_data->reg.map.opf, (short)p_data->stscfg[h_status].max, &hthh, &hthl);
|
|
m1120_convdata_short_to_2byte(p_data->reg.map.opf, (short)p_data->stscfg[h_status].min, <hh, <hl);
|
|
}
|
|
|
|
|
|
/**************1 Added for lenovo requirement--liulf2***************/
|
|
#else
|
|
if( (raw >=-512) && (raw < p_data->thrhigh) ) {
|
|
m1120_convdata_short_to_2byte(p_data->reg.map.opf, p_data->thrhigh, &hthh, &hthl);
|
|
m1120_convdata_short_to_2byte(p_data->reg.map.opf, -512, <hh, <hl);
|
|
} else if ( (raw >= p_data->thrlow) && (raw <= 511)) {
|
|
m1120_convdata_short_to_2byte(p_data->reg.map.opf, 511, &hthh, &hthl);
|
|
m1120_convdata_short_to_2byte(p_data->reg.map.opf, p_data->thrlow, <hh, <hl);
|
|
}
|
|
#endif
|
|
}
|
|
|
|
err = m1120_i2c_set_reg(p_data->client, M1120_REG_HTHH, hthh);
|
|
if(err) return err;
|
|
err = m1120_i2c_set_reg(p_data->client, M1120_REG_HTHL, hthl);
|
|
if(err) return err;
|
|
err = m1120_i2c_set_reg(p_data->client, M1120_REG_LTHH, lthh);
|
|
if(err) return err;
|
|
err = m1120_i2c_set_reg(p_data->client, M1120_REG_LTHL, lthl);
|
|
if(err) return err;
|
|
|
|
dbg("threshold : (0x%02X%02X[%d], 0x%02X%02X[%d])", \
|
|
hthh, hthl, \
|
|
m1120_convdata_2byte_to_short(p_data->reg.map.opf, hthh, hthl), \
|
|
lthh, lthl, \
|
|
m1120_convdata_2byte_to_short(p_data->reg.map.opf, lthh, lthl) \
|
|
);
|
|
|
|
err = m1120_clear_interrupt(dev);
|
|
if(err) return err;
|
|
}
|
|
|
|
return err;
|
|
}
|
|
|
|
static int m1120_set_operation_mode(struct device *dev, int mode)
|
|
{
|
|
struct i2c_client *client = to_i2c_client(dev);
|
|
m1120_data_t *p_data = i2c_get_clientdata(client);
|
|
u8 opf = p_data->reg.map.opf;
|
|
int err = -1;
|
|
|
|
switch(mode) {
|
|
case OPERATION_MODE_POWERDOWN:
|
|
|
|
if((irq_status == 1) && (irq_status != 2)){ /* disable irq */
|
|
disable_irq(p_data->irq);
|
|
irq_status = 0;
|
|
}
|
|
opf &= (0xFF - M1120_VAL_OPF_HSSON_ON);
|
|
err = m1120_i2c_set_reg(client, M1120_REG_OPF, opf);
|
|
mxinfo(&client->dev, "operation mode was chnaged to OPERATION_MODE_POWERDOWN");
|
|
break;
|
|
case OPERATION_MODE_MEASUREMENT:
|
|
opf &= (0xFF - M1120_VAL_OPF_EFRD_ON);
|
|
opf |= M1120_VAL_OPF_HSSON_ON;
|
|
err = m1120_i2c_set_reg(client, M1120_REG_OPF, opf);
|
|
if(p_data->reg.map.intsrs & M1120_DETECTION_MODE_INTERRUPT) {
|
|
if((irq_status == 0) && (irq_status != 2)){
|
|
enable_irq(p_data->irq);
|
|
irq_status = 1;
|
|
}
|
|
}
|
|
mxinfo(&client->dev, "operation mode was chnaged to OPERATION_MODE_MEASUREMENT");
|
|
break;
|
|
case OPERATION_MODE_FUSEROMACCESS:
|
|
opf |= M1120_VAL_OPF_EFRD_ON;
|
|
opf |= M1120_VAL_OPF_HSSON_ON;
|
|
err = m1120_i2c_set_reg(client, M1120_REG_OPF, opf);
|
|
mxinfo(&client->dev, "operation mode was chnaged to OPERATION_MODE_FUSEROMACCESS");
|
|
break;
|
|
}
|
|
|
|
return err;
|
|
}
|
|
|
|
static int m1120_set_detection_mode(struct device *dev, u8 mode)
|
|
{
|
|
struct i2c_client *client = to_i2c_client(dev);
|
|
m1120_data_t *p_data = i2c_get_clientdata(client);
|
|
u8 data;
|
|
int err = 0;
|
|
|
|
if(mode & M1120_DETECTION_MODE_INTERRUPT) {
|
|
|
|
/* config threshold */
|
|
m1120_update_interrupt_threshold(dev, p_data->last_data);
|
|
|
|
/* write intsrs */
|
|
data = p_data->reg.map.intsrs | M1120_DETECTION_MODE_INTERRUPT;
|
|
err = m1120_i2c_set_reg(p_data->client, M1120_REG_INTSRS, data);
|
|
|
|
p_data->stscfg_init = 1;
|
|
if(err) return err;
|
|
|
|
} else {
|
|
|
|
/* write intsrs */
|
|
data = p_data->reg.map.intsrs & (0xFF - M1120_DETECTION_MODE_INTERRUPT);
|
|
err = m1120_i2c_set_reg(p_data->client, M1120_REG_INTSRS, data);
|
|
if(err) return err;
|
|
}
|
|
|
|
return err;
|
|
}
|
|
|
|
|
|
static int m1120_init_device(struct device *dev)
|
|
{
|
|
struct i2c_client *client = to_i2c_client(dev);
|
|
m1120_data_t *p_data = i2c_get_clientdata(client);
|
|
int err = -1;
|
|
|
|
/* (1) vdd and vid power up */
|
|
err = m1120_set_power(dev, 1);
|
|
if(err) {
|
|
mxerr(&client->dev, "m1120 power-on was failed (%d)", err);
|
|
return err;
|
|
}
|
|
|
|
/* (2) init variables */
|
|
atomic_set(&p_data->atm.enable, 0);
|
|
atomic_set(&p_data->atm.delay, M1120_DELAY_MIN);
|
|
#ifdef M1120_DBG_ENABLE
|
|
atomic_set(&p_data->atm.debug, 1);
|
|
#else
|
|
atomic_set(&p_data->atm.debug, 0);
|
|
#endif
|
|
p_data->calibrated_data = 0;
|
|
p_data->last_data = 0;
|
|
// p_data->irq_enabled = 0;
|
|
p_data->irq_first = 1;
|
|
|
|
close = 70;
|
|
stand = 10;
|
|
lift = -100;
|
|
open_a = (close + stand)/4 + stand;
|
|
open_b = (stand + lift)/2;
|
|
|
|
#if (M1120_RESULT_STATUS_NUM==3)
|
|
/* init stscfg */
|
|
/* p_data->stscfg[2].type = M1120_RESULT_STATUS_C; //180 degree
|
|
p_data->stscfg[2].min = -110;
|
|
p_data->stscfg[2].max = -90;
|
|
|
|
p_data->stscfg[1].type = M1120_RESULT_STATUS_B; //90 degree
|
|
p_data->stscfg[1].min = 0;
|
|
p_data->stscfg[1].max = 20;
|
|
|
|
p_data->stscfg[0].type = M1120_RESULT_STATUS_A; //0 degree
|
|
p_data->stscfg[0].min = 120;
|
|
p_data->stscfg[0].max = 140;
|
|
*/
|
|
p_data->stscfg[6].type = M1120_RESULT_STATUS_G; //180 degree
|
|
p_data->stscfg[6].min = -511;
|
|
p_data->stscfg[6].max = lift + 10;
|
|
|
|
p_data->stscfg[5].type = M1120_RESULT_STATUS_F; //135~180 area
|
|
p_data->stscfg[5].min = lift + 10;
|
|
p_data->stscfg[5].max = open_b;
|
|
|
|
p_data->stscfg[4].type = M1120_RESULT_STATUS_E; //90~135 area
|
|
p_data->stscfg[4].min = open_b;
|
|
p_data->stscfg[4].max = stand - 10;
|
|
|
|
p_data->stscfg[3].type = M1120_RESULT_STATUS_D; //90 degree
|
|
p_data->stscfg[3].min = stand - 10;
|
|
p_data->stscfg[3].max = stand + 10;
|
|
|
|
p_data->stscfg[2].type = M1120_RESULT_STATUS_C; //45~90 area
|
|
p_data->stscfg[2].min = stand + 10;
|
|
p_data->stscfg[2].max = open_a;
|
|
|
|
p_data->stscfg[1].type = M1120_RESULT_STATUS_B; //0~45 area
|
|
p_data->stscfg[1].min = open_a;
|
|
p_data->stscfg[1].max = close -10;
|
|
|
|
p_data->stscfg[0].type = M1120_RESULT_STATUS_A; //0 degree
|
|
p_data->stscfg[0].min = close - 10;
|
|
p_data->stscfg[0].max = 511;
|
|
|
|
#else
|
|
p_data->thrhigh = M1120_DETECT_RANGE_HIGH;
|
|
p_data->thrlow = M1120_DETECT_RANGE_LOW;
|
|
#endif
|
|
m1120_set_delay(&client->dev, M1120_DELAY_MAX);
|
|
m1120_set_debug(&client->dev, 0);
|
|
|
|
/* (3) reset registers */
|
|
err = m1120_reset_device(dev);
|
|
if(err) {
|
|
mxerr(&client->dev, "m1120_reset_device was failed (%d)", err);
|
|
return err;
|
|
}
|
|
|
|
mxinfo(&client->dev, "initializing device was success");
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int m1120_reset_device(struct device *dev)
|
|
{
|
|
int err = 0;
|
|
u8 id = 0xFF, data = 0x00;
|
|
|
|
struct i2c_client *client = to_i2c_client(dev);
|
|
m1120_data_t *p_data = i2c_get_clientdata(client);
|
|
|
|
if( (p_data == NULL) || (p_data->client == NULL) ) return -ENODEV;
|
|
|
|
/* (1) sw reset */
|
|
err = m1120_i2c_set_reg(p_data->client, M1120_REG_SRST, M1120_VAL_SRST_RESET);
|
|
if(err) {
|
|
mxerr(&client->dev, "sw-reset was failed(%d)", err);
|
|
return err;
|
|
}
|
|
msleep(5); // wait 5ms
|
|
dbg("wait 5ms after vdd power up");
|
|
|
|
/* (2) check id */
|
|
err = m1120_i2c_get_reg(p_data->client, M1120_REG_DID, &id);
|
|
if (err < 0) return err;
|
|
if (id != M1120_VAL_DID) {
|
|
mxerr(&client->dev, "current device id(0x%02X) is not M1120 device id(0x%02X)", id, M1120_VAL_DID);
|
|
return -ENXIO;
|
|
}
|
|
|
|
/* (3) init variables */
|
|
/* (3-1) persint */
|
|
data = M1120_PERSISTENCE_COUNT;
|
|
err = m1120_i2c_set_reg(p_data->client, M1120_REG_PERSINT, data);
|
|
/* (3-2) intsrs */
|
|
data = M1120_DETECTION_MODE | M1120_SENSITIVITY_TYPE;
|
|
if(data & M1120_DETECTION_MODE_INTERRUPT) {
|
|
data |= M1120_INTERRUPT_TYPE;
|
|
}
|
|
err = m1120_i2c_set_reg(p_data->client, M1120_REG_INTSRS, data);
|
|
/* (3-3) opf */
|
|
data = M1120_OPERATION_FREQUENCY | M1120_OPERATION_RESOLUTION;
|
|
err = m1120_i2c_set_reg(p_data->client, M1120_REG_OPF, data);
|
|
|
|
/* (4) write variable to register */
|
|
err = m1120_set_detection_mode(dev, M1120_DETECTION_MODE);
|
|
if(err) {
|
|
mxerr(&client->dev, "m1120_set_detection_mode was failed(%d)", err);
|
|
return err;
|
|
}
|
|
|
|
/* (5) set power-down mode */
|
|
err = m1120_set_operation_mode(dev, OPERATION_MODE_POWERDOWN);
|
|
if(err) {
|
|
mxerr(&client->dev, "m1120_set_detection_mode was failed(%d)", err);
|
|
return err;
|
|
}
|
|
#if 0
|
|
err = m1120_i2c_set_reg(p_data->client, 0x6F, 0x6E);
|
|
err = m1120_i2c_set_reg(p_data->client, 0x50, 0x60);
|
|
err = m1120_i2c_set_reg(p_data->client, 0x51, 0x08);
|
|
err = m1120_i2c_set_reg(p_data->client, 0x52, 0x19);
|
|
err = m1120_i2c_set_reg(p_data->client, 0x53, 0x19);
|
|
err = m1120_i2c_set_reg(p_data->client, 0x54, 0x03);
|
|
err = m1120_i2c_set_reg(p_data->client, 0x55, 0x01);
|
|
err = m1120_i2c_set_reg(p_data->client, 0x56, 0x25);
|
|
err = m1120_i2c_set_reg(p_data->client, 0x5C, 0x06);
|
|
err = m1120_i2c_set_reg(p_data->client, 0x5D, 0x7B);
|
|
err = m1120_i2c_set_reg(p_data->client, 0x5E, 0x9C);
|
|
err = m1120_i2c_set_reg(p_data->client, 0x5F, 0x15);
|
|
err = m1120_i2c_set_reg(p_data->client, 0x60, 0xEF);
|
|
err = m1120_i2c_set_reg(p_data->client, 0x67, 0x50);
|
|
err = m1120_i2c_set_reg(p_data->client, 0x68, 0x60);
|
|
err = m1120_i2c_set_reg(p_data->client, 0x65, 0x10);
|
|
msleep(10);
|
|
#endif
|
|
return err;
|
|
}
|
|
|
|
|
|
static int m1120_set_calibration(struct device *dev)
|
|
{
|
|
struct i2c_client *client = to_i2c_client(dev);
|
|
m1120_data_t *p_data = i2c_get_clientdata(client);
|
|
int retrycnt = 10, cnt = 0;
|
|
|
|
short raw = 0;
|
|
int err;
|
|
int cal_store = 0, cnt_s = 0;
|
|
|
|
m1120_set_operation_mode(dev, OPERATION_MODE_MEASUREMENT);
|
|
for(cnt=0; cnt<retrycnt; cnt++) {
|
|
msleep(M1120_DELAY_FOR_READY);
|
|
err = m1120_measure(p_data, &raw);
|
|
if(!err) {
|
|
cal_store += raw;
|
|
cnt_s++;
|
|
}
|
|
}
|
|
cal_store /= cnt_s;
|
|
p_data->calibrated_data = cal_store;
|
|
cal_data = cal_store;
|
|
if(!m1120_get_enable(dev)) m1120_set_operation_mode(dev, OPERATION_MODE_POWERDOWN);
|
|
|
|
return err;
|
|
}
|
|
|
|
static int m1120_get_calibrated_data(struct device *dev, int* data)
|
|
{
|
|
struct i2c_client *client = to_i2c_client(dev);
|
|
m1120_data_t *p_data = i2c_get_clientdata(client);
|
|
|
|
int err = 0;
|
|
|
|
if(p_data == NULL) err = -ENODEV;
|
|
else *data = p_data->calibrated_data;
|
|
|
|
return err;
|
|
}
|
|
|
|
static int m1120_measure(m1120_data_t *p_data, short *raw)
|
|
{
|
|
struct i2c_client *client = p_data->client;
|
|
int err;
|
|
u8 buf[3];
|
|
int st1_is_ok = 0;
|
|
|
|
// (1) read data
|
|
err = m1120_i2c_read(client, M1120_REG_ST1, buf, sizeof(buf));
|
|
if(err) return err;
|
|
|
|
// (2) collect data
|
|
if(p_data->reg.map.intsrs & M1120_VAL_INTSRS_INT_ON) {
|
|
// check st1 at interrupt mode
|
|
if( ! (buf[0] & 0x10) ) {
|
|
st1_is_ok = 1;
|
|
}
|
|
} else {
|
|
// check st1 at polling mode
|
|
if(buf[0] & 0x01) {
|
|
st1_is_ok = 1;
|
|
}
|
|
}
|
|
|
|
st1_is_ok = 1;
|
|
if(st1_is_ok) {
|
|
*raw = m1120_convdata_2byte_to_short(p_data->reg.map.opf, buf[2], buf[1]);
|
|
} else {
|
|
mxerr(&client->dev, "st1(0x%02X) is not DRDY", buf[0]);
|
|
err = -1;
|
|
}
|
|
|
|
// if(m1120_get_debug(&client->dev)) {
|
|
dbg("raw data (%d)", *raw);
|
|
// }
|
|
|
|
return err;
|
|
}
|
|
|
|
|
|
static int m1120_get_result_status(m1120_data_t* p_data, int raw)
|
|
{
|
|
int status, status_loop;
|
|
|
|
#if (M1120_RESULT_STATUS_NUM==3)
|
|
/** if( (raw<p_data->stscfg[2].max) ) status = p_data->stscfg[2].type;
|
|
else if( (raw > p_data->stscfg[2].max) && (raw<p_data->stscfg[0].min) ) status = p_data->stscfg[1].type;
|
|
else if( (raw >= p_data->stscfg[0].min) ) status = p_data->stscfg[0].type;
|
|
else status = M1120_RESULT_STATUS_UNKNOWN;
|
|
**/
|
|
for(status_loop = 0;status_loop < 7; status_loop++){
|
|
if((raw<p_data->stscfg[status_loop].max)&&(raw>p_data->stscfg[status_loop].min)){
|
|
status = p_data->stscfg[status_loop].type;
|
|
break;
|
|
}
|
|
}
|
|
if(status_loop > 6)
|
|
status = M1120_RESULT_STATUS_UNKNOWN;
|
|
|
|
dbg("Result is status [0x%02X]", status);
|
|
#else
|
|
if(p_data->thrhigh <= raw) {
|
|
status = M1120_RESULT_STATUS_B;
|
|
} else if(p_data->thrlow >= raw) {
|
|
status = M1120_RESULT_STATUS_A;
|
|
} else {
|
|
status = p_data->last_data;
|
|
}
|
|
|
|
|
|
|
|
if(p_data->thrhigh <= raw) {
|
|
status = M1120_RESULT_STATUS_B;
|
|
} else if(p_data->thrlow >= raw) {
|
|
status = M1120_RESULT_STATUS_A;
|
|
} else {
|
|
status = p_data->last_data;
|
|
}
|
|
|
|
switch(status) {
|
|
case M1120_RESULT_STATUS_A:
|
|
dbg("Result is status [A]\n");
|
|
break;
|
|
case M1120_RESULT_STATUS_B:
|
|
dbg("Result is status [B]\n");
|
|
break;
|
|
}
|
|
#endif
|
|
return status;
|
|
}
|
|
|
|
/* *************************************************
|
|
input device interface
|
|
************************************************* */
|
|
|
|
static int m1120_input_dev_init(m1120_data_t *p_data)
|
|
{
|
|
struct input_dev *dev;
|
|
int err;
|
|
|
|
dev = input_allocate_device();
|
|
if (!dev) {
|
|
return -ENOMEM;
|
|
}
|
|
dev->name = M1120_DRIVER_NAME;
|
|
dev->id.bustype = BUS_I2C;
|
|
|
|
#if (M1120_EVENT_TYPE == EV_ABS)
|
|
input_set_drvdata(dev, p_data);
|
|
input_set_capability(dev, M1120_EVENT_TYPE, ABS_MISC);
|
|
set_bit(EV_ABS, dev->evbit);
|
|
set_bit(EV_KEY, dev->evbit);
|
|
set_bit(EV_SYN, dev->evbit);
|
|
input_set_abs_params(dev, M1120_EVENT_CODE, M1120_EVENT_DATA_CAPABILITY_MIN, M1120_EVENT_DATA_CAPABILITY_MAX, 0, 0);
|
|
#elif (M1120_EVENT_TYPE == EV_KEY)
|
|
input_set_drvdata(dev, p_data);
|
|
set_bit(EV_KEY, dev->evbit);
|
|
set_bit(KEY_ANGLE_0, dev->keybit);
|
|
set_bit(KEY_ANGLE_90, dev->keybit);
|
|
set_bit(KEY_ANGLE_180, dev->keybit);
|
|
set_bit(KEY_ANGLE_CAMERA, dev->keybit);
|
|
set_bit(KEY_ANGLE_OPEN, dev->keybit);
|
|
input_set_capability(dev, M1120_EVENT_TYPE, M1120_EVENT_CODE);
|
|
#else
|
|
#error ("[ERR] M1120_EVENT_TYPE is not defined.")
|
|
#endif
|
|
|
|
err = input_register_device(dev);
|
|
if (err < 0) {
|
|
input_free_device(dev);
|
|
return err;
|
|
}
|
|
|
|
p_data->input_dev = dev;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void m1120_input_dev_terminate(m1120_data_t *p_data)
|
|
{
|
|
struct input_dev *dev = p_data->input_dev;
|
|
|
|
input_unregister_device(dev);
|
|
input_free_device(dev);
|
|
}
|
|
|
|
/* *************************************************
|
|
misc device interface
|
|
************************************************* */
|
|
|
|
static int m1120_misc_dev_open( struct inode*, struct file* );
|
|
static int m1120_misc_dev_release( struct inode*, struct file* );
|
|
static long m1120_misc_dev_ioctl(struct file* file, unsigned int cmd, unsigned long arg);
|
|
static ssize_t m1120_misc_dev_read( struct file *filp, char *buf, size_t count, loff_t *ofs );
|
|
static ssize_t m1120_misc_dev_write( struct file *filp, const char *buf, size_t count, loff_t *ofs );
|
|
static unsigned int m1120_misc_dev_poll( struct file *filp, struct poll_table_struct *pwait );
|
|
|
|
static struct file_operations m1120_misc_dev_fops =
|
|
{
|
|
.owner = THIS_MODULE,
|
|
.open = m1120_misc_dev_open,
|
|
.unlocked_ioctl = m1120_misc_dev_ioctl,
|
|
.release = m1120_misc_dev_release,
|
|
.read = m1120_misc_dev_read,
|
|
.write = m1120_misc_dev_write,
|
|
.poll = m1120_misc_dev_poll,
|
|
};
|
|
|
|
static struct miscdevice m1120_misc_dev =
|
|
{
|
|
.minor = MISC_DYNAMIC_MINOR,
|
|
.name = M1120_DRIVER_NAME,
|
|
.fops = &m1120_misc_dev_fops,
|
|
};
|
|
/* m1120 misc device file operation */
|
|
static int m1120_misc_dev_open( struct inode* inode, struct file* file)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
static int m1120_misc_dev_release( struct inode* inode, struct file* file)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
static long m1120_misc_dev_ioctl(struct file* file, unsigned int cmd, unsigned long arg)
|
|
{
|
|
long ret = 0;
|
|
|
|
void __user *argp = (void __user *)arg;
|
|
int kbuf = 0;
|
|
int caldata = 0;
|
|
|
|
switch( cmd ) {
|
|
case M1120_IOCTL_SET_ENABLE:
|
|
if(copy_from_user(&kbuf, argp, sizeof(kbuf))) return -EFAULT;
|
|
dbg("M1120_IOCTL_SET_ENABLE(%d)", kbuf);
|
|
m1120_set_enable(&p_m1120_data->client->dev, kbuf);
|
|
break;
|
|
case M1120_IOCTL_GET_ENABLE:
|
|
kbuf = m1120_get_enable(&p_m1120_data->client->dev);
|
|
dbg("M1120_IOCTL_GET_ENABLE(%d)", kbuf);
|
|
if(copy_to_user(argp, &kbuf, sizeof(kbuf))) return -EFAULT;
|
|
break;
|
|
case M1120_IOCTL_SET_DELAY:
|
|
if(copy_from_user(&kbuf, argp, sizeof(kbuf))) return -EFAULT;
|
|
dbg("M1120_IOCTL_SET_DELAY(%d)", kbuf);
|
|
m1120_set_delay(&p_m1120_data->client->dev, kbuf);
|
|
break;
|
|
case M1120_IOCTL_GET_DELAY:
|
|
kbuf = m1120_get_delay(&p_m1120_data->client->dev);
|
|
dbg("M1120_IOCTL_GET_DELAY(%d)", kbuf);
|
|
if(copy_to_user(argp, &kbuf, sizeof(kbuf))) return -EFAULT;
|
|
break;
|
|
case M1120_IOCTL_SET_CALIBRATION:
|
|
dbg("M1120_IOCTL_SET_CALIBRATION");
|
|
kbuf = m1120_set_calibration(&p_m1120_data->client->dev);
|
|
if(copy_to_user(argp, &kbuf, sizeof(kbuf))) return -EFAULT;
|
|
break;
|
|
case M1120_IOCTL_GET_CALIBRATED_DATA:
|
|
dbg("M1120_IOCTL_GET_CALIBRATED_DATA");
|
|
kbuf = m1120_get_calibrated_data(&p_m1120_data->client->dev, &caldata);
|
|
if(copy_to_user(argp, &caldata, sizeof(caldata))) return -EFAULT;
|
|
dbg("calibrated data (%d)", caldata);
|
|
break;
|
|
case M1120_IOCTL_SET_REG:
|
|
if(copy_from_user(&kbuf, argp, sizeof(kbuf))) return -EFAULT;
|
|
dbg("M1120_IOCTL_SET_REG([0x%02X] %02X", (u8)((kbuf>>8)&0xFF), (u8)(kbuf&0xFF));
|
|
m1120_set_reg(&p_m1120_data->client->dev, &kbuf);
|
|
dbgn(" (%s))\n", (kbuf&0xFF00)?"Not Ok":"Ok");
|
|
if(copy_to_user(argp, &kbuf, sizeof(kbuf))) return -EFAULT;
|
|
break;
|
|
case M1120_IOCTL_GET_REG:
|
|
if(copy_from_user(&kbuf, argp, sizeof(kbuf))) return -EFAULT;
|
|
dbg("M1120_IOCTL_GET_REG([0x%02X]", (u8)((kbuf>>8)&0xFF) );
|
|
m1120_get_reg(&p_m1120_data->client->dev, &kbuf);
|
|
dbgn(" 0x%02X (%s))\n", (u8)(kbuf&0xFF), (kbuf&0xFF00)?"Not Ok":"Ok");
|
|
if(copy_to_user(argp, &kbuf, sizeof(kbuf))) return -EFAULT;
|
|
break;
|
|
case M1120_IOCTL_SET_INTERRUPT:
|
|
if(copy_from_user(&kbuf, argp, sizeof(kbuf))) return -EFAULT;
|
|
dbg("M1120_IOCTL_SET_INTERRUPT(%d)", kbuf);
|
|
if(kbuf) {
|
|
m1120_set_detection_mode(&p_m1120_data->client->dev, M1120_DETECTION_MODE_INTERRUPT);
|
|
} else {
|
|
m1120_set_detection_mode(&p_m1120_data->client->dev, M1120_DETECTION_MODE_POLLING);
|
|
}
|
|
break;
|
|
case M1120_IOCTL_GET_INTERRUPT:
|
|
kbuf = (p_m1120_data->reg.map.intsrs & M1120_DETECTION_MODE_INTERRUPT) ? 1 : 0 ;
|
|
dbg("M1120_IOCTL_GET_INTERRUPT(%d)", kbuf);
|
|
if(copy_to_user(argp, &kbuf, sizeof(kbuf)));
|
|
break;
|
|
case M1120_IOCTL_SET_THRESHOLD_HIGH:
|
|
if(copy_from_user(&kbuf, argp, sizeof(kbuf))) return -EFAULT;
|
|
dbg("M1120_IOCTL_SET_THRESHOLD_HIGH(%d)", kbuf);
|
|
p_m1120_data->thrhigh = kbuf;
|
|
break;
|
|
case M1120_IOCTL_GET_THRESHOLD_HIGH:
|
|
kbuf = p_m1120_data->thrhigh;
|
|
dbg("M1120_IOCTL_GET_THRESHOLD_HIGH(%d)", kbuf);
|
|
if(copy_to_user(argp, &kbuf, sizeof(kbuf)));
|
|
break;
|
|
case M1120_IOCTL_SET_THRESHOLD_LOW:
|
|
if(copy_from_user(&kbuf, argp, sizeof(kbuf))) return -EFAULT;
|
|
dbg("M1120_IOCTL_SET_THRESHOLD_LOW(%d)", kbuf);
|
|
p_m1120_data->thrlow = kbuf;
|
|
break;
|
|
case M1120_IOCTL_GET_THRESHOLD_LOW:
|
|
kbuf = p_m1120_data->thrlow;
|
|
dbg("M1120_IOCTL_GET_THRESHOLD_LOW(%d)", kbuf);
|
|
if(copy_to_user(argp, &kbuf, sizeof(kbuf)));
|
|
break;
|
|
default:
|
|
return -ENOTTY;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
static ssize_t m1120_misc_dev_read( struct file *filp, char *buf, size_t count, loff_t *ofs )
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
static ssize_t m1120_misc_dev_write( struct file *filp, const char *buf, size_t count, loff_t *ofs )
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
static unsigned int m1120_misc_dev_poll( struct file *filp, struct poll_table_struct *pwait )
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
/* *************************************************
|
|
sysfs attributes
|
|
************************************************* */
|
|
static ssize_t m1120_enable_show(struct device *dev,
|
|
struct device_attribute *attr, char *buf)
|
|
{
|
|
return sprintf(buf, "%d\n", m1120_get_enable(dev));
|
|
}
|
|
|
|
static ssize_t m1120_enable_store(struct device *dev,
|
|
struct device_attribute *attr,
|
|
const char *buf, size_t count)
|
|
{
|
|
unsigned long enable = simple_strtoul(buf, NULL, 10);
|
|
/***********get calibration data from factory/hcal_data1********************/
|
|
char cal_data[14]; //calibration data readed from factory/hcal_data.
|
|
char s_data[5]; //buffer for storing each rawdata.
|
|
static int c_data[3]; //storing each rawdata.
|
|
struct file *fp;
|
|
mm_segment_t fs;
|
|
loff_t pos;
|
|
int loop1, loop2, loop3;
|
|
/***********get calibration data from factory/hcal_data1********************/
|
|
|
|
if ((enable == 0) || (enable == 1)) {
|
|
m1120_set_enable(dev, enable);
|
|
}
|
|
/***********get calibration data from factory/hcal_data2********************/
|
|
mdelay(10);
|
|
fp = filp_open("/factory/hcal_data", O_RDWR|O_CREAT, 0644);
|
|
if(IS_ERR(fp)){
|
|
printk("open, file error.\n");
|
|
}else{
|
|
|
|
fs = get_fs();
|
|
set_fs(KERNEL_DS);
|
|
pos = 0;
|
|
vfs_read(fp, cal_data, sizeof(cal_data), &pos);
|
|
printk("read: %s\n", cal_data);
|
|
filp_close(fp, NULL);
|
|
set_fs(fs);
|
|
|
|
if(cal_data != NULL){
|
|
loop3 = 0;
|
|
for(loop1 = 0;(cal_data[loop1] != '\0');loop1++){
|
|
for(loop2 = 0;(cal_data[loop1] != '/');loop2++){
|
|
s_data[loop2] = cal_data[loop1];
|
|
if(cal_data[loop1] == '\0')
|
|
break;
|
|
loop1++;
|
|
}
|
|
s_data[loop2] = '\0';
|
|
sscanf(s_data,"%d", &(c_data[loop3++]));
|
|
}
|
|
if((c_data[0] != 0)&&(c_data[1] != 0)&&(c_data[2] != 0)){
|
|
close = c_data[0];
|
|
stand = c_data[1];
|
|
lift = c_data[2];
|
|
}else
|
|
return count;
|
|
|
|
printk("---------------- %d %d %d\n", close, stand, lift);
|
|
}
|
|
}
|
|
open_a = (close + stand)/4 + stand;
|
|
open_b = (stand + lift)/2;
|
|
|
|
p_m1120_data->stscfg[0].max = 511;
|
|
p_m1120_data->stscfg[0].min = close - 10;
|
|
p_m1120_data->stscfg[1].max = close - 10;
|
|
p_m1120_data->stscfg[1].min = open_a;
|
|
p_m1120_data->stscfg[2].max = open_a;
|
|
p_m1120_data->stscfg[2].min = stand + 10;
|
|
p_m1120_data->stscfg[3].max = stand + 10;
|
|
p_m1120_data->stscfg[3].min = stand - 10;
|
|
p_m1120_data->stscfg[4].max = stand - 10;
|
|
p_m1120_data->stscfg[4].min = open_b;
|
|
p_m1120_data->stscfg[5].max = open_b;
|
|
p_m1120_data->stscfg[5].min = lift + 10;
|
|
p_m1120_data->stscfg[6].max = lift + 10;
|
|
p_m1120_data->stscfg[6].min = -511;
|
|
printk("read: %d %d %d \n", close, stand, lift);
|
|
/***********get calibration data from factory/hcal_data2********************/
|
|
|
|
return count;
|
|
}
|
|
|
|
static ssize_t m1120_delay_show(struct device *dev,
|
|
struct device_attribute *attr, char *buf)
|
|
{
|
|
return sprintf(buf, "%d\n", m1120_get_delay(dev));
|
|
}
|
|
|
|
static ssize_t m1120_delay_store(struct device *dev,
|
|
struct device_attribute *attr,
|
|
const char *buf, size_t count)
|
|
{
|
|
unsigned long delay = simple_strtoul(buf, NULL, 10);
|
|
|
|
if (delay > M1120_DELAY_MAX) {
|
|
delay = M1120_DELAY_MAX;
|
|
}
|
|
|
|
m1120_set_delay(dev, delay);
|
|
|
|
return count;
|
|
}
|
|
|
|
static ssize_t m1120_debug_show(struct device *dev,
|
|
struct device_attribute *attr, char *buf)
|
|
{
|
|
return sprintf(buf, "%d\n", m1120_get_debug(dev));
|
|
}
|
|
|
|
static ssize_t m1120_debug_store(struct device *dev,
|
|
struct device_attribute *attr,
|
|
const char *buf, size_t count)
|
|
{
|
|
unsigned long debug = simple_strtoul(buf, NULL, 10);
|
|
|
|
m1120_set_debug(dev, debug);
|
|
|
|
return count;
|
|
}
|
|
|
|
static ssize_t m1120_wake_store(struct device *dev,
|
|
struct device_attribute *attr,
|
|
const char *buf, size_t count)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
/************calibration************/
|
|
static ssize_t m1120_calibration_show(struct device *dev,
|
|
struct device_attribute *attr, char *buf)
|
|
{
|
|
return sprintf(buf, "data[0] = %d, data[90] = %d, data[180] = %d.\n", close, stand, lift);
|
|
}
|
|
|
|
static ssize_t m1120_calibration_store(struct device *dev,
|
|
struct device_attribute *attr,
|
|
const char *buf, size_t count)
|
|
{
|
|
unsigned long cal_angle = simple_strtoul(buf, NULL, 10);
|
|
/***********store calibration data in factory/hcal_data3********************/
|
|
static char buf1[14];
|
|
char buf2[14];
|
|
char flag1[] = "/";
|
|
struct file *fp;
|
|
mm_segment_t fs;
|
|
loff_t pos;
|
|
/***********store calibration data in factory/hcal_data3********************/
|
|
|
|
if(cal_angle == 0){
|
|
m1120_set_calibration(&p_m1120_data->client->dev);
|
|
close = cal_data;
|
|
open_a = (close + stand)/2;
|
|
p_m1120_data->stscfg[0].max = 511;
|
|
p_m1120_data->stscfg[0].min = close - 10;
|
|
p_m1120_data->stscfg[1].max = close - 10;
|
|
p_m1120_data->stscfg[1].min = open_a;
|
|
p_m1120_data->stscfg[2].max = open_a;
|
|
}else if(cal_angle == 1){
|
|
m1120_set_calibration(&p_m1120_data->client->dev);
|
|
stand = cal_data;
|
|
open_a = (close + stand)/2;
|
|
open_b = (stand + lift)/2;
|
|
p_m1120_data->stscfg[1].min = open_a;
|
|
p_m1120_data->stscfg[2].max = open_a;
|
|
p_m1120_data->stscfg[2].min = stand + 10;
|
|
p_m1120_data->stscfg[3].max = stand + 10;
|
|
p_m1120_data->stscfg[3].min = stand - 10;
|
|
p_m1120_data->stscfg[4].max = stand - 10;
|
|
p_m1120_data->stscfg[4].min = open_b;
|
|
p_m1120_data->stscfg[5].max = open_b;
|
|
}else if(cal_angle == 2){
|
|
m1120_set_calibration(&p_m1120_data->client->dev);
|
|
lift = cal_data;
|
|
open_b = (stand + lift)/2;
|
|
p_m1120_data->stscfg[4].min = open_b;
|
|
p_m1120_data->stscfg[5].max = open_b;
|
|
p_m1120_data->stscfg[5].min = lift + 10;
|
|
p_m1120_data->stscfg[6].max = lift + 10;
|
|
p_m1120_data->stscfg[6].min = -511;
|
|
|
|
}else
|
|
printk("------");
|
|
|
|
/***********store calibration data in factory/hcal_data4********************/
|
|
|
|
sprintf(buf2, "%d", close);
|
|
strcat(buf2, flag1);
|
|
strcpy(buf1, buf2);
|
|
sprintf(buf2, "%d", stand);
|
|
strcat(buf2, flag1);
|
|
strcat(buf1, buf2);
|
|
sprintf(buf2, "%d", lift);
|
|
strcat(buf1, buf2);
|
|
|
|
|
|
fp = filp_open("/factory/hcal_data", O_RDWR|O_CREAT, 0644);
|
|
if(IS_ERR(fp)){
|
|
printk("creat file error.\n");
|
|
return -1;
|
|
}
|
|
|
|
fs = get_fs();
|
|
set_fs(KERNEL_DS);
|
|
pos = 0;
|
|
vfs_write(fp, buf1,sizeof(buf1), &pos);
|
|
pos = 0;
|
|
vfs_read(fp, buf1, sizeof(buf1), &pos);
|
|
printk("read: %s\n", buf1);
|
|
filp_close(fp, NULL);
|
|
set_fs(fs);
|
|
/***********store calibration data in factory/hcal_data4********************/
|
|
|
|
return count;
|
|
}
|
|
|
|
static ssize_t m1120_cal_result_show(struct device *dev,
|
|
struct device_attribute *attr, char *buf)
|
|
{
|
|
/*
|
|
int cal_result = 0;
|
|
|
|
if((close - stand) < 45)
|
|
cal_result += 1;
|
|
if((stand - lift) < 80)
|
|
cal_result += 2;
|
|
|
|
|
|
return sprintf(buf, "%d\n", cal_result);
|
|
*/
|
|
/* oujf1 */
|
|
int err = -1;
|
|
|
|
if ((close > 0) && (lift < 0) && (close > stand) && (stand > lift))
|
|
err = 0;
|
|
|
|
return sprintf(buf, "%d\n", err);
|
|
}
|
|
static ssize_t m1120_hall_status_show(struct device *dev,
|
|
struct device_attribute *attr, char *buf)
|
|
{
|
|
return sprintf(buf, "%d\n", p_m1120_data->last_data);
|
|
}
|
|
static DEVICE_ATTR(calibration, S_IRUGO|S_IWUSR|S_IWGRP, m1120_calibration_show, m1120_calibration_store);
|
|
static DEVICE_ATTR(result, S_IRUGO, m1120_cal_result_show, NULL);
|
|
static DEVICE_ATTR(hall_status, S_IRUGO, m1120_hall_status_show, NULL);
|
|
/************calibration***********/
|
|
static DEVICE_ATTR(enable, S_IRUGO|S_IWUSR|S_IWGRP, m1120_enable_show, m1120_enable_store);
|
|
static DEVICE_ATTR(delay, S_IRUGO|S_IWUSR|S_IWGRP, m1120_delay_show, m1120_delay_store);
|
|
static DEVICE_ATTR(debug, S_IRUGO|S_IWUSR|S_IWGRP, m1120_debug_show, m1120_debug_store);
|
|
static DEVICE_ATTR(wake, S_IWUSR|S_IWGRP, NULL, m1120_wake_store);
|
|
|
|
static struct attribute *m1120_attributes[] = {
|
|
&dev_attr_calibration.attr,
|
|
&dev_attr_result.attr,
|
|
&dev_attr_hall_status.attr,
|
|
&dev_attr_enable.attr,
|
|
&dev_attr_delay.attr,
|
|
&dev_attr_debug.attr,
|
|
&dev_attr_wake.attr,
|
|
NULL
|
|
};
|
|
|
|
static struct attribute_group m1120_attribute_group = {
|
|
.attrs = m1120_attributes
|
|
};
|
|
|
|
|
|
/* *************************************************
|
|
i2c client
|
|
************************************************* */
|
|
|
|
int m1120_i2c_drv_probe(struct i2c_client *client, const struct i2c_device_id *id)
|
|
{
|
|
m1120_platform_data_t *p_platform;
|
|
m1120_data_t *p_data;
|
|
int err = 0;
|
|
|
|
dbg_func_in();
|
|
|
|
/* (1) allocation memory for p_m1120_data */
|
|
p_data = kzalloc(sizeof(m1120_data_t), GFP_KERNEL);
|
|
if (!p_data) {
|
|
mxerr(&client->dev, "kernel memory alocation was failed");
|
|
err = -ENOMEM;
|
|
goto error_0;
|
|
}
|
|
|
|
/* (2) init mutex variable */
|
|
mutex_init(&p_data->mtx.enable);
|
|
mutex_init(&p_data->mtx.data);
|
|
|
|
/* (3) config i2c client */
|
|
if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
|
|
mxerr(&client->dev, "i2c_check_functionality was failed");
|
|
err = -ENODEV;
|
|
goto error_1;
|
|
}
|
|
i2c_set_clientdata(client, p_data);
|
|
p_data->client = client;
|
|
|
|
/* (4) get platform data */
|
|
p_platform = client->dev.platform_data;
|
|
if(p_platform) {
|
|
p_data->power_vi2c = p_platform->power_vi2c;
|
|
p_data->power_vdd = p_platform->power_vdd;
|
|
p_data->igpio = p_platform->interrupt_gpio;
|
|
p_data->irq = p_platform->interrupt_irq;
|
|
}
|
|
else {
|
|
p_data->power_vi2c = -1;
|
|
p_data->power_vdd = -1;
|
|
p_data->igpio = 155;
|
|
p_data->irq = gpio_to_irq(155);
|
|
}
|
|
|
|
/* (5) setup interrupt gpio */
|
|
if(p_data->igpio != -1) {
|
|
err = gpio_request(p_data->igpio, "m1120_irq");
|
|
if (err){
|
|
mxerr(&client->dev, "gpio_request was failed(%d)", err);
|
|
goto error_1;
|
|
}
|
|
mxinfo(&client->dev, "gpio_request was success");
|
|
err = gpio_direction_input(p_data->igpio);
|
|
if (err < 0) {
|
|
mxerr(&client->dev, "gpio_direction_input was failed(%d)", err);
|
|
goto error_2;
|
|
}
|
|
mxinfo(&client->dev, "gpio_direction_input was success");
|
|
err = request_irq(p_data->irq, &m1120_irq_handler, IRQF_TRIGGER_FALLING, M1120_IRQ_NAME, 0);
|
|
irq_status = 1;
|
|
}
|
|
|
|
|
|
/* (6) reset and init device */
|
|
err = m1120_init_device(&p_data->client->dev);
|
|
if(err) {
|
|
mxerr(&client->dev, "m1120_init_device was failed(%d)", err);
|
|
goto error_1;
|
|
}
|
|
mxinfo(&client->dev, "%s was found", id->name);
|
|
|
|
/* (7) config work function */
|
|
INIT_DELAYED_WORK(&p_data->work, m1120_work_func);
|
|
|
|
/* (8) init input device */
|
|
err = m1120_input_dev_init(p_data);
|
|
if(err) {
|
|
mxerr(&client->dev, "m1120_input_dev_init was failed(%d)", err);
|
|
goto error_1;
|
|
}
|
|
mxinfo(&client->dev, "%s was initialized", M1120_DRIVER_NAME);
|
|
|
|
/* (9) create sysfs group */
|
|
err = sysfs_create_group(&p_data->input_dev->dev.kobj, &m1120_attribute_group);
|
|
if(err) {
|
|
mxerr(&client->dev, "sysfs_create_group was failed(%d)", err);
|
|
goto error_3;
|
|
}
|
|
|
|
/* (10) register misc device */
|
|
err = misc_register(&m1120_misc_dev);
|
|
if(err) {
|
|
mxerr(&client->dev, "misc_register was failed(%d)", err);
|
|
goto error_4;
|
|
}
|
|
|
|
/* (11) imigrate p_data to p_m1120_data */
|
|
p_m1120_data = p_data;
|
|
dbg("%s : %s was probed.", __func__, M1120_DRIVER_NAME);
|
|
|
|
return 0;
|
|
|
|
error_4:
|
|
sysfs_remove_group(&p_data->input_dev->dev.kobj, &m1120_attribute_group);
|
|
|
|
error_3:
|
|
m1120_input_dev_terminate(p_data);
|
|
|
|
error_2:
|
|
if(p_data->igpio != -1) {
|
|
gpio_free(p_data->igpio);
|
|
}
|
|
|
|
error_1:
|
|
kfree(p_data);
|
|
|
|
error_0:
|
|
|
|
return err;
|
|
}
|
|
|
|
static int m1120_i2c_drv_remove(struct i2c_client *client)
|
|
{
|
|
m1120_data_t *p_data = i2c_get_clientdata(client);
|
|
|
|
free_irq(p_data->irq, NULL);
|
|
m1120_set_enable(&client->dev, 0);
|
|
misc_deregister(&m1120_misc_dev);
|
|
sysfs_remove_group(&p_data->input_dev->dev.kobj, &m1120_attribute_group);
|
|
m1120_input_dev_terminate(p_data);
|
|
if(p_data->igpio!= -1) {
|
|
gpio_free(p_data->igpio);
|
|
}
|
|
kfree(p_data);
|
|
|
|
return 0;
|
|
}
|
|
/*
|
|
static int m1120_i2c_drv_suspend(struct i2c_client *client, pm_message_t mesg)
|
|
{
|
|
m1120_data_t *p_data = i2c_get_clientdata(client);
|
|
|
|
dbg_func_in();
|
|
|
|
mutex_lock(&p_data->mtx.enable);
|
|
|
|
if (m1120_get_enable(&client->dev)) {
|
|
if(p_data->reg.map.intsrs & M1120_DETECTION_MODE_INTERRUPT) {
|
|
m1120_set_operation_mode(&client->dev, OPERATION_MODE_MEASUREMENT);
|
|
} else {
|
|
cancel_delayed_work_sync(&p_data->work);
|
|
m1120_set_detection_mode(&client->dev, M1120_DETECTION_MODE_INTERRUPT);
|
|
}
|
|
}
|
|
|
|
mutex_unlock(&p_data->mtx.enable);
|
|
|
|
dbg_func_out();
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int m1120_i2c_drv_resume(struct i2c_client *client)
|
|
{
|
|
m1120_data_t *p_data = i2c_get_clientdata(client);
|
|
|
|
dbg_func_in();
|
|
|
|
mutex_lock(&p_data->mtx.enable);
|
|
|
|
if (m1120_get_enable(&client->dev)) {
|
|
if(p_data->reg.map.intsrs & M1120_DETECTION_MODE_INTERRUPT) {
|
|
m1120_set_detection_mode(&client->dev, M1120_DETECTION_MODE_POLLING);
|
|
schedule_delayed_work(&p_data->work, msecs_to_jiffies(m1120_get_delay(&client->dev)));
|
|
}
|
|
}
|
|
|
|
mutex_unlock(&p_data->mtx.enable);
|
|
|
|
dbg_func_out();
|
|
|
|
return 0;
|
|
}
|
|
*/
|
|
static const struct i2c_device_id m1120_i2c_drv_id_table[] = {
|
|
{M1120_DRIVER_NAME, 0 },
|
|
{ }
|
|
};
|
|
|
|
static struct i2c_driver m1120_driver = {
|
|
.driver = {
|
|
.name = M1120_DRIVER_NAME,
|
|
},
|
|
.probe = m1120_i2c_drv_probe,
|
|
.remove = m1120_i2c_drv_remove,
|
|
.id_table = m1120_i2c_drv_id_table,
|
|
// .suspend = m1120_i2c_drv_suspend,
|
|
// .resume = m1120_i2c_drv_resume,
|
|
};
|
|
|
|
static int __init m1120_platform_init(void)
|
|
{
|
|
int i2c_busnum = 5;
|
|
struct i2c_board_info i2c_info;
|
|
void *pdata = NULL;
|
|
|
|
//intel_scu_ipc_msic_vprog3(1);
|
|
|
|
memset(&i2c_info, 0, sizeof(i2c_info));
|
|
strncpy(i2c_info.type, M1120_DRIVER_NAME, strlen(M1120_DRIVER_NAME));
|
|
|
|
i2c_info.addr = 0x0C;
|
|
// i2c_info.interrupt_gpio = 25;
|
|
i2c_info.irq = gpio_to_irq(155);
|
|
|
|
pr_info("I2C bus = %d, name = %16.16s, irq = 0x%2x, addr = 0x%x\n",
|
|
i2c_busnum,
|
|
i2c_info.type,
|
|
i2c_info.irq,
|
|
i2c_info.addr);
|
|
|
|
// pdata = m1120_platform_data(&i2c_info);
|
|
|
|
if(pdata != NULL)
|
|
i2c_info.platform_data = pdata;
|
|
else
|
|
printk("%s, pdata is NULL\n", __func__);
|
|
|
|
return i2c_register_board_info(i2c_busnum, &i2c_info, 1);
|
|
}
|
|
|
|
fs_initcall(m1120_platform_init);
|
|
|
|
static int __init m1120_driver_init(void)
|
|
{
|
|
printk(KERN_INFO "%s\n", __func__);
|
|
return i2c_add_driver(&m1120_driver);
|
|
}
|
|
module_init(m1120_driver_init);
|
|
|
|
static void __exit m1120_driver_exit(void)
|
|
{
|
|
printk(KERN_INFO "%s\n", __func__);
|
|
i2c_del_driver(&m1120_driver);
|
|
}
|
|
module_exit(m1120_driver_exit);
|
|
|
|
MODULE_AUTHOR("shpark <seunghwan.park@magnachip.com>");
|
|
MODULE_VERSION(M1120_DRIVER_VERSION);
|
|
MODULE_DESCRIPTION("M1120 hallswitch driver");
|
|
MODULE_LICENSE("GPL");
|
|
|