blob: 3b701f59796d5b02c566eada54cb065794dc800f [file] [log] [blame]
/*
* Copyright (C) 2013 Samsung Electronics. All rights reserved.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* version 2 as published by the Free Software Foundation.
*
* 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., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA
*/
#include <linux/fs.h>
#include <linux/uaccess.h>
#include <linux/module.h>
#include <linux/i2c.h>
#include <linux/errno.h>
#include <linux/input.h>
#include <linux/workqueue.h>
#include <linux/slab.h>
#include <linux/delay.h>
#include <linux/completion.h>
#include <linux/gpio.h>
#include <linux/of_gpio.h>
#include <linux/regulator/consumer.h>
#include <linux/sensor/sensors_core.h>
#include "ak09916c_reg.h"
/* Rx buffer size. i.e ST,TMPS,H1X,H1Y,H1Z*/
#define SENSOR_DATA_SIZE 9
#define AK09916C_DEFAULT_DELAY 200000000LL
#define AK09916C_DRDY_TIMEOUT_MS 100
#define AK09916C_WIA1_VALUE 0x48
#ifdef CONFIG_SENSORS_AK09918C
#define AK09916C_WIA2_VALUE 0x0C
#else
#define AK09916C_WIA2_VALUE 0x09
#endif
#define I2C_M_WR 0 /* for i2c Write */
#define I2c_M_RD 1 /* for i2c Read */
#define VENDOR_NAME "AKM"
#ifdef CONFIG_SENSORS_AK09918C
#define MODEL_NAME "AK09918C"
#else
#define MODEL_NAME "AK09916C"
#endif
#define MODULE_NAME "magnetic_sensor"
#define AK09916C_TOP_LOWER_RIGHT 0
#define AK09916C_TOP_LOWER_LEFT 1
#define AK09916C_TOP_UPPER_LEFT 2
#define AK09916C_TOP_UPPER_RIGHT 3
#define AK09916C_BOTTOM_LOWER_RIGHT 4
#define AK09916C_BOTTOM_LOWER_LEFT 5
#define AK09916C_BOTTOM_UPPER_LEFT 6
#define AK09916C_BOTTOM_UPPER_RIGHT 7
#define SELF_TEST_HX_MIN -200
#define SELF_TEST_HX_MAX 200
#define SELF_TEST_HY_MIN -200
#define SELF_TEST_HY_MAX 200
#define SELF_TEST_HZ_MIN -1000
#ifdef CONFIG_SENSORS_AK09918C
#define SELF_TEST_HZ_MAX -150
#else
#define SELF_TEST_HZ_MAX -200
#endif
#define MAG_LOG_TIME 15 /* 15 sec */
struct ak09916c_v {
union {
s16 v[3];
struct {
s16 x;
s16 y;
s16 z;
};
};
};
struct ak09916c_p {
struct i2c_client *client;
struct input_dev *input;
struct device *factory_device;
struct ak09916c_v magdata;
struct mutex lock;
struct mutex enable_lock;
ktime_t delay;
struct work_struct mag_work;
struct workqueue_struct *wq;
struct hrtimer mag_timer;
int time_count;
atomic_t enable;
#if defined(CONFIG_SENSORS_SW_RESET)
int reset_state;
#endif
int reset_flag;
u8 asa[3];
u32 chip_pos;
u32 si_p[27];
};
static int ak09916c_i2c_read(struct i2c_client *client,
unsigned char reg_addr, unsigned char *buf)
{
int ret;
struct i2c_msg msg[2];
#if defined(CONFIG_SENSORS_SW_RESET)
struct ak09916c_p *data =
(struct ak09916c_p *)i2c_get_clientdata(client);
if (data->reset_state)
return -EPERM;
#endif
msg[0].addr = client->addr;
msg[0].flags = I2C_M_WR;
msg[0].len = 1;
msg[0].buf = &reg_addr;
msg[1].addr = client->addr;
msg[1].flags = I2C_M_RD;
msg[1].len = 1;
msg[1].buf = buf;
ret = i2c_transfer(client->adapter, msg, 2);
if (ret < 0) {
SENSOR_ERR("i2c bus read error %d\n", ret);
return ret;
}
return 0;
}
static int ak09916c_i2c_write(struct i2c_client *client,
unsigned char reg_addr, unsigned char buf)
{
int ret;
struct i2c_msg msg;
unsigned char w_buf[2];
#if defined(CONFIG_SENSORS_SW_RESET)
struct ak09916c_p *data =
(struct ak09916c_p *)i2c_get_clientdata(client);
if (data->reset_state) {
SENSOR_INFO("reset_state %d start!!!\n", data->reset_state);
return -EPERM;
}
#endif
w_buf[0] = reg_addr;
w_buf[1] = buf;
msg.addr = client->addr;
msg.flags = I2C_M_WR;
msg.len = 2;
msg.buf = (char *)w_buf;
ret = i2c_transfer(client->adapter, &msg, 1);
if (ret < 0) {
SENSOR_ERR("i2c bus write error %d\n", ret);
return ret;
}
return 0;
}
static int ak09916c_i2c_read_block(struct i2c_client *client,
unsigned char reg_addr, unsigned char *buf, unsigned char len)
{
int ret;
struct i2c_msg msg[2];
#if defined(CONFIG_SENSORS_SW_RESET)
struct ak09916c_p *data =
(struct ak09916c_p *)i2c_get_clientdata(client);
if (data->reset_state)
return -EPERM;
#endif
msg[0].addr = client->addr;
msg[0].flags = I2C_M_WR;
msg[0].len = 1;
msg[0].buf = &reg_addr;
msg[1].addr = client->addr;
msg[1].flags = I2C_M_RD;
msg[1].len = len;
msg[1].buf = buf;
ret = i2c_transfer(client->adapter, msg, 2);
if (ret < 0) {
SENSOR_ERR("i2c bus read error %d\n", ret);
return ret;
}
return 0;
}
static int ak09916c_ecs_set_mode_power_down(struct ak09916c_p *data)
{
unsigned char reg;
int ret;
reg = AK09916C_MODE_POWERDOWN;
ret = ak09916c_i2c_write(data->client, AK09916C_REG_CNTL2, reg);
return ret;
}
static int ak09916c_ecs_set_mode(struct ak09916c_p *data, char mode)
{
u8 reg;
int ret;
switch (mode & 0x1F) {
case AK09916C_MODE_SNG_MEASURE:
reg = AK09916C_MODE_SNG_MEASURE;
ret = ak09916c_i2c_write(data->client, AK09916C_REG_CNTL2, reg);
break;
case AK09916C_MODE_POWERDOWN:
ret = ak09916c_ecs_set_mode_power_down(data);
break;
case AK09916C_MODE_SELF_TEST:
reg = AK09916C_MODE_SELF_TEST;
ret = ak09916c_i2c_write(data->client, AK09916C_REG_CNTL2, reg);
break;
default:
return -EINVAL;
}
if (ret < 0)
return ret;
/* Wait at least 300us after changing mode. */
udelay(100);
return 0;
}
static int ak09916c_read_mag_xyz(struct ak09916c_p *data,
struct ak09916c_v *mag)
{
u8 temp[SENSOR_DATA_SIZE] = {0, };
int ret = 0, retries = 0;
mutex_lock(&data->lock);
ret = ak09916c_ecs_set_mode(data, AK09916C_MODE_SNG_MEASURE);
if (ret < 0)
goto exit_i2c_read_err;
if(!atomic_read(&data->enable))
udelay(10000);
again:
ret = ak09916c_i2c_read(data->client, AK09916C_REG_ST1, &temp[0]);
if (ret < 0)
goto exit_i2c_read_err;
/* Check ST bit */
if (!(temp[0] & 0x01)) {
if ((retries++ < 3) && (temp[0] == 0)) {
SENSOR_INFO("Data not ready!\n");
goto again;
} else {
// If data is not ready to read, store previous data to avoid event gap
mag->x = data->magdata.x;
mag->y = data->magdata.y;
mag->z = data->magdata.z;
ret = 0;
goto exit;
}
}
/* Get measurement data from AK09918 */
/* (HXL + HXH) + (HYL + HYH) + (HZL + HZH) + TMPS + ST2 = 8bytes */
ret = ak09916c_i2c_read_block(data->client, AK09916C_REG_ST1 + 1,
&temp[1], SENSOR_DATA_SIZE - 1);
if (ret < 0)
goto exit_i2c_read_err;
mag->x = temp[1] | (temp[2] << 8);
mag->y = temp[3] | (temp[4] << 8);
mag->z = temp[5] | (temp[6] << 8);
remap_sensor_data(mag->v, data->chip_pos);
goto exit;
exit_i2c_read_err:
SENSOR_ERR("I2C read fail, ST1 = %u, ST2 = %u\n", temp[0], temp[8]);
exit:
mutex_unlock(&data->lock);
return ret;
}
static enum hrtimer_restart ak09916c_timer_func(struct hrtimer *timer)
{
struct ak09916c_p *data = container_of(timer,
struct ak09916c_p, mag_timer);
if (!work_pending(&data->mag_work))
queue_work(data->wq, &data->mag_work);
hrtimer_forward_now(&data->mag_timer, data->delay);
return HRTIMER_RESTART;
}
static void ak09916c_work_func(struct work_struct *work)
{
int ret;
struct ak09916c_v mag;
struct ak09916c_p *data = container_of(work,
struct ak09916c_p, mag_work);
#ifdef CONFIG_SENSORS_SW_RESET
if (data->reset_state) {
SENSOR_ERR("reset state (%d)\n", data->reset_state);
return;
}
#endif
ret = ak09916c_read_mag_xyz(data, &mag);
if (ret >= 0) {
input_report_rel(data->input, REL_X, mag.x);
input_report_rel(data->input, REL_Y, mag.y);
input_report_rel(data->input, REL_Z, mag.z);
if (data->reset_flag) {
SENSOR_INFO("Magnetic cal reset!\n");
input_report_rel(data->input, REL_RZ, data->reset_flag);
data->reset_flag = 0;
}
input_sync(data->input);
data->magdata = mag;
}
if ((ktime_to_ns(data->delay) * data->time_count)
>= ((int64_t)MAG_LOG_TIME * NSEC_PER_SEC)) {
SENSOR_INFO("x = %d, y = %d, z = %d\n",
mag.x, mag.y, mag.z);
data->time_count = 0;
} else {
data->time_count++;
}
}
static void ak09916c_set_enable(struct ak09916c_p *data, int enable)
{
int pre_enable = atomic_read(&data->enable);
if (enable) {
if (pre_enable == 0) {
ak09916c_ecs_set_mode(data, AK09916C_MODE_SNG_MEASURE);
hrtimer_start(&data->mag_timer, data->delay,
HRTIMER_MODE_REL);
atomic_set(&data->enable, 1);
}
} else {
if (pre_enable == 1) {
hrtimer_cancel(&data->mag_timer);
cancel_work_sync(&data->mag_work);
ak09916c_ecs_set_mode(data, AK09916C_MODE_POWERDOWN);
atomic_set(&data->enable, 0);
}
}
}
static ssize_t ak09916c_enable_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct ak09916c_p *data = dev_get_drvdata(dev);
return snprintf(buf, PAGE_SIZE, "%d\n", atomic_read(&data->enable));
}
static ssize_t ak09916c_enable_store(struct device *dev,
struct device_attribute *attr, const char *buf, size_t size)
{
u8 enable;
int ret;
struct ak09916c_p *data = dev_get_drvdata(dev);
ret = kstrtou8(buf, 2, &enable);
if (ret) {
SENSOR_ERR("Invalid Argument\n");
return ret;
}
SENSOR_INFO("new_value = %u\n", enable);
#if defined(CONFIG_SENSORS_SW_RESET)
if (data->reset_state) {
SENSOR_INFO("sw reset come enable is = %u\n", enable);
atomic_set(&data->enable, enable);
return size;
}
#endif
mutex_lock(&data->enable_lock);
if ((enable == 0) || (enable == 1))
ak09916c_set_enable(data, (int)enable);
mutex_unlock(&data->enable_lock);
return size;
}
static ssize_t ak09916c_delay_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct ak09916c_p *data = dev_get_drvdata(dev);
return snprintf(buf, PAGE_SIZE, "%lld\n", ktime_to_ns(data->delay));
}
static ssize_t ak09916c_delay_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t size)
{
int ret;
int64_t delay;
struct ak09916c_p *data = dev_get_drvdata(dev);
ret = kstrtoll(buf, 10, &delay);
if (ret) {
SENSOR_ERR("Invalid Argument\n");
return ret;
}
mutex_lock(&data->enable_lock);
data->delay = ns_to_ktime(delay);
mutex_unlock(&data->enable_lock);
SENSOR_INFO("poll_delay = %lld\n", delay);
return size;
}
static DEVICE_ATTR(poll_delay, S_IRUGO | S_IWUSR | S_IWGRP,
ak09916c_delay_show, ak09916c_delay_store);
static DEVICE_ATTR(enable, S_IRUGO | S_IWUSR | S_IWGRP,
ak09916c_enable_show, ak09916c_enable_store);
static struct attribute *ak09916c_attributes[] = {
&dev_attr_poll_delay.attr,
&dev_attr_enable.attr,
NULL
};
static struct attribute_group ak09916c_attribute_group = {
.attrs = ak09916c_attributes
};
static int ak09916c_selftest(struct ak09916c_p *data, int *dac_ret, int *sf)
{
u8 temp[SENSOR_DATA_SIZE], reg;
s16 x, y, z;
int retry_count = 0;
int ready_count = 0;
int ret;
data->reset_flag = 1;
retry:
mutex_lock(&data->lock);
/* power down */
*dac_ret = ak09916c_ecs_set_mode(data, AK09916C_MODE_POWERDOWN);
*dac_ret += ak09916c_i2c_read(data->client, AK09916C_REG_CNTL2, &reg);
/* read device info */
ak09916c_i2c_read_block(data->client, AK09916C_REG_WIA1, temp, 2);
SENSOR_INFO("device id = 0x%x, info = 0x%x\n", temp[0], temp[1]);
/* start self test */
reg = AK09916C_MODE_SELF_TEST;
ak09916c_i2c_write(data->client, AK09916C_REG_CNTL2, reg);
/* wait for data ready */
while (ready_count < 10) {
usleep_range(20000, 21000);
ret = ak09916c_i2c_read(data->client, AK09916C_REG_ST1, &reg);
if ((reg == 1) && (ret == 0))
break;
ready_count++;
}
/* Get measurement data from AK09918 */
/* (HXL + HXH) + (HYL + HYH) + (HZL + HZH) + TMPS + ST2 = 8bytes */
ak09916c_i2c_read_block(data->client, AK09916C_REG_HXL,
temp, SENSOR_DATA_SIZE - 1);
mutex_unlock(&data->lock);
x = temp[0] | (temp[1] << 8);
y = temp[2] | (temp[3] << 8);
z = temp[4] | (temp[5] << 8);
SENSOR_INFO("self test x = %d, y = %d, z = %d\n", x, y, z);
if ((x >= SELF_TEST_HX_MIN) && (x <= SELF_TEST_HX_MAX))
SENSOR_INFO("x passed self test, %d<=x<=%d\n", SELF_TEST_HX_MIN, SELF_TEST_HX_MAX);
else
SENSOR_INFO("x failed self test, %d<=x<=%d\n", SELF_TEST_HX_MIN, SELF_TEST_HX_MAX);
if ((y >= SELF_TEST_HY_MIN) && (y <= SELF_TEST_HY_MAX))
SENSOR_INFO("y passed self test, %d<=y<=%d\n", SELF_TEST_HY_MIN, SELF_TEST_HY_MAX);
else
SENSOR_INFO("y failed self test, %d<=y<=%d\n", SELF_TEST_HY_MIN, SELF_TEST_HY_MAX);
if ((z >= SELF_TEST_HZ_MIN) && (z <= SELF_TEST_HZ_MAX))
SENSOR_INFO("z passed self test, %d<=z<=%d\n", SELF_TEST_HZ_MIN, SELF_TEST_HZ_MAX);
else
SENSOR_INFO("z failed self test, %d<=z<=%d\n", SELF_TEST_HZ_MIN, SELF_TEST_HZ_MAX);
sf[0] = x;
sf[1] = y;
sf[2] = z;
if (((x >= SELF_TEST_HX_MIN) && (x <= SELF_TEST_HX_MAX)) &&
((y >= SELF_TEST_HY_MIN) && (y <= SELF_TEST_HY_MAX)) &&
((z >= SELF_TEST_HZ_MIN) && (z <= SELF_TEST_HZ_MAX))) {
SENSOR_INFO("successful.\n");
ret = 0;
} else {
if (retry_count < 5) {
retry_count++;
pr_warn("############################################");
pr_warn("%s, retry_count=%d\n", __func__, retry_count);
pr_warn("############################################");
goto retry;
} else {
SENSOR_ERR("failed.\n");
ret = -1;
}
}
return ret;
}
static ssize_t ak09916c_vendor_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
return snprintf(buf, PAGE_SIZE, "%s\n", VENDOR_NAME);
}
static ssize_t ak09916c_name_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
return snprintf(buf, PAGE_SIZE, "%s\n", MODEL_NAME);
}
static ssize_t ak09916c_get_asa(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct ak09916c_p *data = dev_get_drvdata(dev);
return snprintf(buf, PAGE_SIZE, "%u,%u,%u\n",
data->asa[0], data->asa[1], data->asa[2]);
}
static ssize_t ak09916c_dhr_sensor_info_store(struct device *dev,
struct device_attribute *attr, const char *buf, size_t size)
{
int i = 0;
struct ak09916c_p *data = dev_get_drvdata(dev);
if (sscanf(buf,
"%u %u %u %u %u %u %u %u %u "\
"%u %u %u %u %u %u %u %u %u "\
"%u %u %u %u %u %u %u %u %u",
&data->si_p[0], &data->si_p[1], &data->si_p[2], &data->si_p[3],
&data->si_p[4], &data->si_p[5], &data->si_p[6], &data->si_p[7],
&data->si_p[8], &data->si_p[9], &data->si_p[10], &data->si_p[11],
&data->si_p[12], &data->si_p[13], &data->si_p[14], &data->si_p[15],
&data->si_p[16], &data->si_p[17], &data->si_p[18], &data->si_p[19],
&data->si_p[20], &data->si_p[21], &data->si_p[22], &data->si_p[23],
&data->si_p[24], &data->si_p[25], &data->si_p[26]) != 27) {
SENSOR_ERR("The number of data are wrong\n");
for (i = 0; i < 27; i++) {
data->si_p[i] = 0;
}
}
return size;
}
static ssize_t ak09916c_dhr_sensor_info_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct ak09916c_p *data = dev_get_drvdata(dev);
return snprintf(buf, PAGE_SIZE,
"\"SI_PARAMETER\":\""\
"%u %u %u %u %u %u %u %u %u "\
"%u %u %u %u %u %u %u %u %u "\
"%u %u %u %u %u %u %u %u %u\"\n",
data->si_p[0], data->si_p[1], data->si_p[2], data->si_p[3], data->si_p[4],
data->si_p[5], data->si_p[6], data->si_p[7], data->si_p[8], data->si_p[9],
data->si_p[10], data->si_p[11], data->si_p[12], data->si_p[13], data->si_p[14],
data->si_p[15], data->si_p[16], data->si_p[17], data->si_p[18], data->si_p[19],
data->si_p[20], data->si_p[21], data->si_p[22], data->si_p[23], data->si_p[24],
data->si_p[25], data->si_p[26]);
}
static ssize_t ak09916c_get_selftest(struct device *dev,
struct device_attribute *attr, char *buf)
{
int status, dac_ret = -1, adc_ret = -1;
int sf_ret, sf[3] = {0,}, retries;
struct ak09916c_v mag;
struct ak09916c_p *data = dev_get_drvdata(dev);
/* STATUS */
if ((data->asa[0] == 0) | (data->asa[0] == 0xff)
| (data->asa[1] == 0) | (data->asa[1] == 0xff)
| (data->asa[2] == 0) | (data->asa[2] == 0xff))
status = -1;
else
status = 0;
if (atomic_read(&data->enable) == 1) {
hrtimer_cancel(&data->mag_timer);
cancel_work_sync(&data->mag_work);
ak09916c_ecs_set_mode(data, AK09916C_MODE_POWERDOWN);
}
sf_ret = ak09916c_selftest(data, &dac_ret, sf);
for (retries = 0; retries < 5; retries++) {
if (ak09916c_read_mag_xyz(data, &mag) == 0) {
if ((mag.x < 6500) && (mag.x > -6500)
&& (mag.y < 6500) && (mag.y > -6500)
&& (mag.z < 6500) && (mag.z > -6500))
adc_ret = 0;
else
SENSOR_ERR("adc specout %d, %d, %d\n",
mag.x, mag.y, mag.z);
break;
}
SENSOR_ERR("adc retries %d", retries);
}
if (atomic_read(&data->enable) == 1) {
ak09916c_ecs_set_mode(data, AK09916C_MODE_SNG_MEASURE);
hrtimer_start(&data->mag_timer, data->delay,
HRTIMER_MODE_REL);
}
return snprintf(buf, PAGE_SIZE, "%d,%d,%d,%d,%d,%d,%d,%d,%d,%d\n",
status, sf_ret, sf[0], sf[1], sf[2], dac_ret,
adc_ret, mag.x, mag.y, mag.z);
}
static ssize_t ak09916c_check_registers(struct device *dev,
struct device_attribute *attr, char *buf)
{
u8 temp[13];
struct ak09916c_p *data = dev_get_drvdata(dev);
mutex_lock(&data->lock);
/* power down */
ak09916c_ecs_set_mode(data, AK09916C_MODE_POWERDOWN);
/* get the value */
ak09916c_i2c_read_block(data->client, AK09916C_REG_WIA1, temp, 13);
mutex_unlock(&data->lock);
return snprintf(buf, PAGE_SIZE,
"%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d\n",
temp[0], temp[1], temp[2], temp[3], temp[4], temp[5],
temp[6], temp[7], temp[8], temp[9], temp[10], temp[11],
temp[12]);
}
static ssize_t ak09916c_check_cntl(struct device *dev,
struct device_attribute *attr, char *buf)
{
u8 reg;
int ret = 0;
struct ak09916c_p *data = dev_get_drvdata(dev);
mutex_lock(&data->lock);
/* power down */
ret = ak09916c_ecs_set_mode(data, AK09916C_MODE_POWERDOWN);
ret += ak09916c_i2c_read(data->client, AK09916C_REG_CNTL2, &reg);
mutex_unlock(&data->lock);
return snprintf(buf, PAGE_SIZE, "%s\n",
(((reg == AK09916C_MODE_POWERDOWN) &&
(ret == 0)) ? "OK" : "NG"));
}
static ssize_t ak09916c_get_status(struct device *dev,
struct device_attribute *attr, char *buf)
{
bool success;
struct ak09916c_p *data = dev_get_drvdata(dev);
if ((data->asa[0] == 0) | (data->asa[0] == 0xff)
| (data->asa[1] == 0) | (data->asa[1] == 0xff)
| (data->asa[2] == 0) | (data->asa[2] == 0xff))
success = false;
else
success = true;
return snprintf(buf, PAGE_SIZE, "%s\n", (success ? "OK" : "NG"));
}
static ssize_t ak09916c_adc(struct device *dev,
struct device_attribute *attr, char *buf)
{
bool success = false;
int ret;
struct ak09916c_p *data = dev_get_drvdata(dev);
struct ak09916c_v mag = data->magdata;
if (atomic_read(&data->enable) == 1) {
success = true;
usleep_range(20000, 21000);
goto exit;
}
ret = ak09916c_read_mag_xyz(data, &mag);
if (ret < 0)
success = false;
else
success = true;
data->magdata = mag;
exit:
return snprintf(buf, PAGE_SIZE, "%s,%d,%d,%d\n",
(success ? "OK" : "NG"), mag.x, mag.y, mag.z);
}
static ssize_t ak09916c_raw_data_write(struct device *dev,
struct device_attribute *attr, const char *buf, size_t size)
{
u8 enable;
int ret;
struct ak09916c_p *data = dev_get_drvdata(dev);
ret = kstrtou8(buf, 2, &enable);
if (ret) {
SENSOR_ERR("Invalid Argument\n");
return size;
}
SENSOR_INFO("%u\n", enable);
if (enable == 1)
data->reset_flag = 1;
return size;
}
static ssize_t ak09916c_raw_data_read(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct ak09916c_p *data = dev_get_drvdata(dev);
struct ak09916c_v mag = data->magdata;
if (atomic_read(&data->enable) == 1) {
usleep_range(20000, 21000);
goto exit;
}
ak09916c_read_mag_xyz(data, &mag);
data->magdata = mag;
exit:
return snprintf(buf, PAGE_SIZE, "%d,%d,%d\n", mag.x, mag.y, mag.z);
}
static int ak09916c_check_device(struct ak09916c_p *data)
{
unsigned char buf[2];
int ret;
ret = ak09916c_i2c_read_block(data->client, AK09916C_REG_WIA1, buf, 2);
if (ret < 0) {
SENSOR_ERR("unable to read AK09916C_REG_WIA1\n");
return ret;
}
ret = ak09916c_ecs_set_mode(data, AK09916C_MODE_POWERDOWN);
if (ret < 0) {
SENSOR_ERR("Error in setting power down mode\n");
return ret;
}
if ((buf[0] != AK09916C_WIA1_VALUE)
|| (buf[1] != AK09916C_WIA2_VALUE)) {
SENSOR_ERR("Wrong Device. Value = %u, %u\n", buf[0], buf[1]);
return -ENXIO;
}
return 0;
}
#ifdef CONFIG_SENSORS_SW_RESET
static ssize_t ak09916c_power_reset_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct ak09916c_p *data = dev_get_drvdata(dev);
int enabled = atomic_read(&data->enable);
SENSOR_INFO("magnetic power reset start!!!\n");
data->reset_state = 1;
mutex_lock(&data->enable_lock);
if (enabled) {
hrtimer_cancel(&data->mag_timer);
cancel_work_sync(&data->mag_work);
SENSOR_INFO("cancel hrtimer done!!!\n");
}
mutex_unlock(&data->enable_lock);
SENSOR_INFO("magnetic power reset end!!!\n");
return snprintf(buf, PAGE_SIZE, "%d\n", enabled);
}
static ssize_t ak09916c_sw_reset_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct ak09916c_p *data = dev_get_drvdata(dev);
int enabled = atomic_read(&data->enable);
SENSOR_INFO("magnetic sw reset start!!!\n");
mutex_lock(&data->enable_lock);
data->reset_state = 0;
if (enabled == 1) {
SENSOR_INFO("magnetic was enabled so make enable!!!\n");
ak09916c_ecs_set_mode(data, AK09916C_MODE_SNG_MEASURE);
hrtimer_start(&data->mag_timer, data->delay,
HRTIMER);
} else {
SENSOR_INFO("magnetic was disabled so make disable!!!\n");
ak09916c_ecs_set_mode(data, AK09916C_MODE_POWERDOWN);
}
mutex_unlock(&data->enable_lock);
SENSOR_INFO("magnetic sw reset end!!!\n");
return snprintf(buf, PAGE_SIZE, "%d\n", enabled);
}
#endif
static DEVICE_ATTR(name, S_IRUGO, ak09916c_name_show, NULL);
static DEVICE_ATTR(vendor, S_IRUGO, ak09916c_vendor_show, NULL);
static DEVICE_ATTR(raw_data, S_IRUGO | S_IWUSR | S_IWGRP,
ak09916c_raw_data_read, ak09916c_raw_data_write);
static DEVICE_ATTR(adc, S_IRUGO, ak09916c_adc, NULL);
static DEVICE_ATTR(dac, S_IRUGO, ak09916c_check_cntl, NULL);
static DEVICE_ATTR(chk_registers, S_IRUGO, ak09916c_check_registers, NULL);
static DEVICE_ATTR(selftest, S_IRUGO, ak09916c_get_selftest, NULL);
static DEVICE_ATTR(asa, S_IRUGO, ak09916c_get_asa, NULL);
static DEVICE_ATTR(status, S_IRUGO, ak09916c_get_status, NULL);
#ifdef CONFIG_SENSORS_SW_RESET
static DEVICE_ATTR(power_reset, S_IRUSR | S_IRGRP, ak09916c_power_reset_show, NULL);
static DEVICE_ATTR(sw_reset, S_IRUSR | S_IRGRP, ak09916c_sw_reset_show, NULL);
#endif
static DEVICE_ATTR(dhr_sensor_info, S_IRUGO | S_IWUSR | S_IWGRP,
ak09916c_dhr_sensor_info_show, ak09916c_dhr_sensor_info_store);
static struct device_attribute *sensor_attrs[] = {
&dev_attr_name,
&dev_attr_vendor,
&dev_attr_raw_data,
&dev_attr_adc,
&dev_attr_dac,
&dev_attr_chk_registers,
&dev_attr_selftest,
&dev_attr_asa,
&dev_attr_status,
#ifdef CONFIG_SENSORS_SW_RESET
&dev_attr_power_reset,
&dev_attr_sw_reset,
#endif
&dev_attr_dhr_sensor_info,
NULL,
};
static int ak09916c_input_init(struct ak09916c_p *data)
{
int ret = 0;
struct input_dev *dev;
dev = input_allocate_device();
if (!dev)
return -ENOMEM;
dev->name = MODULE_NAME;
dev->id.bustype = BUS_I2C;
input_set_capability(dev, EV_REL, REL_X);
input_set_capability(dev, EV_REL, REL_Y);
input_set_capability(dev, EV_REL, REL_Z);
input_set_capability(dev, EV_REL, REL_RZ); /* cal reset flag */
input_set_drvdata(dev, data);
ret = input_register_device(dev);
if (ret < 0) {
input_free_device(dev);
return ret;
}
ret = sensors_create_symlink(&dev->dev.kobj, dev->name);
if (ret < 0) {
input_unregister_device(dev);
return ret;
}
/* sysfs node creation */
ret = sysfs_create_group(&dev->dev.kobj, &ak09916c_attribute_group);
if (ret < 0) {
sensors_remove_symlink(&data->input->dev.kobj,
data->input->name);
input_unregister_device(dev);
return ret;
}
data->input = dev;
return 0;
}
static int ak09916c_parse_dt(struct ak09916c_p *data, struct device *dev)
{
struct device_node *dNode = dev->of_node;
if (dNode == NULL)
return -ENODEV;
if (of_property_read_u32(dNode,
"ak09916c-i2c,chip_pos", &data->chip_pos) < 0)
data->chip_pos = AK09916C_TOP_LOWER_RIGHT;
return 0;
}
static int ak09916c_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
int ret = -ENODEV;
struct ak09916c_p *data = NULL;
SENSOR_INFO("Start!\n");
if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
SENSOR_ERR("i2c_check_functionality error\n");
goto exit;
}
data = kzalloc(sizeof(struct ak09916c_p), GFP_KERNEL);
if (data == NULL) {
SENSOR_ERR("kzalloc error\n");
ret = -ENOMEM;
goto exit_kzalloc;
}
ret = ak09916c_parse_dt(data, &client->dev);
if (ret < 0) {
SENSOR_ERR("of_node error\n");
goto exit_of_node;
}
i2c_set_clientdata(client, data);
data->client = client;
ret = ak09916c_check_device(data);
if (ret < 0) {
SENSOR_ERR("check_device fail (err=%d)\n", ret);
goto exit_set_mode_check_device;
}
/* input device init */
ret = ak09916c_input_init(data);
if (ret < 0)
goto exit_input_init;
ret = sensors_register(&data->factory_device, data, sensor_attrs,
MODULE_NAME);
if (ret) {
SENSOR_ERR("failed to sensors_register (%d)\n", ret);
goto exit_sensor_register_failed;
}
/* workqueue init */
hrtimer_init(&data->mag_timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
data->delay = ns_to_ktime(AK09916C_DEFAULT_DELAY);
data->mag_timer.function = ak09916c_timer_func;
data->wq = create_singlethread_workqueue("mag_wq");
if (!data->wq) {
ret = -ENOMEM;
SENSOR_ERR("could not create accel workqueue\n");
goto exit_sensor_register_failed;
}
INIT_WORK(&data->mag_work, ak09916c_work_func);
mutex_init(&data->lock);
#ifdef CONFIG_SENSORS_SW_RESET
data->reset_state = 0;
#endif
mutex_init(&data->enable_lock);
atomic_set(&data->enable, 0);
data->asa[0] = 128;
data->asa[1] = 128;
data->asa[2] = 128;
data->reset_flag = 0;
SENSOR_INFO("done!(chip pos : %d)\n", data->chip_pos);
return 0;
exit_sensor_register_failed:
sysfs_remove_group(&data->input->dev.kobj, &ak09916c_attribute_group);
sensors_remove_symlink(&data->input->dev.kobj, data->input->name);
input_unregister_device(data->input);
exit_input_init:
exit_set_mode_check_device:
exit_of_node:
kfree(data);
exit_kzalloc:
exit:
SENSOR_ERR("fail\n");
return ret;
}
static void ak09916c_shutdown(struct i2c_client *client)
{
struct ak09916c_p *data =
(struct ak09916c_p *)i2c_get_clientdata(client);
SENSOR_INFO("\n");
if (atomic_read(&data->enable) == 1) {
hrtimer_cancel(&data->mag_timer);
cancel_work_sync(&data->mag_work);
ak09916c_ecs_set_mode(data, AK09916C_MODE_POWERDOWN);
}
}
static int ak09916c_remove(struct i2c_client *client)
{
struct ak09916c_p *data =
(struct ak09916c_p *)i2c_get_clientdata(client);
if (atomic_read(&data->enable) == 1)
ak09916c_set_enable(data, 0);
mutex_destroy(&data->lock);
mutex_destroy(&data->enable_lock);
sensors_unregister(data->factory_device, sensor_attrs);
sensors_remove_symlink(&data->input->dev.kobj, data->input->name);
sysfs_remove_group(&data->input->dev.kobj, &ak09916c_attribute_group);
input_unregister_device(data->input);
kfree(data);
return 0;
}
static int ak09916c_suspend(struct device *dev)
{
struct ak09916c_p *data = dev_get_drvdata(dev);
if (atomic_read(&data->enable) == 1) {
hrtimer_cancel(&data->mag_timer);
cancel_work_sync(&data->mag_work);
ak09916c_ecs_set_mode(data, AK09916C_MODE_POWERDOWN);
}
return 0;
}
static int ak09916c_resume(struct device *dev)
{
struct ak09916c_p *data = dev_get_drvdata(dev);
if (atomic_read(&data->enable) == 1) {
ak09916c_ecs_set_mode(data, AK09916C_MODE_SNG_MEASURE);
hrtimer_start(&data->mag_timer, data->delay,
HRTIMER_MODE_REL);
}
return 0;
}
static struct of_device_id ak09916c_match_table[] = {
{ .compatible = "ak09916c-i2c",},
{},
};
static const struct i2c_device_id ak09916c_id[] = {
{ "ak09916c_match_table", 0 },
{ }
};
static const struct dev_pm_ops ak09916c_pm_ops = {
.suspend = ak09916c_suspend,
.resume = ak09916c_resume
};
static struct i2c_driver ak09916c_driver = {
.driver = {
.name = MODEL_NAME,
.owner = THIS_MODULE,
.of_match_table = ak09916c_match_table,
.pm = &ak09916c_pm_ops
},
.probe = ak09916c_probe,
.shutdown = ak09916c_shutdown,
.remove = ak09916c_remove,
.id_table = ak09916c_id,
};
static int __init ak09916c_init(void)
{
return i2c_add_driver(&ak09916c_driver);
}
static void __exit ak09916c_exit(void)
{
i2c_del_driver(&ak09916c_driver);
}
module_init(ak09916c_init);
module_exit(ak09916c_exit);
MODULE_DESCRIPTION("AK09916C compass driver");
MODULE_AUTHOR("Samsung Electronics");
MODULE_LICENSE("GPL");