summaryrefslogtreecommitdiff
path: root/drivers/sensor/bmm050_driver.c
diff options
context:
space:
mode:
authorYuval Adam <_@yuv.al>2017-08-30 08:25:59 +0000
committerYuval Adam <_@yuv.al>2017-08-30 08:25:59 +0000
commit8e4bff4cab0ddac6060645b0715210484d02ff40 (patch)
tree3a5c3024371a04692a3a6d9974d001cdff8ebf84 /drivers/sensor/bmm050_driver.c
Initial file dump from open source releaseHEADmaster
Diffstat (limited to 'drivers/sensor/bmm050_driver.c')
-rw-r--r--drivers/sensor/bmm050_driver.c1593
1 files changed, 1593 insertions, 0 deletions
diff --git a/drivers/sensor/bmm050_driver.c b/drivers/sensor/bmm050_driver.c
new file mode 100644
index 00000000..662f8ca1
--- /dev/null
+++ b/drivers/sensor/bmm050_driver.c
@@ -0,0 +1,1593 @@
+/*
+ * (C) Copyright 2013 Bosch Sensortec GmbH All Rights Reserved
+ *
+ * This software program is licensed subject to the GNU General Public License
+ * (GPL).Version 2,June 1991, available at http://www.fsf.org/copyleft/gpl.html
+ *
+ * @date Jun 12th, 2013
+ * @version v2.5.5
+ * @brief BMM050 Linux Driver
+ */
+
+#include <linux/version.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/i2c.h>
+#include <linux/interrupt.h>
+#include <linux/input.h>
+#include <linux/workqueue.h>
+#include <linux/mutex.h>
+#include <linux/slab.h>
+#include <linux/delay.h>
+#include <linux/sensors_core.h>
+
+#ifdef CONFIG_HAS_EARLYSUSPEND
+#include <linux/earlysuspend.h>
+#endif
+
+#define DEBUG 1
+
+#include <linux/bst_sensor_common.h>
+
+#include "bmm050.h"
+#include "bs_log.h"
+
+/* sensor specific */
+#define SENSOR_NAME "bmm050"
+
+#define SENSOR_CHIP_ID_BMM (0x32)
+
+#define BMM_REG_NAME(name) BMM050_##name
+#define BMM_VAL_NAME(name) BMM050_##name
+#define BMM_CALL_API(name) bmm050_##name
+
+#define BMM_I2C_WRITE_DELAY_TIME 1
+
+#define BMM_DEFAULT_REPETITION_XY BMM_VAL_NAME(REGULAR_REPXY)
+#define BMM_DEFAULT_REPETITION_Z BMM_VAL_NAME(REGULAR_REPZ)
+#define BMM_DEFAULT_ODR BMM_VAL_NAME(REGULAR_DR)
+/* generic */
+#define BMM_MAX_RETRY_I2C_XFER (100)
+#define BMM_MAX_RETRY_WAKEUP (5)
+#define BMM_MAX_RETRY_WAIT_DRDY (100)
+
+#define BMM_DELAY_MIN (1)
+#define BMM_DELAY_DEFAULT (200)
+
+#define MAG_VALUE_MAX (32767)
+#define MAG_VALUE_MIN (-32768)
+
+#define BYTES_PER_LINE (16)
+
+#define BMM_SELF_TEST 1
+#define BMM_ADV_TEST 2
+
+#define BMM_OP_MODE_UNKNOWN (-1)
+
+static struct device *bmm_device = NULL;
+struct op_mode_map {
+ char *op_mode_name;
+ long op_mode;
+};
+
+static const u8 odr_map[] = {10, 2, 6, 8, 15, 20, 25, 30};
+static const struct op_mode_map op_mode_maps[] = {
+ {"normal", BMM_VAL_NAME(NORMAL_MODE)},
+ {"forced", BMM_VAL_NAME(FORCED_MODE)},
+ {"suspend", BMM_VAL_NAME(SUSPEND_MODE)},
+ {"sleep", BMM_VAL_NAME(SLEEP_MODE)},
+};
+
+
+struct bmm_client_data {
+ struct bmm050 device;
+ struct i2c_client *client;
+ struct input_dev *input;
+ struct delayed_work work;
+
+#ifdef CONFIG_HAS_EARLYSUSPEND
+ struct early_suspend early_suspend_handler;
+#endif
+
+ atomic_t delay;
+ /* whether the system in suspend state */
+ atomic_t in_suspend;
+
+ struct bmm050_mdata_s32 value;
+ u8 enable:1;
+ s8 op_mode:4;
+ u8 odr;
+ u8 rept_xy;
+ u8 rept_z;
+
+ s16 result_test;
+
+ struct mutex mutex_power_mode;
+
+ /* controls not only reg, but also workqueue */
+ struct mutex mutex_op_mode;
+ struct mutex mutex_enable;
+ struct mutex mutex_odr;
+ struct mutex mutex_rept_xy;
+ struct mutex mutex_rept_z;
+
+ struct mutex mutex_value;
+#ifdef CONFIG_BMM_USE_PLATFORM_DATA
+ struct bosch_sensor_specific *bst_pd;
+#endif
+};
+
+static struct i2c_client *bmm_client;
+/* i2c operation for API */
+static void bmm_delay(u32 msec);
+static char bmm_i2c_read(struct i2c_client *client, u8 reg_addr,
+ u8 *data, u8 len);
+static char bmm_i2c_write(struct i2c_client *client, u8 reg_addr,
+ u8 *data, u8 len);
+
+static void bmm_dump_reg(struct i2c_client *client);
+static int bmm_wakeup(struct i2c_client *client);
+static int bmm_check_chip_id(struct i2c_client *client);
+
+static int bmm_pre_suspend(struct i2c_client *client);
+static int bmm_post_resume(struct i2c_client *client);
+
+#ifdef CONFIG_HAS_EARLYSUSPEND
+static void bmm_early_suspend(struct early_suspend *handler);
+static void bmm_late_resume(struct early_suspend *handler);
+#endif
+
+static int bmm_restore_hw_cfg(struct i2c_client *client);
+
+static void bmm_remap_sensor_data(struct bmm050_mdata_s32 *val,
+ struct bmm_client_data *client_data)
+{
+#ifdef CONFIG_BMM_USE_PLATFORM_DATA
+ struct bosch_sensor_data bsd;
+
+ if (NULL == client_data->bst_pd)
+ return;
+
+ bsd.x = val->datax;
+ bsd.y = val->datay;
+ bsd.z = val->dataz;
+
+ bst_remap_sensor_data_dft_tab(&bsd,
+ client_data->bst_pd->place);
+
+ val->datax = bsd.x;
+ val->datay = bsd.y;
+ val->dataz = bsd.z;
+#else
+ (void)val;
+ (void)client_data;
+#endif
+}
+
+static int bmm_check_chip_id(struct i2c_client *client)
+{
+ int err = 0;
+ u8 chip_id = 0;
+
+ bmm_i2c_read(client, BMM_REG_NAME(CHIP_ID), &chip_id, 1);
+ PINFO("read chip id result: %#x", chip_id);
+
+ if ((chip_id & 0xff) != SENSOR_CHIP_ID_BMM)
+ err = -1;
+
+ return err;
+}
+
+static void bmm_delay(u32 msec)
+{
+ mdelay(msec);
+}
+
+static inline int bmm_get_forced_drdy_time(int rept_xy, int rept_z)
+{
+ return (145 * rept_xy + 500 * rept_z + 980 + (1000 - 1)) / 1000;
+}
+
+
+static void bmm_dump_reg(struct i2c_client *client)
+{
+#ifdef DEBUG
+ int i;
+ u8 dbg_buf[64];
+ u8 dbg_buf_str[64 * 3 + 1] = "";
+
+ for (i = 0; i < BYTES_PER_LINE; i++) {
+ dbg_buf[i] = i;
+ sprintf(dbg_buf_str + i * 3, "%02x%c",
+ dbg_buf[i],
+ (((i + 1) % BYTES_PER_LINE == 0) ? '\n' : ' '));
+ }
+ printk(KERN_DEBUG "%s\n", dbg_buf_str);
+
+ bmm_i2c_read(client, BMM_REG_NAME(CHIP_ID), dbg_buf, 64);
+ for (i = 0; i < 64; i++) {
+ sprintf(dbg_buf_str + i * 3, "%02x%c",
+ dbg_buf[i],
+ (((i + 1) % BYTES_PER_LINE == 0) ? '\n' : ' '));
+ }
+ printk(KERN_DEBUG "%s\n", dbg_buf_str);
+#endif
+}
+
+static int bmm_wakeup(struct i2c_client *client)
+{
+ int err = 0;
+ int try_times = BMM_MAX_RETRY_WAKEUP;
+ const u8 value = 0x01;
+ u8 dummy;
+
+ PINFO("waking up the chip...");
+
+ while (try_times) {
+ err = bmm_i2c_write(client,
+ BMM_REG_NAME(POWER_CNTL), (u8 *)&value, 1);
+ mdelay(BMM_I2C_WRITE_DELAY_TIME);
+ dummy = 0;
+
+ err = bmm_i2c_read(client, BMM_REG_NAME(POWER_CNTL), &dummy, 1);
+ if (value == dummy)
+ break;
+
+ try_times--;
+ }
+
+ PINFO("wake up result: %s, tried times: %d",
+ (try_times > 0) ? "succeed" : "fail",
+ BMM_MAX_RETRY_WAKEUP - try_times + 1);
+
+ err = (try_times > 0) ? 0 : -1;
+
+ return err;
+}
+
+/* i2c read routine for API*/
+static char bmm_i2c_read(struct i2c_client *client, u8 reg_addr,
+ u8 *data, u8 len)
+{
+#if !defined BMM_USE_BASIC_I2C_FUNC
+ s32 dummy;
+ if (NULL == client)
+ return -1;
+
+ while (0 != len--) {
+#ifdef BMM_SMBUS
+ dummy = i2c_smbus_read_byte_data(client, reg_addr);
+ if (dummy < 0) {
+ PERR("i2c bus read error");
+ return -1;
+ }
+ *data = (u8)(dummy & 0xff);
+#else
+ dummy = i2c_master_send(client, (char *)&reg_addr, 1);
+ if (dummy < 0)
+ return -1;
+
+ dummy = i2c_master_recv(client, (char *)data, 1);
+ if (dummy < 0)
+ return -1;
+#endif
+ reg_addr++;
+ data++;
+ }
+ return 0;
+#else
+ int retry;
+
+ struct i2c_msg msg[] = {
+ {
+ .addr = client->addr,
+ .flags = 0,
+ .len = 1,
+ .buf = &reg_addr,
+ },
+
+ {
+ .addr = client->addr,
+ .flags = I2C_M_RD,
+ .len = len,
+ .buf = data,
+ },
+ };
+
+ for (retry = 0; retry < BMM_MAX_RETRY_I2C_XFER; retry++) {
+ if (i2c_transfer(client->adapter, msg, ARRAY_SIZE(msg)) > 0)
+ break;
+ else
+ mdelay(BMM_I2C_WRITE_DELAY_TIME);
+ }
+
+ if (BMM_MAX_RETRY_I2C_XFER <= retry) {
+ PERR("I2C xfer error");
+ return -EIO;
+ }
+
+ return 0;
+#endif
+}
+
+/* i2c write routine for */
+static char bmm_i2c_write(struct i2c_client *client, u8 reg_addr,
+ u8 *data, u8 len)
+{
+#if !defined BMM_USE_BASIC_I2C_FUNC
+ s32 dummy;
+
+#ifndef BMM_SMBUS
+ u8 buffer[2];
+#endif
+
+ if (NULL == client)
+ return -1;
+
+ while (0 != len--) {
+#ifdef BMM_SMBUS
+ dummy = i2c_smbus_write_byte_data(client, reg_addr, *data);
+#else
+ buffer[0] = reg_addr;
+ buffer[1] = *data;
+ dummy = i2c_master_send(client, (char *)buffer, 2);
+#endif
+ reg_addr++;
+ data++;
+ if (dummy < 0) {
+ PERR("error writing i2c bus");
+ return -1;
+ }
+
+ }
+ return 0;
+#else
+ u8 buffer[2];
+ int retry;
+ struct i2c_msg msg[] = {
+ {
+ .addr = client->addr,
+ .flags = 0,
+ .len = 2,
+ .buf = buffer,
+ },
+ };
+
+ while (0 != len--) {
+ buffer[0] = reg_addr;
+ buffer[1] = *data;
+ for (retry = 0; retry < BMM_MAX_RETRY_I2C_XFER; retry++) {
+ if (i2c_transfer(client->adapter, msg,
+ ARRAY_SIZE(msg)) > 0) {
+ break;
+ } else {
+ mdelay(BMM_I2C_WRITE_DELAY_TIME);
+ }
+ }
+ if (BMM_MAX_RETRY_I2C_XFER <= retry) {
+ PERR("I2C xfer error");
+ return -EIO;
+ }
+ reg_addr++;
+ data++;
+ }
+
+ return 0;
+#endif
+}
+
+static char bmm_i2c_read_wrapper(u8 dev_addr, u8 reg_addr, u8 *data, u8 len)
+{
+ char err = 0;
+ err = bmm_i2c_read(bmm_client, reg_addr, data, len);
+
+ return err;
+}
+
+static char bmm_i2c_write_wrapper(u8 dev_addr, u8 reg_addr, u8 *data, u8 len)
+{
+ char err = 0;
+ err = bmm_i2c_write(bmm_client, reg_addr, data, len);
+ return err;
+}
+
+/* this function exists for optimization of speed,
+ * because it is frequently called */
+static inline int bmm_set_forced_mode(struct i2c_client *client)
+{
+ int err = 0;
+
+ /* FORCED_MODE */
+ const u8 value = 0x02;
+ err = bmm_i2c_write(client, BMM_REG_NAME(CONTROL), (u8 *)&value, 1);
+
+ return err;
+}
+
+static void bmm_work_func(struct work_struct *work)
+{
+ struct bmm_client_data *client_data =
+ container_of((struct delayed_work *)work,
+ struct bmm_client_data, work);
+ struct i2c_client *client = client_data->client;
+ unsigned long delay =
+ msecs_to_jiffies(atomic_read(&client_data->delay));
+
+ mutex_lock(&client_data->mutex_value);
+
+ mutex_lock(&client_data->mutex_op_mode);
+ if (BMM_VAL_NAME(NORMAL_MODE) != client_data->op_mode)
+ bmm_set_forced_mode(client);
+
+ mutex_unlock(&client_data->mutex_op_mode);
+
+ BMM_CALL_API(read_mdataXYZ_s32)(&client_data->value);
+ bmm_remap_sensor_data(&client_data->value, client_data);
+
+ input_report_abs(client_data->input, ABS_X, client_data->value.datax);
+ input_report_abs(client_data->input, ABS_Y, client_data->value.datay);
+ input_report_abs(client_data->input, ABS_Z, client_data->value.dataz);
+ mutex_unlock(&client_data->mutex_value);
+
+ input_sync(client_data->input);
+
+ schedule_delayed_work(&client_data->work, delay);
+}
+
+
+static int bmm_set_odr(struct i2c_client *client, u8 odr)
+{
+ int err = 0;
+
+ err = BMM_CALL_API(set_datarate)(odr);
+ mdelay(BMM_I2C_WRITE_DELAY_TIME);
+
+ return err;
+}
+
+static int bmm_get_odr(struct i2c_client *client, u8 *podr)
+{
+ int err = 0;
+ u8 value;
+
+ err = BMM_CALL_API(get_datarate)(&value);
+ if (!err)
+ *podr = value;
+
+ return err;
+}
+
+static ssize_t bmm_show_chip_id(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ return sprintf(buf, "%d\n", SENSOR_CHIP_ID_BMM);
+}
+
+static ssize_t bmm_show_op_mode(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ int ret;
+ struct input_dev *input = to_input_dev(dev);
+ struct bmm_client_data *client_data = input_get_drvdata(input);
+ u8 op_mode = 0xff;
+ u8 power_mode;
+
+ mutex_lock(&client_data->mutex_power_mode);
+ BMM_CALL_API(get_powermode)(&power_mode);
+ if (power_mode) {
+ mutex_lock(&client_data->mutex_op_mode);
+ BMM_CALL_API(get_functional_state)(&op_mode);
+ mutex_unlock(&client_data->mutex_op_mode);
+ } else {
+ op_mode = BMM_VAL_NAME(SUSPEND_MODE);
+ }
+
+ mutex_unlock(&client_data->mutex_power_mode);
+
+ PDEBUG("op_mode: %d", op_mode);
+
+ ret = sprintf(buf, "%d\n", op_mode);
+
+ return ret;
+}
+
+
+static inline int bmm_get_op_mode_idx(u8 op_mode)
+{
+ int i = 0;
+
+ for (i = 0; i < ARRAY_SIZE(op_mode_maps); i++) {
+ if (op_mode_maps[i].op_mode == op_mode)
+ break;
+ }
+
+ if (i < ARRAY_SIZE(op_mode_maps))
+ return i;
+ else
+ return -1;
+}
+
+
+static int bmm_set_op_mode(struct bmm_client_data *client_data, int op_mode)
+{
+ int err = 0;
+
+ err = BMM_CALL_API(set_functional_state)(
+ op_mode);
+
+ if (BMM_VAL_NAME(SUSPEND_MODE) == op_mode)
+ atomic_set(&client_data->in_suspend, 1);
+ else
+ atomic_set(&client_data->in_suspend, 0);
+
+ return err;
+}
+
+static ssize_t bmm_store_op_mode(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ int err = 0;
+ int i;
+ struct input_dev *input = to_input_dev(dev);
+ struct bmm_client_data *client_data = input_get_drvdata(input);
+ struct i2c_client *client = client_data->client;
+ long op_mode;
+
+ err = strict_strtoul(buf, 10, &op_mode);
+ if (err)
+ return err;
+
+ mutex_lock(&client_data->mutex_power_mode);
+
+ i = bmm_get_op_mode_idx(op_mode);
+
+ if (i != -1) {
+ mutex_lock(&client_data->mutex_op_mode);
+ if (op_mode != client_data->op_mode) {
+ if (BMM_VAL_NAME(FORCED_MODE) == op_mode) {
+ /* special treat of forced mode
+ * for optimization */
+ err = bmm_set_forced_mode(client);
+ } else {
+ err = bmm_set_op_mode(client_data, op_mode);
+ }
+
+ if (!err) {
+ if (BMM_VAL_NAME(FORCED_MODE) == op_mode)
+ client_data->op_mode =
+ BMM_OP_MODE_UNKNOWN;
+ else
+ client_data->op_mode = op_mode;
+ }
+ }
+ mutex_unlock(&client_data->mutex_op_mode);
+ } else {
+ err = -EINVAL;
+ }
+
+ mutex_unlock(&client_data->mutex_power_mode);
+
+ if (err)
+ return err;
+ else
+ return count;
+}
+
+static ssize_t bmm_show_odr(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ unsigned char data = 0;
+ struct input_dev *input = to_input_dev(dev);
+ struct bmm_client_data *client_data = input_get_drvdata(input);
+ struct i2c_client *client = client_data->client;
+ int err;
+ u8 power_mode;
+
+ mutex_lock(&client_data->mutex_power_mode);
+ BMM_CALL_API(get_powermode)(&power_mode);
+ if (power_mode) {
+ mutex_lock(&client_data->mutex_odr);
+ err = bmm_get_odr(client, &data);
+ mutex_unlock(&client_data->mutex_odr);
+ } else {
+ err = -EIO;
+ }
+ mutex_unlock(&client_data->mutex_power_mode);
+
+ if (!err) {
+ if (data < ARRAY_SIZE(odr_map))
+ err = sprintf(buf, "%d\n", odr_map[data]);
+ else
+ err = -EINVAL;
+ }
+
+ return err;
+}
+
+static ssize_t bmm_store_odr(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ unsigned long tmp;
+ unsigned char data;
+ int err;
+ struct input_dev *input = to_input_dev(dev);
+ struct bmm_client_data *client_data = input_get_drvdata(input);
+ struct i2c_client *client = client_data->client;
+ u8 power_mode;
+ int i;
+
+ err = strict_strtoul(buf, 10, &tmp);
+ if (err)
+ return err;
+
+ if (tmp > 255)
+ return -EINVAL;
+
+ data = (unsigned char)tmp;
+
+ mutex_lock(&client_data->mutex_power_mode);
+ BMM_CALL_API(get_powermode)(&power_mode);
+ if (power_mode) {
+ for (i = 0; i < ARRAY_SIZE(odr_map); i++) {
+ if (odr_map[i] == data)
+ break;
+ }
+
+ if (i < ARRAY_SIZE(odr_map)) {
+ mutex_lock(&client_data->mutex_odr);
+ err = bmm_set_odr(client, i);
+ if (!err)
+ client_data->odr = i;
+
+ mutex_unlock(&client_data->mutex_odr);
+ } else {
+ err = -EINVAL;
+ }
+ } else {
+ err = -EIO;
+ }
+
+ mutex_unlock(&client_data->mutex_power_mode);
+ if (err)
+ return err;
+
+ return count;
+}
+
+static ssize_t bmm_show_rept_xy(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ unsigned char data = 0;
+ struct input_dev *input = to_input_dev(dev);
+ struct bmm_client_data *client_data = input_get_drvdata(input);
+ int err;
+ u8 power_mode;
+
+ mutex_lock(&client_data->mutex_power_mode);
+ BMM_CALL_API(get_powermode)(&power_mode);
+ printk(KERN_ALERT"%s : value of power_mode after get_powermode %u \n",__func__, power_mode);
+
+ if (power_mode) {
+ mutex_lock(&client_data->mutex_rept_xy);
+ err = BMM_CALL_API(get_repetitions_XY)(&data);
+ printk(KERN_ALERT"%s : value of power_mode after get_repretitions_XY %u \n",__func__, data);
+ mutex_unlock(&client_data->mutex_rept_xy);
+ } else {
+ err = -EIO;
+ }
+
+ mutex_unlock(&client_data->mutex_power_mode);
+
+ if (err)
+ return err;
+
+ return sprintf(buf, "%d\n", data);
+}
+
+static ssize_t bmm_store_rept_xy(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ unsigned long tmp = 0;
+ struct input_dev *input = to_input_dev(dev);
+ struct bmm_client_data *client_data = input_get_drvdata(input);
+ int err;
+ u8 data;
+ u8 power_mode;
+
+ err = strict_strtoul(buf, 10, &tmp);
+ if (err)
+ return err;
+
+ if (tmp > 255)
+ return -EINVAL;
+
+ data = (unsigned char)tmp;
+
+ mutex_lock(&client_data->mutex_power_mode);
+ BMM_CALL_API(get_powermode)(&power_mode);
+ printk(KERN_ALERT"%s : value of power_mode after get_powermode %u\n",__func__, power_mode);
+
+ if (power_mode) {
+ mutex_lock(&client_data->mutex_rept_xy);
+ err = BMM_CALL_API(set_repetitions_XY)(data);
+ printk(KERN_ALERT "%s : value of power_mode after set_repretitions_XY %u \n",__func__,data);
+
+ if (!err) {
+ mdelay(BMM_I2C_WRITE_DELAY_TIME);
+ client_data->rept_xy = data;
+ }
+ mutex_unlock(&client_data->mutex_rept_xy);
+ } else {
+ err = -EIO;
+ }
+ mutex_unlock(&client_data->mutex_power_mode);
+
+ if (err)
+ return err;
+
+ return count;
+}
+
+static ssize_t bmm_show_rept_z(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ unsigned char data = 0;
+ struct input_dev *input = to_input_dev(dev);
+ struct bmm_client_data *client_data = input_get_drvdata(input);
+ int err;
+ u8 power_mode;
+
+ mutex_lock(&client_data->mutex_power_mode);
+ BMM_CALL_API(get_powermode)(&power_mode);
+ printk(KERN_ALERT"%s : value of power_mode after get_powermode %u\n",__func__, power_mode);
+ if (power_mode) {
+ mutex_lock(&client_data->mutex_rept_z);
+ err = BMM_CALL_API(get_repetitions_Z)(&data);
+ printk(KERN_ALERT"%s : value of power_mode after get_repretitions_XY %u \n",__func__, data);
+ mutex_unlock(&client_data->mutex_rept_z);
+ } else {
+ err = -EIO;
+ }
+
+ mutex_unlock(&client_data->mutex_power_mode);
+
+ if (err)
+ return err;
+
+ return sprintf(buf, "%d\n", data);
+}
+
+static ssize_t bmm_store_rept_z(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ unsigned long tmp = 0;
+ struct input_dev *input = to_input_dev(dev);
+ struct bmm_client_data *client_data = input_get_drvdata(input);
+ int err;
+ u8 data;
+ u8 power_mode;
+
+ err = strict_strtoul(buf, 10, &tmp);
+ if (err)
+ return err;
+
+ if (tmp > 255)
+ return -EINVAL;
+
+ data = (unsigned char)tmp;
+
+ mutex_lock(&client_data->mutex_power_mode);
+ BMM_CALL_API(get_powermode)(&power_mode);
+ printk(KERN_ALERT" %s : value of power_mode after get_powermode %u\n",__func__, power_mode);
+ if (power_mode) {
+ mutex_lock(&client_data->mutex_rept_z);
+ err = BMM_CALL_API(set_repetitions_Z)(data);
+ printk(KERN_ALERT"%s : value of power_mode after set_repretitions_XY %u \n",__func__, data);
+ if (!err) {
+ mdelay(BMM_I2C_WRITE_DELAY_TIME);
+ client_data->rept_z = data;
+ }
+ mutex_unlock(&client_data->mutex_rept_z);
+ } else {
+ err = -EIO;
+ }
+ mutex_unlock(&client_data->mutex_power_mode);
+
+ if (err)
+ return err;
+
+ return count;
+}
+
+
+static ssize_t bmm_show_value(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct input_dev *input = to_input_dev(dev);
+ struct bmm_client_data *client_data = input_get_drvdata(input);
+ int count;
+ struct bmm050_mdata_s32 value = {0, 0, 0, 0, 0};
+
+ BMM_CALL_API(read_mdataXYZ_s32)(&value);
+ if (value.drdy) {
+ bmm_remap_sensor_data(&value, client_data);
+ client_data->value = value;
+ } else
+ PERR("data not ready");
+
+ count = sprintf(buf, "%d %d %d\n",
+ client_data->value.datax,
+ client_data->value.datay,
+ client_data->value.dataz);
+ PDEBUG("%d %d %d",
+ client_data->value.datax,
+ client_data->value.datay,
+ client_data->value.dataz);
+
+ return count;
+}
+
+
+static ssize_t bmm_show_value_raw(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct bmm050_mdata value;
+ int count;
+
+ BMM_CALL_API(get_raw_xyz)(&value);
+
+ count = sprintf(buf, "%hd %hd %hd\n",
+ value.datax,
+ value.datay,
+ value.dataz);
+
+ return count;
+}
+
+
+static ssize_t bmm_show_enable(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct input_dev *input = to_input_dev(dev);
+ struct bmm_client_data *client_data = input_get_drvdata(input);
+ int err;
+
+ mutex_lock(&client_data->mutex_enable);
+ err = sprintf(buf, "%d\n", client_data->enable);
+ mutex_unlock(&client_data->mutex_enable);
+ return err;
+}
+
+static void bmm_input_destroy(struct bmm_client_data *client_data)
+{
+ struct input_dev *dev = client_data->input;
+
+ input_unregister_device(dev);
+ input_free_device(dev);
+}
+
+static ssize_t bmm_store_enable(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ unsigned long data;
+ int err;
+ struct input_dev *input = to_input_dev(dev);
+ struct bmm_client_data *client_data = input_get_drvdata(input);
+
+ err = strict_strtoul(buf, 10, &data);
+ if (err)
+ return err;
+
+ data = data ? 1 : 0;
+ mutex_lock(&client_data->mutex_enable);
+
+ if (data != client_data->enable) {
+ if (data) {
+
+ err = bmm_set_op_mode(client_data, BMM_VAL_NAME(NORMAL_MODE));
+ if (err)
+ {
+ PERR(" %s : fail to init h/w of %s",__func__, SENSOR_NAME);
+ err = -EIO;
+ goto exit_err_sysfs;
+ }
+ schedule_delayed_work(
+ &client_data->work,
+ msecs_to_jiffies(atomic_read(
+ &client_data->delay)));
+ } else {
+
+ err = bmm_set_op_mode(client_data, BMM_VAL_NAME(SUSPEND_MODE));
+ if (err)
+ {
+ PERR(" %s : fail to init h/w of %s",__func__, SENSOR_NAME);
+ err = -EIO;
+ goto exit_err_sysfs;
+ }
+ cancel_delayed_work_sync(&client_data->work);
+ }
+
+ client_data->enable = data;
+ }
+ mutex_unlock(&client_data->mutex_enable);
+
+exit_err_sysfs:
+ if (err)
+ bmm_input_destroy(client_data);
+
+ return count;
+}
+
+static ssize_t bmm_show_delay(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct input_dev *input = to_input_dev(dev);
+ struct bmm_client_data *client_data = input_get_drvdata(input);
+
+ return sprintf(buf, "%d\n", atomic_read(&client_data->delay));
+
+}
+
+static ssize_t bmm_store_delay(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ unsigned long data;
+ int err;
+ struct input_dev *input = to_input_dev(dev);
+ struct bmm_client_data *client_data = input_get_drvdata(input);
+
+ err = strict_strtoul(buf, 10, &data);
+ if (err)
+ return err;
+
+ if (data <= 0) {
+ err = -EINVAL;
+ return err;
+ }
+
+ if (data < BMM_DELAY_MIN)
+ data = BMM_DELAY_MIN;
+
+ atomic_set(&client_data->delay, data);
+
+ return count;
+}
+
+static ssize_t bmm_show_test(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct input_dev *input = to_input_dev(dev);
+ struct bmm_client_data *client_data = input_get_drvdata(input);
+ int err;
+
+ err = sprintf(buf, "%d\n", client_data->result_test);
+ return err;
+}
+
+static ssize_t bmm_store_test(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ unsigned long data;
+ int err;
+ struct input_dev *input = to_input_dev(dev);
+ struct bmm_client_data *client_data = input_get_drvdata(input);
+ struct i2c_client *client = client_data->client;
+ u8 dummy;
+
+ err = strict_strtoul(buf, 10, &data);
+ if (err)
+ return err;
+
+ /* the following code assumes the work thread is not running */
+ if (BMM_SELF_TEST == data) {
+ /* self test */
+ err = bmm_set_op_mode(client_data, BMM_VAL_NAME(SLEEP_MODE));
+ mdelay(3);
+ err = BMM_CALL_API(set_selftest)(1);
+ mdelay(3);
+ err = BMM_CALL_API(get_self_test_XYZ)(&dummy);
+ client_data->result_test = dummy;
+ } else if (BMM_ADV_TEST == data) {
+ /* advanced self test */
+ err = BMM_CALL_API(perform_advanced_selftest)(
+ &client_data->result_test);
+ } else {
+ err = -EINVAL;
+ }
+
+ if (!err) {
+ BMM_CALL_API(soft_reset)();
+ mdelay(BMM_I2C_WRITE_DELAY_TIME);
+ bmm_restore_hw_cfg(client);
+ }
+
+ if (err)
+ count = -1;
+
+ return count;
+}
+
+
+static ssize_t bmm_show_reg(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ int err = 0;
+ int i;
+ u8 dbg_buf[64];
+ u8 dbg_buf_str[64 * 3 + 1] = "";
+ struct input_dev *input = to_input_dev(dev);
+ struct bmm_client_data *client_data = input_get_drvdata(input);
+ struct i2c_client *client = client_data->client;
+
+ for (i = 0; i < BYTES_PER_LINE; i++) {
+ dbg_buf[i] = i;
+ sprintf(dbg_buf_str + i * 3, "%02x%c",
+ dbg_buf[i],
+ (((i + 1) % BYTES_PER_LINE == 0) ? '\n' : ' '));
+ }
+ memcpy(buf, dbg_buf_str, BYTES_PER_LINE * 3);
+
+ for (i = 0; i < BYTES_PER_LINE * 3 - 1; i++)
+ dbg_buf_str[i] = '-';
+
+ dbg_buf_str[i] = '\n';
+ memcpy(buf + BYTES_PER_LINE * 3, dbg_buf_str, BYTES_PER_LINE * 3);
+
+
+ bmm_i2c_read(client, BMM_REG_NAME(CHIP_ID), dbg_buf, 64);
+ for (i = 0; i < 64; i++) {
+ sprintf(dbg_buf_str + i * 3, "%02x%c",
+ dbg_buf[i],
+ (((i + 1) % BYTES_PER_LINE == 0) ? '\n' : ' '));
+ }
+ memcpy(buf + BYTES_PER_LINE * 3 + BYTES_PER_LINE * 3,
+ dbg_buf_str, 64 * 3);
+
+ err = BYTES_PER_LINE * 3 + BYTES_PER_LINE * 3 + 64 * 3;
+ return err;
+}
+
+
+static ssize_t bmm_show_place(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+#ifdef CONFIG_BMM_USE_PLATFORM_DATA
+ struct input_dev *input = to_input_dev(dev);
+ struct bmm_client_data *client_data = input_get_drvdata(input);
+#endif
+ int place = BOSCH_SENSOR_PLACE_UNKNOWN;
+
+#ifdef CONFIG_BMM_USE_PLATFORM_DATA
+ if (NULL != client_data->bst_pd)
+ place = client_data->bst_pd->place;
+#endif
+ return sprintf(buf, "%d\n", place);
+}
+
+
+static DEVICE_ATTR(chip_id, S_IRUGO,
+ bmm_show_chip_id, NULL);
+static DEVICE_ATTR(op_mode, S_IRUGO|S_IWUSR,
+ bmm_show_op_mode, bmm_store_op_mode);
+static DEVICE_ATTR(odr, S_IRUGO|S_IWUSR,
+ bmm_show_odr, bmm_store_odr);
+static DEVICE_ATTR(rept_xy, S_IRUGO|S_IWUSR,
+ bmm_show_rept_xy, bmm_store_rept_xy);
+static DEVICE_ATTR(rept_z, S_IRUGO|S_IWUSR,
+ bmm_show_rept_z, bmm_store_rept_z);
+static DEVICE_ATTR(value, S_IRUGO,
+ bmm_show_value, NULL);
+static DEVICE_ATTR(value_raw, S_IRUGO,
+ bmm_show_value_raw, NULL);
+static DEVICE_ATTR(enable, S_IRUGO|S_IWUSR,
+ bmm_show_enable, bmm_store_enable);
+static DEVICE_ATTR(delay, S_IRUGO|S_IWUSR,
+ bmm_show_delay, bmm_store_delay);
+static DEVICE_ATTR(test, S_IRUGO|S_IWUSR,
+ bmm_show_test, bmm_store_test);
+static DEVICE_ATTR(reg, S_IRUGO,
+ bmm_show_reg, NULL);
+static DEVICE_ATTR(place, S_IRUGO,
+ bmm_show_place, NULL);
+
+static struct attribute *bmm_attributes[] = {
+ &dev_attr_chip_id.attr,
+ &dev_attr_op_mode.attr,
+ &dev_attr_odr.attr,
+ &dev_attr_rept_xy.attr,
+ &dev_attr_rept_z.attr,
+ &dev_attr_value.attr,
+ &dev_attr_value_raw.attr,
+ &dev_attr_enable.attr,
+ &dev_attr_delay.attr,
+ &dev_attr_test.attr,
+ &dev_attr_reg.attr,
+ &dev_attr_place.attr,
+ NULL
+};
+
+
+static struct attribute_group bmm_attribute_group = {
+ .attrs = bmm_attributes
+};
+
+
+static struct device_attribute *bmm050_attr[] = {
+ &dev_attr_value,
+ &dev_attr_value_raw,
+ &dev_attr_enable,
+ NULL,
+ };
+
+static int bmm_input_init(struct bmm_client_data *client_data)
+{
+ struct input_dev *dev;
+ int err = 0;
+
+ dev = input_allocate_device();
+ if (NULL == dev)
+ return -ENOMEM;
+
+ dev->name = SENSOR_NAME;
+ dev->id.bustype = BUS_I2C;
+
+ input_set_capability(dev, EV_ABS, ABS_MISC);
+ input_set_abs_params(dev, ABS_X, MAG_VALUE_MIN, MAG_VALUE_MAX, 0, 0);
+ input_set_abs_params(dev, ABS_Y, MAG_VALUE_MIN, MAG_VALUE_MAX, 0, 0);
+ input_set_abs_params(dev, ABS_Z, MAG_VALUE_MIN, MAG_VALUE_MAX, 0, 0);
+ input_set_drvdata(dev, client_data);
+
+ err = input_register_device(dev);
+ if (err < 0) {
+ input_free_device(dev);
+ return err;
+ }
+ client_data->input = dev;
+
+ return 0;
+}
+
+static int bmm_restore_hw_cfg(struct i2c_client *client)
+{
+ int err = 0;
+ u8 value;
+ struct bmm_client_data *client_data =
+ (struct bmm_client_data *)i2c_get_clientdata(client);
+ int op_mode;
+
+ mutex_lock(&client_data->mutex_op_mode);
+ err = bmm_set_op_mode(client_data, BMM_VAL_NAME(SLEEP_MODE));
+
+ if (bmm_get_op_mode_idx(client_data->op_mode) != -1)
+ err = bmm_set_op_mode(client_data, client_data->op_mode);
+
+ op_mode = client_data->op_mode;
+ mutex_unlock(&client_data->mutex_op_mode);
+
+ if (BMM_VAL_NAME(SUSPEND_MODE) == op_mode)
+ return err;
+
+ PINFO("app did not close this sensor before suspend");
+
+ mutex_lock(&client_data->mutex_odr);
+ BMM_CALL_API(set_datarate)(client_data->odr);
+ mdelay(BMM_I2C_WRITE_DELAY_TIME);
+ mutex_unlock(&client_data->mutex_odr);
+
+ mutex_lock(&client_data->mutex_rept_xy);
+ err = bmm_i2c_write(client, BMM_REG_NAME(NO_REPETITIONS_XY),
+ &client_data->rept_xy, 1);
+ mdelay(BMM_I2C_WRITE_DELAY_TIME);
+ err = bmm_i2c_read(client, BMM_REG_NAME(NO_REPETITIONS_XY), &value, 1);
+ PINFO("BMM_NO_REPETITIONS_XY: %02x", value);
+ mutex_unlock(&client_data->mutex_rept_xy);
+
+ mutex_lock(&client_data->mutex_rept_z);
+ err = bmm_i2c_write(client, BMM_REG_NAME(NO_REPETITIONS_Z),
+ &client_data->rept_z, 1);
+ mdelay(BMM_I2C_WRITE_DELAY_TIME);
+ err = bmm_i2c_read(client, BMM_REG_NAME(NO_REPETITIONS_Z), &value, 1);
+ PINFO("BMM_NO_REPETITIONS_Z: %02x", value);
+ mutex_unlock(&client_data->mutex_rept_z);
+
+ mutex_lock(&client_data->mutex_op_mode);
+ if (BMM_OP_MODE_UNKNOWN == (int)client_data->op_mode) {
+ bmm_set_forced_mode(client);
+ PINFO("set forced mode after hw_restore");
+ mdelay(bmm_get_forced_drdy_time(client_data->rept_xy,
+ client_data->rept_z));
+ }
+ mutex_unlock(&client_data->mutex_op_mode);
+
+
+ PINFO("register dump after init");
+ bmm_dump_reg(client);
+
+ return err;
+}
+
+static int bmm_probe(struct i2c_client *client, const struct i2c_device_id *id)
+{
+ int err = 0;
+ struct bmm_client_data *client_data = NULL;
+ int dummy;
+
+ printk(KERN_ALERT"%s : function entrance\n",__func__);
+
+ if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
+ PERR("%s : i2c_check_functionality error!\n",__func__);
+ err = -EIO;
+ goto exit_err_clean;
+ }
+
+ if (NULL == bmm_client) {
+ bmm_client = client;
+ } else {
+ PERR("this driver does not support multiple clients");
+ err = -EBUSY;
+ return err;
+ }
+
+ /* wake up the chip */
+ dummy = bmm_wakeup(client);
+ if (dummy < 0) {
+ PERR("Cannot wake up %s, I2C xfer error", SENSOR_NAME);
+ err = -EIO;
+ goto exit_err_clean;
+ }
+
+ PINFO("register dump after waking up");
+ bmm_dump_reg(client);
+ /* check chip id */
+ err = bmm_check_chip_id(client);
+ if (!err) {
+ PNOTICE("Bosch Sensortec Device %s detected: %#x",
+ SENSOR_NAME, client->addr);
+ } else {
+ PERR("Bosch Sensortec Device not found, chip id mismatch");
+ err = -1;
+ goto exit_err_clean;
+ }
+
+ client_data = kzalloc(sizeof(struct bmm_client_data), GFP_KERNEL);
+ if (NULL == client_data) {
+ PERR("no memory available");
+ err = -ENOMEM;
+ goto exit_err_clean;
+ }
+
+ i2c_set_clientdata(client, client_data);
+ client_data->client = client;
+
+ mutex_init(&client_data->mutex_power_mode);
+ mutex_init(&client_data->mutex_op_mode);
+ mutex_init(&client_data->mutex_enable);
+ mutex_init(&client_data->mutex_odr);
+ mutex_init(&client_data->mutex_rept_xy);
+ mutex_init(&client_data->mutex_rept_z);
+ mutex_init(&client_data->mutex_value);
+
+ /* input device init */
+ err = bmm_input_init(client_data);
+ if (err < 0)
+ goto exit_err_clean;
+
+ /* sysfs node creation */
+ err = sysfs_create_group(&client_data->input->dev.kobj,
+ &bmm_attribute_group);
+ printk(KERN_ALERT"%s : sysfs for magnetic created \n",__func__);
+
+ if (err < 0)
+ goto exit_err_sysfs;
+
+#ifdef CONFIG_BMM_USE_PLATFORM_DATA
+
+ printk(KERN_ALERT"%s Entering Plat Data bmm \n",__func__);
+ if (NULL != client->dev.platform_data) {
+ client_data->bst_pd = kzalloc(sizeof(*client_data->bst_pd),
+ GFP_KERNEL);
+
+ if (NULL != client_data->bst_pd) {
+ memcpy(client_data->bst_pd, client->dev.platform_data,
+ sizeof(*client_data->bst_pd));
+
+ printk(KERN_ALERT" %s : platform data of bmm %s: place: %d, irq: %d",__func__,
+ client_data->bst_pd->name,
+ client_data->bst_pd->place,
+ client_data->bst_pd->irq);
+ }
+ }
+#endif
+
+
+ /* workqueue init */
+ INIT_DELAYED_WORK(&client_data->work, bmm_work_func);
+ atomic_set(&client_data->delay, BMM_DELAY_DEFAULT);
+
+ /* h/w init */
+ client_data->device.bus_read = bmm_i2c_read_wrapper;
+ client_data->device.bus_write = bmm_i2c_write_wrapper;
+ client_data->device.delay_msec = bmm_delay;
+
+ BMM_CALL_API(init)(&client_data->device);
+
+ bmm_dump_reg(client);
+
+ PDEBUG(" %s : trimming_reg x1: %d y1: %d x2: %d y2: %d xy1: %d xy2: %d\n",__func__,
+ client_data->device.dig_x1,
+ client_data->device.dig_y1,
+ client_data->device.dig_x2,
+ client_data->device.dig_y2,
+ client_data->device.dig_xy1,
+ client_data->device.dig_xy2);
+
+ PDEBUG(" %s : trimming_reg z1: %d z2: %d z3: %d z4: %d xyz1: %d\n",__func__,
+ client_data->device.dig_z1,
+ client_data->device.dig_z2,
+ client_data->device.dig_z3,
+ client_data->device.dig_z4,
+ client_data->device.dig_xyz1);
+
+ client_data->enable = 0;
+ /* now it's power on which is considered as resuming from suspend */
+ client_data->op_mode = BMM_VAL_NAME(SUSPEND_MODE);
+ client_data->odr = BMM_DEFAULT_ODR;
+ client_data->rept_xy = BMM_DEFAULT_REPETITION_XY;
+ client_data->rept_z = BMM_DEFAULT_REPETITION_Z;
+
+
+#if 0
+ err = bmm_restore_hw_cfg(client);
+#else
+
+ err = bmm_set_op_mode(client_data, BMM_VAL_NAME(SUSPEND_MODE));
+ if (err) {
+ PERR(" %s : fail to init h/w of %s",__func__, SENSOR_NAME);
+ err = -EIO;
+ goto exit_err_sysfs;
+ }
+#endif
+ printk(KERN_ALERT"%s ... Removed SUSPEND_MODE \n",__func__);
+#ifdef CONFIG_HAS_EARLYSUSPEND
+ client_data->early_suspend_handler.level =
+ EARLY_SUSPEND_LEVEL_BLANK_SCREEN + 1;
+ client_data->early_suspend_handler.suspend = bmm_early_suspend;
+ client_data->early_suspend_handler.resume = bmm_late_resume;
+ register_early_suspend(&client_data->early_suspend_handler);
+#endif
+
+ err = sensors_register(&bmm_device,client_data,bmm050_attr, "magnetic_sensor");
+ if (err < 0 )
+ {
+ pr_err("%s: could not register" "magnetic_sensor device(%d).\n", __func__, err);
+ goto exit_err_sysfs;
+ }
+
+ PNOTICE(" %s : sensor %s probed successfully \n",__func__, SENSOR_NAME);
+
+ PDEBUG(" %s : i2c_client: %p client_data: %p i2c_device: %p input: %p\n",__func__,
+ client, client_data, &client->dev, client_data->input);
+
+ return 0;
+
+exit_err_sysfs:
+ if (err)
+ bmm_input_destroy(client_data);
+
+exit_err_clean:
+ if (err) {
+ if (client_data != NULL) {
+#ifdef CONFIG_BMM_USE_PLATFORM_DATA
+ if (NULL != client_data->bst_pd) {
+ kfree(client_data->bst_pd);
+ client_data->bst_pd = NULL;
+ }
+#endif
+ kfree(client_data);
+ client_data = NULL;
+ }
+
+ bmm_client = NULL;
+ }
+
+ return err;
+}
+
+static int bmm_pre_suspend(struct i2c_client *client)
+{
+ int err = 0;
+ struct bmm_client_data *client_data =
+ (struct bmm_client_data *)i2c_get_clientdata(client);
+ PDEBUG("function entrance");
+
+ mutex_lock(&client_data->mutex_enable);
+ if (client_data->enable) {
+ cancel_delayed_work_sync(&client_data->work);
+ PDEBUG("cancel work");
+ }
+ mutex_unlock(&client_data->mutex_enable);
+
+ return err;
+}
+
+static int bmm_post_resume(struct i2c_client *client)
+{
+ int err = 0;
+ struct bmm_client_data *client_data =
+ (struct bmm_client_data *)i2c_get_clientdata(client);
+
+ mutex_lock(&client_data->mutex_enable);
+ if (client_data->enable) {
+ schedule_delayed_work(&client_data->work,
+ msecs_to_jiffies(
+ atomic_read(&client_data->delay)));
+ }
+ mutex_unlock(&client_data->mutex_enable);
+
+ return err;
+}
+
+#ifdef CONFIG_HAS_EARLYSUSPEND
+static void bmm_early_suspend(struct early_suspend *handler)
+{
+ int err = 0;
+ struct bmm_client_data *client_data =
+ (struct bmm_client_data *)container_of(handler,
+ struct bmm_client_data, early_suspend_handler);
+ struct i2c_client *client = client_data->client;
+ u8 power_mode;
+ PDEBUG("function entrance");
+
+ mutex_lock(&client_data->mutex_power_mode);
+ BMM_CALL_API(get_powermode)(&power_mode);
+ if (power_mode) {
+ err = bmm_pre_suspend(client);
+ err = bmm_set_op_mode(client_data, BMM_VAL_NAME(SUSPEND_MODE));
+ }
+ mutex_unlock(&client_data->mutex_power_mode);
+
+}
+
+static void bmm_late_resume(struct early_suspend *handler)
+{
+ int err = 0;
+ struct bmm_client_data *client_data =
+ (struct bmm_client_data *)container_of(handler,
+ struct bmm_client_data, early_suspend_handler);
+ struct i2c_client *client = client_data->client;
+ PDEBUG("function entrance");
+
+ mutex_lock(&client_data->mutex_power_mode);
+
+ err = bmm_restore_hw_cfg(client);
+ /* post resume operation */
+ bmm_post_resume(client);
+
+ mutex_unlock(&client_data->mutex_power_mode);
+}
+#else
+static int bmm_suspend(struct i2c_client *client, pm_message_t mesg)
+{
+ int err = 0;
+ struct bmm_client_data *client_data =
+ (struct bmm_client_data *)i2c_get_clientdata(client);
+ u8 power_mode;
+
+ PDEBUG("function entrance");
+
+ mutex_lock(&client_data->mutex_power_mode);
+ BMM_CALL_API(get_powermode)(&power_mode);
+ if (power_mode) {
+ err = bmm_pre_suspend(client);
+ err = bmm_set_op_mode(client_data, BMM_VAL_NAME(SUSPEND_MODE));
+ }
+ mutex_unlock(&client_data->mutex_power_mode);
+
+ return err;
+}
+
+static int bmm_resume(struct i2c_client *client)
+{
+ int err = 0;
+ struct bmm_client_data *client_data =
+ (struct bmm_client_data *)i2c_get_clientdata(client);
+
+ PDEBUG("function entrance");
+
+ mutex_lock(&client_data->mutex_power_mode);
+ err = bmm_restore_hw_cfg(client);
+ /* post resume operation */
+ bmm_post_resume(client);
+
+ mutex_unlock(&client_data->mutex_power_mode);
+
+ return err;
+}
+#endif
+
+static int bmm_remove(struct i2c_client *client)
+{
+ int err = 0;
+ struct bmm_client_data *client_data =
+ (struct bmm_client_data *)i2c_get_clientdata(client);
+
+ if (NULL != client_data) {
+#ifdef CONFIG_HAS_EARLYSUSPEND
+ unregister_early_suspend(&client_data->early_suspend_handler);
+#endif
+
+ mutex_lock(&client_data->mutex_op_mode);
+ if (BMM_VAL_NAME(NORMAL_MODE) == client_data->op_mode) {
+ cancel_delayed_work_sync(&client_data->work);
+ PDEBUG("cancel work");
+ }
+ mutex_unlock(&client_data->mutex_op_mode);
+
+ err = bmm_set_op_mode(client_data, BMM_VAL_NAME(SUSPEND_MODE));
+ mdelay(BMM_I2C_WRITE_DELAY_TIME);
+
+ sensors_unregister(bmm_device, bmm050_attr);
+
+ sysfs_remove_group(&client_data->input->dev.kobj,
+ &bmm_attribute_group);
+ bmm_input_destroy(client_data);
+
+#ifdef CONFIG_BMM_USE_PLATFORM_DATA
+ if (NULL != client_data->bst_pd) {
+ kfree(client_data->bst_pd);
+ client_data->bst_pd = NULL;
+ }
+#endif
+ kfree(client_data);
+
+ bmm_client = NULL;
+ }
+
+ return err;
+}
+
+static const struct i2c_device_id bmm_id[] = {
+ {SENSOR_NAME, 0},
+ {}
+};
+
+MODULE_DEVICE_TABLE(i2c, bmm_id);
+
+static struct i2c_driver bmm_driver = {
+ .driver = {
+ .owner = THIS_MODULE,
+ .name = SENSOR_NAME,
+ },
+ .class = I2C_CLASS_HWMON,
+ .id_table = bmm_id,
+ .probe = bmm_probe,
+ .remove = bmm_remove,
+#ifndef CONFIG_HAS_EARLYSUSPEND
+ .suspend = bmm_suspend,
+ .resume = bmm_resume,
+#endif
+};
+
+static int __init BMM_init(void)
+{
+ return i2c_add_driver(&bmm_driver);
+}
+
+static void __exit BMM_exit(void)
+{
+ i2c_del_driver(&bmm_driver);
+}
+
+MODULE_AUTHOR("Zhengguang.Guo <Zhengguang.Guo@bosch-sensortec.com>");
+MODULE_DESCRIPTION("driver for " SENSOR_NAME);
+MODULE_LICENSE("GPL");
+
+module_init(BMM_init);
+module_exit(BMM_exit);