hwmon: lps331ap: Add lps331ap baro sensor for cvt
authorjli127 <jian.d.li@intel.com>
Thu, 2 Feb 2012 00:53:24 +0000 (08:53 +0800)
committerbuildbot <buildbot@intel.com>
Thu, 16 Feb 2012 01:57:35 +0000 (17:57 -0800)
BZ: 22002

Add lps331ap baro sensor for Clover Trail platform.

Change-Id: I3e811fc403ddff5aeb7a7589b4ea639ec762a3a7
Signed-off-by: jli127 <jian.d.li@intel.com>
Reviewed-on: http://android.intel.com:8080/33633
Reviewed-by: Liu, Hong <hong.liu@intel.com>
Reviewed-by: Du, Alek <alek.du@intel.com>
Reviewed-by: Koskinen, Ilkka <ilkka.koskinen@intel.com>
Tested-by: Wang, Zhifeng <zhifeng.wang@intel.com>
Reviewed-by: buildbot <buildbot@intel.com>
Tested-by: buildbot <buildbot@intel.com>
arch/x86/platform/intel-mid/board-blackbay.c
drivers/hwmon/Kconfig
drivers/hwmon/Makefile
drivers/hwmon/lps331ap.c [new file with mode: 0644]

index 2b774d2..de621ac 100644 (file)
@@ -1641,7 +1641,7 @@ struct devs_id __initconst device_ids[] = {
        {"lsm303dl", SFI_DEV_TYPE_I2C, 0, &lsm303dlhc_accel_platform_data},
        {"lsm303cmp", SFI_DEV_TYPE_I2C, 0, &no_platform_data},
        {"l3g4200d", SFI_DEV_TYPE_I2C, 0, &l3g4200d_platform_data},
-
+       {"lps331ap", SFI_DEV_TYPE_I2C, 0, &no_platform_data},
        {},
 };
 
index 35452f9..c189230 100644 (file)
@@ -1423,6 +1423,16 @@ config SENSORS_MS5607
          If you say yes here you get support for the Measurement MS5607 digital
          pressure sensor.
 
+config SENSORS_LPS331AP
+       bool "LPS331AP pressure sensor Driver"
+       depends on I2C && INPUT
+       default y
+       help
+          Say yes here to enable LPS331AP pressure sensor driver
+
+          Pressure sensor LPS331AP from STMicroelectronics
+          please search on
+          http://www.st.com/ for more details.
 
 config MSIC_GPADC
        tristate "MSIC GPADC driver for Intel Medfield platform"
index 64f2e43..5140dd0 100644 (file)
@@ -124,6 +124,7 @@ obj-$(CONFIG_SENSORS_HMC5883)           += hmc5883.o
 obj-$(CONFIG_SENSORS_MPU3050)          += mpu3050.o
 obj-$(CONFIG_SENSORS_MS5607)           += ms5607.o
 obj-$(CONFIG_SENSORS_L3G4200D_POLL)    += l3g4200d_poll.o
+obj-$(CONFIG_SENSORS_LPS331AP)         += lps331ap.o
 obj-$(CONFIG_MSIC_GPADC)       += intel_mid_gpadc.o
 obj-$(CONFIG_SENSORS_MID_CURRENT)      += intel_mid_ocd.o
 obj-$(CONFIG_SENSORS_LSM303_MAG)   += lsm303dlhc_compass.o
diff --git a/drivers/hwmon/lps331ap.c b/drivers/hwmon/lps331ap.c
new file mode 100644 (file)
index 0000000..c0086a6
--- /dev/null
@@ -0,0 +1,559 @@
+/*
+ * lps331ap.c - ST LPS331AP Pressure Sensor Driver
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include <linux/i2c.h>
+#include <linux/input.h>
+#include <linux/workqueue.h>
+#include <linux/slab.h>
+#include <linux/earlysuspend.h>
+
+#define REF_P_XL       0x08
+#define REF_P_L                0x09
+#define REF_P_H                0x0A
+#define REF_T_L                0x0B
+#define REF_T_H                0x0C
+#define WHO_AM_I       0x0F
+
+#define RES_CONF       0x10
+
+#define CTRL_REG1      0x20
+#define POWER_MODE     7
+#define POWER_DOWN     (0 << POWER_MODE)
+#define POWER_ON       (1 << POWER_MODE)
+#define ODR_SELECT     4
+#define ODR0           (0 << ODR_SELECT)
+#define ODR1           (1 << ODR_SELECT)
+#define ODR2           (2 << ODR_SELECT)
+#define ODR3           (3 << ODR_SELECT)
+#define ODR4           (4 << ODR_SELECT)
+#define ODR5           (5 << ODR_SELECT)
+#define ODR6           (6 << ODR_SELECT)
+#define ODR7           (7 << ODR_SELECT)
+#define DIFF_EN                3
+#define INT_CIRCUIT_ENABLE     (1 << DIFF_EN)
+#define INT_CIRCUIT_DISABLE    (0 << DIFF_EN)
+#define BDU                    2
+#define CONT_DATA_UPDATE       (0 << BDU)
+#define BLOCK_DATA_UPDATE      (1 << BDU)
+#define DELTA_EN       1
+#define DELTA_P_ENABLE         (1 << DELTA_EN)
+#define DELTA_P_DISABLE                (0 << DELTA_EN)
+#define SIM_SELECT     0
+#define WIRE_INTER_4           (0 << SIM_SELECT)
+#define WIRE_INTER_3           (1 << SIM_SELECT)
+
+#define CTRL_REG2      0x21
+#define BOOT           0x80
+#define SWRESET                0x04
+#define AUTO_ZERO      0x02
+#define ONE_SHOT       0x01
+
+#define CTRL_REG3      0x22
+#define INT_H_L                7
+#define INT_ACTIVE_H   (0 << INT_H_L)
+#define INT_ACTIVE_L   (1 << INT_H_L)
+#define PP_OD          6
+#define PUSH_PULL              (0 << PP_OD)
+#define OPEN_DRAIN             (1 << PP_OD)
+#define INT2_CTRL      3
+#define INT2_P_HIGH            (1 << INT2_CTRL)
+#define INT2_P_LOW             (2 << INT2_CTRL)
+#define INT2_P_H_L             (3 << INT2_CTRL)
+#define INT2_DATARDY   (4 << INT2_CTRL)
+#define INT1_CTRL      0
+#define INT1_P_HIGH            (1 << INT1_CTRL)
+#define INT1_P_LOW             (2 << INT1_CTRL)
+#define INT1_P_H_L             (3 << INT1_CTRL)
+#define INT1_DATARDY   (4 << INT1_CTRL)
+
+
+#define INT_CFG_REG    0x23
+#define INT_CFG_LIR    0x04
+#define INT_CFG_PL_E   0x02
+#define INT_CFG_PH_E   0x01
+
+#define INT_SRC_REG    0x24
+#define INT_SRC_IA     0x04
+#define INT_SRC_PL     0x02
+#define INT_SRC_PH     0x01
+
+#define THS_P_LOW_REG  0x25
+#define THS_P_HIGH_REG 0x26
+
+#define STATUS_REG     0x27
+#define STATUS_P_DA    0x02
+#define STATUS_T_DA    0x01
+#define STATUS_P_OR    0x20
+#define STATUS_T_OR    0x10
+
+#define I2C_AUTO_INC   0x80
+#define PRESS_OUT_XL   0x28
+#define PRESS_OUT_L    0x29
+#define PRESS_OUT_H    0x2A
+
+#define TEMP_OUT_L     0x2B
+#define TEMP_OUT_H     0x2C
+
+#define AMP_CTRL       0x30
+
+#define LPS331AP_DEFAULT_DELAY 200
+#define LPS331AP_MIN_DELAY     75
+
+#define LPS331AP_DEVICE_NAME "lps331ap"
+
+struct lps331ap_data {
+       struct i2c_client *client;
+       /*
+        * This mutex protect lps331ap from data race condition.
+        * Any function need to conmmunicate with lps331ap device
+        * or to set lps331ap delay_ms and enabled state should acquire
+        * this lock first.
+        */
+       struct mutex lock;
+
+       struct delayed_work input_work;
+
+       struct input_dev *input_dev;
+       struct early_suspend es;
+
+       int delay_ms;
+       int enabled;
+       int need_resume;
+};
+
+static int lps331ap_read(struct lps331ap_data *lps331ap, u8 reg, u8 *val)
+{
+       int ret;
+
+       ret = i2c_smbus_read_byte_data(lps331ap->client, reg);
+       if (ret >= 0) {
+               *val = ret;
+               ret = 0;
+       }
+
+       return ret;
+}
+
+static int lps331ap_write(struct lps331ap_data *lps331ap, u8 reg, u8 val)
+{
+       return i2c_smbus_write_byte_data(lps331ap->client, reg, val);
+}
+
+static void lps331ap_get_data(struct lps331ap_data *lps331ap,
+               unsigned long *data)
+{
+       int err;
+       u8 buf[3];
+
+       err = i2c_smbus_read_i2c_block_data(lps331ap->client,
+                       PRESS_OUT_XL | I2C_AUTO_INC, 3, buf);
+       if (err < 0)
+               return;
+
+       data[0] = 65536 * buf[2];
+       data[0] += 256 * buf[1];
+       data[0] += buf[0];
+
+       err = i2c_smbus_read_i2c_block_data(lps331ap->client,
+                       TEMP_OUT_L | I2C_AUTO_INC, 2, buf);
+       if (err < 0)
+               return;
+
+       data[1] = 256 * buf[1];
+       data[1] += buf[0];
+}
+
+static void lps331ap_report_values(struct lps331ap_data *lps331ap,
+               unsigned long *data)
+{
+       input_report_rel(lps331ap->input_dev, REL_X, data[0]);
+       input_report_rel(lps331ap->input_dev, REL_Y, data[1]);
+
+       input_sync(lps331ap->input_dev);
+}
+
+static int lps331ap_initchip(struct lps331ap_data *lps331ap)
+{
+       int ret;
+
+       ret = lps331ap_write(lps331ap, RES_CONF, 0x34);
+       if (ret < 0)
+               return ret;
+
+       ret = lps331ap_write(lps331ap, CTRL_REG1, POWER_DOWN);
+       if (ret < 0)
+               return ret;
+
+       ret = lps331ap_write(lps331ap, CTRL_REG1,
+                       POWER_DOWN | ODR7 | INT_CIRCUIT_DISABLE
+                       | BLOCK_DATA_UPDATE     | DELTA_P_DISABLE);
+       return ret;
+}
+
+static void lps331ap_power_off(struct lps331ap_data *lps331ap)
+{
+       int err;
+       u8 val;
+
+       err = lps331ap_read(lps331ap, CTRL_REG1, &val);
+       if (err < 0) {
+               dev_err(&lps331ap->client->dev, "power off failed: %d\n", err);
+               return;
+       }
+
+       val &= ~POWER_ON;
+       err = lps331ap_write(lps331ap, CTRL_REG1, val);
+       if (err < 0)
+               dev_err(&lps331ap->client->dev, "power off failed: %d\n", err);
+}
+
+static int lps331ap_power_on(struct lps331ap_data *lps331ap)
+{
+       int err;
+       u8 val;
+
+       err = lps331ap_read(lps331ap, CTRL_REG1, &val);
+       if (err < 0) {
+               dev_err(&lps331ap->client->dev, "power on failed: %d\n", err);
+               return err;
+       }
+
+       val |= POWER_ON;
+       err = lps331ap_write(lps331ap, CTRL_REG1, val);
+       if (err < 0)
+               dev_err(&lps331ap->client->dev, "power on failed: %d\n", err);
+
+       return err;
+}
+
+static void lps331ap_enable(struct lps331ap_data *lps331ap)
+{
+       lps331ap->enabled = 1;
+       lps331ap_power_on(lps331ap);
+       schedule_delayed_work(&lps331ap->input_work,
+                       msecs_to_jiffies(lps331ap->delay_ms));
+}
+
+static void lps331ap_disable(struct lps331ap_data *lps331ap)
+{
+       lps331ap->enabled = 0;
+       cancel_delayed_work(&lps331ap->input_work);
+       lps331ap_power_off(lps331ap);
+}
+
+static void lps331ap_input_work_func(struct work_struct *work)
+{
+       struct lps331ap_data *lps331ap;
+       unsigned long pt[2] = { 0 };
+
+       lps331ap = container_of((struct delayed_work *)work,
+                       struct lps331ap_data, input_work);
+
+       mutex_lock(&lps331ap->lock);
+
+       if (!lps331ap->enabled)
+               goto out;
+
+       lps331ap_get_data(lps331ap, pt);
+       lps331ap_report_values(lps331ap, pt);
+
+       schedule_delayed_work(&lps331ap->input_work,
+                       msecs_to_jiffies(lps331ap->delay_ms));
+out:
+       mutex_unlock(&lps331ap->lock);
+}
+
+static int lps331ap_input_init(struct lps331ap_data *lps331ap)
+{
+       int err;
+       struct input_dev *input;
+
+       INIT_DELAYED_WORK(&lps331ap->input_work, lps331ap_input_work_func);
+       input = input_allocate_device();
+       if (!input) {
+               dev_err(&lps331ap->client->dev, "input device allocate failed\n");
+               return -ENOMEM;
+       }
+
+       input->name = "lps331ap_pressure";
+       input->id.bustype = BUS_I2C;
+       input->dev.parent = &lps331ap->client->dev;
+       input_set_drvdata(input, lps331ap);
+
+       set_bit(EV_REL, input->evbit);
+       set_bit(REL_X, input->relbit);
+       set_bit(REL_Y, input->relbit);
+
+       err = input_register_device(input);
+       if (err) {
+               dev_err(&lps331ap->client->dev,
+                               "unable to register input device %s: %d\n",
+                               input->name, err);
+               goto err1;
+       }
+
+       lps331ap->input_dev = input;
+       return 0;
+err1:
+       input_free_device(input);
+       return err;
+}
+
+/* sysfs */
+static ssize_t lps331ap_delay_show(struct device *dev,
+               struct device_attribute *attr, char *buf)
+{
+       struct i2c_client *client = to_i2c_client(dev);
+       struct lps331ap_data *lps331ap = i2c_get_clientdata(client);
+       int delay_ms;
+
+       mutex_lock(&lps331ap->lock);
+       delay_ms = lps331ap->delay_ms;
+       mutex_unlock(&lps331ap->lock);
+
+       return sprintf(buf, "%d\n", delay_ms);
+}
+
+static ssize_t lps331ap_delay_store(struct device *dev,
+               struct device_attribute *attr, const char *buf, size_t count)
+{
+       unsigned long polltime;
+       struct i2c_client *client = to_i2c_client(dev);
+       struct lps331ap_data *lps331ap = i2c_get_clientdata(client);
+
+       if (strict_strtoul(buf, 10, &polltime))
+               return -EINVAL;
+
+       polltime = (polltime > LPS331AP_MIN_DELAY) ?
+                       polltime : LPS331AP_MIN_DELAY;
+
+       mutex_lock(&lps331ap->lock);
+       lps331ap->delay_ms = polltime;
+       mutex_unlock(&lps331ap->lock);
+
+       return count;
+}
+
+static ssize_t lps331ap_enable_show(struct device *dev,
+               struct device_attribute *attr, char *buf)
+{
+       struct i2c_client *client = to_i2c_client(dev);
+       struct lps331ap_data *lps331ap = i2c_get_clientdata(client);
+       int enabled;
+
+       mutex_lock(&lps331ap->lock);
+       enabled = lps331ap->enabled;
+       mutex_unlock(&lps331ap->lock);
+
+       return sprintf(buf, "%d\n", lps331ap->enabled);
+}
+
+#define LPS331AP_SYSFS_POWERON         1
+#define LPS331AP_SYSFS_POWERDOWN       0
+
+static ssize_t lps331ap_enable_store(struct device *dev,
+               struct device_attribute *attr, const char *buf, size_t count)
+{
+       struct i2c_client *client = to_i2c_client(dev);
+       struct lps331ap_data *lps331ap = i2c_get_clientdata(client);
+       unsigned long val;
+
+       if (strict_strtoul(buf, 10, &val))
+               return -EINVAL;
+
+       if (val != LPS331AP_SYSFS_POWERON && val != LPS331AP_SYSFS_POWERDOWN)
+               return -EINVAL;
+
+       mutex_lock(&lps331ap->lock);
+       if (val && !lps331ap->enabled)
+               lps331ap_enable(lps331ap);
+       else if (!val && lps331ap->enabled)
+               lps331ap_disable(lps331ap);
+       else
+               dev_dbg(&client->dev, "invalid power switch\n");
+
+       mutex_unlock(&lps331ap->lock);
+
+       return count;
+}
+
+static DEVICE_ATTR(poll, S_IRUGO|S_IWUSR, lps331ap_delay_show,
+               lps331ap_delay_store);
+static DEVICE_ATTR(enable, S_IRUGO|S_IWUSR, lps331ap_enable_show,
+               lps331ap_enable_store);
+
+static struct attribute *lps331ap_attributes[] = {
+       &dev_attr_poll.attr,
+       &dev_attr_enable.attr,
+       NULL
+};
+
+static struct attribute_group lps331ap_attribute_group = {
+       .attrs = lps331ap_attributes
+};
+
+#ifdef CONFIG_HAS_EARLYSUSPEND
+static void lps331ap_early_suspend(struct early_suspend *h)
+{
+       struct lps331ap_data *lps331ap =
+               container_of(h, struct lps331ap_data, es);
+
+       mutex_lock(&lps331ap->lock);
+       lps331ap->need_resume = lps331ap->enabled;
+       if (lps331ap->enabled)
+               lps331ap_disable(lps331ap);
+       mutex_unlock(&lps331ap->lock);
+}
+
+static void lps331ap_late_resume(struct early_suspend *h)
+{
+       struct lps331ap_data *lps331ap =
+               container_of(h, struct lps331ap_data, es);
+
+       mutex_lock(&lps331ap->lock);
+       if (lps331ap->need_resume)
+               lps331ap_enable(lps331ap);
+       mutex_unlock(&lps331ap->lock);
+}
+#endif /* CONFIG_HAS_EARLYSUSPEND */
+
+static int __devinit lps331ap_probe(struct i2c_client *client,
+               const struct i2c_device_id *id)
+{
+       int err;
+       struct lps331ap_data *lps331ap;
+
+       if (!i2c_check_functionality(client->adapter,
+                               I2C_FUNC_I2C | I2C_FUNC_SMBUS_I2C_BLOCK)) {
+               dev_err(&client->dev, "client not i2c capable\n");
+               return -ENODEV;
+       }
+
+       lps331ap = kzalloc(sizeof(struct lps331ap_data), GFP_KERNEL);
+       if (!lps331ap) {
+               dev_err(&client->dev,
+                               "failed to allocate memory data for mudule data\n");
+               return -ENOMEM;
+       }
+
+       mutex_init(&lps331ap->lock);
+       lps331ap->client = client;
+       i2c_set_clientdata(client, lps331ap);
+
+       lps331ap->delay_ms = LPS331AP_DEFAULT_DELAY;
+
+       err = lps331ap_initchip(lps331ap);
+       if (err < 0) {
+               dev_err(&client->dev, "lps331ap_initchip failed\n");
+               goto fail_init;
+       }
+
+       err = lps331ap_input_init(lps331ap);
+       if (err < 0) {
+               dev_err(&client->dev, "input init error\n");
+               goto err_input_init;
+       }
+
+       lps331ap->enabled = 0;
+
+#ifdef CONFIG_HAS_EARLYSUSPEND
+       lps331ap->es.level = EARLY_SUSPEND_LEVEL_DISABLE_FB + 10;
+       lps331ap->es.suspend = lps331ap_early_suspend;
+       lps331ap->es.resume = lps331ap_late_resume;
+       register_early_suspend(&lps331ap->es);
+#endif
+
+       err = sysfs_create_group(&client->dev.kobj, &lps331ap_attribute_group);
+       if (err) {
+               dev_err(&client->dev, "sysfs can not create group\n");
+               goto err_sysfs;
+       }
+
+       return 0;
+
+err_sysfs:
+       unregister_early_suspend(&lps331ap->es);
+       input_unregister_device(lps331ap->input_dev);
+err_input_init:
+fail_init:
+       kfree(lps331ap);
+       return err;
+}
+
+static int __devexit lps331ap_remove(struct i2c_client *client)
+{
+       struct lps331ap_data *lps331ap = i2c_get_clientdata(client);
+
+       sysfs_remove_group(&client->dev.kobj, &lps331ap_attribute_group);
+       cancel_delayed_work_sync(&lps331ap->input_work);
+       unregister_early_suspend(&lps331ap->es);
+       input_unregister_device(lps331ap->input_dev);
+       lps331ap_power_off(lps331ap);
+       kfree(lps331ap);
+       return 0;
+}
+
+#ifdef CONFIG_PM
+static int lps331ap_suspend(struct device *dev)
+{
+       return 0;
+}
+
+static int lps331ap_resume(struct device *dev)
+{
+       return 0;
+}
+#endif /* CONFIG_PM */
+
+       static const struct dev_pm_ops lps331ap_pm_ops = {
+               SET_SYSTEM_SLEEP_PM_OPS(lps331ap_suspend,
+                               lps331ap_resume)
+       };
+
+static const struct i2c_device_id lps331ap_id[] = {
+       {LPS331AP_DEVICE_NAME, 0},
+       {},
+};
+
+static struct i2c_driver lps331ap_driver = {
+       .driver = {
+               .name = LPS331AP_DEVICE_NAME,
+               .owner = THIS_MODULE,
+               .pm = &lps331ap_pm_ops,
+       },
+       .probe = lps331ap_probe,
+       .remove = __devexit_p(lps331ap_remove),
+       .id_table = lps331ap_id,
+};
+
+static int __init lps331ap_init(void)
+{
+       return i2c_add_driver(&lps331ap_driver);
+}
+
+static void __exit lps331ap_exit(void)
+{
+       i2c_del_driver(&lps331ap_driver);
+}
+
+module_init(lps331ap_init);
+module_exit(lps331ap_exit);
+
+MODULE_DESCRIPTION("lps331ap pressure sensor driver");
+MODULE_AUTHOR("Xun Wang <xun.a.wang@intel.com>");
+MODULE_LICENSE("GPL");