aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorSamu Onkalo <samu.p.onkalo@nokia.com>2009-12-14 21:01:43 -0500
committerLinus Torvalds <torvalds@linux-foundation.org>2009-12-15 11:53:36 -0500
commit2db4a76d5f3554e9e5632c8f91828313318579c8 (patch)
tree63089645aab712fe58e382e36d7fbac14dbd150f
parent641615abfac0b7c5e6f242a6db77f7690925b443 (diff)
lis3: selftest support
Implement selftest feature as specified by chip manufacturer. Control: read selftest sysfs entry Response: "OK x y z" or "FAIL x y z" where x, y, and z are difference between selftest mode and normal mode. Test is passed when values are within acceptance limit values. Acceptance limits are provided via platform data. See chip spesifications for acceptance limits. If limits are not properly set, OK / FAIL decision is meaningless. However, userspace application can still make decision based on the numeric x, y, z values. Selftest is meant for HW diagnostic purposes. It is not meant to be called during normal use of the chip. It may cause false interrupt events. Selftest mode delays polling of the normal results but it doesn't cause wrong values. Chip must be in static state during selftest. Any acceration during the test causes most probably failure. Signed-off-by: Samu Onkalo <samu.p.onkalo@nokia.com> Acked-by: Éric Piel <Eric.Piel@tremplin-utc.net> Cc: Pavel Machek <pavel@ucw.cz> Signed-off-by: Andrew Morton <akpm@linux-foundation.org> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
-rw-r--r--drivers/hwmon/lis3lv02d.c67
-rw-r--r--drivers/hwmon/lis3lv02d.h14
-rw-r--r--include/linux/lis3lv02d.h3
3 files changed, 81 insertions, 3 deletions
diff --git a/drivers/hwmon/lis3lv02d.c b/drivers/hwmon/lis3lv02d.c
index 39b9ac8e18ed..55ec883e2026 100644
--- a/drivers/hwmon/lis3lv02d.c
+++ b/drivers/hwmon/lis3lv02d.c
@@ -106,9 +106,11 @@ static void lis3lv02d_get_xyz(struct lis3lv02d *lis3, int *x, int *y, int *z)
106{ 106{
107 int position[3]; 107 int position[3];
108 108
109 mutex_lock(&lis3->mutex);
109 position[0] = lis3->read_data(lis3, OUTX); 110 position[0] = lis3->read_data(lis3, OUTX);
110 position[1] = lis3->read_data(lis3, OUTY); 111 position[1] = lis3->read_data(lis3, OUTY);
111 position[2] = lis3->read_data(lis3, OUTZ); 112 position[2] = lis3->read_data(lis3, OUTZ);
113 mutex_unlock(&lis3->mutex);
112 114
113 *x = lis3lv02d_get_axis(lis3->ac.x, position); 115 *x = lis3lv02d_get_axis(lis3->ac.x, position);
114 *y = lis3lv02d_get_axis(lis3->ac.y, position); 116 *y = lis3lv02d_get_axis(lis3->ac.y, position);
@@ -133,6 +135,55 @@ static int lis3lv02d_get_odr(void)
133 return val; 135 return val;
134} 136}
135 137
138static int lis3lv02d_selftest(struct lis3lv02d *lis3, s16 results[3])
139{
140 u8 reg;
141 s16 x, y, z;
142 u8 selftest;
143 int ret;
144
145 mutex_lock(&lis3->mutex);
146 if (lis3_dev.whoami == WAI_12B)
147 selftest = CTRL1_ST;
148 else
149 selftest = CTRL1_STP;
150
151 lis3->read(lis3, CTRL_REG1, &reg);
152 lis3->write(lis3, CTRL_REG1, (reg | selftest));
153 msleep(lis3->pwron_delay / lis3lv02d_get_odr());
154
155 /* Read directly to avoid axis remap */
156 x = lis3->read_data(lis3, OUTX);
157 y = lis3->read_data(lis3, OUTY);
158 z = lis3->read_data(lis3, OUTZ);
159
160 /* back to normal settings */
161 lis3->write(lis3, CTRL_REG1, reg);
162 msleep(lis3->pwron_delay / lis3lv02d_get_odr());
163
164 results[0] = x - lis3->read_data(lis3, OUTX);
165 results[1] = y - lis3->read_data(lis3, OUTY);
166 results[2] = z - lis3->read_data(lis3, OUTZ);
167
168 ret = 0;
169 if (lis3->pdata) {
170 int i;
171 for (i = 0; i < 3; i++) {
172 /* Check against selftest acceptance limits */
173 if ((results[i] < lis3->pdata->st_min_limits[i]) ||
174 (results[i] > lis3->pdata->st_max_limits[i])) {
175 ret = -EIO;
176 goto fail;
177 }
178 }
179 }
180
181 /* test passed */
182fail:
183 mutex_unlock(&lis3->mutex);
184 return ret;
185}
186
136void lis3lv02d_poweroff(struct lis3lv02d *lis3) 187void lis3lv02d_poweroff(struct lis3lv02d *lis3)
137{ 188{
138 /* disable X,Y,Z axis and power down */ 189 /* disable X,Y,Z axis and power down */
@@ -365,6 +416,17 @@ void lis3lv02d_joystick_disable(void)
365EXPORT_SYMBOL_GPL(lis3lv02d_joystick_disable); 416EXPORT_SYMBOL_GPL(lis3lv02d_joystick_disable);
366 417
367/* Sysfs stuff */ 418/* Sysfs stuff */
419static ssize_t lis3lv02d_selftest_show(struct device *dev,
420 struct device_attribute *attr, char *buf)
421{
422 int result;
423 s16 values[3];
424
425 result = lis3lv02d_selftest(&lis3_dev, values);
426 return sprintf(buf, "%s %d %d %d\n", result == 0 ? "OK" : "FAIL",
427 values[0], values[1], values[2]);
428}
429
368static ssize_t lis3lv02d_position_show(struct device *dev, 430static ssize_t lis3lv02d_position_show(struct device *dev,
369 struct device_attribute *attr, char *buf) 431 struct device_attribute *attr, char *buf)
370{ 432{
@@ -394,12 +456,14 @@ static ssize_t lis3lv02d_rate_show(struct device *dev,
394 return sprintf(buf, "%d\n", lis3lv02d_get_odr()); 456 return sprintf(buf, "%d\n", lis3lv02d_get_odr());
395} 457}
396 458
459static DEVICE_ATTR(selftest, S_IRUSR, lis3lv02d_selftest_show, NULL);
397static DEVICE_ATTR(position, S_IRUGO, lis3lv02d_position_show, NULL); 460static DEVICE_ATTR(position, S_IRUGO, lis3lv02d_position_show, NULL);
398static DEVICE_ATTR(calibrate, S_IRUGO|S_IWUSR, lis3lv02d_calibrate_show, 461static DEVICE_ATTR(calibrate, S_IRUGO|S_IWUSR, lis3lv02d_calibrate_show,
399 lis3lv02d_calibrate_store); 462 lis3lv02d_calibrate_store);
400static DEVICE_ATTR(rate, S_IRUGO, lis3lv02d_rate_show, NULL); 463static DEVICE_ATTR(rate, S_IRUGO, lis3lv02d_rate_show, NULL);
401 464
402static struct attribute *lis3lv02d_attributes[] = { 465static struct attribute *lis3lv02d_attributes[] = {
466 &dev_attr_selftest.attr,
403 &dev_attr_position.attr, 467 &dev_attr_position.attr,
404 &dev_attr_calibrate.attr, 468 &dev_attr_calibrate.attr,
405 &dev_attr_rate.attr, 469 &dev_attr_rate.attr,
@@ -455,6 +519,8 @@ int lis3lv02d_init_device(struct lis3lv02d *dev)
455 return -EINVAL; 519 return -EINVAL;
456 } 520 }
457 521
522 mutex_init(&dev->mutex);
523
458 lis3lv02d_add_fs(dev); 524 lis3lv02d_add_fs(dev);
459 lis3lv02d_poweron(dev); 525 lis3lv02d_poweron(dev);
460 526
@@ -507,4 +573,3 @@ EXPORT_SYMBOL_GPL(lis3lv02d_init_device);
507MODULE_DESCRIPTION("ST LIS3LV02Dx three-axis digital accelerometer driver"); 573MODULE_DESCRIPTION("ST LIS3LV02Dx three-axis digital accelerometer driver");
508MODULE_AUTHOR("Yan Burman, Eric Piel, Pavel Machek"); 574MODULE_AUTHOR("Yan Burman, Eric Piel, Pavel Machek");
509MODULE_LICENSE("GPL"); 575MODULE_LICENSE("GPL");
510
diff --git a/drivers/hwmon/lis3lv02d.h b/drivers/hwmon/lis3lv02d.h
index c57f21f45676..166794cb91a3 100644
--- a/drivers/hwmon/lis3lv02d.h
+++ b/drivers/hwmon/lis3lv02d.h
@@ -98,7 +98,7 @@ enum lis3_who_am_i {
98 WAI_6B = 0x52, /* 6 bits: LIS331DLF - not supported */ 98 WAI_6B = 0x52, /* 6 bits: LIS331DLF - not supported */
99}; 99};
100 100
101enum lis3lv02d_ctrl1 { 101enum lis3lv02d_ctrl1_12b {
102 CTRL1_Xen = 0x01, 102 CTRL1_Xen = 0x01,
103 CTRL1_Yen = 0x02, 103 CTRL1_Yen = 0x02,
104 CTRL1_Zen = 0x04, 104 CTRL1_Zen = 0x04,
@@ -107,8 +107,17 @@ enum lis3lv02d_ctrl1 {
107 CTRL1_DF1 = 0x20, 107 CTRL1_DF1 = 0x20,
108 CTRL1_PD0 = 0x40, 108 CTRL1_PD0 = 0x40,
109 CTRL1_PD1 = 0x80, 109 CTRL1_PD1 = 0x80,
110 CTRL1_DR = 0x80, /* Data rate on 8 bits */
111}; 110};
111
112/* Delta to ctrl1_12b version */
113enum lis3lv02d_ctrl1_8b {
114 CTRL1_STM = 0x08,
115 CTRL1_STP = 0x10,
116 CTRL1_FS = 0x20,
117 CTRL1_PD = 0x40,
118 CTRL1_DR = 0x80,
119};
120
112enum lis3lv02d_ctrl2 { 121enum lis3lv02d_ctrl2 {
113 CTRL2_DAS = 0x01, 122 CTRL2_DAS = 0x01,
114 CTRL2_SIM = 0x02, 123 CTRL2_SIM = 0x02,
@@ -218,6 +227,7 @@ struct lis3lv02d {
218 unsigned long misc_opened; /* bit0: whether the device is open */ 227 unsigned long misc_opened; /* bit0: whether the device is open */
219 228
220 struct lis3lv02d_platform_data *pdata; /* for passing board config */ 229 struct lis3lv02d_platform_data *pdata; /* for passing board config */
230 struct mutex mutex; /* Serialize poll and selftest */
221}; 231};
222 232
223int lis3lv02d_init_device(struct lis3lv02d *lis3); 233int lis3lv02d_init_device(struct lis3lv02d *lis3);
diff --git a/include/linux/lis3lv02d.h b/include/linux/lis3lv02d.h
index 89701355c74f..f1ca0dcc1628 100644
--- a/include/linux/lis3lv02d.h
+++ b/include/linux/lis3lv02d.h
@@ -55,6 +55,9 @@ struct lis3lv02d_platform_data {
55 s8 axis_z; 55 s8 axis_z;
56 int (*setup_resources)(void); 56 int (*setup_resources)(void);
57 int (*release_resources)(void); 57 int (*release_resources)(void);
58 /* Limits for selftest are specified in chip data sheet */
59 s16 st_min_limits[3]; /* min pass limit x, y, z */
60 s16 st_max_limits[3]; /* max pass limit x, y, z */
58}; 61};
59 62
60#endif /* __LIS3LV02D_H_ */ 63#endif /* __LIS3LV02D_H_ */