aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/misc/lis3lv02d
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/misc/lis3lv02d')
-rw-r--r--drivers/misc/lis3lv02d/lis3lv02d.c192
-rw-r--r--drivers/misc/lis3lv02d/lis3lv02d.h49
-rw-r--r--drivers/misc/lis3lv02d/lis3lv02d_i2c.c32
-rw-r--r--drivers/misc/lis3lv02d/lis3lv02d_spi.c21
4 files changed, 285 insertions, 9 deletions
diff --git a/drivers/misc/lis3lv02d/lis3lv02d.c b/drivers/misc/lis3lv02d/lis3lv02d.c
index a981e2a42f92..4a87e5c0a320 100644
--- a/drivers/misc/lis3lv02d/lis3lv02d.c
+++ b/drivers/misc/lis3lv02d/lis3lv02d.c
@@ -39,6 +39,7 @@
#include <linux/miscdevice.h>
#include <linux/pm_runtime.h>
#include <linux/atomic.h>
+#include <linux/of_device.h>
#include "lis3lv02d.h"
#define DRIVER_NAME "lis3lv02d"
@@ -80,6 +81,15 @@
#define LIS3_SENSITIVITY_12B ((LIS3_ACCURACY * 1000) / 1024)
#define LIS3_SENSITIVITY_8B (18 * LIS3_ACCURACY)
+/*
+ * LIS331DLH spec says 1LSBs corresponds 4G/4096 -> 1LSB is 1000/1024 mG.
+ * Below macros defines sensitivity values for +/-2G. Dataout bits for
+ * +/-2G range is 12 bits so 4 bits adjustment must be done to get 12bit
+ * data from 16bit value. Currently this driver supports only 2G range.
+ */
+#define LIS3DLH_SENSITIVITY_2G ((LIS3_ACCURACY * 1000) / 1024)
+#define SHIFT_ADJ_2G 4
+
#define LIS3_DEFAULT_FUZZ_12B 3
#define LIS3_DEFAULT_FLAT_12B 3
#define LIS3_DEFAULT_FUZZ_8B 1
@@ -135,6 +145,19 @@ static s16 lis3lv02d_read_12(struct lis3lv02d *lis3, int reg)
return (s16)((hi << 8) | lo);
}
+/* 12bits for 2G range, 13 bits for 4G range and 14 bits for 8G range */
+static s16 lis331dlh_read_data(struct lis3lv02d *lis3, int reg)
+{
+ u8 lo, hi;
+ int v;
+
+ lis3->read(lis3, reg - 1, &lo);
+ lis3->read(lis3, reg, &hi);
+ v = (int) ((hi << 8) | lo);
+
+ return (s16) v >> lis3->shift_adj;
+}
+
/**
* lis3lv02d_get_axis - For the given axis, give the value converted
* @axis: 1,2,3 - can also be negative
@@ -195,6 +218,7 @@ static void lis3lv02d_get_xyz(struct lis3lv02d *lis3, int *x, int *y, int *z)
static int lis3_12_rates[4] = {40, 160, 640, 2560};
static int lis3_8_rates[2] = {100, 400};
static int lis3_3dc_rates[16] = {0, 1, 10, 25, 50, 100, 200, 400, 1600, 5000};
+static int lis3_3dlh_rates[4] = {50, 100, 400, 1000};
/* ODR is Output Data Rate */
static int lis3lv02d_get_odr(struct lis3lv02d *lis3)
@@ -267,7 +291,7 @@ static int lis3lv02d_selftest(struct lis3lv02d *lis3, s16 results[3])
(LIS3_IRQ1_DATA_READY | LIS3_IRQ2_DATA_READY));
}
- if (lis3->whoami == WAI_3DC) {
+ if ((lis3->whoami == WAI_3DC) || (lis3->whoami == WAI_3DLH)) {
ctlreg = CTRL_REG4;
selftest = CTRL4_ST0;
} else {
@@ -398,9 +422,17 @@ int lis3lv02d_poweron(struct lis3lv02d *lis3)
lis3->read(lis3, CTRL_REG2, &reg);
if (lis3->whoami == WAI_12B)
reg |= CTRL2_BDU | CTRL2_BOOT;
+ else if (lis3->whoami == WAI_3DLH)
+ reg |= CTRL2_BOOT_3DLH;
else
reg |= CTRL2_BOOT_8B;
lis3->write(lis3, CTRL_REG2, reg);
+
+ if (lis3->whoami == WAI_3DLH) {
+ lis3->read(lis3, CTRL_REG4, &reg);
+ reg |= CTRL4_BDU;
+ lis3->write(lis3, CTRL_REG4, reg);
+ }
}
err = lis3lv02d_get_pwron_wait(lis3);
@@ -912,6 +944,154 @@ static void lis3lv02d_8b_configure(struct lis3lv02d *lis3,
}
}
+#ifdef CONFIG_OF
+int lis3lv02d_init_dt(struct lis3lv02d *lis3)
+{
+ struct lis3lv02d_platform_data *pdata;
+ struct device_node *np = lis3->of_node;
+ u32 val;
+
+ if (!lis3->of_node)
+ return 0;
+
+ pdata = kzalloc(sizeof(*pdata), GFP_KERNEL);
+ if (!pdata)
+ return -ENOMEM;
+
+ if (of_get_property(np, "st,click-single-x", NULL))
+ pdata->click_flags |= LIS3_CLICK_SINGLE_X;
+ if (of_get_property(np, "st,click-double-x", NULL))
+ pdata->click_flags |= LIS3_CLICK_DOUBLE_X;
+
+ if (of_get_property(np, "st,click-single-y", NULL))
+ pdata->click_flags |= LIS3_CLICK_SINGLE_Y;
+ if (of_get_property(np, "st,click-double-y", NULL))
+ pdata->click_flags |= LIS3_CLICK_DOUBLE_Y;
+
+ if (of_get_property(np, "st,click-single-z", NULL))
+ pdata->click_flags |= LIS3_CLICK_SINGLE_Z;
+ if (of_get_property(np, "st,click-double-z", NULL))
+ pdata->click_flags |= LIS3_CLICK_DOUBLE_Z;
+
+ if (!of_property_read_u32(np, "st,click-threshold-x", &val))
+ pdata->click_thresh_x = val;
+ if (!of_property_read_u32(np, "st,click-threshold-y", &val))
+ pdata->click_thresh_y = val;
+ if (!of_property_read_u32(np, "st,click-threshold-z", &val))
+ pdata->click_thresh_z = val;
+
+ if (!of_property_read_u32(np, "st,click-time-limit", &val))
+ pdata->click_time_limit = val;
+ if (!of_property_read_u32(np, "st,click-latency", &val))
+ pdata->click_latency = val;
+ if (!of_property_read_u32(np, "st,click-window", &val))
+ pdata->click_window = val;
+
+ if (of_get_property(np, "st,irq1-disable", NULL))
+ pdata->irq_cfg |= LIS3_IRQ1_DISABLE;
+ if (of_get_property(np, "st,irq1-ff-wu-1", NULL))
+ pdata->irq_cfg |= LIS3_IRQ1_FF_WU_1;
+ if (of_get_property(np, "st,irq1-ff-wu-2", NULL))
+ pdata->irq_cfg |= LIS3_IRQ1_FF_WU_2;
+ if (of_get_property(np, "st,irq1-data-ready", NULL))
+ pdata->irq_cfg |= LIS3_IRQ1_DATA_READY;
+ if (of_get_property(np, "st,irq1-click", NULL))
+ pdata->irq_cfg |= LIS3_IRQ1_CLICK;
+
+ if (of_get_property(np, "st,irq2-disable", NULL))
+ pdata->irq_cfg |= LIS3_IRQ2_DISABLE;
+ if (of_get_property(np, "st,irq2-ff-wu-1", NULL))
+ pdata->irq_cfg |= LIS3_IRQ2_FF_WU_1;
+ if (of_get_property(np, "st,irq2-ff-wu-2", NULL))
+ pdata->irq_cfg |= LIS3_IRQ2_FF_WU_2;
+ if (of_get_property(np, "st,irq2-data-ready", NULL))
+ pdata->irq_cfg |= LIS3_IRQ2_DATA_READY;
+ if (of_get_property(np, "st,irq2-click", NULL))
+ pdata->irq_cfg |= LIS3_IRQ2_CLICK;
+
+ if (of_get_property(np, "st,irq-open-drain", NULL))
+ pdata->irq_cfg |= LIS3_IRQ_OPEN_DRAIN;
+ if (of_get_property(np, "st,irq-active-low", NULL))
+ pdata->irq_cfg |= LIS3_IRQ_ACTIVE_LOW;
+
+ if (!of_property_read_u32(np, "st,wu-duration-1", &val))
+ pdata->duration1 = val;
+ if (!of_property_read_u32(np, "st,wu-duration-2", &val))
+ pdata->duration2 = val;
+
+ if (of_get_property(np, "st,wakeup-x-lo", NULL))
+ pdata->wakeup_flags |= LIS3_WAKEUP_X_LO;
+ if (of_get_property(np, "st,wakeup-x-hi", NULL))
+ pdata->wakeup_flags |= LIS3_WAKEUP_X_HI;
+ if (of_get_property(np, "st,wakeup-y-lo", NULL))
+ pdata->wakeup_flags |= LIS3_WAKEUP_Y_LO;
+ if (of_get_property(np, "st,wakeup-y-hi", NULL))
+ pdata->wakeup_flags |= LIS3_WAKEUP_Y_HI;
+ if (of_get_property(np, "st,wakeup-z-lo", NULL))
+ pdata->wakeup_flags |= LIS3_WAKEUP_Z_LO;
+ if (of_get_property(np, "st,wakeup-z-hi", NULL))
+ pdata->wakeup_flags |= LIS3_WAKEUP_Z_HI;
+
+ if (!of_property_read_u32(np, "st,highpass-cutoff-hz", &val)) {
+ switch (val) {
+ case 1:
+ pdata->hipass_ctrl = LIS3_HIPASS_CUTFF_1HZ;
+ break;
+ case 2:
+ pdata->hipass_ctrl = LIS3_HIPASS_CUTFF_2HZ;
+ break;
+ case 4:
+ pdata->hipass_ctrl = LIS3_HIPASS_CUTFF_4HZ;
+ break;
+ case 8:
+ pdata->hipass_ctrl = LIS3_HIPASS_CUTFF_8HZ;
+ break;
+ }
+ }
+
+ if (of_get_property(np, "st,hipass1-disable", NULL))
+ pdata->hipass_ctrl |= LIS3_HIPASS1_DISABLE;
+ if (of_get_property(np, "st,hipass2-disable", NULL))
+ pdata->hipass_ctrl |= LIS3_HIPASS2_DISABLE;
+
+ if (of_get_property(np, "st,axis-x", &val))
+ pdata->axis_x = val;
+ if (of_get_property(np, "st,axis-y", &val))
+ pdata->axis_y = val;
+ if (of_get_property(np, "st,axis-z", &val))
+ pdata->axis_z = val;
+
+ if (of_get_property(np, "st,default-rate", NULL))
+ pdata->default_rate = val;
+
+ if (of_get_property(np, "st,min-limit-x", &val))
+ pdata->st_min_limits[0] = val;
+ if (of_get_property(np, "st,min-limit-y", &val))
+ pdata->st_min_limits[1] = val;
+ if (of_get_property(np, "st,min-limit-z", &val))
+ pdata->st_min_limits[2] = val;
+
+ if (of_get_property(np, "st,max-limit-x", &val))
+ pdata->st_max_limits[0] = val;
+ if (of_get_property(np, "st,max-limit-y", &val))
+ pdata->st_max_limits[1] = val;
+ if (of_get_property(np, "st,max-limit-z", &val))
+ pdata->st_max_limits[2] = val;
+
+
+ lis3->pdata = pdata;
+
+ return 0;
+}
+
+#else
+int lis3lv02d_init_dt(struct lis3lv02d *lis3)
+{
+ return 0;
+}
+#endif
+EXPORT_SYMBOL_GPL(lis3lv02d_init_dt);
+
/*
* Initialise the accelerometer and the various subsystems.
* Should be rather independent of the bus system.
@@ -956,6 +1136,16 @@ int lis3lv02d_init_device(struct lis3lv02d *lis3)
lis3->odr_mask = CTRL1_ODR0|CTRL1_ODR1|CTRL1_ODR2|CTRL1_ODR3;
lis3->scale = LIS3_SENSITIVITY_8B;
break;
+ case WAI_3DLH:
+ pr_info("16 bits lis331dlh sensor found\n");
+ lis3->read_data = lis331dlh_read_data;
+ lis3->mdps_max_val = 2048; /* 12 bits for 2G */
+ lis3->shift_adj = SHIFT_ADJ_2G;
+ lis3->pwron_delay = LIS3_PWRON_DELAY_WAI_8B;
+ lis3->odrs = lis3_3dlh_rates;
+ lis3->odr_mask = CTRL1_DR0 | CTRL1_DR1;
+ lis3->scale = LIS3DLH_SENSITIVITY_2G;
+ break;
default:
pr_err("unknown sensor type 0x%X\n", lis3->whoami);
return -EINVAL;
diff --git a/drivers/misc/lis3lv02d/lis3lv02d.h b/drivers/misc/lis3lv02d/lis3lv02d.h
index 2b1482ad3f16..c439c827eea8 100644
--- a/drivers/misc/lis3lv02d/lis3lv02d.h
+++ b/drivers/misc/lis3lv02d/lis3lv02d.h
@@ -26,12 +26,12 @@
/*
* This driver tries to support the "digital" accelerometer chips from
* STMicroelectronics such as LIS3LV02DL, LIS302DL, LIS3L02DQ, LIS331DL,
- * LIS35DE, or LIS202DL. They are very similar in terms of programming, with
- * almost the same registers. In addition to differing on physical properties,
- * they differ on the number of axes (2/3), precision (8/12 bits), and special
- * features (freefall detection, click...). Unfortunately, not all the
- * differences can be probed via a register.
- * They can be connected either via I²C or SPI.
+ * LIS331DLH, LIS35DE, or LIS202DL. They are very similar in terms of
+ * programming, with almost the same registers. In addition to differing
+ * on physical properties, they differ on the number of axes (2/3),
+ * precision (8/12 bits), and special features (freefall detection,
+ * click...). Unfortunately, not all the differences can be probed via
+ * a register. They can be connected either via I²C or SPI.
*/
#include <linux/lis3lv02d.h>
@@ -96,12 +96,22 @@ enum lis3lv02d_reg {
};
enum lis3_who_am_i {
+ WAI_3DLH = 0x32, /* 16 bits: LIS331DLH */
WAI_3DC = 0x33, /* 8 bits: LIS3DC, HP3DC */
WAI_12B = 0x3A, /* 12 bits: LIS3LV02D[LQ]... */
WAI_8B = 0x3B, /* 8 bits: LIS[23]02D[LQ]... */
WAI_6B = 0x52, /* 6 bits: LIS331DLF - not supported */
};
+enum lis3_type {
+ LIS3LV02D,
+ LIS3DC,
+ HP3DC,
+ LIS2302D,
+ LIS331DLF,
+ LIS331DLH,
+};
+
enum lis3lv02d_ctrl1_12b {
CTRL1_Xen = 0x01,
CTRL1_Yen = 0x02,
@@ -129,6 +139,27 @@ enum lis3lv02d_ctrl1_3dc {
CTRL1_ODR3 = 0x80,
};
+enum lis331dlh_ctrl1 {
+ CTRL1_DR0 = 0x08,
+ CTRL1_DR1 = 0x10,
+ CTRL1_PM0 = 0x20,
+ CTRL1_PM1 = 0x40,
+ CTRL1_PM2 = 0x80,
+};
+
+enum lis331dlh_ctrl2 {
+ CTRL2_HPEN1 = 0x04,
+ CTRL2_HPEN2 = 0x08,
+ CTRL2_FDS_3DLH = 0x10,
+ CTRL2_BOOT_3DLH = 0x80,
+};
+
+enum lis331dlh_ctrl4 {
+ CTRL4_STSIGN = 0x08,
+ CTRL4_BLE = 0x40,
+ CTRL4_BDU = 0x80,
+};
+
enum lis3lv02d_ctrl2 {
CTRL2_DAS = 0x01,
CTRL2_SIM = 0x02,
@@ -279,9 +310,14 @@ struct lis3lv02d {
int data_ready_count[2];
atomic_t wake_thread;
unsigned char irq_cfg;
+ unsigned int shift_adj;
struct lis3lv02d_platform_data *pdata; /* for passing board config */
struct mutex mutex; /* Serialize poll and selftest */
+
+#ifdef CONFIG_OF
+ struct device_node *of_node;
+#endif
};
int lis3lv02d_init_device(struct lis3lv02d *lis3);
@@ -290,5 +326,6 @@ void lis3lv02d_joystick_disable(struct lis3lv02d *lis3);
void lis3lv02d_poweroff(struct lis3lv02d *lis3);
int lis3lv02d_poweron(struct lis3lv02d *lis3);
int lis3lv02d_remove_fs(struct lis3lv02d *lis3);
+int lis3lv02d_init_dt(struct lis3lv02d *lis3);
extern struct lis3lv02d lis3_dev;
diff --git a/drivers/misc/lis3lv02d/lis3lv02d_i2c.c b/drivers/misc/lis3lv02d/lis3lv02d_i2c.c
index e8c0019da97a..60ec8689d6e3 100644
--- a/drivers/misc/lis3lv02d/lis3lv02d_i2c.c
+++ b/drivers/misc/lis3lv02d/lis3lv02d_i2c.c
@@ -31,6 +31,10 @@
#include <linux/i2c.h>
#include <linux/pm_runtime.h>
#include <linux/delay.h>
+#include <linux/of.h>
+#include <linux/of_platform.h>
+#include <linux/of_device.h>
+
#include "lis3lv02d.h"
#define DRV_NAME "lis3lv02d_i2c"
@@ -90,7 +94,11 @@ static int lis3_i2c_init(struct lis3lv02d *lis3)
if (ret < 0)
return ret;
- reg |= CTRL1_PD0 | CTRL1_Xen | CTRL1_Yen | CTRL1_Zen;
+ if (lis3->whoami == WAI_3DLH)
+ reg |= CTRL1_PM0 | CTRL1_Xen | CTRL1_Yen | CTRL1_Zen;
+ else
+ reg |= CTRL1_PD0 | CTRL1_Xen | CTRL1_Yen | CTRL1_Zen;
+
return lis3->write(lis3, CTRL_REG1, reg);
}
@@ -98,12 +106,30 @@ static int lis3_i2c_init(struct lis3lv02d *lis3)
static union axis_conversion lis3lv02d_axis_map =
{ .as_array = { LIS3_DEV_X, LIS3_DEV_Y, LIS3_DEV_Z } };
+#ifdef CONFIG_OF
+static struct of_device_id lis3lv02d_i2c_dt_ids[] = {
+ { .compatible = "st,lis3lv02d" },
+ {}
+};
+MODULE_DEVICE_TABLE(of, lis3lv02d_i2c_dt_ids);
+#endif
+
static int __devinit lis3lv02d_i2c_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
int ret = 0;
struct lis3lv02d_platform_data *pdata = client->dev.platform_data;
+#ifdef CONFIG_OF
+ if (of_match_device(lis3lv02d_i2c_dt_ids, &client->dev)) {
+ lis3_dev.of_node = client->dev.of_node;
+ ret = lis3lv02d_init_dt(&lis3_dev);
+ if (ret)
+ return ret;
+ pdata = lis3_dev.pdata;
+ }
+#endif
+
if (pdata) {
if ((pdata->driver_features & LIS3_USE_BLOCK_READ) &&
(i2c_check_functionality(client->adapter,
@@ -231,7 +257,8 @@ static int lis3_i2c_runtime_resume(struct device *dev)
#endif /* CONFIG_PM_RUNTIME */
static const struct i2c_device_id lis3lv02d_id[] = {
- {"lis3lv02d", 0 },
+ {"lis3lv02d", LIS3LV02D},
+ {"lis331dlh", LIS331DLH},
{}
};
@@ -250,6 +277,7 @@ static struct i2c_driver lis3lv02d_i2c_driver = {
.name = DRV_NAME,
.owner = THIS_MODULE,
.pm = &lis3_pm_ops,
+ .of_match_table = of_match_ptr(lis3lv02d_i2c_dt_ids),
},
.probe = lis3lv02d_i2c_probe,
.remove = __devexit_p(lis3lv02d_i2c_remove),
diff --git a/drivers/misc/lis3lv02d/lis3lv02d_spi.c b/drivers/misc/lis3lv02d/lis3lv02d_spi.c
index 80880e984b4f..ccb6475fa059 100644
--- a/drivers/misc/lis3lv02d/lis3lv02d_spi.c
+++ b/drivers/misc/lis3lv02d/lis3lv02d_spi.c
@@ -17,6 +17,9 @@
#include <linux/workqueue.h>
#include <linux/spi/spi.h>
#include <linux/pm.h>
+#include <linux/of.h>
+#include <linux/of_platform.h>
+#include <linux/of_device.h>
#include "lis3lv02d.h"
@@ -58,6 +61,14 @@ static int lis3_spi_init(struct lis3lv02d *lis3)
static union axis_conversion lis3lv02d_axis_normal =
{ .as_array = { 1, 2, 3 } };
+#ifdef CONFIG_OF
+static struct of_device_id lis302dl_spi_dt_ids[] = {
+ { .compatible = "st,lis302dl-spi" },
+ {}
+};
+MODULE_DEVICE_TABLE(of, lis302dl_spi_dt_ids);
+#endif
+
static int __devinit lis302dl_spi_probe(struct spi_device *spi)
{
int ret;
@@ -75,6 +86,15 @@ static int __devinit lis302dl_spi_probe(struct spi_device *spi)
lis3_dev.irq = spi->irq;
lis3_dev.ac = lis3lv02d_axis_normal;
lis3_dev.pdata = spi->dev.platform_data;
+
+#ifdef CONFIG_OF
+ if (of_match_device(lis302dl_spi_dt_ids, &spi->dev)) {
+ lis3_dev.of_node = spi->dev.of_node;
+ ret = lis3lv02d_init_dt(&lis3_dev);
+ if (ret)
+ return ret;
+ }
+#endif
spi_set_drvdata(spi, &lis3_dev);
return lis3lv02d_init_device(&lis3_dev);
@@ -121,6 +141,7 @@ static struct spi_driver lis302dl_spi_driver = {
.name = DRV_NAME,
.owner = THIS_MODULE,
.pm = &lis3lv02d_spi_pm,
+ .of_match_table = of_match_ptr(lis302dl_spi_dt_ids),
},
.probe = lis302dl_spi_probe,
.remove = __devexit_p(lis302dl_spi_remove),