diff options
author | Chethan Krishna N <chethan.krishna@stericsson.com> | 2010-11-17 17:13:52 +0530 |
---|---|---|
committer | Sundar Iyer <sundar.iyer@stericsson.com> | 2010-11-29 16:47:35 +0530 |
commit | 6aeff4e1800b0535a63f8d2b89931782e49bdf02 (patch) | |
tree | 991e19dc472a33909a95df1d2f0c47c8d8a135ad /drivers/hwmon | |
parent | 17134ce72212575a88e0dd9ea4cd25978e31f061 (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/Kconfig | 9 | ||||
-rw-r--r-- | drivers/hwmon/lsm303dlh_a.c | 1534 | ||||
-rw-r--r-- | drivers/hwmon/lsm303dlh_m.c | 909 |
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"); |