/* qmc6308.c - qmc6308 compass driver
*
*
* 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 <cust_mag.h>
#include "qmc6308.h"
#include "mag.h"
#ifdef CONFIG_HAS_EARLYSUSPEND
#include <linux/earlysuspend.h>
#endif
/*----------------------------------------------------------------------------*/
#define DEBUG 1
#define qmc6308_DEV_NAME "qmc6308"
#define DRIVER_VERSION "driver version V1.0"
/*----------------------------------------------------------------------------*/
#define QMC6308_STR_BUFSIZE 32
#define QMC6308_RW_BUFSIZE 16
#define qmc6308_AXIS_X 0
#define qmc6308_AXIS_Y 1
#define qmc6308_AXIS_Z 2
#define qmc6308_AXES_NUM 3
#define qmc6308_DEFAULT_DELAY 100
#define MSE_TAG "[QMC-Msensor] "
#define MSE_FUN(f) pr_info(MSE_TAG"%s\n", __FUNCTION__)
#define MSE_ERR(fmt, args...) pr_err(MSE_TAG"%s %d : "fmt, __FUNCTION__, __LINE__, ##args)
#define MSE_LOG(fmt, args...) pr_info(MSE_TAG fmt, ##args)
static struct i2c_client *this_client = NULL;
static short qmcd_delay = qmc6308_DEFAULT_DELAY;
//static struct mutex sensor_data_mutex;
static struct mutex read_i2c_xyz;
SENSOR_MASK mask = {
.mask0 = 0x80,
.mask1 = 0xA0,
.mask2 = 0xB0,
.mask3 = 0xC0,
.maskid =0x00,
};
static atomic_t open_flag = ATOMIC_INIT(0);
static unsigned char v_open_flag = 0x00;
//static unsigned char maskid = 0x00;
/*----------------------------------------------------------------------------*/
static const struct i2c_device_id qmc6308_i2c_id[] = {{qmc6308_DEV_NAME,0},{}};
static struct mag_hw mag_cust;
static struct mag_hw *qst_hw = &mag_cust;
/* For driver get cust info */
struct mag_hw *get_cust_mag(void)
{
return &mag_cust;
}
/*----------------------------------------------------------------------------*/
static int qmc6308_i2c_probe(struct i2c_client *client, const struct i2c_device_id *id);
static int qmc6308_i2c_remove(struct i2c_client *client);
static int qmc6308_suspend(struct device *dev);
static int qmc6308_resume(struct device *dev);
static int qmc6308_i2c_detect(struct i2c_client *client, struct i2c_board_info *info);
static int qmc6308_batch(int flag, int64_t samplingPeriodNs, int64_t maxBatchReportLatencyNs);
static int qmc6308_flush(void);
static int qmc6308_m_get_data(int *x, int *y, int *z, int *status);
/*----------------------------------------------------------------------------*/
typedef enum {
QMC_FUN_DEBUG = 0x01,
QMC_DATA_DEBUG = 0x02,
QMC_HWM_DEBUG = 0x04,
QMC_CTR_DEBUG = 0x08,
QMC_I2C_DEBUG = 0x10,
} QMC_TRC;
/*----------------------------------------------------------------------------*/
struct qmc6308_i2c_data {
struct i2c_client *client;
struct mag_hw hw;
struct hwmsen_convert cvt;
atomic_t layout;
atomic_t trace;
short sensitivity;
#if defined(CONFIG_HAS_EARLYSUSPEND)
struct early_suspend early_drv;
#endif
};
#define DATA_AVG_DELAY 6
/*----------------------------------------------------------------------------*/
#ifdef CONFIG_OF
static const struct of_device_id mag_of_match[] = {
{ .compatible = "mediatek,msensor", },
{},
};
#endif
#ifdef CONFIG_PM_SLEEP
static const struct dev_pm_ops qmc6308_pm_ops = {
SET_SYSTEM_SLEEP_PM_OPS(qmc6308_suspend, qmc6308_resume)
};
#endif
static struct i2c_driver qmc6308_i2c_driver = {
.driver = {
.name = qmc6308_DEV_NAME,
#ifdef CONFIG_PM_SLEEP
.pm = &qmc6308_pm_ops,
#endif
#ifdef CONFIG_OF
.of_match_table = mag_of_match,
#endif
},
.probe = qmc6308_i2c_probe,
.remove = qmc6308_i2c_remove,
.detect = qmc6308_i2c_detect,
.id_table = qmc6308_i2c_id,
};
static int qmc6308_local_init(void);
static int qmc6308_local_remove(void);
static int qmc6308_init_flag = -1; // 0<==>OK -1 <==> fail
static struct mag_init_info qmc6308_init_info = {
.name = qmc6308_DEV_NAME,
.init = qmc6308_local_init,
.uninit = qmc6308_local_remove,
};
static int mag_i2c_read_block(struct i2c_client *client, u8 addr, u8 *data, u8 len)
{
int err = 0;
u8 beg = addr;
struct i2c_msg msgs[2] = { {0}, {0} };
mutex_lock(&read_i2c_xyz);
msgs[0].addr = client->addr;
msgs[0].flags = 0;
msgs[0].len = 1;
msgs[0].buf = &beg;
msgs[1].addr = client->addr;
msgs[1].flags = I2C_M_RD;
msgs[1].len = len;
msgs[1].buf = data;
if (!client) {
mutex_unlock(&read_i2c_xyz);
return -EINVAL;
} else if (len > C_I2C_FIFO_SIZE) {
mutex_unlock(&read_i2c_xyz);
MSE_ERR(" length %d exceeds %d\n", len, C_I2C_FIFO_SIZE);
return -EINVAL;
}
err = i2c_transfer(client->adapter, msgs, sizeof(msgs) / sizeof(msgs[0]));
if (err != 2) {
MSE_ERR("i2c_transfer error: (%d %p %d) %d\n", addr, data, len, err);
err = -EIO;
} else {
err = 0;
}
mutex_unlock(&read_i2c_xyz);
return err;
}
static int mag_i2c_write_block(struct i2c_client *client, u8 addr, u8 *data, u8 len)
{
/*because address also occupies one byte, the maximum length for write is 7 bytes */
int err = 0, idx = 0, num = 0;
char buf[C_I2C_FIFO_SIZE];
err = 0;
mutex_lock(&read_i2c_xyz);
if (!client) {
mutex_unlock(&read_i2c_xyz);
return -EINVAL;
} else if (len >= C_I2C_FIFO_SIZE) {
mutex_unlock(&read_i2c_xyz);
MSE_ERR(" length %d exceeds %d\n", len, C_I2C_FIFO_SIZE);
return -EINVAL;
}
num = 0;
buf[num++] = addr;
for (idx = 0; idx < len; idx++)
buf[num++] = data[idx];
err = i2c_master_send(client, buf, num);
if (err < 0) {
mutex_unlock(&read_i2c_xyz);
MSE_ERR("send command error!!\n");
return -EFAULT;
}
mutex_unlock(&read_i2c_xyz);
return err;
}
static int I2C_RxData(char *rxData, int length)
{
struct i2c_client *client = this_client;
int res = 0;
char addr = rxData[0];
if ((rxData == NULL) || (length < 1))
return -EINVAL;
res = mag_i2c_read_block(client, addr, rxData, length);
if (res < 0)
return -1;
return 0;
}
static int I2C_TxData(char *txData, int length)
{
struct i2c_client *client = this_client;
int res = 0;
char addr = txData[0];
u8 *buff = &txData[1];
if ((txData == NULL) || (length < 2))
return -EINVAL;
res = mag_i2c_write_block(client, addr, buff, (length - 1));
if (res < 0)
return -1;
return 0;
}
/* X,Y and Z-axis magnetometer data readout
* param *mag pointer to \ref qmc6308_t structure for x,y,z data readout
* note data will be read by multi-byte protocol into a 6 byte structure
*/
static int qmc6308_read_mag_xyz(int *data)
{
int res;
unsigned char mag_data[6];
int hw_d[3] = {0};
int t1 = 0;
unsigned char rdy = 0;
struct i2c_client *client = this_client;
struct qmc6308_i2c_data *clientdata = i2c_get_clientdata(client);
MSE_FUN();
/* Check status register for data availability */
while(!(rdy & 0x01) && (t1 < 3)){
rdy = QMC6308_STATUS_REG;
res = I2C_RxData(&rdy,1);
t1 ++;
MSE_LOG("qmc6308 Status register is (%02X)\n", rdy);
}
mag_data[0] = QMC6308_DATA_OUT_X_LSB_REG;
res = I2C_RxData(mag_data, 6);
if(res != 0)
{
return -EFAULT;
}
MSE_LOG("qmc6308 mag_data[%02x, %02x, %02x, %02x, %02x, %02x]\n",
mag_data[0], mag_data[1], mag_data[2],
mag_data[3], mag_data[4], mag_data[5]);
hw_d[0] = (short)(((mag_data[1]) << 8) | mag_data[0]);
hw_d[1] = (short)(((mag_data[3]) << 8) | mag_data[2]);
hw_d[2] = (short)(((mag_data[5]) << 8) | mag_data[4]);
//Unit:mG 1G = 100uT = 1000mG
hw_d[0] = hw_d[0] * 1000 / clientdata->sensitivity;
hw_d[1] = hw_d[1] * 1000 / clientdata->sensitivity;
hw_d[2] = hw_d[2] * 1000 / clientdata->sensitivity;
MSE_LOG("Hx=%d, Hy=%d, Hz=%d\n",hw_d[0],hw_d[1],hw_d[2]);
data[qmc6308_AXIS_X] = clientdata->cvt.sign[qmc6308_AXIS_X]*hw_d
评论0