aboutsummaryrefslogtreecommitdiff
path: root/drivers/hwmon
diff options
context:
space:
mode:
authorChethan Krishna N <chethan.krishna@stericsson.com>2010-11-17 17:13:52 +0530
committerSundar Iyer <sundar.iyer@stericsson.com>2010-11-29 16:47:35 +0530
commit6aeff4e1800b0535a63f8d2b89931782e49bdf02 (patch)
tree991e19dc472a33909a95df1d2f0c47c8d8a135ad /drivers/hwmon
parent17134ce72212575a88e0dd9ea4cd25978e31f061 (diff)
lsm303dlh : adding input device interface with interrupt
This replaces the previous IOCTL interface with SYSFS interface. Also adds support for input device interface with interrupt. ST-Ericsson ID: ER272838 Signed-off-by: Chethan Krishna N <chethan.krishna@stericsson.com> Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/8451 Reviewed-by: Jonas ABERG <jonas.aberg@stericsson.com> Change-Id: Ie60fb3fbd17534fdeb9ee79cf8dfd98a408253d0
Diffstat (limited to 'drivers/hwmon')
-rw-r--r--drivers/hwmon/Kconfig9
-rw-r--r--drivers/hwmon/lsm303dlh_a.c1534
-rw-r--r--drivers/hwmon/lsm303dlh_m.c909
3 files changed, 1423 insertions, 1029 deletions
diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig
index 1dac968e635..2a6346e2736 100644
--- a/drivers/hwmon/Kconfig
+++ b/drivers/hwmon/Kconfig
@@ -630,6 +630,15 @@ config SENSORS_LSM303DLH
Say Y here if you have a device containing lsm303dlh chip.
+config SENSORS_LSM303DLH_INPUT_DEVICE
+ bool "ST LSM303DLH INPUT DEVICE"
+ depends on SENSORS_LSM303DLH
+ default n
+ help
+ This driver allows device to be used as an input device with
+ interrupts, need to be enabled only when input device support
+ is required.
+
config SENSORS_LTC4215
tristate "Linear Technology LTC4215"
depends on I2C && EXPERIMENTAL
diff --git a/drivers/hwmon/lsm303dlh_a.c b/drivers/hwmon/lsm303dlh_a.c
index 49ef82f6bca..a3f6e180951 100644
--- a/drivers/hwmon/lsm303dlh_a.c
+++ b/drivers/hwmon/lsm303dlh_a.c
@@ -1,803 +1,1093 @@
-/******************** (C) COPYRIGHT 2010 STMicroelectronics ********************
-*
-* File Name : lsm303dlh_a_char.c
-* Authors : MH - C&I BU - Application Team
-* : Carmine Iascone (carmine.iascone@st.com)
-* : Matteo Dameno (matteo.dameno@st.com)
-* Version : V 0.3
-* Date : 24/02/2010
-* Description : LSM303DLH 6D module sensor API
-*
-********************************************************************************
-*
-* Author: Mian Yousaf Kaukab <mian.yousaf.kaukab@stericsson.com>
-* Copyright 2010 (c) ST-Ericsson AB
-*
-********************************************************************************
-*
-* 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.
-*
-* THE PRESENT SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES
-* OR CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED, FOR THE SOLE
-* PURPOSE TO SUPPORT YOUR APPLICATION DEVELOPMENT.
-* AS A RESULT, STMICROELECTRONICS SHALL NOT BE HELD LIABLE FOR ANY DIRECT,
-* INDIRECT OR CONSEQUENTIAL DAMAGES WITH RESPECT TO ANY CLAIMS ARISING FROM THE
-* CONTENT OF SUCH SOFTWARE AND/OR THE USE MADE BY CUSTOMERS OF THE CODING
-* INFORMATION CONTAINED HEREIN IN CONNECTION WITH THEIR PRODUCTS.
-*
-* THIS SOFTWARE IS SPECIFICALLY DESIGNED FOR EXCLUSIVE USE WITH ST PARTS.
-*
-******************************************************************************/
-
-#include <linux/kernel.h>
-#include <linux/module.h>
-#include <linux/fs.h>
+/*
+ * lsm303dlh_a.c
+ * ST 3-Axis Accelerometer Driver
+ *
+ * Copyright (C) 2010 STMicroelectronics
+ * Author: Carmine Iascone (carmine.iascone@st.com)
+ * Author: Matteo Dameno (matteo.dameno@st.com)
+ *
+ * Copyright (C) 2010 STEricsson
+ * Author: Mian Yousaf Kaukab <mian.yousaf.kaukab@stericsson.com>
+ * Updated:Preetham Rao Kaskurthi <preetham.rao@stericsson.com>
+ *
+ * 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, see <http://www.gnu.org/licenses/>.
+ */
+
#include <linux/i2c.h>
-#include <linux/i2c-dev.h>
-#include <linux/uaccess.h>
-#include <linux/lsm303dlh.h>
-#include <linux/miscdevice.h>
-#include <linux/input.h>
-#include <linux/interrupt.h>
#include <linux/slab.h>
-#include <linux/kthread.h>
+#include <linux/mutex.h>
+#include <linux/platform_device.h>
#include <linux/delay.h>
-#include <linux/freezer.h>
-#include <linux/regulator/consumer.h>
+#include <linux/err.h>
-/* Temp defines till GPIO extender driver is fixed. */
-/* #define A_USE_INT */
-/* #define A_USE_GPIO_EXTENDER_INT */
-
-/* lsm303dlh accelerometer registers */
-#define WHO_AM_I 0x0F
-
-/* ctrl 1: pm2 pm1 pm0 dr1 dr0 zenable yenable zenable */
-#define CTRL_REG1 0x20 /* power control reg */
-#define CTRL_REG2 0x21 /* power control reg */
-#define CTRL_REG3 0x22 /* power control reg */
-#define CTRL_REG4 0x23 /* interrupt control reg */
-#define CTRL_REG5 0x24 /* interrupt control reg */
-#define AXISDATA_REG 0x28
-#define INT1_SRC_A_REG 0x31 /* interrupt 1 source reg */
-#define INT2_SRC_A_REG 0x35 /* interrupt 2 source reg */
-#define MULTIPLE_I2C_TR 0x80 /* Multiple byte transfer enable */
-
-/* Sensitivity adjustment */
-#define SHIFT_ADJ_2G 4 /* 1/16*/
-#define SHIFT_ADJ_4G 3 /* 2/16*/
-#define SHIFT_ADJ_8G 2 /* ~3.9/16*/
-
-/* Control register 1 */
-#define LSM303DLH_A_CR1_PM_BIT 5
-#define LSM303DLH_A_CR1_PM_MASK (0x7 << LSM303DLH_A_CR1_PM_BIT)
-#define LSM303DLH_A_CR1_DR_BIT 3
-#define LSM303DLH_A_CR1_DR_MASK (0x3 << LSM303DLH_A_CR1_DR_BIT)
-#define LSM303DLH_A_CR1_EN_BIT 0
-#define LSM303DLH_A_CR1_EN_MASK (0x7 << LSM303DLH_A_CR1_EN_BIT)
-
-/* Control register 2 */
-#define LSM303DLH_A_CR4_ST_BIT 1
-#define LSM303DLH_A_CR4_ST_MASK (0x1 << LSM303DLH_A_CR4_ST_BIT)
-#define LSM303DLH_A_CR4_STS_BIT 3
-#define LSM303DLH_A_CR4_STS_MASK (0x1 << LSM303DLH_A_CR4_STS_BIT)
-#define LSM303DLH_A_CR4_FS_BIT 4
-#define LSM303DLH_A_CR4_FS_MASK (0x3 << LSM303DLH_A_CR4_FS_BIT)
-#define LSM303DLH_A_CR4_BLE_BIT 6
-#define LSM303DLH_A_CR4_BLE_MASK (0x3 << LSM303DLH_A_CR4_BLE_BIT)
-
-/* Control register 3 */
-#define LSM303DLH_A_CR3_I1_BIT 0
-#define LSM303DLH_A_CR3_I1_MASK (0x3 << LSM303DLH_A_CR3_I1_BIT)
-#define LSM303DLH_A_CR3_LIR1_BIT 2
-#define LSM303DLH_A_CR3_LIR1_MASK (0x1 << LSM303DLH_A_CR3_LIR1_BIT)
-#define LSM303DLH_A_CR3_I2_BIT 3
-#define LSM303DLH_A_CR3_I2_MASK (0x3 << LSM303DLH_A_CR3_I2_BIT)
-#define LSM303DLH_A_CR3_LIR2_BIT 5
-#define LSM303DLH_A_CR3_LIR2_MASK (0x1 << LSM303DLH_A_CR3_LIR2_BIT)
-#define LSM303DLH_A_CR3_PPOD_BIT 6
-#define LSM303DLH_A_CR3_PPOD_MASK (0x1 << LSM303DLH_A_CR3_PPOD_BIT)
-#define LSM303DLH_A_CR3_IHL_BIT 7
-#define LSM303DLH_A_CR3_IHL_MASK (0x1 << LSM303DLH_A_CR3_IHL_BIT)
-
-#define LSM303DLH_A_CR3_I_SELF 0x0
-#define LSM303DLH_A_CR3_I_OR 0x1
-#define LSM303DLH_A_CR3_I_DATA 0x2
-#define LSM303DLH_A_CR3_I_BOOT 0x3
-
-#define LSM303DLH_A_CR3_LIR_LATCH 0x1
+#ifdef CONFIG_SENSORS_LSM303DLH_INPUT_DEVICE
+#include <linux/input.h>
+#include <linux/interrupt.h>
+#include <mach/gpio.h>
+#endif
-/*
- * lsm303dlh_a acceleration data
- * brief Structure containing acceleration values for x,y and z-axis in
- * signed short
- */
+#include <linux/lsm303dlh.h>
+#include <linux/regulator/consumer.h>
+ /* lsm303dlh accelerometer registers */
+ #define WHO_AM_I 0x0F
+
+ /* ctrl 1: pm2 pm1 pm0 dr1 dr0 zenable yenable zenable */
+ #define CTRL_REG1 0x20 /* power control reg */
+ #define CTRL_REG2 0x21 /* power control reg */
+ #define CTRL_REG3 0x22 /* power control reg */
+ #define CTRL_REG4 0x23 /* interrupt control reg */
+ #define CTRL_REG5 0x24 /* interrupt control reg */
+
+ #define STATUS_REG 0x27 /* status register */
+
+ #define AXISDATA_REG 0x28 /* axis data */
+
+ #define INT1_CFG 0x30 /* interrupt 1 configuration */
+ #define INT1_SRC 0x31 /* interrupt 1 source reg */
+ #define INT1_THS 0x32 /* interrupt 1 threshold */
+ #define INT1_DURATION 0x33 /* interrupt 1 threshold */
+
+ #define INT2_CFG 0x34 /* interrupt 2 configuration */
+ #define INT2_SRC 0x35 /* interrupt 2 source reg */
+ #define INT2_THS 0x36 /* interrupt 2 threshold */
+ #define INT2_DURATION 0x37 /* interrupt 2 threshold */
+
+ /* Sensitivity adjustment */
+ #define SHIFT_ADJ_2G 4 /* 1/16*/
+ #define SHIFT_ADJ_4G 3 /* 2/16*/
+ #define SHIFT_ADJ_8G 2 /* ~3.9/16*/
+
+ /* Control register 1 */
+ #define LSM303DLH_A_CR1_PM_BIT 5
+ #define LSM303DLH_A_CR1_PM_MASK (0x7 << LSM303DLH_A_CR1_PM_BIT)
+ #define LSM303DLH_A_CR1_DR_BIT 3
+ #define LSM303DLH_A_CR1_DR_MASK (0x3 << LSM303DLH_A_CR1_DR_BIT)
+ #define LSM303DLH_A_CR1_EN_BIT 0
+ #define LSM303DLH_A_CR1_EN_MASK (0x7 << LSM303DLH_A_CR1_EN_BIT)
+
+ /* Control register 2 */
+ #define LSM303DLH_A_CR4_ST_BIT 1
+ #define LSM303DLH_A_CR4_ST_MASK (0x1 << LSM303DLH_A_CR4_ST_BIT)
+ #define LSM303DLH_A_CR4_STS_BIT 3
+ #define LSM303DLH_A_CR4_STS_MASK (0x1 << LSM303DLH_A_CR4_STS_BIT)
+ #define LSM303DLH_A_CR4_FS_BIT 4
+ #define LSM303DLH_A_CR4_FS_MASK (0x3 << LSM303DLH_A_CR4_FS_BIT)
+ #define LSM303DLH_A_CR4_BLE_BIT 6
+ #define LSM303DLH_A_CR4_BLE_MASK (0x3 << LSM303DLH_A_CR4_BLE_BIT)
+ #define LSM303DLH_A_CR4_BDU_BIT 7
+ #define LSM303DLH_A_CR4_BDU_MASK (0x1 << LSM303DLH_A_CR4_BDU_BIT)
+
+ /* Control register 3 */
+ #define LSM303DLH_A_CR3_I1_BIT 0
+ #define LSM303DLH_A_CR3_I1_MASK (0x3 << LSM303DLH_A_CR3_I1_BIT)
+ #define LSM303DLH_A_CR3_LIR1_BIT 2
+ #define LSM303DLH_A_CR3_LIR1_MASK (0x1 << LSM303DLH_A_CR3_LIR1_BIT)
+ #define LSM303DLH_A_CR3_I2_BIT 3
+ #define LSM303DLH_A_CR3_I2_MASK (0x3 << LSM303DLH_A_CR3_I2_BIT)
+ #define LSM303DLH_A_CR3_LIR2_BIT 5
+ #define LSM303DLH_A_CR3_LIR2_MASK (0x1 << LSM303DLH_A_CR3_LIR2_BIT)
+ #define LSM303DLH_A_CR3_PPOD_BIT 6
+ #define LSM303DLH_A_CR3_PPOD_MASK (0x1 << LSM303DLH_A_CR3_PPOD_BIT)
+ #define LSM303DLH_A_CR3_IHL_BIT 7
+ #define LSM303DLH_A_CR3_IHL_MASK (0x1 << LSM303DLH_A_CR3_IHL_BIT)
+
+ #define LSM303DLH_A_CR3_I_SELF 0x0
+ #define LSM303DLH_A_CR3_I_OR 0x1
+ #define LSM303DLH_A_CR3_I_DATA 0x2
+ #define LSM303DLH_A_CR3_I_BOOT 0x3
+
+ #define LSM303DLH_A_CR3_LIR_LATCH 0x1
+
+ /* Range */
+ #define LSM303DLH_A_RANGE_2G 0x00
+ #define LSM303DLH_A_RANGE_4G 0x01
+ #define LSM303DLH_A_RANGE_8G 0x03
+
+ /* Mode */
+ #define LSM303DLH_A_MODE_OFF 0x00
+ #define LSM303DLH_A_MODE_NORMAL 0x01
+ #define LSM303DLH_A_MODE_LP_HALF 0x02
+ #define LSM303DLH_A_MODE_LP_1 0x03
+ #define LSM303DLH_A_MODE_LP_2 0x02
+ #define LSM303DLH_A_MODE_LP_5 0x05
+ #define LSM303DLH_A_MODE_LP_10 0x06
+
+ /* Rate */
+ #define LSM303DLH_A_RATE_50 0x00
+ #define LSM303DLH_A_RATE_100 0x01
+ #define LSM303DLH_A_RATE_400 0x02
+ #define LSM303DLH_A_RATE_1000 0x03
+
+/* Multiple byte transfer enable */
+#define MULTIPLE_I2C_TR 0x80
+
+/* Range -2048 to 2047 */
struct lsm303dlh_a_t {
- short x; /* < x-axis acceleration data. Range -2048 to 2047. */
- short y; /* < y-axis acceleration data. Range -2048 to 2047. */
- short z; /* < z-axis acceleration data. Range -2048 to 2047. */
+ short x;
+ short y;
+ short z;
};
+/*
+ * accelerometer local data
+ */
struct lsm303dlh_a_data {
struct i2c_client *client;
- struct lsm303dlh_platform_data pdata;
+ /* lock for sysfs operations */
+ struct mutex lock;
+ struct lsm303dlh_a_t data;
+
+#ifdef CONFIG_SENSORS_LSM303DLH_INPUT_DEVICE
struct input_dev *input_dev;
- struct work_struct work;
- struct regulator *regulator;
- u8 shift_adj;
- u8 pre_suspend_mode;
- u8 mode;
- u8 rate;
- short ms;
- atomic_t user_count;
-#ifndef A_USE_INT
- struct task_struct *kthread;
+ struct input_dev *input_dev2;
#endif
-};
-static struct lsm303dlh_a_data *file_private;
+ struct lsm303dlh_platform_data pdata;
+ struct regulator *regulator;
-/* set lsm303dlh accelerometer bandwidth */
-int lsm303dlh_a_set_rate(struct lsm303dlh_a_data *ldata, unsigned char bw)
-{
- unsigned char data;
+ unsigned char range;
+ unsigned char mode;
+ unsigned char rate;
+ int shift_adjust;
- if (ldata->mode == LSM303DLH_A_MODE_OFF) {
- dev_err(&ldata->client->dev,
- "lsm303dlh is already off, cannot set rate\n");
- return -EINVAL;
- }
+ unsigned char interrupt_control;
+ unsigned int interrupt_channel;
- data = i2c_smbus_read_byte_data(ldata->client, CTRL_REG1);
- data &= ~LSM303DLH_A_CR1_DR_MASK;
- ldata->rate = bw;
- data |= ((bw << LSM303DLH_A_CR1_DR_BIT) & LSM303DLH_A_CR1_DR_MASK);
+ unsigned char interrupt_configure[2];
+ unsigned char interrupt_duration[2];
+ unsigned char interrupt_threshold[2];
+};
- return i2c_smbus_write_byte_data(ldata->client, CTRL_REG1, data);
+static int lsm303dlh_a_write(struct lsm303dlh_a_data *ddata, u8 reg,
+ u8 val, char *msg)
+{
+ int ret = i2c_smbus_write_byte_data(ddata->client, reg, val);
+ if (ret < 0)
+ dev_err(&ddata->client->dev,
+ "i2c_smbus_write_byte_data failed error %d\
+ Register (%s)\n", ret, msg);
+ return ret;
}
-/* read selected bandwidth from lsm303dlh_acc */
-int lsm303dlh_a_get_bandwidth(struct lsm303dlh_a_data *ldata, unsigned char *bw)
+static int lsm303dlh_a_read(struct lsm303dlh_a_data *ddata, u8 reg, char *msg)
{
- unsigned char data;
+ int ret = i2c_smbus_read_byte_data(ddata->client, reg);
+ if (ret < 0)
+ dev_err(&ddata->client->dev,
+ "i2c_smbus_read_byte_data failed error %d\
+ Register (%s)\n", ret, msg);
+ return ret;
+}
- if (ldata->mode == LSM303DLH_A_MODE_OFF) {
- dev_err(&ldata->client->dev,
- "lsm303dlh is already off, cannot get bandwidth\n");
- return -EINVAL;
- }
+static int lsm303dlh_a_restore(struct lsm303dlh_a_data *ddata)
+{
+ unsigned char reg;
+ unsigned char shifted_mode = (ddata->mode << LSM303DLH_A_CR1_PM_BIT);
+ unsigned char shifted_rate = (ddata->rate << LSM303DLH_A_CR1_DR_BIT);
+ unsigned char context = (shifted_mode | shifted_rate);
+ int ret = 0;
+
+ /* BDU should be enabled by default/recommened */
+ reg = ddata->range;
+ reg |= LSM303DLH_A_CR4_BDU_MASK;
+
+ ret = lsm303dlh_a_write(ddata, CTRL_REG1, context,
+ "CTRL_REG1");
+ if (ret < 0)
+ goto fail;
+
+ ret = lsm303dlh_a_write(ddata, CTRL_REG4, reg, "CTRL_REG4");
+
+ if (ret < 0)
+ goto fail;
+
+ ret = lsm303dlh_a_write(ddata, CTRL_REG3, ddata->interrupt_control,
+ "CTRL_REG3");
+
+ if (ret < 0)
+ goto fail;
+
+ ret = lsm303dlh_a_write(ddata, INT1_CFG, ddata->interrupt_configure[0],
+ "INT1_CFG");
+
+ if (ret < 0)
+ goto fail;
+
+ ret = lsm303dlh_a_write(ddata, INT2_CFG, ddata->interrupt_configure[1],
+ "INT2_CFG");
+
+ if (ret < 0)
+ goto fail;
+
+ ret = lsm303dlh_a_write(ddata, INT1_THS, ddata->interrupt_threshold[0],
+ "INT1_THS");
+
+ if (ret < 0)
+ goto fail;
+
+ ret = lsm303dlh_a_write(ddata, INT2_THS, ddata->interrupt_threshold[1],
+ "INT2_THS");
+
+ if (ret < 0)
+ goto fail;
+
+ ret = lsm303dlh_a_write(ddata, INT1_DURATION,
+ ddata->interrupt_duration[0], "INT1_DURATION");
+
+ if (ret < 0)
+ goto fail;
- data = i2c_smbus_read_byte_data(ldata->client, CTRL_REG1);
- data &= LSM303DLH_A_CR1_DR_MASK;
- data >>= LSM303DLH_A_CR1_DR_BIT;
+ ret = lsm303dlh_a_write(ddata, INT1_DURATION,
+ ddata->interrupt_duration[1], "INT1_DURATION");
- return data;
+ if (ret < 0)
+ goto fail;
+
+fail:
+ return ret;
}
-#ifndef A_USE_INT
-static int lsm303dlh_a_kthread(void *data);
-#endif
-int lsm303dlh_a_set_mode(struct lsm303dlh_a_data *ldata, unsigned char mode)
+static int lsm303dlh_a_readdata(struct lsm303dlh_a_data *ddata)
{
- struct i2c_client *client = ldata->client;
- unsigned char data;
- unsigned char latch, config;
- int res;
+ unsigned char acc_data[6];
+ short data[3];
+
+ int ret = i2c_smbus_read_i2c_block_data(ddata->client,
+ AXISDATA_REG | MULTIPLE_I2C_TR, 6, acc_data);
+ if (ret < 0) {
+ dev_err(&ddata->client->dev,
+ "i2c_smbus_read_byte_data failed error %d\
+ Register AXISDATA_REG \n", ret);
+ return ret;
+ }
- if (ldata->mode == mode)
- return 0;
+ data[0] = (short) (((acc_data[1]) << 8) | acc_data[0]);
+ data[1] = (short) (((acc_data[3]) << 8) | acc_data[2]);
+ data[2] = (short) (((acc_data[5]) << 8) | acc_data[4]);
- /* turn on supplies if already off */
- if (ldata->mode == LSM303DLH_A_MODE_OFF)
- regulator_enable(ldata->regulator);
+ data[0] >>= ddata->shift_adjust;
+ data[1] >>= ddata->shift_adjust;
+ data[2] >>= ddata->shift_adjust;
- /*
- * if to be enabled, configure active high, latched interrupt when
- * data is ready.
- */
- if (mode != LSM303DLH_A_MODE_OFF) {
- latch = LSM303DLH_A_CR3_LIR_LATCH << LSM303DLH_A_CR3_LIR1_BIT;
- config = LSM303DLH_A_CR3_I_DATA << LSM303DLH_A_CR3_I1_BIT;
- config |= latch;
- res = i2c_smbus_write_byte_data(client, CTRL_REG3, config);
- if (res < 0) {
- dev_err(&client->dev, "set latch config failed\n");
- regulator_disable(ldata->regulator);
- return res;
- }
+ /* taking position and orientation of x,y,z axis into account*/
+
+ data[ddata->pdata.axis_map_x] = ddata->pdata.negative_x ?
+ -data[ddata->pdata.axis_map_x] : data[ddata->pdata.axis_map_x];
+ data[ddata->pdata.axis_map_y] = ddata->pdata.negative_y ?
+ -data[ddata->pdata.axis_map_y] : data[ddata->pdata.axis_map_y];
+ data[ddata->pdata.axis_map_z] = ddata->pdata.negative_z ?
+ -data[ddata->pdata.axis_map_z] : data[ddata->pdata.axis_map_z];
+
+ ddata->data.x = data[ddata->pdata.axis_map_x];
+ ddata->data.y = data[ddata->pdata.axis_map_y];
+ ddata->data.z = data[ddata->pdata.axis_map_z];
+
+ return ret;
+}
+
+static ssize_t lsm303dlh_a_show_data(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct lsm303dlh_a_data *ddata = platform_get_drvdata(pdev);
+ int ret = 0;
+
+ if (ddata->mode == LSM303DLH_A_MODE_OFF) {
+ dev_info(&ddata->client->dev,
+ "device is switched off,make it ON using MODE");
+ return ret;
}
- data = i2c_smbus_read_byte_data(ldata->client, CTRL_REG1);
- data &= ~LSM303DLH_A_CR1_PM_MASK;
- data |= ((mode << LSM303DLH_A_CR1_PM_BIT) & LSM303DLH_A_CR1_PM_MASK);
- res = i2c_smbus_write_byte_data(ldata->client, CTRL_REG1, data);
- if (res < 0) {
- dev_err(&client->dev, "error enabling accelerometer\n");
- regulator_disable(ldata->regulator);
- return res;
+ mutex_lock(&ddata->lock);
+
+ ret = lsm303dlh_a_readdata(ddata);
+
+ if (ret < 0) {
+ mutex_unlock(&ddata->lock);
+ return ret;
}
- /* if new mode is off, disable the regulators too */
- if (mode == LSM303DLH_A_MODE_OFF)
- regulator_disable(ldata->regulator);
+ mutex_unlock(&ddata->lock);
- /* update the new mode accordingly */
- ldata->mode = mode;
+ return sprintf(buf, "%8x:%8x:%8x\n", ddata->data.x, ddata->data.y,
+ ddata->data.z);
+}
-#ifndef A_USE_INT
- if (mode) {
- if (!ldata->kthread) {
- ldata->kthread = kthread_run(lsm303dlh_a_kthread, ldata,
- "klsm303dlh_a");
- if (IS_ERR(ldata->kthread))
- return PTR_ERR(ldata->kthread);
- }
+#ifdef CONFIG_SENSORS_LSM303DLH_INPUT_DEVICE
+static irqreturn_t lsm303dlh_a_gpio_irq(int irq, void *device_data)
+{
+
+ struct lsm303dlh_a_data *ddata = device_data;
+ int ret;
+ unsigned char reg;
+ struct input_dev *input;
+
+ /* know your interrupt source */
+ if (irq == gpio_to_irq(ddata->pdata.irq_a1)) {
+ reg = INT1_SRC;
+ input = ddata->input_dev;
+ } else if (irq == gpio_to_irq(ddata->pdata.irq_a2)) {
+ reg = INT2_SRC;
+ input = ddata->input_dev2;
} else {
- if (ldata->kthread) {
- kthread_stop(ldata->kthread);
- ldata->kthread = NULL;
- }
+ dev_err(&ddata->client->dev, "spurious interrupt");
+ return IRQ_HANDLED;
}
+
+ /* read the axis */
+ ret = lsm303dlh_a_readdata(ddata);
+ if (ret < 0)
+ dev_err(&ddata->client->dev,
+ "reading data of xyz failed error %d\n", ret);
+
+ input_report_abs(input, ABS_X, ddata->data.x);
+ input_report_abs(input, ABS_Y, ddata->data.y);
+ input_report_abs(input, ABS_Z, ddata->data.z);
+ input_sync(input);
+
+ /* clear the value by reading it */
+ ret = lsm303dlh_a_read(ddata, reg, "INTTERUPT SOURCE");
+ if (ret < 0)
+ dev_err(&ddata->client->dev,
+ "clearing interrupt source failed error %d\n", ret);
+
+ return IRQ_HANDLED;
+
+}
#endif
- return 0;
+static ssize_t lsm303dlh_a_show_interrupt_control(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct lsm303dlh_a_data *ddata = platform_get_drvdata(pdev);
+
+ return sprintf(buf, "%d\n", ddata->interrupt_control);
}
-int lsm303dlh_a_set_range(struct lsm303dlh_a_data *ldata, unsigned char range)
+static ssize_t lsm303dlh_a_store_interrupt_control(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
{
- switch (range) {
- case LSM303DLH_A_RANGE_2G:
- ldata->shift_adj = SHIFT_ADJ_2G;
- break;
- case LSM303DLH_A_RANGE_4G:
- ldata->shift_adj = SHIFT_ADJ_4G;
- break;
- case LSM303DLH_A_RANGE_8G:
- ldata->shift_adj = SHIFT_ADJ_8G;
- break;
- default:
- return -EINVAL;
+ struct platform_device *pdev = to_platform_device(dev);
+ struct lsm303dlh_a_data *ddata = platform_get_drvdata(pdev);
+ unsigned long val;
+ int error;
+
+ if (ddata->mode == LSM303DLH_A_MODE_OFF) {
+ dev_info(&ddata->client->dev,
+ "device is switched off,make it ON using MODE");
+ return count;
}
- range <<= LSM303DLH_A_CR4_FS_BIT;
+ error = strict_strtoul(buf, 0, &val);
+ if (error)
+ return error;
- return i2c_smbus_write_byte_data(ldata->client, CTRL_REG4, range);
+ mutex_lock(&ddata->lock);
+
+ ddata->interrupt_control = val;
+
+ error = lsm303dlh_a_write(ddata, CTRL_REG3, val, "CTRL_REG3");
+ if (error < 0) {
+ mutex_unlock(&ddata->lock);
+ return error;
+ }
+
+ mutex_unlock(&ddata->lock);
+
+ return count;
}
-/* i2c read routine for lsm303dlh accelerometer */
-static char lsm303dlh_a_i2c_read(struct lsm303dlh_a_data *ldata,
- unsigned char reg_addr,
- unsigned char *data,
- unsigned char len)
+static ssize_t lsm303dlh_a_show_interrupt_channel(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
{
- int res;
-
- res = i2c_smbus_read_i2c_block_data(ldata->client,
- reg_addr | MULTIPLE_I2C_TR, len, data);
- if (res < 0) {
- dev_err(&ldata->client->dev, "failed to read %#x: %d\n",
- reg_addr, res);
- return res;
- }
+ struct platform_device *pdev = to_platform_device(dev);
+ struct lsm303dlh_a_data *ddata = platform_get_drvdata(pdev);
- return len;
+ return sprintf(buf, "%d\n", ddata->interrupt_channel);
}
-/* X,Y and Z-axis acceleration data readout */
-int lsm303dlh_a_read_xyz(struct lsm303dlh_a_data *ldata,
- struct lsm303dlh_a_t *data)
+static ssize_t lsm303dlh_a_store_interrupt_channel(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
{
- int res;
- unsigned char acc_data[6];
- int hw_d[3] = { 0 };
+ struct platform_device *pdev = to_platform_device(dev);
+ struct lsm303dlh_a_data *ddata = platform_get_drvdata(pdev);
+ unsigned long val;
+ int error;
+
+ error = strict_strtoul(buf, 0, &val);
+ if (error)
+ return error;
- /* check lsm303dlh_a_client */
- if (ldata->client == NULL) {
- printk(KERN_ERR "I2C driver not install\n");
- return -EFAULT;
+ ddata->interrupt_channel = val;
+
+ return count;
+}
+
+static ssize_t lsm303dlh_a_show_interrupt_configure(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct lsm303dlh_a_data *ddata = platform_get_drvdata(pdev);
+
+ return sprintf(buf, "%d\n",
+ ddata->interrupt_configure[ddata->interrupt_channel]);
+}
+
+static ssize_t lsm303dlh_a_store_interrupt_configure(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct lsm303dlh_a_data *ddata = platform_get_drvdata(pdev);
+ unsigned long val;
+ int error;
+
+ if (ddata->mode == LSM303DLH_A_MODE_OFF) {
+ dev_info(&ddata->client->dev,
+ "device is switched off,make it ON using MODE");
+ return count;
}
- res = lsm303dlh_a_i2c_read(ldata, AXISDATA_REG, &acc_data[0], 6);
-
- if (res >= 0) {
- hw_d[0] = (short) (((acc_data[1]) << 8) | acc_data[0]);
- hw_d[1] = (short) (((acc_data[3]) << 8) | acc_data[2]);
- hw_d[2] = (short) (((acc_data[5]) << 8) | acc_data[4]);
-
- hw_d[0] >>= ldata->shift_adj;
- hw_d[1] >>= ldata->shift_adj;
- hw_d[2] >>= ldata->shift_adj;
-
- data->x = ldata->pdata.negative_x ?
- -hw_d[ldata->pdata.axis_map_x] :
- hw_d[ldata->pdata.axis_map_x];
- data->y = ldata->pdata.negative_y ?
- -hw_d[ldata->pdata.axis_map_y] :
- hw_d[ldata->pdata.axis_map_y];
- data->z = ldata->pdata.negative_z ?
- -hw_d[ldata->pdata.axis_map_z] :
- hw_d[ldata->pdata.axis_map_z];
+ error = strict_strtoul(buf, 0, &val);
+ if (error)
+ return error;
+
+ mutex_lock(&ddata->lock);
+
+ ddata->interrupt_configure[ddata->interrupt_channel] = val;
+
+ if (ddata->interrupt_channel == 0x0)
+ error = lsm303dlh_a_write(ddata, INT1_CFG, val, "INT1_CFG");
+ else
+ error = lsm303dlh_a_write(ddata, INT2_CFG, val, "INT2_CFG");
+
+ if (error < 0) {
+ mutex_unlock(&ddata->lock);
+ return error;
}
- return res;
+ mutex_unlock(&ddata->lock);
+
+ return count;
}
-/* open command for lsm303dlh_a device file */
-static int lsm303dlh_a_open(struct inode *inode, struct file *filp)
+static ssize_t lsm303dlh_a_show_interrupt_duration(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
{
- struct lsm303dlh_a_data *ldata = file_private;
+ struct platform_device *pdev = to_platform_device(dev);
+ struct lsm303dlh_a_data *ddata = platform_get_drvdata(pdev);
- filp->private_data = ldata;
+ return sprintf(buf, "%d\n",
+ ddata->interrupt_duration[ddata->interrupt_channel]);
+}
- if (ldata->client == NULL) {
- printk(KERN_ERR"I2C driver not install\n");
- return -EINVAL;
+static ssize_t lsm303dlh_a_store_interrupt_duration(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct lsm303dlh_a_data *ddata = platform_get_drvdata(pdev);
+ unsigned long val;
+ int error;
+
+ if (ddata->mode == LSM303DLH_A_MODE_OFF) {
+ dev_info(&ddata->client->dev,
+ "device is switched off,make it ON using MODE");
+ return count;
}
- atomic_inc(&ldata->user_count);
+ error = strict_strtoul(buf, 0, &val);
+ if (error)
+ return error;
- dev_dbg(&ldata->client->dev, "lsm303dlh_a has been opened\n");
+ mutex_lock(&ddata->lock);
- return 0;
+ ddata->interrupt_duration[ddata->interrupt_channel] = val;
+
+ if (ddata->interrupt_channel == 0x0)
+ error = lsm303dlh_a_write(ddata, INT1_DURATION, val,
+ "INT1_DURATION");
+ else
+ error = lsm303dlh_a_write(ddata, INT2_DURATION, val,
+ "INT2_DURATION");
+
+ if (error < 0) {
+ mutex_unlock(&ddata->lock);
+ return error;
+ }
+
+ mutex_unlock(&ddata->lock);
+
+ return count;
}
-/* release command for lsm303dlh_a device file */
-static int lsm303dlh_a_close(struct inode *inode, struct file *filp)
+static ssize_t lsm303dlh_a_show_interrupt_threshold(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
{
- struct lsm303dlh_a_data *ldata = filp->private_data;
- int res;
+ struct platform_device *pdev = to_platform_device(dev);
+ struct lsm303dlh_a_data *ddata = platform_get_drvdata(pdev);
- res = atomic_dec_and_test(&ldata->user_count);
+ return sprintf(buf, "%d\n",
+ ddata->interrupt_threshold[ddata->interrupt_channel]);
+}
-#ifndef A_USE_INT
- if ((res) && (ldata->kthread)) {
- kthread_stop(ldata->kthread);
- ldata->kthread = NULL;
+static ssize_t lsm303dlh_a_store_interrupt_threshold(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct lsm303dlh_a_data *ddata = platform_get_drvdata(pdev);
+ unsigned long val;
+ int error;
+
+ if (ddata->mode == LSM303DLH_A_MODE_OFF) {
+ dev_info(&ddata->client->dev,
+ "device is switched off,make it ON using MODE");
+ return count;
}
-#endif
- dev_dbg(&ldata->client->dev, "lsm303dlh_a has been closed\n");
+ error = strict_strtoul(buf, 0, &val);
+ if (error)
+ return error;
- return 0;
+ mutex_lock(&ddata->lock);
+
+ ddata->interrupt_threshold[ddata->interrupt_channel] = val;
+
+ if (ddata->interrupt_channel == 0x0)
+ error = lsm303dlh_a_write(ddata, INT1_THS, val, "INT1_THS");
+ else
+ error = lsm303dlh_a_write(ddata, INT2_THS, val, "INT2_THS");
+
+ if (error < 0) {
+ mutex_unlock(&ddata->lock);
+ return error;
+ }
+
+ mutex_unlock(&ddata->lock);
+
+ return count;
}
-/* ioctl command for lsm303dlh_a device file */
-static int lsm303dlh_a_ioctl(struct inode *inode, struct file *filp,
- unsigned int cmd, unsigned long arg)
+static ssize_t lsm303dlh_a_show_range(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
{
- int err = 0;
- unsigned char buf[1];
- struct lsm303dlh_a_data *ldata = filp->private_data;
-
- /* check lsm303dlh_a_client */
- if (ldata->client == NULL) {
- printk(KERN_ERR "I2C driver not install\n");
- return -EFAULT;
+ struct platform_device *pdev = to_platform_device(dev);
+ struct lsm303dlh_a_data *ddata = platform_get_drvdata(pdev);
+
+ return sprintf(buf, "%d\n", ddata->range >> LSM303DLH_A_CR4_FS_BIT);
+}
+
+static ssize_t lsm303dlh_a_store_range(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct lsm303dlh_a_data *ddata = platform_get_drvdata(pdev);
+ unsigned long val;
+ unsigned long bdu_enabled_val;
+ int error;
+
+ if (ddata->mode == LSM303DLH_A_MODE_OFF) {
+ dev_info(&ddata->client->dev,
+ "device is switched off,make it ON using MODE");
+ return count;
}
- /* cmd mapping */
+ error = strict_strtoul(buf, 0, &val);
+ if (error)
+ return error;
- switch (cmd) {
+ if (val < LSM303DLH_A_RANGE_2G || val > LSM303DLH_A_RANGE_8G)
+ return -EINVAL;
- case LSM303DLH_A_SET_RANGE:
- if (copy_from_user(buf, (unsigned char *)arg, 1) != 0) {
- dev_err(&ldata->client->dev, "copy_from_user error\n");
- return -EFAULT;
- }
- err = lsm303dlh_a_set_range(ldata, buf[0]);
- return err;
- case LSM303DLH_A_SET_MODE:
- if (copy_from_user(buf, (unsigned char *)arg, 1) != 0) {
- dev_err(&ldata->client->dev, "copy_from_user error\n");
- return -EFAULT;
- }
- err = lsm303dlh_a_set_mode(ldata, buf[0]);
- return err;
- case LSM303DLH_A_SET_RATE:
- if (copy_from_user(buf, (unsigned char *)arg, 1) != 0) {
- dev_err(&ldata->client->dev, "copy_from_user error\n");
- return -EFAULT;
- }
- err = lsm303dlh_a_set_rate(ldata, buf[0]);
- return err;
- case LSM303DLH_A_GET_MODE:
- if (copy_to_user((unsigned char *)arg,
- &ldata->mode, sizeof(ldata->mode)) != 0) {
- dev_err(&ldata->client->dev, "copy_to_user error\n");
- return -EFAULT;
- }
- return 0;
+ mutex_lock(&ddata->lock);
+
+ ddata->range = val;
+ ddata->range <<= LSM303DLH_A_CR4_FS_BIT;
+
+ /*
+ * Block mode update is recommended for not
+ * ending up reading different values
+ */
+ bdu_enabled_val = ddata->range;
+ bdu_enabled_val |= LSM303DLH_A_CR4_BDU_MASK;
+
+ error = lsm303dlh_a_write(ddata, CTRL_REG4, bdu_enabled_val,
+ "CTRL_REG4");
+ if (error < 0) {
+ mutex_unlock(&ddata->lock);
+ return error;
+ }
+
+ switch (val) {
+ case LSM303DLH_A_RANGE_2G:
+ ddata->shift_adjust = SHIFT_ADJ_2G;
+ break;
+ case LSM303DLH_A_RANGE_4G:
+ ddata->shift_adjust = SHIFT_ADJ_4G;
+ break;
+ case LSM303DLH_A_RANGE_8G:
+ ddata->shift_adjust = SHIFT_ADJ_8G;
+ break;
default:
- return 0;
+ return -EINVAL;
}
+
+ mutex_unlock(&ddata->lock);
+
+ return count;
}
-#ifndef A_USE_INT
-static int lsm303dlh_a_calculate_ms(struct lsm303dlh_a_data *ldata)
+static ssize_t lsm303dlh_a_show_mode(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
{
- int ms;
- if (ldata->mode == LSM303DLH_A_MODE_NORMAL) {
- if (ldata->rate == LSM303DLH_A_RATE_50)
- ms = 20;
- else if (ldata->rate == LSM303DLH_A_RATE_100)
- ms = 10;
- else if (ldata->rate == LSM303DLH_A_RATE_400)
- ms = 2;
- else if (ldata->rate == LSM303DLH_A_RATE_1000)
- ms = 1;
- else
- ms = 1000;
- } else {
- if (ldata->mode == LSM303DLH_A_MODE_LP_HALF)
- ms = 2000;
- else if (ldata->mode == LSM303DLH_A_MODE_LP_1)
- ms = 1000;
- else if (ldata->mode == LSM303DLH_A_MODE_LP_2)
- ms = 500;
- else if (ldata->mode == LSM303DLH_A_MODE_LP_5)
- ms = 200;
- else if (ldata->mode == LSM303DLH_A_MODE_LP_10)
- ms = 100;
- else
- ms = 1000;
- }
- return ms;
+ struct platform_device *pdev = to_platform_device(dev);
+ struct lsm303dlh_a_data *ddata = platform_get_drvdata(pdev);
+
+ return sprintf(buf, "%d\n", ddata->mode);
}
-#endif /* A_USE_INT */
-#ifdef A_USE_INT
-static void lsm303dlh_a_work_func(struct work_struct *work)
-#else
-static void lsm303dlh_a_work_func(struct lsm303dlh_a_data *ldata)
-#endif /* A_USE_INT */
+static ssize_t lsm303dlh_a_store_mode(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
{
- int res;
- struct lsm303dlh_a_t abuf;
+ struct platform_device *pdev = to_platform_device(dev);
+ struct lsm303dlh_a_data *ddata = platform_get_drvdata(pdev);
+ unsigned long val;
+ unsigned char data;
+ int error;
+
+ error = strict_strtoul(buf, 0, &val);
+ if (error)
+ return error;
+
+ /* not in correct range */
+
+ if (val < LSM303DLH_A_MODE_OFF || val > LSM303DLH_A_MODE_LP_10)
+ return -EINVAL;
-#ifdef A_USE_INT
- struct lsm303dlh_a_data *ldata =
- container_of(work, struct lsm303dlh_a_data, work);
+ /* if same mode as existing, return */
+ if (ddata->mode == val)
+ return 0;
+
+ /* turn on the supplies if already off */
+ if (ddata->regulator && ddata->mode == LSM303DLH_A_MODE_OFF) {
+ regulator_enable(ddata->regulator);
+#ifdef CONFIG_SENSORS_LSM303DLH_INPUT_DEVICE
+ enable_irq(gpio_to_irq(ddata->pdata.irq_a1));
+ enable_irq(gpio_to_irq(ddata->pdata.irq_a2));
#endif
- /* Clear interrupt. */
- res = i2c_smbus_read_byte_data(ldata->client, INT1_SRC_A_REG);
+ }
- res = lsm303dlh_a_read_xyz(ldata, &abuf);
+ mutex_lock(&ddata->lock);
- if (res >= 0) {
- input_report_abs(ldata->input_dev, ABS_X, abuf.x);
- input_report_abs(ldata->input_dev, ABS_Y, abuf.y);
- input_report_abs(ldata->input_dev, ABS_Z, abuf.z);
+ data = lsm303dlh_a_read(ddata, CTRL_REG1, "CTRL_REG1");
+
+ data &= ~LSM303DLH_A_CR1_PM_MASK;
- input_sync(ldata->input_dev);
+ ddata->mode = val;
+
+ data |= ((val << LSM303DLH_A_CR1_PM_BIT) & LSM303DLH_A_CR1_PM_MASK);
+
+ error = lsm303dlh_a_write(ddata, CTRL_REG1, data, "CTRL_REG1");
+ if (error < 0) {
+ if (ddata->regulator)
+ regulator_disable(ddata->regulator);
+ mutex_unlock(&ddata->lock);
+ return error;
}
-#ifdef A_USE_INT
- enable_irq(ldata->client->irq);
-#endif
-}
+ mutex_unlock(&ddata->lock);
-#ifndef A_USE_INT
-/*
- * Kthread polling function
- * @data: unused - here to conform to threadfn prototype
- */
-static int lsm303dlh_a_kthread(void *data)
-{
- struct lsm303dlh_a_data *ldata = data;
+ if (val == LSM303DLH_A_MODE_OFF) {
+#ifdef CONFIG_SENSORS_LSM303DLH_INPUT_DEVICE
+ disable_irq(gpio_to_irq(ddata->pdata.irq_a1));
+ disable_irq(gpio_to_irq(ddata->pdata.irq_a2));
+#endif
+ /*
+ * No need to store context here
+ * it is not like suspend/resume
+ * but fall back to default values
+ */
+ ddata->rate = LSM303DLH_A_RATE_50;
+ ddata->range = LSM303DLH_A_RANGE_2G;
+ ddata->shift_adjust = SHIFT_ADJ_2G;
+
+ if (ddata->regulator)
+ regulator_disable(ddata->regulator);
- while (!kthread_should_stop()) {
- lsm303dlh_a_work_func(ldata);
- try_to_freeze();
- msleep_interruptible(lsm303dlh_a_calculate_ms(ldata));
}
- return 0;
+ return count;
}
-#endif /* A_USE_INT */
-
-#ifdef A_USE_INT
-#ifdef A_USE_GPIO_EXTENDER_INT
-void lsm303dlh_a_interrupt(void *dev_id)
-#else
-static irqreturn_t lsm303dlh_a_interrupt(int irq, void *dev_id)
-#endif /* A_USE_GPIO_EXTENDER_INT */
+
+static ssize_t lsm303dlh_a_show_rate(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
{
- struct lsm303dlh_a_data *ldata = dev_id;
-#ifndef A_USE_GPIO_EXTENDER_INT
- disable_irq(ldata->client->irq);
-#endif /* A_USE_GPIO_EXTENDER_INT */
- schedule_work(&ldata->work);
+ struct platform_device *pdev = to_platform_device(dev);
+ struct lsm303dlh_a_data *ddata = platform_get_drvdata(pdev);
-#ifndef A_USE_GPIO_EXTENDER_INT
- return IRQ_HANDLED;
-#endif /* A_USE_GPIO_EXTENDER_INT */
+ return sprintf(buf, "%d\n", ddata->rate);
}
-#endif /* A_USE_GPIO_EXTENDER_INT */
-static const struct file_operations lsm303dlh_a_fops = {
- .owner = THIS_MODULE,
- .open = lsm303dlh_a_open,
- .release = lsm303dlh_a_close,
- .ioctl = lsm303dlh_a_ioctl,
-};
-
-static struct miscdevice lsm303dlh_a_misc_device = {
- .minor = MISC_DYNAMIC_MINOR,
- .name = "lsm303dlh_a",
- .fops = &lsm303dlh_a_fops,
-};
-
-static int lsm303dlh_a_validate_pdata(struct lsm303dlh_a_data *adata)
+static ssize_t lsm303dlh_a_store_rate(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
{
- if ((adata->pdata.axis_map_x > 2) ||
- (adata->pdata.axis_map_y > 2) ||
- (adata->pdata.axis_map_z > 2) ||
- (adata->pdata.axis_map_x == adata->pdata.axis_map_y) ||
- (adata->pdata.axis_map_x == adata->pdata.axis_map_z) ||
- (adata->pdata.axis_map_y == adata->pdata.axis_map_z)) {
- dev_err(&adata->client->dev,
- "invalid axis_map value x:%u y:%u z%u\n",
- adata->pdata.axis_map_x, adata->pdata.axis_map_y,
- adata->pdata.axis_map_z);
- return -EINVAL;
+ struct platform_device *pdev = to_platform_device(dev);
+ struct lsm303dlh_a_data *ddata = platform_get_drvdata(pdev);
+ unsigned long val;
+ unsigned char data;
+ int error;
+
+ if (ddata->mode == LSM303DLH_A_MODE_OFF) {
+ dev_info(&ddata->client->dev,
+ "device is switched off,make it ON using MODE");
+ return count;
}
- /* Only allow 0 and 1 */
- if ((adata->pdata.negative_x > 1) ||
- (adata->pdata.negative_y > 1) ||
- (adata->pdata.negative_z > 1)) {
- dev_err(&adata->client->dev,
- "invalid negate value x:%u y:%u z:%u\n",
- adata->pdata.negative_x, adata->pdata.negative_y,
- adata->pdata.negative_z);
+ error = strict_strtoul(buf, 0, &val);
+ if (error)
+ return error;
+
+ if (val < LSM303DLH_A_MODE_OFF || val > LSM303DLH_A_MODE_LP_10)
return -EINVAL;
+
+ mutex_lock(&ddata->lock);
+
+ data = lsm303dlh_a_read(ddata, CTRL_REG1, "CTRL_REG1");
+
+ data &= ~LSM303DLH_A_CR1_DR_MASK;
+
+ ddata->rate = val;
+
+ data |= ((val << LSM303DLH_A_CR1_DR_BIT) & LSM303DLH_A_CR1_DR_MASK);
+
+ error = lsm303dlh_a_write(ddata, CTRL_REG1, data, "CTRL_REG1");
+ if (error < 0) {
+ mutex_unlock(&ddata->lock);
+ return error;
}
-#ifdef A_USE_GPIO_EXTENDER_INT
- if ((!adata->pdata.free_irq) || (!adata->pdata.register_irq))
- return -EINVAL;
-#endif /* A_USE_GPIO_EXTENDER_INT */
+ mutex_unlock(&ddata->lock);
- return 0;
+ return count;
}
-static int lsm303dlh_a_probe(struct i2c_client *client,
- const struct i2c_device_id *devid)
-{
- int err = 0;
- int tempvalue;
- struct lsm303dlh_a_data *ldata;
-
- if (client->dev.platform_data == NULL) {
- dev_err(&client->dev, "platform data is NULL. exiting.\n");
- err = -ENODEV;
- goto exit;
- }
- if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
- err = -ENODEV;
- goto exit;
- }
+static DEVICE_ATTR(data, S_IRUGO, lsm303dlh_a_show_data, NULL);
- /*
- * OK. For now, we presume we have a valid client. We now create the
- * client structure, even though we cannot fill it completely yet.
- */
- ldata = kzalloc(sizeof(*ldata), GFP_KERNEL);
- if (ldata == NULL) {
- dev_err(&client->dev,
- "failed to allocate memory for module data\n");
- err = -ENOMEM;
- goto exit;
- }
+static DEVICE_ATTR(range, S_IWUGO | S_IRUGO,
+ lsm303dlh_a_show_range, lsm303dlh_a_store_range);
+
+static DEVICE_ATTR(mode, S_IWUGO | S_IRUGO,
+ lsm303dlh_a_show_mode, lsm303dlh_a_store_mode);
+
+static DEVICE_ATTR(rate, S_IWUGO | S_IRUGO,
+ lsm303dlh_a_show_rate, lsm303dlh_a_store_rate);
+
+static DEVICE_ATTR(interrupt_control, S_IWUGO | S_IRUGO,
+ lsm303dlh_a_show_interrupt_control,
+ lsm303dlh_a_store_interrupt_control);
- i2c_set_clientdata(client, ldata);
- ldata->client = client;
+static DEVICE_ATTR(interrupt_channel, S_IWUGO | S_IRUGO,
+ lsm303dlh_a_show_interrupt_channel,
+ lsm303dlh_a_store_interrupt_channel);
- file_private = ldata;
+static DEVICE_ATTR(interrupt_configure, S_IWUGO | S_IRUGO,
+ lsm303dlh_a_show_interrupt_configure,
+ lsm303dlh_a_store_interrupt_configure);
- memcpy(&ldata->pdata, client->dev.platform_data, sizeof(ldata->pdata));
+static DEVICE_ATTR(interrupt_duration, S_IWUGO | S_IRUGO,
+ lsm303dlh_a_show_interrupt_duration,
+ lsm303dlh_a_store_interrupt_duration);
- err = lsm303dlh_a_validate_pdata(ldata);
- if (err < 0) {
- dev_err(&client->dev, "failed to validate platform data\n");
- goto exit_kfree;
+static DEVICE_ATTR(interrupt_threshold, S_IWUGO | S_IRUGO,
+ lsm303dlh_a_show_interrupt_threshold,
+ lsm303dlh_a_store_interrupt_threshold);
+
+static struct attribute *lsm303dlh_a_attributes[] = {
+ &dev_attr_data.attr,
+ &dev_attr_range.attr,
+ &dev_attr_mode.attr,
+ &dev_attr_rate.attr,
+ &dev_attr_interrupt_control.attr,
+ &dev_attr_interrupt_channel.attr,
+ &dev_attr_interrupt_configure.attr,
+ &dev_attr_interrupt_duration.attr,
+ &dev_attr_interrupt_threshold.attr,
+ NULL
+};
+
+static const struct attribute_group lsm303dlh_a_attr_group = {
+ .attrs = lsm303dlh_a_attributes,
+};
+
+static int __devinit lsm303dlh_a_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ int ret;
+ struct lsm303dlh_a_data *ddata = NULL;
+
+ ddata = kzalloc(sizeof(struct lsm303dlh_a_data), GFP_KERNEL);
+ if (ddata == NULL) {
+ ret = -ENOMEM;
+ goto err_op_failed;
}
- /* by default the mode is off until we dont get supplied */
- ldata->mode = LSM303DLH_A_MODE_OFF;
- dev_set_name(&client->dev, ldata->pdata.name);
+ ddata->client = client;
+ i2c_set_clientdata(client, ddata);
+
+ /* copy platform specific data */
+ memcpy(&ddata->pdata, client->dev.platform_data, sizeof(ddata->pdata));
+ ddata->mode = LSM303DLH_A_MODE_OFF;
+ ddata->rate = LSM303DLH_A_RATE_50;
+ ddata->range = LSM303DLH_A_RANGE_2G;
+ ddata->shift_adjust = SHIFT_ADJ_2G;
+ dev_set_name(&client->dev, ddata->pdata.name_a);
- ldata->regulator = regulator_get(&client->dev, "v-accel");
- if (IS_ERR(ldata->regulator)) {
+ ddata->regulator = regulator_get(&client->dev, "v-accel");
+ if (IS_ERR(ddata->regulator)) {
dev_err(&client->dev, "failed to get regulator\n");
- err = PTR_ERR(ldata->regulator);
- goto exit_free_regulator;
+ ret = PTR_ERR(ddata->regulator);
+ ddata->regulator = NULL;
}
- regulator_enable(ldata->regulator);
- err = i2c_smbus_read_byte(client);
- if (err < 0) {
- dev_err(&client->dev, "i2c_smbus_read_byte error!!\n");
- err = -ENOMEM;
- goto exit_disable_regulator;
- } else {
- dev_info(&client->dev, "accelerometer detected\n");
- }
+ if (ddata->regulator)
+ regulator_enable(ddata->regulator);
- /* read chip id */
- tempvalue = i2c_smbus_read_byte_data(client, WHO_AM_I);
- if (!(tempvalue & 0x00FF) == 0x0032) {
- err = -EINVAL;
- ldata->client = NULL;
- goto exit_disable_regulator;
- }
+ ret = lsm303dlh_a_read(ddata, WHO_AM_I, "WHO_AM_I");
+ if (ret < 0)
+ goto exit_free_regulator;
- /* when we disable the regulator, also update the mode */
- regulator_disable(ldata->regulator);
-
-#ifdef A_USE_INT
- INIT_WORK(&ldata->work, lsm303dlh_a_work_func);
-#ifdef A_USE_GPIO_EXTENDER_INT
- if (ldata->pdata.register_irq(&client->dev, client,
- lsm303dlh_a_interrupt)) {
-#else
- if (request_irq(client->irq, lsm303dlh_a_interrupt,
- IRQF_TRIGGER_HIGH, "lsm303dlh_a", ldata)) {
-#endif /* A_USE_GPIO_EXTENDER_INT */
- err = -EBUSY;
- dev_err(&client->dev, "request irq failed\n");
- goto exit_disable_regulator;
- }
-#endif /* A_USE_INT */
+ dev_info(&client->dev, "3-Axis Accelerometer, ID : %d\n",
+ ret);
+
+ mutex_init(&ddata->lock);
+
+ ret = sysfs_create_group(&client->dev.kobj, &lsm303dlh_a_attr_group);
+ if (ret)
+ goto exit_free_regulator;
+
+#ifdef CONFIG_SENSORS_LSM303DLH_INPUT_DEVICE
+ /* accelerometer has two interrupts channels
+ (thresholds,durations and sources)
+ and can support two input devices */
- if (misc_register(&lsm303dlh_a_misc_device)) {
- dev_err(&client->dev, "misc_register failed\n");
- err = -EINVAL;
- goto exit_free_irq;
+ ddata->input_dev = input_allocate_device();
+ if (!ddata->input_dev) {
+ ret = -ENOMEM;
+ dev_err(&client->dev, "Failed to allocate input device\n");
+ goto exit_free_regulator;
}
- ldata->input_dev = input_allocate_device();
- if (!ldata->input_dev) {
- err = -ENOMEM;
+ ddata->input_dev2 = input_allocate_device();
+ if (!ddata->input_dev2) {
+ ret = -ENOMEM;
dev_err(&client->dev, "Failed to allocate input device\n");
- goto exit_deregister_misc;
+ goto err_input_alloc_failed;
}
- set_bit(EV_ABS, ldata->input_dev->evbit);
+ set_bit(EV_ABS, ddata->input_dev->evbit);
+ set_bit(EV_ABS, ddata->input_dev2->evbit);
/* x-axis acceleration */
- input_set_abs_params(ldata->input_dev, ABS_X, -2048, 2047, 0, 0);
+ input_set_abs_params(ddata->input_dev, ABS_X, -32768, 32767, 0, 0);
+ input_set_abs_params(ddata->input_dev2, ABS_X, -32768, 32767, 0, 0);
/* y-axis acceleration */
- input_set_abs_params(ldata->input_dev, ABS_Y, -2048, 2047, 0, 0);
+ input_set_abs_params(ddata->input_dev, ABS_Y, -32768, 32767, 0, 0);
+ input_set_abs_params(ddata->input_dev2, ABS_Y, -32768, 32767, 0, 0);
/* z-axis acceleration */
- input_set_abs_params(ldata->input_dev, ABS_Z, -2048, 2047, 0, 0);
+ input_set_abs_params(ddata->input_dev, ABS_Z, -32768, 32767, 0, 0);
+ input_set_abs_params(ddata->input_dev2, ABS_Z, -32768, 32767, 0, 0);
- ldata->input_dev->name = "compass";
+ ddata->input_dev->name = "accelerometer";
+ ddata->input_dev2->name = "motion";
- err = input_register_device(ldata->input_dev);
- if (err) {
+ ret = input_register_device(ddata->input_dev);
+ if (ret) {
dev_err(&client->dev, "Unable to register input device: %s\n",
- ldata->input_dev->name);
- goto exit_deregister_misc;
+ ddata->input_dev->name);
+ goto err_input_register_failed;
}
- return 0;
-
-exit_deregister_misc:
- misc_deregister(&lsm303dlh_a_misc_device);
-exit_free_irq:
-#ifdef A_USE_INT
-#ifdef A_USE_GPIO_EXTENDER_INT
- ldata->pdata.free_irq(&client->dev);
-#else
- free_irq(client->irq, ldata);
-#endif /* A_USE_GPIO_EXTENDER_INT */
-#endif /* A_USE_INT */
-exit_disable_regulator:
- regulator_disable(ldata->regulator);
-exit_free_regulator:
- regulator_put(ldata->regulator);
-exit_kfree:
- kfree(ldata);
-exit:
- dev_info(&client->dev, "%s: Driver Initialization failed\n", __FILE__);
- return err;
-}
+ ret = input_register_device(ddata->input_dev2);
+ if (ret) {
+ dev_err(&client->dev, "Unable to register input device: %s\n",
+ ddata->input_dev->name);
+ goto err_input_register_failed2;
+ }
-static int lsm303dlh_a_remove(struct i2c_client *client)
-{
- struct lsm303dlh_a_data *ldata = i2c_get_clientdata(client);
+ /* Register interrupt */
+ ret = request_threaded_irq(gpio_to_irq(ddata->pdata.irq_a1), NULL,
+ lsm303dlh_a_gpio_irq,
+ IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING,
+ "lsm303dlh_a", ddata);
+ if (ret) {
+ dev_err(&client->dev, "request irq1 failed\n");
+ goto err_input_failed;
+ }
- dev_info(&client->dev, "lsm303dlh_a driver removing\n");
-#ifdef A_USE_INT
-#ifdef A_USE_GPIO_EXTENDER_INT
- ldata->pdata.free_irq(&client->dev);
-#else
- free_irq(client->irq, ldata);
-#endif /* A_USE_GPIO_EXTENDER_INT */
-#endif /* A_USE_INT */
+ ret = request_threaded_irq(gpio_to_irq(ddata->pdata.irq_a2), NULL,
+ lsm303dlh_a_gpio_irq,
+ IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING,
+ "lsm303dlh_a", ddata);
+ if (ret) {
+ dev_err(&client->dev, "request irq2 failed\n");
+ goto err_input_failed;
+ }
- misc_deregister(&lsm303dlh_a_misc_device);
+ /* only mode can enable it */
+ disable_irq(gpio_to_irq(ddata->pdata.irq_a1));
+ disable_irq(gpio_to_irq(ddata->pdata.irq_a2));
- input_unregister_device(ldata->input_dev);
+#endif
- if (ldata->mode != LSM303DLH_A_MODE_OFF)
- lsm303dlh_a_set_mode(ldata, LSM303DLH_A_MODE_OFF);
+ return ret;
+
+#ifdef CONFIG_SENSORS_LSM303DLH_INPUT_DEVICE
+err_input_failed:
+ input_unregister_device(ddata->input_dev2);
+err_input_register_failed2:
+ input_unregister_device(ddata->input_dev);
+err_input_register_failed:
+ input_free_device(ddata->input_dev2);
+err_input_alloc_failed:
+ input_free_device(ddata->input_dev);
+#endif
+exit_free_regulator:
+ if (ddata->regulator) {
+ regulator_disable(ddata->regulator);
+ regulator_put(ddata->regulator);
+ }
+err_op_failed:
+ kfree(ddata);
+ dev_err(&client->dev, "probe function fails %x", ret);
+ return ret;
+}
- regulator_put(ldata->regulator);
+static int __devexit lsm303dlh_a_remove(struct i2c_client *client)
+{
+ struct lsm303dlh_a_data *ddata;
+
+ ddata = i2c_get_clientdata(client);
+#ifdef CONFIG_SENSORS_LSM303DLH_INPUT_DEVICE
+ input_unregister_device(ddata->input_dev);
+ input_unregister_device(ddata->input_dev2);
+ input_free_device(ddata->input_dev);
+ input_free_device(ddata->input_dev2);
+#endif
+ sysfs_remove_group(&client->dev.kobj, &lsm303dlh_a_attr_group);
+
+ /* safer to make device off */
+ if (ddata->mode != LSM303DLH_A_MODE_OFF) {
+ lsm303dlh_a_write(ddata, CTRL_REG1, 0, "CONTROL");
+ if (ddata->regulator) {
+ regulator_disable(ddata->regulator);
+ regulator_put(ddata->regulator);
+ }
+ }
- kfree(ldata);
+ i2c_set_clientdata(client, NULL);
+ kfree(ddata);
return 0;
}
+
#ifdef CONFIG_PM
static int lsm303dlh_a_suspend(struct device *dev)
{
- int res;
- struct lsm303dlh_a_data *ldata = dev_get_drvdata(dev);
+ struct lsm303dlh_a_data *ddata;
+ int ret;
- if (ldata->mode == LSM303DLH_A_MODE_OFF)
- return 0;
+ ddata = dev_get_drvdata(dev);
- /* mode is not off; safely read the ctrl register */
- res = i2c_smbus_read_byte_data(ldata->client, CTRL_REG1);
- if (res < 0)
- return res;
+ if (ddata->mode == LSM303DLH_A_MODE_OFF)
+ return 0;
- /* save the previous state */
- ldata->pre_suspend_mode = res & 0xFF;
+#ifdef CONFIG_SENSORS_LSM303DLH_INPUT_DEVICE
+ disable_irq(gpio_to_irq(ddata->pdata.irq_a1));
+ disable_irq(gpio_to_irq(ddata->pdata.irq_a2));
+#endif
- lsm303dlh_a_set_mode(ldata, LSM303DLH_A_MODE_OFF);
+ ret = lsm303dlh_a_write(ddata, CTRL_REG1,
+ LSM303DLH_A_MODE_OFF, "CONTROL");
-#if defined(A_USE_INT) && !defined(A_USE_GPIO_EXTENDER_INT)
- disable_irq(ldata->client->irq);
-#endif
+ if (ddata->regulator)
+ regulator_disable(ddata->regulator);
- return 0;
+ return ret;
}
static int lsm303dlh_a_resume(struct device *dev)
{
- struct lsm303dlh_a_data *ldata = dev_get_drvdata(dev);
- int mode;
+ struct lsm303dlh_a_data *ddata;
+ int ret;
- mode = (ldata->pre_suspend_mode & LSM303DLH_A_CR1_PM_MASK)
- >> LSM303DLH_A_CR1_PM_BIT;
+ ddata = dev_get_drvdata(dev);
- lsm303dlh_a_set_mode(ldata, mode);
- lsm303dlh_a_set_rate(ldata, ldata->rate);
+ /* in correct mode, no need to change it */
+ if (ddata->mode == LSM303DLH_A_MODE_OFF)
+ return 0;
-#if defined(A_USE_INT) && !defined(A_USE_GPIO_EXTENDER_INT)
- enable_irq(ldata->client->irq);
+#ifdef CONFIG_SENSORS_LSM303DLH_INPUT_DEVICE
+ enable_irq(gpio_to_irq(ddata->pdata.irq_a1));
+ enable_irq(gpio_to_irq(ddata->pdata.irq_a2));
#endif
- return 0;
+
+ if (ddata->regulator)
+ regulator_enable(ddata->regulator);
+
+ ret = lsm303dlh_a_restore(ddata);
+
+ if (ret < 0)
+ dev_err(&ddata->client->dev,
+ "Error while resuming the device");
+
+ return ret;
}
static const struct dev_pm_ops lsm303dlh_a_dev_pm_ops = {
.suspend = lsm303dlh_a_suspend,
.resume = lsm303dlh_a_resume,
};
-#endif
+#endif /* CONFIG_PM */
static const struct i2c_device_id lsm303dlh_a_id[] = {
{ "lsm303dlh_a", 0 },
- {},
+ { },
};
-MODULE_DEVICE_TABLE(i2c, lsm303dlh_a_id);
-
static struct i2c_driver lsm303dlh_a_driver = {
- .class = I2C_CLASS_HWMON,
- .probe = lsm303dlh_a_probe,
- .remove = __devexit_p(lsm303dlh_a_remove),
- .id_table = lsm303dlh_a_id,
+ .probe = lsm303dlh_a_probe,
+ .remove = lsm303dlh_a_remove,
+ .id_table = lsm303dlh_a_id,
.driver = {
- .owner = THIS_MODULE,
- .name = "lsm303dlh_a",
+ .name = "lsm303dlh_a",
#ifdef CONFIG_PM
.pm = &lsm303dlh_a_dev_pm_ops,
#endif
@@ -811,16 +1101,12 @@ static int __init lsm303dlh_a_init(void)
static void __exit lsm303dlh_a_exit(void)
{
- printk(KERN_INFO "lsm303dlh_a exit\n");
-
i2c_del_driver(&lsm303dlh_a_driver);
- return;
}
-module_init(lsm303dlh_a_init);
-module_exit(lsm303dlh_a_exit);
+module_init(lsm303dlh_a_init)
+module_exit(lsm303dlh_a_exit)
-MODULE_DESCRIPTION("lsm303dlh accelerometer driver");
-MODULE_AUTHOR("STMicroelectronics");
+MODULE_DESCRIPTION("lSM303DLH 3-Axis Accelerometer Driver");
MODULE_LICENSE("GPL");
-
+MODULE_AUTHOR("STMicroelectronics");
diff --git a/drivers/hwmon/lsm303dlh_m.c b/drivers/hwmon/lsm303dlh_m.c
index e210ea25709..58b27656d02 100644
--- a/drivers/hwmon/lsm303dlh_m.c
+++ b/drivers/hwmon/lsm303dlh_m.c
@@ -1,47 +1,44 @@
-/******************** (C) COPYRIGHT 2010 STMicroelectronics ********************
-*
-* File Name : lsm303dlh_m.c
-* Authors : MH - C&I BU - Application Team
-* : Carmine Iascone (carmine.iascone@st.com)
-* : Matteo Dameno (matteo.dameno@st.com)
-* Version : V 0.3
-* Date : 24/02/2010
-*
-********************************************************************************
-*
-* Author: Mian Yousaf Kaukab <mian.yousaf.kaukab@stericsson.com>
-* Copyright 2010 (c) ST-Ericsson AB
-*
-********************************************************************************
-*
-* 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.
-*
-* THE PRESENT SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES
-* OR CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED, FOR THE SOLE
-* PURPOSE TO SUPPORT YOUR APPLICATION DEVELOPMENT.
-* AS A RESULT, STMICROELECTRONICS SHALL NOT BE HELD LIABLE FOR ANY DIRECT,
-* INDIRECT OR CONSEQUENTIAL DAMAGES WITH RESPECT TO ANY CLAIMS ARISING FROM THE
-* CONTENT OF SUCH SOFTWARE AND/OR THE USE MADE BY CUSTOMERS OF THE CODING
-* INFORMATION CONTAINED HEREIN IN CONNECTION WITH THEIR PRODUCTS.
-*
-* THIS SOFTWARE IS SPECIFICALLY DESIGNED FOR EXCLUSIVE USE WITH ST PARTS.
-*
-*******************************************************************************/
+/*
+ * lsm303dlh_m.c
+ * ST 3-Axis Magnetometer Driver
+ *
+ * Copyright (C) 2010 STMicroelectronics
+ * Author: Carmine Iascone (carmine.iascone@st.com)
+ * Author: Matteo Dameno (matteo.dameno@st.com)
+ *
+ * Copyright (C) 2010 STEricsson
+ * Author: Mian Yousaf Kaukab <mian.yousaf.kaukab@stericsson.com>
+ * Updated:Preetham Rao Kaskurthi <preetham.rao@stericsson.com>
+ *
+ * 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, see <http://www.gnu.org/licenses/>.
+ */
-#include <linux/kernel.h>
-#include <linux/module.h>
-#include <linux/slab.h>
-#include <linux/fs.h>
#include <linux/i2c.h>
-#include <linux/i2c-dev.h>
-#include <linux/uaccess.h>
-#include <linux/lsm303dlh.h>
-#include <linux/miscdevice.h>
+#include <linux/slab.h>
+#include <linux/mutex.h>
+#include <linux/platform_device.h>
#include <linux/delay.h>
-#include <linux/regulator/consumer.h>
+#include <linux/err.h>
+#ifdef CONFIG_SENSORS_LSM303DLH_INPUT_DEVICE
+#include <linux/input.h>
+#include <linux/interrupt.h>
+#include <mach/gpio.h>
+#endif
+
+#include <linux/lsm303dlh.h>
+#include <linux/regulator/consumer.h>
+#include <linux/kernel.h>
/* lsm303dlh magnetometer registers */
#define IRA_REG_M 0x0A
@@ -50,7 +47,7 @@
#define CRA_REG_M 0x00 /* Configuration register A */
#define CRB_REG_M 0x01 /* Configuration register B */
#define MR_REG_M 0x02 /* Mode register */
-#define SR_REG_M 0x09 /* Status register */
+#define SR_REG_M 0x09 /* Status register */
/* Output register start address*/
#define OUT_X_M 0x03
@@ -97,163 +94,302 @@
#define LSM303DLH_M_SR_REN_BIT 2
#define LSM303DLH_M_SR_REN_MASK (0x1 << LSM303DLH_M_SR_REN_BIT)
+/* Magnetometer gain setting */
+#define LSM303DLH_M_RANGE_1_3G 0x01
+#define LSM303DLH_M_RANGE_1_9G 0x02
+#define LSM303DLH_M_RANGE_2_5G 0x03
+#define LSM303DLH_M_RANGE_4_0G 0x04
+#define LSM303DLH_M_RANGE_4_7G 0x05
+#define LSM303DLH_M_RANGE_5_6G 0x06
+#define LSM303DLH_M_RANGE_8_1G 0x07
+
+/* Magnetometer capturing mode */
+#define LSM303DLH_M_MODE_CONTINUOUS 0
+#define LSM303DLH_M_MODE_SINGLE 1
+#define LSM303DLH_M_MODE_SLEEP 3
+
+/* Magnetometer output data rate */
+#define LSM303DLH_M_RATE_00_75 0x00
+#define LSM303DLH_M_RATE_01_50 0x01
+#define LSM303DLH_M_RATE_03_00 0x02
+#define LSM303DLH_M_RATE_07_50 0x03
+#define LSM303DLH_M_RATE_15_00 0x04
+#define LSM303DLH_M_RATE_30_00 0x05
+#define LSM303DLH_M_RATE_75_00 0x06
+
+/* Multiple byte transfer enable */
+#define MULTIPLE_I2C_TR 0x80
+
/*
- * LSM303DLH_M magnetometer local data
+ * magnetometer local data
*/
struct lsm303dlh_m_data {
struct i2c_client *client;
- struct lsm303dlh_platform_data pdata;
+ /* lock for sysfs operations */
+ struct mutex lock;
+
+#ifdef CONFIG_SENSORS_LSM303DLH_INPUT_DEVICE
+ struct input_dev *input_dev;
+#endif
struct regulator *regulator;
+ struct lsm303dlh_platform_data pdata;
+
short gain[3];
- short ms_delay;
+ short data[3];
unsigned char mode;
unsigned char rate;
- unsigned char pre_suspend_mode;
+ unsigned char range;
};
-static struct lsm303dlh_m_data *file_private;
+static int lsm303dlh_m_set_mode(struct lsm303dlh_m_data *ddata,
+ unsigned char mode);
+static int lsm303dlh_m_write(struct lsm303dlh_m_data *ddata,
+ u8 reg, u8 val, char *msg)
+{
+ int ret = i2c_smbus_write_byte_data(ddata->client, reg, val);
+ if (ret < 0)
+ dev_err(&ddata->client->dev,
+ "i2c_smbus_write_byte_data failed error %d\
+ Register (%s)\n", ret, msg);
+ return ret;
+}
+
+static int lsm303dlh_m_restore(struct lsm303dlh_m_data *ddata)
+{
+ int ret = 0;
+
+ ret = lsm303dlh_m_write(ddata, CRB_REG_M, ddata->range, "SET RANGE");
+
+ if (ret < 0)
+ goto fail;
+
+ ret = lsm303dlh_m_write(ddata, CRA_REG_M, ddata->rate, "SET RATE");
+
+ if (ret < 0)
+ goto fail;
+
+ ret = lsm303dlh_m_set_mode(ddata, ddata->mode);
+
+ if (ret < 0)
+ goto fail;
+
+fail:
+ return ret;
+}
+
+static int lsm303dlh_m_read_multi(struct lsm303dlh_m_data *ddata, u8 reg,
+ u8 count, u8 *val, char *msg)
+{
+ int ret = i2c_smbus_read_i2c_block_data(ddata->client,
+ reg | MULTIPLE_I2C_TR, count, val);
+ if (ret < 0)
+ dev_err(&ddata->client->dev,
+ "i2c_smbus_read_i2c_block_data failed error %d\
+ Register (%s)\n", ret, msg);
+ return ret;
+}
+
+static ssize_t lsm303dlh_m_show_rate(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct lsm303dlh_m_data *ddata = platform_get_drvdata(pdev);
+
+ return sprintf(buf, "%d\n", ddata->rate >> LSM303DLH_M_CRA_DO_BIT);
+}
/* set lsm303dlh magnetometer bandwidth */
-int lsm303dlh_m_set_rate(struct lsm303dlh_m_data *ldata, unsigned char bw)
+static ssize_t lsm303dlh_m_store_rate(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
{
- unsigned char data = 0;
- int res;
+ struct platform_device *pdev = to_platform_device(dev);
+ struct lsm303dlh_m_data *ddata = platform_get_drvdata(pdev);
+ unsigned long val;
+ unsigned char data;
+ int error;
- switch (bw) {
- case LSM303DLH_M_RATE_00_75:
- ldata->ms_delay = 1334;
- break;
- case LSM303DLH_M_RATE_01_50:
- ldata->ms_delay = 667;
- break;
- case LSM303DLH_M_RATE_03_00:
- ldata->ms_delay = 334;
- break;
- case LSM303DLH_M_RATE_07_50:
- ldata->ms_delay = 134;
- break;
- case LSM303DLH_M_RATE_15_00:
- ldata->ms_delay = 67;
- break;
- case LSM303DLH_M_RATE_30_00:
- ldata->ms_delay = 34;
- break;
- case LSM303DLH_M_RATE_75_00:
- ldata->ms_delay = 14;
- break;
- default:
- return -EINVAL;
+ if (ddata->mode == LSM303DLH_M_MODE_SLEEP) {
+ dev_info(&ddata->client->dev,
+ "device is switched off,make it ON using MODE");
+ return count;
}
- data |= ((bw << LSM303DLH_M_CRA_DO_BIT) & LSM303DLH_M_CRA_DO_MASK);
+ error = strict_strtoul(buf, 0, &val);
+ if (error)
+ return error;
- res = i2c_smbus_write_byte_data(ldata->client, CRA_REG_M, data);
+ mutex_lock(&ddata->lock);
- ldata->rate = bw;
+ data = ((val << LSM303DLH_M_CRA_DO_BIT) & LSM303DLH_M_CRA_DO_MASK);
+ ddata->rate = data;
- /* 100ms delay needed after setting mode. */
- msleep(100);
+ error = lsm303dlh_m_write(ddata, CRA_REG_M, data, "SET RATE");
+
+ if (error < 0) {
+ mutex_unlock(&ddata->lock);
+ return error;
+ }
- return res;
+ mutex_unlock(&ddata->lock);
+
+ return count;
}
-/* read selected bandwidth from lsm303dlh_mag */
-int lsm303dlh_m_get_bandwidth(struct lsm303dlh_m_data *ldata, unsigned char *bw)
+static int lsm303dlh_m_xyz_read(struct lsm303dlh_m_data *ddata)
{
- unsigned char data;
+ unsigned char xyz_data[6];
+ int ret = lsm303dlh_m_read_multi(ddata, OUT_X_M,
+ 6, xyz_data, "OUT_X_M");
+ if (ret < 0)
+ return -EINVAL;
- data = i2c_smbus_read_byte_data(ldata->client, CRA_REG_M);
- data &= LSM303DLH_M_CRA_DO_MASK;
- data >>= LSM303DLH_M_CRA_DO_BIT;
+ /* MSB is at lower address */
+ ddata->data[0] = (short)
+ (((xyz_data[0]) << 8) | xyz_data[1]);
+ ddata->data[1] = (short)
+ (((xyz_data[2]) << 8) | xyz_data[3]);
+ ddata->data[2] = (short)
+ (((xyz_data[4]) << 8) | xyz_data[5]);
+
+ /* taking orientation of x,y,z axis into account*/
+
+ ddata->data[ddata->pdata.axis_map_x] = ddata->pdata.negative_x ?
+ -ddata->data[ddata->pdata.axis_map_x] :
+ ddata->data[ddata->pdata.axis_map_x];
+ ddata->data[ddata->pdata.axis_map_y] = ddata->pdata.negative_y ?
+ -ddata->data[ddata->pdata.axis_map_y] :
+ ddata->data[ddata->pdata.axis_map_y];
+ ddata->data[ddata->pdata.axis_map_z] = ddata->pdata.negative_z ?
+ -ddata->data[ddata->pdata.axis_map_z] :
+ ddata->data[ddata->pdata.axis_map_z];
+
+ return ret;
+}
- return data;
+static ssize_t lsm303dlh_m_gain(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct lsm303dlh_m_data *ddata = platform_get_drvdata(pdev);
+
+ return sprintf(buf, "%8x:%8x:%8x\n",
+ ddata->gain[ddata->pdata.axis_map_x],
+ ddata->gain[ddata->pdata.axis_map_y],
+ ddata->gain[ddata->pdata.axis_map_z]);
}
-/* i2c read routine for lsm303dlh magnetometer */
-static int lsm303dlh_m_one_axis(struct lsm303dlh_m_data *ldata,
- unsigned char reg_addr,
- u8 negative,
- short *axis_data)
+static ssize_t lsm303dlh_m_values(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
{
- s32 read_data;
- short data;
+ struct platform_device *pdev = to_platform_device(dev);
+ struct lsm303dlh_m_data *ddata = platform_get_drvdata(pdev);
+ int ret = 0;
+
+ if (ddata->mode == LSM303DLH_M_MODE_SLEEP) {
+ dev_info(&ddata->client->dev,
+ "device is switched off,make it ON using MODE");
+ return ret;
+ }
- /* No global client pointer? */
- if (ldata->client == NULL)
- return -EINVAL;
+ mutex_lock(&ddata->lock);
- /* MSB is at lower address. */
- read_data = i2c_smbus_read_word_data(ldata->client, reg_addr);
- if (read_data < 0)
+ ret = lsm303dlh_m_xyz_read(ddata);
+ if (ret < 0) {
+ mutex_unlock(&ddata->lock);
return -EINVAL;
+ }
- data = ((read_data & 0xFF) << 8) | ((read_data >> 8) & 0xFF);
+ mutex_unlock(&ddata->lock);
- *axis_data = negative ? -data : data;
+ /* taking orientation of x,y,z axis into account*/
- return 0;
+ return sprintf(buf, "%8x:%8x:%8x\n",
+ ddata->data[ddata->pdata.axis_map_x],
+ ddata->data[ddata->pdata.axis_map_y],
+ ddata->data[ddata->pdata.axis_map_z]);
}
-
-/* X,Y and Z-axis magnetometer data readout
- * param *data pointer to \ref 6 bytes buffer for x,y,z data readout
- * note data will be read by multi-byte protocol into a 6 byte structure
- */
-ssize_t lsm303dlh_m_read(struct file *filp, char __user *buf, size_t count,
- loff_t *f_pos)
+static int lsm303dlh_m_set_mode(struct lsm303dlh_m_data *ddata,
+ unsigned char mode)
{
- int res;
- short xyz_data[3];
- struct lsm303dlh_m_data *ldata = filp->private_data;
+ int ret;
- if (count < sizeof(xyz_data))
- return -EINVAL;
+ mode = (mode << LSM303DLH_M_MR_MD_BIT);
+
+ ret = i2c_smbus_write_byte_data(ddata->client, MR_REG_M, mode);
- while (1) {
- res = i2c_smbus_read_byte_data(ldata->client, SR_REG_M);
- if (res < 0)
- return res;
+ if (ret < 0)
+ dev_err(&ddata->client->dev,
+ "i2c_smbus_write_byte_data failed error %d\
+ Register (%s)\n", ret, "MODE CONTROL");
+
+ return ret;
+}
+
+#ifdef CONFIG_SENSORS_LSM303DLH_INPUT_DEVICE
+
+static irqreturn_t lsm303dlh_m_gpio_irq(int irq, void *device_data)
+{
+ struct lsm303dlh_m_data *ddata = device_data;
+ int ret;
- if (res & LSM303DLH_M_SR_RDY_MASK)
- break;
+ ret = lsm303dlh_m_xyz_read(ddata);
- /* Wait for sampling period. */
- if (msleep_interruptible(ldata->ms_delay))
- return -EINTR;
+ if (ret < 0) {
+ dev_err(&ddata->client->dev,
+ "reading data of xyz failed error %d\n", ret);
+ return IRQ_NONE;
}
- res = lsm303dlh_m_one_axis(ldata, OUT_X_M, ldata->pdata.negative_x,
- &xyz_data[ldata->pdata.axis_map_x]);
- if (res != 0)
- return -EINVAL;
+ /* taking orientation of x,y,z axis into account*/
- res = lsm303dlh_m_one_axis(ldata, OUT_Y_M, ldata->pdata.negative_y,
- &xyz_data[ldata->pdata.axis_map_y]);
- if (res != 0)
- return -EINVAL;
+ input_report_abs(ddata->input_dev, ABS_X,
+ ddata->data[ddata->pdata.axis_map_x]);
+ input_report_abs(ddata->input_dev, ABS_Y,
+ ddata->data[ddata->pdata.axis_map_y]);
+ input_report_abs(ddata->input_dev, ABS_Z,
+ ddata->data[ddata->pdata.axis_map_z]);
+ input_sync(ddata->input_dev);
- res = lsm303dlh_m_one_axis(ldata, OUT_Z_M, ldata->pdata.negative_z,
- &xyz_data[ldata->pdata.axis_map_z]);
- if (res != 0)
- return -EINVAL;
+ return IRQ_HANDLED;
- dev_dbg(&ldata->client->dev, "Hx= %d, Hy= %d, Hz= %d\n",
- xyz_data[ldata->pdata.axis_map_x],
- xyz_data[ldata->pdata.axis_map_y],
- xyz_data[ldata->pdata.axis_map_z]);
+}
+#endif
- if (copy_to_user(buf, xyz_data, sizeof(xyz_data)) != 0) {
- dev_err(&ldata->client->dev, "copy_to error\n");
- res = -EFAULT;
- }
+static ssize_t lsm303dlh_m_show_range(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct lsm303dlh_m_data *ddata = platform_get_drvdata(pdev);
- return sizeof(xyz_data);
+ return sprintf(buf, "%d\n", ddata->range >> LSM303DLH_M_CRB_GN_BIT);
}
-int lsm303dlh_m_set_range(struct lsm303dlh_m_data *ldata, unsigned char range)
+static ssize_t lsm303dlh_m_store_range(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count)
{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct lsm303dlh_m_data *ddata = platform_get_drvdata(pdev);
short xy_gain;
short z_gain;
+ unsigned long range;
+
+ int error;
+
+ if (ddata->mode == LSM303DLH_M_MODE_SLEEP) {
+ dev_info(&ddata->client->dev,
+ "device is switched off,make it ON using MODE");
+ return count;
+ }
+
+ error = strict_strtoul(buf, 0, &range);
+ if (error)
+ return error;
switch (range) {
case LSM303DLH_M_RANGE_1_3G:
@@ -288,299 +424,254 @@ int lsm303dlh_m_set_range(struct lsm303dlh_m_data *ldata, unsigned char range)
return -EINVAL;
}
- ldata->gain[ldata->pdata.axis_map_x] = xy_gain;
- ldata->gain[ldata->pdata.axis_map_y] = xy_gain;
- ldata->gain[ldata->pdata.axis_map_z] = z_gain;
+ mutex_lock(&ddata->lock);
+
+ ddata->gain[ddata->pdata.axis_map_x] = xy_gain;
+ ddata->gain[ddata->pdata.axis_map_y] = xy_gain;
+ ddata->gain[ddata->pdata.axis_map_z] = z_gain;
range <<= LSM303DLH_M_CRB_GN_BIT;
+ range &= LSM303DLH_M_CRB_GN_MASK;
+
+ ddata->range = range;
+
+ error = lsm303dlh_m_write(ddata, CRB_REG_M, range, "SET RANGE");
+ mutex_unlock(&ddata->lock);
- return i2c_smbus_write_byte_data(ldata->client, CRB_REG_M, range);
+ if (error < 0)
+ return error;
+ return count;
}
-int lsm303dlh_m_set_mode(struct lsm303dlh_m_data *ldata, unsigned char mode)
+static ssize_t lsm303dlh_m_show_mode(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
{
- int res;
- struct i2c_client *client = ldata->client;
- unsigned char data;
+ struct platform_device *pdev = to_platform_device(dev);
+ struct lsm303dlh_m_data *ddata = platform_get_drvdata(pdev);
- if (ldata->mode == mode)
- return 0;
+ return sprintf(buf, "%d\n", ddata->mode);
+}
- if (ldata->mode == LSM303DLH_M_MODE_SLEEP)
- regulator_enable(ldata->regulator);
+static ssize_t lsm303dlh_m_store_mode(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct lsm303dlh_m_data *ddata = platform_get_drvdata(pdev);
+ unsigned long mode;
+ int error;
- data = (mode << LSM303DLH_M_MR_MD_BIT) & LSM303DLH_M_MR_MD_MASK;
- res = i2c_smbus_write_byte_data(ldata->client, MR_REG_M, data);
- if (res < 0) {
- dev_err(&client->dev, "failed to write ctrl reg\n");
- regulator_disable(ldata->regulator);
- return res;
- }
+ error = strict_strtoul(buf, 0, &mode);
+ if (error)
+ return error;
- /* if sleep mode is requested, disable the regulator */
- if (mode == LSM303DLH_M_MODE_SLEEP)
- regulator_disable(ldata->regulator);
+ /* if same mode as existing, return */
+ if (ddata->mode == mode)
+ return 0;
- /* update the new mode accordingly */
- ldata->mode = mode;
+ /* turn on the supplies if already off */
+ if (ddata->mode == LSM303DLH_M_MODE_SLEEP) {
+ regulator_enable(ddata->regulator);
- return 0;
-}
+#ifdef CONFIG_SENSORS_LSM303DLH_INPUT_DEVICE
+ enable_irq(gpio_to_irq(ddata->pdata.irq_m));
+#endif
+ }
-/* open command for lsm303dlh_m device file */
-static int lsm303dlh_m_open(struct inode *inode, struct file *filp)
-{
- struct lsm303dlh_m_data *ldata = file_private;
+ mutex_lock(&ddata->lock);
- filp->private_data = ldata;
+ error = lsm303dlh_m_set_mode(ddata, mode);
- if (ldata->client == NULL) {
- printk(KERN_ERR"I2C driver not install\n");
- return -EINVAL;
- } else if (filp->f_flags & O_NONBLOCK) {
- dev_err(&ldata->client->dev,
- "Non Blocking operations are not supported\n");
- return -EAGAIN;
- }
+ ddata->mode = (mode >> LSM303DLH_M_MR_MD_BIT);
+ mutex_unlock(&ddata->lock);
- dev_dbg(&ldata->client->dev, "lsm303dlh_m has been opened\n");
+ if (error < 0)
+ return error;
- return 0;
-}
+ if (mode == LSM303DLH_M_MODE_SLEEP) {
-/* release command for lsm303dlh_m device file */
-static int lsm303dlh_m_close(struct inode *inode, struct file *filp)
-{
- struct lsm303dlh_m_data *ldata = filp->private_data;
+#ifdef CONFIG_SENSORS_LSM303DLH_INPUT_DEVICE
+ disable_irq(gpio_to_irq(ddata->pdata.irq_m));
+#endif
- dev_dbg(&ldata->client->dev, "lsm303dlh_m has been closed\n");
- return 0;
+ /*
+ * No need to store context here, it is not like
+ * suspend/resume but fall back to default values
+ */
+ ddata->rate = LSM303DLH_M_RATE_00_75;
+ ddata->range = LSM303DLH_M_RANGE_1_3G;
+ ddata->range <<= LSM303DLH_M_CRB_GN_BIT;
+ ddata->range &= LSM303DLH_M_CRB_GN_MASK;
+ ddata->gain[ddata->pdata.axis_map_x] = XY_GAIN_1_3;
+ ddata->gain[ddata->pdata.axis_map_y] = XY_GAIN_1_3;
+ ddata->gain[ddata->pdata.axis_map_z] = Z_GAIN_1_3;
+
+ regulator_disable(ddata->regulator);
+ }
+
+ return count;
}
+static DEVICE_ATTR(gain, S_IRUGO, lsm303dlh_m_gain, NULL);
-/* ioctl command for lsm303dlh_m device file */
-static int lsm303dlh_m_ioctl(struct inode *inode, struct file *filp,
- unsigned int cmd, unsigned long arg)
-{
- int err = 0;
- unsigned char data[6];
- struct lsm303dlh_m_data *ldata = filp->private_data;
-
- /* check lsm303dlh_m_client */
- if (ldata->client == NULL) {
- printk(KERN_ERR"I2C driver not install\n");
- return -EFAULT;
- }
+static DEVICE_ATTR(data, S_IRUGO, lsm303dlh_m_values, NULL);
- /* cmd mapping */
- switch (cmd) {
-
- case LSM303DLH_M_SET_RANGE:
- if (copy_from_user(data, (unsigned char *)arg, 1) != 0) {
- dev_err(&ldata->client->dev, "copy_from_user error\n");
- return -EFAULT;
- }
- err = lsm303dlh_m_set_range(ldata, *data);
- return err;
-
- case LSM303DLH_M_SET_RATE:
- if (copy_from_user(data, (unsigned char *)arg, 1) != 0) {
- dev_err(&ldata->client->dev, "copy_from_user error\n");
- return -EFAULT;
- }
- err = lsm303dlh_m_set_rate(ldata, *data);
- return err;
-
- case LSM303DLH_M_SET_MODE:
- if (copy_from_user(data, (unsigned char *)arg, 1) != 0) {
- dev_err(&ldata->client->dev, "copy_from_user error\n");
- return -EFAULT;
- }
- err = lsm303dlh_m_set_mode(ldata, *data);
- return err;
-
- case LSM303DLH_M_GET_GAIN:
- if (copy_to_user((unsigned char *)arg,
- ldata->gain, sizeof(ldata->gain)) != 0) {
- dev_err(&ldata->client->dev, "copy_to_user error\n");
- return -EFAULT;
- }
- return 0;
+static DEVICE_ATTR(mode, S_IWUGO | S_IRUGO,
+ lsm303dlh_m_show_mode, lsm303dlh_m_store_mode);
- case LSM303DLH_M_GET_MODE:
- if (copy_to_user((unsigned char *)arg,
- &ldata->mode, sizeof(ldata->mode)) != 0) {
- dev_err(&ldata->client->dev, "copy_to_user error\n");
- return -EFAULT;
- }
- return 0;
+static DEVICE_ATTR(range, S_IWUGO | S_IRUGO,
+ lsm303dlh_m_show_range, lsm303dlh_m_store_range);
- default:
- return -EINVAL;
- }
-}
+static DEVICE_ATTR(rate, S_IWUGO | S_IRUGO,
+ lsm303dlh_m_show_rate, lsm303dlh_m_store_rate);
-static const struct file_operations lsm303dlh_m_fops = {
- .owner = THIS_MODULE,
- .llseek = no_llseek,
- .read = lsm303dlh_m_read,
- .open = lsm303dlh_m_open,
- .release = lsm303dlh_m_close,
- .ioctl = lsm303dlh_m_ioctl,
+static struct attribute *lsm303dlh_m_attributes[] = {
+ &dev_attr_gain.attr,
+ &dev_attr_data.attr,
+ &dev_attr_mode.attr,
+ &dev_attr_range.attr,
+ &dev_attr_rate.attr,
+ NULL
};
-static struct miscdevice lsm303dlh_m_misc_device = {
- .minor = MISC_DYNAMIC_MINOR,
- .name = "lsm303dlh_m",
- .fops = &lsm303dlh_m_fops,
+static const struct attribute_group lsm303dlh_m_attr_group = {
+ .attrs = lsm303dlh_m_attributes,
};
-static int lsm303dlh_m_validate_pdata(struct lsm303dlh_m_data *mdata)
+static int __devinit lsm303dlh_m_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
{
- if ((mdata->pdata.axis_map_x > 2) ||
- (mdata->pdata.axis_map_y > 2) ||
- (mdata->pdata.axis_map_z > 2) ||
- (mdata->pdata.axis_map_x == mdata->pdata.axis_map_y) ||
- (mdata->pdata.axis_map_x == mdata->pdata.axis_map_z) ||
- (mdata->pdata.axis_map_y == mdata->pdata.axis_map_z)) {
- dev_err(&mdata->client->dev,
- "invalid axis_map value x:%u y:%u z%u\n",
- mdata->pdata.axis_map_x, mdata->pdata.axis_map_y,
- mdata->pdata.axis_map_z);
- return -EINVAL;
+ int ret;
+ struct lsm303dlh_m_data *ddata = NULL;
+ unsigned char version[3];
+
+ ddata = kzalloc(sizeof(struct lsm303dlh_m_data), GFP_KERNEL);
+ if (ddata == NULL) {
+ ret = -ENOMEM;
+ goto err_op_failed;
}
- /* Only allow 0 and 1 */
- if ((mdata->pdata.negative_x > 1) ||
- (mdata->pdata.negative_y > 1) ||
- (mdata->pdata.negative_z > 1)) {
- dev_err(&mdata->client->dev,
- "invalid negate value x:%u y:%u z:%u\n",
- mdata->pdata.negative_x, mdata->pdata.negative_y,
- mdata->pdata.negative_z);
- return -EINVAL;
- }
+ ddata->client = client;
+ i2c_set_clientdata(client, ddata);
- return 0;
-}
+ /* copy platform specific data */
+ memcpy(&ddata->pdata, client->dev.platform_data, sizeof(ddata->pdata));
-int lsm303dlh_m_probe(struct i2c_client *client,
- const struct i2c_device_id *devid)
-{
- int err = 0;
- int tempvalue;
- struct lsm303dlh_m_data *ldata;
-
- if (client->dev.platform_data == NULL) {
- dev_err(&client->dev, "platform data is NULL. exiting.\n");
- err = -ENODEV;
- goto exit;
- }
+ ddata->mode = LSM303DLH_M_MODE_SLEEP;
+ ddata->rate = LSM303DLH_M_RATE_00_75;
+ ddata->range = LSM303DLH_M_RANGE_1_3G;
+ ddata->range <<= LSM303DLH_M_CRB_GN_BIT;
+ ddata->range &= LSM303DLH_M_CRB_GN_MASK;
+ ddata->gain[ddata->pdata.axis_map_x] = XY_GAIN_1_3;
+ ddata->gain[ddata->pdata.axis_map_y] = XY_GAIN_1_3;
+ ddata->gain[ddata->pdata.axis_map_z] = Z_GAIN_1_3;
+ dev_set_name(&client->dev, ddata->pdata.name_m);
+ ddata->regulator = regulator_get(&client->dev, "v-mag");
-#if 0 /* TEMP */
- if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
- err = -ENODEV;
- goto exit;
+ if (IS_ERR(ddata->regulator)) {
+ dev_err(&client->dev, "failed to get regulator\n");
+ ret = PTR_ERR(ddata->regulator);
+ ddata->regulator = NULL;
}
- if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_I2C_BLOCK))
- goto exit;
-#endif
-
- /*
- * OK. For now, we presume we have a valid client. We now create the
- * client structure, even though we cannot fill it completely yet.
- */
+ if (ddata->regulator)
+ regulator_enable(ddata->regulator);
- ldata = kzalloc(sizeof(*ldata), GFP_KERNEL);
+ ret = lsm303dlh_m_read_multi(ddata, IRA_REG_M, 3, version, "IRA_REG_M");
+ if (ret < 0)
+ goto exit_free_regulator;
- if (ldata == NULL) {
- err = -ENOMEM;
- goto exit;
- }
+ dev_info(&client->dev, "Magnetometer, ID : %x:%x:%x",
+ version[0], version[1], version[2]);
- file_private = ldata;
+ mutex_init(&ddata->lock);
- /* Initialize gain by 1 to avoid any divided by 0 errors */
- ldata->gain[0] = 1;
- ldata->gain[1] = 1;
- ldata->gain[2] = 1;
+ ret = sysfs_create_group(&client->dev.kobj, &lsm303dlh_m_attr_group);
+ if (ret)
+ goto exit_free_regulator;
- i2c_set_clientdata(client, ldata);
- ldata->client = client;
+#ifdef CONFIG_SENSORS_LSM303DLH_INPUT_DEVICE
- memcpy(&ldata->pdata, client->dev.platform_data, sizeof(ldata->pdata));
- err = lsm303dlh_m_validate_pdata(ldata);
- if (err < 0) {
- dev_err(&client->dev, "failed to validate platform data\n");
- goto exit_kfree;
+ ddata->input_dev = input_allocate_device();
+ if (!ddata->input_dev) {
+ ret = -ENOMEM;
+ dev_err(&client->dev, "Failed to allocate input device\n");
+ goto exit_free_regulator;
}
- ldata->mode = LSM303DLH_M_MODE_SLEEP;
- dev_set_name(&client->dev, ldata->pdata.name);
+ set_bit(EV_ABS, ddata->input_dev->evbit);
- ldata->regulator = regulator_get(&client->dev, "v-mag");
- if (IS_ERR(ldata->regulator)) {
- dev_err(&client->dev, "failed to get regulator\n");
- err = PTR_ERR(ldata->regulator);
- goto exit_free_regulator;
- }
- regulator_enable(ldata->regulator);
-
- err = i2c_smbus_read_byte(client);
- if (err < 0) {
- dev_err(&client->dev, "i2c_smbus_read_byte error!!\n");
- err = -ENODEV;
- goto exit_disable_regulator;
- } else {
- dev_info(&client->dev, "magnetometer detected\n");
- }
+ /* x-axis acceleration */
+ input_set_abs_params(ddata->input_dev, ABS_X, -32768, 32767, 0, 0);
+ /* y-axis acceleration */
+ input_set_abs_params(ddata->input_dev, ABS_Y, -32768, 32767, 0, 0);
+ /* z-axis acceleration */
+ input_set_abs_params(ddata->input_dev, ABS_Z, -32768, 32767, 0, 0);
- /* read chip id */
- tempvalue = i2c_smbus_read_word_data(client, IRA_REG_M);
- if (!(tempvalue & 0x00FF) == 0x0048) {
- ldata->client = NULL;
- err = -EINVAL;
- goto exit_disable_regulator;
+ ddata->input_dev->name = "magnetometer";
+
+ ret = input_register_device(ddata->input_dev);
+ if (ret) {
+ dev_err(&client->dev, "Unable to register input device: %s\n",
+ ddata->input_dev->name);
+ goto err_input_register_failed;
}
- if (misc_register(&lsm303dlh_m_misc_device)) {
- dev_err(&client->dev, "misc_register failed\n");
- err = -EINVAL;
- goto error;
+ /* register interrupt */
+ ret = request_threaded_irq(gpio_to_irq(ddata->pdata.irq_m), NULL,
+ lsm303dlh_m_gpio_irq,
+ IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING, "lsm303dlh_m",
+ ddata);
+ if (ret) {
+ dev_err(&client->dev, "request irq EGPIO_PIN_1 failed\n");
+ goto err_input_failed;
}
- regulator_disable(ldata->regulator);
+ disable_irq(gpio_to_irq(ddata->pdata.irq_m));
+#endif
- return 0;
+ return ret;
-error:
- dev_err(&client->dev, "%s: Driver Initialization failed\n", __FILE__);
-exit_disable_regulator:
- regulator_disable(ldata->regulator);
+#ifdef CONFIG_SENSORS_LSM303DLH_INPUT_DEVICE
+err_input_failed:
+ input_unregister_device(ddata->input_dev);
+err_input_register_failed:
+ input_free_device(ddata->input_dev);
+#endif
exit_free_regulator:
- regulator_put(ldata->regulator);
-exit_kfree:
- kfree(ldata);
-exit:
- return err;
+ if (ddata->regulator) {
+ regulator_disable(ddata->regulator);
+ regulator_put(ddata->regulator);
+ }
+err_op_failed:
+ dev_err(&client->dev, " lsm303dlh_m_probe failed %x", ret);
+ kfree(ddata);
+ return ret;
}
-static int lsm303dlh_m_remove(struct i2c_client *client)
+static int __devexit lsm303dlh_m_remove(struct i2c_client *client)
{
- struct lsm303dlh_m_data *data = i2c_get_clientdata(client);
+ struct lsm303dlh_m_data *ddata;
- dev_info(&client->dev, "LSM303DLH_M driver removing\n");
+ ddata = i2c_get_clientdata(client);
- misc_deregister(&lsm303dlh_m_misc_device);
+#ifdef CONFIG_SENSORS_LSM303DLH_INPUT_DEVICE
+ input_unregister_device(ddata->input_dev);
+ input_free_device(ddata->input_dev);
+#endif
- if (data->mode != LSM303DLH_M_MODE_SLEEP)
- lsm303dlh_m_set_mode(data, LSM303DLH_M_MODE_SLEEP);
+ sysfs_remove_group(&client->dev.kobj, &lsm303dlh_m_attr_group);
- regulator_put(data->regulator);
+ /* safer to make device off */
+ if (ddata->mode != LSM303DLH_M_MODE_SLEEP) {
+ lsm303dlh_m_set_mode(ddata, LSM303DLH_M_MODE_SLEEP);
+ regulator_disable(ddata->regulator);
+ }
- kfree(data);
+ regulator_put(ddata->regulator);
+ i2c_set_clientdata(client, NULL);
+ kfree(ddata);
return 0;
}
@@ -588,78 +679,86 @@ static int lsm303dlh_m_remove(struct i2c_client *client)
#ifdef CONFIG_PM
static int lsm303dlh_m_suspend(struct device *dev)
{
- int reg_data;
- struct lsm303dlh_m_data *ldata = dev_get_drvdata(dev);
+ struct lsm303dlh_m_data *ddata;
+ int ret;
- if (ldata->mode == LSM303DLH_M_MODE_SLEEP)
- return 0;
+ ddata = dev_get_drvdata(dev);
- /* mode is not off; safely read the ctrl register */
- reg_data = i2c_smbus_read_byte_data(ldata->client, CRA_REG_M);
- if (reg_data < 0)
- return reg_data;
+ if (ddata->mode == LSM303DLH_M_MODE_SLEEP)
+ return 0;
- ldata->pre_suspend_mode = reg_data & 0xFF;
+#ifdef CONFIG_SENSORS_LSM303DLH_INPUT_DEVICE
+ disable_irq(gpio_to_irq(ddata->pdata.irq_m));
+#endif
- lsm303dlh_m_set_mode(ldata, LSM303DLH_M_MODE_SLEEP);
+ ret = lsm303dlh_m_set_mode(ddata, LSM303DLH_M_MODE_SLEEP);
+ if (ret < 0)
+ return ret;
- return 0;
+ regulator_disable(ddata->regulator);
+ return ret;
}
static int lsm303dlh_m_resume(struct device *dev)
{
- struct lsm303dlh_m_data *ldata = dev_get_drvdata(dev);
+ struct lsm303dlh_m_data *ddata;
+ int ret;
- /* restore device to pre-suspend mode */
- lsm303dlh_m_set_mode(ldata, ldata->pre_suspend_mode);
- lsm303dlh_m_set_rate(ldata, ldata->rate);
+ ddata = dev_get_drvdata(dev);
- return 0;
-}
+ /* in correct mode, no need to change it */
+ if (ddata->mode == LSM303DLH_M_MODE_SLEEP)
+ return 0;
+
+#ifdef CONFIG_SENSORS_LSM303DLH_INPUT_DEVICE
+ enable_irq(gpio_to_irq(ddata->pdata.irq_m));
+#endif
+ regulator_enable(ddata->regulator);
+
+ ret = lsm303dlh_m_restore(ddata);
+
+ if (ret < 0)
+ return ret;
+
+ return ret;
+}
static const struct dev_pm_ops lsm303dlh_m_dev_pm_ops = {
.suspend = lsm303dlh_m_suspend,
.resume = lsm303dlh_m_resume,
};
-#endif
+#endif /* CONFIG_PM */
static const struct i2c_device_id lsm303dlh_m_id[] = {
{ "lsm303dlh_m", 0 },
{ },
};
-MODULE_DEVICE_TABLE(i2c, lsm303dlh_m_id);
-
static struct i2c_driver lsm303dlh_m_driver = {
- .class = I2C_CLASS_HWMON,
- .probe = lsm303dlh_m_probe,
- .remove = __devexit_p(lsm303dlh_m_remove),
- .id_table = lsm303dlh_m_id,
+ .probe = lsm303dlh_m_probe,
+ .remove = lsm303dlh_m_remove,
+ .id_table = lsm303dlh_m_id,
.driver = {
- .owner = THIS_MODULE,
.name = "lsm303dlh_m",
#ifdef CONFIG_PM
- .pm = &lsm303dlh_m_dev_pm_ops,
+ .pm = &lsm303dlh_m_dev_pm_ops,
#endif
},
};
static int __init lsm303dlh_m_init(void)
{
- /* Add i2c driver for lsm303dlh magnetometer */
return i2c_add_driver(&lsm303dlh_m_driver);
}
static void __exit lsm303dlh_m_exit(void)
{
i2c_del_driver(&lsm303dlh_m_driver);
- return;
}
module_init(lsm303dlh_m_init);
module_exit(lsm303dlh_m_exit);
-MODULE_DESCRIPTION("lsm303dlh magnetometer driver");
-MODULE_AUTHOR("STMicroelectronics");
+MODULE_DESCRIPTION("lSM303DLH 3-Axis Magnetometer Driver");
MODULE_LICENSE("GPL");
-
+MODULE_AUTHOR("STMicroelectronics");