From d7c90be4603f5cc4364a6bb648393f924f0a6f1f Mon Sep 17 00:00:00 2001 From: Tony Månsson Date: Tue, 22 Mar 2011 14:23:03 +0100 Subject: Adding l3g4200d Gyroscope --- drivers/hwmon/Kconfig | 11 + drivers/hwmon/Makefile | 1 + drivers/hwmon/l3g4200d.c | 644 +++++++++++++++++++++++++++++++++++++++++++++++ include/linux/l3g4200d.h | 27 ++ 4 files changed, 683 insertions(+) create mode 100644 drivers/hwmon/l3g4200d.c create mode 100644 include/linux/l3g4200d.h diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig index 8deedc1b984..324f9b084d0 100644 --- a/drivers/hwmon/Kconfig +++ b/drivers/hwmon/Kconfig @@ -687,6 +687,17 @@ config SENSORS_LTC4151 This driver can also be built as a module. If so, the module will be called ltc4151. +config SENSORS_L3G4200D + tristate "ST L3G4200D 3-axis gyroscope" + depends on I2C + default n + help + If you say yes here you get support for 3-axis gyroscope device + L3g4200D. + + This driver can also be built as a module. If so, the module + will be called l3g4200d. + config SENSORS_LTC4215 tristate "Linear Technology LTC4215" depends on I2C && EXPERIMENTAL diff --git a/drivers/hwmon/Makefile b/drivers/hwmon/Makefile index 6d3f11f7181..6e9aa4dccc4 100644 --- a/drivers/hwmon/Makefile +++ b/drivers/hwmon/Makefile @@ -84,6 +84,7 @@ obj-$(CONFIG_SENSORS_LM93) += lm93.o obj-$(CONFIG_SENSORS_LM95241) += lm95241.o obj-$(CONFIG_SENSORS_LM95245) += lm95245.o obj-$(CONFIG_SENSORS_LTC4151) += ltc4151.o +obj-$(CONFIG_SENSORS_L3G4200D) += l3g4200d.o obj-$(CONFIG_SENSORS_LTC4215) += ltc4215.o obj-$(CONFIG_SENSORS_LTC4245) += ltc4245.o obj-$(CONFIG_SENSORS_LTC4261) += ltc4261.o diff --git a/drivers/hwmon/l3g4200d.c b/drivers/hwmon/l3g4200d.c new file mode 100644 index 00000000000..718abbb748b --- /dev/null +++ b/drivers/hwmon/l3g4200d.c @@ -0,0 +1,644 @@ +/* + * ST L3G4200D 3-Axis Gyroscope Driver + * + * Copyright (C) ST-Ericsson SA 2011 + * Author: Chethan Krishna N for ST-Ericsson + * Licence terms: GNU General Public Licence (GPL) version 2 + */ + +#include +#include +#include +#include +#include +#include + +#include +#include + +/* l3g4200d gyroscope registers */ + +#define WHO_AM_I 0x0F + +#define CTRL_REG1 0x20 /* CTRL REG1 */ +#define CTRL_REG2 0x21 /* CTRL REG2 */ +#define CTRL_REG3 0x22 /* CTRL_REG3 */ +#define CTRL_REG4 0x23 /* CTRL_REG4 */ +#define CTRL_REG5 0x24 /* CTRL_REG5 */ + +#define AXISDATA_REG 0x28 + +/** Registers Contents */ + +#define WHOAMI_L3G4200D 0x00D3 /* Expected content for WAI register*/ + +/* CTRL_REG1 */ +#define PM_OFF 0x00 +#define PM_ON 0x01 +#define ENABLE_ALL_AXES 0x07 +#define BW00 0x00 +#define BW01 0x10 +#define BW10 0x20 +#define BW11 0x30 +#define ODR00 0x00 /* ODR = 100Hz */ +#define ODR01 0x40 /* ODR = 200Hz */ +#define ODR10 0x80 /* ODR = 400Hz */ +#define ODR11 0xC0 /* ODR = 800Hz */ +#define L3G4200D_PM_BIT 3 +#define L3G4200D_PM_MASK (0x01 << L3G4200D_PM_BIT) +#define L3G4200D_ODR_BIT 4 +#define L3G4200D_ODR_MASK (0x0F << L3G4200D_ODR_BIT) +#define L3G4200D_ODR_MIN_VAL 0x00 +#define L3G4200D_ODR_MAX_VAL 0x0F + +/* CTRL_REG4 */ +#define FS250 0x00 +#define FS500 0x01 +#define FS2000 0x03 +#define BDU_ENABLE 0x80 +#define L3G4200D_FS_BIT 6 +#define L3G4200D_FS_MASK (0x3 << L3G4200D_FS_BIT) + +/* multiple byte transfer enable */ +#define MULTIPLE_I2C_TR 0x80 + +/* device status defines */ +#define DEVICE_OFF 0 +#define DEVICE_ON 1 +#define DEVICE_SUSPENDED 2 + +/* + * L3G4200D gyroscope data + * brief structure containing gyroscope values for yaw, pitch and roll in + * signed short + */ + +struct l3g4200d_gyro_values { + short x; /* x-axis angular rate data. */ + short y; /* y-axis angluar rate data. */ + short z; /* z-axis angular rate data. */ +}; + +struct l3g4200d_data { + struct i2c_client *client; + struct mutex lock; + struct l3g4200d_gyro_values data; + struct l3g4200d_gyr_platform_data pdata; + struct regulator *regulator; + unsigned char powermode; + unsigned char odr; + unsigned char range; + int device_status; +}; + +static int l3g4200d_write(struct l3g4200d_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 l3g4200d_read(struct l3g4200d_data *ddata, u8 reg, char *msg) +{ + 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; +} + +static int l3g4200d_readdata(struct l3g4200d_data *ddata) +{ + unsigned char gyro_data[6]; + short data[3]; + int ret; + + ret = i2c_smbus_read_i2c_block_data(ddata->client, + AXISDATA_REG | MULTIPLE_I2C_TR, 6, gyro_data); + if (ret < 0) { + dev_err(&ddata->client->dev, + "i2c_smbus_read_byte_data failed error %d\ + Register AXISDATA_REG\n", ret); + return ret; + } + + data[0] = (short) (((gyro_data[1]) << 8) | gyro_data[0]); + data[1] = (short) (((gyro_data[3]) << 8) | gyro_data[2]); + data[2] = (short) (((gyro_data[5]) << 8) | gyro_data[4]); + + 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 l3g4200d_show_gyrodata(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct platform_device *pdev = to_platform_device(dev); + struct l3g4200d_data *ddata = platform_get_drvdata(pdev); + int ret = 0; + + mutex_lock(&ddata->lock); + + if (ddata->powermode == PM_OFF || + ddata->device_status == DEVICE_SUSPENDED) { + mutex_unlock(&ddata->lock); + return ret; + } + + ret = l3g4200d_readdata(ddata); + + if (ret < 0) { + mutex_unlock(&ddata->lock); + return ret; + } + + mutex_unlock(&ddata->lock); + + return sprintf(buf, "%8x:%8x:%8x\n", ddata->data.x, ddata->data.y, + ddata->data.z); +} + +static ssize_t l3g4200d_show_range(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct platform_device *pdev = to_platform_device(dev); + struct l3g4200d_data *ddata = platform_get_drvdata(pdev); + + return sprintf(buf, "%d\n", ddata->range); +} + +static ssize_t l3g4200d_store_range(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct platform_device *pdev = to_platform_device(dev); + struct l3g4200d_data *ddata = platform_get_drvdata(pdev); + long received_value; + unsigned char value; + int error; + + error = strict_strtol(buf, 0, &received_value); + if (error) + return error; + + /* check if the received range is in valid range */ + if (received_value < FS250 || received_value > FS2000) + return -EINVAL; + + mutex_lock(&ddata->lock); + + if (ddata->powermode == PM_OFF) { + dev_info(&ddata->client->dev, + "The device is switched off, turn it ON using powermode\n"); + mutex_unlock(&ddata->lock); + return count; + } + + /* enable the BDU bit */ + value = BDU_ENABLE; + value |= ((received_value << L3G4200D_FS_BIT) & L3G4200D_FS_MASK); + + ddata->range = received_value; + + error = l3g4200d_write(ddata, CTRL_REG4, value, "CTRL_REG4"); + if (error < 0) { + mutex_unlock(&ddata->lock); + return error; + } + + mutex_unlock(&ddata->lock); + return count; +} + +static ssize_t l3g4200d_show_datarate(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct platform_device *pdev = to_platform_device(dev); + struct l3g4200d_data *ddata = platform_get_drvdata(pdev); + + return sprintf(buf, "%d\n", ddata->odr >> L3G4200D_ODR_BIT); +} + +static ssize_t l3g4200d_store_datarate(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct platform_device *pdev = to_platform_device(dev); + struct l3g4200d_data *ddata = platform_get_drvdata(pdev); + long received_value; + unsigned char value; + int error; + + error = strict_strtol(buf, 0, &received_value); + if (error) + return error; + + /* check if the received output datarate value is in valid range */ + if (received_value < L3G4200D_ODR_MIN_VAL || + received_value > L3G4200D_ODR_MAX_VAL) + return -EINVAL; + + mutex_lock(&ddata->lock); + + if (ddata->powermode == PM_OFF) { + dev_info(&ddata->client->dev, + "The device is switched off, turn it ON using powermode\n"); + mutex_unlock(&ddata->lock); + return count; + } + + /* + * read the current contents of CTRL_REG1 + * retain any bits set other than the odr bits + */ + error = l3g4200d_read(ddata, CTRL_REG1, "CTRL_REG1"); + + if (error < 0) { + mutex_unlock(&ddata->lock); + return error; + } else + value = error; + + value &= ~L3G4200D_ODR_MASK; + value |= ((received_value << L3G4200D_ODR_BIT) & L3G4200D_ODR_MASK); + + ddata->odr = received_value << L3G4200D_ODR_BIT; + + error = l3g4200d_write(ddata, CTRL_REG1, value, "CTRL_REG1"); + if (error < 0) { + mutex_unlock(&ddata->lock); + return error; + } + + mutex_unlock(&ddata->lock); + return count; +} + +static ssize_t l3g4200d_show_powermode(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct platform_device *pdev = to_platform_device(dev); + struct l3g4200d_data *ddata = platform_get_drvdata(pdev); + + return sprintf(buf, "%d\n", ddata->powermode); +} + +static ssize_t l3g4200d_store_powermode(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct platform_device *pdev = to_platform_device(dev); + struct l3g4200d_data *ddata = platform_get_drvdata(pdev); + long received_value; + unsigned char value; + int error; + + error = strict_strtol(buf, 0, &received_value); + if (error) + return error; + + /* check if the received power mode is either 0 or 1 */ + if (received_value < PM_OFF || received_value > PM_ON) + return -EINVAL; + + mutex_lock(&ddata->lock); + + if (ddata->device_status == DEVICE_SUSPENDED && + received_value == PM_OFF) { + ddata->powermode = received_value; + mutex_unlock(&ddata->lock); + return 0; + } + + /* if sent value is same as current value do nothing */ + if (ddata->powermode == received_value) { + mutex_unlock(&ddata->lock); + return 0; + } + + /* turn on the power suppliy if it was turned off previously */ + if (ddata->regulator && ddata->powermode == PM_OFF + && (ddata->device_status == DEVICE_OFF + || ddata->device_status == DEVICE_SUSPENDED)) { + regulator_enable(ddata->regulator); + ddata->device_status = DEVICE_ON; + } + + /* + * read the current contents of CTRL_REG1 + * retain any bits set other than the power bit + */ + error = l3g4200d_read(ddata, CTRL_REG1, "CTRL_REG1"); + + if (error < 0) { + if (ddata->regulator && ddata->device_status == DEVICE_ON) { + regulator_disable(ddata->regulator); + ddata->device_status = DEVICE_OFF; + } + mutex_unlock(&ddata->lock); + return error; + } else + value = error; + + value &= ~L3G4200D_PM_MASK; + value |= ((received_value << L3G4200D_PM_BIT) & L3G4200D_PM_MASK); + + ddata->powermode = received_value; + + error = l3g4200d_write(ddata, CTRL_REG1, value, "CTRL_REG1"); + if (error < 0) { + if (ddata->regulator && ddata->device_status == DEVICE_ON) { + regulator_disable(ddata->regulator); + ddata->device_status = DEVICE_OFF; + } + mutex_unlock(&ddata->lock); + return error; + } + + if (received_value == PM_OFF) { + /* set the other configuration values to defaults */ + ddata->odr = ODR00 | BW00; + ddata->range = FS250; + + /* turn off the power supply */ + if (ddata->regulator && ddata->device_status == DEVICE_ON) { + regulator_disable(ddata->regulator); + ddata->device_status = DEVICE_OFF; + } + } + mutex_unlock(&ddata->lock); + return count; +} + +static DEVICE_ATTR(gyrodata, S_IRUGO, l3g4200d_show_gyrodata, NULL); + +static DEVICE_ATTR(range, S_IRUGO | S_IWUGO, + l3g4200d_show_range, l3g4200d_store_range); + +static DEVICE_ATTR(datarate, S_IRUGO | S_IWUGO, + l3g4200d_show_datarate, l3g4200d_store_datarate); + +static DEVICE_ATTR(powermode, S_IRUGO | S_IWUGO, + l3g4200d_show_powermode, l3g4200d_store_powermode); + +static struct attribute *l3g4200d_attributes[] = { + &dev_attr_gyrodata.attr, + &dev_attr_range.attr, + &dev_attr_datarate.attr, + &dev_attr_powermode.attr, + NULL +}; + +static const struct attribute_group l3g4200d_attr_group = { + .attrs = l3g4200d_attributes, +}; + +static int __devinit l3g4200d_probe(struct i2c_client *client, + const struct i2c_device_id *devid) +{ + int ret = -1; + struct l3g4200d_data *ddata = NULL; + + if (!i2c_check_functionality(client->adapter, + I2C_FUNC_SMBUS_BYTE_DATA | I2C_FUNC_SMBUS_READ_I2C_BLOCK)) + goto exit; + + ddata = kzalloc(sizeof(struct l3g4200d_data), GFP_KERNEL); + if (ddata == NULL) { + ret = -ENOMEM; + goto error_op_failed; + } + + ddata->client = client; + i2c_set_clientdata(client, ddata); + + memcpy(&ddata->pdata, client->dev.platform_data, sizeof(ddata->pdata)); + /* store default values in the data structure */ + ddata->odr = ODR00 | BW00; + ddata->range = FS250; + ddata->powermode = PM_OFF; + ddata->device_status = DEVICE_OFF; + + dev_set_name(&client->dev, ddata->pdata.name_gyr); + + ddata->regulator = regulator_get(&client->dev, "v-gyro"); + if (IS_ERR(ddata->regulator)) { + dev_err(&client->dev, "failed to get regulator\n"); + ret = PTR_ERR(ddata->regulator); + ddata->regulator = NULL; + } + + if (ddata->regulator) { + regulator_enable(ddata->regulator); + ddata->device_status = DEVICE_ON; + } + + ret = l3g4200d_read(ddata, WHO_AM_I, "WHO_AM_I"); + if (ret < 0) + goto exit_free_regulator; + + if (ret == WHOAMI_L3G4200D) + dev_info(&client->dev, "3-Axis Gyroscope device identification: %d\n", ret); + else + dev_info(&client->dev, "Gyroscope identification did not match\n"); + + mutex_init(&ddata->lock); + + ret = sysfs_create_group(&client->dev.kobj, &l3g4200d_attr_group); + if (ret) + goto exit_free_regulator; + + /* + * turn off the supplies until somebody turns on the device + * using l3g4200d_store_powermode + */ + if (ddata->device_status == DEVICE_ON && ddata->regulator) { + regulator_disable(ddata->regulator); + ddata->device_status = DEVICE_OFF; + } + + return ret; + +exit_free_regulator: + if (ddata->device_status == DEVICE_ON && ddata->regulator) { + regulator_disable(ddata->regulator); + regulator_put(ddata->regulator); + ddata->device_status = DEVICE_OFF; + } +error_op_failed: + kfree(ddata); +exit: + dev_err(&client->dev, "probe function failed %x\n", ret); + return ret; +} + +static int __devexit l3g4200d_remove(struct i2c_client *client) +{ + struct l3g4200d_data *ddata; + ddata = i2c_get_clientdata(client); + sysfs_remove_group(&client->dev.kobj, &l3g4200d_attr_group); + + /* safer to turn off the device */ + if (ddata->powermode != PM_OFF) { + l3g4200d_write(ddata, CTRL_REG1, PM_OFF, "CONTROL"); + if (ddata->regulator && ddata->device_status == DEVICE_ON) { + regulator_disable(ddata->regulator); + regulator_put(ddata->regulator); + ddata->device_status = DEVICE_OFF; + } + } + + i2c_set_clientdata(client, NULL); + kfree(ddata); + + return 0; +} + +static int l3g4200d_do_suspend(struct l3g4200d_data *ddata) +{ + int ret; + + mutex_lock(&ddata->lock); + + if (ddata->powermode == PM_OFF) { + mutex_unlock(&ddata->lock); + return 0; + } + + ret = l3g4200d_write(ddata, CTRL_REG1, PM_OFF, "CONTROL"); + + /* turn off the power when suspending the device */ + if (ddata->regulator) + regulator_disable(ddata->regulator); + + ddata->device_status = DEVICE_SUSPENDED; + + mutex_unlock(&ddata->lock); + return ret; +} + +static int l3g4200d_do_resume(struct l3g4200d_data *ddata) +{ + unsigned char range_value; + unsigned char shifted_powermode = (ddata->powermode << L3G4200D_PM_BIT); + unsigned char shifted_odr = (ddata->odr << L3G4200D_ODR_BIT); + unsigned context = ((shifted_powermode | shifted_odr) | ENABLE_ALL_AXES); + int ret = 0; + + mutex_lock(&ddata->lock); + + if (ddata->device_status == DEVICE_ON) + goto fail; + + /* in correct mode, no need to change it */ + if (ddata->powermode == PM_OFF) { + ddata->device_status = DEVICE_OFF; + goto fail; + } else { + ddata->device_status = DEVICE_ON; + } + + /* turn on the power when resuming the device */ + if (ddata->regulator) + regulator_enable(ddata->regulator); + + ret = l3g4200d_write(ddata, CTRL_REG1, context, "CONTROL"); + if (ret < 0) + goto fail; + + range_value = ddata->range; + range_value <<= L3G4200D_FS_BIT; + range_value |= BDU_ENABLE; + + ret = l3g4200d_write(ddata, CTRL_REG4, range_value, "RANGE"); + +fail: + mutex_unlock(&ddata->lock); + return ret; +} + +#ifdef CONFIG_PM +static int l3g4200d_suspend(struct device *dev) +{ + struct l3g4200d_data *ddata; + int ret; + + ddata = dev_get_drvdata(dev); + + ret = l3g4200d_do_suspend(ddata); + + return ret; +} + +static int l3g4200d_resume(struct device *dev) +{ + struct l3g4200d_data *ddata; + int ret; + + ddata = dev_get_drvdata(dev); + + ret = l3g4200d_do_resume(ddata); + if (ret < 0) + dev_err(&ddata->client->dev, + "Error while resuming the device\n"); + + return ret; +} + +static const struct dev_pm_ops l3g4200d_dev_pm_ops = { + .suspend = l3g4200d_suspend, + .resume = l3g4200d_resume, +}; +#endif + +static const struct i2c_device_id l3g4200d_id[] = { + {"l3g4200d", 0 }, + { }, +}; + +static struct i2c_driver l3g4200d_driver = { + .driver = { + .name = "l3g4200d", +#ifdef CONFIG_PM + .pm = &l3g4200d_dev_pm_ops, +#endif + }, + .probe = l3g4200d_probe, + .remove = l3g4200d_remove, + .id_table = l3g4200d_id, +}; + +static int __init l3g4200d_init(void) +{ + return i2c_add_driver(&l3g4200d_driver); +} + +static void __exit l3g4200d_exit(void) +{ + i2c_del_driver(&l3g4200d_driver); +} + +module_init(l3g4200d_init); +module_exit(l3g4200d_exit); + +MODULE_DESCRIPTION("l3g4200d digital gyroscope driver"); +MODULE_AUTHOR("Chethan Krishna N"); +MODULE_LICENSE("GPL"); diff --git a/include/linux/l3g4200d.h b/include/linux/l3g4200d.h new file mode 100644 index 00000000000..28459601e4f --- /dev/null +++ b/include/linux/l3g4200d.h @@ -0,0 +1,27 @@ +/* + * ST L3G4200D 3-Axis Gyroscope header file + * + * Copyright (C) ST-Ericsson SA 2011 + * Author: Chethan Krishna N for ST-Ericsson + * Licence terms: GNU General Public Licence (GPL) version 2 + */ + +#ifndef __L3G4200D_H__ +#define __L3G4200D_H__ + +#ifdef __KERNEL__ +struct l3g4200d_gyr_platform_data { + const char *name_gyr; + + u8 axis_map_x; + u8 axis_map_y; + u8 axis_map_z; + + u8 negative_x; + u8 negative_y; + u8 negative_z; +}; + +#endif /* __KERNEL__ */ + +#endif /* __L3G4200D_H__ */ -- cgit v1.2.3 From 7d1bd90be3854842014fbdff89f1751df9cd5567 Mon Sep 17 00:00:00 2001 From: Philippe Langlais Date: Thu, 28 Apr 2011 11:41:24 +0200 Subject: hwmon: change L3G4200D regulators name for 2.6.38 Signed-off-by: Philippe Langlais --- drivers/hwmon/l3g4200d.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/hwmon/l3g4200d.c b/drivers/hwmon/l3g4200d.c index 718abbb748b..bd78d4b003b 100644 --- a/drivers/hwmon/l3g4200d.c +++ b/drivers/hwmon/l3g4200d.c @@ -439,7 +439,7 @@ static int __devinit l3g4200d_probe(struct i2c_client *client, dev_set_name(&client->dev, ddata->pdata.name_gyr); - ddata->regulator = regulator_get(&client->dev, "v-gyro"); + ddata->regulator = regulator_get(&client->dev, "vdd"); if (IS_ERR(ddata->regulator)) { dev_err(&client->dev, "failed to get regulator\n"); ret = PTR_ERR(ddata->regulator); -- cgit v1.2.3 From ef46524884b91447e5d785ca64a0444f165dbbe1 Mon Sep 17 00:00:00 2001 From: Philippe Langlais Date: Tue, 18 Oct 2011 11:16:07 +0200 Subject: hwmon: add st lsm303dlh driver Signed-off-by: Mian Yousaf Kaukab Signed-off-by: Philippe Langlais --- drivers/hwmon/Kconfig | 23 + drivers/hwmon/Makefile | 1 + drivers/hwmon/lsm303dlh_a.c | 1270 +++++++++++++++++++++++++++++++++++++++++++ drivers/hwmon/lsm303dlh_m.c | 876 +++++++++++++++++++++++++++++ include/linux/lsm303dlh.h | 56 ++ 5 files changed, 2226 insertions(+) create mode 100644 drivers/hwmon/lsm303dlh_a.c create mode 100644 drivers/hwmon/lsm303dlh_m.c create mode 100644 include/linux/lsm303dlh.h diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig index 324f9b084d0..72524d16630 100644 --- a/drivers/hwmon/Kconfig +++ b/drivers/hwmon/Kconfig @@ -687,6 +687,29 @@ config SENSORS_LTC4151 This driver can also be built as a module. If so, the module will be called ltc4151. +config SENSORS_LSM303DLH + tristate "ST LSM303DLH 3-axis accelerometer and 3-axis magnetometer" + depends on I2C + default n + help + This driver provides support for the LSM303DLH chip which includes a + 3-axis accelerometer and a 3-axis magnetometer. + + This driver can also be built as modules. If so, the module for + accelerometer will be called lsm303dlh_a and for magnetometer it will + be called lsm303dlh_m. + + 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_L3G4200D tristate "ST L3G4200D 3-axis gyroscope" depends on I2C diff --git a/drivers/hwmon/Makefile b/drivers/hwmon/Makefile index 6e9aa4dccc4..93773c975d3 100644 --- a/drivers/hwmon/Makefile +++ b/drivers/hwmon/Makefile @@ -84,6 +84,7 @@ obj-$(CONFIG_SENSORS_LM93) += lm93.o obj-$(CONFIG_SENSORS_LM95241) += lm95241.o obj-$(CONFIG_SENSORS_LM95245) += lm95245.o obj-$(CONFIG_SENSORS_LTC4151) += ltc4151.o +obj-$(CONFIG_SENSORS_LSM303DLH) += lsm303dlh_a.o lsm303dlh_m.o obj-$(CONFIG_SENSORS_L3G4200D) += l3g4200d.o obj-$(CONFIG_SENSORS_LTC4215) += ltc4215.o obj-$(CONFIG_SENSORS_LTC4245) += ltc4245.o diff --git a/drivers/hwmon/lsm303dlh_a.c b/drivers/hwmon/lsm303dlh_a.c new file mode 100644 index 00000000000..bbcf1663061 --- /dev/null +++ b/drivers/hwmon/lsm303dlh_a.c @@ -0,0 +1,1270 @@ +/* + * 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 + * Updated:Preetham Rao Kaskurthi + * + * 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 . + */ + +#include +#include +#include +#include +#include +#include + +#ifdef CONFIG_SENSORS_LSM303DLH_INPUT_DEVICE +#include +#include +#include +#endif + +#include +#include + + /* 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) + #define LSM303DLH_A_CR1_AXIS_ENABLE 7 + + /* 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 + + /* Sleep & Wake */ + #define LSM303DLH_A_SLEEPWAKE_DISABLE 0x00 + #define LSM303DLH_A_SLEEPWAKE_ENABLE 0x3 + +/* Multiple byte transfer enable */ +#define MULTIPLE_I2C_TR 0x80 + +/* device status defines */ +#define DEVICE_OFF 0 +#define DEVICE_ON 1 +#define DEVICE_SUSPENDED 2 + +/* Range -2048 to 2047 */ +struct lsm303dlh_a_t { + short x; + short y; + short z; +}; + +/* + * accelerometer local data + */ +struct lsm303dlh_a_data { + struct i2c_client *client; + /* lock for sysfs operations */ + struct mutex lock; + struct lsm303dlh_a_t data; + +#ifdef CONFIG_SENSORS_LSM303DLH_INPUT_DEVICE + struct input_dev *input_dev; + struct input_dev *input_dev2; +#endif + + struct lsm303dlh_platform_data pdata; + struct regulator *regulator; + + unsigned char range; + unsigned char mode; + unsigned char rate; + unsigned char sleep_wake; + int shift_adjust; + + unsigned char interrupt_control; + unsigned int interrupt_channel; + + unsigned char interrupt_configure[2]; + unsigned char interrupt_duration[2]; + unsigned char interrupt_threshold[2]; + int device_status; +}; + +#ifdef CONFIG_HAS_EARLYSUSPEND +static void lsm303dlh_a_early_suspend(struct early_suspend *data); +static void lsm303dlh_a_late_resume(struct early_suspend *data); +#endif + +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; +} + +static int lsm303dlh_a_read(struct lsm303dlh_a_data *ddata, u8 reg, char *msg) +{ + 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; +} + +static int lsm303dlh_a_do_suspend(struct lsm303dlh_a_data *ddata) +{ + int ret; + + mutex_lock(&ddata->lock); + + if (ddata->mode == LSM303DLH_A_MODE_OFF) { + mutex_unlock(&ddata->lock); + return 0; + } + +#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 + + ret = lsm303dlh_a_write(ddata, CTRL_REG1, + LSM303DLH_A_MODE_OFF, "CONTROL"); + + if (ddata->regulator) + regulator_disable(ddata->regulator); + + ddata->device_status = DEVICE_SUSPENDED; + + mutex_unlock(&ddata->lock); + + return ret; +} + +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; + + mutex_lock(&ddata->lock); + + if (ddata->device_status == DEVICE_ON) { + mutex_unlock(&ddata->lock); + return 0; + } + + /* in correct mode, no need to change it */ + if (ddata->mode == LSM303DLH_A_MODE_OFF) { + ddata->device_status = DEVICE_OFF; + mutex_unlock(&ddata->lock); + return 0; + } else + ddata->device_status = DEVICE_ON; + +#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 + + if (ddata->regulator) + regulator_enable(ddata->regulator); + /* BDU should be enabled by default/recommened */ + reg = ddata->range; + reg |= LSM303DLH_A_CR4_BDU_MASK; + context |= LSM303DLH_A_CR1_AXIS_ENABLE; + + 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; + + ret = lsm303dlh_a_write(ddata, INT1_DURATION, + ddata->interrupt_duration[1], "INT1_DURATION"); + + if (ret < 0) + goto fail; + +fail: + mutex_unlock(&ddata->lock); + return ret; +} + + +static int lsm303dlh_a_readdata(struct lsm303dlh_a_data *ddata) +{ + 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; + } + + 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]); + + data[0] >>= ddata->shift_adjust; + data[1] >>= ddata->shift_adjust; + data[2] >>= ddata->shift_adjust; + + /* 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; + + mutex_lock(&ddata->lock); + + if (ddata->mode == LSM303DLH_A_MODE_OFF || + ddata->device_status == DEVICE_SUSPENDED) { + mutex_unlock(&ddata->lock); + return ret; + } + + ret = lsm303dlh_a_readdata(ddata); + + if (ret < 0) { + mutex_unlock(&ddata->lock); + return ret; + } + + mutex_unlock(&ddata->lock); + + return sprintf(buf, "%8x:%8x:%8x\n", ddata->data.x, ddata->data.y, + ddata->data.z); +} + +#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 { + 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 + +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); +} + +static ssize_t lsm303dlh_a_store_interrupt_control(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; + + error = strict_strtoul(buf, 0, &val); + if (error) + return error; + + mutex_lock(&ddata->lock); + + if (ddata->mode == LSM303DLH_A_MODE_OFF) { + dev_info(&ddata->client->dev, + "device is switched off,make it ON using MODE"); + mutex_unlock(&ddata->lock); + return count; + } + + 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; +} + +static ssize_t lsm303dlh_a_show_interrupt_channel(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_channel); +} + +static ssize_t lsm303dlh_a_store_interrupt_channel(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; + + error = strict_strtoul(buf, 0, &val); + if (error) + return error; + + 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; + + error = strict_strtoul(buf, 0, &val); + if (error) + return error; + + mutex_lock(&ddata->lock); + + if (ddata->mode == LSM303DLH_A_MODE_OFF) { + dev_info(&ddata->client->dev, + "device is switched off,make it ON using MODE"); + mutex_unlock(&ddata->lock); + return count; + } + + 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; + } + + mutex_unlock(&ddata->lock); + + return count; +} + +static ssize_t lsm303dlh_a_show_interrupt_duration(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_duration[ddata->interrupt_channel]); +} + +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; + + error = strict_strtoul(buf, 0, &val); + if (error) + return error; + + mutex_lock(&ddata->lock); + + + if (ddata->mode == LSM303DLH_A_MODE_OFF) { + dev_info(&ddata->client->dev, + "device is switched off,make it ON using MODE"); + mutex_unlock(&ddata->lock); + return count; + } + + 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; +} + +static ssize_t lsm303dlh_a_show_interrupt_threshold(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_threshold[ddata->interrupt_channel]); +} + +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; + + error = strict_strtoul(buf, 0, &val); + if (error) + return error; + + mutex_lock(&ddata->lock); + + if (ddata->mode == LSM303DLH_A_MODE_OFF) { + dev_info(&ddata->client->dev, + "device is switched off,make it ON using MODE"); + mutex_unlock(&ddata->lock); + return count; + } + + 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; +} + +static ssize_t lsm303dlh_a_show_range(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->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); + long val; + unsigned long bdu_enabled_val; + int error; + + + error = strict_strtol(buf, 0, &val); + if (error) + return error; + + if (val < LSM303DLH_A_RANGE_2G || val > LSM303DLH_A_RANGE_8G) + return -EINVAL; + + mutex_lock(&ddata->lock); + + if (ddata->mode == LSM303DLH_A_MODE_OFF) { + dev_info(&ddata->client->dev, + "device is switched off,make it ON using MODE"); + mutex_unlock(&ddata->lock); + return count; + } + + 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 -EINVAL; + } + + mutex_unlock(&ddata->lock); + + return count; +} + +static ssize_t lsm303dlh_a_show_mode(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->mode); +} + +static ssize_t lsm303dlh_a_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_a_data *ddata = platform_get_drvdata(pdev); + long val; + unsigned char data; + int error; + + error = strict_strtol(buf, 0, &val); + if (error) + return error; + + mutex_lock(&ddata->lock); + + /* not in correct range */ + + if (val < LSM303DLH_A_MODE_OFF || val > LSM303DLH_A_MODE_LP_10) { + mutex_unlock(&ddata->lock); + return -EINVAL; + } + + if (ddata->device_status == DEVICE_SUSPENDED && + val == LSM303DLH_A_MODE_OFF) { + ddata->mode = val; + mutex_unlock(&ddata->lock); + return 0; + } + + /* if same mode as existing, return */ + if (ddata->mode == val) { + mutex_unlock(&ddata->lock); + return 0; + } + + /* turn on the supplies if already off */ + if (ddata->regulator && ddata->mode == LSM303DLH_A_MODE_OFF + && (ddata->device_status == DEVICE_OFF + || ddata->device_status == DEVICE_SUSPENDED)) { + regulator_enable(ddata->regulator); + ddata->device_status = DEVICE_ON; +#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 + } + + data = lsm303dlh_a_read(ddata, CTRL_REG1, "CTRL_REG1"); + + data &= ~LSM303DLH_A_CR1_PM_MASK; + + 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 && ddata->device_status == DEVICE_ON) { + regulator_disable(ddata->regulator); + ddata->device_status = DEVICE_OFF; + } + mutex_unlock(&ddata->lock); + return error; + } + + 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 && ddata->device_status == DEVICE_ON) { + regulator_disable(ddata->regulator); + ddata->device_status = DEVICE_OFF; + } + } + mutex_unlock(&ddata->lock); + + return count; +} + +static ssize_t lsm303dlh_a_show_rate(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->rate); +} + +static ssize_t lsm303dlh_a_store_rate(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); + long val; + unsigned char data; + int error; + + error = strict_strtol(buf, 0, &val); + if (error) + return error; + + if (val < LSM303DLH_A_RATE_50 || val > LSM303DLH_A_RATE_1000) + return -EINVAL; + + mutex_lock(&ddata->lock); + + if (ddata->mode == LSM303DLH_A_MODE_OFF) { + dev_info(&ddata->client->dev, + "device is switched off,make it ON using MODE"); + mutex_unlock(&ddata->lock); + return count; + } + + 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; + } + + mutex_unlock(&ddata->lock); + + return count; +} + +static ssize_t lsm303dlh_a_show_sleepwake(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->sleep_wake); +} + +static ssize_t lsm303dlh_a_store_sleepwake(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); + 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; + } + + error = strict_strtoul(buf, 0, &val); + if (error) + return error; + + mutex_lock(&ddata->lock); + + ddata->sleep_wake = val; + + error = lsm303dlh_a_write(ddata, CTRL_REG5, ddata->sleep_wake, + "CTRL_REG5"); + if (error < 0) { + mutex_unlock(&ddata->lock); + return error; + } + + mutex_unlock(&ddata->lock); + + return count; +} + +static DEVICE_ATTR(data, S_IRUGO, lsm303dlh_a_show_data, NULL); + +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(sleep_wake, S_IWUGO | S_IRUGO, + lsm303dlh_a_show_sleepwake, lsm303dlh_a_store_sleepwake); + +static DEVICE_ATTR(interrupt_control, S_IWUGO | S_IRUGO, + lsm303dlh_a_show_interrupt_control, + lsm303dlh_a_store_interrupt_control); + +static DEVICE_ATTR(interrupt_channel, S_IWUGO | S_IRUGO, + lsm303dlh_a_show_interrupt_channel, + lsm303dlh_a_store_interrupt_channel); + +static DEVICE_ATTR(interrupt_configure, S_IWUGO | S_IRUGO, + lsm303dlh_a_show_interrupt_configure, + lsm303dlh_a_store_interrupt_configure); + +static DEVICE_ATTR(interrupt_duration, S_IWUGO | S_IRUGO, + lsm303dlh_a_show_interrupt_duration, + lsm303dlh_a_store_interrupt_duration); + +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_sleep_wake.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; + } + + 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->sleep_wake = LSM303DLH_A_SLEEPWAKE_DISABLE; + ddata->shift_adjust = SHIFT_ADJ_2G; + ddata->device_status = DEVICE_OFF; + dev_set_name(&client->dev, ddata->pdata.name_a); + + ddata->regulator = regulator_get(&client->dev, "vdd"); + if (IS_ERR(ddata->regulator)) { + dev_err(&client->dev, "failed to get regulator\n"); + ret = PTR_ERR(ddata->regulator); + ddata->regulator = NULL; + } + + if (ddata->regulator) { + regulator_enable(ddata->regulator); + ddata->device_status = DEVICE_ON; + } + + ret = lsm303dlh_a_read(ddata, WHO_AM_I, "WHO_AM_I"); + if (ret < 0) + goto exit_free_regulator; + + 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 */ + + 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; + } + + ddata->input_dev2 = input_allocate_device(); + if (!ddata->input_dev2) { + ret = -ENOMEM; + dev_err(&client->dev, "Failed to allocate input device\n"); + goto err_input_alloc_failed; + } + + set_bit(EV_ABS, ddata->input_dev->evbit); + set_bit(EV_ABS, ddata->input_dev2->evbit); + + /* x-axis acceleration */ + 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(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(ddata->input_dev, ABS_Z, -32768, 32767, 0, 0); + input_set_abs_params(ddata->input_dev2, ABS_Z, -32768, 32767, 0, 0); + + ddata->input_dev->name = "accelerometer"; + ddata->input_dev2->name = "motion"; + + 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; + } + + 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; + } + + /* 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; + } + + 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; + } + + /* only mode can enable it */ + disable_irq(gpio_to_irq(ddata->pdata.irq_a1)); + disable_irq(gpio_to_irq(ddata->pdata.irq_a2)); + +#endif + +#ifdef CONFIG_HAS_EARLYSUSPEND + ddata->early_suspend.level = + EARLY_SUSPEND_LEVEL_BLANK_SCREEN + 1; + ddata->early_suspend.suspend = lsm303dlh_a_early_suspend; + ddata->early_suspend.resume = lsm303dlh_a_late_resume; + register_early_suspend(&ddata->early_suspend); +#endif + + if (ddata->device_status == DEVICE_ON && ddata->regulator) { + regulator_disable(ddata->regulator); + ddata->device_status = DEVICE_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->device_status == DEVICE_ON && ddata->regulator) { + regulator_disable(ddata->regulator); + regulator_put(ddata->regulator); + ddata->device_status = DEVICE_OFF; + } +err_op_failed: + kfree(ddata); + dev_err(&client->dev, "probe function fails %x", ret); + return ret; +} + +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 && ddata->device_status == DEVICE_ON) { + regulator_disable(ddata->regulator); + regulator_put(ddata->regulator); + ddata->device_status = DEVICE_OFF; + } + } + + i2c_set_clientdata(client, NULL); + kfree(ddata); + + return 0; +} + +#if (!defined(CONFIG_HAS_EARLYSUSPEND) && defined(CONFIG_PM)) +static int lsm303dlh_a_suspend(struct device *dev) +{ + struct lsm303dlh_a_data *ddata; + int ret; + + ddata = dev_get_drvdata(dev); + + ret = lsm303dlh_a_do_suspend(ddata); + + return ret; +} + +static int lsm303dlh_a_resume(struct device *dev) +{ + struct lsm303dlh_a_data *ddata; + int ret; + + ddata = dev_get_drvdata(dev); + + 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, +}; +#else +static void lsm303dlh_a_early_suspend(struct early_suspend *data) +{ + struct lsm303dlh_a_data *ddata = + container_of(data, struct lsm303dlh_a_data, early_suspend); + int ret; + + ret = lsm303dlh_a_do_suspend(ddata); +} + +static void lsm303dlh_a_late_resume(struct early_suspend *data) +{ + struct lsm303dlh_a_data *ddata = + container_of(data, struct lsm303dlh_a_data, early_suspend); + int ret; + + ret = lsm303dlh_a_restore(ddata); + if (ret < 0) + dev_err(&ddata->client->dev, + "lsm303dlh_a late resume failed\n"); +} +#endif /* CONFIG_PM */ + +static const struct i2c_device_id lsm303dlh_a_id[] = { + { "lsm303dlh_a", 0 }, + { }, +}; + +static struct i2c_driver lsm303dlh_a_driver = { + .probe = lsm303dlh_a_probe, + .remove = lsm303dlh_a_remove, + .id_table = lsm303dlh_a_id, + .driver = { + .name = "lsm303dlh_a", + #if (!defined(CONFIG_HAS_EARLYSUSPEND) && defined(CONFIG_PM)) + .pm = &lsm303dlh_a_dev_pm_ops, + #endif + }, +}; + +static int __init lsm303dlh_a_init(void) +{ + return i2c_add_driver(&lsm303dlh_a_driver); +} + +static void __exit lsm303dlh_a_exit(void) +{ + i2c_del_driver(&lsm303dlh_a_driver); +} + +module_init(lsm303dlh_a_init) +module_exit(lsm303dlh_a_exit) + +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 new file mode 100644 index 00000000000..6f40c4b55eb --- /dev/null +++ b/drivers/hwmon/lsm303dlh_m.c @@ -0,0 +1,876 @@ +/* + * 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 + * Updated:Preetham Rao Kaskurthi + * + * 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 . + */ + +#include +#include +#include +#include +#include +#include + +#ifdef CONFIG_SENSORS_LSM303DLH_INPUT_DEVICE +#include +#include +#include +#endif + +#include +#include +#include + +/* lsm303dlh magnetometer registers */ +#define IRA_REG_M 0x0A + +/* Magnetometer registers */ +#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 */ + +/* Output register start address*/ +#define OUT_X_M 0x03 +#define OUT_Y_M 0x05 +#define OUT_Z_M 0x07 + +/* Magnetometer X-Y gain */ +#define XY_GAIN_1_3 1055 /* XY gain at 1.3G */ +#define XY_GAIN_1_9 795 /* XY gain at 1.9G */ +#define XY_GAIN_2_5 635 /* XY gain at 2.5G */ +#define XY_GAIN_4_0 430 /* XY gain at 4.0G */ +#define XY_GAIN_4_7 375 /* XY gain at 4.7G */ +#define XY_GAIN_5_6 320 /* XY gain at 5.6G */ +#define XY_GAIN_8_1 230 /* XY gain at 8.1G */ + +/* Magnetometer Z gain */ +#define Z_GAIN_1_3 950 /* Z gain at 1.3G */ +#define Z_GAIN_1_9 710 /* Z gain at 1.9G */ +#define Z_GAIN_2_5 570 /* Z gain at 2.5G */ +#define Z_GAIN_4_0 385 /* Z gain at 4.0G */ +#define Z_GAIN_4_7 335 /* Z gain at 4.7G */ +#define Z_GAIN_5_6 285 /* Z gain at 5.6G */ +#define Z_GAIN_8_1 205 /* Z gain at 8.1G */ + +/* Control A regsiter. */ +#define LSM303DLH_M_CRA_DO_BIT 2 +#define LSM303DLH_M_CRA_DO_MASK (0x7 << LSM303DLH_M_CRA_DO_BIT) +#define LSM303DLH_M_CRA_MS_BIT 0 +#define LSM303DLH_M_CRA_MS_MASK (0x3 << LSM303DLH_M_CRA_MS_BIT) + +/* Control B regsiter. */ +#define LSM303DLH_M_CRB_GN_BIT 5 +#define LSM303DLH_M_CRB_GN_MASK (0x7 << LSM303DLH_M_CRB_GN_BIT) + +/* Control Mode regsiter. */ +#define LSM303DLH_M_MR_MD_BIT 0 +#define LSM303DLH_M_MR_MD_MASK (0x3 << LSM303DLH_M_MR_MD_BIT) + +/* Control Status regsiter. */ +#define LSM303DLH_M_SR_RDY_BIT 0 +#define LSM303DLH_M_SR_RDY_MASK (0x1 << LSM303DLH_M_SR_RDY_BIT) +#define LSM303DLH_M_SR_LOC_BIT 1 +#define LSM303DLH_M_SR_LCO_MASK (0x1 << LSM303DLH_M_SR_LOC_BIT) +#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 + +/* device status defines */ +#define DEVICE_OFF 0 +#define DEVICE_ON 1 +#define DEVICE_SUSPENDED 2 + +/* + * magnetometer local data + */ +struct lsm303dlh_m_data { + struct i2c_client *client; + /* 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 data[3]; + unsigned char mode; + unsigned char rate; + unsigned char range; + int device_status; +}; + +#ifdef CONFIG_HAS_EARLYSUSPEND +static void lsm303dlh_m_early_suspend(struct early_suspend *data); +static void lsm303dlh_m_late_resume(struct early_suspend *data); +#endif + +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_do_suspend(struct lsm303dlh_m_data *ddata) +{ + int ret; + + mutex_lock(&ddata->lock); + + if (ddata->mode == LSM303DLH_M_MODE_SLEEP) { + mutex_unlock(&ddata->lock); + return 0; + } + +#ifdef CONFIG_SENSORS_LSM303DLH_INPUT_DEVICE + disable_irq(gpio_to_irq(ddata->pdata.irq_m)); +#endif + + ret = lsm303dlh_m_set_mode(ddata, LSM303DLH_M_MODE_SLEEP); + + if (ddata->regulator) + regulator_disable(ddata->regulator); + + ddata->device_status = DEVICE_SUSPENDED; + + mutex_unlock(&ddata->lock); + + return ret; +} + +static int lsm303dlh_m_restore(struct lsm303dlh_m_data *ddata) +{ + int ret = 0; + + mutex_lock(&ddata->lock); + + if (ddata->device_status == DEVICE_ON) { + mutex_unlock(&ddata->lock); + return 0; + } + + /* in correct mode, no need to change it */ + if (ddata->mode == LSM303DLH_M_MODE_SLEEP) { + ddata->device_status = DEVICE_OFF; + mutex_unlock(&ddata->lock); + return 0; + } else + ddata->device_status = DEVICE_ON; + +#ifdef CONFIG_SENSORS_LSM303DLH_INPUT_DEVICE + enable_irq(gpio_to_irq(ddata->pdata.irq_m)); +#endif + + if (ddata->regulator) + regulator_enable(ddata->regulator); + + 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: + mutex_unlock(&ddata->lock); + 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 */ +static ssize_t lsm303dlh_m_store_rate(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 val; + unsigned char data; + int error; + + error = strict_strtoul(buf, 0, &val); + if (error) + return error; + + mutex_lock(&ddata->lock); + if (ddata->mode == LSM303DLH_M_MODE_SLEEP) { + dev_info(&ddata->client->dev, + "device is switched off,make it ON using MODE"); + mutex_unlock(&ddata->lock); + return count; + } + + data = ((val << LSM303DLH_M_CRA_DO_BIT) & LSM303DLH_M_CRA_DO_MASK); + ddata->rate = data; + + error = lsm303dlh_m_write(ddata, CRA_REG_M, data, "SET RATE"); + + if (error < 0) { + mutex_unlock(&ddata->lock); + return error; + } + + mutex_unlock(&ddata->lock); + + return count; +} + +static int lsm303dlh_m_xyz_read(struct lsm303dlh_m_data *ddata) +{ + 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; + + /* 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; +} + +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]); +} + +static ssize_t lsm303dlh_m_values(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); + int ret = 0; + + mutex_lock(&ddata->lock); + + if (ddata->mode == LSM303DLH_M_MODE_SLEEP || + ddata->device_status == DEVICE_SUSPENDED) { + mutex_unlock(&ddata->lock); + return ret; + } + + ret = lsm303dlh_m_xyz_read(ddata); + + if (ret < 0) { + mutex_unlock(&ddata->lock); + return -EINVAL; + } + + mutex_unlock(&ddata->lock); + + /* taking orientation of x,y,z axis into account*/ + + 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]); +} + +static int lsm303dlh_m_set_mode(struct lsm303dlh_m_data *ddata, + unsigned char mode) +{ + int ret; + + mode = (mode << LSM303DLH_M_MR_MD_BIT); + + ret = i2c_smbus_write_byte_data(ddata->client, MR_REG_M, mode); + + 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; + + ret = lsm303dlh_m_xyz_read(ddata); + + if (ret < 0) { + dev_err(&ddata->client->dev, + "reading data of xyz failed error %d\n", ret); + return IRQ_NONE; + } + + /* taking orientation of x,y,z axis into account*/ + + 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); + + return IRQ_HANDLED; + +} +#endif + +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 sprintf(buf, "%d\n", ddata->range >> LSM303DLH_M_CRB_GN_BIT); +} + +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; + + error = strict_strtoul(buf, 0, &range); + + if (error) + return error; + + mutex_lock(&ddata->lock); + + if (ddata->mode == LSM303DLH_M_MODE_SLEEP) { + dev_info(&ddata->client->dev, + "device is switched off,make it ON using MODE"); + mutex_unlock(&ddata->lock); + return count; + } + + switch (range) { + case LSM303DLH_M_RANGE_1_3G: + xy_gain = XY_GAIN_1_3; + z_gain = Z_GAIN_1_3; + break; + case LSM303DLH_M_RANGE_1_9G: + xy_gain = XY_GAIN_1_9; + z_gain = Z_GAIN_1_9; + break; + case LSM303DLH_M_RANGE_2_5G: + xy_gain = XY_GAIN_2_5; + z_gain = Z_GAIN_2_5; + break; + case LSM303DLH_M_RANGE_4_0G: + xy_gain = XY_GAIN_4_0; + z_gain = Z_GAIN_4_0; + break; + case LSM303DLH_M_RANGE_4_7G: + xy_gain = XY_GAIN_4_7; + z_gain = Z_GAIN_4_7; + break; + case LSM303DLH_M_RANGE_5_6G: + xy_gain = XY_GAIN_5_6; + z_gain = Z_GAIN_5_6; + break; + case LSM303DLH_M_RANGE_8_1G: + xy_gain = XY_GAIN_8_1; + z_gain = Z_GAIN_8_1; + break; + default: + return -EINVAL; + } + + 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); + + if (error < 0) + return error; + + return count; +} + +static ssize_t lsm303dlh_m_show_mode(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->mode); +} + +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; + + error = strict_strtoul(buf, 0, &mode); + if (error) + return error; + + mutex_lock(&ddata->lock); + + if (ddata->device_status == DEVICE_SUSPENDED && + mode == LSM303DLH_M_MODE_SLEEP) { + ddata->mode = (mode >> LSM303DLH_M_MR_MD_BIT); + mutex_unlock(&ddata->lock); + return 0; + } + + /* if same mode as existing, return */ + if (ddata->mode == mode) { + mutex_unlock(&ddata->lock); + return 0; + } + + /* turn on the supplies if already off */ + if (ddata->mode == LSM303DLH_M_MODE_SLEEP && ddata->regulator + && (ddata->device_status == DEVICE_OFF + || ddata->device_status == DEVICE_SUSPENDED)) { + regulator_enable(ddata->regulator); + ddata->device_status = DEVICE_ON; + +#ifdef CONFIG_SENSORS_LSM303DLH_INPUT_DEVICE + enable_irq(gpio_to_irq(ddata->pdata.irq_m)); +#endif + } + + error = lsm303dlh_m_set_mode(ddata, mode); + + ddata->mode = (mode >> LSM303DLH_M_MR_MD_BIT); + if (error < 0) { + if (ddata->regulator && ddata->device_status == DEVICE_ON) { + regulator_disable(ddata->regulator); + ddata->device_status = DEVICE_OFF; + } + mutex_unlock(&ddata->lock); + return error; + } + + if (mode == LSM303DLH_M_MODE_SLEEP) { + +#ifdef CONFIG_SENSORS_LSM303DLH_INPUT_DEVICE + disable_irq(gpio_to_irq(ddata->pdata.irq_m)); +#endif + + /* + * 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; + + if (ddata->regulator && ddata->device_status == DEVICE_ON) { + regulator_disable(ddata->regulator); + ddata->device_status = DEVICE_OFF; + } + } + mutex_unlock(&ddata->lock); + + return count; +} + +static DEVICE_ATTR(gain, S_IRUGO, lsm303dlh_m_gain, NULL); + +static DEVICE_ATTR(data, S_IRUGO, lsm303dlh_m_values, NULL); + +static DEVICE_ATTR(mode, S_IWUGO | S_IRUGO, + lsm303dlh_m_show_mode, lsm303dlh_m_store_mode); + +static DEVICE_ATTR(range, S_IWUGO | S_IRUGO, + lsm303dlh_m_show_range, lsm303dlh_m_store_range); + +static DEVICE_ATTR(rate, S_IWUGO | S_IRUGO, + lsm303dlh_m_show_rate, lsm303dlh_m_store_rate); + +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 const struct attribute_group lsm303dlh_m_attr_group = { + .attrs = lsm303dlh_m_attributes, +}; + +static int __devinit lsm303dlh_m_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + 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; + } + + 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_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; + ddata->device_status = DEVICE_OFF; + dev_set_name(&client->dev, ddata->pdata.name_m); + ddata->regulator = regulator_get(&client->dev, "vdd"); + + if (IS_ERR(ddata->regulator)) { + dev_err(&client->dev, "failed to get regulator\n"); + ret = PTR_ERR(ddata->regulator); + ddata->regulator = NULL; + } + + if (ddata->regulator) { + regulator_enable(ddata->regulator); + ddata->device_status = DEVICE_ON; + } + + ret = lsm303dlh_m_read_multi(ddata, IRA_REG_M, 3, version, "IRA_REG_M"); + if (ret < 0) + goto exit_free_regulator; + + dev_info(&client->dev, "Magnetometer, ID : %x:%x:%x", + version[0], version[1], version[2]); + + mutex_init(&ddata->lock); + + ret = sysfs_create_group(&client->dev.kobj, &lsm303dlh_m_attr_group); + if (ret) + goto exit_free_regulator; + +#ifdef CONFIG_SENSORS_LSM303DLH_INPUT_DEVICE + + 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; + } + + set_bit(EV_ABS, ddata->input_dev->evbit); + + /* 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); + + 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; + } + + /* 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; + } + + disable_irq(gpio_to_irq(ddata->pdata.irq_m)); +#endif + +#ifdef CONFIG_HAS_EARLYSUSPEND + ddata->early_suspend.level = + EARLY_SUSPEND_LEVEL_BLANK_SCREEN + 1; + ddata->early_suspend.suspend = lsm303dlh_m_early_suspend; + ddata->early_suspend.resume = lsm303dlh_m_late_resume; + register_early_suspend(&ddata->early_suspend); +#endif + + if (ddata->device_status == DEVICE_ON && ddata->regulator) { + regulator_disable(ddata->regulator); + ddata->device_status = DEVICE_OFF; + } + + return ret; + +#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: + if (ddata->device_status == DEVICE_ON && ddata->regulator) { + regulator_disable(ddata->regulator); + regulator_put(ddata->regulator); + ddata->device_status = DEVICE_OFF; + } +err_op_failed: + dev_err(&client->dev, "lsm303dlh_m_probe failed %x", ret); + kfree(ddata); + return ret; +} + +static int __devexit lsm303dlh_m_remove(struct i2c_client *client) +{ + struct lsm303dlh_m_data *ddata; + + ddata = i2c_get_clientdata(client); + +#ifdef CONFIG_SENSORS_LSM303DLH_INPUT_DEVICE + input_unregister_device(ddata->input_dev); + input_free_device(ddata->input_dev); +#endif + + sysfs_remove_group(&client->dev.kobj, &lsm303dlh_m_attr_group); + + /* safer to make device off */ + if (ddata->mode != LSM303DLH_M_MODE_SLEEP) { + lsm303dlh_m_set_mode(ddata, LSM303DLH_M_MODE_SLEEP); + if (ddata->regulator && ddata->device_status == DEVICE_ON) { + regulator_disable(ddata->regulator); + regulator_put(ddata->regulator); + ddata->device_status = DEVICE_OFF; + } + } + + i2c_set_clientdata(client, NULL); + kfree(ddata); + + return 0; +} + +#if (!defined(CONFIG_HAS_EARLYSUSPEND) && defined(CONFIG_PM)) +static int lsm303dlh_m_suspend(struct device *dev) +{ + struct lsm303dlh_m_data *ddata; + int ret; + + ddata = dev_get_drvdata(dev); + + ret = lsm303dlh_m_do_suspend(ddata); + if (ret < 0) + dev_err(&ddata->client->dev, + "Error while suspending the device"); + + return ret; +} + +static int lsm303dlh_m_resume(struct device *dev) +{ + struct lsm303dlh_m_data *ddata; + int ret; + + ddata = dev_get_drvdata(dev); + + ret = lsm303dlh_m_restore(ddata); + + if (ret < 0) + dev_err(&ddata->client->dev, + "Error while resuming the device"); + + return ret; +} +static const struct dev_pm_ops lsm303dlh_m_dev_pm_ops = { + .suspend = lsm303dlh_m_suspend, + .resume = lsm303dlh_m_resume, +}; +#else +static void lsm303dlh_m_early_suspend(struct early_suspend *data) +{ + struct lsm303dlh_m_data *ddata = + container_of(data, struct lsm303dlh_m_data, early_suspend); + int ret; + + ret = lsm303dlh_m_do_suspend(ddata); + if (ret < 0) + dev_err(&ddata->client->dev, + "Error while suspending the device"); +} + +static void lsm303dlh_m_late_resume(struct early_suspend *data) +{ + struct lsm303dlh_m_data *ddata = + container_of(data, struct lsm303dlh_m_data, early_suspend); + int ret; + + ret = lsm303dlh_m_restore(ddata); + + if (ret < 0) + dev_err(&ddata->client->dev, + "lsm303dlh_m late resume failed\n"); +} +#endif /* CONFIG_PM */ + +static const struct i2c_device_id lsm303dlh_m_id[] = { + { "lsm303dlh_m", 0 }, + { }, +}; + +static struct i2c_driver lsm303dlh_m_driver = { + .probe = lsm303dlh_m_probe, + .remove = lsm303dlh_m_remove, + .id_table = lsm303dlh_m_id, + .driver = { + .name = "lsm303dlh_m", + #if (!defined(CONFIG_HAS_EARLYSUSPEND) && defined(CONFIG_PM)) + .pm = &lsm303dlh_m_dev_pm_ops, + #endif + }, +}; + +static int __init lsm303dlh_m_init(void) +{ + return i2c_add_driver(&lsm303dlh_m_driver); +} + +static void __exit lsm303dlh_m_exit(void) +{ + i2c_del_driver(&lsm303dlh_m_driver); +} + +module_init(lsm303dlh_m_init); +module_exit(lsm303dlh_m_exit); + +MODULE_DESCRIPTION("lSM303DLH 3-Axis Magnetometer Driver"); +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("STMicroelectronics"); diff --git a/include/linux/lsm303dlh.h b/include/linux/lsm303dlh.h new file mode 100644 index 00000000000..d9fd6148198 --- /dev/null +++ b/include/linux/lsm303dlh.h @@ -0,0 +1,56 @@ +/* + * lsm303dlh.h + * ST 3-Axis Accelerometer/Magnetometer header file + * + * 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 + * Updated:Preetham Rao Kaskurthi + * + * 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 . + */ + +#ifndef __LSM303DLH_H__ +#define __LSM303DLH_H__ + +#include + +#ifdef __KERNEL__ +struct lsm303dlh_platform_data { + + /* name of device for regulator */ + + const char *name_a; /* acelerometer name */ + const char *name_m; /* magnetometer name */ + + /* interrupt data */ + u32 irq_a1; /* interrupt line 1 of accelrometer*/ + u32 irq_a2; /* interrupt line 2 of accelrometer*/ + u32 irq_m; /* interrupt line of magnetometer*/ + + /* position of x,y and z axis */ + u8 axis_map_x; /* [0-2] */ + u8 axis_map_y; /* [0-2] */ + u8 axis_map_z; /* [0-2] */ + + /* orientation of x,y and z axis */ + u8 negative_x; /* [0-1] */ + u8 negative_y; /* [0-1] */ + u8 negative_z; /* [0-1] */ +}; +#endif /* __KERNEL__ */ + +#endif /* __LSM303DLH_H__ */ -- cgit v1.2.3 From 47ee99b72214330c23a4928441df62a9d6afdcc3 Mon Sep 17 00:00:00 2001 From: Chethan Krishna N Date: Tue, 8 Mar 2011 15:18:34 +0530 Subject: lsm303dlh: Reboot memory content after suspend/resume It is observed that device screen orientation doesn't behave as expected sometimes after suspend/resume. Rebooting the device's internal registers to ensure good device behavior. ST Ericsson ID : ER327596 Signed-off-by: Chethan Krishna N Change-Id: I6bdecf0959a3497b00d57d907d5a7a7d67b12c32 Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/17814 Reviewed-by: Srinidhi KASAGAR --- drivers/hwmon/lsm303dlh_a.c | 25 ++++++++++++++++++++----- 1 file changed, 20 insertions(+), 5 deletions(-) diff --git a/drivers/hwmon/lsm303dlh_a.c b/drivers/hwmon/lsm303dlh_a.c index bbcf1663061..307a07cb031 100644 --- a/drivers/hwmon/lsm303dlh_a.c +++ b/drivers/hwmon/lsm303dlh_a.c @@ -324,6 +324,8 @@ static int lsm303dlh_a_restore(struct lsm303dlh_a_data *ddata) goto fail; fail: + /* write to the boot bit to reboot memory content */ + lsm303dlh_a_write(ddata, CTRL_REG2, 0x80, "CTRL_REG2"); mutex_unlock(&ddata->lock); return ret; } @@ -757,6 +759,7 @@ static ssize_t lsm303dlh_a_store_mode(struct device *dev, long val; unsigned char data; int error; + bool set_boot_bit = false; error = strict_strtol(buf, 0, &val); if (error) @@ -771,11 +774,15 @@ static ssize_t lsm303dlh_a_store_mode(struct device *dev, return -EINVAL; } - if (ddata->device_status == DEVICE_SUSPENDED && - val == LSM303DLH_A_MODE_OFF) { - ddata->mode = val; - mutex_unlock(&ddata->lock); - return 0; + if (ddata->device_status == DEVICE_SUSPENDED) { + if (val == LSM303DLH_A_MODE_OFF) { + ddata->mode = val; + mutex_unlock(&ddata->lock); + return 0; + } else { + /* device is turning on after suspend, reset memory */ + set_boot_bit = true; + } } /* if same mode as existing, return */ @@ -814,6 +821,14 @@ static ssize_t lsm303dlh_a_store_mode(struct device *dev, return error; } + /* + * Power on request when device is in suspended state + * write to the boot bit in CTRL_REG2 to reboot memory content + * and ensure correct device behavior after it resumes + */ + if (set_boot_bit) + lsm303dlh_a_write(ddata, CTRL_REG2, 0x80, "CTRL_REG2"); + if (val == LSM303DLH_A_MODE_OFF) { #ifdef CONFIG_SENSORS_LSM303DLH_INPUT_DEVICE disable_irq(gpio_to_irq(ddata->pdata.irq_a1)); -- cgit v1.2.3 From 8170dd77845d4eaa27f8a57fec14d806b8bf88a6 Mon Sep 17 00:00:00 2001 From: Chethan Krishna N Date: Tue, 15 Mar 2011 11:15:06 +0530 Subject: lsm303dlh: Check written values for lsm303dlh_a_write calls Check written values for all calls of lsm303dlh_a_write function calls. ST Ericsson ID: ER328752 Change-Id: I88d1bc5d92cebb7c3483b3ba3073d155336c4909 Signed-off-by: Chethan Krishna N Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/18317 Reviewed-by: Srinidhi KASAGAR --- drivers/hwmon/lsm303dlh_a.c | 32 +++++++++++++++++++++++++++----- 1 file changed, 27 insertions(+), 5 deletions(-) diff --git a/drivers/hwmon/lsm303dlh_a.c b/drivers/hwmon/lsm303dlh_a.c index 307a07cb031..6d413cb837f 100644 --- a/drivers/hwmon/lsm303dlh_a.c +++ b/drivers/hwmon/lsm303dlh_a.c @@ -278,6 +278,12 @@ static int lsm303dlh_a_restore(struct lsm303dlh_a_data *ddata) ret = lsm303dlh_a_write(ddata, CTRL_REG4, reg, "CTRL_REG4"); + if (ret < 0) + goto fail; + + /* write to the boot bit to reboot memory content */ + ret = lsm303dlh_a_write(ddata, CTRL_REG2, 0x80, "CTRL_REG2"); + if (ret < 0) goto fail; @@ -324,8 +330,8 @@ static int lsm303dlh_a_restore(struct lsm303dlh_a_data *ddata) goto fail; fail: - /* write to the boot bit to reboot memory content */ - lsm303dlh_a_write(ddata, CTRL_REG2, 0x80, "CTRL_REG2"); + if (ret < 0) + dev_err(&ddata->client->dev, "could not restore the device %d\n", ret); mutex_unlock(&ddata->lock); return ret; } @@ -826,8 +832,17 @@ static ssize_t lsm303dlh_a_store_mode(struct device *dev, * write to the boot bit in CTRL_REG2 to reboot memory content * and ensure correct device behavior after it resumes */ - if (set_boot_bit) - lsm303dlh_a_write(ddata, CTRL_REG2, 0x80, "CTRL_REG2"); + if (set_boot_bit) { + error = lsm303dlh_a_write(ddata, CTRL_REG2, 0x80, "CTRL_REG2"); + if (error < 0) { + if (ddata->regulator && ddata->device_status == DEVICE_ON) { + regulator_disable(ddata->regulator); + ddata->device_status = DEVICE_OFF; + } + mutex_unlock(&ddata->lock); + return error; + } + } if (val == LSM303DLH_A_MODE_OFF) { #ifdef CONFIG_SENSORS_LSM303DLH_INPUT_DEVICE @@ -1168,6 +1183,7 @@ err_op_failed: static int __devexit lsm303dlh_a_remove(struct i2c_client *client) { + int ret; struct lsm303dlh_a_data *ddata; ddata = i2c_get_clientdata(client); @@ -1181,7 +1197,13 @@ static int __devexit lsm303dlh_a_remove(struct i2c_client *client) /* safer to make device off */ if (ddata->mode != LSM303DLH_A_MODE_OFF) { - lsm303dlh_a_write(ddata, CTRL_REG1, 0, "CONTROL"); + ret = lsm303dlh_a_write(ddata, CTRL_REG1, 0, "CONTROL"); + + if (ret < 0) { + dev_err(&client->dev, "could not turn off the device %d", ret); + return ret; + } + if (ddata->regulator && ddata->device_status == DEVICE_ON) { regulator_disable(ddata->regulator); regulator_put(ddata->regulator); -- cgit v1.2.3 From a2283e55fb118961b53483dd9bf411bf42386d13 Mon Sep 17 00:00:00 2001 From: Chethan Krishna N Date: Tue, 12 Apr 2011 16:43:53 +0530 Subject: lsm303dlh/l3g4200d: return count after storing mode corrects behavior while returning from store mode function calls. ST Ericsson ID : AP335036 Signed-off-by: Chethan Krishna N Change-Id: Ifa9a99eb713a51e1fc5b53eaacefacbf1db26d90 Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/20697 Reviewed-by: Chethan Krishna N Tested-by: Chethan Krishna N Reviewed-by: Srinidhi KASAGAR --- drivers/hwmon/l3g4200d.c | 4 ++-- drivers/hwmon/lsm303dlh_a.c | 4 ++-- drivers/hwmon/lsm303dlh_m.c | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/drivers/hwmon/l3g4200d.c b/drivers/hwmon/l3g4200d.c index bd78d4b003b..cb362ab7254 100644 --- a/drivers/hwmon/l3g4200d.c +++ b/drivers/hwmon/l3g4200d.c @@ -325,13 +325,13 @@ static ssize_t l3g4200d_store_powermode(struct device *dev, received_value == PM_OFF) { ddata->powermode = received_value; mutex_unlock(&ddata->lock); - return 0; + return count; } /* if sent value is same as current value do nothing */ if (ddata->powermode == received_value) { mutex_unlock(&ddata->lock); - return 0; + return count; } /* turn on the power suppliy if it was turned off previously */ diff --git a/drivers/hwmon/lsm303dlh_a.c b/drivers/hwmon/lsm303dlh_a.c index 6d413cb837f..7721310be06 100644 --- a/drivers/hwmon/lsm303dlh_a.c +++ b/drivers/hwmon/lsm303dlh_a.c @@ -784,7 +784,7 @@ static ssize_t lsm303dlh_a_store_mode(struct device *dev, if (val == LSM303DLH_A_MODE_OFF) { ddata->mode = val; mutex_unlock(&ddata->lock); - return 0; + return count; } else { /* device is turning on after suspend, reset memory */ set_boot_bit = true; @@ -794,7 +794,7 @@ static ssize_t lsm303dlh_a_store_mode(struct device *dev, /* if same mode as existing, return */ if (ddata->mode == val) { mutex_unlock(&ddata->lock); - return 0; + return count; } /* turn on the supplies if already off */ diff --git a/drivers/hwmon/lsm303dlh_m.c b/drivers/hwmon/lsm303dlh_m.c index 6f40c4b55eb..8352b3d0281 100644 --- a/drivers/hwmon/lsm303dlh_m.c +++ b/drivers/hwmon/lsm303dlh_m.c @@ -535,13 +535,13 @@ static ssize_t lsm303dlh_m_store_mode(struct device *dev, mode == LSM303DLH_M_MODE_SLEEP) { ddata->mode = (mode >> LSM303DLH_M_MR_MD_BIT); mutex_unlock(&ddata->lock); - return 0; + return count; } /* if same mode as existing, return */ if (ddata->mode == mode) { mutex_unlock(&ddata->lock); - return 0; + return count; } /* turn on the supplies if already off */ -- cgit v1.2.3 From a2ea60e5ec438a66518a02f1b91703b815ae1579 Mon Sep 17 00:00:00 2001 From: Philippe Langlais Date: Thu, 20 Oct 2011 10:31:40 +0200 Subject: lsm303dlh: add DocBook documentation Adding DocBook documentation for lsm303dlh accelerometer and magnetometer drivers. ST-Ericsson ID: 277198 Change-Id: Idfca43aa6ffaa39b7f73d0135dc6d2b01a0a44c6 Signed-off-by: Chethan Krishna N Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/23784 Reviewed-by: Srinidhi KASAGAR Conflicts: Documentation/DocBook/Makefile --- Documentation/DocBook/lsm303dlh.tmpl | 90 ++++++++++++++++++++++++++++++++++++ drivers/hwmon/lsm303dlh_a.c | 23 ++++++++- drivers/hwmon/lsm303dlh_m.c | 16 ++++++- include/linux/lsm303dlh.h | 45 ++++++++++-------- 4 files changed, 150 insertions(+), 24 deletions(-) create mode 100644 Documentation/DocBook/lsm303dlh.tmpl diff --git a/Documentation/DocBook/lsm303dlh.tmpl b/Documentation/DocBook/lsm303dlh.tmpl new file mode 100644 index 00000000000..3b1c6afa65f --- /dev/null +++ b/Documentation/DocBook/lsm303dlh.tmpl @@ -0,0 +1,90 @@ + + + + + + LSM303DLH Accelerometer and Magnetometer + + + + Chethan Krishna + N + +
+ chethan.krishna@stericsson.com +
+
+
+
+ + + 2010 + ST-Ericsson + + + + + Linux standard functions + + + + + + License terms: GNU General Public License (GPL) version 2. + + + +
+ + + + + Introduction + + This documentation describes the accelerometer and magnetometer drivers for lsm303dlh sensor chip. + + + + + Known Bugs And Assumptions + + + + None + + + None. + + + + + + + + + Public Functions Provided + + This accelerometer/magnetometer drivers don't export any functions. + + + + + Structures + + This chapter contains the autogenerated documentation of the structures which are + used in the accelerometer/magnetometer drivers. + +!Iinclude/linux/lsm303dlh.h + + + + Internal Functions Provided + + This chapter contains the autogenerated documentation of the internal functions. + +!Idrivers/hwmon/lsm303dlh_a.c +!Idrivers/hwmon/lsm303dlh_m.c + + +
diff --git a/drivers/hwmon/lsm303dlh_a.c b/drivers/hwmon/lsm303dlh_a.c index 7721310be06..1436e77a0d6 100644 --- a/drivers/hwmon/lsm303dlh_a.c +++ b/drivers/hwmon/lsm303dlh_a.c @@ -149,8 +149,27 @@ struct lsm303dlh_a_t { short z; }; -/* - * accelerometer local data +/** + * struct lsm303dlh_a_data - data structure used by lsm303dlh_a driver + * @client: i2c client + * @lock: mutex lock for sysfs operations + * @data: lsm303dlh_a_t struct containing x, y and z values + * @input_dev: input device + * @input_dev2: input device + * @pdata: lsm303dlh platform data + * @regulator: regulator + * @range: current range value of accelerometer + * @mode: current mode of operation + * @rate: current sampling rate + * @sleep_wake: sleep wake setting + * @shift_adjust: current shift adjust value set according to range + * @interrupt_control: interrupt control settings + * @interrupt_channel: interrupt channel 0 or 1 + * @interrupt_configure: interrupt configurations for two channels + * @interrupt_duration: interrupt duration for two channels + * @interrupt_threshold: interrupt threshold for two channels + * @early_suspend: early suspend structure + * @device_status: device is ON, OFF or SUSPENDED */ struct lsm303dlh_a_data { struct i2c_client *client; diff --git a/drivers/hwmon/lsm303dlh_m.c b/drivers/hwmon/lsm303dlh_m.c index 8352b3d0281..42ef63157ae 100644 --- a/drivers/hwmon/lsm303dlh_m.c +++ b/drivers/hwmon/lsm303dlh_m.c @@ -125,8 +125,20 @@ #define DEVICE_ON 1 #define DEVICE_SUSPENDED 2 -/* - * magnetometer local data +/** + * struct lsm303dlh_m_data - data structure used by lsm303dlh_m driver + * @client: i2c client + * @lock: mutex lock for sysfs operations + * @input_dev: input device + * @regulator: regulator + * @pdata: lsm303dlh platform data + * @gain: x, y and z axes gain + * @data: Magnetic field values of x, y and z axes + * @mode: current mode of operation + * @rate: current sampling rate + * @range: current range value of magnetometer + * @early_suspend: early suspend structure + * @device_status: device is ON, OFF or SUSPENDED */ struct lsm303dlh_m_data { struct i2c_client *client; diff --git a/include/linux/lsm303dlh.h b/include/linux/lsm303dlh.h index d9fd6148198..ad369b1fbd5 100644 --- a/include/linux/lsm303dlh.h +++ b/include/linux/lsm303dlh.h @@ -29,27 +29,32 @@ #include #ifdef __KERNEL__ +/** + * struct lsm303dlh_platform_data - platform datastructure for lsm303dlh + * @name_a: accelerometer name + * @name_m: magnetometer name + * @irq_a1: interrupt line 1 of accelerometer + * @irq_a2: interrupt line 2 of accelerometer + * @irq_m: interrupt line of magnetometer + * @axis_map_x: x axis position on the hardware, 0 1 or 2 + * @axis_map_y: y axis position on the hardware, 0 1 or 2 + * @axis_map_z: z axis position on the hardware, 0 1 or 2 + * @negative_x: x axis is orientation, 0 or 1 + * @negative_y: y axis is orientation, 0 or 1 + * @negative_z: z axis is orientation, 0 or 1 + */ struct lsm303dlh_platform_data { - - /* name of device for regulator */ - - const char *name_a; /* acelerometer name */ - const char *name_m; /* magnetometer name */ - - /* interrupt data */ - u32 irq_a1; /* interrupt line 1 of accelrometer*/ - u32 irq_a2; /* interrupt line 2 of accelrometer*/ - u32 irq_m; /* interrupt line of magnetometer*/ - - /* position of x,y and z axis */ - u8 axis_map_x; /* [0-2] */ - u8 axis_map_y; /* [0-2] */ - u8 axis_map_z; /* [0-2] */ - - /* orientation of x,y and z axis */ - u8 negative_x; /* [0-1] */ - u8 negative_y; /* [0-1] */ - u8 negative_z; /* [0-1] */ + const char *name_a; + const char *name_m; + u32 irq_a1; + u32 irq_a2; + u32 irq_m; + u8 axis_map_x; + u8 axis_map_y; + u8 axis_map_z; + u8 negative_x; + u8 negative_y; + u8 negative_z; }; #endif /* __KERNEL__ */ -- cgit v1.2.3 From 84b47ea1b23440dbcc9fcc824812d882b093e64f Mon Sep 17 00:00:00 2001 From: Jonas Aaberg Date: Tue, 24 May 2011 11:45:00 +0200 Subject: hwmon: lsm303dlh_m: Fix ifdefs ST-Ericsson Linux next: Not tested, ask SSM for ER ST-Ericsson ID: - ST-Ericsson FOSS-OUT ID: Trivial Change-Id: I085ff9f7e54491b6dde56c956dada8ebd6c7d8a1 Signed-off-by: Jonas Aaberg Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/23698 --- drivers/hwmon/lsm303dlh_m.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/drivers/hwmon/lsm303dlh_m.c b/drivers/hwmon/lsm303dlh_m.c index 42ef63157ae..5d35dc2208d 100644 --- a/drivers/hwmon/lsm303dlh_m.c +++ b/drivers/hwmon/lsm303dlh_m.c @@ -177,6 +177,7 @@ static int lsm303dlh_m_write(struct lsm303dlh_m_data *ddata, return ret; } +#if defined(CONFIG_HAS_EARLYSUSPEND) || defined(CONFIG_PM) static int lsm303dlh_m_do_suspend(struct lsm303dlh_m_data *ddata) { int ret; @@ -249,6 +250,7 @@ fail: mutex_unlock(&ddata->lock); return ret; } +#endif static int lsm303dlh_m_read_multi(struct lsm303dlh_m_data *ddata, u8 reg, u8 count, u8 *val, char *msg) @@ -791,7 +793,8 @@ static int __devexit lsm303dlh_m_remove(struct i2c_client *client) return 0; } -#if (!defined(CONFIG_HAS_EARLYSUSPEND) && defined(CONFIG_PM)) +#ifndef CONFIG_HAS_EARLYSUSPEND +#ifdef CONFIG_PM static int lsm303dlh_m_suspend(struct device *dev) { struct lsm303dlh_m_data *ddata; @@ -826,6 +829,7 @@ static const struct dev_pm_ops lsm303dlh_m_dev_pm_ops = { .suspend = lsm303dlh_m_suspend, .resume = lsm303dlh_m_resume, }; +#endif #else static void lsm303dlh_m_early_suspend(struct early_suspend *data) { -- cgit v1.2.3 From 413c02ff697be07b5ac955c780d870e2f69c9250 Mon Sep 17 00:00:00 2001 From: Jonas Aaberg Date: Tue, 24 May 2011 11:44:33 +0200 Subject: hwmon: lsm303dlh_a: Fix ifdefs ST-Ericsson Linux next: Not tested, ask SSM for ER ST-Ericsson ID: - ST-Ericsson FOSS-OUT ID: Trivial Change-Id: Ia2cdb2cde8f0baffe5b8f5847754780068153d9e Signed-off-by: Jonas Aaberg Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/23697 --- drivers/hwmon/lsm303dlh_a.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/drivers/hwmon/lsm303dlh_a.c b/drivers/hwmon/lsm303dlh_a.c index 1436e77a0d6..678f35a7730 100644 --- a/drivers/hwmon/lsm303dlh_a.c +++ b/drivers/hwmon/lsm303dlh_a.c @@ -226,6 +226,7 @@ static int lsm303dlh_a_read(struct lsm303dlh_a_data *ddata, u8 reg, char *msg) return ret; } +#if defined(CONFIG_HAS_EARLYSUSPEND) || defined(CONFIG_PM) static int lsm303dlh_a_do_suspend(struct lsm303dlh_a_data *ddata) { int ret; @@ -354,7 +355,7 @@ fail: mutex_unlock(&ddata->lock); return ret; } - +#endif static int lsm303dlh_a_readdata(struct lsm303dlh_a_data *ddata) { @@ -1236,7 +1237,8 @@ static int __devexit lsm303dlh_a_remove(struct i2c_client *client) return 0; } -#if (!defined(CONFIG_HAS_EARLYSUSPEND) && defined(CONFIG_PM)) +#ifndef CONFIG_HAS_EARLYSUSPEND +#ifdef CONFIG_PM static int lsm303dlh_a_suspend(struct device *dev) { struct lsm303dlh_a_data *ddata; @@ -1268,6 +1270,7 @@ static const struct dev_pm_ops lsm303dlh_a_dev_pm_ops = { .suspend = lsm303dlh_a_suspend, .resume = lsm303dlh_a_resume, }; +#endif #else static void lsm303dlh_a_early_suspend(struct early_suspend *data) { -- cgit v1.2.3 From 7455275d1ce7591c96d4b068f833bce428c735f6 Mon Sep 17 00:00:00 2001 From: Jonas Aaberg Date: Tue, 24 May 2011 11:43:09 +0200 Subject: hwmon: l3g4200d: Fix ifdefs ST-Ericsson Linux next: Not tested, ask SSM for ER ST-Ericsson ID: - ST-Ericsson FOSS-OUT ID: Trivial Change-Id: Ie228d5ac6533ff74d103a79ef379137428e92652 Signed-off-by: Jonas Aaberg Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/23696 --- drivers/hwmon/l3g4200d.c | 52 +++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 51 insertions(+), 1 deletion(-) diff --git a/drivers/hwmon/l3g4200d.c b/drivers/hwmon/l3g4200d.c index cb362ab7254..39a4e32ee2b 100644 --- a/drivers/hwmon/l3g4200d.c +++ b/drivers/hwmon/l3g4200d.c @@ -16,6 +16,10 @@ #include #include +#ifdef CONFIG_HAS_EARLYSUSPEND +#include +#endif + /* l3g4200d gyroscope registers */ #define WHO_AM_I 0x0F @@ -85,12 +89,20 @@ struct l3g4200d_data { struct l3g4200d_gyro_values data; struct l3g4200d_gyr_platform_data pdata; struct regulator *regulator; +#ifdef CONFIG_HAS_EARLYSUSPEND + struct early_suspend early_suspend; +#endif unsigned char powermode; unsigned char odr; unsigned char range; int device_status; }; +#ifdef CONFIG_HAS_EARLYSUSPEND +static void l3g4200d_early_suspend(struct early_suspend *ddata); +static void l3g4200d_late_resume(struct early_suspend *ddata); +#endif + static int l3g4200d_write(struct l3g4200d_data *ddata, u8 reg, u8 val, char *msg) { @@ -465,6 +477,13 @@ static int __devinit l3g4200d_probe(struct i2c_client *client, ret = sysfs_create_group(&client->dev.kobj, &l3g4200d_attr_group); if (ret) goto exit_free_regulator; +#ifdef CONFIG_HAS_EARLYSUSPEND + ddata->early_suspend.level = + EARLY_SUSPEND_LEVEL_BLANK_SCREEN + 1; + ddata->early_suspend.suspend = l3g4200d_early_suspend; + ddata->early_suspend.resume = l3g4200d_late_resume; + register_early_suspend(&ddata->early_suspend); +#endif /* * turn off the supplies until somebody turns on the device @@ -511,6 +530,7 @@ static int __devexit l3g4200d_remove(struct i2c_client *client) return 0; } +#if defined(CONFIG_HAS_EARLYSUSPEND) || defined(CONFIG_PM) static int l3g4200d_do_suspend(struct l3g4200d_data *ddata) { @@ -574,7 +594,9 @@ fail: mutex_unlock(&ddata->lock); return ret; } +#endif +#ifndef CONFIG_HAS_EARLYSUSPEND #ifdef CONFIG_PM static int l3g4200d_suspend(struct device *dev) { @@ -584,6 +606,9 @@ static int l3g4200d_suspend(struct device *dev) ddata = dev_get_drvdata(dev); ret = l3g4200d_do_suspend(ddata); + if (ret < 0) + dev_err(&ddata->client->dev, + "Error while suspending the device\n"); return ret; } @@ -608,6 +633,31 @@ static const struct dev_pm_ops l3g4200d_dev_pm_ops = { .resume = l3g4200d_resume, }; #endif +#else +static void l3g4200d_early_suspend(struct early_suspend *data) +{ + struct l3g4200d_data *ddata = + container_of(data, struct l3g4200d_data, early_suspend); + int ret; + + ret = l3g4200d_do_suspend(ddata); + if (ret < 0) + dev_err(&ddata->client->dev, + "Error while suspending the device\n"); +} + +static void l3g4200d_late_resume(struct early_suspend *data) +{ + struct l3g4200d_data *ddata = + container_of(data, struct l3g4200d_data, early_suspend); + int ret; + + ret = l3g4200d_do_resume(ddata); + if (ret < 0) + dev_err(&ddata->client->dev, + "Error while resuming the device\n"); +} +#endif static const struct i2c_device_id l3g4200d_id[] = { {"l3g4200d", 0 }, @@ -617,7 +667,7 @@ static const struct i2c_device_id l3g4200d_id[] = { static struct i2c_driver l3g4200d_driver = { .driver = { .name = "l3g4200d", -#ifdef CONFIG_PM +#if (!defined(CONFIG_HAS_EARLYSUSPEND) && defined(CONFIG_PM)) .pm = &l3g4200d_dev_pm_ops, #endif }, -- cgit v1.2.3 From 185839d15a77d70bbbf539e6afa42bc1ce629eee Mon Sep 17 00:00:00 2001 From: Chethan Krishna N Date: Wed, 25 May 2011 19:36:32 +0530 Subject: lsm303dlh: adding functions under CONFIG_SENSORS_INPUT_DEVICE Functions that are called only when CONFIG_SENSORS_INPUT_DEVICE is enabled are put under the same switch Change-Id: If652e8ab2ca0d3482d24145bb5d908f5dbacf965 Signed-off-by: Chethan Krishna N Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/23882 Reviewed-by: Srinidhi KASAGAR --- drivers/hwmon/lsm303dlh_a.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/drivers/hwmon/lsm303dlh_a.c b/drivers/hwmon/lsm303dlh_a.c index 678f35a7730..af0ceee2e45 100644 --- a/drivers/hwmon/lsm303dlh_a.c +++ b/drivers/hwmon/lsm303dlh_a.c @@ -307,6 +307,7 @@ static int lsm303dlh_a_restore(struct lsm303dlh_a_data *ddata) if (ret < 0) goto fail; +#ifdef CONFIG_SENSORS_LSM303DLH_INPUT_DEVICE ret = lsm303dlh_a_write(ddata, CTRL_REG3, ddata->interrupt_control, "CTRL_REG3"); @@ -348,6 +349,7 @@ static int lsm303dlh_a_restore(struct lsm303dlh_a_data *ddata) if (ret < 0) goto fail; +#endif fail: if (ret < 0) @@ -465,7 +467,6 @@ static irqreturn_t lsm303dlh_a_gpio_irq(int irq, void *device_data) return IRQ_HANDLED; } -#endif static ssize_t lsm303dlh_a_show_interrupt_control(struct device *dev, struct device_attribute *attr, @@ -692,6 +693,7 @@ static ssize_t lsm303dlh_a_store_interrupt_threshold(struct device *dev, return count; } +#endif static ssize_t lsm303dlh_a_show_range(struct device *dev, struct device_attribute *attr, @@ -1002,6 +1004,7 @@ static DEVICE_ATTR(rate, S_IWUGO | S_IRUGO, static DEVICE_ATTR(sleep_wake, S_IWUGO | S_IRUGO, lsm303dlh_a_show_sleepwake, lsm303dlh_a_store_sleepwake); +#ifdef CONFIG_SENSORS_LSM303DLH_INPUT_DEVICE static DEVICE_ATTR(interrupt_control, S_IWUGO | S_IRUGO, lsm303dlh_a_show_interrupt_control, lsm303dlh_a_store_interrupt_control); @@ -1021,6 +1024,7 @@ static DEVICE_ATTR(interrupt_duration, S_IWUGO | S_IRUGO, static DEVICE_ATTR(interrupt_threshold, S_IWUGO | S_IRUGO, lsm303dlh_a_show_interrupt_threshold, lsm303dlh_a_store_interrupt_threshold); +#endif static struct attribute *lsm303dlh_a_attributes[] = { &dev_attr_data.attr, @@ -1028,11 +1032,13 @@ static struct attribute *lsm303dlh_a_attributes[] = { &dev_attr_mode.attr, &dev_attr_rate.attr, &dev_attr_sleep_wake.attr, +#ifdef CONFIG_SENSORS_LSM303DLH_INPUT_DEVICE &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, +#endif NULL }; -- cgit v1.2.3 From cda2b57dc46fbab546d594a2bae0d58c280bc36d Mon Sep 17 00:00:00 2001 From: Chethan Krishna N Date: Thu, 25 Aug 2011 12:15:25 +0530 Subject: drivers:hwmon: add support for lsm303dlhc sensor Adding support for lsm303dlhc acclerometer and magnetometer sensor chip. ST-Ericsson ID: 317051 ST-Ericsson FOSS-OUT ID: NA ST-Ericsson Linux next: NA Change-Id: If55ac20c2665b44d3af45d7af0f2e3e16570d8a7 Signed-off-by: Chethan Krishna N Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/26993 Reviewed-by: QATEST Reviewed-by: Srinidhi KASAGAR --- drivers/hwmon/Kconfig | 14 + drivers/hwmon/Makefile | 1 + drivers/hwmon/lsm303dlh_a.c | 15 + drivers/hwmon/lsm303dlh_m.c | 4 + drivers/hwmon/lsm303dlhc_a.c | 646 +++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 680 insertions(+) create mode 100644 drivers/hwmon/lsm303dlhc_a.c diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig index 72524d16630..cd1468b1b3a 100644 --- a/drivers/hwmon/Kconfig +++ b/drivers/hwmon/Kconfig @@ -710,6 +710,20 @@ config SENSORS_LSM303DLH_INPUT_DEVICE interrupts, need to be enabled only when input device support is required. +config SENSORS_LSM303DLHC + tristate "ST LSM303DLHC 3-axis accelerometer and 3-axis magnetometer" + depends on I2C + default n + help + This driver provides support for the LSM303DLHC chip which includes a + 3-axis accelerometer and a 3-axis magnetometer. + + This driver can also be built as modules. If so, the module for + accelerometer will be called lsm303dlhc_a and for magnetometer it will + be called lsm303dlh_m. + + Say Y here if you have a device containing lsm303dlhc chip. + config SENSORS_L3G4200D tristate "ST L3G4200D 3-axis gyroscope" depends on I2C diff --git a/drivers/hwmon/Makefile b/drivers/hwmon/Makefile index 93773c975d3..27eb2a571f8 100644 --- a/drivers/hwmon/Makefile +++ b/drivers/hwmon/Makefile @@ -85,6 +85,7 @@ obj-$(CONFIG_SENSORS_LM95241) += lm95241.o obj-$(CONFIG_SENSORS_LM95245) += lm95245.o obj-$(CONFIG_SENSORS_LTC4151) += ltc4151.o obj-$(CONFIG_SENSORS_LSM303DLH) += lsm303dlh_a.o lsm303dlh_m.o +obj-$(CONFIG_SENSORS_LSM303DLHC) += lsm303dlhc_a.o lsm303dlh_m.o obj-$(CONFIG_SENSORS_L3G4200D) += l3g4200d.o obj-$(CONFIG_SENSORS_LTC4215) += ltc4215.o obj-$(CONFIG_SENSORS_LTC4245) += ltc4245.o diff --git a/drivers/hwmon/lsm303dlh_a.c b/drivers/hwmon/lsm303dlh_a.c index af0ceee2e45..d3c30bc49e0 100644 --- a/drivers/hwmon/lsm303dlh_a.c +++ b/drivers/hwmon/lsm303dlh_a.c @@ -198,6 +198,7 @@ struct lsm303dlh_a_data { unsigned char interrupt_duration[2]; unsigned char interrupt_threshold[2]; int device_status; + int id; }; #ifdef CONFIG_HAS_EARLYSUSPEND @@ -990,6 +991,18 @@ static ssize_t lsm303dlh_a_store_sleepwake(struct device *dev, return count; } +static ssize_t lsm303dlh_a_show_id(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->id); +} + +static DEVICE_ATTR(id, S_IRUGO, lsm303dlh_a_show_id, NULL); + static DEVICE_ATTR(data, S_IRUGO, lsm303dlh_a_show_data, NULL); static DEVICE_ATTR(range, S_IWUGO | S_IRUGO, @@ -1027,6 +1040,7 @@ static DEVICE_ATTR(interrupt_threshold, S_IWUGO | S_IRUGO, #endif static struct attribute *lsm303dlh_a_attributes[] = { + &dev_attr_id.attr, &dev_attr_data.attr, &dev_attr_range.attr, &dev_attr_mode.attr, @@ -1089,6 +1103,7 @@ static int __devinit lsm303dlh_a_probe(struct i2c_client *client, dev_info(&client->dev, "3-Axis Accelerometer, ID : %d\n", ret); + ddata->id = ret; mutex_init(&ddata->lock); diff --git a/drivers/hwmon/lsm303dlh_m.c b/drivers/hwmon/lsm303dlh_m.c index 5d35dc2208d..254f44d40c6 100644 --- a/drivers/hwmon/lsm303dlh_m.c +++ b/drivers/hwmon/lsm303dlh_m.c @@ -117,6 +117,10 @@ #define LSM303DLH_M_RATE_30_00 0x05 #define LSM303DLH_M_RATE_75_00 0x06 +#ifdef CONFIG_SENSORS_LSM303DLHC +#define LSM303DLH_M_RATE_220_00 0x07 +#endif + /* Multiple byte transfer enable */ #define MULTIPLE_I2C_TR 0x80 diff --git a/drivers/hwmon/lsm303dlhc_a.c b/drivers/hwmon/lsm303dlhc_a.c new file mode 100644 index 00000000000..2da79affdd1 --- /dev/null +++ b/drivers/hwmon/lsm303dlhc_a.c @@ -0,0 +1,646 @@ +/* + * ST LSM303DLHC 3-Axis Accelerometer Driver + * + * Copyright (C) ST-Ericsson SA 2011 + * Author: Chethan Krishna N for ST-Ericsson + * Licence terms: GNU General Public Licence (GPL) version 2 + */ + +#include +#include +#include +#include +#include +#include + +#include +#include + +#define WHO_AM_I 0x0F + +/* lsm303dlhc accelerometer registers */ +#define CTRL_REG1 0x20 +#define CTRL_REG2 0x21 +#define CTRL_REG3 0x22 +#define CTRL_REG4 0x23 +#define CTRL_REG5 0x24 +#define CTRL_REG6 0x25 + +/* lsm303dlhc accelerometer defines */ +#define LSM303DLHC_A_MODE_OFF 0x00 +#define LSM303DLHC_A_MODE_ON 0x04 +#define LSM303DLHC_A_MODE_MAX 0x09 +#define LSM303DLHC_A_CR1_MODE_BIT 4 +#define LSM303DLHC_A_CR1_MODE_MASK (0xF << LSM303DLHC_A_CR1_MODE_BIT) + #define LSM303DLHC_A_CR1_AXIS_ENABLE 7 + +/* Range */ +#define LSM303DLHC_A_RANGE_2G 0x00 +#define LSM303DLHC_A_RANGE_4G 0x01 +#define LSM303DLHC_A_RANGE_8G 0x02 +#define LSM303DLHC_A_RANGE_16G 0x03 +#define LSM303DLHC_A_CR4_FS_BIT 4 + +/* Sensitivity adjustment */ +#define SHIFT_ADJ_2G 4 /* 1/16*/ +#define SHIFT_ADJ_4G 3 /* 2/16*/ +#define SHIFT_ADJ_8G 2 /* ~3.9/16*/ +#define SHIFT_ADJ_16G 1 /* ~3.9/16*/ + +#define AXISDATA_REG 0x29 /* axis data */ + +/* lsm303dlh magnetometer registers */ +#define IRA_REG_M 0x0A + +/* multiple byte transfer enable */ +#define MULTIPLE_I2C_TR 0x80 + +/* device status defines */ +#define DEVICE_OFF 0 +#define DEVICE_ON 1 +#define DEVICE_SUSPENDED 2 + +struct lsm303dlhc_a_t { + short x; + short y; + short z; +}; + +/** + * struct lsm303dlhc_a_data - data structure used by lsm303dlhc_a driver + * @client: i2c client + * @lock: mutex lock for sysfs operations + * @data: lsm303dlhc_a_t struct containing x, y and z values + * @pdata: lsm303dlh platform data + * @regulator: regulator + * @range: current range value of accelerometer + * @mode: current mode of operation + * @rate: current sampling rate + * @shift_adjust: current shift adjust value set according to range + * @device_status: device is ON, OFF or SUSPENDED + * @id: accelerometer device id + */ +struct lsm303dlhc_a_data { + struct i2c_client *client; + /* lock for sysfs operations */ + struct mutex lock; + struct lsm303dlhc_a_t data; + struct lsm303dlh_platform_data pdata; + struct regulator *regulator; + unsigned char range; + unsigned char mode; + unsigned char rate; + int shift_adjust; + int device_status; + int id; +}; + +static int lsm303dlhc_a_write(struct lsm303dlhc_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; +} + +static int lsm303dlhc_a_read(struct lsm303dlhc_a_data *ddata, u8 reg, char *msg) +{ + 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; +} + +#ifdef (CONFIG_PM) +static int lsm303dlhc_a_do_suspend(struct lsm303dlhc_a_data *ddata) +{ + int ret; + + mutex_lock(&ddata->lock); + + if (ddata->mode == LSM303DLHC_A_MODE_OFF) { + ret = 0; + goto exit; + } + + ret = lsm303dlhc_a_write(ddata, CTRL_REG1, + LSM303DLHC_A_MODE_OFF, "CONTROL"); + + if (ddata->regulator) + regulator_disable(ddata->regulator); + + ddata->device_status = DEVICE_SUSPENDED; + +exit: + mutex_unlock(&ddata->lock); + + return ret; +} + +static int lsm303dlhc_a_restore(struct lsm303dlhc_a_data *ddata) +{ + unsigned char reg; + unsigned char shifted_mode = (ddata->mode << LSM303DLHC_A_CR1_MODE_BIT); + int ret = 0; + + mutex_lock(&ddata->lock); + + if (ddata->device_status == DEVICE_ON) { + mutex_unlock(&ddata->lock); + return 0; + } + + /* in correct mode, no need to change it */ + if (ddata->mode == LSM303DLHC_A_MODE_OFF) { + ddata->device_status = DEVICE_OFF; + goto fail; + } else + ddata->device_status = DEVICE_ON; + + if (ddata->regulator) + regulator_enable(ddata->regulator); + + /* BDU should be enabled by default/recommened */ + reg = ddata->range; + shifted_mode |= LSM303DLHC_A_CR1_AXIS_ENABLE; + + ret = lsm303dlhc_a_write(ddata, CTRL_REG1, shifted_mode, + "CTRL_REG1"); + if (ret < 0) + goto fail; + + ret = lsm303dlhc_a_write(ddata, CTRL_REG4, reg, "CTRL_REG4"); + + if (ret < 0) + goto fail; + + /* write to the boot bit to reboot memory content */ + ret = lsm303dlhc_a_write(ddata, CTRL_REG5, 0x80, "CTRL_REG5"); + + if (ret < 0) + goto fail; + +fail: + if (ret < 0) + dev_err(&ddata->client->dev, + "could not restore the device %d\n", ret); + mutex_unlock(&ddata->lock); + return ret; +} +#endif + +static int lsm303dlhc_a_readdata(struct lsm303dlhc_a_data *ddata) +{ + 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; + } + + 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]); + + data[0] >>= ddata->shift_adjust; + data[1] >>= ddata->shift_adjust; + data[2] >>= ddata->shift_adjust; + + /* 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 lsm303dlhc_a_show_data(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct platform_device *pdev = to_platform_device(dev); + struct lsm303dlhc_a_data *ddata = platform_get_drvdata(pdev); + int ret = 0; + + mutex_lock(&ddata->lock); + + if (ddata->mode == LSM303DLHC_A_MODE_OFF || + ddata->device_status == DEVICE_SUSPENDED) { + mutex_unlock(&ddata->lock); + return ret; + } + + ret = lsm303dlhc_a_readdata(ddata); + + if (ret < 0) { + mutex_unlock(&ddata->lock); + return ret; + } + + mutex_unlock(&ddata->lock); + + return sprintf(buf, "%8x:%8x:%8x\n", ddata->data.x, ddata->data.y, + ddata->data.z); +} + +static ssize_t lsm303dlhc_a_show_range(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct platform_device *pdev = to_platform_device(dev); + struct lsm303dlhc_a_data *ddata = platform_get_drvdata(pdev); + + return sprintf(buf, "%d\n", ddata->range >> LSM303DLHC_A_CR4_FS_BIT); +} + +static ssize_t lsm303dlhc_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 lsm303dlhc_a_data *ddata = platform_get_drvdata(pdev); + long val; + int error; + + error = strict_strtol(buf, 0, &val); + if (error) + return error; + + if (val < LSM303DLHC_A_RANGE_2G || val > LSM303DLHC_A_RANGE_16G) + return -EINVAL; + + mutex_lock(&ddata->lock); + + if (ddata->mode == LSM303DLHC_A_MODE_OFF) { + dev_info(&ddata->client->dev, + "device is switched off,make it ON using MODE"); + mutex_unlock(&ddata->lock); + return count; + } + + ddata->range = val; + ddata->range <<= LSM303DLHC_A_CR4_FS_BIT; + + error = lsm303dlhc_a_write(ddata, CTRL_REG4, ddata->range, + "CTRL_REG4"); + if (error < 0) + return error; + + switch (val) { + case LSM303DLHC_A_RANGE_2G: + ddata->shift_adjust = SHIFT_ADJ_2G; + break; + case LSM303DLHC_A_RANGE_4G: + ddata->shift_adjust = SHIFT_ADJ_4G; + break; + case LSM303DLHC_A_RANGE_8G: + ddata->shift_adjust = SHIFT_ADJ_8G; + break; + case LSM303DLHC_A_RANGE_16G: + ddata->shift_adjust = SHIFT_ADJ_16G; + break; + default: + return -EINVAL; + } + + mutex_unlock(&ddata->lock); + + return count; +} + +static ssize_t lsm303dlhc_a_show_mode(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct platform_device *pdev = to_platform_device(dev); + struct lsm303dlhc_a_data *ddata = platform_get_drvdata(pdev); + + return sprintf(buf, "%d\n", ddata->mode); +} + +static ssize_t lsm303dlhc_a_store_mode(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct platform_device *pdev = to_platform_device(dev); + struct lsm303dlhc_a_data *ddata = platform_get_drvdata(pdev); + long val; + unsigned char data; + int error; + bool set_boot_bit = false; + + error = strict_strtol(buf, 0, &val); + if (error) + return error; + + mutex_lock(&ddata->lock); + + /* not in correct range */ + + if (val < LSM303DLHC_A_MODE_OFF || val > LSM303DLHC_A_MODE_MAX) { + mutex_unlock(&ddata->lock); + return -EINVAL; + } + + if (ddata->device_status == DEVICE_SUSPENDED) { + if (val == LSM303DLHC_A_MODE_OFF) { + ddata->mode = val; + mutex_unlock(&ddata->lock); + return count; + } else { + /* device is turning on after suspend, reset memory */ + set_boot_bit = true; + } + } + + /* if same mode as existing, return */ + if (ddata->mode == val) { + mutex_unlock(&ddata->lock); + return count; + } + + /* turn on the supplies if already off */ + if (ddata->regulator && ddata->mode == LSM303DLHC_A_MODE_OFF + && (ddata->device_status == DEVICE_OFF + || ddata->device_status == DEVICE_SUSPENDED)) { + regulator_enable(ddata->regulator); + ddata->device_status = DEVICE_ON; + } + + data = lsm303dlhc_a_read(ddata, CTRL_REG1, "CTRL_REG1"); + + data &= ~LSM303DLHC_A_CR1_MODE_MASK; + + ddata->mode = val; + + data |= ((val << LSM303DLHC_A_CR1_MODE_BIT) + & LSM303DLHC_A_CR1_MODE_MASK); + + error = lsm303dlhc_a_write(ddata, CTRL_REG1, data, "CTRL_REG1"); + if (error < 0) { + if (ddata->regulator && ddata->device_status == DEVICE_ON) { + regulator_disable(ddata->regulator); + ddata->device_status = DEVICE_OFF; + } + mutex_unlock(&ddata->lock); + return error; + } + + /* + * Power on request when device is in suspended state + * write to the boot bit in CTRL_REG2 to reboot memory content + * and ensure correct device behavior after it resumes + */ + if (set_boot_bit) { + error = lsm303dlhc_a_write(ddata, CTRL_REG5, 0x80, "CTRL_REG5"); + if (error < 0) { + if (ddata->regulator && + ddata->device_status == DEVICE_ON) { + regulator_disable(ddata->regulator); + ddata->device_status = DEVICE_OFF; + } + mutex_unlock(&ddata->lock); + return error; + } + } + + if (val == LSM303DLHC_A_MODE_OFF) { + + /* + * No need to store context here + * it is not like suspend/resume + * but fall back to default values + */ + ddata->range = LSM303DLHC_A_RANGE_2G; + ddata->shift_adjust = SHIFT_ADJ_2G; + + if (ddata->regulator && ddata->device_status == DEVICE_ON) { + regulator_disable(ddata->regulator); + ddata->device_status = DEVICE_OFF; + } + } + mutex_unlock(&ddata->lock); + + return count; +} + +static ssize_t lsm303dlhc_a_show_id(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct platform_device *pdev = to_platform_device(dev); + struct lsm303dlhc_a_data *ddata = platform_get_drvdata(pdev); + + return sprintf(buf, "%d\n", ddata->id); +} + +static DEVICE_ATTR(id, S_IRUGO, lsm303dlhc_a_show_id, NULL); + +static DEVICE_ATTR(data, S_IRUGO, lsm303dlhc_a_show_data, NULL); + +static DEVICE_ATTR(range, S_IWUGO | S_IRUGO, + lsm303dlhc_a_show_range, lsm303dlhc_a_store_range); + +static DEVICE_ATTR(mode, S_IWUGO | S_IRUGO, + lsm303dlhc_a_show_mode, lsm303dlhc_a_store_mode); + +static struct attribute *lsm303dlhc_a_attributes[] = { + &dev_attr_data.attr, + &dev_attr_range.attr, + &dev_attr_mode.attr, + &dev_attr_id.attr, + NULL +}; + +static const struct attribute_group lsm303dlhc_a_attr_group = { + .attrs = lsm303dlhc_a_attributes, +}; + +static int __devinit lsm303dlhc_a_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + int ret; + struct lsm303dlhc_a_data *adata = NULL; + + adata = kzalloc(sizeof(struct lsm303dlhc_a_data), GFP_KERNEL); + if (adata == NULL) { + ret = -ENOMEM; + goto err_op_failed; + } + + adata->client = client; + i2c_set_clientdata(client, adata); + + /* copy platform specific data */ + memcpy(&adata->pdata, client->dev.platform_data, sizeof(adata->pdata)); + adata->mode = LSM303DLHC_A_MODE_OFF; + adata->range = LSM303DLHC_A_RANGE_2G; + adata->shift_adjust = SHIFT_ADJ_2G; + adata->device_status = DEVICE_OFF; + dev_set_name(&client->dev, adata->pdata.name_a); + + adata->regulator = regulator_get(&client->dev, "v-accel"); + if (IS_ERR(adata->regulator)) { + dev_err(&client->dev, "failed to get regulator\n"); + ret = PTR_ERR(adata->regulator); + adata->regulator = NULL; + } + + if (adata->regulator) { + regulator_enable(adata->regulator); + adata->device_status = DEVICE_ON; + } + + ret = lsm303dlhc_a_read(adata, WHO_AM_I, "WHO_AM_I"); + if (ret < 0) + goto exit_free_regulator; + + dev_info(&client->dev, "3-Axis Accelerometer, ID : %d\n", + ret); + adata->id = ret; + + mutex_init(&adata->lock); + + ret = sysfs_create_group(&client->dev.kobj, &lsm303dlhc_a_attr_group); + if (ret) + goto exit_free_regulator; + + if (adata->device_status == DEVICE_ON && adata->regulator) { + regulator_disable(adata->regulator); + adata->device_status = DEVICE_OFF; + } + + return ret; + +exit_free_regulator: + if (adata->device_status == DEVICE_ON && adata->regulator) { + regulator_disable(adata->regulator); + regulator_put(adata->regulator); + adata->device_status = DEVICE_OFF; + } +err_op_failed: + kfree(adata); + dev_err(&client->dev, "probe function fails %x", ret); + return ret; +} + +static int __devexit lsm303dlhc_a_remove(struct i2c_client *client) +{ + int ret; + struct lsm303dlhc_a_data *adata; + + adata = i2c_get_clientdata(client); + sysfs_remove_group(&client->dev.kobj, &lsm303dlhc_a_attr_group); + + /* safer to make device off */ + if (adata->mode != LSM303DLHC_A_MODE_OFF) { + ret = lsm303dlhc_a_write(adata, CTRL_REG1, 0, "CONTROL"); + + if (ret < 0) { + dev_err(&client->dev, + "could not turn off the device %d", + ret); + return ret; + } + + if (adata->regulator && adata->device_status == DEVICE_ON) { + regulator_disable(adata->regulator); + regulator_put(adata->regulator); + adata->device_status = DEVICE_OFF; + } + } + + i2c_set_clientdata(client, NULL); + kfree(adata); + + return 0; +} + +#ifdef (CONFIG_PM) +static int lsm303dlhc_a_suspend(struct device *dev) +{ + struct lsm303dlhc_a_data *ddata; + int ret; + + ddata = dev_get_drvdata(dev); + + ret = lsm303dlhc_a_do_suspend(ddata); + if (ret < 0) + dev_err(&ddata->client->dev, + "Error while suspending the device"); + + return ret; +} + +static int lsm303dlhc_a_resume(struct device *dev) +{ + struct lsm303dlhc_a_data *ddata; + int ret; + + ddata = dev_get_drvdata(dev); + + ret = lsm303dlhc_a_restore(ddata); + + if (ret < 0) + dev_err(&ddata->client->dev, + "Error while resuming the device"); + + return ret; +} +static const struct dev_pm_ops lsm303dlhc_a_dev_pm_ops = { + .suspend = lsm303dlhc_a_suspend, + .resume = lsm303dlhc_a_resume, +}; +#endif /* CONFIG_PM */ + +static const struct i2c_device_id lsm303dlhc_a_id[] = { + { "lsm303dlhc_a", 0 }, + { }, +}; + +static struct i2c_driver lsm303dlhc_a_driver = { + .probe = lsm303dlhc_a_probe, + .remove = lsm303dlhc_a_remove, + .id_table = lsm303dlhc_a_id, + .driver = { + .name = "lsm303dlhc_a", + #ifdef (CONFIG_PM) + .pm = &lsm303dlhc_a_dev_pm_ops, + #endif + }, +}; + +static int __init lsm303dlhc_a_init(void) +{ + return i2c_add_driver(&lsm303dlhc_a_driver); +} + +static void __exit lsm303dlhc_a_exit(void) +{ + i2c_del_driver(&lsm303dlhc_a_driver); +} + +module_init(lsm303dlhc_a_init) +module_exit(lsm303dlhc_a_exit) + +MODULE_DESCRIPTION("lSM303DLH 3-Axis Accelerometer Driver"); +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("STMicroelectronics"); -- cgit v1.2.3 From 967357f610b0855ba29d375f2e0432f690b8089a Mon Sep 17 00:00:00 2001 From: Chethan Krishna N Date: Wed, 13 Jul 2011 16:49:07 +0530 Subject: lsm303dlhc: add early suspend support Adding early suspend and late resume support for lsm303dlhc sensor devices. ST-Ericsson ID: 317051 ST-Ericsson FOSS-OUT ID: NA ST-Ericsson Linux next: NA Signed-off-by: Chethan Krishna N Change-Id: I839bb5bdbd0cb8d5e73a9c8ed3af3df722ac4c99 Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/27117 Reviewed-by: QATOOLS Reviewed-by: QATEST Reviewed-by: Srinidhi KASAGAR --- drivers/hwmon/lsm303dlhc_a.c | 46 +++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 43 insertions(+), 3 deletions(-) diff --git a/drivers/hwmon/lsm303dlhc_a.c b/drivers/hwmon/lsm303dlhc_a.c index 2da79affdd1..f855d57eb9f 100644 --- a/drivers/hwmon/lsm303dlhc_a.c +++ b/drivers/hwmon/lsm303dlhc_a.c @@ -16,6 +16,10 @@ #include #include +#ifdef CONFIG_HAS_EARLYSUSPEND +#include +#endif + #define WHO_AM_I 0x0F /* lsm303dlhc accelerometer registers */ @@ -77,6 +81,7 @@ struct lsm303dlhc_a_t { * @mode: current mode of operation * @rate: current sampling rate * @shift_adjust: current shift adjust value set according to range + * @early_suspend: early suspend structure * @device_status: device is ON, OFF or SUSPENDED * @id: accelerometer device id */ @@ -87,6 +92,7 @@ struct lsm303dlhc_a_data { struct lsm303dlhc_a_t data; struct lsm303dlh_platform_data pdata; struct regulator *regulator; + struct early_suspend early_suspend; unsigned char range; unsigned char mode; unsigned char rate; @@ -95,6 +101,11 @@ struct lsm303dlhc_a_data { int id; }; +#ifdef CONFIG_HAS_EARLYSUSPEND +static void lsm303dlhc_a_early_suspend(struct early_suspend *data); +static void lsm303dlhc_a_late_resume(struct early_suspend *data); +#endif + static int lsm303dlhc_a_write(struct lsm303dlhc_a_data *ddata, u8 reg, u8 val, char *msg) { @@ -116,7 +127,7 @@ static int lsm303dlhc_a_read(struct lsm303dlhc_a_data *ddata, u8 reg, char *msg) return ret; } -#ifdef (CONFIG_PM) +#if defined(CONFIG_HAS_EARLYSUSPEND) || defined(CONFIG_PM) static int lsm303dlhc_a_do_suspend(struct lsm303dlhc_a_data *ddata) { int ret; @@ -523,6 +534,14 @@ static int __devinit lsm303dlhc_a_probe(struct i2c_client *client, if (ret) goto exit_free_regulator; +#ifdef CONFIG_HAS_EARLYSUSPEND + adata->early_suspend.level = + EARLY_SUSPEND_LEVEL_BLANK_SCREEN + 1; + adata->early_suspend.suspend = lsm303dlhc_a_early_suspend; + adata->early_suspend.resume = lsm303dlhc_a_late_resume; + register_early_suspend(&adata->early_suspend); +#endif + if (adata->device_status == DEVICE_ON && adata->regulator) { regulator_disable(adata->regulator); adata->device_status = DEVICE_OFF; @@ -574,7 +593,7 @@ static int __devexit lsm303dlhc_a_remove(struct i2c_client *client) return 0; } -#ifdef (CONFIG_PM) +#if (!defined(CONFIG_HAS_EARLYSUSPEND) && defined(CONFIG_PM)) static int lsm303dlhc_a_suspend(struct device *dev) { struct lsm303dlhc_a_data *ddata; @@ -609,6 +628,27 @@ static const struct dev_pm_ops lsm303dlhc_a_dev_pm_ops = { .suspend = lsm303dlhc_a_suspend, .resume = lsm303dlhc_a_resume, }; +#else +static void lsm303dlhc_a_early_suspend(struct early_suspend *data) +{ + struct lsm303dlhc_a_data *ddata = + container_of(data, struct lsm303dlhc_a_data, early_suspend); + int ret; + + ret = lsm303dlhc_a_do_suspend(ddata); +} + +static void lsm303dlhc_a_late_resume(struct early_suspend *data) +{ + struct lsm303dlhc_a_data *ddata = + container_of(data, struct lsm303dlhc_a_data, early_suspend); + int ret; + + ret = lsm303dlhc_a_restore(ddata); + if (ret < 0) + dev_err(&ddata->client->dev, + "lsm303dlhc_a late resume failed\n"); +} #endif /* CONFIG_PM */ static const struct i2c_device_id lsm303dlhc_a_id[] = { @@ -622,7 +662,7 @@ static struct i2c_driver lsm303dlhc_a_driver = { .id_table = lsm303dlhc_a_id, .driver = { .name = "lsm303dlhc_a", - #ifdef (CONFIG_PM) + #if (!defined(CONFIG_HAS_EARLYSUSPEND) && defined(CONFIG_PM)) .pm = &lsm303dlhc_a_dev_pm_ops, #endif }, -- cgit v1.2.3 From 6783fa1417f0bbe90f75e49d52ec67dd73004158 Mon Sep 17 00:00:00 2001 From: Chethan Krishna N Date: Thu, 25 Aug 2011 13:04:38 +0530 Subject: lsm303dlh: Detect chip version at run time Enable both drivers and detect which probe has to be called based on hardware detected at run time. Change-Id: I7020b63c4345a23404cf06418aebaa876c504c1e Signed-off-by: Chethan Krishna N Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/27883 Reviewed-by: QATEST Reviewed-by: Srinidhi KASAGAR --- drivers/hwmon/Kconfig | 14 -------------- drivers/hwmon/Makefile | 3 +-- 2 files changed, 1 insertion(+), 16 deletions(-) diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig index cd1468b1b3a..72524d16630 100644 --- a/drivers/hwmon/Kconfig +++ b/drivers/hwmon/Kconfig @@ -710,20 +710,6 @@ config SENSORS_LSM303DLH_INPUT_DEVICE interrupts, need to be enabled only when input device support is required. -config SENSORS_LSM303DLHC - tristate "ST LSM303DLHC 3-axis accelerometer and 3-axis magnetometer" - depends on I2C - default n - help - This driver provides support for the LSM303DLHC chip which includes a - 3-axis accelerometer and a 3-axis magnetometer. - - This driver can also be built as modules. If so, the module for - accelerometer will be called lsm303dlhc_a and for magnetometer it will - be called lsm303dlh_m. - - Say Y here if you have a device containing lsm303dlhc chip. - config SENSORS_L3G4200D tristate "ST L3G4200D 3-axis gyroscope" depends on I2C diff --git a/drivers/hwmon/Makefile b/drivers/hwmon/Makefile index 27eb2a571f8..adca284522b 100644 --- a/drivers/hwmon/Makefile +++ b/drivers/hwmon/Makefile @@ -84,8 +84,7 @@ obj-$(CONFIG_SENSORS_LM93) += lm93.o obj-$(CONFIG_SENSORS_LM95241) += lm95241.o obj-$(CONFIG_SENSORS_LM95245) += lm95245.o obj-$(CONFIG_SENSORS_LTC4151) += ltc4151.o -obj-$(CONFIG_SENSORS_LSM303DLH) += lsm303dlh_a.o lsm303dlh_m.o -obj-$(CONFIG_SENSORS_LSM303DLHC) += lsm303dlhc_a.o lsm303dlh_m.o +obj-$(CONFIG_SENSORS_LSM303DLH) += lsm303dlh_a.o lsm303dlh_m.o lsm303dlhc_a.o obj-$(CONFIG_SENSORS_L3G4200D) += l3g4200d.o obj-$(CONFIG_SENSORS_LTC4215) += ltc4215.o obj-$(CONFIG_SENSORS_LTC4245) += ltc4245.o -- cgit v1.2.3 From 3fda9d6fbd963bdc22cb8d040c59f09cf5513fb1 Mon Sep 17 00:00:00 2001 From: Rabin Vincent Date: Mon, 8 Aug 2011 11:51:05 +0530 Subject: lsm303dlhc: fix !CONFIG_HAS_EARLYSUSPEND build ST-Ericsson ID: 317051 ST-Ericsson FOSS-OUT ID: Trivial ST-Ericsson Linux next: NA Change-Id: I605e284d9702c48b0bcca2d0781c1ff0ce0d3c55 Signed-off-by: Rabin Vincent Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/28418 Reviewed-by: QATEST Reviewed-by: Srinidhi KASAGAR --- drivers/hwmon/lsm303dlhc_a.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/hwmon/lsm303dlhc_a.c b/drivers/hwmon/lsm303dlhc_a.c index f855d57eb9f..512d511ece4 100644 --- a/drivers/hwmon/lsm303dlhc_a.c +++ b/drivers/hwmon/lsm303dlhc_a.c @@ -92,7 +92,9 @@ struct lsm303dlhc_a_data { struct lsm303dlhc_a_t data; struct lsm303dlh_platform_data pdata; struct regulator *regulator; +#ifdef CONFIG_HAS_EARLYSUSPEND struct early_suspend early_suspend; +#endif unsigned char range; unsigned char mode; unsigned char rate; -- cgit v1.2.3 From de5507b7d332266e8fd44f2d0351342c13f1c09f Mon Sep 17 00:00:00 2001 From: Chethan Krishna N Date: Thu, 25 Aug 2011 13:44:02 +0530 Subject: lsm303dlhc: Correct out register reading Out register reading method for lsm303dlhc sensor chip is corrected ST-Ericsson ID: 356390 ST-Ericsson FOSS-OUT ID: NA ST-Ericsson Linux next: NA Change-Id: Ia1805ad094886a8d23a29148a9d0e3ba460a06f7 Signed-off-by: Chethan Krishna N Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/28688 Reviewed-by: QATEST Reviewed-by: Philippe LANGLAIS --- drivers/hwmon/Kconfig | 14 ++++++++++++++ drivers/hwmon/Makefile | 3 ++- drivers/hwmon/lsm303dlh_m.c | 11 +++++++++++ drivers/hwmon/lsm303dlhc_a.c | 2 +- 4 files changed, 28 insertions(+), 2 deletions(-) diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig index 72524d16630..cd1468b1b3a 100644 --- a/drivers/hwmon/Kconfig +++ b/drivers/hwmon/Kconfig @@ -710,6 +710,20 @@ config SENSORS_LSM303DLH_INPUT_DEVICE interrupts, need to be enabled only when input device support is required. +config SENSORS_LSM303DLHC + tristate "ST LSM303DLHC 3-axis accelerometer and 3-axis magnetometer" + depends on I2C + default n + help + This driver provides support for the LSM303DLHC chip which includes a + 3-axis accelerometer and a 3-axis magnetometer. + + This driver can also be built as modules. If so, the module for + accelerometer will be called lsm303dlhc_a and for magnetometer it will + be called lsm303dlh_m. + + Say Y here if you have a device containing lsm303dlhc chip. + config SENSORS_L3G4200D tristate "ST L3G4200D 3-axis gyroscope" depends on I2C diff --git a/drivers/hwmon/Makefile b/drivers/hwmon/Makefile index adca284522b..e4a4b4b9edb 100644 --- a/drivers/hwmon/Makefile +++ b/drivers/hwmon/Makefile @@ -84,7 +84,8 @@ obj-$(CONFIG_SENSORS_LM93) += lm93.o obj-$(CONFIG_SENSORS_LM95241) += lm95241.o obj-$(CONFIG_SENSORS_LM95245) += lm95245.o obj-$(CONFIG_SENSORS_LTC4151) += ltc4151.o -obj-$(CONFIG_SENSORS_LSM303DLH) += lsm303dlh_a.o lsm303dlh_m.o lsm303dlhc_a.o +obj-$(CONFIG_SENSORS_LSM303DLH) += lsm303dlh_a.o lsm303dlh_m.o +obj-$(CONFIG_SENSORS_LSM303DLHC)+= lsm303dlhc_a.o obj-$(CONFIG_SENSORS_L3G4200D) += l3g4200d.o obj-$(CONFIG_SENSORS_LTC4215) += ltc4215.o obj-$(CONFIG_SENSORS_LTC4245) += ltc4245.o diff --git a/drivers/hwmon/lsm303dlh_m.c b/drivers/hwmon/lsm303dlh_m.c index 254f44d40c6..aa43d055e67 100644 --- a/drivers/hwmon/lsm303dlh_m.c +++ b/drivers/hwmon/lsm303dlh_m.c @@ -319,6 +319,7 @@ static ssize_t lsm303dlh_m_store_rate(struct device *dev, static int lsm303dlh_m_xyz_read(struct lsm303dlh_m_data *ddata) { unsigned char xyz_data[6]; + short temp; int ret = lsm303dlh_m_read_multi(ddata, OUT_X_M, 6, xyz_data, "OUT_X_M"); if (ret < 0) @@ -332,6 +333,16 @@ static int lsm303dlh_m_xyz_read(struct lsm303dlh_m_data *ddata) ddata->data[2] = (short) (((xyz_data[4]) << 8) | xyz_data[5]); +#ifdef SENSORS_LSM303DLHC + /* + * the out registers are in x, z and y order + * so swap y and z values + */ + temp = ddata->data[1]; + ddata->data[1] = ddata->data[2]; + ddata->data[2] = temp; +#endif + /* taking orientation of x,y,z axis into account*/ ddata->data[ddata->pdata.axis_map_x] = ddata->pdata.negative_x ? diff --git a/drivers/hwmon/lsm303dlhc_a.c b/drivers/hwmon/lsm303dlhc_a.c index 512d511ece4..175a111cc0a 100644 --- a/drivers/hwmon/lsm303dlhc_a.c +++ b/drivers/hwmon/lsm303dlhc_a.c @@ -51,7 +51,7 @@ #define SHIFT_ADJ_8G 2 /* ~3.9/16*/ #define SHIFT_ADJ_16G 1 /* ~3.9/16*/ -#define AXISDATA_REG 0x29 /* axis data */ +#define AXISDATA_REG 0x28 /* axis data */ /* lsm303dlh magnetometer registers */ #define IRA_REG_M 0x0A -- cgit v1.2.3 From 32850037114f00ad4703e6cdadb06f4a18d7f7ba Mon Sep 17 00:00:00 2001 From: Naga Radhesh Date: Tue, 30 Aug 2011 16:09:45 +0530 Subject: hwmon: change regulator for accelerometer change regulator supply from v-accel to vdd ST-Ericsson ID: - ST-Ericsson FOSS-OUT ID: Trivial ST-Ericsson Linux next: Not tested Signed-off-by: Naga Radhesh Change-Id: I16c1f6b3225939cb8c43513487c4a69cb879511d Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/29730 --- drivers/hwmon/lsm303dlhc_a.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/hwmon/lsm303dlhc_a.c b/drivers/hwmon/lsm303dlhc_a.c index 175a111cc0a..17c74595ff2 100644 --- a/drivers/hwmon/lsm303dlhc_a.c +++ b/drivers/hwmon/lsm303dlhc_a.c @@ -510,7 +510,7 @@ static int __devinit lsm303dlhc_a_probe(struct i2c_client *client, adata->device_status = DEVICE_OFF; dev_set_name(&client->dev, adata->pdata.name_a); - adata->regulator = regulator_get(&client->dev, "v-accel"); + adata->regulator = regulator_get(&client->dev, "vdd"); if (IS_ERR(adata->regulator)) { dev_err(&client->dev, "failed to get regulator\n"); ret = PTR_ERR(adata->regulator); -- cgit v1.2.3 From 12e04503f2ca5b3cf9c3df8d22b8a57addb26452 Mon Sep 17 00:00:00 2001 From: Robert Marklund Date: Wed, 26 Oct 2011 15:17:44 +0200 Subject: ux500: Separate sensors and stuib Separate sensors and stuib so we can start one without the other. Snowball want sensors but dont have any uib. Change-Id: I96fd1051c0a85c5e8eccd83b08076b7348d4fc17 Signed-off-by: Robert Marklund Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/35357 Reviewed-by: Philippe LANGLAIS --- arch/arm/mach-ux500/board-mop500-sensors.c | 116 +++++++++++++++++++++++++++++ 1 file changed, 116 insertions(+) create mode 100644 arch/arm/mach-ux500/board-mop500-sensors.c diff --git a/arch/arm/mach-ux500/board-mop500-sensors.c b/arch/arm/mach-ux500/board-mop500-sensors.c new file mode 100644 index 00000000000..51cdb5e50e8 --- /dev/null +++ b/arch/arm/mach-ux500/board-mop500-sensors.c @@ -0,0 +1,116 @@ +/* + * Copyright (C) ST-Ericsson SA 2010 + * + * License terms: GNU General Public License (GPL), version 2 + */ + +#include +#include +#include +#include +#include +#include + +#include "board-mop500.h" + +/* + * LSM303DLH accelerometer + magnetometer sensors + */ +static struct lsm303dlh_platform_data __initdata lsm303dlh_pdata = { + .name_a = "lsm303dlh.0", + .name_m = "lsm303dlh.1", + .axis_map_x = 0, + .axis_map_y = 1, + .axis_map_z = 2, + .negative_x = 1, + .negative_y = 1, + .negative_z = 0, +}; + +static struct l3g4200d_gyr_platform_data __initdata l3g4200d_pdata_u8500 = { + .name_gyr = "l3g4200d", + .axis_map_x = 1, + .axis_map_y = 0, + .axis_map_z = 2, + .negative_x = 0, + .negative_y = 0, + .negative_z = 1, +}; + +static struct lps001wp_prs_platform_data __initdata lps001wp_pdata = { + .poll_interval = 500, + .min_interval = 10, +}; + +static struct i2c_board_info __initdata mop500_i2c2_devices[] = { + { + /* LSM303DLH Accelerometer */ + I2C_BOARD_INFO("lsm303dlh_a", 0x18), + .platform_data = &lsm303dlh_pdata, + }, + { + /* LSM303DLH Magnetometer */ + I2C_BOARD_INFO("lsm303dlh_m", 0x1E), + .platform_data = &lsm303dlh_pdata, + }, + { + /* L3G4200D Gyroscope */ + I2C_BOARD_INFO("l3g4200d", 0x68), + .platform_data = &l3g4200d_pdata_u8500, + }, + { + /* LSP001WM Barometer */ + I2C_BOARD_INFO("lps001wp_prs_sysfs", 0x5C), + .platform_data = &lps001wp_pdata, + }, +}; + +/* + * Register/Add i2c sensors + */ +void mop500_sensors_i2c_add(int busnum, struct i2c_board_info const *info, + unsigned n) +{ + struct i2c_adapter *adap; + struct i2c_client *client; + int i; + + adap = i2c_get_adapter(busnum); + if (!adap) { + /* We have no i2c adapter yet lets create it. */ + pr_info(__FILE__ ": Creating i2c adapter %d\n", busnum); + i2c_register_board_info(busnum, info, n); + return; + } + + for (i = 0; i < n; i++) { + client = i2c_new_device(adap, &info[i]); + if (!client) + pr_err(__FILE__ ": failed to register %s to i2c%d\n", + info[i].type, + busnum); + } + + i2c_put_adapter(adap); +} + + +void __init mop500_sensors_init(void) +{ + if (machine_is_hrefv60()) { + lsm303dlh_pdata.irq_a1 = HREFV60_ACCEL_INT1_GPIO; + lsm303dlh_pdata.irq_a2 = HREFV60_ACCEL_INT2_GPIO; + lsm303dlh_pdata.irq_m = HREFV60_MAGNET_DRDY_GPIO; + } else if (machine_is_snowball()) { + lsm303dlh_pdata.irq_a1 = SNOWBALL_ACCEL_INT1_GPIO; + lsm303dlh_pdata.irq_a2 = SNOWBALL_ACCEL_INT2_GPIO; + lsm303dlh_pdata.irq_m = SNOWBALL_MAGNET_DRDY_GPIO; + } else { + lsm303dlh_pdata.irq_a1 = GPIO_ACCEL_INT1; + lsm303dlh_pdata.irq_a2 = GPIO_ACCEL_INT2; + lsm303dlh_pdata.irq_m = GPIO_MAGNET_DRDY; + } + + mop500_sensors_i2c_add(2, mop500_i2c2_devices, + ARRAY_SIZE(mop500_i2c2_devices)); +} -- cgit v1.2.3 From dce99f8d1a5dcd1bd1ccdee10ead754406a880ae Mon Sep 17 00:00:00 2001 From: Alessandro Rubini Date: Thu, 26 May 2011 09:20:58 +0200 Subject: input/misc: added lps001wp as internally delivered by ST This is the driver that I got from Matteo Dameno and Carmine Iascone, it doesn't currently compile for some missing symbols but I'd better first commit what I got and then make my own changes over it. Signed-off-by: Alessandro Rubini Change-Id: I1dada398da9fe30d5e1f29c9785707c8a84c7a6c Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/24784 Tested-by: Robert MARKLUND Reviewed-by: Philippe LANGLAIS --- drivers/input/misc/lps001wp_prs.c | 1280 +++++++++++++++++++++++++++++++++++++ include/linux/input/lps001wp.h | 80 +++ 2 files changed, 1360 insertions(+) create mode 100644 drivers/input/misc/lps001wp_prs.c create mode 100644 include/linux/input/lps001wp.h diff --git a/drivers/input/misc/lps001wp_prs.c b/drivers/input/misc/lps001wp_prs.c new file mode 100644 index 00000000000..6157c474bb4 --- /dev/null +++ b/drivers/input/misc/lps001wp_prs.c @@ -0,0 +1,1280 @@ + +/******************** (C) COPYRIGHT 2010 STMicroelectronics ******************** +* +* File Name : lps001wp_prs.c +* Authors : MSH - Motion Mems BU - Application Team +* : Matteo Dameno (matteo.dameno@st.com) +* : Carmine Iascone (carmine.iascone@st.com) +* : Both authors are willing to be considered the contact +* : and update points for the driver. +* Version : V 1.1.1 +* Date : 2010/11/22 +* Description : LPS001WP pressure temperature sensor driver +* +******************************************************************************** +* +* 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. +* +****************************************************************************** + + Revision 0.9.0 01/10/2010: + first beta release + Revision 1.1.0 05/11/2010: + add sysfs management + Revision 1.1.1 22/11/2010: + moved to input/misc +******************************************************************************/ + +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include +#include +#include + +#include + + + +#define DEBUG 1 + + +#define PR_ABS_MAX USHORT_MAX + +#define PR_ABS_MIN (u16)(0U) +#define PR_DLT_MAX SHORT_MAX +#define PR_DLT_MIN SHORT_MIN +#define TEMP_MAX SHORT_MAX +#define TEMP_MIN SHORT_MIN + + +#define SENSITIVITY_T_SHIFT 6 /** = 64 LSB/degrC */ +#define SENSITIVITY_P_SHIFT 4 /** = 16 LSB/mbar */ + + +#define OUTDATA_REG 0x28 +#define INDATA_REG 0X30 + +#define WHOAMI_LPS001WP_PRS 0xBA /* Expctd content for WAI */ + +/* CONTROL REGISTERS */ +#define WHO_AM_I 0x0F /* WhoAmI register */ +#define CTRL_REG1 0x20 /* power / ODR control reg */ +#define CTRL_REG2 0x21 /* boot reg */ +#define CTRL_REG3 0x22 /* interrupt control reg */ + +#define STATUS_REG 0X27 /* status reg */ + +#define PRESS_OUT_L OUTDATA_REG + + +#define REF_P_L INDATA_REG /* pressure reference */ +#define REF_P_H 0x31 /* pressure reference */ +#define THS_P_L 0x32 /* pressure threshold */ +#define THS_P_H 0x33 /* pressure threshold */ + +#define INT_CFG 0x34 /* interrupt config */ +#define INT_SRC 0x35 /* interrupt source */ +#define INT_ACK 0x36 /* interrupt acknoledge */ +/* end CONTROL REGISTRES */ + + +/* Barometer and Termometer output data rate ODR */ +#define LPS001WP_PRS_ODR_MASK 0x30 /* Mask to access odr bits only */ +#define LPS001WP_PRS_ODR_7_1 0x00 /* 7Hz baro and 1Hz term ODR */ +#define LPS001WP_PRS_ODR_7_7 0x01 /* 7Hz baro and 7Hz term ODR */ +#define LPS001WP_PRS_ODR_12_12 0x11 /* 12.5Hz baro and 12.5Hz term ODR */ + + +#define LPS001WP_PRS_ENABLE_MASK 0x40 /* */ +#define LPS001WP_PRS_DIFF_MASK 0x08 +#define LPS001WP_PRS_LPOW_MASK 0x80 + +#define LPS001WP_PRS_DIFF_ON 0x08 +#define LPS001WP_PRS_DIFF_OFF 0x00 + +#define LPS001WP_PRS_LPOW_ON 0x80 +#define LPS001WP_PRS_LPOW_OFF 0x00 + +#define FUZZ 0 +#define FLAT 0 +#define I2C_RETRY_DELAY 5 +#define I2C_RETRIES 5 +#define I2C_AUTO_INCREMENT 0x80 + +/* RESUME STATE INDICES */ +#define RES_CTRL_REG1 0 +#define RES_CTRL_REG2 1 +#define RES_CTRL_REG3 2 +#define RES_REF_P_L 3 +#define RES_REF_P_H 4 +#define RES_THS_P_L 5 +#define RES_THS_P_H 6 +#define RES_INT_CFG 7 + +#define RESUME_ENTRIES 8 +/* end RESUME STATE INDICES */ + +/* Pressure Sensor Operating Mode */ +#define LPS001WP_PRS_DIFF_ENABLE 1 +#define LPS001WP_PRS_DIFF_DISABLE 0 +#define LPS001WP_PRS_LPOWER_EN 1 +#define LPS001WP_PRS_LPOWER_DIS 0 + +static const struct { + unsigned int cutoff_ms; + unsigned int mask; +} lps001wp_prs_odr_table[] = { + {80, LPS001WP_PRS_ODR_12_12 }, + {143, LPS001WP_PRS_ODR_7_7 }, + {1000, LPS001WP_PRS_ODR_7_1 }, +}; + +struct lps001wp_prs_data { + struct i2c_client *client; + struct lps001wp_prs_platform_data *pdata; + + struct mutex lock; + struct delayed_work input_work; + + struct input_dev *input_dev; + + int hw_initialized; + /* hw_working=-1 means not tested yet */ + int hw_working; + u8 diff_enabled; + u8 lpowmode_enabled ; + + atomic_t enabled; + int on_before_suspend; + + u8 resume_state[RESUME_ENTRIES]; + +#ifdef DEBUG + u8 reg_addr; +#endif +}; + +struct outputdata { + u16 abspress; + s16 temperature; + s16 deltapress; +}; + + +static int lps001wp_prs_i2c_read(struct lps001wp_prs_data *prs, + u8 *buf, int len) +{ + int err; + int tries = 0; + + struct i2c_msg msgs[] = { + { + .addr = prs->client->addr, + .flags = prs->client->flags & I2C_M_TEN, + .len = 1, + .buf = buf, + }, + { + .addr = prs->client->addr, + .flags = (prs->client->flags & I2C_M_TEN) | I2C_M_RD, + .len = len, + .buf = buf, + }, + }; + + do { + err = i2c_transfer(prs->client->adapter, msgs, 2); + if (err != 2) + msleep_interruptible(I2C_RETRY_DELAY); + } while ((err != 2) && (++tries < I2C_RETRIES)); + + if (err != 2) { + dev_err(&prs->client->dev, "read transfer error\n"); + err = -EIO; + } else { + err = 0; + } + + return err; +} + +static int lps001wp_prs_i2c_write(struct lps001wp_prs_data *prs, + u8 *buf, int len) +{ + int err; + int tries = 0; + struct i2c_msg msgs[] = { + { + .addr = prs->client->addr, + .flags = prs->client->flags & I2C_M_TEN, + .len = len + 1, + .buf = buf, + }, + }; + + do { + err = i2c_transfer(prs->client->adapter, msgs, 1); + if (err != 1) + msleep_interruptible(I2C_RETRY_DELAY); + } while ((err != 1) && (++tries < I2C_RETRIES)); + + if (err != 1) { + dev_err(&prs->client->dev, "write transfer error\n"); + err = -EIO; + } else { + err = 0; + } + + return err; +} + +static int lps001wp_prs_i2c_update(struct lps001wp_prs_data *prs, + u8 reg_address, u8 mask, u8 new_bit_values) +{ + int err = -1; + u8 rdbuf[1] = { reg_address }; + u8 wrbuf[2] = { reg_address , 0x00 }; + + u8 init_val; + u8 updated_val; + err = lps001wp_prs_i2c_read(prs, rdbuf, 1); + if (!(err < 0)) { + init_val = rdbuf[0]; + updated_val = ((mask & new_bit_values) | ((~mask) & init_val)); + wrbuf[1] = updated_val; + err = lps001wp_prs_i2c_write(prs, wrbuf, 2); + } + return err; +} +/* */ + +static int lps001wp_prs_register_write(struct lps001wp_prs_data *prs, u8 *buf, + u8 reg_address, u8 new_value) +{ + int err = -1; + + /* Sets configuration register at reg_address + * NOTE: this is a straight overwrite */ + buf[0] = reg_address; + buf[1] = new_value; + err = lps001wp_prs_i2c_write(prs, buf, 1); + if (err < 0) + return err; + return err; +} + +static int lps001wp_prs_register_read(struct lps001wp_prs_data *prs, u8 *buf, + u8 reg_address) +{ + + int err = -1; + buf[0] = (reg_address); + err = lps001wp_prs_i2c_read(prs, buf, 1); + + return err; +} + +static int lps001wp_prs_register_update(struct lps001wp_prs_data *prs, u8 *buf, + u8 reg_address, u8 mask, u8 new_bit_values) +{ + int err = -1; + u8 init_val; + u8 updated_val; + err = lps001wp_prs_register_read(prs, buf, reg_address); + if (!(err < 0)) { + init_val = buf[0]; + updated_val = ((mask & new_bit_values) | ((~mask) & init_val)); + err = lps001wp_prs_register_write(prs, buf, reg_address, + updated_val); + } + return err; +} + +/* */ + + +static int lps001wp_prs_hw_init(struct lps001wp_prs_data *prs) +{ + int err = -1; + u8 buf[6]; + + printk(KERN_DEBUG "%s: hw init start\n", LPS001WP_PRS_DEV_NAME); + + buf[0] = WHO_AM_I; + err = lps001wp_prs_i2c_read(prs, buf, 1); + if (err < 0) + goto error_firstread; + else + prs->hw_working = 1; + if (buf[0] != WHOAMI_LPS001WP_PRS) { + err = -1; /* TODO:choose the right coded error */ + goto error_unknown_device; + } + + + buf[0] = (I2C_AUTO_INCREMENT | INDATA_REG); + buf[1] = prs->resume_state[RES_REF_P_L]; + buf[2] = prs->resume_state[RES_REF_P_H]; + buf[3] = prs->resume_state[RES_THS_P_L]; + buf[4] = prs->resume_state[RES_THS_P_H]; + err = lps001wp_prs_i2c_write(prs, buf, 4); + if (err < 0) + goto error1; + + buf[0] = (I2C_AUTO_INCREMENT | CTRL_REG1); + buf[1] = prs->resume_state[RES_CTRL_REG1]; + buf[2] = prs->resume_state[RES_CTRL_REG2]; + buf[3] = prs->resume_state[RES_CTRL_REG3]; + err = lps001wp_prs_i2c_write(prs, buf, 3); + if (err < 0) + goto error1; + + buf[0] = INT_CFG; + buf[1] = prs->resume_state[RES_INT_CFG]; + err = lps001wp_prs_i2c_write(prs, buf, 1); + if (err < 0) + goto error1; + + + prs->hw_initialized = 1; + printk(KERN_DEBUG "%s: hw init done\n", LPS001WP_PRS_DEV_NAME); + return 0; + +error_firstread: + prs->hw_working = 0; + dev_warn(&prs->client->dev, "Error reading WHO_AM_I: is device " + "available/working?\n"); + goto error1; +error_unknown_device: + dev_err(&prs->client->dev, + "device unknown. Expected: 0x%x," + " Replies: 0x%x\n", WHOAMI_LPS001WP_PRS, buf[0]); +error1: + prs->hw_initialized = 0; + dev_err(&prs->client->dev, "hw init error 0x%x,0x%x: %d\n", buf[0], + buf[1], err); + return err; +} + +static void lps001wp_prs_device_power_off(struct lps001wp_prs_data *prs) +{ + int err; + u8 buf[2] = { CTRL_REG1, LPS001WP_PRS_PM_OFF }; + + err = lps001wp_prs_i2c_write(prs, buf, 1); + if (err < 0) + dev_err(&prs->client->dev, "soft power off failed: %d\n", err); + + if (prs->pdata->power_off) { + prs->pdata->power_off(); + prs->hw_initialized = 0; + } + if (prs->hw_initialized) { + prs->hw_initialized = 0; + } + +} + +static int lps001wp_prs_device_power_on(struct lps001wp_prs_data *prs) +{ + int err = -1; + + if (prs->pdata->power_on) { + err = prs->pdata->power_on(); + if (err < 0) { + dev_err(&prs->client->dev, + "power_on failed: %d\n", err); + return err; + } + } + + if (!prs->hw_initialized) { + err = lps001wp_prs_hw_init(prs); + if (prs->hw_working == 1 && err < 0) { + lps001wp_prs_device_power_off(prs); + return err; + } + } + + return 0; +} + + + +int lps001wp_prs_update_odr(struct lps001wp_prs_data *prs, int poll_interval_ms) +{ + int err = -1; + int i; + + u8 buf[2]; + u8 updated_val; + u8 init_val; + u8 new_val; + u8 mask = LPS001WP_PRS_ODR_MASK; + + /* Following, looks for the longest possible odr interval scrolling the + * odr_table vector from the end (shortest interval) backward (longest + * interval), to support the poll_interval requested by the system. + * It must be the longest interval lower then the poll interval.*/ + for (i = ARRAY_SIZE(lps001wp_prs_odr_table) - 1; i >= 0; i--) { + if (lps001wp_prs_odr_table[i].cutoff_ms <= poll_interval_ms) + break; + } + + new_val = lps001wp_prs_odr_table[i].mask; + + /* If device is currently enabled, we need to write new + * configuration out to it */ + if (atomic_read(&prs->enabled)) { + buf[0] = CTRL_REG1; + err = lps001wp_prs_i2c_read(prs, buf, 1); + if (err < 0) + goto error; + init_val = buf[0]; + prs->resume_state[RES_CTRL_REG1] = init_val; + + buf[0] = CTRL_REG1; + updated_val = ((mask & new_val) | ((~mask) & init_val)); + buf[1] = updated_val; + buf[0] = CTRL_REG1; + err = lps001wp_prs_i2c_write(prs, buf, 1); + if (err < 0) + goto error; + prs->resume_state[RES_CTRL_REG1] = updated_val; + } + return err; + +error: + dev_err(&prs->client->dev, "update odr failed 0x%x,0x%x: %d\n", + buf[0], buf[1], err); + + return err; +} + +static int lps001wp_prs_set_press_reference(struct lps001wp_prs_data *prs, + u16 new_reference) +{ + int err = -1; + u8 const reg_addressL = REF_P_L; + u8 const reg_addressH = REF_P_H; + u8 bit_valuesL, bit_valuesH; + u8 buf[2]; + + bit_valuesL = (u8) (new_reference & 0x00FF); + bit_valuesH = (u8)((new_reference & 0xFF00) >> 8); + + err = lps001wp_prs_register_write(prs, buf, reg_addressL, + bit_valuesL); + if (err < 0) + return err; + err = lps001wp_prs_register_write(prs, buf, reg_addressH, + bit_valuesH); + if (err < 0) { + lps001wp_prs_register_write(prs, buf, reg_addressL, + prs->resume_state[RES_REF_P_L]); + return err; + } + prs->resume_state[RES_REF_P_L] = bit_valuesL; + prs->resume_state[RES_REF_P_H] = bit_valuesH; + return err; +} + +static int lps001wp_prs_get_press_reference(struct lps001wp_prs_data *prs, + u16 *buf16) +{ + int err = -1; + + u8 bit_valuesL, bit_valuesH; + u8 buf[2] = {0}; + u16 temp = 0; + + err = lps001wp_prs_register_read(prs, buf, REF_P_L); + if (err < 0) + return err; + bit_valuesL = buf[0]; + err = lps001wp_prs_register_read(prs, buf, REF_P_H); + if (err < 0) + return err; + bit_valuesH = buf[0]; + + temp = (((u16) bit_valuesH) << 8); + *buf16 = (temp | ((u16) bit_valuesL)); + + return err; +} + +static int lps001wp_prs_lpow_manage(struct lps001wp_prs_data *prs, u8 control) +{ + int err = -1; + u8 buf[2] = {0x00, 0x00}; + u8 const mask = LPS001WP_PRS_LPOW_MASK; + u8 bit_values = LPS001WP_PRS_LPOW_OFF; + + if (control >= LPS001WP_PRS_LPOWER_EN) { + bit_values = LPS001WP_PRS_LPOW_ON; + } + + err = lps001wp_prs_register_update(prs, buf, CTRL_REG1, + mask, bit_values); + + if (err < 0) + return err; + prs->resume_state[RES_CTRL_REG1] = ((mask & bit_values) | + (~mask & prs->resume_state[RES_CTRL_REG1])); + if (bit_values == LPS001WP_PRS_LPOW_ON) + prs->lpowmode_enabled = 1; + else + prs->lpowmode_enabled = 0; + return err; +} + +static int lps001wp_prs_diffen_manage(struct lps001wp_prs_data *prs, u8 control) +{ + int err = -1; + u8 buf[2] = {0x00, 0x00}; + u8 const mask = LPS001WP_PRS_DIFF_MASK; + u8 bit_values = LPS001WP_PRS_DIFF_OFF; + + if (control >= LPS001WP_PRS_DIFF_ENABLE) { + bit_values = LPS001WP_PRS_DIFF_ON; + } + + err = lps001wp_prs_register_update(prs, buf, CTRL_REG1, + mask, bit_values); + + if (err < 0) + return err; + prs->resume_state[RES_CTRL_REG1] = ((mask & bit_values) | + (~mask & prs->resume_state[RES_CTRL_REG1])); + if (bit_values == LPS001WP_PRS_DIFF_ON) + prs->diff_enabled = 1; + else + prs->diff_enabled = 0; + return err; +} + + +static int lps001wp_prs_get_presstemp_data(struct lps001wp_prs_data *prs, + struct outputdata *out) +{ + int err = -1; + /* Data bytes from hardware PRESS_OUT_L, PRESS_OUT_H, + * TEMP_OUT_L, TEMP_OUT_H, + * DELTA_L, DELTA_H */ + u8 prs_data[6]; + + u16 abspr; + s16 temperature, deltapr; + int regToRead = 4; + prs_data[4] = 0; + prs_data[5] = 0; + + if (prs->diff_enabled) + regToRead = 6; + + prs_data[0] = (I2C_AUTO_INCREMENT | OUTDATA_REG); + err = lps001wp_prs_i2c_read(prs, prs_data, regToRead); + if (err < 0) + return err; + + abspr = ((((u16) prs_data[1] << 8) | ((u16) prs_data[0]))); + temperature = ((s16) (((u16) prs_data[3] << 8) | ((u16)prs_data[2]))); + + out->abspress = abspr; + out->temperature = temperature; + + deltapr = ((s16) (((u16) prs_data[5] << 8) | ((u16)prs_data[4]))); + out->deltapress = deltapr; + + return err; +} + +static void lps001wp_prs_report_values(struct lps001wp_prs_data *prs, + struct outputdata *out) +{ + input_report_abs(prs->input_dev, ABS_PR, out->abspress); + input_report_abs(prs->input_dev, ABS_TEMP, out->temperature); + input_report_abs(prs->input_dev, ABS_DLTPR, out->deltapress); + input_sync(prs->input_dev); +} + +static int lps001wp_prs_enable(struct lps001wp_prs_data *prs) +{ + int err; + + if (!atomic_cmpxchg(&prs->enabled, 0, 1)) { + err = lps001wp_prs_device_power_on(prs); + if (err < 0) { + atomic_set(&prs->enabled, 0); + return err; + } + schedule_delayed_work(&prs->input_work, + msecs_to_jiffies(prs->pdata->poll_interval)); + } + + return 0; +} + +static int lps001wp_prs_disable(struct lps001wp_prs_data *prs) +{ + if (atomic_cmpxchg(&prs->enabled, 1, 0)) { + cancel_delayed_work_sync(&prs->input_work); + lps001wp_prs_device_power_off(prs); + } + + return 0; +} + +static ssize_t read_single_reg(struct device *dev, char *buf, u8 reg) +{ + ssize_t ret; + struct lps001wp_prs_data *prs = dev_get_drvdata(dev); + int rc = 0; + + u8 data = reg; + rc = lps001wp_prs_i2c_read(prs, &data, 1); + /*TODO: error need to be managed */ + ret = sprintf(buf, "0x%02x\n", data); + return ret; + +} + +static int write_reg(struct device *dev, const char *buf, u8 reg) +{ + int rc = 0; + struct lps001wp_prs_data *prs = dev_get_drvdata(dev); + u8 x[2]; + unsigned long val; + + if (strict_strtoul(buf, 16, &val)) + return -EINVAL; + + x[0] = reg; + x[1] = val; + rc = lps001wp_prs_i2c_write(prs, x, 1); + /*TODO: error need to be managed */ + return rc; +} + +static ssize_t attr_get_polling_rate(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + int val; + struct lps001wp_prs_data *prs = dev_get_drvdata(dev); + mutex_lock(&prs->lock); + val = prs->pdata->poll_interval; + mutex_unlock(&prs->lock); + return sprintf(buf, "%d\n", val); +} + +static ssize_t attr_set_polling_rate(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t size) +{ + struct lps001wp_prs_data *prs = dev_get_drvdata(dev); + unsigned long interval_ms; + + if (strict_strtoul(buf, 10, &interval_ms)) + return -EINVAL; + if (!interval_ms) + return -EINVAL; + mutex_lock(&prs->lock); + prs->pdata->poll_interval = interval_ms; + lps001wp_prs_update_odr(prs, interval_ms); + mutex_unlock(&prs->lock); + return size; +} + +static ssize_t attr_get_diff_enable(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + u8 val; + struct lps001wp_prs_data *prs = dev_get_drvdata(dev); + mutex_lock(&prs->lock); + val = prs->diff_enabled; + mutex_unlock(&prs->lock); + return sprintf(buf, "%d\n", val); +} + +static ssize_t attr_set_diff_enable(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t size) +{ + struct lps001wp_prs_data *prs = dev_get_drvdata(dev); + unsigned long val; + + if (strict_strtoul(buf, 10, &val)) + return -EINVAL; + + mutex_lock(&prs->lock); + lps001wp_prs_diffen_manage(prs, (u8) val); + mutex_unlock(&prs->lock); + return size; +} + +static ssize_t attr_get_enable(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct lps001wp_prs_data *prs = dev_get_drvdata(dev); + int val = atomic_read(&prs->enabled); + return sprintf(buf, "%d\n", val); +} + +static ssize_t attr_set_enable(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t size) +{ + struct lps001wp_prs_data *prs = dev_get_drvdata(dev); + unsigned long val; + + if (strict_strtoul(buf, 10, &val)) + return -EINVAL; + + if (val) + lps001wp_prs_enable(prs); + else + lps001wp_prs_disable(prs); + + return size; +} + +static ssize_t attr_get_press_ref(struct device *dev, + struct device_attribute *attr, char *buf) +{ + int err = -1; + struct lps001wp_prs_data *prs = dev_get_drvdata(dev); + u16 val = 0; + + mutex_lock(&prs->lock); + err = lps001wp_prs_get_press_reference(prs, &val); + mutex_unlock(&prs->lock); + if (err < 0) + return err; + + return sprintf(buf, "%d\n", val); +} + +static ssize_t attr_set_press_ref(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t size) +{ + int err = -1; + struct lps001wp_prs_data *prs = dev_get_drvdata(dev); + unsigned long val = 0; + + if (strict_strtoul(buf, 10, &val)) + return -EINVAL; + + if (val < PR_ABS_MIN || val > PR_ABS_MAX) + return -EINVAL; + + mutex_lock(&prs->lock); + err = lps001wp_prs_set_press_reference(prs, val); + mutex_unlock(&prs->lock); + if (err < 0) + return err; + return size; +} + + +static ssize_t attr_get_lowpowmode(struct device *dev, + struct device_attribute *attr, char *buf) +{ + u8 val; + struct lps001wp_prs_data *prs = dev_get_drvdata(dev); + mutex_lock(&prs->lock); + val = prs->lpowmode_enabled; + mutex_unlock(&prs->lock); + return sprintf(buf, "%d\n", val); +} + +static ssize_t attr_set_lowpowmode(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t size) +{ + int err = -1; + struct lps001wp_prs_data *prs = dev_get_drvdata(dev); + unsigned long val; + + if (strict_strtoul(buf, 10, &val)) + return -EINVAL; + + mutex_lock(&prs->lock); + err = lps001wp_prs_lpow_manage(prs, (u8) val); + mutex_unlock(&prs->lock); + if (err < 0) + return err; + return size; +} + + +#ifdef DEBUG +static ssize_t attr_reg_set(struct device *dev, struct device_attribute *attr, + const char *buf, size_t size) +{ + int rc; + struct lps001wp_prs_data *prs = dev_get_drvdata(dev); + u8 x[2]; + unsigned long val; + + if (strict_strtoul(buf, 16, &val)) + return -EINVAL; + mutex_lock(&prs->lock); + x[0] = prs->reg_addr; + mutex_unlock(&prs->lock); + x[1] = val; + rc = lps001wp_prs_i2c_write(prs, x, 1); + /*TODO: error need to be managed */ + return size; +} + +static ssize_t attr_reg_get(struct device *dev, struct device_attribute *attr, + char *buf) +{ + ssize_t ret; + struct lps001wp_prs_data *prs = dev_get_drvdata(dev); + int rc; + u8 data; + + mutex_lock(&prs->lock); + data = prs->reg_addr; + mutex_unlock(&prs->lock); + rc = lps001wp_prs_i2c_read(prs, &data, 1); + /*TODO: error need to be managed */ + ret = sprintf(buf, "0x%02x\n", data); + return ret; +} + +static ssize_t attr_addr_set(struct device *dev, struct device_attribute *attr, + const char *buf, size_t size) +{ + struct lps001wp_prs_data *prs = dev_get_drvdata(dev); + unsigned long val; + if (strict_strtoul(buf, 16, &val)) + return -EINVAL; + mutex_lock(&prs->lock); + prs->reg_addr = val; + mutex_unlock(&prs->lock); + return size; +} +#endif + + + +static struct device_attribute attributes[] = { + __ATTR(pollrate_ms, 0664, attr_get_polling_rate, attr_set_polling_rate), + __ATTR(enable, 0664, attr_get_enable, attr_set_enable), + __ATTR(diff_enable, 0664, attr_get_diff_enable, attr_set_diff_enable), + __ATTR(press_reference, 0664, attr_get_press_ref, attr_set_press_ref), + __ATTR(lowpow_enable, 0664, attr_get_lowpowmode, attr_set_lowpowmode), +#ifdef DEBUG + __ATTR(reg_value, 0664, attr_reg_get, attr_reg_set), + __ATTR(reg_addr, 0220, NULL, attr_addr_set), +#endif +}; + +static int create_sysfs_interfaces(struct device *dev) +{ + int i; + for (i = 0; i < ARRAY_SIZE(attributes); i++) + if (device_create_file(dev, attributes + i)) + goto error; + return 0; + +error: + for ( ; i >= 0; i--) + device_remove_file(dev, attributes + i); + dev_err(dev, "%s:Unable to create interface\n", __func__); + return -1; +} + +static int remove_sysfs_interfaces(struct device *dev) +{ + int i; + for (i = 0; i < ARRAY_SIZE(attributes); i++) + device_remove_file(dev, attributes + i); + return 0; +} + + +static void lps001wp_prs_input_work_func(struct work_struct *work) +{ + struct lps001wp_prs_data *prs; + + struct outputdata output; + struct outputdata *out = &output; + int err; + + prs = container_of((struct delayed_work *)work, + struct lps001wp_prs_data, input_work); + + mutex_lock(&prs->lock); + err = lps001wp_prs_get_presstemp_data(prs, out); + if (err < 0) + dev_err(&prs->client->dev, "get_pressure_data failed\n"); + else + lps001wp_prs_report_values(prs, out); + + schedule_delayed_work(&prs->input_work, + msecs_to_jiffies(prs->pdata->poll_interval)); + mutex_unlock(&prs->lock); +} + +int lps001wp_prs_input_open(struct input_dev *input) +{ + struct lps001wp_prs_data *prs = input_get_drvdata(input); + + return lps001wp_prs_enable(prs); +} + +void lps001wp_prs_input_close(struct input_dev *dev) +{ + struct lps001wp_prs_data *prs = input_get_drvdata(dev); + + lps001wp_prs_disable(prs); +} + + +static int lps001wp_prs_validate_pdata(struct lps001wp_prs_data *prs) +{ + prs->pdata->poll_interval = max(prs->pdata->poll_interval, + prs->pdata->min_interval); + + /* Enforce minimum polling interval */ + if (prs->pdata->poll_interval < prs->pdata->min_interval) { + dev_err(&prs->client->dev, "minimum poll interval violated\n"); + return -EINVAL; + } + + return 0; +} + +static int lps001wp_prs_input_init(struct lps001wp_prs_data *prs) +{ + int err; + + INIT_DELAYED_WORK(&prs->input_work, lps001wp_prs_input_work_func); + prs->input_dev = input_allocate_device(); + if (!prs->input_dev) { + err = -ENOMEM; + dev_err(&prs->client->dev, "input device allocate failed\n"); + goto err0; + } + + prs->input_dev->open = lps001wp_prs_input_open; + prs->input_dev->close = lps001wp_prs_input_close; + prs->input_dev->name = LPS001WP_PRS_DEV_NAME; + prs->input_dev->id.bustype = BUS_I2C; + prs->input_dev->dev.parent = &prs->client->dev; + + input_set_drvdata(prs->input_dev, prs); + + set_bit(EV_ABS, prs->input_dev->evbit); + + input_set_abs_params(prs->input_dev, ABS_PR, + PR_ABS_MIN, PR_ABS_MAX, FUZZ, FLAT); + input_set_abs_params(prs->input_dev, ABS_TEMP, + PR_DLT_MIN, PR_DLT_MAX, FUZZ, FLAT); + input_set_abs_params(prs->input_dev, ABS_DLTPR, + TEMP_MIN, TEMP_MAX, FUZZ, FLAT); + + + prs->input_dev->name = "LPS001WP barometer"; + + err = input_register_device(prs->input_dev); + if (err) { + dev_err(&prs->client->dev, + "unable to register input polled device %s\n", + prs->input_dev->name); + goto err1; + } + + return 0; + +err1: + input_free_device(prs->input_dev); +err0: + return err; +} + +static void lps001wp_prs_input_cleanup(struct lps001wp_prs_data *prs) +{ + input_unregister_device(prs->input_dev); + input_free_device(prs->input_dev); +} + +static int lps001wp_prs_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct lps001wp_prs_data *prs; + int err = -1; + int tempvalue; + + pr_info("%s: probe start.\n", LPS001WP_PRS_DEV_NAME); + + if (client->dev.platform_data == NULL) { + dev_err(&client->dev, "platform data is NULL. exiting.\n"); + err = -ENODEV; + goto exit_check_functionality_failed; + } + + if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) { + dev_err(&client->dev, "client not i2c capable\n"); + err = -ENODEV; + goto exit_check_functionality_failed; + } + + if (!i2c_check_functionality(client->adapter, + I2C_FUNC_SMBUS_BYTE | + I2C_FUNC_SMBUS_BYTE_DATA | + I2C_FUNC_SMBUS_WORD_DATA)) { + dev_err(&client->dev, "client not smb-i2c capable:2\n"); + err = -EIO; + goto exit_check_functionality_failed; + } + + + if (!i2c_check_functionality(client->adapter, + I2C_FUNC_SMBUS_I2C_BLOCK)){ + dev_err(&client->dev, "client not smb-i2c capable:3\n"); + err = -EIO; + goto exit_check_functionality_failed; + } + + + prs = kzalloc(sizeof(struct lps001wp_prs_data), GFP_KERNEL); + if (prs == NULL) { + err = -ENOMEM; + dev_err(&client->dev, + "failed to allocate memory for module data: " + "%d\n", err); + goto exit_alloc_data_failed; + } + + mutex_init(&prs->lock); + mutex_lock(&prs->lock); + + prs->client = client; + i2c_set_clientdata(client, prs); + + + if (i2c_smbus_read_byte(client) < 0) { + printk(KERN_ERR "i2c_smbus_read_byte error!!\n"); + goto err_mutexunlockfreedata; + } else { + printk(KERN_DEBUG "%s Device detected!\n", + LPS001WP_PRS_DEV_NAME); + } + + /* read chip id */ + tempvalue = i2c_smbus_read_word_data(client, WHO_AM_I); + if ((tempvalue & 0x00FF) == WHOAMI_LPS001WP_PRS) { + printk(KERN_DEBUG "%s I2C driver registered!\n", + LPS001WP_PRS_DEV_NAME); + } else { + prs->client = NULL; + printk(KERN_DEBUG "I2C driver not registered!" + " Device unknown\n"); + goto err_mutexunlockfreedata; + } + + prs->pdata = kmalloc(sizeof(*prs->pdata), GFP_KERNEL); + if (prs->pdata == NULL) { + err = -ENOMEM; + dev_err(&client->dev, + "failed to allocate memory for pdata: %d\n", + err); + goto err_mutexunlockfreedata; + } + + memcpy(prs->pdata, client->dev.platform_data, sizeof(*prs->pdata)); + + err = lps001wp_prs_validate_pdata(prs); + if (err < 0) { + dev_err(&client->dev, "failed to validate platform data\n"); + goto exit_kfree_pdata; + } + + i2c_set_clientdata(client, prs); + + + if (prs->pdata->init) { + err = prs->pdata->init(); + if (err < 0) { + dev_err(&client->dev, "init failed: %d\n", err); + goto err2; + } + } + + memset(prs->resume_state, 0, ARRAY_SIZE(prs->resume_state)); + + prs->resume_state[RES_CTRL_REG1] = LPS001WP_PRS_PM_NORMAL; + prs->resume_state[RES_CTRL_REG2] = 0x00; + prs->resume_state[RES_CTRL_REG3] = 0x00; + prs->resume_state[RES_REF_P_L] = 0x00; + prs->resume_state[RES_REF_P_H] = 0x00; + prs->resume_state[RES_THS_P_L] = 0x00; + prs->resume_state[RES_THS_P_H] = 0x00; + prs->resume_state[RES_INT_CFG] = 0x00; + + err = lps001wp_prs_device_power_on(prs); + if (err < 0) { + dev_err(&client->dev, "power on failed: %d\n", err); + goto err2; + } + + prs->diff_enabled = 0; + prs->lpowmode_enabled = 0; + atomic_set(&prs->enabled, 1); + + err = lps001wp_prs_update_odr(prs, prs->pdata->poll_interval); + if (err < 0) { + dev_err(&client->dev, "update_odr failed\n"); + goto err_power_off; + } + + err = lps001wp_prs_input_init(prs); + if (err < 0) { + dev_err(&client->dev, "input init failed\n"); + goto err_power_off; + } + + + err = create_sysfs_interfaces(&client->dev); + if (err < 0) { + dev_err(&client->dev, + "device LPS001WP_PRS_DEV_NAME sysfs register failed\n"); + goto err_input_cleanup; + } + + + lps001wp_prs_device_power_off(prs); + + /* As default, do not report information */ + atomic_set(&prs->enabled, 0); + + + mutex_unlock(&prs->lock); + + dev_info(&client->dev, "%s: probed\n", LPS001WP_PRS_DEV_NAME); + + return 0; + +/* +remove_sysfs_int: + remove_sysfs_interfaces(&client->dev); +*/ +err_input_cleanup: + lps001wp_prs_input_cleanup(prs); +err_power_off: + lps001wp_prs_device_power_off(prs); +err2: + if (prs->pdata->exit) + prs->pdata->exit(); +exit_kfree_pdata: + kfree(prs->pdata); + +err_mutexunlockfreedata: + mutex_unlock(&prs->lock); + kfree(prs); +exit_alloc_data_failed: +exit_check_functionality_failed: + printk(KERN_ERR "%s: Driver Init failed\n", LPS001WP_PRS_DEV_NAME); + return err; +} + +static int __devexit lps001wp_prs_remove(struct i2c_client *client) +{ + struct lps001wp_prs_data *prs = i2c_get_clientdata(client); + + lps001wp_prs_input_cleanup(prs); + lps001wp_prs_device_power_off(prs); + remove_sysfs_interfaces(&client->dev); + + if (prs->pdata->exit) + prs->pdata->exit(); + kfree(prs->pdata); + kfree(prs); + + return 0; +} + +static int lps001wp_prs_resume(struct i2c_client *client) +{ + struct lps001wp_prs_data *prs = i2c_get_clientdata(client); + + if (prs->on_before_suspend) + return lps001wp_prs_enable(prs); + return 0; +} + +static int lps001wp_prs_suspend(struct i2c_client *client, pm_message_t mesg) +{ + struct lps001wp_prs_data *prs = i2c_get_clientdata(client); + + prs->on_before_suspend = atomic_read(&prs->enabled); + return lps001wp_prs_disable(prs); +} + +static const struct i2c_device_id lps001wp_prs_id[] + = { { LPS001WP_PRS_DEV_NAME, 0}, { },}; + +MODULE_DEVICE_TABLE(i2c, lps001wp_prs_id); + +static struct i2c_driver lps001wp_prs_driver = { + .driver = { + .name = LPS001WP_PRS_DEV_NAME, + .owner = THIS_MODULE, + }, + .probe = lps001wp_prs_probe, + .remove = __devexit_p(lps001wp_prs_remove), + .id_table = lps001wp_prs_id, + .resume = lps001wp_prs_resume, + .suspend = lps001wp_prs_suspend, +}; + +static int __init lps001wp_prs_init(void) +{ + printk(KERN_DEBUG "%s barometer driver: init\n", + LPS001WP_PRS_DEV_NAME); + return i2c_add_driver(&lps001wp_prs_driver); +} + +static void __exit lps001wp_prs_exit(void) +{ + #if DEBUG + printk(KERN_DEBUG "%s barometer driver exit\n", + LPS001WP_PRS_DEV_NAME); + #endif + i2c_del_driver(&lps001wp_prs_driver); + return; +} + +module_init(lps001wp_prs_init); +module_exit(lps001wp_prs_exit); + +MODULE_DESCRIPTION("STMicrolelectronics lps001wp pressure sensor sysfs driver"); +MODULE_AUTHOR("Matteo Dameno, Carmine Iascone, STMicroelectronics"); +MODULE_LICENSE("GPL"); + diff --git a/include/linux/input/lps001wp.h b/include/linux/input/lps001wp.h new file mode 100644 index 00000000000..aa5eac9af8f --- /dev/null +++ b/include/linux/input/lps001wp.h @@ -0,0 +1,80 @@ +/******************** (C) COPYRIGHT 2010 STMicroelectronics ******************** +* +* File Name : lps001wp.h +* Authors : MSH - Motion Mems BU - Application Team +* : Matteo Dameno (matteo.dameno@st.com)* +* : Carmine Iascone (carmine.iascone@st.com) +* Version : V 1.1.1 +* Date : 05/11/2010 +* Description : LPS001WP pressure temperature sensor driver +* +******************************************************************************** +* +* 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. +* +*******************************************************************************/ + +#ifndef __LPS001WP_H__ +#define __LPS001WP_H__ + + +#include + +#define SAD0L 0x00 +#define SAD0H 0x01 +#define LPS001WP_PRS_I2C_SADROOT 0x2E +#define LPS001WP_PRS_I2C_SAD_L ((LPS001WP_PRS_I2C_SADROOT<<1)|SAD0L) +#define LPS001WP_PRS_I2C_SAD_H ((LPS001WP_PRS_I2C_SADROOT<<1)|SAD0H) +#define LPS001WP_PRS_DEV_NAME "lps001wp_prs_sysfs" + +/* input define mappings */ +#define ABS_PR ABS_PRESSURE +#define ABS_TEMP ABS_GAS +#define ABS_DLTPR ABS_MISC + + + +/************************************************/ +/* Pressure section defines */ +/************************************************/ + +/* Pressure Sensor Operating Mode */ +#define LPS001WP_PRS_ENABLE 0x01 +#define LPS001WP_PRS_DISABLE 0x00 + + + + +#define LPS001WP_PRS_PM_NORMAL 0x40 +#define LPS001WP_PRS_PM_OFF LPS001WP_PRS_DISABLE + +#define SENSITIVITY_T 64 /** = 64 LSB/degrC */ +#define SENSITIVITY_P 16 /** = 16 LSB/mbar */ + + +#ifdef __KERNEL__ +struct lps001wp_prs_platform_data { + + int poll_interval; + int min_interval; + + int (*init)(void); + void (*exit)(void); + int (*power_on)(void); + int (*power_off)(void); + +}; + +#endif /* __KERNEL__ */ + +#endif /* __LPS001WP_H__ */ -- cgit v1.2.3 From 9d5b9cd273d72e40bbe843bf360db4013575675e Mon Sep 17 00:00:00 2001 From: Alessandro Rubini Date: Thu, 26 May 2011 12:44:52 +0200 Subject: input/misc: fix lps001wp_prs.c so it will compile (next commit) This fixes some undefined SHORT_MAX and so on. I use explicit hex because it is not short, it's exactly 16 bits. Moreover, it adds some needed headers and removes some unused ones. Signed-off-by: Alessandro Rubini Change-Id: I1029884822ba869fc14c34793a50f61e5f4889b2 Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/24796 Tested-by: Robert MARKLUND Reviewed-by: Philippe LANGLAIS --- drivers/input/misc/lps001wp_prs.c | 24 ++++++++++-------------- 1 file changed, 10 insertions(+), 14 deletions(-) diff --git a/drivers/input/misc/lps001wp_prs.c b/drivers/input/misc/lps001wp_prs.c index 6157c474bb4..9ec96ba3863 100644 --- a/drivers/input/misc/lps001wp_prs.c +++ b/drivers/input/misc/lps001wp_prs.c @@ -35,19 +35,16 @@ moved to input/misc ******************************************************************************/ +#include +#include +#include +#include #include #include #include -#include #include - #include -#include - #include -#include -#include -#include #include #include @@ -57,13 +54,12 @@ #define DEBUG 1 -#define PR_ABS_MAX USHORT_MAX - -#define PR_ABS_MIN (u16)(0U) -#define PR_DLT_MAX SHORT_MAX -#define PR_DLT_MIN SHORT_MIN -#define TEMP_MAX SHORT_MAX -#define TEMP_MIN SHORT_MIN +#define PR_ABS_MAX 0xffff +#define PR_ABS_MIN 0x0000 +#define PR_DLT_MAX 0x7ffff +#define PR_DLT_MIN -0x80000 /* 16-bit signed value */ +#define TEMP_MAX 0x7fff +#define TEMP_MIN -0x80000 /* 16-bit signed value */ #define SENSITIVITY_T_SHIFT 6 /** = 64 LSB/degrC */ -- cgit v1.2.3 From 299611b440f59522387b51490d618010f81163d6 Mon Sep 17 00:00:00 2001 From: Alessandro Rubini Date: Thu, 26 May 2011 12:46:17 +0200 Subject: drivers/misc: compile lps001wp using a Kconfig entry The value is "default y" for the MACH_U8500 family. Note that this is not ready for upstream because I have no dependencies in place for the entry. "make randomconfig" will find it sooner or later. Signed-off-by: Alessandro Rubini Change-Id: If490d78630a3388e0c7ffa97f2c8e5faf92e373f Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/24797 Reviewed-by: Philippe LANGLAIS Tested-by: Robert MARKLUND --- drivers/input/misc/Kconfig | 7 +++++++ drivers/input/misc/Makefile | 1 + 2 files changed, 8 insertions(+) diff --git a/drivers/input/misc/Kconfig b/drivers/input/misc/Kconfig index 7faf4a7fcaa..842443b0743 100644 --- a/drivers/input/misc/Kconfig +++ b/drivers/input/misc/Kconfig @@ -312,6 +312,13 @@ config INPUT_KXTJ9_POLLED_MODE help Say Y here if you need accelerometer to work in polling mode. +config INPUT_LPS001WP + tristate "LPS0001WP pressure sensor from ST Micro" + default y if MACH_U8500 + help + This is a pressure sensor connected to I2C, mounted on the + snowball and other ST-E boards + config INPUT_POWERMATE tristate "Griffin PowerMate and Contour Jog support" depends on USB_ARCH_HAS_HCD diff --git a/drivers/input/misc/Makefile b/drivers/input/misc/Makefile index f55cdf4916f..8f40a58f05c 100644 --- a/drivers/input/misc/Makefile +++ b/drivers/input/misc/Makefile @@ -29,6 +29,7 @@ obj-$(CONFIG_HP_SDC_RTC) += hp_sdc_rtc.o obj-$(CONFIG_INPUT_IXP4XX_BEEPER) += ixp4xx-beeper.o obj-$(CONFIG_INPUT_KEYSPAN_REMOTE) += keyspan_remote.o obj-$(CONFIG_INPUT_KXTJ9) += kxtj9.o +obj-$(CONFIG_INPUT_LPS001WP) += lps001wp_prs.o obj-$(CONFIG_INPUT_M68K_BEEP) += m68kspkr.o obj-$(CONFIG_INPUT_MAX8925_ONKEY) += max8925_onkey.o obj-$(CONFIG_INPUT_MAX8997_HAPTIC) += max8997_haptic.o -- cgit v1.2.3 From 56f708fbd59dd1d8835d761c8eec490b7a992aa3 Mon Sep 17 00:00:00 2001 From: Robert Marklund Date: Fri, 28 Oct 2011 17:54:26 +0200 Subject: input/misc: Add regulator support to lps001wp Change-Id: I39f57da20017ff7e963d0001a99dd426e12c77d0 Signed-off-by: Robert Marklund Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/35833 Reviewed-by: Philippe LANGLAIS --- drivers/input/misc/lps001wp_prs.c | 36 +++++++++++++++++++++++++++--------- include/linux/input/lps001wp.h | 2 -- 2 files changed, 27 insertions(+), 11 deletions(-) diff --git a/drivers/input/misc/lps001wp_prs.c b/drivers/input/misc/lps001wp_prs.c index 9ec96ba3863..cb60762ac61 100644 --- a/drivers/input/misc/lps001wp_prs.c +++ b/drivers/input/misc/lps001wp_prs.c @@ -46,6 +46,7 @@ #include #include #include +#include #include @@ -164,6 +165,8 @@ struct lps001wp_prs_data { u8 resume_state[RESUME_ENTRIES]; + struct regulator *regulator; + #ifdef DEBUG u8 reg_addr; #endif @@ -380,10 +383,13 @@ static void lps001wp_prs_device_power_off(struct lps001wp_prs_data *prs) if (err < 0) dev_err(&prs->client->dev, "soft power off failed: %d\n", err); - if (prs->pdata->power_off) { - prs->pdata->power_off(); - prs->hw_initialized = 0; + /* disable regulator */ + if (prs->regulator) { + err = regulator_disable(prs->regulator); + if (err < 0) + dev_err(&prs->client->dev, "failed to disable regulator\n"); } + if (prs->hw_initialized) { prs->hw_initialized = 0; } @@ -394,15 +400,23 @@ static int lps001wp_prs_device_power_on(struct lps001wp_prs_data *prs) { int err = -1; - if (prs->pdata->power_on) { - err = prs->pdata->power_on(); - if (err < 0) { - dev_err(&prs->client->dev, - "power_on failed: %d\n", err); - return err; + /* get the regulator the first time */ + if (!prs->regulator) { + prs->regulator = regulator_get(&prs->client->dev, "vdd"); + if (IS_ERR(prs->regulator)) { + dev_err(&prs->client->dev, "failed to get regulator\n"); + prs->regulator = NULL; + return PTR_ERR(prs->regulator); } } + /* enable it also */ + err = regulator_enable(prs->regulator); + if (err < 0) { + dev_err(&prs->client->dev, "failed to enable regulator\n"); + return err; + } + if (!prs->hw_initialized) { err = lps001wp_prs_hw_init(prs); if (prs->hw_working == 1 && err < 0) { @@ -1210,6 +1224,10 @@ static int __devexit lps001wp_prs_remove(struct i2c_client *client) if (prs->pdata->exit) prs->pdata->exit(); + + if (prs->regulator) + regulator_put(prs->regulator); + kfree(prs->pdata); kfree(prs); diff --git a/include/linux/input/lps001wp.h b/include/linux/input/lps001wp.h index aa5eac9af8f..779a415ea68 100644 --- a/include/linux/input/lps001wp.h +++ b/include/linux/input/lps001wp.h @@ -70,8 +70,6 @@ struct lps001wp_prs_platform_data { int (*init)(void); void (*exit)(void); - int (*power_on)(void); - int (*power_off)(void); }; -- cgit v1.2.3 From b14a9a11d30b91aec23da9ee9ad2ebdcf5f187e9 Mon Sep 17 00:00:00 2001 From: Robert Marklund Date: Mon, 21 Nov 2011 15:13:19 +0100 Subject: ux500: Detect accelerometer i2c address on snowball in board sensors file Snowball V7 and above the accelerometer changed i2c address and there is now way to detect that in runtime. So between V7 and V10 we need to probe for the right address. Signed-off-by: Robert Marklund --- arch/arm/mach-ux500/board-mop500-sensors.c | 82 +++++++++++++++++++++++++++--- 1 file changed, 76 insertions(+), 6 deletions(-) diff --git a/arch/arm/mach-ux500/board-mop500-sensors.c b/arch/arm/mach-ux500/board-mop500-sensors.c index 51cdb5e50e8..d546eb1be93 100644 --- a/arch/arm/mach-ux500/board-mop500-sensors.c +++ b/arch/arm/mach-ux500/board-mop500-sensors.c @@ -10,6 +10,7 @@ #include #include #include +#include #include "board-mop500.h" @@ -43,11 +44,6 @@ static struct lps001wp_prs_platform_data __initdata lps001wp_pdata = { }; static struct i2c_board_info __initdata mop500_i2c2_devices[] = { - { - /* LSM303DLH Accelerometer */ - I2C_BOARD_INFO("lsm303dlh_a", 0x18), - .platform_data = &lsm303dlh_pdata, - }, { /* LSM303DLH Magnetometer */ I2C_BOARD_INFO("lsm303dlh_m", 0x1E), @@ -65,6 +61,31 @@ static struct i2c_board_info __initdata mop500_i2c2_devices[] = { }, }; +/* + * Break this out due to the fact that this have changed address on snowball + */ +static struct i2c_board_info __initdata mop500_2_i2c2_devices[] = { + { + /* LSM303DLH Accelerometer */ + I2C_BOARD_INFO("lsm303dlh_a", 0x18), + .platform_data = &lsm303dlh_pdata, + }, +}; + +/* + * This is needed due to the fact that the i2c address changed in V7 =< + * and there is no way of knowing if the HW is V7 or higher so we just + * have to try and fail. + */ +static struct i2c_board_info __initdata snowball_i2c2_devices[] = { + { + /* LSM303DLH Accelerometer */ + I2C_BOARD_INFO("lsm303dlh_a", 0x19), + .platform_data = &lsm303dlh_pdata, + }, +}; + + /* * Register/Add i2c sensors */ @@ -94,9 +115,43 @@ void mop500_sensors_i2c_add(int busnum, struct i2c_board_info const *info, i2c_put_adapter(adap); } +/* + * Register/Add i2c sensors + */ +void mop500_sensors_probe_add_lsm303dlh_a(void) +{ + static const int busnum = 2; + struct i2c_adapter *adap; + struct i2c_client *client; + static const unsigned short i2c_addr_list[] = { + 0x18, 0x19, I2C_CLIENT_END }; + struct i2c_board_info i2c_info = { + /* LSM303DLH Accelerometer */ + I2C_BOARD_INFO("lsm303dlh_a", 0), + .platform_data = &lsm303dlh_pdata, + }; + + adap = i2c_get_adapter(busnum); + if (!adap) { + /* We have no i2c adapter yet lets create it. */ + pr_err(__FILE__ ": Could not get adapter %d\n", busnum); + return; + } + client = i2c_new_probed_device(adap, &i2c_info, + i2c_addr_list, NULL); + if (!client) + pr_err(__FILE__ ": failed to register %s to i2c%d\n", + i2c_info.type, + busnum); + i2c_put_adapter(adap); +} -void __init mop500_sensors_init(void) +static int mop500_sensors_init(void) { + + if (!machine_is_snowball() && !uib_is_stuib()) + return 0; + if (machine_is_hrefv60()) { lsm303dlh_pdata.irq_a1 = HREFV60_ACCEL_INT1_GPIO; lsm303dlh_pdata.irq_a2 = HREFV60_ACCEL_INT2_GPIO; @@ -113,4 +168,19 @@ void __init mop500_sensors_init(void) mop500_sensors_i2c_add(2, mop500_i2c2_devices, ARRAY_SIZE(mop500_i2c2_devices)); + + if (machine_is_snowball()) { + if (cpu_is_u8500v21()) + /* This is ugly but we cant know what address + * to use */ + mop500_sensors_probe_add_lsm303dlh_a(); + else /* Add the accelerometer with new addr */ + mop500_sensors_i2c_add(2, snowball_i2c2_devices, + ARRAY_SIZE(snowball_i2c2_devices)); + } else /* none snowball have the old addr */ + mop500_sensors_i2c_add(2, mop500_2_i2c2_devices, + ARRAY_SIZE(mop500_2_i2c2_devices)); + return 0; } + +module_init(mop500_sensors_init); -- cgit v1.2.3 From 0e786029c4315c9da329236705f4e4b63a49458c Mon Sep 17 00:00:00 2001 From: Philippe Langlais Date: Fri, 2 Dec 2011 14:29:15 +0100 Subject: sensors: Include module.h after 3.2 update Signed-off-by: Philippe Langlais --- drivers/hwmon/l3g4200d.c | 1 + drivers/hwmon/lsm303dlh_a.c | 1 + drivers/hwmon/lsm303dlh_m.c | 1 + 3 files changed, 3 insertions(+) diff --git a/drivers/hwmon/l3g4200d.c b/drivers/hwmon/l3g4200d.c index 39a4e32ee2b..fcac3afc044 100644 --- a/drivers/hwmon/l3g4200d.c +++ b/drivers/hwmon/l3g4200d.c @@ -6,6 +6,7 @@ * Licence terms: GNU General Public Licence (GPL) version 2 */ +#include #include #include #include diff --git a/drivers/hwmon/lsm303dlh_a.c b/drivers/hwmon/lsm303dlh_a.c index d3c30bc49e0..65e92dfb0ff 100644 --- a/drivers/hwmon/lsm303dlh_a.c +++ b/drivers/hwmon/lsm303dlh_a.c @@ -23,6 +23,7 @@ * this program. If not, see . */ +#include #include #include #include diff --git a/drivers/hwmon/lsm303dlh_m.c b/drivers/hwmon/lsm303dlh_m.c index aa43d055e67..98704dc5a0b 100644 --- a/drivers/hwmon/lsm303dlh_m.c +++ b/drivers/hwmon/lsm303dlh_m.c @@ -23,6 +23,7 @@ * this program. If not, see . */ +#include #include #include #include -- cgit v1.2.3 From 945dcdf5c9fd7e2406efd69de71d6ac6b0c1505b Mon Sep 17 00:00:00 2001 From: Philippe Langlais Date: Mon, 5 Dec 2011 11:28:28 +0100 Subject: mach-ux500: sensors: Add __init directive for mop500_sensors_init() Signed-off-by: Philippe Langlais --- arch/arm/mach-ux500/board-mop500-sensors.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/arm/mach-ux500/board-mop500-sensors.c b/arch/arm/mach-ux500/board-mop500-sensors.c index d546eb1be93..bc08e332699 100644 --- a/arch/arm/mach-ux500/board-mop500-sensors.c +++ b/arch/arm/mach-ux500/board-mop500-sensors.c @@ -146,7 +146,7 @@ void mop500_sensors_probe_add_lsm303dlh_a(void) i2c_put_adapter(adap); } -static int mop500_sensors_init(void) +static int __init mop500_sensors_init(void) { if (!machine_is_snowball() && !uib_is_stuib()) -- cgit v1.2.3 From 8a5e1d02337f18783802fc5d7dcf668b534be5de Mon Sep 17 00:00:00 2001 From: Rabin Vincent Date: Tue, 11 Oct 2011 17:21:46 +0530 Subject: hwmon: lsm303dlh: use regulator_set_optimum_mode() Inform the regulator framework about the current consumption, to be used with DRMS. Note that the current consumption in sleep modes is much lower, but we do not inform the regulator framework about those for now. ST-Ericsson ID: 366715 ST-Ericsson Linux next: NA ST-Ericsson FOSS-OUT ID: Trivial Change-Id: I52b9162eead0091595b731e34ebca3bfc4d3d9be Signed-off-by: Rabin Vincent Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/34436 Reviewed-by: QATOOLS Reviewed-by: Bengt JONSSON Reviewed-by: Naga RADHESH Y --- drivers/hwmon/lsm303dlh_a.c | 6 ++++++ drivers/hwmon/lsm303dlh_m.c | 6 ++++++ 2 files changed, 12 insertions(+) diff --git a/drivers/hwmon/lsm303dlh_a.c b/drivers/hwmon/lsm303dlh_a.c index 65e92dfb0ff..c723cfee11e 100644 --- a/drivers/hwmon/lsm303dlh_a.c +++ b/drivers/hwmon/lsm303dlh_a.c @@ -1094,6 +1094,12 @@ static int __devinit lsm303dlh_a_probe(struct i2c_client *client, } if (ddata->regulator) { + /* + * 0.83 milliamps typical with magnetic sensor setting ODR = + * 7.5 Hz, Accelerometer sensor ODR = 50 Hz. Double for + * safety. + */ + regulator_set_optimum_mode(ddata->regulator, 830 * 2); regulator_enable(ddata->regulator); ddata->device_status = DEVICE_ON; } diff --git a/drivers/hwmon/lsm303dlh_m.c b/drivers/hwmon/lsm303dlh_m.c index 98704dc5a0b..5ade20cea97 100644 --- a/drivers/hwmon/lsm303dlh_m.c +++ b/drivers/hwmon/lsm303dlh_m.c @@ -690,6 +690,12 @@ static int __devinit lsm303dlh_m_probe(struct i2c_client *client, } if (ddata->regulator) { + /* + * 0.83 milliamps typical with magnetic sensor setting ODR = + * 7.5 Hz, Accelerometer sensor ODR = 50 Hz. Double for + * safety. + */ + regulator_set_optimum_mode(ddata->regulator, 830 * 2); regulator_enable(ddata->regulator); ddata->device_status = DEVICE_ON; } -- cgit v1.2.3 From 311a1d5209b0a5d660ce6e485d61b8df224a1d6f Mon Sep 17 00:00:00 2001 From: Rabin Vincent Date: Tue, 11 Oct 2011 17:22:56 +0530 Subject: hwmon: lsm303dlhc_a: use regulator_set_optimum_mode() ST-Ericsson ID: 366715 ST-Ericsson Linux next: NA ST-Ericsson FOSS-OUT ID: Trivial Change-Id: I3fd6e8025e658110505e77c724f6bf9b00f6ce9c Signed-off-by: Rabin Vincent Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/35539 Reviewed-by: Naga RADHESH Y Reviewed-by: QABUILD --- drivers/hwmon/lsm303dlhc_a.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/drivers/hwmon/lsm303dlhc_a.c b/drivers/hwmon/lsm303dlhc_a.c index 17c74595ff2..1c6d25f9b87 100644 --- a/drivers/hwmon/lsm303dlhc_a.c +++ b/drivers/hwmon/lsm303dlhc_a.c @@ -518,6 +518,11 @@ static int __devinit lsm303dlhc_a_probe(struct i2c_client *client, } if (adata->regulator) { + /* + * 130 microamps typical with magnetic sensor setting ODR = 7.5 + * Hz, Accelerometer sensor ODR = 50 Hz. Double for safety. + */ + regulator_set_optimum_mode(adata->regulator, 130 * 2); regulator_enable(adata->regulator); adata->device_status = DEVICE_ON; } -- cgit v1.2.3 From 91f25d6ed091a20af26c337e15aaec417788fefd Mon Sep 17 00:00:00 2001 From: Naga Radhesh Date: Mon, 31 Oct 2011 18:30:38 +0530 Subject: hwmon:(lsm303dlh)enable X, Y and Z axis by default Enable x,y and z axis bits of accelerometer, to read data from x,y and z axis. ST-Ericsson ID: 369951 ST-Ericsson Linux next: NA ST-Ericsson FOSS-OUT ID: Trivial Change-Id:Ie8cdf30c210cd90520914b051c342050a80bf350 Signed-off-by: Naga Radhesh Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/35929 Reviewed-by: QATOOLS Reviewed-by: QABUILD Reviewed-by: Srinidhi KASAGAR --- drivers/hwmon/lsm303dlh_a.c | 7 ++++++- drivers/hwmon/lsm303dlhc_a.c | 7 +++++++ 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/drivers/hwmon/lsm303dlh_a.c b/drivers/hwmon/lsm303dlh_a.c index c723cfee11e..5321fb47155 100644 --- a/drivers/hwmon/lsm303dlh_a.c +++ b/drivers/hwmon/lsm303dlh_a.c @@ -835,10 +835,15 @@ static ssize_t lsm303dlh_a_store_mode(struct device *dev, data = lsm303dlh_a_read(ddata, CTRL_REG1, "CTRL_REG1"); + /* + * If chip doesn't get reset during suspend/resume, + * x,y and z axis bits are getting cleared,so set + * these bits to get x,y,z axis data. + */ + data |= LSM303DLH_A_CR1_AXIS_ENABLE; data &= ~LSM303DLH_A_CR1_PM_MASK; 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"); diff --git a/drivers/hwmon/lsm303dlhc_a.c b/drivers/hwmon/lsm303dlhc_a.c index 1c6d25f9b87..a8b1cd95fa9 100644 --- a/drivers/hwmon/lsm303dlhc_a.c +++ b/drivers/hwmon/lsm303dlhc_a.c @@ -400,6 +400,13 @@ static ssize_t lsm303dlhc_a_store_mode(struct device *dev, data = lsm303dlhc_a_read(ddata, CTRL_REG1, "CTRL_REG1"); + /* + * If chip doesn't get reset during suspend/resume, + * x,y and z axis bits are getting cleared,so set + * these bits to get x,y,z data. + */ + data |= LSM303DLHC_A_CR1_AXIS_ENABLE; + data &= ~LSM303DLHC_A_CR1_MODE_MASK; ddata->mode = val; -- cgit v1.2.3 From d6b9b1256559d5f827ff2aa6620d24e5f3abdcd9 Mon Sep 17 00:00:00 2001 From: Anil Kumar Date: Tue, 8 Nov 2011 14:53:46 +0530 Subject: drivers: hwmon: l3g4200d: Provide gyroscope temperature via sysfs Add sysfs interface to read gyroscope temperature ST-Ericsson ID: 371726 ST-Ericsson Linux next: NA ST-Ericsson FOSS-OUT ID: Trivial Change-Id: I8fe2fa836c92f927cb70742a06a8b87b0beaec2f Signed-off-by: Anil Kumar Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/36832 Reviewed-by: QABUILD Reviewed-by: Naga RADHESH Y Reviewed-by: Srinidhi KASAGAR --- drivers/hwmon/l3g4200d.c | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/drivers/hwmon/l3g4200d.c b/drivers/hwmon/l3g4200d.c index fcac3afc044..c07baa09595 100644 --- a/drivers/hwmon/l3g4200d.c +++ b/drivers/hwmon/l3g4200d.c @@ -30,6 +30,7 @@ #define CTRL_REG3 0x22 /* CTRL_REG3 */ #define CTRL_REG4 0x23 /* CTRL_REG4 */ #define CTRL_REG5 0x24 /* CTRL_REG5 */ +#define OUT_TEMP 0x26 /* OUT_TEMP */ #define AXISDATA_REG 0x28 @@ -401,6 +402,24 @@ static ssize_t l3g4200d_store_powermode(struct device *dev, return count; } +static ssize_t l3g4200d_show_gyrotemp(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct platform_device *pdev = to_platform_device(dev); + struct l3g4200d_data *ddata = platform_get_drvdata(pdev); + int ret; + + if (ddata->powermode == PM_OFF || + ddata->device_status == DEVICE_SUSPENDED) + return -EINVAL; + + ret = l3g4200d_read(ddata, OUT_TEMP, "OUT_TEMP"); + if (ret < 0) + return ret; + + return sprintf(buf, "%d\n", ret); +} + static DEVICE_ATTR(gyrodata, S_IRUGO, l3g4200d_show_gyrodata, NULL); static DEVICE_ATTR(range, S_IRUGO | S_IWUGO, @@ -412,11 +431,14 @@ static DEVICE_ATTR(datarate, S_IRUGO | S_IWUGO, static DEVICE_ATTR(powermode, S_IRUGO | S_IWUGO, l3g4200d_show_powermode, l3g4200d_store_powermode); +static DEVICE_ATTR(gyrotemp, S_IRUGO, l3g4200d_show_gyrotemp, NULL); + static struct attribute *l3g4200d_attributes[] = { &dev_attr_gyrodata.attr, &dev_attr_range.attr, &dev_attr_datarate.attr, &dev_attr_powermode.attr, + &dev_attr_gyrotemp.attr, NULL }; -- cgit v1.2.3 From 3d9343d19c7fe803adad87a0af2a017a9ef6ead0 Mon Sep 17 00:00:00 2001 From: Naga Radhesh Date: Tue, 22 Nov 2011 17:24:38 +0530 Subject: hwmon: add mutex unlock in error cases In some error cases mutexunlock is missed, so add mutex unlock in error cases. ST-Ericsson ID: 371373 ST-Ericsson Linux next: NA ST-Ericsson FOSS-OUT ID: Trivial Signed-off-by: Naga Radhesh Change-Id: If3648c5d3e8dad623c7b87fa77bef1f28ba2a056 Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/39253 Reviewed-by: QATOOLS Reviewed-by: QABUILD Reviewed-by: Jonas ABERG --- drivers/hwmon/lsm303dlh_a.c | 1 + drivers/hwmon/lsm303dlh_m.c | 1 + drivers/hwmon/lsm303dlhc_a.c | 5 ++++- 3 files changed, 6 insertions(+), 1 deletion(-) diff --git a/drivers/hwmon/lsm303dlh_a.c b/drivers/hwmon/lsm303dlh_a.c index 5321fb47155..309d60ae8ec 100644 --- a/drivers/hwmon/lsm303dlh_a.c +++ b/drivers/hwmon/lsm303dlh_a.c @@ -762,6 +762,7 @@ static ssize_t lsm303dlh_a_store_range(struct device *dev, ddata->shift_adjust = SHIFT_ADJ_8G; break; default: + mutex_unlock(&ddata->lock); return -EINVAL; } diff --git a/drivers/hwmon/lsm303dlh_m.c b/drivers/hwmon/lsm303dlh_m.c index 5ade20cea97..311e88fb58c 100644 --- a/drivers/hwmon/lsm303dlh_m.c +++ b/drivers/hwmon/lsm303dlh_m.c @@ -516,6 +516,7 @@ static ssize_t lsm303dlh_m_store_range(struct device *dev, z_gain = Z_GAIN_8_1; break; default: + mutex_unlock(&ddata->lock); return -EINVAL; } diff --git a/drivers/hwmon/lsm303dlhc_a.c b/drivers/hwmon/lsm303dlhc_a.c index a8b1cd95fa9..8b7e2fe37b9 100644 --- a/drivers/hwmon/lsm303dlhc_a.c +++ b/drivers/hwmon/lsm303dlhc_a.c @@ -314,8 +314,10 @@ static ssize_t lsm303dlhc_a_store_range(struct device *dev, error = lsm303dlhc_a_write(ddata, CTRL_REG4, ddata->range, "CTRL_REG4"); - if (error < 0) + if (error < 0) { + mutex_unlock(&ddata->lock); return error; + } switch (val) { case LSM303DLHC_A_RANGE_2G: @@ -331,6 +333,7 @@ static ssize_t lsm303dlhc_a_store_range(struct device *dev, ddata->shift_adjust = SHIFT_ADJ_16G; break; default: + mutex_unlock(&ddata->lock); return -EINVAL; } -- cgit v1.2.3 From 04fb350c99fa8b59dbce567a696ce422bdc054dd Mon Sep 17 00:00:00 2001 From: Philippe Langlais Date: Fri, 13 Jan 2012 10:59:07 +0100 Subject: Documentation:add kernel-doc for sensor Add kernel docs for sensors Signed-off-by: Naga Radhesh --- Documentation/DocBook/lsm303dlh.tmpl | 1 + drivers/hwmon/lsm303dlh_a.c | 1 + 2 files changed, 2 insertions(+) diff --git a/Documentation/DocBook/lsm303dlh.tmpl b/Documentation/DocBook/lsm303dlh.tmpl index 3b1c6afa65f..1000481e205 100644 --- a/Documentation/DocBook/lsm303dlh.tmpl +++ b/Documentation/DocBook/lsm303dlh.tmpl @@ -84,6 +84,7 @@ This chapter contains the autogenerated documentation of the internal functions. !Idrivers/hwmon/lsm303dlh_a.c +!Idrivers/hwmon/lsm303dlhc_a.c !Idrivers/hwmon/lsm303dlh_m.c diff --git a/drivers/hwmon/lsm303dlh_a.c b/drivers/hwmon/lsm303dlh_a.c index 309d60ae8ec..b9888add334 100644 --- a/drivers/hwmon/lsm303dlh_a.c +++ b/drivers/hwmon/lsm303dlh_a.c @@ -171,6 +171,7 @@ struct lsm303dlh_a_t { * @interrupt_threshold: interrupt threshold for two channels * @early_suspend: early suspend structure * @device_status: device is ON, OFF or SUSPENDED + * @id: accelerometer device id */ struct lsm303dlh_a_data { struct i2c_client *client; -- cgit v1.2.3 From e38b899c7d35e03ad9a66e40a7ca0092e34aa969 Mon Sep 17 00:00:00 2001 From: Naga Radhesh Date: Tue, 8 Nov 2011 17:21:03 +0530 Subject: lsm303dlh_m: Check for DLHC device to invert y,z Invert y,z co-ordinates as specified in data sheet if device is LSM303DLHC ST-Ericsson ID: 374970 ST-Ericsson Linux next: NA ST-Ericsson FOSS-OUT ID: Trivial Change-Id:I45e706e3f783dd58e02f23d9579f3422ef7fc984 Signed-off-by: Naga Radhesh Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/36886 Reviewed-by: QATOOLS Reviewed-by: QABUILD Reviewed-by: Srinidhi KASAGAR Reviewed-by: QATEST Reviewed-by: Jonas ABERG --- drivers/hwmon/lsm303dlh_m.c | 23 +++++++++++++---------- include/linux/lsm303dlh.h | 2 ++ 2 files changed, 15 insertions(+), 10 deletions(-) diff --git a/drivers/hwmon/lsm303dlh_m.c b/drivers/hwmon/lsm303dlh_m.c index 311e88fb58c..abc91ccb032 100644 --- a/drivers/hwmon/lsm303dlh_m.c +++ b/drivers/hwmon/lsm303dlh_m.c @@ -130,6 +130,9 @@ #define DEVICE_ON 1 #define DEVICE_SUSPENDED 2 +/* device CHIP ID defines */ +#define LSM303DLHC_CHIP_ID 51 + /** * struct lsm303dlh_m_data - data structure used by lsm303dlh_m driver * @client: i2c client @@ -334,16 +337,16 @@ static int lsm303dlh_m_xyz_read(struct lsm303dlh_m_data *ddata) ddata->data[2] = (short) (((xyz_data[4]) << 8) | xyz_data[5]); -#ifdef SENSORS_LSM303DLHC - /* - * the out registers are in x, z and y order - * so swap y and z values - */ - temp = ddata->data[1]; - ddata->data[1] = ddata->data[2]; - ddata->data[2] = temp; -#endif - + /* check if chip is DHLC */ + if (ddata->pdata.chip_id == LSM303DLHC_CHIP_ID) { + /* + * the out registers are in x, z and y order + * so swap y and z values + */ + temp = ddata->data[1]; + ddata->data[1] = ddata->data[2]; + ddata->data[2] = temp; + } /* taking orientation of x,y,z axis into account*/ ddata->data[ddata->pdata.axis_map_x] = ddata->pdata.negative_x ? diff --git a/include/linux/lsm303dlh.h b/include/linux/lsm303dlh.h index ad369b1fbd5..a565faa79ba 100644 --- a/include/linux/lsm303dlh.h +++ b/include/linux/lsm303dlh.h @@ -42,6 +42,7 @@ * @negative_x: x axis is orientation, 0 or 1 * @negative_y: y axis is orientation, 0 or 1 * @negative_z: z axis is orientation, 0 or 1 + * @chip_id: to store ID of the LSM chip */ struct lsm303dlh_platform_data { const char *name_a; @@ -55,6 +56,7 @@ struct lsm303dlh_platform_data { u8 negative_x; u8 negative_y; u8 negative_z; + u32 chip_id; }; #endif /* __KERNEL__ */ -- cgit v1.2.3 From 78ec329add085434c6cdc5ff199b633a7431dadb Mon Sep 17 00:00:00 2001 From: Philippe Langlais Date: Fri, 13 Jan 2012 12:46:53 +0100 Subject: mach-ux500: lsm303dh: Read the chip_id of LSM303 Check which accelerometer chip is mounted and read the chip ID to detect whether chip is LSM303DHL/LSM303DHLC. Signed-off-by: Naga Radhesh Signed-off-by: Philippe Langlais --- arch/arm/mach-ux500/board-mop500-sensors.c | 36 +++++++++++++++++++++++++++++- 1 file changed, 35 insertions(+), 1 deletion(-) diff --git a/arch/arm/mach-ux500/board-mop500-sensors.c b/arch/arm/mach-ux500/board-mop500-sensors.c index bc08e332699..eff8295e97a 100644 --- a/arch/arm/mach-ux500/board-mop500-sensors.c +++ b/arch/arm/mach-ux500/board-mop500-sensors.c @@ -146,10 +146,38 @@ void mop500_sensors_probe_add_lsm303dlh_a(void) i2c_put_adapter(adap); } +/* + * Check which accelerometer chip is mounted on UIB and + * read the chip ID to detect whether chip is LSM303DHL/LSM303DHLC. + */ +static int mop500_get_acc_id(void) +{ + int status; + union i2c_smbus_data data; + struct i2c_adapter *i2c2; + + i2c2 = i2c_get_adapter(2); + if (!i2c2) { + pr_err("failed to get i2c adapter\n"); + return -1; + } + status = i2c_smbus_xfer(i2c2, 0x18 , 0 , + I2C_SMBUS_READ, 0x0F , + I2C_SMBUS_BYTE_DATA, &data); + if (status < 0) { + status = i2c_smbus_xfer(i2c2, 0x19 , 0 , + I2C_SMBUS_READ, 0x0F , + I2C_SMBUS_BYTE_DATA, &data); + } + i2c_put_adapter(i2c2); + return (status < 0) ? status : data.byte; +} + static int __init mop500_sensors_init(void) { + int ret; - if (!machine_is_snowball() && !uib_is_stuib()) + if (!machine_is_snowball() && !uib_is_stuib() && !uib_is_u8500uibr3()) return 0; if (machine_is_hrefv60()) { @@ -166,6 +194,12 @@ static int __init mop500_sensors_init(void) lsm303dlh_pdata.irq_m = GPIO_MAGNET_DRDY; } + ret = mop500_get_acc_id(); + if (ret < 0) + printk(KERN_ERR " Failed to get Accelerometer chip ID\n"); + else + lsm303dlh_pdata.chip_id = ret; + mop500_sensors_i2c_add(2, mop500_i2c2_devices, ARRAY_SIZE(mop500_i2c2_devices)); -- cgit v1.2.3 From ee0510103d625ab6e45d00ebe2890dcefc6bf34b Mon Sep 17 00:00:00 2001 From: Naga Radhesh Date: Fri, 9 Dec 2011 12:44:42 +0530 Subject: drivers: Change sysfs attributes permission change write permissions for syfs attributes to user only, give only read permissions for group and others. ST-Ericsson ID: 361755 ST-Ericsson Linux next: NA ST-Ericsson FOSS-OUT ID: Trivial Change-Id:I25a49f191b29386fffb34b4a39695125811bfb42 Signed-off-by: Naga Radhesh Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/41791 Reviewed-by: QATOOLS Reviewed-by: QABUILD Reviewed-by: QATEST Reviewed-by: Bibek BASU Reviewed-by: Srinidhi KASAGAR --- drivers/hwmon/l3g4200d.c | 6 +++--- drivers/hwmon/lsm303dlh_a.c | 8 ++++---- drivers/hwmon/lsm303dlh_m.c | 6 +++--- drivers/hwmon/lsm303dlhc_a.c | 4 ++-- 4 files changed, 12 insertions(+), 12 deletions(-) diff --git a/drivers/hwmon/l3g4200d.c b/drivers/hwmon/l3g4200d.c index c07baa09595..ffe3e1a9730 100644 --- a/drivers/hwmon/l3g4200d.c +++ b/drivers/hwmon/l3g4200d.c @@ -422,13 +422,13 @@ static ssize_t l3g4200d_show_gyrotemp(struct device *dev, static DEVICE_ATTR(gyrodata, S_IRUGO, l3g4200d_show_gyrodata, NULL); -static DEVICE_ATTR(range, S_IRUGO | S_IWUGO, +static DEVICE_ATTR(range, S_IRUGO | S_IWUSR, l3g4200d_show_range, l3g4200d_store_range); -static DEVICE_ATTR(datarate, S_IRUGO | S_IWUGO, +static DEVICE_ATTR(datarate, S_IRUGO | S_IWUSR, l3g4200d_show_datarate, l3g4200d_store_datarate); -static DEVICE_ATTR(powermode, S_IRUGO | S_IWUGO, +static DEVICE_ATTR(powermode, S_IRUGO | S_IWUSR, l3g4200d_show_powermode, l3g4200d_store_powermode); static DEVICE_ATTR(gyrotemp, S_IRUGO, l3g4200d_show_gyrotemp, NULL); diff --git a/drivers/hwmon/lsm303dlh_a.c b/drivers/hwmon/lsm303dlh_a.c index b9888add334..84a4a76b9ab 100644 --- a/drivers/hwmon/lsm303dlh_a.c +++ b/drivers/hwmon/lsm303dlh_a.c @@ -1013,16 +1013,16 @@ static DEVICE_ATTR(id, S_IRUGO, lsm303dlh_a_show_id, NULL); static DEVICE_ATTR(data, S_IRUGO, lsm303dlh_a_show_data, NULL); -static DEVICE_ATTR(range, S_IWUGO | S_IRUGO, +static DEVICE_ATTR(range, S_IWUSR | S_IRUGO, lsm303dlh_a_show_range, lsm303dlh_a_store_range); -static DEVICE_ATTR(mode, S_IWUGO | S_IRUGO, +static DEVICE_ATTR(mode, S_IWUSR | S_IRUGO, lsm303dlh_a_show_mode, lsm303dlh_a_store_mode); -static DEVICE_ATTR(rate, S_IWUGO | S_IRUGO, +static DEVICE_ATTR(rate, S_IWUSR | S_IRUGO, lsm303dlh_a_show_rate, lsm303dlh_a_store_rate); -static DEVICE_ATTR(sleep_wake, S_IWUGO | S_IRUGO, +static DEVICE_ATTR(sleep_wake, S_IWUSR | S_IRUGO, lsm303dlh_a_show_sleepwake, lsm303dlh_a_store_sleepwake); #ifdef CONFIG_SENSORS_LSM303DLH_INPUT_DEVICE diff --git a/drivers/hwmon/lsm303dlh_m.c b/drivers/hwmon/lsm303dlh_m.c index abc91ccb032..52f71ae6e96 100644 --- a/drivers/hwmon/lsm303dlh_m.c +++ b/drivers/hwmon/lsm303dlh_m.c @@ -634,13 +634,13 @@ static DEVICE_ATTR(gain, S_IRUGO, lsm303dlh_m_gain, NULL); static DEVICE_ATTR(data, S_IRUGO, lsm303dlh_m_values, NULL); -static DEVICE_ATTR(mode, S_IWUGO | S_IRUGO, +static DEVICE_ATTR(mode, S_IWUSR | S_IRUGO, lsm303dlh_m_show_mode, lsm303dlh_m_store_mode); -static DEVICE_ATTR(range, S_IWUGO | S_IRUGO, +static DEVICE_ATTR(range, S_IWUSR | S_IRUGO, lsm303dlh_m_show_range, lsm303dlh_m_store_range); -static DEVICE_ATTR(rate, S_IWUGO | S_IRUGO, +static DEVICE_ATTR(rate, S_IWUSR | S_IRUGO, lsm303dlh_m_show_rate, lsm303dlh_m_store_rate); static struct attribute *lsm303dlh_m_attributes[] = { diff --git a/drivers/hwmon/lsm303dlhc_a.c b/drivers/hwmon/lsm303dlhc_a.c index 8b7e2fe37b9..95d403b08dd 100644 --- a/drivers/hwmon/lsm303dlhc_a.c +++ b/drivers/hwmon/lsm303dlhc_a.c @@ -479,10 +479,10 @@ static DEVICE_ATTR(id, S_IRUGO, lsm303dlhc_a_show_id, NULL); static DEVICE_ATTR(data, S_IRUGO, lsm303dlhc_a_show_data, NULL); -static DEVICE_ATTR(range, S_IWUGO | S_IRUGO, +static DEVICE_ATTR(range, S_IWUSR | S_IRUGO, lsm303dlhc_a_show_range, lsm303dlhc_a_store_range); -static DEVICE_ATTR(mode, S_IWUGO | S_IRUGO, +static DEVICE_ATTR(mode, S_IWUSR | S_IRUGO, lsm303dlhc_a_show_mode, lsm303dlhc_a_store_mode); static struct attribute *lsm303dlhc_a_attributes[] = { -- cgit v1.2.3 From b43999758f2baf6c901ce88272948a7112f047c2 Mon Sep 17 00:00:00 2001 From: Naga Radhesh Date: Wed, 30 Nov 2011 20:30:37 +0530 Subject: lps001wp_prs:add compilation switch for work queue add compilation switch for work queue,input subsystem. ST-Ericsson ID: 371766 ST-Ericsson Linux next: NA ST-Ericsson FOSS-OUT ID: Trivial Change-Id:I0cb3ea3e4f9ea64de427a2b0784032a4eab55580 Signed-off-by: Naga Radhesh Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/40402 Reviewed-by: QABUILD Reviewed-by: Anil KUMAR (STE) Reviewed-by: Jonas ABERG Reviewed-by: Srinidhi KASAGAR --- drivers/input/misc/Kconfig | 9 +++++ drivers/input/misc/lps001wp_prs.c | 82 ++++++++++----------------------------- 2 files changed, 29 insertions(+), 62 deletions(-) diff --git a/drivers/input/misc/Kconfig b/drivers/input/misc/Kconfig index 842443b0743..a787c38fac1 100644 --- a/drivers/input/misc/Kconfig +++ b/drivers/input/misc/Kconfig @@ -319,6 +319,15 @@ config INPUT_LPS001WP This is a pressure sensor connected to I2C, mounted on the snowball and other ST-E boards +config LPS001WP_INPUT_DEVICE + bool "ST LPS001WP INPUT DEVICE" + depends on INPUT_LPS001WP + default n + help + This driver allows device to be used as an input device + need to be enabled only when input device support + is required. + config INPUT_POWERMATE tristate "Griffin PowerMate and Contour Jog support" depends on USB_ARCH_HAS_HCD diff --git a/drivers/input/misc/lps001wp_prs.c b/drivers/input/misc/lps001wp_prs.c index cb60762ac61..65f9ca7891b 100644 --- a/drivers/input/misc/lps001wp_prs.c +++ b/drivers/input/misc/lps001wp_prs.c @@ -97,9 +97,8 @@ /* Barometer and Termometer output data rate ODR */ #define LPS001WP_PRS_ODR_MASK 0x30 /* Mask to access odr bits only */ #define LPS001WP_PRS_ODR_7_1 0x00 /* 7Hz baro and 1Hz term ODR */ -#define LPS001WP_PRS_ODR_7_7 0x01 /* 7Hz baro and 7Hz term ODR */ -#define LPS001WP_PRS_ODR_12_12 0x11 /* 12.5Hz baro and 12.5Hz term ODR */ - +#define LPS001WP_PRS_ODR_7_7 0x10 /* 7Hz baro and 7Hz term ODR */ +#define LPS001WP_PRS_ODR_12_12 0x30 /* 12.5Hz baro and 12.5Hz term ODR */ #define LPS001WP_PRS_ENABLE_MASK 0x40 /* */ #define LPS001WP_PRS_DIFF_MASK 0x08 @@ -150,9 +149,10 @@ struct lps001wp_prs_data { struct lps001wp_prs_platform_data *pdata; struct mutex lock; +#ifdef CONFIG_LPS001WP_INPUT_DEVICE struct delayed_work input_work; - struct input_dev *input_dev; +#endif int hw_initialized; /* hw_working=-1 means not tested yet */ @@ -246,26 +246,6 @@ static int lps001wp_prs_i2c_write(struct lps001wp_prs_data *prs, return err; } -static int lps001wp_prs_i2c_update(struct lps001wp_prs_data *prs, - u8 reg_address, u8 mask, u8 new_bit_values) -{ - int err = -1; - u8 rdbuf[1] = { reg_address }; - u8 wrbuf[2] = { reg_address , 0x00 }; - - u8 init_val; - u8 updated_val; - err = lps001wp_prs_i2c_read(prs, rdbuf, 1); - if (!(err < 0)) { - init_val = rdbuf[0]; - updated_val = ((mask & new_bit_values) | ((~mask) & init_val)); - wrbuf[1] = updated_val; - err = lps001wp_prs_i2c_write(prs, wrbuf, 2); - } - return err; -} -/* */ - static int lps001wp_prs_register_write(struct lps001wp_prs_data *prs, u8 *buf, u8 reg_address, u8 new_value) { @@ -618,6 +598,7 @@ static int lps001wp_prs_get_presstemp_data(struct lps001wp_prs_data *prs, return err; } +#ifdef CONFIG_LPS001WP_INPUT_DEVICE static void lps001wp_prs_report_values(struct lps001wp_prs_data *prs, struct outputdata *out) { @@ -626,6 +607,7 @@ static void lps001wp_prs_report_values(struct lps001wp_prs_data *prs, input_report_abs(prs->input_dev, ABS_DLTPR, out->deltapress); input_sync(prs->input_dev); } +#endif static int lps001wp_prs_enable(struct lps001wp_prs_data *prs) { @@ -637,8 +619,10 @@ static int lps001wp_prs_enable(struct lps001wp_prs_data *prs) atomic_set(&prs->enabled, 0); return err; } +#ifdef CONFIG_LPS001WP_INPUT_DEVICE schedule_delayed_work(&prs->input_work, msecs_to_jiffies(prs->pdata->poll_interval)); +#endif } return 0; @@ -647,44 +631,15 @@ static int lps001wp_prs_enable(struct lps001wp_prs_data *prs) static int lps001wp_prs_disable(struct lps001wp_prs_data *prs) { if (atomic_cmpxchg(&prs->enabled, 1, 0)) { +#ifdef CONFIG_LPS001WP_INPUT_DEVICE cancel_delayed_work_sync(&prs->input_work); +#endif lps001wp_prs_device_power_off(prs); } return 0; } -static ssize_t read_single_reg(struct device *dev, char *buf, u8 reg) -{ - ssize_t ret; - struct lps001wp_prs_data *prs = dev_get_drvdata(dev); - int rc = 0; - - u8 data = reg; - rc = lps001wp_prs_i2c_read(prs, &data, 1); - /*TODO: error need to be managed */ - ret = sprintf(buf, "0x%02x\n", data); - return ret; - -} - -static int write_reg(struct device *dev, const char *buf, u8 reg) -{ - int rc = 0; - struct lps001wp_prs_data *prs = dev_get_drvdata(dev); - u8 x[2]; - unsigned long val; - - if (strict_strtoul(buf, 16, &val)) - return -EINVAL; - - x[0] = reg; - x[1] = val; - rc = lps001wp_prs_i2c_write(prs, x, 1); - /*TODO: error need to be managed */ - return rc; -} - static ssize_t attr_get_polling_rate(struct device *dev, struct device_attribute *attr, char *buf) @@ -927,7 +882,7 @@ static int remove_sysfs_interfaces(struct device *dev) return 0; } - +#ifdef CONFIG_LPS001WP_INPUT_DEVICE static void lps001wp_prs_input_work_func(struct work_struct *work) { struct lps001wp_prs_data *prs; @@ -964,7 +919,7 @@ void lps001wp_prs_input_close(struct input_dev *dev) lps001wp_prs_disable(prs); } - +#endif static int lps001wp_prs_validate_pdata(struct lps001wp_prs_data *prs) { @@ -979,11 +934,10 @@ static int lps001wp_prs_validate_pdata(struct lps001wp_prs_data *prs) return 0; } - +#ifdef CONFIG_LPS001WP_INPUT_DEVICE static int lps001wp_prs_input_init(struct lps001wp_prs_data *prs) { int err; - INIT_DELAYED_WORK(&prs->input_work, lps001wp_prs_input_work_func); prs->input_dev = input_allocate_device(); if (!prs->input_dev) { @@ -1033,7 +987,7 @@ static void lps001wp_prs_input_cleanup(struct lps001wp_prs_data *prs) input_unregister_device(prs->input_dev); input_free_device(prs->input_dev); } - +#endif static int lps001wp_prs_probe(struct i2c_client *client, const struct i2c_device_id *id) { @@ -1163,13 +1117,13 @@ static int lps001wp_prs_probe(struct i2c_client *client, dev_err(&client->dev, "update_odr failed\n"); goto err_power_off; } - +#ifdef CONFIG_LPS001WP_INPUT_DEVICE err = lps001wp_prs_input_init(prs); if (err < 0) { dev_err(&client->dev, "input init failed\n"); goto err_power_off; } - +#endif err = create_sysfs_interfaces(&client->dev); if (err < 0) { @@ -1196,7 +1150,9 @@ remove_sysfs_int: remove_sysfs_interfaces(&client->dev); */ err_input_cleanup: +#ifdef CONFIG_LPS001WP_INPUT_DEVICE lps001wp_prs_input_cleanup(prs); +#endif err_power_off: lps001wp_prs_device_power_off(prs); err2: @@ -1218,7 +1174,9 @@ static int __devexit lps001wp_prs_remove(struct i2c_client *client) { struct lps001wp_prs_data *prs = i2c_get_clientdata(client); +#ifdef CONFIG_LPS001WP_INPUT_DEVICE lps001wp_prs_input_cleanup(prs); +#endif lps001wp_prs_device_power_off(prs); remove_sysfs_interfaces(&client->dev); -- cgit v1.2.3 From 00ade0eb5af35213ff44ae02d2d79d173655d7b1 Mon Sep 17 00:00:00 2001 From: Naga Radhesh Date: Mon, 5 Dec 2011 16:58:11 +0530 Subject: [ANDROID]:input:lps001wp_prs:add regulator,sysfs add regulator,sysfs paths and add error handling in all error cases. ST-Ericsson ID: 371766 ST-Ericsson Linux next: NA ST-Ericsson FOSS-OUT ID: Trivial Change-Id:I9efe9ab97b1a7d051d26e7a803aa7cecccc4d6f4 Signed-off-by: Naga Radhesh Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/41011 Reviewed-by: QATOOLS Reviewed-by: QABUILD Reviewed-by: Srinidhi KASAGAR --- drivers/input/misc/lps001wp_prs.c | 249 +++++++++++++++++++++++++++++++------- 1 file changed, 207 insertions(+), 42 deletions(-) diff --git a/drivers/input/misc/lps001wp_prs.c b/drivers/input/misc/lps001wp_prs.c index 65f9ca7891b..c6c29c34c45 100644 --- a/drivers/input/misc/lps001wp_prs.c +++ b/drivers/input/misc/lps001wp_prs.c @@ -68,7 +68,7 @@ #define OUTDATA_REG 0x28 -#define INDATA_REG 0X30 +#define REF_PRESS_REG 0X30 #define WHOAMI_LPS001WP_PRS 0xBA /* Expctd content for WAI */ @@ -83,7 +83,7 @@ #define PRESS_OUT_L OUTDATA_REG -#define REF_P_L INDATA_REG /* pressure reference */ +#define REF_P_L REF_PRESS_REG /* pressure reference */ #define REF_P_H 0x31 /* pressure reference */ #define THS_P_L 0x32 /* pressure threshold */ #define THS_P_H 0x33 /* pressure threshold */ @@ -144,6 +144,24 @@ static const struct { {1000, LPS001WP_PRS_ODR_7_1 }, }; +/** + * struct lps001wp_prs_data - data structure used by lps001wp_prs driver + * @client: i2c client + * @pdata: lsm303dlh platform data + * @lock: mutex lock for sysfs operations + * @input_work: work queue to read sensor data + * @input_dev: input device + * @regulator: regulator + * @early_suspend: early suspend structure + * @hw_initialized: saves hw initialisation status + * @hw_working: saves hw status + * @diff_enabled: store value of diff enable + * @lpowmode_enabled: flag to set lowpower mode + * @enabled: to store mode of device + * @on_before_suspend: to store status of device during suspend + * @resume_state:store regester values + * @reg_addr: stores reg address to debug + */ struct lps001wp_prs_data { struct i2c_client *client; struct lps001wp_prs_platform_data *pdata; @@ -249,7 +267,7 @@ static int lps001wp_prs_i2c_write(struct lps001wp_prs_data *prs, static int lps001wp_prs_register_write(struct lps001wp_prs_data *prs, u8 *buf, u8 reg_address, u8 new_value) { - int err = -1; + int err = -EINVAL; /* Sets configuration register at reg_address * NOTE: this is a straight overwrite */ @@ -265,7 +283,7 @@ static int lps001wp_prs_register_read(struct lps001wp_prs_data *prs, u8 *buf, u8 reg_address) { - int err = -1; + int err = -EINVAL; buf[0] = (reg_address); err = lps001wp_prs_i2c_read(prs, buf, 1); @@ -275,7 +293,7 @@ static int lps001wp_prs_register_read(struct lps001wp_prs_data *prs, u8 *buf, static int lps001wp_prs_register_update(struct lps001wp_prs_data *prs, u8 *buf, u8 reg_address, u8 mask, u8 new_bit_values) { - int err = -1; + int err = -EINVAL; u8 init_val; u8 updated_val; err = lps001wp_prs_register_read(prs, buf, reg_address); @@ -293,10 +311,11 @@ static int lps001wp_prs_register_update(struct lps001wp_prs_data *prs, u8 *buf, static int lps001wp_prs_hw_init(struct lps001wp_prs_data *prs) { - int err = -1; + int err = -EINVAL; u8 buf[6]; - printk(KERN_DEBUG "%s: hw init start\n", LPS001WP_PRS_DEV_NAME); + dev_dbg(&prs->client->dev, "%s: hw init start\n", + LPS001WP_PRS_DEV_NAME); buf[0] = WHO_AM_I; err = lps001wp_prs_i2c_read(prs, buf, 1); @@ -305,12 +324,12 @@ static int lps001wp_prs_hw_init(struct lps001wp_prs_data *prs) else prs->hw_working = 1; if (buf[0] != WHOAMI_LPS001WP_PRS) { - err = -1; /* TODO:choose the right coded error */ + err = -EINVAL; /* TODO:choose the right coded error */ goto error_unknown_device; } - buf[0] = (I2C_AUTO_INCREMENT | INDATA_REG); + buf[0] = (I2C_AUTO_INCREMENT | REF_PRESS_REG); buf[1] = prs->resume_state[RES_REF_P_L]; buf[2] = prs->resume_state[RES_REF_P_H]; buf[3] = prs->resume_state[RES_THS_P_L]; @@ -335,7 +354,7 @@ static int lps001wp_prs_hw_init(struct lps001wp_prs_data *prs) prs->hw_initialized = 1; - printk(KERN_DEBUG "%s: hw init done\n", LPS001WP_PRS_DEV_NAME); + dev_dbg(&prs->client->dev, "%s: hw init done\n", LPS001WP_PRS_DEV_NAME); return 0; error_firstread: @@ -378,7 +397,7 @@ static void lps001wp_prs_device_power_off(struct lps001wp_prs_data *prs) static int lps001wp_prs_device_power_on(struct lps001wp_prs_data *prs) { - int err = -1; + int err = -EINVAL; /* get the regulator the first time */ if (!prs->regulator) { @@ -412,7 +431,7 @@ static int lps001wp_prs_device_power_on(struct lps001wp_prs_data *prs) int lps001wp_prs_update_odr(struct lps001wp_prs_data *prs, int poll_interval_ms) { - int err = -1; + int err = -EINVAL; int i; u8 buf[2]; @@ -463,12 +482,17 @@ error: static int lps001wp_prs_set_press_reference(struct lps001wp_prs_data *prs, u16 new_reference) { - int err = -1; + int err = -EINVAL; u8 const reg_addressL = REF_P_L; u8 const reg_addressH = REF_P_H; u8 bit_valuesL, bit_valuesH; u8 buf[2]; - + /* + * We need to set new configurations, only if device + * is currently enabled + */ + if (!atomic_read(&prs->enabled)) + return err; bit_valuesL = (u8) (new_reference & 0x00FF); bit_valuesH = (u8)((new_reference & 0xFF00) >> 8); @@ -491,12 +515,17 @@ static int lps001wp_prs_set_press_reference(struct lps001wp_prs_data *prs, static int lps001wp_prs_get_press_reference(struct lps001wp_prs_data *prs, u16 *buf16) { - int err = -1; + int err = -EINVAL; u8 bit_valuesL, bit_valuesH; u8 buf[2] = {0}; u16 temp = 0; - + /* + * We need to read configurations, only if device + * is currently enabled + */ + if (!atomic_read(&prs->enabled)) + return err; err = lps001wp_prs_register_read(prs, buf, REF_P_L); if (err < 0) return err; @@ -514,11 +543,17 @@ static int lps001wp_prs_get_press_reference(struct lps001wp_prs_data *prs, static int lps001wp_prs_lpow_manage(struct lps001wp_prs_data *prs, u8 control) { - int err = -1; + int err = -EINVAL; u8 buf[2] = {0x00, 0x00}; u8 const mask = LPS001WP_PRS_LPOW_MASK; u8 bit_values = LPS001WP_PRS_LPOW_OFF; + /* + * We need to set new configurations, only if device + * is currently enabled + */ + if (!atomic_read(&prs->enabled)) + return err; if (control >= LPS001WP_PRS_LPOWER_EN) { bit_values = LPS001WP_PRS_LPOW_ON; } @@ -539,11 +574,17 @@ static int lps001wp_prs_lpow_manage(struct lps001wp_prs_data *prs, u8 control) static int lps001wp_prs_diffen_manage(struct lps001wp_prs_data *prs, u8 control) { - int err = -1; + int err = -EINVAL; u8 buf[2] = {0x00, 0x00}; u8 const mask = LPS001WP_PRS_DIFF_MASK; u8 bit_values = LPS001WP_PRS_DIFF_OFF; + /* + * We need to set new configurations, only if device + * is currently enabled + */ + if (!atomic_read(&prs->enabled)) + return err; if (control >= LPS001WP_PRS_DIFF_ENABLE) { bit_values = LPS001WP_PRS_DIFF_ON; } @@ -566,7 +607,7 @@ static int lps001wp_prs_diffen_manage(struct lps001wp_prs_data *prs, u8 control) static int lps001wp_prs_get_presstemp_data(struct lps001wp_prs_data *prs, struct outputdata *out) { - int err = -1; + int err = -EINVAL; /* Data bytes from hardware PRESS_OUT_L, PRESS_OUT_H, * TEMP_OUT_L, TEMP_OUT_H, * DELTA_L, DELTA_H */ @@ -589,8 +630,8 @@ static int lps001wp_prs_get_presstemp_data(struct lps001wp_prs_data *prs, abspr = ((((u16) prs_data[1] << 8) | ((u16) prs_data[0]))); temperature = ((s16) (((u16) prs_data[3] << 8) | ((u16)prs_data[2]))); - out->abspress = abspr; - out->temperature = temperature; + out->abspress = (abspr >> SENSITIVITY_P_SHIFT); + out->temperature = (temperature >> SENSITIVITY_T_SHIFT); deltapr = ((s16) (((u16) prs_data[5] << 8) | ((u16)prs_data[4]))); out->deltapress = deltapr; @@ -614,6 +655,8 @@ static int lps001wp_prs_enable(struct lps001wp_prs_data *prs) int err; if (!atomic_cmpxchg(&prs->enabled, 0, 1)) { + if (prs->regulator) + regulator_enable(prs->regulator); err = lps001wp_prs_device_power_on(prs); if (err < 0) { atomic_set(&prs->enabled, 0); @@ -635,6 +678,8 @@ static int lps001wp_prs_disable(struct lps001wp_prs_data *prs) cancel_delayed_work_sync(&prs->input_work); #endif lps001wp_prs_device_power_off(prs); + if (prs->regulator) + regulator_disable(prs->regulator); } return 0; @@ -658,6 +703,7 @@ static ssize_t attr_set_polling_rate(struct device *dev, { struct lps001wp_prs_data *prs = dev_get_drvdata(dev); unsigned long interval_ms; + int err = -EINVAL; if (strict_strtoul(buf, 10, &interval_ms)) return -EINVAL; @@ -665,7 +711,12 @@ static ssize_t attr_set_polling_rate(struct device *dev, return -EINVAL; mutex_lock(&prs->lock); prs->pdata->poll_interval = interval_ms; - lps001wp_prs_update_odr(prs, interval_ms); + err = lps001wp_prs_update_odr(prs, interval_ms); + if (err < 0) { + dev_err(&prs->client->dev, "failed to update odr %ld\n", + interval_ms); + size = err; + } mutex_unlock(&prs->lock); return size; } @@ -688,12 +739,18 @@ static ssize_t attr_set_diff_enable(struct device *dev, { struct lps001wp_prs_data *prs = dev_get_drvdata(dev); unsigned long val; + int err = -EINVAL; if (strict_strtoul(buf, 10, &val)) return -EINVAL; mutex_lock(&prs->lock); - lps001wp_prs_diffen_manage(prs, (u8) val); + err = lps001wp_prs_diffen_manage(prs, (u8) val); + if (err < 0) { + dev_err(&prs->client->dev, "failed to diff enable %ld\n", val); + mutex_unlock(&prs->lock); + return err; + } mutex_unlock(&prs->lock); return size; } @@ -727,15 +784,17 @@ static ssize_t attr_set_enable(struct device *dev, static ssize_t attr_get_press_ref(struct device *dev, struct device_attribute *attr, char *buf) { - int err = -1; + int err = -EINVAL; struct lps001wp_prs_data *prs = dev_get_drvdata(dev); u16 val = 0; mutex_lock(&prs->lock); err = lps001wp_prs_get_press_reference(prs, &val); mutex_unlock(&prs->lock); - if (err < 0) + if (err < 0) { + dev_err(&prs->client->dev, "failed to get ref press\n"); return err; + } return sprintf(buf, "%d\n", val); } @@ -744,7 +803,7 @@ static ssize_t attr_set_press_ref(struct device *dev, struct device_attribute *attr, const char *buf, size_t size) { - int err = -1; + int err = -EINVAL; struct lps001wp_prs_data *prs = dev_get_drvdata(dev); unsigned long val = 0; @@ -757,8 +816,11 @@ static ssize_t attr_set_press_ref(struct device *dev, mutex_lock(&prs->lock); err = lps001wp_prs_set_press_reference(prs, val); mutex_unlock(&prs->lock); - if (err < 0) + if (err < 0) { + dev_err(&prs->client->dev, "failed to set ref press %ld\n", + val); return err; + } return size; } @@ -778,7 +840,7 @@ static ssize_t attr_set_lowpowmode(struct device *dev, struct device_attribute *attr, const char *buf, size_t size) { - int err = -1; + int err = -EINVAL; struct lps001wp_prs_data *prs = dev_get_drvdata(dev); unsigned long val; @@ -788,12 +850,86 @@ static ssize_t attr_set_lowpowmode(struct device *dev, mutex_lock(&prs->lock); err = lps001wp_prs_lpow_manage(prs, (u8) val); mutex_unlock(&prs->lock); - if (err < 0) + if (err < 0) { + dev_err(&prs->client->dev, "failed to set low powermode\n"); return err; + } return size; } +static ssize_t lps001wp_prs_get_press_data(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct lps001wp_prs_data *prs = dev_get_drvdata(dev); + struct outputdata out; + int err = -EINVAL; + mutex_lock(&prs->lock); + /* + * If device is currently enabled, we need to read + * data from it. + */ + if (!atomic_read(&prs->enabled)) + goto out; + err = lps001wp_prs_get_presstemp_data(prs, &out); + if (err < 0) { + dev_err(&prs->client->dev, "get_pressure_data failed\n"); + goto out; + } + mutex_unlock(&prs->lock); + return sprintf(buf, "%d", out.abspress); +out: + mutex_unlock(&prs->lock); + return err; +} +static ssize_t lps001wp_prs_get_deltapr_data(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct lps001wp_prs_data *prs = dev_get_drvdata(dev); + struct outputdata out; + int err = -EINVAL; + mutex_lock(&prs->lock); + /* + * If device is currently enabled, we need to read + * data from it. + */ + if (!atomic_read(&prs->enabled)) { + mutex_unlock(&prs->lock); + return err; + } + err = lps001wp_prs_get_presstemp_data(prs, &out); + if (err < 0) { + dev_err(&prs->client->dev, "get_deltapress_data failed\n"); + mutex_unlock(&prs->lock); + return err; + } + mutex_unlock(&prs->lock); + return sprintf(buf, "%d", out.deltapress); +} +static ssize_t lps001wp_prs_get_temp_data(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct lps001wp_prs_data *prs = dev_get_drvdata(dev); + struct outputdata out; + int err = -EINVAL; + mutex_lock(&prs->lock); + /* + * If device is currently enabled, we need to read + * data from it. + */ + if (!atomic_read(&prs->enabled)) { + mutex_unlock(&prs->lock); + return err; + } + err = lps001wp_prs_get_presstemp_data(prs, &out); + if (err < 0) { + dev_err(&prs->client->dev, "get_temperature_data failed\n"); + mutex_unlock(&prs->lock); + return err; + } + mutex_unlock(&prs->lock); + return sprintf(buf, "%d", out.temperature); +} #ifdef DEBUG static ssize_t attr_reg_set(struct device *dev, struct device_attribute *attr, const char *buf, size_t size) @@ -848,14 +984,21 @@ static ssize_t attr_addr_set(struct device *dev, struct device_attribute *attr, static struct device_attribute attributes[] = { - __ATTR(pollrate_ms, 0664, attr_get_polling_rate, attr_set_polling_rate), - __ATTR(enable, 0664, attr_get_enable, attr_set_enable), - __ATTR(diff_enable, 0664, attr_get_diff_enable, attr_set_diff_enable), - __ATTR(press_reference, 0664, attr_get_press_ref, attr_set_press_ref), - __ATTR(lowpow_enable, 0664, attr_get_lowpowmode, attr_set_lowpowmode), + __ATTR(pollrate_ms, S_IWUSR | S_IRUGO, attr_get_polling_rate, + attr_set_polling_rate), + __ATTR(enable, S_IWUSR | S_IRUGO, attr_get_enable, attr_set_enable), + __ATTR(diff_enable, S_IWUSR | S_IRUGO, attr_get_diff_enable, + attr_set_diff_enable), + __ATTR(press_reference, S_IWUSR | S_IRUGO, attr_get_press_ref, + attr_set_press_ref), + __ATTR(lowpow_enable, S_IWUSR | S_IRUGO, attr_get_lowpowmode, + attr_set_lowpowmode), + __ATTR(press_data, S_IRUGO, lps001wp_prs_get_press_data, NULL), + __ATTR(temp_data, S_IRUGO, lps001wp_prs_get_temp_data, NULL), + __ATTR(deltapr_data, S_IRUGO, lps001wp_prs_get_deltapr_data, NULL), #ifdef DEBUG - __ATTR(reg_value, 0664, attr_reg_get, attr_reg_set), - __ATTR(reg_addr, 0220, NULL, attr_addr_set), + __ATTR(reg_value, S_IWUSR | S_IRUGO, attr_reg_get, attr_reg_set), + __ATTR(reg_addr, S_IWUSR, NULL, attr_addr_set), #endif }; @@ -992,7 +1135,7 @@ static int lps001wp_prs_probe(struct i2c_client *client, const struct i2c_device_id *id) { struct lps001wp_prs_data *prs; - int err = -1; + int err = -EINVAL; int tempvalue; pr_info("%s: probe start.\n", LPS001WP_PRS_DEV_NAME); @@ -1042,23 +1185,31 @@ static int lps001wp_prs_probe(struct i2c_client *client, prs->client = client; i2c_set_clientdata(client, prs); + prs->regulator = regulator_get(&client->dev, "vdd"); + if (IS_ERR(prs->regulator)) { + dev_err(&client->dev, "failed to get regulator\n"); + err = PTR_ERR(prs->regulator); + prs->regulator = NULL; + } + if (prs->regulator) + regulator_enable(prs->regulator); if (i2c_smbus_read_byte(client) < 0) { - printk(KERN_ERR "i2c_smbus_read_byte error!!\n"); + dev_err(&client->dev, "i2c_smbus_read_byte error!!\n"); goto err_mutexunlockfreedata; } else { - printk(KERN_DEBUG "%s Device detected!\n", + dev_dbg(&client->dev, "%s Device detected!\n", LPS001WP_PRS_DEV_NAME); } /* read chip id */ tempvalue = i2c_smbus_read_word_data(client, WHO_AM_I); if ((tempvalue & 0x00FF) == WHOAMI_LPS001WP_PRS) { - printk(KERN_DEBUG "%s I2C driver registered!\n", + dev_dbg(&client->dev, "%s I2C driver registered!\n", LPS001WP_PRS_DEV_NAME); } else { prs->client = NULL; - printk(KERN_DEBUG "I2C driver not registered!" + dev_dbg(&client->dev, "I2C driver not registered!" " Device unknown\n"); goto err_mutexunlockfreedata; } @@ -1135,6 +1286,9 @@ static int lps001wp_prs_probe(struct i2c_client *client, lps001wp_prs_device_power_off(prs); + if (prs->regulator) + regulator_disable(prs->regulator); + /* As default, do not report information */ atomic_set(&prs->enabled, 0); @@ -1164,9 +1318,14 @@ exit_kfree_pdata: err_mutexunlockfreedata: mutex_unlock(&prs->lock); kfree(prs); + if (prs->regulator) { + regulator_disable(prs->regulator); + regulator_put(prs->regulator); + } exit_alloc_data_failed: exit_check_functionality_failed: - printk(KERN_ERR "%s: Driver Init failed\n", LPS001WP_PRS_DEV_NAME); + dev_err(&client->dev, "%s: Driver Init failed\n", + LPS001WP_PRS_DEV_NAME); return err; } @@ -1179,6 +1338,12 @@ static int __devexit lps001wp_prs_remove(struct i2c_client *client) #endif lps001wp_prs_device_power_off(prs); remove_sysfs_interfaces(&client->dev); + if (prs->regulator) { + /* Disable the regulator if device is enabled. */ + if (atomic_read(&prs->enabled)) + regulator_disable(prs->regulator); + regulator_put(prs->regulator); + } if (prs->pdata->exit) prs->pdata->exit(); -- cgit v1.2.3 From 4b9870d69495c7f45991d858c73fd9228949a93d Mon Sep 17 00:00:00 2001 From: Philippe Langlais Date: Mon, 16 Jan 2012 09:00:34 +0100 Subject: mach-ux500: sensors: Change platform data for pressure sensor Set driver name to lps001wp_prs & poll interval to 1 sec Signed-off-by: Naga Radhesh Signed-off-by: Philippe Langlais --- arch/arm/mach-ux500/board-mop500-sensors.c | 4 ++-- include/linux/input/lps001wp.h | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/arch/arm/mach-ux500/board-mop500-sensors.c b/arch/arm/mach-ux500/board-mop500-sensors.c index eff8295e97a..006d6e408a2 100644 --- a/arch/arm/mach-ux500/board-mop500-sensors.c +++ b/arch/arm/mach-ux500/board-mop500-sensors.c @@ -39,7 +39,7 @@ static struct l3g4200d_gyr_platform_data __initdata l3g4200d_pdata_u8500 = { }; static struct lps001wp_prs_platform_data __initdata lps001wp_pdata = { - .poll_interval = 500, + .poll_interval = 1000, .min_interval = 10, }; @@ -56,7 +56,7 @@ static struct i2c_board_info __initdata mop500_i2c2_devices[] = { }, { /* LSP001WM Barometer */ - I2C_BOARD_INFO("lps001wp_prs_sysfs", 0x5C), + I2C_BOARD_INFO("lps001wp_prs", 0x5C), .platform_data = &lps001wp_pdata, }, }; diff --git a/include/linux/input/lps001wp.h b/include/linux/input/lps001wp.h index 779a415ea68..46e2fc10ede 100644 --- a/include/linux/input/lps001wp.h +++ b/include/linux/input/lps001wp.h @@ -35,7 +35,7 @@ #define LPS001WP_PRS_I2C_SADROOT 0x2E #define LPS001WP_PRS_I2C_SAD_L ((LPS001WP_PRS_I2C_SADROOT<<1)|SAD0L) #define LPS001WP_PRS_I2C_SAD_H ((LPS001WP_PRS_I2C_SADROOT<<1)|SAD0H) -#define LPS001WP_PRS_DEV_NAME "lps001wp_prs_sysfs" +#define LPS001WP_PRS_DEV_NAME "lps001wp_prs" /* input define mappings */ #define ABS_PR ABS_PRESSURE -- cgit v1.2.3 From 9935c3921e48c015f5a4e71e565c1bd8ba0348a6 Mon Sep 17 00:00:00 2001 From: Naga Radhesh Date: Fri, 16 Dec 2011 17:14:44 +0530 Subject: Documentation:add kernel-doc for pressure sensor add kernel-docs for the pressure sensor. ST-Ericsson ID: 371766 ST-Ericsson Linux next: NA ST-Ericsson FOSS-OUT ID: Trivial Change-Id:I1977bdbc13dde20ca14f50e70f0f4746367a172b Signed-off-by: Naga Radhesh Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/42897 Reviewed-by: QATOOLS Reviewed-by: QABUILD Reviewed-by: Srinidhi KASAGAR --- Documentation/DocBook/Makefile | 3 +- Documentation/DocBook/lps001wp_prs.tmpl | 89 +++++++++++++++++++++++++++++++++ include/linux/input/lps001wp.h | 9 ++++ 3 files changed, 100 insertions(+), 1 deletion(-) create mode 100644 Documentation/DocBook/lps001wp_prs.tmpl diff --git a/Documentation/DocBook/Makefile b/Documentation/DocBook/Makefile index 66725a3d30d..851250c8400 100644 --- a/Documentation/DocBook/Makefile +++ b/Documentation/DocBook/Makefile @@ -14,7 +14,8 @@ DOCBOOKS := z8530book.xml mcabook.xml device-drivers.xml \ genericirq.xml s390-drivers.xml uio-howto.xml scsi.xml \ 80211.xml debugobjects.xml sh.xml regulator.xml \ alsa-driver-api.xml writing-an-alsa-driver.xml \ - tracepoint.xml drm.xml media_api.xml + tracepoint.xml drm.xml media_api.xml \ + lps001wp_prs.xml include $(srctree)/Documentation/DocBook/media/Makefile diff --git a/Documentation/DocBook/lps001wp_prs.tmpl b/Documentation/DocBook/lps001wp_prs.tmpl new file mode 100644 index 00000000000..4b3f69ab967 --- /dev/null +++ b/Documentation/DocBook/lps001wp_prs.tmpl @@ -0,0 +1,89 @@ + + + + + + LPS001WP Pressure and temperature + + + + Matteo Dameno,Carmine Iascone + + +
+ matteo.dameno@st.com,carmine.iascone@st.com +
+
+
+
+ + + 2011 + ST-Ericsson + + + + + Linux standard functions + + + + + + License terms: GNU General Public License (GPL) version 2. + + + +
+ + + + + Introduction + + This documentation describes the pressure and temperature sensor driver for LPS001WP chip. + + + + + Known Bugs And Assumptions + + + + None + + + None. + + + + + + + + + Structures + + This chapter contains the autogenerated documentation of the structures which are + used in the pressure/temperature sensor driver. + +!Iinclude/linux/input/lps001wp.h + + + + Public Functions Provided + + This pressure/temperature drivers don't export any functions. + + + + + Internal Functions Provided + + This chapter contains the autogenerated documentation of the internal functions. + +!Idrivers/input/misc/lps001wp_prs.c + + +
diff --git a/include/linux/input/lps001wp.h b/include/linux/input/lps001wp.h index 46e2fc10ede..d83cf924048 100644 --- a/include/linux/input/lps001wp.h +++ b/include/linux/input/lps001wp.h @@ -63,6 +63,15 @@ #ifdef __KERNEL__ +/** + * struct lps001wp_prs_platform_data - platform datastructure for lps001wp_prs + * @poll_interval: maximum polling interval + * @min_interval: minimum polling interval + * @init: pointer to init function + * @exit: pointer to deinitialisation function + * @power_on: pointer to device enable function + * @power_off: pointer to device disable function + */ struct lps001wp_prs_platform_data { int poll_interval; -- cgit v1.2.3 From c71bae55dacc97bd08ac52b908d4c31c65816746 Mon Sep 17 00:00:00 2001 From: Naga Radhesh Date: Tue, 27 Dec 2011 11:48:31 +0530 Subject: lps001wp_prs:free prs struct after regulator free free the prs structure after freeing regulator ST-Ericsson ID: 401383 ST-Ericsson Linux next: NA ST-Ericsson FOSS-OUT ID: Trivial Change-Id:Iba5a833e427a83ad3e7201499f13e120545a307d Signed-off-by: Naga Radhesh Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/43851 Reviewed-by: Bibek BASU Reviewed-by: Rabin VINCENT --- drivers/input/misc/lps001wp_prs.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/input/misc/lps001wp_prs.c b/drivers/input/misc/lps001wp_prs.c index c6c29c34c45..9b1f147e1a8 100644 --- a/drivers/input/misc/lps001wp_prs.c +++ b/drivers/input/misc/lps001wp_prs.c @@ -1317,11 +1317,11 @@ exit_kfree_pdata: err_mutexunlockfreedata: mutex_unlock(&prs->lock); - kfree(prs); if (prs->regulator) { regulator_disable(prs->regulator); regulator_put(prs->regulator); } + kfree(prs); exit_alloc_data_failed: exit_check_functionality_failed: dev_err(&client->dev, "%s: Driver Init failed\n", -- cgit v1.2.3 From 95f0c2e14bd14aae093635207b56aeaf3ca566d2 Mon Sep 17 00:00:00 2001 From: Philippe Langlais Date: Wed, 15 Feb 2012 16:01:20 +0100 Subject: mach-ux500: sensors: move U8500 UIBs sensors management into board-mop500-sensors.c Signed-off-by: Philippe Langlais --- arch/arm/mach-ux500/board-mop500-sensors.c | 28 ++++++++++++++++++++++++---- 1 file changed, 24 insertions(+), 4 deletions(-) diff --git a/arch/arm/mach-ux500/board-mop500-sensors.c b/arch/arm/mach-ux500/board-mop500-sensors.c index 006d6e408a2..492d2a08aa5 100644 --- a/arch/arm/mach-ux500/board-mop500-sensors.c +++ b/arch/arm/mach-ux500/board-mop500-sensors.c @@ -15,7 +15,7 @@ #include "board-mop500.h" /* - * LSM303DLH accelerometer + magnetometer sensors + * LSM303DLH accelerometer + magnetometer & L3G4200D Gyroscope sensors */ static struct lsm303dlh_platform_data __initdata lsm303dlh_pdata = { .name_a = "lsm303dlh.0", @@ -28,7 +28,7 @@ static struct lsm303dlh_platform_data __initdata lsm303dlh_pdata = { .negative_z = 0, }; -static struct l3g4200d_gyr_platform_data __initdata l3g4200d_pdata_u8500 = { +static struct l3g4200d_gyr_platform_data __initdata l3g4200d_pdata = { .name_gyr = "l3g4200d", .axis_map_x = 1, .axis_map_y = 0, @@ -38,6 +38,10 @@ static struct l3g4200d_gyr_platform_data __initdata l3g4200d_pdata_u8500 = { .negative_z = 1, }; +/* + * Platform data for pressure sensor, + * poll interval and min interval in millseconds. + */ static struct lps001wp_prs_platform_data __initdata lps001wp_pdata = { .poll_interval = 1000, .min_interval = 10, @@ -52,7 +56,7 @@ static struct i2c_board_info __initdata mop500_i2c2_devices[] = { { /* L3G4200D Gyroscope */ I2C_BOARD_INFO("l3g4200d", 0x68), - .platform_data = &l3g4200d_pdata_u8500, + .platform_data = &l3g4200d_pdata, }, { /* LSP001WM Barometer */ @@ -177,7 +181,8 @@ static int __init mop500_sensors_init(void) { int ret; - if (!machine_is_snowball() && !uib_is_stuib() && !uib_is_u8500uibr3()) + if (!machine_is_snowball() && !uib_is_stuib() && + !uib_is_u8500uib() && !uib_is_u8500uibr3()) return 0; if (machine_is_hrefv60()) { @@ -194,6 +199,21 @@ static int __init mop500_sensors_init(void) lsm303dlh_pdata.irq_m = GPIO_MAGNET_DRDY; } + /* Special sensors data for 8500 UIBs */ + if (uib_is_u8500uib() || uib_is_u8500uibr3()) { + lsm303dlh_pdata.axis_map_x = 1; + lsm303dlh_pdata.axis_map_y = 0; + lsm303dlh_pdata.negative_x = 0; + lsm303dlh_pdata.negative_y = 0; + lsm303dlh_pdata.negative_z = 1; + + l3g4200d_pdata.axis_map_x = 0; + l3g4200d_pdata.axis_map_y = 1; + l3g4200d_pdata.negative_x = 1; + l3g4200d_pdata.negative_y = 0; + l3g4200d_pdata.negative_z = 1; + } + ret = mop500_get_acc_id(); if (ret < 0) printk(KERN_ERR " Failed to get Accelerometer chip ID\n"); -- cgit v1.2.3 From 43df7eff551fdd8b5ce0ca96a1a2db3ad6f30942 Mon Sep 17 00:00:00 2001 From: Philippe Langlais Date: Wed, 15 Feb 2012 11:09:52 +0100 Subject: lsm303dlhc: Include Signed-off-by: Philippe Langlais --- drivers/hwmon/lsm303dlhc_a.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/hwmon/lsm303dlhc_a.c b/drivers/hwmon/lsm303dlhc_a.c index 95d403b08dd..f4012442b68 100644 --- a/drivers/hwmon/lsm303dlhc_a.c +++ b/drivers/hwmon/lsm303dlhc_a.c @@ -6,6 +6,7 @@ * Licence terms: GNU General Public Licence (GPL) version 2 */ +#include #include #include #include -- cgit v1.2.3 From af4ffb547c27243bb50f2ac8862251c61817c233 Mon Sep 17 00:00:00 2001 From: Philippe Langlais Date: Wed, 15 Feb 2012 15:46:59 +0100 Subject: mach-ux500: Fix, update Chipid by reading 0x0F register Fix update ChipId by reading 0x0F register,this chipid is used in magnetometer driver to differentiate b/w lsm303dlh and lsm303dlhc device. Signed-off-by: Naga Radhesh Signed-off-by: Philippe Langlais --- arch/arm/mach-ux500/board-mop500-sensors.c | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/arch/arm/mach-ux500/board-mop500-sensors.c b/arch/arm/mach-ux500/board-mop500-sensors.c index 492d2a08aa5..e62112de204 100644 --- a/arch/arm/mach-ux500/board-mop500-sensors.c +++ b/arch/arm/mach-ux500/board-mop500-sensors.c @@ -84,7 +84,7 @@ static struct i2c_board_info __initdata mop500_2_i2c2_devices[] = { static struct i2c_board_info __initdata snowball_i2c2_devices[] = { { /* LSM303DLH Accelerometer */ - I2C_BOARD_INFO("lsm303dlh_a", 0x19), + I2C_BOARD_INFO("lsm303dlhc_a", 0x19), .platform_data = &lsm303dlh_pdata, }, }; @@ -220,13 +220,9 @@ static int __init mop500_sensors_init(void) else lsm303dlh_pdata.chip_id = ret; - mop500_sensors_i2c_add(2, mop500_i2c2_devices, - ARRAY_SIZE(mop500_i2c2_devices)); - if (machine_is_snowball()) { if (cpu_is_u8500v21()) - /* This is ugly but we cant know what address - * to use */ + /* This is ugly but we cant know what address to use */ mop500_sensors_probe_add_lsm303dlh_a(); else /* Add the accelerometer with new addr */ mop500_sensors_i2c_add(2, snowball_i2c2_devices, @@ -234,6 +230,9 @@ static int __init mop500_sensors_init(void) } else /* none snowball have the old addr */ mop500_sensors_i2c_add(2, mop500_2_i2c2_devices, ARRAY_SIZE(mop500_2_i2c2_devices)); + + mop500_sensors_i2c_add(2, mop500_i2c2_devices, + ARRAY_SIZE(mop500_i2c2_devices)); return 0; } -- cgit v1.2.3 From 22ba5f9e665f148206927e4626ca84c00409b8ca Mon Sep 17 00:00:00 2001 From: Philippe Langlais Date: Tue, 14 Feb 2012 07:46:48 +0100 Subject: lsm303dlh: add support for Android early suspend Signed-off-by: Philippe Langlais --- drivers/hwmon/lsm303dlh_a.c | 13 ++++++++++--- drivers/hwmon/lsm303dlh_m.c | 10 ++++++++-- 2 files changed, 18 insertions(+), 5 deletions(-) diff --git a/drivers/hwmon/lsm303dlh_a.c b/drivers/hwmon/lsm303dlh_a.c index 84a4a76b9ab..a6e724facc3 100644 --- a/drivers/hwmon/lsm303dlh_a.c +++ b/drivers/hwmon/lsm303dlh_a.c @@ -38,6 +38,9 @@ #endif #include +#ifdef CONFIG_HAS_EARLYSUSPEND +#include +#endif #include /* lsm303dlh accelerometer registers */ @@ -199,6 +202,9 @@ struct lsm303dlh_a_data { unsigned char interrupt_configure[2]; unsigned char interrupt_duration[2]; unsigned char interrupt_threshold[2]; +#ifdef CONFIG_HAS_EARLYSUSPEND + struct early_suspend early_suspend; +#endif int device_status; int id; }; @@ -289,6 +295,7 @@ static int lsm303dlh_a_restore(struct lsm303dlh_a_data *ddata) if (ddata->regulator) regulator_enable(ddata->regulator); + /* BDU should be enabled by default/recommened */ reg = ddata->range; reg |= LSM303DLH_A_CR4_BDU_MASK; @@ -1340,9 +1347,9 @@ static struct i2c_driver lsm303dlh_a_driver = { .id_table = lsm303dlh_a_id, .driver = { .name = "lsm303dlh_a", - #if (!defined(CONFIG_HAS_EARLYSUSPEND) && defined(CONFIG_PM)) - .pm = &lsm303dlh_a_dev_pm_ops, - #endif +#if (!defined(CONFIG_HAS_EARLYSUSPEND) && defined(CONFIG_PM)) + .pm = &lsm303dlh_a_dev_pm_ops, +#endif }, }; diff --git a/drivers/hwmon/lsm303dlh_m.c b/drivers/hwmon/lsm303dlh_m.c index 52f71ae6e96..11815e3470f 100644 --- a/drivers/hwmon/lsm303dlh_m.c +++ b/drivers/hwmon/lsm303dlh_m.c @@ -39,6 +39,9 @@ #include #include +#ifdef CONFIG_HAS_EARLYSUSPEND +#include +#endif #include /* lsm303dlh magnetometer registers */ @@ -164,6 +167,9 @@ struct lsm303dlh_m_data { unsigned char mode; unsigned char rate; unsigned char range; +#ifdef CONFIG_HAS_EARLYSUSPEND + struct early_suspend early_suspend; +#endif int device_status; }; @@ -894,9 +900,9 @@ static struct i2c_driver lsm303dlh_m_driver = { .id_table = lsm303dlh_m_id, .driver = { .name = "lsm303dlh_m", - #if (!defined(CONFIG_HAS_EARLYSUSPEND) && defined(CONFIG_PM)) +#if (!defined(CONFIG_HAS_EARLYSUSPEND) && defined(CONFIG_PM)) .pm = &lsm303dlh_m_dev_pm_ops, - #endif +#endif }, }; -- cgit v1.2.3 From 4655b9c95ddc2e087eec81cd4b05a072f09f2f23 Mon Sep 17 00:00:00 2001 From: Naga RADHESH Y Date: Tue, 14 Feb 2012 07:47:04 +0100 Subject: Android:lps001wp: add early suspend,late resume add early suspend,late resume functionlity for pressure sensor. ST-Ericsson ID: 371766 ST-Ericsson Linux next: NA ST-Ericsson FOSS-OUT ID: Trivial Change-Id:If517adc3bbef3d203a9be2af946c3264cb35b265 Signed-off-by: Naga Radhesh --- drivers/input/misc/lps001wp_prs.c | 58 +++++++++++++++++++++++++++++++-------- 1 file changed, 47 insertions(+), 11 deletions(-) diff --git a/drivers/input/misc/lps001wp_prs.c b/drivers/input/misc/lps001wp_prs.c index 9b1f147e1a8..1618dfaa16f 100644 --- a/drivers/input/misc/lps001wp_prs.c +++ b/drivers/input/misc/lps001wp_prs.c @@ -49,12 +49,12 @@ #include #include - - +#ifdef CONFIG_HAS_EARLYSUSPEND +#include +#endif #define DEBUG 1 - #define PR_ABS_MAX 0xffff #define PR_ABS_MIN 0x0000 #define PR_DLT_MAX 0x7ffff @@ -171,6 +171,9 @@ struct lps001wp_prs_data { struct delayed_work input_work; struct input_dev *input_dev; #endif +#ifdef CONFIG_HAS_EARLYSUSPEND + struct early_suspend early_suspend; +#endif int hw_initialized; /* hw_working=-1 means not tested yet */ @@ -196,6 +199,11 @@ struct outputdata { s16 deltapress; }; +#ifdef CONFIG_HAS_EARLYSUSPEND +static void lps001wp_prs_early_suspend(struct early_suspend *data); +static void lps001wp_prs_late_resume(struct early_suspend *data); +#endif + static int lps001wp_prs_i2c_read(struct lps001wp_prs_data *prs, u8 *buf, int len) @@ -1282,6 +1290,13 @@ static int lps001wp_prs_probe(struct i2c_client *client, "device LPS001WP_PRS_DEV_NAME sysfs register failed\n"); goto err_input_cleanup; } +#ifdef CONFIG_HAS_EARLYSUSPEND + prs->early_suspend.level = + EARLY_SUSPEND_LEVEL_BLANK_SCREEN + 1; + prs->early_suspend.suspend = lps001wp_prs_early_suspend; + prs->early_suspend.resume = lps001wp_prs_late_resume; + register_early_suspend(&prs->early_suspend); +#endif lps001wp_prs_device_power_off(prs); @@ -1356,24 +1371,44 @@ static int __devexit lps001wp_prs_remove(struct i2c_client *client) return 0; } - -static int lps001wp_prs_resume(struct i2c_client *client) +#if (!defined(CONFIG_HAS_EARLYSUSPEND) && defined(CONFIG_PM)) +static int lps001wp_prs_resume(struct device *dev) { - struct lps001wp_prs_data *prs = i2c_get_clientdata(client); + struct lps001wp_prs_data *prs = dev_get_drvdata(dev); if (prs->on_before_suspend) return lps001wp_prs_enable(prs); return 0; } -static int lps001wp_prs_suspend(struct i2c_client *client, pm_message_t mesg) +static int lps001wp_prs_suspend(struct device *dev) { - struct lps001wp_prs_data *prs = i2c_get_clientdata(client); - + struct lps001wp_prs_data *prs = dev_get_drvdata(dev); prs->on_before_suspend = atomic_read(&prs->enabled); return lps001wp_prs_disable(prs); } +static const struct dev_pm_ops lps001wp_prs_dev_pm_ops = { + .suspend = lps001wp_prs_suspend, + .resume = lps001wp_prs_resume, +}; +#else +static void lps001wp_prs_early_suspend(struct early_suspend *data) +{ + struct lps001wp_prs_data *prs = + container_of(data, struct lps001wp_prs_data, early_suspend); + prs->on_before_suspend = atomic_read(&prs->enabled); + lps001wp_prs_disable(prs); +} + +static void lps001wp_prs_late_resume(struct early_suspend *data) +{ + struct lps001wp_prs_data *prs = + container_of(data, struct lps001wp_prs_data, early_suspend); + if (prs->on_before_suspend) + lps001wp_prs_enable(prs); +} +#endif static const struct i2c_device_id lps001wp_prs_id[] = { { LPS001WP_PRS_DEV_NAME, 0}, { },}; @@ -1383,12 +1418,13 @@ static struct i2c_driver lps001wp_prs_driver = { .driver = { .name = LPS001WP_PRS_DEV_NAME, .owner = THIS_MODULE, +#if (!defined(CONFIG_HAS_EARLYSUSPEND) && defined(CONFIG_PM)) + .pm = &lps001wp_prs_dev_pm_ops, +#endif }, .probe = lps001wp_prs_probe, .remove = __devexit_p(lps001wp_prs_remove), .id_table = lps001wp_prs_id, - .resume = lps001wp_prs_resume, - .suspend = lps001wp_prs_suspend, }; static int __init lps001wp_prs_init(void) -- cgit v1.2.3 From 654b71a446e238a8fb1c80749ed083f9b4d74e82 Mon Sep 17 00:00:00 2001 From: Benn Pörscke Date: Mon, 20 Feb 2012 09:59:07 +0100 Subject: input:lps001wp_prs: Correct sysfs file permissions --- drivers/input/misc/lps001wp_prs.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/input/misc/lps001wp_prs.c b/drivers/input/misc/lps001wp_prs.c index 1618dfaa16f..45a322729ff 100644 --- a/drivers/input/misc/lps001wp_prs.c +++ b/drivers/input/misc/lps001wp_prs.c @@ -994,7 +994,7 @@ static ssize_t attr_addr_set(struct device *dev, struct device_attribute *attr, static struct device_attribute attributes[] = { __ATTR(pollrate_ms, S_IWUSR | S_IRUGO, attr_get_polling_rate, attr_set_polling_rate), - __ATTR(enable, S_IWUSR | S_IRUGO, attr_get_enable, attr_set_enable), + __ATTR(enable, S_IWUGO | S_IRUGO, attr_get_enable, attr_set_enable), __ATTR(diff_enable, S_IWUSR | S_IRUGO, attr_get_diff_enable, attr_set_diff_enable), __ATTR(press_reference, S_IWUSR | S_IRUGO, attr_get_press_ref, -- cgit v1.2.3 From ba644aea2dd062c6ce7fab6a0a2e825ed3bc1f6e Mon Sep 17 00:00:00 2001 From: srinidhi kasagar Date: Thu, 12 Jan 2012 17:50:01 +0530 Subject: hwmon: l3g4200,lsm303x: fix up the error handling handle the errors properly. ST-Ericsson ID: 370799 ST-Ericsson FOSS-OUT ID: Trivial ST-Ericsson Linux next: N/A Signed-off-by: srinidhi kasagar Change-Id: I74d3f2e159bba9847fc4aab6287d23d38fb722e8 Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/45272 Reviewed-by: QATOOLS Reviewed-by: Naga RADHESH Y Reviewed-by: QABUILD --- drivers/hwmon/l3g4200d.c | 4 +++- drivers/hwmon/lsm303dlh_a.c | 6 +++++- drivers/hwmon/lsm303dlh_m.c | 10 +++++++--- drivers/hwmon/lsm303dlhc_a.c | 6 +++++- 4 files changed, 20 insertions(+), 6 deletions(-) diff --git a/drivers/hwmon/l3g4200d.c b/drivers/hwmon/l3g4200d.c index ffe3e1a9730..ad7d9ee6e05 100644 --- a/drivers/hwmon/l3g4200d.c +++ b/drivers/hwmon/l3g4200d.c @@ -458,8 +458,9 @@ static int __devinit l3g4200d_probe(struct i2c_client *client, ddata = kzalloc(sizeof(struct l3g4200d_data), GFP_KERNEL); if (ddata == NULL) { + dev_err(&client->dev, "memory alocation failed\n"); ret = -ENOMEM; - goto error_op_failed; + goto exit; } ddata->client = client; @@ -479,6 +480,7 @@ static int __devinit l3g4200d_probe(struct i2c_client *client, dev_err(&client->dev, "failed to get regulator\n"); ret = PTR_ERR(ddata->regulator); ddata->regulator = NULL; + goto error_op_failed; } if (ddata->regulator) { diff --git a/drivers/hwmon/lsm303dlh_a.c b/drivers/hwmon/lsm303dlh_a.c index a6e724facc3..22600c550f9 100644 --- a/drivers/hwmon/lsm303dlh_a.c +++ b/drivers/hwmon/lsm303dlh_a.c @@ -1083,8 +1083,9 @@ static int __devinit lsm303dlh_a_probe(struct i2c_client *client, ddata = kzalloc(sizeof(struct lsm303dlh_a_data), GFP_KERNEL); if (ddata == NULL) { + dev_err(&client->dev, "memory alocation failed\n"); ret = -ENOMEM; - goto err_op_failed; + goto err_alloc; } ddata->client = client; @@ -1105,6 +1106,7 @@ static int __devinit lsm303dlh_a_probe(struct i2c_client *client, dev_err(&client->dev, "failed to get regulator\n"); ret = PTR_ERR(ddata->regulator); ddata->regulator = NULL; + goto err_op_failed; } if (ddata->regulator) { @@ -1237,8 +1239,10 @@ exit_free_regulator: regulator_put(ddata->regulator); ddata->device_status = DEVICE_OFF; } + err_op_failed: kfree(ddata); +err_alloc: dev_err(&client->dev, "probe function fails %x", ret); return ret; } diff --git a/drivers/hwmon/lsm303dlh_m.c b/drivers/hwmon/lsm303dlh_m.c index 11815e3470f..270e532f78b 100644 --- a/drivers/hwmon/lsm303dlh_m.c +++ b/drivers/hwmon/lsm303dlh_m.c @@ -671,8 +671,9 @@ static int __devinit lsm303dlh_m_probe(struct i2c_client *client, ddata = kzalloc(sizeof(struct lsm303dlh_m_data), GFP_KERNEL); if (ddata == NULL) { + dev_err(&client->dev, "memory alocation failed\n"); ret = -ENOMEM; - goto err_op_failed; + goto err_alloc; } ddata->client = client; @@ -691,12 +692,13 @@ static int __devinit lsm303dlh_m_probe(struct i2c_client *client, ddata->gain[ddata->pdata.axis_map_z] = Z_GAIN_1_3; ddata->device_status = DEVICE_OFF; dev_set_name(&client->dev, ddata->pdata.name_m); - ddata->regulator = regulator_get(&client->dev, "vdd"); + ddata->regulator = regulator_get(&client->dev, "vdd"); if (IS_ERR(ddata->regulator)) { dev_err(&client->dev, "failed to get regulator\n"); ret = PTR_ERR(ddata->regulator); ddata->regulator = NULL; + goto err_op_failed; } if (ddata->regulator) { @@ -790,9 +792,11 @@ exit_free_regulator: regulator_put(ddata->regulator); ddata->device_status = DEVICE_OFF; } + err_op_failed: - dev_err(&client->dev, "lsm303dlh_m_probe failed %x", ret); kfree(ddata); +err_alloc: + dev_err(&client->dev, "lsm303dlh_m_probe failed %x", ret); return ret; } diff --git a/drivers/hwmon/lsm303dlhc_a.c b/drivers/hwmon/lsm303dlhc_a.c index f4012442b68..cf00ce9d50b 100644 --- a/drivers/hwmon/lsm303dlhc_a.c +++ b/drivers/hwmon/lsm303dlhc_a.c @@ -506,8 +506,9 @@ static int __devinit lsm303dlhc_a_probe(struct i2c_client *client, adata = kzalloc(sizeof(struct lsm303dlhc_a_data), GFP_KERNEL); if (adata == NULL) { + dev_err(&client->dev, "memory alocation failed\n"); ret = -ENOMEM; - goto err_op_failed; + goto err_alloc; } adata->client = client; @@ -526,6 +527,7 @@ static int __devinit lsm303dlhc_a_probe(struct i2c_client *client, dev_err(&client->dev, "failed to get regulator\n"); ret = PTR_ERR(adata->regulator); adata->regulator = NULL; + goto err_op_failed; } if (adata->regulator) { @@ -573,8 +575,10 @@ exit_free_regulator: regulator_put(adata->regulator); adata->device_status = DEVICE_OFF; } + err_op_failed: kfree(adata); +err_alloc: dev_err(&client->dev, "probe function fails %x", ret); return ret; } -- cgit v1.2.3 From a51c334ac1571b89e79d9dd147b34a138de07293 Mon Sep 17 00:00:00 2001 From: Naga Radhesh Date: Tue, 21 Feb 2012 18:55:14 +0530 Subject: l3g4200d: change L3G4200D_FS_BIT value In CTRL_R4, bit 5 and bit 6 are used to set fullscale value.so L3G4200D_FS_BIT should be 4. ST-Ericsson ID: 413619 ST-Ericsson Linux next: NA ST-Ericsson FOSS-OUT ID: Trivial Signed-off-by: Naga Radhesh Change-Id: Idf7c00d5eaaeb1eda53b9ef311080b1d5d4c228b Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/49760 Reviewed-by: QATOOLS Reviewed-by: QABUILD Reviewed-by: QATEST Reviewed-by: Robert LIND Reviewed-by: Srinidhi KASAGAR --- drivers/hwmon/l3g4200d.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/hwmon/l3g4200d.c b/drivers/hwmon/l3g4200d.c index ad7d9ee6e05..d3e0b46b169 100644 --- a/drivers/hwmon/l3g4200d.c +++ b/drivers/hwmon/l3g4200d.c @@ -62,7 +62,7 @@ #define FS500 0x01 #define FS2000 0x03 #define BDU_ENABLE 0x80 -#define L3G4200D_FS_BIT 6 +#define L3G4200D_FS_BIT 4 #define L3G4200D_FS_MASK (0x3 << L3G4200D_FS_BIT) /* multiple byte transfer enable */ -- cgit v1.2.3 From 56bde254da35de0eea481035bfcd50dbec3ea22f Mon Sep 17 00:00:00 2001 From: Philippe Langlais Date: Tue, 6 Mar 2012 13:17:02 +0100 Subject: sensor: board: Add U8520 & U9540 machine support Signed-off-by: Philippe Langlais --- arch/arm/mach-ux500/board-mop500-sensors.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/arm/mach-ux500/board-mop500-sensors.c b/arch/arm/mach-ux500/board-mop500-sensors.c index e62112de204..8569cf9248a 100644 --- a/arch/arm/mach-ux500/board-mop500-sensors.c +++ b/arch/arm/mach-ux500/board-mop500-sensors.c @@ -185,7 +185,7 @@ static int __init mop500_sensors_init(void) !uib_is_u8500uib() && !uib_is_u8500uibr3()) return 0; - if (machine_is_hrefv60()) { + if (machine_is_hrefv60() || machine_is_u8520() || machine_is_u9540()) { lsm303dlh_pdata.irq_a1 = HREFV60_ACCEL_INT1_GPIO; lsm303dlh_pdata.irq_a2 = HREFV60_ACCEL_INT2_GPIO; lsm303dlh_pdata.irq_m = HREFV60_MAGNET_DRDY_GPIO; -- cgit v1.2.3