android_kernel_samsung_msm8226/drivers/sensors/hscdtd008a_i2c.c

822 lines
17 KiB
C

/* hscdtd008a_i2c.c
*
* GeoMagneticField device driver for I2C (HSCDTD008A)
*
* Copyright (C) 2012 ALPS ELECTRIC CO., LTD. All Rights Reserved.
*
* This software is licensed under the terms of the GNU General Public
* License version 2, as published by the Free Software Foundation, and
* may be copied, distributed, and modified under those terms.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
*/
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/i2c.h>
#include <linux/delay.h>
#include <linux/slab.h>
#include <linux/regulator/consumer.h>
#undef CONFIG_HAS_EARLYSUSPEND
#ifdef CONFIG_HAS_EARLYSUSPEND
#include <linux/earlysuspend.h>
#endif
#include <linux/err.h>
#include <linux/of_gpio.h>
#include "sensors_core.h"
#define I2C_RETRY_DELAY 5
#define I2C_RETRIES 5
#define I2C_HSCD_ADDR (0x0c) /* 000 1100 */
#define HSCD_DRIVER_NAME "HSCDTD008"
#define CHIP_DEV_NAME "HSCDTD008A"
#define CHIP_DEV_VENDOR "ALPS"
#undef ALPS_DEBUG
#define alps_dbgmsg(str, args...) pr_debug("%s: " str, __func__, ##args)
#define alps_errmsg(str, args...) pr_err("%s: " str, __func__, ##args)
#define alps_info(str, args...) pr_info("%s: " str, __func__, ##args)
#define HSCD_STB 0x0C
#define HSCD_XOUT 0x10
#define HSCD_YOUT 0x12
#define HSCD_ZOUT 0x14
#define HSCD_XOUT_H 0x11
#define HSCD_XOUT_L 0x10
#define HSCD_YOUT_H 0x13
#define HSCD_YOUT_L 0x12
#define HSCD_ZOUT_H 0x15
#define HSCD_ZOUT_L 0x14
#define HSCD_STATUS 0x18
#define HSCD_CTRL1 0x1b
#define HSCD_CTRL2 0x1c
#define HSCD_CTRL3 0x1d
#define HSCD_CTRL4 0x1e
#define HSCD_TCS_TIME 10000 /* Measure temp. of every 10 sec */
/* hscdtd008a chip id */
#define DEVICE_ID 0x49
/* hscd magnetic sensor chip identification register */
#define WHO_AM_I 0x0F
#define RETRY_COUNT 10
static struct i2c_driver hscd_driver;
static struct i2c_client *this_client;
#ifdef CONFIG_HAS_EARLYSUSPEND
static struct early_suspend hscd_early_suspend_handler;
#endif
struct hscd_power_data {
struct regulator *regulator_vdd;
struct regulator *regulator_vio;
};
struct hscd_platform_data{
unsigned int irq_gpio;
u32 irq_gpio_flags;
const u8 *reg_vdd;
const u8 *reg_vio;
};
static struct hscd_power_data hscd_power;
static atomic_t flgEna;
static atomic_t delay;
static atomic_t flgSuspend;
static int tcs_thr;
static int tcs_cnt;
static int hscd_i2c_readm(char *rxData, int length)
{
int err;
int tries = 0;
struct i2c_msg msgs[] = {
{
.addr = this_client->addr,
.flags = 0,
.len = 1,
.buf = rxData,
},
{
.addr = this_client->addr,
.flags = I2C_M_RD,
.len = length,
.buf = rxData,
},
};
do {
err = i2c_transfer(this_client->adapter, msgs, 2);
} while ((err != 2) && (++tries < I2C_RETRIES));
if (err != 2) {
dev_err(&this_client->adapter->dev, "read transfer error\n");
err = -EIO;
} else {
err = 0;
}
return err;
}
static int hscd_i2c_writem(char *txData, int length)
{
int err;
int tries = 0;
#ifdef ALPS_DEBUG
int i;
#endif
struct i2c_msg msg[] = {
{
.addr = this_client->addr,
.flags = 0,
.len = length,
.buf = txData,
},
};
#ifdef ALPS_DEBUG
pr_debug("%s : ", __func__);
for (i = 0; i < length; i++)
pr_debug("0X%02X, ", txData[i]);
pr_debug("\n");
#endif
do {
err = i2c_transfer(this_client->adapter, msg, 1);
} while ((err != 1) && (++tries < I2C_RETRIES));
if (err != 1) {
dev_err(&this_client->adapter->dev, "write transfer error\n");
err = -EIO;
} else {
err = 0;
}
return err;
}
static int hscd_power_on(int onoff)
{
int err = 0;
alps_info("is called\n");
if (onoff == 1)
{
if (hscd_power.regulator_vdd) {
err = regulator_enable(hscd_power.regulator_vdd);
if (err) {
alps_errmsg("Couldn't enable VDD %d\n", err);
return err;
}
}
if (hscd_power.regulator_vio) {
err = regulator_enable(hscd_power.regulator_vio);
if (err) {
alps_errmsg("Couldn't enable VIO %d\n", err);
return err;
}
}
}
else if (onoff == 0) {
if (hscd_power.regulator_vdd) {
regulator_disable(hscd_power.regulator_vdd);
/*regulator_put(hscd_power.regulator_vdd);*/
}
if (hscd_power.regulator_vio) {
regulator_disable(hscd_power.regulator_vio);
/*regulator_put(hscd_power.regulator_vio);*/
}
}
msleep(60);
return err;
}
int hscd_self_test_A(void)
{
u8 sx[2], cr1[1];
if ( (this_client == NULL) || (atomic_read(&flgSuspend) == 1) )
{
alps_errmsg("ALPS_ERR hscd_self_test_A validation fail\n");
return -1;
}
alps_info("is called\n");
/* Control register1 backup */
cr1[0] = HSCD_CTRL1;
if (hscd_i2c_readm(cr1, 1))
return 1;
#ifdef ALPS_DEBUG
else
alps_dbgmsg("Control resister1 value, %02X\n", cr1[0]);
#endif
mdelay(1);
/* Move active Mode (force state) */
sx[0] = HSCD_CTRL1;
sx[1] = 0x8A;
if (hscd_i2c_writem(sx, 2))
return 1;
/* Get inital value of self-test-A register */
sx[0] = HSCD_STB;
hscd_i2c_readm(sx, 1);
mdelay(1);
sx[0] = HSCD_STB;
if (hscd_i2c_readm(sx, 1))
return 1;
#ifdef ALPS_DEBUG
else
alps_dbgmsg("Self test A register value, %02X\n", sx[0]);
#endif
if (sx[0] != 0x55) {
alps_errmsg("Err! self-test-A, initial value is %02X\n", sx[0]);
return 2;
}
/* do self-test*/
sx[0] = HSCD_CTRL3;
sx[1] = 0x10;
if (hscd_i2c_writem(sx, 2))
return 1;
mdelay(3);
/* Get 1st value of self-test-A register */
sx[0] = HSCD_STB;
if (hscd_i2c_readm(sx, 1))
return 1;
#ifdef ALPS_DEBUG
else
alps_dbgmsg("Self test register value, %02X\n", sx[0]);
#endif
if (sx[0] != 0xAA) {
alps_errmsg("Err! self-test, 1st value is %02X\n", sx[0]);
return 3;
}
mdelay(3);
/* Get 2nd value of self-test register */
sx[0] = HSCD_STB;
if (hscd_i2c_readm(sx, 1))
return 1;
#ifdef ALPS_DEBUG
else
alps_dbgmsg("Self test register value, %02X\n", sx[0]);
#endif
if (sx[0] != 0x55) {
alps_errmsg("Err! self-test, 2nd value is %02X\n", sx[0]);
return 4;
}
/* Resume */
sx[0] = HSCD_CTRL1;
sx[1] = cr1[0];
if (hscd_i2c_writem(sx, 2))
return 1;
return 0;
}
EXPORT_SYMBOL(hscd_self_test_A);
int hscd_self_test_B(void)
{
if ( (this_client == NULL) || atomic_read(&flgSuspend) == 1)
{
alps_errmsg("ALPS_ERR hscd_self_test_B validation fail\n");
return -1;
}
alps_info("is called\n");
return 0;
}
EXPORT_SYMBOL(hscd_self_test_B);
static int hscd_soft_reset(void)
{
int rc;
u8 buf[2];
#ifdef ALPS_DEBUG
alps_dbgmsg("Software Reset\n");
#endif
buf[0] = HSCD_CTRL3;
buf[1] = 0x80;
rc = hscd_i2c_writem(buf, 2);
msleep(20);
return rc;
}
static int hscd_tcs_setup(void)
{
int rc;
u8 buf[2];
buf[0] = HSCD_CTRL3;
buf[1] = 0x02;
rc = hscd_i2c_writem(buf, 2);
msleep(20);
tcs_thr = HSCD_TCS_TIME / atomic_read(&delay);
tcs_cnt = 0;
return rc;
}
static int hscd_force_setup(void)
{
u8 buf[2];
buf[0] = HSCD_CTRL3;
buf[1] = 0x40;
return hscd_i2c_writem(buf, 2);
}
int hscd_get_magnetic_field_data(int *xyz)
{
int err = -1;
int i;
u8 sx[6] = {0, };
if ( (this_client == NULL) || ( atomic_read(&flgSuspend) == 1 ) )
{
alps_errmsg("ALPS_ERR hscd_get_magnetic_field_data validation fail\n");
return err;
}
sx[0] = HSCD_XOUT;
err = hscd_i2c_readm(sx, 6);
if (err < 0) {
alps_errmsg("Fail to read data from i2c\n");
return err;
}
for (i = 0; i < 3; i++)
xyz[i] = (int) ((short)((sx[2*i + 1] << 8) | (sx[2*i])));
#ifdef ALPS_DEBUG
/*** DEBUG OUTPUT - REMOVE ***/
alps_dbgmsg("x = %d, y = %d, z = %d\n", xyz[0], xyz[1], xyz[2]);
/*** <end> DEBUG OUTPUT - REMOVE ***/
#endif
if (++tcs_cnt > tcs_thr)
hscd_tcs_setup();
hscd_force_setup();
return err;
}
EXPORT_SYMBOL(hscd_get_magnetic_field_data);
void hscd_activate(int flgatm, int flg, int dtime)
{
u8 buf[2];
int Ena = atomic_read(&flgEna);
if (this_client == NULL)
return;
else if ((atomic_read(&delay) == dtime)
&& (atomic_read(&flgEna) == flg)
&& (flgatm == 1))
return;
alps_info("is called\n");
if (flg != 0) {
buf[0] = HSCD_CTRL4; /* 15 bit signed value */
buf[1] = 0x90;
hscd_i2c_writem(buf, 2);
mdelay(1);
tcs_cnt = tcs_cnt * atomic_read(&delay) / dtime;
tcs_thr = HSCD_TCS_TIME / dtime;
}
if ((!flg) && (Ena)) {
hscd_soft_reset();
} else if (!flg) {
buf[0] = HSCD_CTRL1;
buf[1] = 0x0A;
hscd_i2c_writem(buf, 2);
} else if ((flg) && (!Ena)) {
buf[0] = HSCD_CTRL1;
buf[1] = 0x8A;
hscd_i2c_writem(buf, 2);
mdelay(1);
hscd_tcs_setup();
hscd_force_setup();
}
if (flgatm) {
atomic_set(&flgEna, flg);
atomic_set(&delay, dtime);
}
}
EXPORT_SYMBOL(hscd_activate);
/*
static void hscd_register_init(void)
{
u8 buf[2];
alps_info("is called\n");
buf[0] = HSCD_CTRL3;
buf[1] = 0x80;
hscd_i2c_writem(buf, 2);
mdelay(5);
atomic_set(&delay, 100);
hscd_activate(0, 1, atomic_read(&delay));
hscd_get_magnetic_field_data(v);
alps_dbgmsg("x = %d, y = %d, z = %d\n", v[0], v[1], v[2]);
hscd_activate(0, 0, atomic_read(&delay));
}
*/
static ssize_t selftest_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
int result1, result2;
if (!atomic_read(&flgEna))
hscd_power_on(1);
result1 = hscd_self_test_A();
result2 = hscd_self_test_B();
/*if (!atomic_read(&flgEna))
hscd_power_off();*/
if (result1 == 0)
result1 = 1;
else
result1 = 0;
if (result2 == 0)
result2 = 1;
else
result2 = 0;
alps_info("result, A = %d, B = %d\n", result1, result2);
return snprintf(buf, PAGE_SIZE, "%d, %d\n", result1, result2);
}
static ssize_t status_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
int result;
if (!atomic_read(&flgEna))
hscd_power_on(1);
result = hscd_self_test_B();
/*if (!atomic_read(&flgEna))
hscd_power_off();*/
if (result == 0)
result = 1;
else
result = 0;
return snprintf(buf, PAGE_SIZE, "%d,%d\n", result, 0);
}
static ssize_t adc_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
int data[3];
if (!atomic_read(&flgEna))
hscd_activate(0, 1, 100);
msleep(20);
hscd_get_magnetic_field_data(data);
alps_dbgmsg("x = %d, y = %d, z = %d\n", data[0], data[1], data[2]);
if (!atomic_read(&flgEna))
hscd_activate(0, 0, 100);
return snprintf(buf, PAGE_SIZE, "%d,%d,%d\n",
data[0], data[1], data[2]);
}
static ssize_t name_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
return snprintf(buf, PAGE_SIZE, "%s\n", CHIP_DEV_NAME);
}
static ssize_t vendor_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
return snprintf(buf, PAGE_SIZE, "%s\n", CHIP_DEV_VENDOR);
}
static ssize_t mag_raw_data_read(struct device *dev,
struct device_attribute *attr, char *buf)
{
int xyz[3] = {0, };
alps_dbgmsg("is called\n");
if (!atomic_read(&flgEna)) {
hscd_activate(0, 1, 100);
msleep(20);
}
hscd_get_magnetic_field_data(xyz);
if (!atomic_read(&flgEna))
hscd_activate(0, 0, 100);
return snprintf(buf, PAGE_SIZE, "%d,%d,%d\n",
xyz[0], xyz[1], xyz[2]);
}
static DEVICE_ATTR(selftest, S_IRUGO | S_IWUSR | S_IWGRP,
selftest_show, NULL);
static DEVICE_ATTR(status, S_IRUGO | S_IWUSR | S_IWGRP,
status_show, NULL);
static DEVICE_ATTR(adc, S_IRUGO | S_IWUSR | S_IWGRP,
adc_show, NULL);
static DEVICE_ATTR(raw_data, S_IRUGO | S_IWUSR | S_IWGRP,
mag_raw_data_read, NULL);
static DEVICE_ATTR(name, S_IRUGO | S_IWUSR | S_IWGRP, name_show, NULL);
static DEVICE_ATTR(vendor, S_IRUGO | S_IWUSR | S_IWGRP, vendor_show, NULL);
static struct device_attribute *magnetic_attrs[] = {
&dev_attr_selftest,
&dev_attr_status,
&dev_attr_adc,
&dev_attr_name,
&dev_attr_vendor,
&dev_attr_raw_data,
NULL,
};
#ifdef CONFIG_OF
/* device tree parsing */
static int hscd_parse_dt(struct device *dev,struct hscd_platform_data *pdata)
{
struct device_node *np = dev->of_node;
pdata->irq_gpio = of_get_named_gpio_flags(np, "HSCDTD008-i2c,irq_gpio",
0, &pdata->irq_gpio_flags);
pdata->reg_vdd = of_get_property(np,"HSCDTD008-i2c,reg_vdd", NULL);
pdata->reg_vio = of_get_property(np,"HSCDTD008-i2c,reg_vio", NULL);
return 0;
}
#else
static int hscd_parse_dt(struct device *dev,
struct hscd_platform_data *pdata)
{
return -ENODEV;
}
#endif
static int hscd_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
int ret = 0;
struct device *magnetic_device = NULL;
#ifdef CONFIG_OF
int err;
struct hscd_platform_data *platform_data;
#endif
alps_info("is called\n");
this_client = client;
pr_info("\n hscd_probe start");
platform_data = devm_kzalloc (&client->dev,
sizeof(struct hscd_platform_data), GFP_KERNEL);
if(!platform_data) {
dev_err(&client->dev, "Failed to allocate memory\n");
return -ENOMEM;
}
err = hscd_parse_dt(&client->dev, platform_data);
if(err)
return err;
if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
dev_err(&client->adapter->dev, "client not i2c capable (hscd_probe)\n");
ret = -ENOMEM;
goto exit;
}
hscd_power.regulator_vdd = NULL;
hscd_power.regulator_vio = NULL;
hscd_power.regulator_vdd = regulator_get(&client->dev,platform_data->reg_vdd);
if (IS_ERR(hscd_power.regulator_vdd)) {
ret = PTR_ERR(hscd_power.regulator_vdd);
hscd_power.regulator_vdd = NULL;
alps_errmsg("Failed to get hscd_i2c_vdd %d\n", ret);
goto err_setup_regulator;
}
hscd_power.regulator_vio = regulator_get(&client->dev,platform_data->reg_vio);
if (IS_ERR(hscd_power.regulator_vio)) {
ret = PTR_ERR(hscd_power.regulator_vio);
hscd_power.regulator_vio = NULL;
alps_errmsg("Failed to get hscd_i2c_vio %d\n", ret);
goto err_setup_regulator;
}
/* turn on the power */
hscd_power_on(1);
hscd_soft_reset();
/* read chip id */
ret = i2c_smbus_read_byte_data(this_client, WHO_AM_I);
alps_info("Device ID = 0x%x, Reading ID = 0x%x\n", DEVICE_ID, ret);
if (ret == DEVICE_ID) /* Normal Operation */
ret = 0;
else {
if (ret < 0)
alps_errmsg("i2c for reading chip id failed\n");
else {
alps_errmsg("Device identification failed\n");
ret = -ENODEV;
}
goto err_setup_regulator;
}
err=sensors_register(magnetic_device, NULL, magnetic_attrs, "magnetic_sensor");
//sensors_register(magnetic_device, NULL, magnetic_attrs,
// "magnetic_sensor");
if (err < 0)
return err;
atomic_set(&flgEna, 0);
atomic_set(&delay, 100);
atomic_set(&flgSuspend, 0);
tcs_cnt = 0;
tcs_thr = HSCD_TCS_TIME / atomic_read(&delay);
alps_info("is Successful\n");
pr_info("\n hscd probe success");
return 0;
err_setup_regulator:
if (hscd_power.regulator_vdd) {
regulator_disable(hscd_power.regulator_vdd);
regulator_put(hscd_power.regulator_vdd);
}
if (hscd_power.regulator_vio) {
regulator_disable(hscd_power.regulator_vio);
regulator_put(hscd_power.regulator_vio);
}
exit:
this_client = NULL;
alps_errmsg("Failed (errno = %d)\n", ret);
return ret;
}
static int __devexit hscd_remove(struct i2c_client *client)
{
alps_info("is called\n");
hscd_activate(0, 0, atomic_read(&delay));
#ifdef CONFIG_HAS_EARLYSUSPEND
unregister_early_suspend(&hscd_early_suspend_handler);
#endif
if (hscd_power.regulator_vdd) {
regulator_disable(hscd_power.regulator_vdd);
regulator_put(hscd_power.regulator_vdd);
}
if (hscd_power.regulator_vio) {
regulator_disable(hscd_power.regulator_vio);
regulator_put(hscd_power.regulator_vio);
}
this_client = NULL;
return 0;
}
static int hscd_suspend(struct i2c_client *client, pm_message_t mesg)
{
alps_info("is called\n");
atomic_set(&flgSuspend, 1);
if (atomic_read(&flgEna))
hscd_activate(0, 0, atomic_read(&delay));
#if !(defined(CONFIG_MACH_CRATERQ_CHN_OPEN)||defined(CONFIG_MACH_MS01_CHN_CTC)||defined(CONFIG_MACH_BAFFIN2_CHN_CMCC) || defined(CONFIG_SEC_GNOTE_PROJECT) )
hscd_power_on(0);
#endif
return 0;
}
static int hscd_resume(struct i2c_client *client)
{
alps_info("is called\n");
atomic_set(&flgSuspend, 0);
if (atomic_read(&flgEna)) {
atomic_set(&flgEna, 0);
hscd_activate(1, 1 , atomic_read(&delay));
}
#if !(defined(CONFIG_MACH_CRATERQ_CHN_OPEN)||defined(CONFIG_MACH_MS01_CHN_CTC)||defined(CONFIG_MACH_BAFFIN2_CHN_CMCC) || defined(CONFIG_SEC_GNOTE_PROJECT) )
hscd_power_on(1);
#endif
return 0;
}
#ifdef CONFIG_HAS_EARLYSUSPEND
static void hscd_early_suspend(struct early_suspend *handler)
{
alps_info("is called\n");
hscd_suspend(this_client, PMSG_SUSPEND);
}
static void hscd_early_resume(struct early_suspend *handler)
{
alps_info("is called\n");
hscd_resume(this_client);
}
#endif
static const struct i2c_device_id ALPS_id[] = {
{ HSCD_DRIVER_NAME, 0 },
{ }
};
#ifdef CONFIG_OF
static struct of_device_id magnetic_match_table[] = {
{.compatible = "magnetic,HSCDTD008",},
{},
};
#else
#define magnetic_match_table NULL
#endif
static struct i2c_driver hscd_driver = {
.probe = hscd_probe,
.remove = hscd_remove,
.id_table = ALPS_id,
.driver = {
.name = HSCD_DRIVER_NAME,
.of_match_table = magnetic_match_table,
},
.suspend = hscd_suspend,
.resume = hscd_resume,
};
#ifdef CONFIG_HAS_EARLYSUSPEND
static struct early_suspend hscd_early_suspend_handler = {
.suspend = hscd_early_suspend,
.resume = hscd_early_resume,
};
#endif
static int __init hscd_init(void)
{
alps_info("is called\n");
return i2c_add_driver(&hscd_driver);
}
static void __exit hscd_exit(void)
{
alps_info("is called\n");
i2c_del_driver(&hscd_driver);
}
module_init(hscd_init);
module_exit(hscd_exit);
MODULE_DESCRIPTION("Alps HSCDTD008A Compass Device");
MODULE_AUTHOR("ALPS ELECTRIC CO., LTD.");
MODULE_LICENSE("GPL v2");